Конфигурация Docker для Django+Vuejs+Gunicorn+Nginx приводит к 502 Bad Gateway
Context
Я разрабатываю приложение, которое должно быть развернуто один раз для каждого клиента (это не многопользовательское приложение, которое будут использовать все мои клиенты).
В конечном итоге он будет развернут на AWS с такими сервисами, как RDS (DB), S3 (хранение) и ECS (контейнерный сервис).
Вы можете увидеть, как я себе это представляю с помощью изображения ниже, если это может помочь ответить мне:
Мое приложение хорошо работает локально без Docker.
Но я хочу использовать докеризованную версию локально, прежде чем пытаться развернуть ее на AWS, потому что я хочу иметь возможность запустить ее локально с помощью Docker, прежде чем пытаться сделать это на AWS (...).
Поскольку все мои клиенты будут иметь свой собственный экземпляр (и клиент1 может иметь другую версию, чем клиент2 в определенное время), я подумал об одном контейнере со всем необходимым, как вы можете видеть на изображении: приложение Django, собранные файлы Vue, gunicorn и nginx.
Вопрос 1 : Хорошая ли идея сделать это таким образом? Или лучше использовать docker-compose с несколькими сервисами (backend (django) & nginx). Приведет ли это к созданию нескольких контейнеров?
Проблема
Для этого я выполнил следующую конфигурацию :
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
от nginx при попытке обратиться к 127.0.0.1 в браузере:и я не могу понять, почему. Наверное, это какая-то путаница с моей стороны...
Вопрос 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 integration with ECS иdocker compose up
ваше приложение в ECS напрямую.Оба эти решения реализуют второй описанный мной шаблон (1 контейнер на задачу). Если вам нужен шаблон "все контейнеры в задаче", вам следует создать собственный шаблон CloudFormation.