IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。在本地跑得好好的 Django一上服务器就各种报错环境不一致、依赖缺失、进程守护……传统部署的坑数不胜数。本文将带你用Gunicorn Nginx Docker三件套把 Django 项目丝滑地送上云服务器。全程手摸手有丰富的控制台输出与可复现的例子新手能看懂进阶者也能收获最佳实践。1. 先弄明白这三者分别是什么Gunicorn一个 WSGI HTTP 服务器专为运行 Python Web 应用而生。相比 Django 自带的runserver它支持多进程、更稳定是生产环境的标配。Nginx高性能的反向代理和静态文件服务器。在这里它负责接收外界请求动态内容转发给 Gunicorn静态文件直接由自己返回极大提升效率。Docker将应用及其依赖打包成容器消除“我这能跑你那不行”的环境差异问题。结合 Docker Compose 可以轻松编排多服务Web Nginx 数据库等。整体数据流用户浏览器 - 云服务器 80/443 端口 - Nginx 容器 - 动态请求 - Gunicorn 容器 - Django 应用2. 预备一个示例 Django 项目假设我们的项目名为myblog结构如下myblog/ ├── manage.py ├── myblog/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── blog/# 一个简单应用│ ├── views.py │ └── urls.py ├── requirements.txt └── Dockerfile快速创建一个最简版本实际操作时你可以用自己的项目django-admin startproject myblogcdmyblog python manage.py startapp blog在blog/views.py中加入一个简单视图from django.httpimportHttpResponse def home(request):returnHttpResponse(Hello, Django Docker!)在myblog/urls.py中注册from django.contribimportadmin from django.urlsimportpath from blog.viewsimporthome urlpatterns[path(admin/, admin.site.urls), path(, home,namehome),]修改settings.py允许外部访问部署时务必改为具体域名或 IPALLOWED_HOSTS[*]# 仅测试用生产环境请指定真实域名STATIC_ROOTBASE_DIR /staticfiles# 收集静态文件的目录生成依赖文件pip freezerequirements.txtrequirements.txt内容示例Django4.2gunicorn21.2.03. 手写 Dockerfile用 Gunicorn 启动 Django在项目根目录创建Dockerfile# 使用官方 Python 镜像slim 版本更小FROM python:3.11-slim# 设置环境变量避免 Python 生成 .pyc 文件并开启标准输出ENVPYTHONDONTWRITEBYTECODE1ENVPYTHONUNBUFFERED1# 设置工作目录WORKDIR /app# 安装系统依赖如果连接数据库可能需要RUNapt-getupdateapt-getinstall-y--no-install-recommends\gcc libpq-dev\rm-rf/var/lib/apt/lists/*# 复制并安装 Python 依赖COPY requirements.txt.RUN pipinstall--upgradepippipinstall-rrequirements.txt# 复制项目代码COPY..# 收集静态文件RUN python manage.py collectstatic--noinput# 暴露 8000 端口Gunicorn 默认监听EXPOSE8000# 启动命令使用 Gunicorn 运行 WSGI 应用CMD[gunicorn,myblog.wsgi:application,--bind,0.0.0.0:8000]构建镜像你能看到类似下面的输出$dockerbuild-tmyblog:latest.Sending build context to Docker daemon45.06kB Step1/10:FROM python:3.11-slim ---a1b2c3d4e5f6 Step2/10:ENVPYTHONDONTWRITEBYTECODE1---Runningin12ab34cd56ef ---7890abcd1234... Step10/10:CMD[gunicorn,myblog.wsgi:application,--bind,0.0.0.0:8000]---Runninginef567890abcd ---fedc09876543 Successfully built fedc09876543 Successfully tagged myblog:latest跑起来试试$dockerrun-d-p8000:8000--namemyblog-test myblog:latest查看容器日志熟悉的 Gunicorn 启动信息$dockerlogs myblog-test[2026-05-1714:23:45 0000][1][INFO]Starting gunicorn21.2.0[2026-05-1714:23:45 0000][1][INFO]Listening at: http://0.0.0.0:8000(1)[2026-05-1714:23:45 0000][1][INFO]Using worker:sync[2026-05-1714:23:45 0000][8][INFO]Booting worker with pid:8浏览器访问http://localhost:8000就能看到Hello, Django Docker!。停止并删除测试容器docker rm -f myblog-test进阶提示可以通过--workers参数调整进程数或通过环境变量WEB_CONCURRENCY动态设置后面会提到。4. 引入 Nginx更贴近生产环境的编排生产环境中我们不会把 Gunicorn 直接暴露给用户而是前面再放一个 Nginx。这里使用Docker Compose定义两个服务并用共享卷处理静态文件。4.1 Nginx 配置文件在项目根目录创建nginx/nginx.confupstream myblog_app{# web 是 docker-compose 中 Django 服务的名称server web:8000;}server{listen80;server_name _;# 用真实域名时替换# 静态文件直接由 Nginx 提供location /static/{alias/staticfiles/;}# 动态请求转发给 Gunicornlocation /{proxy_pass http://myblog_app;proxy_set_header Host$host;proxy_set_header X-Real-IP$remote_addr;proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto$scheme;}}4.2 Docker Compose 编排文件在项目根目录创建docker-compose.ymlversion:3.8services: web: build:.# 不直接暴露端口仅内部使用expose: -8000volumes: - static_volume:/app/staticfiles# 共享静态文件卷environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY:-change-me}-DEBUGFalse command: gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers3restart: unless-stopped nginx: image: nginx:alpine ports: -80:80volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - static_volume:/staticfiles:ro# 只读方式挂载静态文件卷depends_on: - web restart: unless-stopped volumes: static_volume:新手理解static_volume是一个命名卷web 服务运行collectstatic后静态文件就存入其中。Nginx 挂载同一个卷直接向用户提供/static/下的文件完全不经过 Django速度飞快。4.3 启动编排观察日志$docker-composeup-dCreating networkmyblog_defaultwith the default driver Creating volumemyblog_static_volumewith default driver Creating myblog_web_1...doneCreating myblog_nginx_1...done查看所有容器的状态$docker-composepsName Command State Ports ---------------------------------------------------------------------------------------------- myblog_nginx_1 /docker-entrypoint.sh ngin... Up0.0.0.0:80-80/tcp myblog_web_1 gunicorn myblog.wsgi:applic... Up8000/tcp看一下日志确认两个服务都正常$docker-composelogs-f# web 输出web_1|[2026-05-1714:30:01 0000][1][INFO]Starting gunicorn21.2.0 web_1|[2026-05-1714:30:01 0000][1][INFO]Listening at: http://0.0.0.0:8000(1)web_1|[2026-05-1714:30:01 0000][8][INFO]Booting worker with pid:8web_1|[2026-05-1714:30:01 0000][9][INFO]Booting worker with pid:9web_1|[2026-05-1714:30:01 0000][10][INFO]Booting worker with pid:10# nginx 输出nginx_1|/docker-entrypoint.sh: Configuration complete;readyforstart up访问http://localhostNginx 的 80 端口同样得到Hello, Django Docker!。静态文件测试访问http://localhost/static/admin/css/base.css能看到 Django admin 的样式证明 Nginx 直接返回了静态文件。5. 部署到真实的云服务器假设你有一台 Ubuntu 22.04 的云服务器阿里云、腾讯云、AWS 均可并已通过 SSH 登录。5.1 服务器环境准备# 更新包索引sudoaptupdate# 安装 Dockercurl-fsSLhttps://get.docker.com|sudosh# 启动 Docker 并设置开机自启sudosystemctlenabledocker--now# 安装 Docker Compose独立插件方式sudoaptinstalldocker-compose-plugin-y# 验证docker--versiondockercompose version控制台输出示例$docker--versionDocker version24.0.7, build afdd53b $dockercompose version Docker Compose version v2.21.05.2 上传项目代码推荐使用 Git 管理代码# 在服务器上克隆项目以 GitHub 为例cd/optgitclone https://github.com/yourname/myblog.gitcdmyblog5.3 配置环境变量重要创建.env文件存储敏感信息生产环境务必修改DJANGO_SECRET_KEY你的超级长随机字符串DEBUGFalseALLOWED_HOSTS你的服务器IP或域名并在docker-compose.yml中传递给 web 服务同时修改 Gunicorn 命令引用环境变量web:... environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY}-DEBUG${DEBUG:-False}-ALLOWED_HOSTS${ALLOWED_HOSTS}command:gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers${WEB_CONCURRENCY:-3}对应的settings.py中读取这些变量importos SECRET_KEYos.environ.get(DJANGO_SECRET_KEY,fallback-key)DEBUGos.environ.get(DEBUG,False)TrueALLOWED_HOSTSos.environ.get(ALLOWED_HOSTS,*).split(,)5.4 在服务器上启动应用$dockercompose up-d--build[]Building23.4s(12/12)FINISHED[internal]load build definition from Dockerfile0.0stransferring dockerfile: 348B0.0s...[]Running3/3 ✔ Network myblog_default Created0.1s ✔ Container myblog-web-1 Started1.2s ✔ Container myblog-nginx-1 Started1.5s检查运行状态$dockercomposepsNAME COMMAND SERVICE STATUS PORTS myblog-nginx-1/docker-entrypoint.…nginx running0.0.0.0:80-80/tcp myblog-web-1gunicorn myblog.wsgi…web running8000/tcp现在在云服务器的安全组中开放 80 端口用浏览器访问服务器公网 IP就能看到你的 Django 应用了6. 进阶实战加入 PostgreSQL 数据库SQLite 不适合生产我们再加一个数据库服务完整演示多容器编排。修改docker-compose.yml增加db服务services: db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data/ environment: -POSTGRES_DB${DB_NAME:-myblog}-POSTGRES_USER${DB_USER:-mybloguser}-POSTGRES_PASSWORD${DB_PASSWORD:-securepassword}restart: unless-stopped web: build:.expose: -8000volumes: - static_volume:/app/staticfiles environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY}-DEBUGFalse -DATABASE_URLpostgres://${DB_USER}:${DB_PASSWORD}db:5432/${DB_NAME}depends_on: - db command:gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers3restart: unless-stopped nginx: image: nginx:alpine ports: -80:80volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - static_volume:/staticfiles:ro depends_on: - web restart: unless-stopped volumes: postgres_data: static_volume:在.env中增加DB_NAMEmyblogDB_USERmybloguserDB_PASSWORD你的复杂密码在 Django 的settings.py使用dj-database-url解析数据库连接需加入requirements.txtimportdj_database_url DATABASES{default:dj_database_url.config(defaultos.environ.get(DATABASE_URL,sqlite:///db.sqlite3))}重新构建并启动$dockercompose up-d--buildCreating myblog_db_1...doneCreating myblog_web_1...doneCreating myblog_nginx_1...done查看数据库容器日志$dockercompose logs db db_1|2026-05-1714:45:00.123 UTC[1]LOG: database system is ready to accept connections执行数据库迁移$dockercomposeexecweb python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK...完美现在你的 Django 应用已经连接上了生产级 PostgreSQL。7. 更进一步HTTPS、CI/CD 等最佳实践简览7.1 开启 HTTPS可以在 Nginx 容器内配置 SSL最简单的方法是使用certbot及其 Nginx 插件但容器化环境下推荐使用Certbot 官方镜像或Traefik。这里给出一个手动整合 Certbot 的思路先以 HTTP 启动通过certbot获取证书映射到宿主机的证书目录。修改 Nginx 配置监听 443证书路径指向挂载的文件。使用docker compose exec nginx nginx -s reload热重载。关键 Nginx SSL 配置片段server{listen443ssl;ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;...}7.2 使用 CI/CD 自动部署例如 GitHub Actions 工作流在推送代码后自动构建镜像并部署到服务器- name: Deploy to Server uses: appleboy/ssh-actionv1.0 with: host:${{ secrets.SERVER_HOST }}username:${{ secrets.SERVER_USER }}key:${{ secrets.SSH_PRIVATE_KEY }}script:|cd/opt/mybloggitpulldockercompose up-d--build7.3 性能与监控Worker 数量建议(2 * CPU核心数) 1可通过WEB_CONCURRENCY环境变量调整。日志docker compose logs -f实时查看生产环境建议接入 ELK 或 Loki。健康检查在docker-compose.yml中为 web 服务添加healthcheck配合depends_on条件使用condition: service_healthy。8. 常见问题快速排查9. 总结通过这篇长文我们从零开始经历了创建一个简单的 Django 项目用 Dockerfile 将其容器化并用 Gunicorn 启动用 Docker Compose 编排 Nginx Gunicorn处理静态文件部署到云服务器并连接 PostgreSQL探索了 HTTPS、CI/CD 等进阶方向Gunicorn Nginx Docker这套组合已经成为 Django 生产部署的事实标准。它隔离环境、简化运维、易于扩展无论是个人项目还是企业级应用都能游刃有余。希望这篇文章能帮你跨过从开发到上线的鸿沟享受流畅的部署体验。
深入浅出:使用 Gunicorn + Nginx + Docker 将 Django 项目部署到云服务器
IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。在本地跑得好好的 Django一上服务器就各种报错环境不一致、依赖缺失、进程守护……传统部署的坑数不胜数。本文将带你用Gunicorn Nginx Docker三件套把 Django 项目丝滑地送上云服务器。全程手摸手有丰富的控制台输出与可复现的例子新手能看懂进阶者也能收获最佳实践。1. 先弄明白这三者分别是什么Gunicorn一个 WSGI HTTP 服务器专为运行 Python Web 应用而生。相比 Django 自带的runserver它支持多进程、更稳定是生产环境的标配。Nginx高性能的反向代理和静态文件服务器。在这里它负责接收外界请求动态内容转发给 Gunicorn静态文件直接由自己返回极大提升效率。Docker将应用及其依赖打包成容器消除“我这能跑你那不行”的环境差异问题。结合 Docker Compose 可以轻松编排多服务Web Nginx 数据库等。整体数据流用户浏览器 - 云服务器 80/443 端口 - Nginx 容器 - 动态请求 - Gunicorn 容器 - Django 应用2. 预备一个示例 Django 项目假设我们的项目名为myblog结构如下myblog/ ├── manage.py ├── myblog/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── blog/# 一个简单应用│ ├── views.py │ └── urls.py ├── requirements.txt └── Dockerfile快速创建一个最简版本实际操作时你可以用自己的项目django-admin startproject myblogcdmyblog python manage.py startapp blog在blog/views.py中加入一个简单视图from django.httpimportHttpResponse def home(request):returnHttpResponse(Hello, Django Docker!)在myblog/urls.py中注册from django.contribimportadmin from django.urlsimportpath from blog.viewsimporthome urlpatterns[path(admin/, admin.site.urls), path(, home,namehome),]修改settings.py允许外部访问部署时务必改为具体域名或 IPALLOWED_HOSTS[*]# 仅测试用生产环境请指定真实域名STATIC_ROOTBASE_DIR /staticfiles# 收集静态文件的目录生成依赖文件pip freezerequirements.txtrequirements.txt内容示例Django4.2gunicorn21.2.03. 手写 Dockerfile用 Gunicorn 启动 Django在项目根目录创建Dockerfile# 使用官方 Python 镜像slim 版本更小FROM python:3.11-slim# 设置环境变量避免 Python 生成 .pyc 文件并开启标准输出ENVPYTHONDONTWRITEBYTECODE1ENVPYTHONUNBUFFERED1# 设置工作目录WORKDIR /app# 安装系统依赖如果连接数据库可能需要RUNapt-getupdateapt-getinstall-y--no-install-recommends\gcc libpq-dev\rm-rf/var/lib/apt/lists/*# 复制并安装 Python 依赖COPY requirements.txt.RUN pipinstall--upgradepippipinstall-rrequirements.txt# 复制项目代码COPY..# 收集静态文件RUN python manage.py collectstatic--noinput# 暴露 8000 端口Gunicorn 默认监听EXPOSE8000# 启动命令使用 Gunicorn 运行 WSGI 应用CMD[gunicorn,myblog.wsgi:application,--bind,0.0.0.0:8000]构建镜像你能看到类似下面的输出$dockerbuild-tmyblog:latest.Sending build context to Docker daemon45.06kB Step1/10:FROM python:3.11-slim ---a1b2c3d4e5f6 Step2/10:ENVPYTHONDONTWRITEBYTECODE1---Runningin12ab34cd56ef ---7890abcd1234... Step10/10:CMD[gunicorn,myblog.wsgi:application,--bind,0.0.0.0:8000]---Runninginef567890abcd ---fedc09876543 Successfully built fedc09876543 Successfully tagged myblog:latest跑起来试试$dockerrun-d-p8000:8000--namemyblog-test myblog:latest查看容器日志熟悉的 Gunicorn 启动信息$dockerlogs myblog-test[2026-05-1714:23:45 0000][1][INFO]Starting gunicorn21.2.0[2026-05-1714:23:45 0000][1][INFO]Listening at: http://0.0.0.0:8000(1)[2026-05-1714:23:45 0000][1][INFO]Using worker:sync[2026-05-1714:23:45 0000][8][INFO]Booting worker with pid:8浏览器访问http://localhost:8000就能看到Hello, Django Docker!。停止并删除测试容器docker rm -f myblog-test进阶提示可以通过--workers参数调整进程数或通过环境变量WEB_CONCURRENCY动态设置后面会提到。4. 引入 Nginx更贴近生产环境的编排生产环境中我们不会把 Gunicorn 直接暴露给用户而是前面再放一个 Nginx。这里使用Docker Compose定义两个服务并用共享卷处理静态文件。4.1 Nginx 配置文件在项目根目录创建nginx/nginx.confupstream myblog_app{# web 是 docker-compose 中 Django 服务的名称server web:8000;}server{listen80;server_name _;# 用真实域名时替换# 静态文件直接由 Nginx 提供location /static/{alias/staticfiles/;}# 动态请求转发给 Gunicornlocation /{proxy_pass http://myblog_app;proxy_set_header Host$host;proxy_set_header X-Real-IP$remote_addr;proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto$scheme;}}4.2 Docker Compose 编排文件在项目根目录创建docker-compose.ymlversion:3.8services: web: build:.# 不直接暴露端口仅内部使用expose: -8000volumes: - static_volume:/app/staticfiles# 共享静态文件卷environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY:-change-me}-DEBUGFalse command: gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers3restart: unless-stopped nginx: image: nginx:alpine ports: -80:80volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - static_volume:/staticfiles:ro# 只读方式挂载静态文件卷depends_on: - web restart: unless-stopped volumes: static_volume:新手理解static_volume是一个命名卷web 服务运行collectstatic后静态文件就存入其中。Nginx 挂载同一个卷直接向用户提供/static/下的文件完全不经过 Django速度飞快。4.3 启动编排观察日志$docker-composeup-dCreating networkmyblog_defaultwith the default driver Creating volumemyblog_static_volumewith default driver Creating myblog_web_1...doneCreating myblog_nginx_1...done查看所有容器的状态$docker-composepsName Command State Ports ---------------------------------------------------------------------------------------------- myblog_nginx_1 /docker-entrypoint.sh ngin... Up0.0.0.0:80-80/tcp myblog_web_1 gunicorn myblog.wsgi:applic... Up8000/tcp看一下日志确认两个服务都正常$docker-composelogs-f# web 输出web_1|[2026-05-1714:30:01 0000][1][INFO]Starting gunicorn21.2.0 web_1|[2026-05-1714:30:01 0000][1][INFO]Listening at: http://0.0.0.0:8000(1)web_1|[2026-05-1714:30:01 0000][8][INFO]Booting worker with pid:8web_1|[2026-05-1714:30:01 0000][9][INFO]Booting worker with pid:9web_1|[2026-05-1714:30:01 0000][10][INFO]Booting worker with pid:10# nginx 输出nginx_1|/docker-entrypoint.sh: Configuration complete;readyforstart up访问http://localhostNginx 的 80 端口同样得到Hello, Django Docker!。静态文件测试访问http://localhost/static/admin/css/base.css能看到 Django admin 的样式证明 Nginx 直接返回了静态文件。5. 部署到真实的云服务器假设你有一台 Ubuntu 22.04 的云服务器阿里云、腾讯云、AWS 均可并已通过 SSH 登录。5.1 服务器环境准备# 更新包索引sudoaptupdate# 安装 Dockercurl-fsSLhttps://get.docker.com|sudosh# 启动 Docker 并设置开机自启sudosystemctlenabledocker--now# 安装 Docker Compose独立插件方式sudoaptinstalldocker-compose-plugin-y# 验证docker--versiondockercompose version控制台输出示例$docker--versionDocker version24.0.7, build afdd53b $dockercompose version Docker Compose version v2.21.05.2 上传项目代码推荐使用 Git 管理代码# 在服务器上克隆项目以 GitHub 为例cd/optgitclone https://github.com/yourname/myblog.gitcdmyblog5.3 配置环境变量重要创建.env文件存储敏感信息生产环境务必修改DJANGO_SECRET_KEY你的超级长随机字符串DEBUGFalseALLOWED_HOSTS你的服务器IP或域名并在docker-compose.yml中传递给 web 服务同时修改 Gunicorn 命令引用环境变量web:... environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY}-DEBUG${DEBUG:-False}-ALLOWED_HOSTS${ALLOWED_HOSTS}command:gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers${WEB_CONCURRENCY:-3}对应的settings.py中读取这些变量importos SECRET_KEYos.environ.get(DJANGO_SECRET_KEY,fallback-key)DEBUGos.environ.get(DEBUG,False)TrueALLOWED_HOSTSos.environ.get(ALLOWED_HOSTS,*).split(,)5.4 在服务器上启动应用$dockercompose up-d--build[]Building23.4s(12/12)FINISHED[internal]load build definition from Dockerfile0.0stransferring dockerfile: 348B0.0s...[]Running3/3 ✔ Network myblog_default Created0.1s ✔ Container myblog-web-1 Started1.2s ✔ Container myblog-nginx-1 Started1.5s检查运行状态$dockercomposepsNAME COMMAND SERVICE STATUS PORTS myblog-nginx-1/docker-entrypoint.…nginx running0.0.0.0:80-80/tcp myblog-web-1gunicorn myblog.wsgi…web running8000/tcp现在在云服务器的安全组中开放 80 端口用浏览器访问服务器公网 IP就能看到你的 Django 应用了6. 进阶实战加入 PostgreSQL 数据库SQLite 不适合生产我们再加一个数据库服务完整演示多容器编排。修改docker-compose.yml增加db服务services: db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data/ environment: -POSTGRES_DB${DB_NAME:-myblog}-POSTGRES_USER${DB_USER:-mybloguser}-POSTGRES_PASSWORD${DB_PASSWORD:-securepassword}restart: unless-stopped web: build:.expose: -8000volumes: - static_volume:/app/staticfiles environment: -DJANGO_SECRET_KEY${DJANGO_SECRET_KEY}-DEBUGFalse -DATABASE_URLpostgres://${DB_USER}:${DB_PASSWORD}db:5432/${DB_NAME}depends_on: - db command:gunicorn myblog.wsgi:application--bind0.0.0.0:8000--workers3restart: unless-stopped nginx: image: nginx:alpine ports: -80:80volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - static_volume:/staticfiles:ro depends_on: - web restart: unless-stopped volumes: postgres_data: static_volume:在.env中增加DB_NAMEmyblogDB_USERmybloguserDB_PASSWORD你的复杂密码在 Django 的settings.py使用dj-database-url解析数据库连接需加入requirements.txtimportdj_database_url DATABASES{default:dj_database_url.config(defaultos.environ.get(DATABASE_URL,sqlite:///db.sqlite3))}重新构建并启动$dockercompose up-d--buildCreating myblog_db_1...doneCreating myblog_web_1...doneCreating myblog_nginx_1...done查看数据库容器日志$dockercompose logs db db_1|2026-05-1714:45:00.123 UTC[1]LOG: database system is ready to accept connections执行数据库迁移$dockercomposeexecweb python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK...完美现在你的 Django 应用已经连接上了生产级 PostgreSQL。7. 更进一步HTTPS、CI/CD 等最佳实践简览7.1 开启 HTTPS可以在 Nginx 容器内配置 SSL最简单的方法是使用certbot及其 Nginx 插件但容器化环境下推荐使用Certbot 官方镜像或Traefik。这里给出一个手动整合 Certbot 的思路先以 HTTP 启动通过certbot获取证书映射到宿主机的证书目录。修改 Nginx 配置监听 443证书路径指向挂载的文件。使用docker compose exec nginx nginx -s reload热重载。关键 Nginx SSL 配置片段server{listen443ssl;ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;...}7.2 使用 CI/CD 自动部署例如 GitHub Actions 工作流在推送代码后自动构建镜像并部署到服务器- name: Deploy to Server uses: appleboy/ssh-actionv1.0 with: host:${{ secrets.SERVER_HOST }}username:${{ secrets.SERVER_USER }}key:${{ secrets.SSH_PRIVATE_KEY }}script:|cd/opt/mybloggitpulldockercompose up-d--build7.3 性能与监控Worker 数量建议(2 * CPU核心数) 1可通过WEB_CONCURRENCY环境变量调整。日志docker compose logs -f实时查看生产环境建议接入 ELK 或 Loki。健康检查在docker-compose.yml中为 web 服务添加healthcheck配合depends_on条件使用condition: service_healthy。8. 常见问题快速排查9. 总结通过这篇长文我们从零开始经历了创建一个简单的 Django 项目用 Dockerfile 将其容器化并用 Gunicorn 启动用 Docker Compose 编排 Nginx Gunicorn处理静态文件部署到云服务器并连接 PostgreSQL探索了 HTTPS、CI/CD 等进阶方向Gunicorn Nginx Docker这套组合已经成为 Django 生产部署的事实标准。它隔离环境、简化运维、易于扩展无论是个人项目还是企业级应用都能游刃有余。希望这篇文章能帮你跨过从开发到上线的鸿沟享受流畅的部署体验。