cytopia / devilbox

A modern Docker LAMP stack and MEAN stack for local development
http://devilbox.org
MIT License
4.39k stars 653 forks source link

Working on NodeJS project - use nginx as reverse proxy / upstream Node app #240

Closed mrpsiho closed 5 years ago

mrpsiho commented 6 years ago

Hi! Hope you are doing well. I have a question about using devilbox while working on NodeJS project. In essence, I would like to ask how to access my Node app via custom domain like 'node-app.loc'?

I start my Node app inside a container via terminal and it is up and running on localhost:3000. But I am lost how to access it? Visiting http://node-app.loc gives me Error 403 Forbidden. I guess I could achieve what I need by modifying nginx.yml within '.devilbox' folder of my project folder, however, I don't know what exactly to put inside this config file. Please, help.

cytopia commented 6 years ago

This needs to be automated ;-) I still have https support on the agenda and will come to this issue afterwards.

For now, have a look at vhost-gen to generate yourself a reverse proxy config.

jebog commented 6 years ago

Thanks i think this is the answer for my question i will and will make a feed back

stkrzysiak commented 6 years ago

I'm curious what the plan is to implement this, I notice that vhost_gen takes an r flag for this yet the nginx container never passes -r(from what I can tell), so it seems that the fix for this would be on the container level, perhaps a check for rproxy in create-vhost.sh? If I'm anywhere in the ballpark on how this might work then let me know and I'll make some time to work on it this weekend and maybe have a PR at some point.

For others trying to sort this out, here's a gist with how I got it to work with my node project on port 3000. The important pieces are the IP, port, and the php_fpm ...enable: false piece. Without that last piece vhost_gen fails.

cytopia commented 6 years ago

@stkrzysiak this is my current plan for setting it up:

  1. The webserver have a TCP API on some port that is accessable by the PHP container
  2. The API is able to create and delete vhosts (reverse) on call
  3. The PHP container will use watcher to monitor new/released listening ports
  4. The PHP container will then trigger the API of the webserver adding/removing vhosts

Workflow sounds pretty simple. Once I have the API server finished and integrated into all web server it should more or less be a self-writer ;-)

science695 commented 6 years ago

Have you thought about having it watch for changes in a proxy config file instead of a complicated API setup?

i.e.:

PROXY_CONF_DIR=proxy_conf/

and in there, each host to proxy would have a config file:

nodesubdomain.yml

(or env or whatever format)

That would have the 4 or 5 config settings for what ip/port to forward to.

I am doing something similar to this by having a new folder for that project (in the projects folder), and then I put in a custom .devilbox/apache24.yml file which added the reverse proxy.

ekam230 commented 6 years ago

Please, write step-by-step manual for use nodejs server on port in devilbox stack.

my attempts weren't crowned with success

science695 commented 6 years ago
  1. Make a folder for this project (or move it) in the folder where devilbox is looking for projects.
  2. Make a .devilbox/ folder inside it.
  3. Copy in the appropriate virtualhost template for your setup (nginx, apache22, apache24, ...): https://devilbox.readthedocs.io/en/latest/vhost-gen/customize-specific-virtual-host.html?highlight=virtual%20host#templates-explained
  4. Edit that file and change all the proxy lines to proxy to your host:port

    SetHandler proxy:fcgi://PHP_ADDR:PHP_PORT

would change to:

SetHandler proxy:http://172.168.238.1:PORT

  1. If just changing that one line, you may need to put this ip and port in the several proxy lines.

I'm sorry there are no step-by-step instructions. What you are asking has no easy configurable option, and is only possible with this type of advanced configuration.

cytopia commented 5 years ago

Status update

There are two components to making this work:

  1. Auto-starting node application
  2. Reverse proxy for node application

The auto-start feature is currently PR-ed and will be merged soon. It will allow you to automatically have any node/npm (or others) spin up during Devilbox startup: https://github.com/cytopia/devilbox/pull/446

The second part will only require some documentation to copy/paste working vhost-gen configs into the project directory. So I will have to create three config templates:

  1. Apache 2.2
  2. Apache 2.4
  3. Nginx
cytopia commented 5 years ago

@mrpsiho @jebog @stkrzysiak @science695 @ekam230

Status

I have successfully managed to get Node up and running behind Nginx reverse proxy:

Goal:

When the Devilbox starts up, the Node project should automatically start and be available via the vhosts.php intranet page by:

Project info

Reverse vhost for Nginx

How to customize individual vhosts: https://devilbox.readthedocs.io/en/latest/vhost-gen/customize-specific-virtual-host.html

