Dockerizing MERN APP for Production -仅请参阅:欢迎来到NGINX!“

34gzjxbg  于 2023-08-03  发布在  Nginx
关注(0)|答案(2)|浏览(111)

我对Docker和NGINX完全陌生,所以我基本上是边走边学。我试图让我的Dockerized MERN应用程序设置为生产(不是一个关键的应用程序,只是为了我的学习目的),在阅读了无数的教程后,我已经得到了我的应用程序只为开发环境。然而,正如标题所示,我想让它在一个产品类型的环境中工作(考虑在AWS中托管),所以我需要更多地优化它。
我会给你看我的Dockerfile.prod的前端:

FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx
# Copy build assets from build/ folder & place them in nginx/html
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80

字符串
我的生产NGINX配置文件:

upstream client { 
    server client:3000;
}
upstream api {
    server api:3001;
}

server {
    listen 80;
    
    location / { 
        root /usr/share/nginx/html;
        include /etc/nginx/mime.types;
        try_files $uri $uri/ /index.html;
    }
    
    location /sockjs-node {
        proxy_pass http://client;
        proxy_http_version 1.1;
        # setting 'Upgrade' allows a tunnel setup between client & proxied server http://nginx.org/en/docs/http/websocket.html
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }

    location /api {
        proxy_pass http://api;
    }
}


我的Dockerfile.prod for NGINX

FROM nginx
COPY ./prod.conf /etc/nginx/conf.d/default.conf


最后,我的docker-compose.prod.yml文件:

version: '3'
services:
  nginx:
    depends_on:
      - api
      - client
    restart: always
    build:
      dockerfile: Dockerfile.prod
      context: ./nginx
    ports:
      - "3000:80"
  api:
    build:
      dockerfile: Dockerfile
      context: ./backend
    volumes:
      - '/app/node_modules'
      - './backend:/app'
    command: npm start
  client:
    build: 
      dockerfile: Dockerfile.prod
      context: ./frontend
    volumes:
      - '/app/node_modules'
      - './frontend:/app'
    environment:
      - WDS_SOCKET_PORT=0
    tty: true
    stdin_open: true


根据我的理解,NGINX容器首先使用其www.example.com构建Dockerfile.prod,因此使用我制作的prod.conf覆盖其默认的.conf文件。然后在构建'client'容器(我的前端)并执行其各自的www.example.com时Dockerfile.prod,构建内容将被复制到NGINX的html文件夹中。所以从技术上讲,“欢迎使用NGINX”屏幕应该被覆盖了,对吗?
然而,当检查复制构建内容的位置时,情况似乎并非如此(请参阅NGINX Image /usr/share/nginx/html contents)。就其价值而言,客户端容器似乎是在NGINX容器之前构建的,至少从我看到的打印出日志的顺序来看(参见Docker Compose Output)。无论如何,似乎我做错了什么,我唯一的预感是前端www.example.com的NGINX部分Dockerfile.prodNGINX的Dockerfile.pro的执行覆盖。有人能帮帮我吗我只是在这一点上超越困惑。
目录结构:

.
├── frontend
│   ├── Dockerfile
│   ├── Dockerfile.prod
│   ├── babel.config.json
│   ├── nginx
│   │   └── default.conf
├── nginx
│   ├── Dockerfile
│   ├── Dockerfile.prod
│   ├── default.conf
│   └── prod.conf
├── docker-compose.prod.yml
├── docker-compose.yml


Docker Compose Output
NGINX Image /usr/share/nginx/html contents

oyt4ldly

oyt4ldly1#

让我们从为生产提供react应用程序开始,我推荐以下教程:https://dev.to/thexdev/dockerize-production-reactjs-3ai9
因此,您可能需要以下Dockerfile.prod

FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:stable
EXPOSE 80
COPY ./your/path/default.conf /etc/nginx/conf.d/default.conf # That file needs to be created, you can adjust the first path to yours
COPY --from=build /app/dist /usr/share/nginx/html  # Please have a look if its /app/dist or /app/build

字符串
您需要创建的default.conf文件应该如下所示:

server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}


我还建议在Dockerfile所在的目录下创建一个.dockerignore文件,内容如下:

