Docker Django+Vuejs+Gunicorn+Nginx 的配置导致 502 Bad Gateway

Docker configuration for Django+Vuejs+Gunicorn+Nginx lead to 502 Bad Gateway

上下文

我正在开发一个必须为每个客户部署一次的应用程序(它不是我的所有客户都会使用的多租户应用程序)。

最终,它将部署在 AWS 上,提供 RDS(数据库)、S3(存储)和 ECS(容器服务)等服务。

你可以通过下图看看我想象的样子,如果能帮到我回答的话:

我的应用程序在没有 Docker 的情况下在本地运行良好。

但我想在尝试将其部署到 AWS 之前在本地使用它的 dockerized 版本,因为我希望能够在尝试将其部署到 Docker 之前在本地使用 运行 它AWS (...).

因为我所有的客户都有自己的实例(并且客户 1 有时可能有与客户 2 不同的版本),我想到了一个包含所有需要的容器,如图所示:Django 应用程序, Vue 内置文件, gunicorn 和 nginx.

Question 1 : Is it a good idea to do it like this ? Or should I use a docker-compose thing with multiple services (backend (django) & nginx). Would it lead to multiple containers ?

问题

为此,我进行了以下配置:

Docker文件:

FROM node:lts-alpine as build-frontend-stage
WORKDIR /frontend
COPY ./frontend/package*.json /frontend/
RUN npm install
COPY ./frontend .
RUN npm run build

FROM python:3.8.10-slim as build-backend-stage
RUN apt-get update
RUN apt-get install --yes --no-install-recommends \
    g++ \
    libpq-dev \
    nginx
WORKDIR /backend
RUN pip install --upgrade pip
COPY ./backend/requirements.txt /backend
RUN pip install -r requirements.txt
COPY ./backend .
COPY --from=build-frontend-stage /frontend/dist/static /backend/static
COPY --from=build-frontend-stage /frontend/dist/index.html /backend/static
COPY ./docker-entrypoint.sh /backend
RUN ["chmod", "+x", "/backend/docker-entrypoint.sh"]
RUN rm -rf /etc/nginx/sites-available/default
RUN rm -rf /etc/nginx/sites-enabled/default
COPY ./nginx/nginx.config /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/nginx.config /etc/nginx/sites-enabled

ENTRYPOINT ["bash", "/backend/docker-entrypoint.sh"]

这导致容器具有以下结构:

ls -al

drwxr-xr-x 1 root root 4096 Oct 21 14:21 .
drwxr-xr-x 1 root root 4096 Oct 22 08:56 ..
drwxrwxr-x 1 root root 4096 Oct 22 08:56 backend
-rwxrwxr-x 1 root root  552 Oct 21 14:20 docker-entrypoint.sh
-rwxrwxr-x 1 root root  638 Oct  5 12:37 manage.py
-rw-rw-r-- 1 root root  115 Oct  5 10:05 requirements.txt
drwxr-xr-x 1 root root 4096 Oct 21 14:09 static

静态文件夹中有:

ls -al static

drwxr-xr-x 1 root root 4096 Oct 21 14:09 .
drwxr-xr-x 1 root root 4096 Oct 21 14:21 ..
drwxr-xr-x 2 root root 4096 Oct 18 14:47 css
drwxr-xr-x 2 root root 4096 Oct 18 14:47 fonts
-rw-r--r-- 1 root root  773 Oct 18 14:47 index.html
drwxr-xr-x 2 root root 4096 Oct 18 14:47 js

我有一个这样的 nginx 配置:

upstream app_server {
    server unix:gunicorn.sock fail_timeout=0;
}

server {
    listen 80;

    root /backend/static;
    index index.html;
    include /etc/nginx/mime.types;

    location / {
        proxy_pass http://app_server;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        
    }
}

Question 2 : I use a gunicorn.sock thing. When I do not, and use something like 0.0.0.0:8000 or whatever, I usually can't even reach nginx, it reaches gunicorn directly, don't know why...

我的Docker-compose是这样的:

version: "3.8"
services:
  web:
    network_mode: host
    env_file:
      - local.env
    build: .
    ports:
      - "8000:8000"

我的 local.env 文件有环境变量,例如:

