简体   繁体   English

Dockerized NGINX 配置 ReactJS App 在 Azure 上运行(容器实例)

[英]Dockerized NGINX Configuration with ReactJS App Running on Azure (Container Instances)

I have a fairly standard ReactJS frontend (using port 3000) app which is served by a NodeJS backend server (using port 5000).我有一个相当标准的 ReactJS 前端(使用端口 3000)应用程序,它由 NodeJS 后端服务器(使用端口 5000)提供服务。 Both apps are Dockerized and I have configured NGINX in order to proxy requests from the frontend to and from the server.这两个应用程序都是 Dockerized 的,我已经配置了 NGINX 以便代理从前端到服务器的请求。

Dockerfile for front end (with NGINX "baked in"): Dockerfile 用于前端(带有 NGINX“烘焙”):

FROM node:lts-alpine as build

WORKDIR /app

COPY ./package.json ./
COPY ./package-lock.json ./

RUN npm install
COPY . .
RUN npm run build

FROM nginx

EXPOSE 3000
EXPOSE 443
EXPOSE 80

COPY ./cert/app.crt /etc/nginx/
COPY ./cert/app.key /etc/nginx/

ENV HTTPS=true
ENV SSL_CRT_FILE=/etc/nginx/app.crt
ENV SSL_KEY_FILE=/etc/nginx/app.key

RUN rm /etc/nginx/conf.d/default.conf

COPY ./default.conf /etc/nginx/nginx.conf
COPY --from=build /app/build/ /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]

Dockerfile for server:服务器 Dockerfile:

FROM node:lts-alpine as build

WORKDIR /app
EXPOSE 5000

ENV NODE_TLS_REJECT_UNAUTHORIZED=0
ENV DANGEROUSLY_DISABLE_HOST_CHECK=true
ENV NODE_CONFIG_DIR=./config/

COPY ./package.json ./
COPY ./package-lock.json ./

RUN npm install

COPY . .
CMD [ "npm", "start" ]

The docker-compose.yml for this setup is此设置的 docker-compose.yml 是

version: '3.8'
services:
  client:
    container_name: client
    depends_on:
      - server
    stdin_open: true
    environment:
      - CHOKIDAR_USEPOLLING=true
      - HTTPS=true
      - SSL_CRT_FILE=/etc/nginx/app.crt
      - SSL_KEY_FILE=/etc/nginx/app.key
    build:
      dockerfile: Dockerfile
      context: ./client
    expose:
      - "8000"
      - "3000"
    ports:
      - "3000:443"
      - "8000:80"
    volumes:
      - ./client:/app
      - /app/node_modules
      - /etc/nginx
    networks:
      - internal-network

  server:
    container_name: server
    build:
      dockerfile: Dockerfile
      context: "./server"
    expose:
      - "5000"
    ports:
      - "5000:5000"
    volumes:
      - /app/node_modules
      - ./server:/app
    networks:
      - internal-network

networks:
  internal-network:
    driver: bridge
    

And crucially, the NGINX default.conf is至关重要的是,NGINX default.conf 是

worker_processes auto;

events {
  worker_connections 1024;
}

pid /var/run/nginx.pid;

http {

    include mime.types;

    upstream loadbalancer {
        server server:5000 weight=3;
    }

    server {
        listen 80 default_server;
        listen [::]:80 default_server;

        server_name _;

        port_in_redirect off;
        absolute_redirect off;

        return 301 https://$host$request_uri;
    }

    server {
        listen [::]:443 ssl;
        listen 443 ssl;

        server_name example.app* example.co* example.uksouth.azurecontainer.io* localhost*;
        error_page 497 https://$host:$server_port$request_uri;

        error_log /var/log/nginx/client-proxy-error.log;
        access_log /var/log/nginx/client-proxy-access.log;

        ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers                ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
        ssl_prefer_server_ciphers  on;
        ssl_session_cache          shared:SSL:10m;
        ssl_session_timeout        24h;

        keepalive_timeout 300;
        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';

        ssl_certificate     /etc/nginx/app.crt;
        ssl_certificate_key /etc/nginx/app.key;

        root /usr/share/nginx/html;
        index index.html index.htm index.nginx-debian.html;

         location / {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            try_files $uri $uri/ /index.html;
        }
        
        location /tours {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            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 $scheme;
            proxy_pass http://loadbalancer;
        }
    }
}

