簡體   English   中英

需要幫助在 VPS 上使用 nginx-proxy 在 Docker 容器中代理 React 和 NodeJS 應用程序

[英]Need help proxying React and NodeJS apps with nginx-proxy on a VPS, in Docker containers

我要做的是在 VPS 上使用 Nestjs + React + MySQL + Nginx 堆棧部署一個 dockerized monorepo 項目(使用 NX 作為 monorepo 框架)。 我希望 nginx 代理監聽主機的 88 端口(因為另一個堆棧使用端口 80,這是一個我不敢碰的舊堆棧)。 VPS 的操作系統是 CentOS 7。

我會盡量保留構建(Dockerfile)的大部分細節,但知道構建工作,它都在我的本地環境中工作(主要是因為我不使用 nginx-proxy 進行本地開發)而且我知道這與我的 Docker 配置(我使用 docker-compose)或主機的網絡有關。

這是堆棧的“鳥瞰圖”:

  • React-frontend 容器正在容器中的端口 4200 上運行一個反應應用程序(使用 nx serve react-frontend),將端口 4200 暴露給主機
  • backend-api 容器在容器的 3333 端口上運行 nodejs 應用程序(使用 nodejs 入口點),將端口暴露給主機
  • 一個 MySQL 容器,運行一個 mysql 服務器,運行在容器的 3306 端口,暴露在 Host 的 3307 端口
  • 使用 jwilder/nginx-proxy docker 鏡像(我也嘗試使用 nginxproxy/nginx-proxy docker 鏡像)的 nginx-proxy 監聽主機的 88 端口並通過代理傳遞將請求重定向到 react-frontend 容器(這是部分我失敗了)。

所以這是我的“compose-prod.yml” docker-compose 文件:

version: "3.7"

networks:
  corp:
    driver: bridge
  nginx-proxy:
    external:
      name: nginx-proxy

volumes:
  backend-db-volume:
    driver: local

services:
  nginx-proxy:
    image: jwilder/nginx-proxy # also tried nginxproxy/nginx-proxy image
    container_name: nginx-proxy
    networks:
      - corp
      - nginx-proxy
    environment:
      HTTP_PORT: 88
    ports:
      - "88:88" # also tried "88:80" but that gives me "connection refused" in the browser
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  backend-db:
    image: backend-db
    hostname: backend-db
    restart: unless-stopped
    volumes:
      - backend-db-volume:/var/lib/mysql
    networks:
      - corp
    build:
      context: ./apps/backend-db
      dockerfile: ./Dockerfile
    ports:
      - 3307:3306
    expose:
      - 3306

  backend-api:
    container_name: backend-api
    depends_on:
      - backend-db
    build:
      context: ./
      cache_from:
        - base-image:nx-base
      dockerfile: ./apps/backend-api/Dockerfile
      args:
        NODE_ENV: "production"
        BUILD_FLAG: ""
    image: backend-api:nx-dev
    ports:
      - "3333:3333"
    environment:
      NODE_ENV: "production"
      PORT: 3333
      [... other env configs ommitted, like DB variables, etc.]
    networks:
      - corp
    restart: on-failure

  react-frontend:
    container_name: react-frontend
    build:
      context: ./
      dockerfile: ./apps/react-frontend/Dockerfile
      args:
        NODE_ENV: "production"
        BUILD_FLAG: ""
    image: react-frontend:nx-dev
    environment:
      VIRTUAL_HOST: react-frontend # note that my domain is react-frontend.com, obfuscated ofc ... which I also tried using in VIRTUAL_HOST config
      VIRTUAL_PORT: 4200
      NGINX_PROXY_CONTAINER: nginx-proxy
      NODE_ENV: "production"
      [...other env configs ommitted]
    ports:
      - "4200:4200"
    expose:
      - 4200
    networks:
      - nginx-proxy
      - corp
    restart: on-failure

nginx-proxy 容器會自動檢測使用 VIRTUAL_HOST 環境運行的容器。 啟用變量,為那些從 compose-prod.yml 文件生成配置。 現在,我使用“docker exec nginx-proxy cat /etc/nginx/conf.d/default.conf”命令生成的配置是這樣的:

# nginx-proxy version : 1.0.1-6-gc4ad18f
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
  default $http_x_forwarded_proto;
  ''      $scheme;
}

# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
  default $http_x_forwarded_port;
  ''      $server_port;
}

# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
  default upgrade;
  '' close;
}

# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
  default off;
  https on;
}

gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '"$http_referer" "$http_user_agent" '
                 '"$upstream_addr"';
access_log off;
                ssl_protocols TLSv1.2 TLSv1.3;
                ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
                ssl_prefer_server_ciphers off;
error_log /dev/stderr;
resolver 127.0.0.11;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Original-URI $request_uri;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
        server_name _; # This is just an invalid value which will never trigger on a real hostname.
        server_tokens off;
        listen 88;
        access_log /var/log/nginx/access.log vhost;
        return 503;
}
        # react-frontend
upstream react-frontend {
        ## Can be connected with "react-frontend_corp" network
        # react-frontend
        server <IP of react-frontend container on Docker network>:4200;
        # Cannot connect to network 'nginx-proxy' of this container
        # Cannot connect to network 'react-frontend_corp' of this container
        ## Can be connected with "nginx-proxy" network
        # react-frontend
        server <IP of react-frontend container on Docker network>:4200;
}
server {
        server_name react-frontend;
        listen 88 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://react-frontend;
        }
}

當我訪問“example.com:88”時,我在瀏覽器中從 nginx 返回一個“503 服務暫時不可用”頁面,我在 nginx 的訪問日志中看到了這一點:

nginx-proxy     | nginx.1     | example.com xx.yy.zz.ip - - [06/Jul/2022:16:48:12 +0000] "GET / HTTP/1.1" 503 592 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36" "-"
nginx-proxy     | nginx.1     | example.com xx.yy.zz.ip - - [06/Jul/2022:16:48:12 +0000] "GET /favicon.ico HTTP/1.1" 503 592 "http://example.com:88/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36" "-"

我省略了 Dockerfiles,因為 nginx-proxy 容器沒有構建,它是從圖像中獲取的,所有構建工作......這是給我帶來麻煩的部署。

有人對我所缺少的有任何指示嗎? 我應該檢查什么? 這是針對個人項目的,即使我可以作為 devop 解決問題,Docker 網絡/部署有時仍然讓我感到困惑。

編輯:我在這里添加 VPS(主機)虛擬主機配置(nginx)......所以也許我可以使用這個配置代理傳遞......我將如何修改這個配置,以便我可以代理傳遞請求到“示例” docker 容器暴露端口 4200(而不是 VPS 上的根目錄)?

# configuration file /etc/nginx/conf.d/users/example.conf:
proxy_cache_path /var/cache/ea-nginx/proxy/example levels=1:2 keys_zone=example:10m inactive=60m;