DB_HOST=127.0.0.1
DB_NAME=db_name
DB_PASSWORD=db_password
DB_PORT=5432
DB_USER=db_user
SECRET_KEY=some_django_secret_key

最后我的切入点是:

#!/bin/bash

DJANGO_SETTINGS_MODULE=backend.settings.production
DJANGO_WSGI_MODULE=backend.wsgi
SOCK_FILE=gunicorn.sock

export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE

echo Migrating...
python manage.py migrate --no-input


service nginx start
service nginx status
echo Sarting gunicorn...
gunicorn ${DJANGO_WSGI_MODULE}:application \
    --workers 3 \
    --bind=unix:$SOCK_FILE \
    --log-file=-

首先,我这样做:

docker-compose -f ./docker-compose-dev.yml up

正确构建所有内容并启动 gunicorn :

web_1  | [2021-10-22 09:06:50 +0000] [53] [INFO] Starting gunicorn 20.0.4
web_1  | [2021-10-22 09:06:50 +0000] [53] [INFO] Listening at: unix:gunicorn.sock (53)
web_1  | [2021-10-22 09:06:50 +0000] [53] [INFO] Using worker: sync
web_1  | [2021-10-22 09:06:50 +0000] [55] [INFO] Booting worker with pid: 55
web_1  | [2021-10-22 09:06:50 +0000] [56] [INFO] Booting worker with pid: 56
web_1  | [2021-10-22 09:06:50 +0000] [57] [INFO] Booting worker with pid: 57

但是,当我尝试在浏览器中访问 127.0.0.1 时,除了 502 Bad Gateway 之外,我无法从 nginx 获得任何东西:

我不明白为什么。我想这一定是我的某种困惑..

Question 3 : Can you help me figure out what is the error in my configuration or giving me some guidance on how to configure such a deployment (for local here, not AWS), or some guidance on how to find the source of the error ?

预先感谢您的阅读。我真的不擅长那些 devOps 的事情,我们将不胜感激。

您不想 运行 将所有内容都放入一个容器中。一个容器应该只做一件事。你有几个选择。 运行 每个模块(nginx、应用程序等)一个容器,并将它们分组到一个 ECS 任务中。该任务是 ECS 中的一个部署和扩展单元,您可以使用 ECS 服务对其进行控制。因此,您可以为每个客户提供 1 个 ECS 服务,并从支持它的 1 个任务开始。如果您需要更多“功能”,ECS 会将您的单个任务扩展为 2 个任务(将所有容器加倍)。等等。

第二种方法(在架构上更好但可能更昂贵)是每个容器都有一个任务,因此总共有 3 个独立任务包装在 3 个独立服务中。然后每个服务都可以自行扩展(例如,如果您需要 NGINX 的更多容量,则只有 NGINX 任务将在 NGINX ECS 服务中扩展。如果您采用这种方法,您将需要为每个客户提供 3 个服务(加上数据库)。

This is a demo application I am using for these experiments that kind of mimics your app. My docker-compose file for this application 看起来像这样:

version: "3.0"
services:
  yelb-ui:
    image: mreferre/yelb-ui:0.7
    depends_on:
      - yelb-appserver
    ports:
      - 80:80
    networks:
      - yelb-network

  yelb-appserver:
    image: mreferre/yelb-appserver:0.5
    depends_on:
      - redis-server
      - yelb-db
    networks:
      - yelb-network

  redis-server:
    image: redis:4.0.2
    networks:
      - yelb-network
    # uncomment the following lines if you want to persist redis data across deployments
    #volumes:
    # - redisdata:/data

  yelb-db:
    image: mreferre/yelb-db:0.5
    networks:
      - yelb-network
    # uncomment the following lines if you want to persist postgres data across deployments
    #volumes:
    #  - postgresqldata:/var/lib/postgresql/data

networks:
  yelb-network:
    driver: bridge # a user defined bridge is required; the default bridge network doesn't support name resolution

这就是您的应用程序在本地 运行 的方式。如果你想 运行 它在 ECS 上,你可以使用像 Copilot or, if you want to stick using docker compose you could leverage the new docker compose integration with ECS 这样的工具和 docker compose up 你的应用程序直接进入 ECS。

这两种解决方案都将实现我描述的第二种模式(每个任务 1 个容器)。如果您想要“一个任务中的所有容器”模式,您应该制作一个本地 CloudFormation 模板。