with this configuration I have two problems:使用此配置我有两个问题:

  1. By running docker-compose up -d , this setup builds and deploys two Docker containers locally.通过运行docker-compose up -d ,此设置在本地构建和部署两个 Docker 容器。 When I use https://localhost:3000/id this works and the data is retrieved and shown in browser correctly - when I type http://localhost:3000/id this gets redirected to http://localhost:443/id and this does not work.当我使用 https://localhost:3000/id 时,这有效并且数据被检索并正确显示在浏览器中 - 当我键入 http://localhost:3000/id 时,它被重定向到 http://localhost:443/id这是行不通的。 I have attempted to use NGINX commands port_in_redirect off; absolute_redirect off;我试图使用 NGINX 命令port_in_redirect off; absolute_redirect off; port_in_redirect off; absolute_redirect off; but this has not helped.但这没有帮助。 How can I make sure that the redirect does not edit the port number?如何确保重定向不会编辑端口号? (this is likely not going to be an issue in production where the port numbers are not used). (这在不使用端口号的生产中可能不会成为问题)。

  2. The bigger problem : the deployment to Azure is done using a docker context and running docker-compose -f./docker-compose-azure.yml up .更大的问题:部署到 Azure 是使用docker context并运行docker-compose -f./docker-compose-azure.yml up This runs and creates two Docker containers and a side-car process.这将运行并创建两个 Docker 容器和一个 side-car 进程。 The docker-compose-azure.yml file is docker-compose-azure.yml 文件是

    version: '3.8' services:版本:'3.8' 服务:

     client: image: dev.azurecr.io/example-client depends_on: - server stdin_open: true environment: - CHOKIDAR_USEPOLLING=true - HTTPS=true - SSL_CRT_FILE=/etc/nginx/app.crt - SSL_KEY_FILE=/etc/nginx/app.key restart: unless-stopped domainname: "example-dev" expose: - "3000" ports: - target: 3000 #published: 3000 protocol: tcp mode: host.networks: - internal.network server: image: dev.azurecr.io/example-server restart: unless-stopped ports: - "5000:5000".networks: - internal.network.networks: internal.network: driver: bridge

If I don't use HTTPS and a simple reverse proxy - the two issues outline above go away.如果我不使用 HTTPS 和一个简单的反向代理 - 上面概述的两个问题 go 就会消失。 But with the configuration above, calls to the Azure FQDN/URL fail;但是使用上面的配置,调用 Azure FQDN/URL 失败; HTTPS requests timing out "ERR_CONNECTION_TIMED_OUT", and for HTTP, the site could not be found. HTTPS 请求超时“ERR_CONNECTION_TIMED_OUT”,对于 HTTP,找不到站点。 What am I doing wrong here?我在这里做错了什么?

Thanks for your time.谢谢你的时间。

I think you need to check/update Nginx configuration file properly and also make sure SSL certificate files are available我认为您需要正确检查/更新 Nginx 配置文件,并确保 SSL 证书文件可用

# http block would be
server {
        listen 80 default_server;
        return 301 https://$server_name$request_uri;
}

and in https server block, you need to update location block在 https 服务器块中,您需要更新location

location /tours {
        proxy_pass http://server:5000;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
}
        
location / {
        try_files $uri $uri/ /index.html;
}

Updated更新

Your Nginx config file would be您的 Nginx 配置文件将是

worker_processes auto;

events {
  worker_connections 1024;
}

pid /var/run/nginx.pid;

http {

    include mime.types;

    server {
        listen [::]:443 ssl;
        listen 443 ssl;

        server_name my-redirected-domain.com my-azure-domain.io localhost;

        access_log /var/log/nginx/client-proxy.log;

        ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers                ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
        ssl_prefer_server_ciphers  on;
        ssl_session_cache          shared:SSL:10m;
        ssl_session_timeout        24h;

        keepalive_timeout 300;
        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';

        ssl_certificate     /etc/nginx/viewform.app.crt;
        ssl_certificate_key /etc/nginx/viewform.app.key;

        root /usr/share/nginx/html;
        index index.html index.htm index.nginx-debian.html;

        location / {
            try_files $uri $uri/ /index.html;
        }
        
        location /tours {
            proxy_pass http://server:5000;
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

    server {
        listen 80 default_server;
        return 301 https://$server_name$request_uri;
    }
}

Use port 443 everywhere to avoid any confusion with port remapping (that can be an advance setup):在任何地方都使用端口 443 以避免与端口重新映射(可以是高级设置)混淆:

1.) Define client container to be running on port 443 : 1.) 定义要在端口443上运行的client容器:

version: '3.8'
services:
  client:
...
    ports:
      - port: 443
        protocol: TCP

2.) Define Nginx to be running on the port 443 with proper TLS setup as you have in your updated nginx.conf 2.) 将 Nginx 定义为在端口443上运行,并使用正确的 TLS 设置,就像您在更新的 nginx.conf 中所做的那样

Deploy and open https://<public IP> (you will very likely need to add sec. exception in the browser).部署并打开https://<public IP> (您很可能需要在浏览器中添加 sec.exception)。

BTW: Azure has quite good article about Nginx with TLS (but more advance setup is used): https://learn.microsoft.com/en-us/azure/container-instances/container-instances-container-group-ssl顺便说一句:Azure 有关于 Nginx 和 TLS 的相当不错的文章(但使用了更高级的设置): https://learn.microsoft.com/en-us/azure/container-instances/container-instances-container-group-ssl

