django - Django+Vuejs+Gunicorn+Nginx 的 Docker 配置导致 502 Bad Gateway
问题描述
语境
我正在开发一个必须为每个客户部署一次的应用程序(它不是我所有客户都会使用的多租户应用程序)。
最终,它将通过 RDS(DB)、S3(存储)和 ECS(容器服务)等服务部署在 AWS 上。
如果可以帮助回答我,您可以通过下图了解我的想象:
我的应用程序在没有 Docker 的情况下在本地运行良好。
但是我想在尝试在 AWS 上部署它之前在本地使用它的 dockerized 版本,因为我希望能够在尝试在 AWS 上执行它之前使用 Docker 在本地运行它(...)。
由于我所有的客户都有自己的实例(有时 customer1 可能有与 customer2 不同的版本),所以我考虑了一个包含所需一切的容器,如图所示:Django 应用程序、Vue 构建文件、gunicorn 和 nginx。
问题 1:这样做是个好主意吗?或者我应该使用具有多个服务(后端(django)和 nginx)的 docker-compose 东西。它会导致多个容器吗?
问题
为此,我做了以下配置:
Dockerfile:
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;
}
}
问题 2:我使用 gunicorn.sock 的东西。当我不这样做时,使用 0.0.0.0:8000 之类的东西,我通常甚至无法到达 nginx,它直接到达 gunicorn,不知道为什么......
我的 Docker-compose 是这样的:
version: "3.8"
services:
web:
network_mode: host
env_file:
- local.env
build: .
ports:
- "8000:8000"
我的 local.env 文件有 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
502 Bad Gateway
但是,在我的浏览器中尝试访问 127.0.0.1 时,除了来自 nginx 之外,我什么也得不到:
我不知道为什么。我想这一定是我的某种困惑..
问题 3:您能帮我弄清楚我的配置中的错误是什么,或者给我一些关于如何配置这种部署的指导(这里是本地的,而不是 AWS),或者关于如何找到错误来源的一些指导?
提前感谢您的阅读。我在那些 devOps 方面真的很糟糕,任何帮助都将不胜感激。
解决方案
您不想将所有内容都运行到单个容器中。一个容器应该只做一件事。你有几个选择。每个模块(nginx、app 等)运行一个容器,并将它们分组到一个 ECS 任务中。该任务是 ECS 中的一个部署和扩展单元,您可以使用 ECS 服务对其进行控制。因此,您可以为每位客户提供 1 个 ECS 服务,并从支持它的 1 个任务开始。如果您需要更多“功能”,ECS 会将您的单个任务扩展到 2 个任务(将所有容器加倍)。等等。
第二种方法(架构更好,但可能更昂贵)是每个容器都有一个任务,因此总共有 3 个独立的任务包含在 3 个独立的服务中。然后,每个服务都可以自行扩展(例如,如果您需要更多 NGINX 容量,则只有 NGINX 任务将在 NGINX ECS 服务中扩展。如果采用这种方法,您将需要为每个客户提供 3 个服务(加上 db)。
这是我用于这些实验的演示应用程序,它模仿了您的应用程序。我docker-compose
的这个应用程序的文件如下所示:
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之类的工具,或者,如果您想坚持使用docker compose
,您可以利用新的docker compose 与 ECS 的集成以及docker compose up
您的应用程序直接进入 ECS。
这两种解决方案都将实现我描述的第二种模式(每个任务 1 个容器)。如果您想要“任务中的所有容器”模式,您应该制作一个原生 CloudFormation 模板。
推荐阅读
- linux - 如何在 MacOS(或 Linux)的笔记本电脑睡眠模式下调查哪个进程导致唤醒?
- node.js - Angular http put请求不发送标头
- javascript - 如何使用 React 列出表格中的元素?
- ios - 快速转换为泛型类类型
- mysql - 在本地 docker 容器上连接 MySQL 时出错:ERROR 2002 (HY000): Can't connect to MySQL server on '127.0.0.1' (115)
- python - 使用“root.protocol”将参数传递给使用 Tkinter 中的默认退出窗口按钮分配的函数的问题
- c++ - Qt QSlider 更改小部件时的奇怪行为
- sql - 计算用多个空格分隔的单词的频率
- javascript - 反应:将 Child1 状态值传递给 Parent 并从 parent 传回 Child2
- python - 从 minidom/getElementsByTagName 到 lxml/xpath