node_modules


这样您就不会将该文件夹复制到映像中-它是由npm i命令自己创建的。
这样你就有了一个生产React应用程序。
然后你应该编辑你的docker-compose.prod.yml

version: '3'
services:
  nginx:
    image: nginx:stable
    container_name: nginx
    depends_on:
      - api
      - client
    restart: always
    volumes:
      - ./sites-enabled:/etc/nginx/conf.d
    ports:
      - "3000:80"
  api:
    container_name: api
    build:
      dockerfile: Dockerfile
      context: ./backend
    volumes:
      - '/app/node_modules'
      - './backend:/app'
    command: npm start
    expose:
      - 3001
  client:
    container_name: client
    build: 
      dockerfile: Dockerfile.prod
      context: ./frontend
    expose:
      - 80


这样你就有了一个nginx示例,它应该作为一个代理,将你的请求路由到API或客户端容器。
要做到这一点,你需要创建sites-enabled目录,你的docker-compose.prod.yml文件就在其中(这样它就可以作为卷挂载到nginx容器中)。在该文件夹中,您需要创建一个包含以下内容的<anyname>.conf文件:

server {
    listen 80;
    
    location / {
        proxy_pass http://client:80;
    }

    location /api/ {
        proxy_pass http://api:3001$request_uri;
    }
}


这将是一个生产设置。每个服务都有一个镜像和一个nginx容器,它接收请求并将其路由到相应的服务。你不应该在客户端容器中使用nginx示例来将请求路由到API容器,这是不好的做法。您的客户端镜像中的nginx仅用于使此容器成为独立容器,它不应与外部交互。

hgncfbus

hgncfbus2#

我想先说@Geilmaker的答案是正确的答案。不过,我也想给予额外的细节,以防任何人有类似的设置。
正如Geilmaker指出的,主要问题是我实际上有两个nginx镜像(一个包含React部分的构建内容)。第一个是在Dockerfile.prod为前端创建的,另一个是在docker-compose.prod.yml执行期间创建的:

nginx:
   depends_on:
     - api
     - client
   restart: always
   build:
     dockerfile: Dockerfile.prod
     context: ./nginx
   ports:
     - "3000:80"

字符串
但我没有暴露nginx镜像的构建内容。相反,我公开了一个只有默认配置的版本。此外,由于前端的Dockerfile.prod只运行了'npm run build',因此React应用程序没有被提供服务& prod.conf被配置为默认为任何默认位置的index.html-此时只有欢迎页面。
因此,我必须做的是依靠为Dockerfile.prod创建的nginx代理来进行前端,并将用于prod.conf的一些配置添加到驻留在Frontend文件夹中的default.conf中(例如Express API的代理)。
结果如下:
前端/Dockerfile.prod

FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx
# Copy build assets from build/ folder & place them in nginx/html
COPY --from=build /app/build /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80


frontend/nginx/default.conf

upstream client { 
    server client:3000;
}
upstream api {
    server api:3001;
}

server {

    listen 80;
    
    location / { 
        root /usr/share/nginx/html;
        include /etc/nginx/mime.types;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    location /sockjs-node {
        proxy_pass http://client;
        proxy_http_version 1.1;
        # setting 'Upgrade' allows a tunnel setup between client & proxied server http://nginx.org/en/docs/http/websocket.html
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }

    location /api {
        proxy_pass http://api;
    }
}


docker-compose.prod.yml看起来像:

version: '3'
services:
  api:
    build:
      dockerfile: Dockerfile
      context: ./backend
    volumes:
      - '/app/node_modules'
      - './backend:/app'
    command: npm start
  client:
    build: 
      dockerfile: Dockerfile.prod
      context: ./frontend
    volumes:
      - '/app/node_modules'
      - './frontend:/app'
    environment:
      - WDS_SOCKET_PORT=0
    ports:
      - '3000:80'
    tty: true
    stdin_open: true


请注意'nginx'定义的删除&'client'定义中出现的端口Map。
执行时
docker-compose -f docker-compose.prod.yml up
我终于可以看到我的React应用程序了。再次感谢@Geilmaker🙏

相关问题