IMHO better redirect from http to https is:恕我直言,最好从 http 重定向到 https 是:

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

I think Jan Garaj 's answer has touched upon all the important bits.我认为Jan Garaj的回答已经触及了所有重要的部分。 Here is my take, trying to give a targeted answer.这是我的看法,试图给出有针对性的答案。

HTTP to HTTPS redirect HTTP 到 HTTPS 重定向

Currently the return 301 statement is using the $host variable that only holds the Hostname and not the port information.目前return 301语句使用的$host变量只包含主机名而不是端口信息。 To capture both, you can use the $http_host variable instead.要捕获两者,您可以改用$http_host变量。 source 来源

server {
    listen         [::]:80;

    #//307 to preserve POST data
    return 307 https://$http_host$request_uri; 
}

Problems with the Azure config Azure 配置的问题

In the Azure config, you have this bit:在 Azure 配置中,你有这个位:

    ports:
      - target: 3000
        #published: 3000
        protocol: tcp 
        mode: host

which identifies 3000 as the internal client port which listens to the requests.它将 3000 标识为侦听请求的内部客户端端口。 But you have to remember that you have a NGINX proxy inside that only listens to ports 80 or 443 (the server blocks in Nginx config).但是您必须记住,您内部有一个 NGINX 代理,它只侦听端口 80 或 443(Nginx 配置中的server块)。 So this is the reason you get the ERR_CONNECTION_TIMED_OUT error because the requests are sent to port 3000 where nothing is listening.所以这就是您收到 ERR_CONNECTION_TIMED_OUT 错误的原因,因为请求被发送到没有任何监听的端口 3000。

As you want to do a HTTPS deployment, you can set this to 443 and the Nginx will take care of the request.当您想进行 HTTPS 部署时,您可以将其设置为 443,然后 Nginx 将处理请求。

enabling HTTP redirect on Azure在 Azure 上启用 HTTP 重定向

The final bit is to configure the Azure deployment such that when a HTTP request is made to your URL, it should get redirected to the HTTPS counterpart.最后一点是配置 Azure 部署,这样当向您的 URL 发出 HTTP 请求时,它应该被重定向到对应的 HTTPS。 We already have the NGINX redirect block for port 80.我们已经有了 80 端口的 NGINX 重定向块。

BUT, it will not help.但是,这无济于事。 As we specify the target to be 443 inside the container, the HTTP request will try to hit 443 and get refused.由于我们将目标指定为容器内的 443,因此 HTTP 请求将尝试命中 443 并被拒绝。 This article also mentions the same towards the end This article最后也提到了同样的事情

Use your browser to navigate to the public IP address of the container group.使用浏览器导航到容器组的公共地址 IP。 The IP address shown in this example is 52.157.22.76, so the URL is https://52.157.22.76 .此示例中显示的 IP 地址为 52.157.22.76,因此 URL 为https://52.157.22.76 You must use HTTPS to see the running application, because of the Nginx server configuration.由于 Nginx 服务器配置,您必须使用 HTTPS 才能查看正在运行的应用程序。 Attempts to connect over HTTP fail.尝试通过 HTTP 连接失败。

This could be solved if it were possible to add another port to Azure config, the port 80.如果可以将另一个端口添加到 Azure 配置,即端口 80,则可以解决此问题。

     ports:
      - port: 443
        protocol: TCP
      - port: 80
        protocol: TCP

I am not sure if Azure allows this, but if it does then thats the final solution.我不确定 Azure 是否允许这样做,但如果允许,那就是最终解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 2 个容器实例(ECS 实例)上运行 ECS 服务 - Running ECS service on 2 container instances (ECS instances) 在 Azure 容器实例上运行 Superset - Running Superset on Azure container Instance 在 Azure Devops 中获取正在运行的 devops 管道实例 - Get running instances of devops pipeline in Azure Devops Azure 容器实例 - 允许出站连接到 inte.net - Azure Container Instances - allow outbound connection to internet Azure 容器实例无法访问 docker 图片 - Azure Container Instances cannot access to docker images Dockerized ReactJS & Express on Elastic Beanstalk - Dockerized ReactJS & Express on Elastic Beanstalk 从 Azure Container Registry 拉取镜像到 Azure Container Instances 时如何使用 SystemAssigned identity? - How can I use a SystemAssigned identity when pulling an image from Azure Container Registry into Azure Container Instances? 如何使用 docker-compose.yaml 和 dockerized nginx 服务器将 fastapi 应用程序部署到 EC2 - How to deploy fastapi app to EC2 with docker-compose.yaml and dockerized nginx server 在 Azure web 应用程序中运行 docker 容器:未响应端口上的 HTTP ping - Running a docker container in Azure web app: didn't respond to HTTP pings on port Azure 容器实例说它运行正常但 URL 没有在浏览器中显示我的应用程序 - Azure Container Instance Says its running Okay but the URL doesnt show my app in the browser
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM