启动一个可用于生产环境的 Docker 化 Django 项目
在 Docker 容器内构建可用于生产环境的 Django 应用对开发者来说非常有用。它最大限度地减少了设置和部署的麻烦,使开发者能够专注于真正重要的部分,例如开发和业务逻辑。
目录
先决条件
本指南假定您已熟悉以下技术:
- 中级 Django
- 初级到中级 Docker
- 熟悉 Postgres、Celery、Redis 和 Nginx
介绍
本指南旨在帮助您启动和组织 Django 项目,使其能够在不同的环境中运行,主要是开发和生产环境。您可以参考此模板,根据自身需求进行修改,最终将其部署到您选择的云服务提供商,例如 AWS、Azure 或 Digital Ocean 等。
注意:如果您在学习本教程的过程中遇到任何问题,可以查看 GitHub 仓库中的代码。
项目配置
首先,在 GitHub 上创建一个仓库。使用 PythonREADME文件和模板初始化该仓库。.gitignore
现在,在你的电脑上,打开终端并运行以下命令来设置并打开你的项目。
mkdir django-docker-template
cd django-docker-template
git clone <link-to-repo> .
code .
在项目根目录中,创建一个名为的文件requirements.txt
touch requirements.txt
并添加以下依赖项:
celery==5.2.7
Django==4.1.2
gunicorn==20.1.0
psycopg2-binary==2.9.5
python-decouple==3.6
redis==4.3.4
然后,创建Dockerfile
touch Dockerfile
并添加以下代码片段:
FROM python:3.10.2-slim-bullseye
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /code
COPY ./requirements.txt .
RUN pip install -r requirements.txt
COPY . .
然后,创建一个docker-compose.yml文件:
touch docker-compose.yml
web并在其中添加一项服务:
version: "3.9"
services:
web:
build: .
volumes:
- .:/code
ports:
- 8000:8000
最后,创建一个.dockerignore文件,以便 Docker 忽略某些文件,从而加快镜像的构建过程。
touch .dockerignore
其中添加以下内容:
.venv
.git
.gitignore
很好,运行以下命令构建镜像:
docker-compose build
这需要一些时间。现在您可以使用此镜像创建 Django 项目。
docker-compose run --rm web django-admin startproject config .
针对不同环境的拆分设置
务必考虑项目运行的不同环境/模式:通常包括开发环境和生产环境。当然,您也可以将类似的逻辑应用于其他可能需要包含的环境。
您可以拆分设置,以指定项目运行的环境,类似于下面所示的示例:
config
│
└───settings
│ │ __init__.py
│ │ base.py
│ │ development.py
│ │ production.py
base.py这将包含与环境无关的通用设置。因此,请将settings.pyDjango 默认创建的所有内容复制到此处settings/base.py,然后删除settings.py不再需要的内容。
然后,base.py在两个环境中导入。环境特定的设置稍后会更新。
# import this in development.py, production.py
from .base import *
环境变量
使用环境变量可以描述不同的环境。`environment`python decouple是最常用的软件包之一,它可以将设置与源代码严格分离。该软件包已在项目根目录中添加,因此只需在项目根目录中requirements.txt创建一个文件即可:.env
touch .env
并添加以下变量:
SECRET_KEY=
ALLOWED_HOSTS=.localhost, .herokuapp.com, .0.0.0.0
DEBUG=True
DJANGO_SETTINGS_MODULE=config.settings.development
请据此更新您的设置:
# base.py
from decouple import config, Csv
SECRET_KEY = config("SECRET_KEY")
DEBUG = config("DEBUG", default=False, cast=bool)
ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=Csv())
DJANGO_SETTINGS_MODULE告诉 Django 要使用哪个设置。通过在环境变量中提供其值,Djangomanage.py可以自动为不同的环境使用相应的设置。因此,请manage.py按如下方式更新:
# manage.py
from decouple import config
os.environ.setdefault("DJANGO_SETTINGS_MODULE", config("DJANGO_SETTINGS_MODULE"))
同时更新文件web中的服务docker-compose.yml,使其从文件中读取环境变量.env:
version: "3.9"
services:
web:
build: .
volumes:
- .:/code
env_file:
- ./.env
ports:
- 8000:8000
那么,有哪些可能的特定环境设置呢?以下列举一些:
1)电子邮件
您可以在开发模式下使用控制台后端将电子邮件写入标准输出。
# development.py
from .base import *
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
在生产模式下,使用类似 SendGrid、Mailgun 等的 SMTP 后端服务。
# production.py
from .base import *
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "'smtp.mailgun.org'"
EMAIL_PORT = 587
EMAIL_HOST_USER = config("EMAIL_USER")
EMAIL_HOST_PASSWORD = config("EMAIL_PASSWORD")
EMAIL_USE_TLS = True
2)媒体和静态文件
在生产环境中,您可能需要使用 AWS S3 等服务来提供静态文件和媒体文件。在这种情况下,拥有多种设置就显得非常有用。
# development.py
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "../", "mediafiles")
STATIC_URL = "static/"
STATIC_ROOT = os.path.join(BASE_DIR, "../", "staticfiles")
然后,您可以添加与 AWS 相关的配置。production.py
3)缓存
理想情况下,开发过程中不需要缓存网站,因此可以在production.py文件中单独添加缓存服务器,例如 Redis。
# production.py
# Redis Cache
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": config("REDIS_BACKEND"),
},
}
此外,您还可以为您的环境单独添加应用程序、中间件等。
Postgres 配置
要配置Postgres,首先需要向 docker-compose.yml 文件中添加一个新服务:
version: "3.9"
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
env_file:
- ./.env
ports:
- 8000:8000
depends_on:
- db
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}
volumes:
postgres_data:
接下来,更新.env文件以包含与数据库相关的变量:
# Database
DB_NAME=
DB_USERNAME=
DB_PASSWORD=
DB_HOSTNAME=db
DB_PORT=5432
最后,更新设置,使用 Postgres RDBMS 而不是 Django 默认使用的 SQLite 引擎。
# base.py
# Remove the sqlite engine and add this
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": config("DB_NAME"),
"USER": config("DB_USERNAME"),
"PASSWORD": config("DB_PASSWORD"),
"HOST": config("DB_HOSTNAME"),
"PORT": config("DB_PORT", cast=int),
}
}
注意:您可能需要为开发环境和生产环境使用不同的数据库。如果是这种情况,您可以移除该DATABASES设置base.py,并为开发环境和生产环境添加不同的数据库。
太好了!现在,重新构建容器,以确保目前为止的所有功能都能正常运行。
docker-compose build
此外,请确保已应用迁移:
docker-compose run --rm web python manage.py migrate
在启动 Django 之前,请确保 Postgres 系统运行正常。
通常情况下,在 Docker 中使用 Postgres 和 Django 时,webDjango 服务会尝试连接db尚未db准备好接受连接的服务器。为了解决这个问题,您可以创建一个简短的 bash 脚本,并在 Docker 的ENTRYPOINT命令中使用它。
在项目根目录中创建一个名为的文件entrypoint.sh
touch entrypoint.sh
添加以下脚本以监听 Postgres 数据库端口,直到它准备好接受连接,然后应用迁移并收集静态文件。
#!/bin/sh
echo 'Waiting for postgres...'
while ! nc -z $DB_HOSTNAME $DB_PORT; do
sleep 0.1
done
echo 'PostgreSQL started'
echo 'Running migrations...'
python manage.py migrate
echo 'Collecting static files...'
python manage.py collectstatic --no-input
exec "$@"
更新本地文件权限
chmod +x entrypoint.sh
要使用此脚本,您需要Netcat在镜像中安装相应的工具。因此,请更新镜像Dockerfile以安装此网络实用程序,并将此 bash 脚本用作 Docker 入口点命令。
FROM python:3.10.2-slim-bullseye
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /code
COPY ./requirements.txt .
RUN apt-get update -y && \
apt-get install -y netcat && \
pip install --upgrade pip && \
pip install -r requirements.txt
COPY ./entrypoint.sh .
RUN chmod +x /code/entrypoint.sh
COPY . .
ENTRYPOINT ["/code/entrypoint.sh"]
重建镜像并启动容器。
docker-compose up --build
Celery 和 Redis 配置
Celery 会在后台异步执行耗时任务,从而使您的 Web 应用能够持续快速响应用户请求。由于 Redis 可以同时作为消息代理和数据库后端,因此建议将其与 Celery 结合使用。
添加 Redis 和 Celery 服务docker-compose.yml
redis:
image: redis:7
celery:
build: .
command: celery -A config worker -l info
volumes:
- .:/code
env_file:
- ./.env
depends_on:
- db
- redis
- web
顺便也更新一下web服务吧:
depends_on:
- redis
- db
设置完成后,导航到 config 文件夹并创建一个名为 的文件celery.py
cd config
touch celery.py
然后,在其中添加以下代码片段:
# config/celery.py
import os
from decouple import config
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", config("DJANGO_SETTINGS_MODULE"))
app = Celery("config")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
接下来,前往base.py并于底部添加以下配置:
# settings/base.py
# Celery
CELERY_BROKER_URL = config("CELERY_BROKER_URL")
CELERY_RESULT_BACKEND = config("REDIS_BACKEND")
更新.env内容,添加上述环境变量:
# Celery
CELERY_BROKER_URL=redis://redis:6379/0
# Redis
REDIS_BACKEND=redis://redis:6379/0
最终更新内容写入__init__.pyconfig 文件夹下的文件中:
# config/__init__.py
from .celery import app as celery_app
__all__ = ('celery_app',)
再测试一次:
docker-compose up --build
针对生产环境调整 Docker Compose
Django 的内置服务器不适合生产环境,因此在生产环境中应该使用像 Gunicorn 这样的生产级 WSGI 服务器。
此外,您还应该考虑添加 Nginx 作为 Gunicorn 的反向代理,并提供静态文件服务。
docker-compose.prod.yml因此,请在项目根目录创建一个名为 `.conf` 的文件,并添加/更新以下服务:
version: "3.9"
services:
web:
build: .
restart: always
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
env_file:
- ./.env
expose:
- 8000
volumes:
- static_volume:/code/staticfiles
- media_volume:/code/mediafiles
depends_on:
- redis
- db
db:
image: postgres:13
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}
redis:
image: redis:7
celery:
build: .
restart: always
command: celery -A config worker -l info
volumes:
- .:/code
env_file:
- ./.env
depends_on:
- db
- redis
- web
nginx:
build: ./nginx
restart: always
ports:
- ${NGINX_PORT}:80
volumes:
- static_volume:/code/staticfiles
- media_volume:/code/mediafiles
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:
上述文件中有几点值得注意:
- 使用 `<service>`
expose而不是 `<service>`ports。这使得该web服务可以暴露给 Docker 内部的其他服务,但不会暴露给宿主机。 - 静态卷和媒体卷,用于持久化由服务生成和使用的
web数据nginx。
别忘了更新配置.env,添加NGINX_PORT环境变量:
# NGINX
NGINX_PORT=80
然后,在项目根目录中,创建以下文件夹和文件:
mkdir nginx
cd nginx
touch Dockerfile
touch nginx.conf
更新相关文件:
# nginx/Dockerfile
FROM nginx:stable-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
EXPOSE 80
- 上述文件会拉取基础 Nginx 镜像,移除默认配置,并复制您创建的配置,即
nginx.conf包含以下内容:
# nginx/nginx.conf
upstream web_app {
server web:8000;
}
server {
listen 80;
location /static/ {
alias /code/staticfiles/;
}
location /media/ {
alias /code/mediafiles/;
}
location / {
proxy_pass http://web_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
- 值得注意的是,上述配置中,静态文件请求和媒体文件请求分别路由到静态文件文件夹和媒体文件文件夹。
在本地测试您的生产环境:
docker-compose -f docker-compose.prod.yml up --build
访问http://localhost/,静态文件也应该能够正确加载。
结论
本教程引导您完成了 Django 应用的容器化,使其既可用于本地开发,也可用于生产环境。除了容器化部署的便捷性之外,在本地使用 Docker 进行开发还能节省时间,因为它最大限度地减少了您需要在机器上进行的配置工作。
如果你在阅读本指南的过程中遇到任何问题,可以查看GitHub上的项目。
祝您编程愉快!🖤
文章来源:https://dev.to/documatic/start-a-production-ready-dockerized-django-project-5eop