/shared/httpd/node/.devilbox/nginx.yml (path inside the container):

---

vhost: |
  server {
      listen       __PORT____DEFAULT_VHOST__;
      server_name  __VHOST_NAME__;

      access_log   "__ACCESS_LOG__" combined;
      error_log    "__ERROR_LOG__" warn;

      location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # CHANGE: The port will be the only thing to adjust for projects
        proxy_pass http://php:8000;
      }
  __REDIRECT__
  __SSL__
  __ALIASES__
  }

# ------------------------------------------------------------------------------------------
# Do not change anything below this line
# ------------------------------------------------------------------------------------------
vhost_type:
  docroot: ""
  rproxy: ""

features:
  ssl: |
    ssl_certificate           __SSL_PATH_CRT__;
    ssl_certificate_key       __SSL_PATH_KEY__;
    ssl_protocols             __SSL_PROTOCOLS__;
    ssl_prefer_server_ciphers __SSL_HONOR_CIPHER_ORDER__;
    ssl_ciphers               __SSL_CIPHERS__;

  redirect: |
    return 301 https://__VHOST_NAME__:__SSL_PORT__$request_uri;

  php_fpm: ""

  alias: |
    location ~ __ALIAS__ {
        root  __PATH__;
    __XDOMAIN_REQ__
    }

  deny: |
    location ~ __REGEX__ {
        deny all;
    }

  server_status: |
    location ~ __REGEX__ {
        stub_status on;
        access_log off;
    }

  xdomain_request: |
    if ( $http_origin ~* (__REGEX__) ) {
        add_header "Access-Control-Allow-Origin" "$http_origin";
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
        add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
        add_header 'Access-Control-Max-Age' 0;
        return 200;
    }

Autostart script:

https://devilbox.readthedocs.io/en/latest/advanced/custom-startup-commands.html

cfg/php-startup-7.2/node.sh (path in the Devilbox git directory)

# https://github.com/Unitech/pm2
npm install pm2 -g
su -c 'cd /shared/httpd/node/node; pm2 start index.js' -l devilbox

Todo:

cytopia commented 5 years ago

pm2 will be bundled with the next round of PHP images (https://github.com/cytopia/devilbox/pull/449). I will add also CI tests to ensure it will work with Apache 2.2, Apache 2.4 and Nginx.

cytopia commented 5 years ago

I've now added vhost-gen templates for reverse proxies that only need copy/paste into the project directory making it easier to set them up.

Additionally reverse proxy projects are also added to integration tests via Travis CI to ensure they work on all Webservers and with all PHP images.

cytopia commented 5 years ago

Update

Reverse proxing applications feature is now finished in above mentioned PR. Currently not only NodeJS applications can easily be proxied, but also any other custom container can be attached and proxied to the webserver with valid HTTPS and even have it be displayed in the vhost section of the intranet:

Examples:

Looks like this feature will now allow to extend the Devilbox with whatever programming language you wanna go with.

I will post the documentation in the coming days

cytopia commented 5 years ago

Done

Its now working like a charm:

General

NodeJS Specific

felixmosh commented 5 years ago

@cytopia first of all THANK YOU for your awesome job, this env is amazing! 👏🏽 I'm trying to implement the node + nginx reverse proxy tutorial (https://devilbox.readthedocs.io/en/latest/examples/setup-reverse-proxy-nodejs.html) and looks like the nginx is not reversing to node.

I've checked with PM2 that the node process is alive, I've curled it with the port (in my case it is 3000) and it works, but when I try to access it from the host, I get the DevilBox interface.

What can be the issue?

cytopia commented 5 years ago

@felixmosh was this issue resolved by your PR: https://github.com/cytopia/devilbox/pull/492/files?

felixmosh commented 5 years ago

Of course no :), I've changed in nginx-reverse.yml to port 3000 as what I use.

cytopia commented 5 years ago

If not, can you paste the resulting vhost config of your project. You can find it on the vhost page, when you click on the wheel:

screenshot 2019-02-03 19-36-45 selection

felixmosh commented 5 years ago

Oh, you are right! I've mapped the wrong url (I have a different config for apache).

Now, I get 403 Forbidden from nginx. This is my config

server {
    listen       443 ssl http2;
    server_name  test-mysql.loc;

    access_log   "/var/log/nginx-stable/test-mysql_ssl-access.log" combined;
    error_log    "/var/log/nginx-stable/test-mysql_ssl-error.log" warn;

    ssl_certificate           /etc/httpd/cert/mass/test-mysql.loc.crt;
    ssl_certificate_key       /etc/httpd/cert/mass/test-mysql.loc.key;
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers               HIGH:!aNULL:!MD5;

    # Define the vhost to serve files
    root         "/shared/httpd/test-mysql/";
    index        index.php index.html index.htm;

    # PHP-FPM Definition
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php?$ {
        try_files $uri = 404;
        include fastcgi_params;

        # https://stackoverflow.com/questions/1733306/nginx-errors-readv-and-recv-failed/51457613#51457613
        fastcgi_keep_conn off;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(.*)$;

        fastcgi_pass php:9000;
        fastcgi_read_timeout 180;

        fastcgi_index index.php;
        fastcgi_intercept_errors on;
    }

    # Alias Definition
    location ~ /devilbox-api/ {
        root  /var/www/default/api;
        # Allow cross domain request from these hosts
        if ( $http_origin ~* (http(s)?://(.*)$) ) {
            add_header "Access-Control-Allow-Origin" "$http_origin";
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Max-Age' 0;
            return 200;
        }
    }

    # Deny Definition
    location ~ /\.git {
        deny all;
    }

    # Deny Definition
    location ~ /\.ht.* {
        deny all;
    }

    # Custom directives

}
server {
    listen       80;
    server_name  test-mysql.loc;

    access_log   "/var/log/nginx-stable/test-mysql-access.log" combined;
    error_log    "/var/log/nginx-stable/test-mysql-error.log" warn;

    # Define the vhost to serve files
    root         "/shared/httpd/test-mysql/";
    index        index.php index.html index.htm;

    # PHP-FPM Definition
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php?$ {
        try_files $uri = 404;
        include fastcgi_params;

        # https://stackoverflow.com/questions/1733306/nginx-errors-readv-and-recv-failed/51457613#51457613
        fastcgi_keep_conn off;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(.*)$;

        fastcgi_pass php:9000;
        fastcgi_read_timeout 180;

        fastcgi_index index.php;
        fastcgi_intercept_errors on;
    }

    # Alias Definition
    location ~ /devilbox-api/ {
        root  /var/www/default/api;
        # Allow cross domain request from these hosts
        if ( $http_origin ~* (http(s)?://(.*)$) ) {
            add_header "Access-Control-Allow-Origin" "$http_origin";
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Max-Age' 0;
            return 200;
        }
    }

    # Deny Definition
    location ~ /\.git {
        deny all;
    }

    # Deny Definition
    location ~ /\.ht.* {
        deny all;
    }

    # Custom directives

}
cytopia commented 5 years ago

This is not a reverse proxy config, it's the normal forward config. How does your nginx.yml (project specific vhost-gen config) look like? Take the one you can find on the intranet vhost-page, when you click on the funnel (next to the wheel) - see my screenshot above.

felixmosh commented 5 years ago

I don't have a funnel, 😒 image

cytopia commented 5 years ago

This is because you simply missed step 5 (the reverse proxy setup):

https://devilbox.readthedocs.io/en/latest/examples/setup-reverse-proxy-nodejs.html#add-reverse-proxy-vhost-gen-config-files

felixmosh commented 5 years ago

I did it, the only thing I didn't done is copy all the *-reverse.yml files (cause I'm using only nginx)

cytopia commented 5 years ago

You will actually need to copy nginx.yml-example-rproxy (from cfg/vhost-gen) into your project: ./devilbox/nginx.yml

felixmosh commented 5 years ago

That is what I've done, the file suppose to be at test-mysql/.devilbox/nginx.yml (with a dot before devilbox, HTTPD_TEMPLATE_DIR=.devilbox)

cytopia commented 5 years ago

Yes, did you then restart the Devilbox?

felixmosh commented 5 years ago

Yeap, I've even tried to docker-compose rm -f

cytopia commented 5 years ago

Are you on the latest master branch?

felixmosh commented 5 years ago

Yes, I'm on latest master, I'm invoking docker-compose up httpd php mysql maybe that is the difference?

felixmosh commented 5 years ago

WOW! 🎊 I've added bind to the execution command and that worked!

docker-compose up httpd php mysql bind solved!

cytopia commented 5 years ago

Hmm strange. Bind should actually autostart, no matter what. That's how it's been defined in docker-compose.yml.

cytopia commented 1 year ago

FYI: @felixmosh @ekam230 @stkrzysiak @mrpsiho @jebog

This has now been automated: https://github.com/cytopia/devilbox/pull/942

agathasweb commented 3 months ago

I noticed something intriguing, maybe it's my lack of experience. But when I use the reverse proxy for PHP to listen to my vue on port 8080 and I need to use PHP in the same environment, PHP no longer works, only vuejs. Is that right or did I configure something wrong?