#### main domain for example ##
server {
    server_name example.com www.example.com mail.example.com;
    listen 80;
    listen [::]:80;

    include conf.d/includes-optional/cloudflare.conf;

    set $CPANEL_APACHE_PROXY_PASS $scheme://apache_backend_${scheme}_51_222_24_216;

    # For includes:
    set $CPANEL_APACHE_PROXY_IP 51.222.24.216;
    set $CPANEL_APACHE_PROXY_SSL_IP 51.222.24.216;

    set $CPANEL_PROXY_CACHE example;
    set $CPANEL_SKIP_PROXY_CACHING 0;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /var/cpanel/ssl/apache_tls/example.com/combined;
    ssl_certificate_key /var/cpanel/ssl/apache_tls/example.com/combined;

    ssl_protocols TLSv1.2 TLSv1.3;
    proxy_ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
    proxy_ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;

    root /home/example/public_html;

    location /cpanelwebcall {
        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass http://127.0.0.1:2082/cpanelwebcall;
    }

    location /Microsoft-Server-ActiveSync {
        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass http://127.0.0.1:2090/Microsoft-Server-ActiveSync;
    }

    location = /favicon.ico {
        allow all;
        log_not_found off;
        access_log off;
        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass $CPANEL_APACHE_PROXY_PASS;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass $CPANEL_APACHE_PROXY_PASS;
    }

    location / {
        proxy_cache $CPANEL_PROXY_CACHE;
        proxy_no_cache $CPANEL_SKIP_PROXY_CACHING;
        proxy_cache_bypass $CPANEL_SKIP_PROXY_CACHING;

        proxy_cache_valid 200 301 302 60m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout http_429 http_500 http_502 http_503 http_504;
        proxy_cache_background_update on;
        proxy_cache_revalidate on;
        proxy_cache_min_uses 1;
        proxy_cache_lock on;

        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass $CPANEL_APACHE_PROXY_PASS;
    }


    include conf.d/server-includes/*.conf;
    include conf.d/users/example/*.conf;
    include conf.d/users/example/example.com/*.conf;
}
server {
    listen 80;
    listen [::]:80;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /var/cpanel/ssl/apache_tls/example.com/combined;
    ssl_certificate_key /var/cpanel/ssl/apache_tls/example.com/combined;

    server_name  cpanel.example.com cpcalendars.example.com cpcontacts.example.com webdisk.example.com webmail.example.com;

    include conf.d/includes-optional/cloudflare.conf;

    set $CPANEL_APACHE_PROXY_PASS $scheme://apache_backend_${scheme}_51_222_24_216;

    # For includes:
    set $CPANEL_APACHE_PROXY_IP 51.222.24.216;
    set $CPANEL_APACHE_PROXY_SSL_IP 51.222.24.216;

    location /.well-known/cpanel-dcv {
        root /home/example/public_html;
        disable_symlinks if_not_owner;
    }

    location /.well-known/pki-validation {
        root /home/example/public_html;
        disable_symlinks if_not_owner;
    }

    location /.well-known/acme-challenge {
        root /home/example/public_html;
        disable_symlinks if_not_owner;
    }

    location / {
        # Force https for service subdomains
        if ($scheme = http) {
            return 301 https://$host$request_uri;
        }

        # no cache
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;

        # pass to Apache
        include conf.d/includes-optional/cpanel-proxy.conf;
        proxy_pass $CPANEL_APACHE_PROXY_PASS;
    }
}

如果您想使用example.comwww.example.com訪問您的應用程序,您必須設置server_name example.com *.example.com; . 您也可以在使用 docker-compose 時使用 DNS 訪問 docker 容器,在您的情況下backend-api:3333react-frontend:4200 您的配置中幾乎沒有更正。

server {
        server_name _; # This is just an invalid value which will never trigger on a real hostname.
        server_tokens off;
        listen 88;
        access_log /var/log/nginx/access.log vhost;
        return 503;
}
        # react-frontend
upstream frontends {
        server react-frontend:4200;
        server react-frontend:4200;
}
upstream backends {
        server backend-api:3333;
        server backend-api:3333;
}
server {
        server_name 127.0.0.1 example.com *.example.com;
        listen 88;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://frontends;
        }
        # if required , only then use it otherwise remove it [for direct api calls]
        location /api/v1 {
                proxy_pass http://backends;
        }
}

可以根據需要添加更多選項或配置。 由於上面代碼段中的server_name _ configs,我們看到了503默認頁面。 如果需要,可以對其進行配置。 (如下面的片段)

server {
        server_name _; # This is just an invalid value which will never trigger on a real hostname.
        server_tokens off;
        listen 88;
        access_log /var/log/nginx/access.log vhost;
        return 403 "..Ops";
}

不確定,如果您在一個撰寫文件中運行所有內容,為什么需要兩個網絡。 如果需要兩個網絡,以下是 docker-compose 文件和nginx.conf的示例。

docker-compose.yaml

version: "3.7"
networks:
  corp:
    driver: bridge
  nginx-proxy:
    external:
      name: nginx-proxy

services:
  nginx-proxy:
    container_name: nginx-proxy
    image: nginx
    ports:
      - 3210:80
    networks:
      - nginx-proxy
      - corp
    volumes:
    - another-nginx.conf:/etc/nginx/conf.d/another-nginx.conf

  react-frontend:
    container_name: react-frontend
    image: httpd
    ports:
      - 3211:80
    networks:
      - nginx-proxy
      - corp
  
  backend-x:
    container_name: backend
    image: nginx
    ports:
      - 3212:80
    networks:
      - corp
  
  db-x:
    container_name: db
    image: httpd
    ports:
      - 3213:80
    networks:
      - corp

another-nginx.conf

server {
    server_name _; # This is just an invalid value which will never trigger on a real hostname.
    server_tokens off;
    listen 88;
    access_log /var/log/nginx/access.log;
    return 503;
}
    # react-frontend
upstream frontends {
    server react-frontend:80;
}
upstream backends {
    server backend:80;
}
server {
    server_name 127.0.0.1 localhost example.com *.example.com;
    listen 80;
    access_log /var/log/nginx/access.log;
    location / {
        proxy_pass http://frontends;
    }
    # if required , only then use it otherwise remove it [for direct api calls]
    location /api/v1 {
        proxy_pass http://backends;
    }
}

如果不存在,則創建nginx-proxy網絡。

docker network create nginx-proxy

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM