如何使用 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 myblog cd myblog python manage.py startapp blog
在 blog/views.py 中加入一个简单视图:
from django.http import HttpResponse
def home(request):
return HttpResponse("Hello, Django & Docker!")在 myblog/urls.py 中注册:
from django.contrib import admin
from django.urls import path
from blog.views import home
urlpatterns = [
path('admin/', admin.site.urls),
path('', home, name='home'),
]修改 settings.py,允许外部访问(部署时务必改为具体域名或 IP):
ALLOWED_HOSTS = ['*'] # 仅测试用,生产环境请指定真实域名 STATIC_ROOT = BASE_DIR / 'staticfiles' # 收集静态文件的目录
生成依赖文件:
pip freeze > requirements.txt
requirements.txt 内容示例:
Django==4.2 gunicorn==21.2.0
3. 手写 Dockerfile:用 Gunicorn 启动 Django
在项目根目录创建 Dockerfile:
# 使用官方 Python 镜像(slim 版本更小)
FROM python:3.11-slim
# 设置环境变量,避免 Python 生成 .pyc 文件,并开启标准输出
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# 设置工作目录
WORKDIR /app
# 安装系统依赖(如果连接数据库可能需要)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev && \
rm -rf /var/lib/apt/lists/*
# 复制并安装 Python 依赖
COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
# 复制项目代码
COPY . .
# 收集静态文件
RUN python manage.py collectstatic --noinput
# 暴露 8000 端口(Gunicorn 默认监听)
EXPOSE 8000
# 启动命令:使用 Gunicorn 运行 WSGI 应用
CMD ["gunicorn", "myblog.wsgi:application", "--bind", "0.0.0.0:8000"]构建镜像,你能看到类似下面的输出:
$ docker build -t myblog:latest . Sending build context to Docker daemon 45.06kB Step 1/10 : FROM python:3.11-slim ---> a1b2c3d4e5f6 Step 2/10 : ENV PYTHONDONTWRITEBYTECODE=1 ---> Running in 12ab34cd56ef ---> 7890abcd1234 ... Step 10/10 : CMD ["gunicorn", "myblog.wsgi:application", "--bind", "0.0.0.0:8000"] ---> Running in ef567890abcd ---> fedc09876543 Successfully built fedc09876543 Successfully tagged myblog:latest
跑起来试试:
$ docker run -d -p 8000:8000 --name myblog-test myblog:latest
查看容器日志,熟悉的 Gunicorn 启动信息:
$ docker logs myblog-test [2026-05-17 14:23:45 +0000] [1] [INFO] Starting gunicorn 21.2.0 [2026-05-17 14:23:45 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2026-05-17 14:23:45 +0000] [1] [INFO] Using worker: sync [2026-05-17 14: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.conf:
upstream myblog_app {
# web 是 docker-compose 中 Django 服务的名称
server web:8000;
}
server {
listen 80;
server_name _; # 用真实域名时替换
# 静态文件直接由 Nginx 提供
location /static/ {
alias /staticfiles/;
}
# 动态请求转发给 Gunicorn
location / {
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.yml:
version: '3.8'
services:
web:
build: .
# 不直接暴露端口,仅内部使用
expose:
- "8000"
volumes:
- static_volume:/app/staticfiles # 共享静态文件卷
environment:
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY:-change-me}
- DEBUG=False
command: gunicorn myblog.wsgi:application --bind 0.0.0.0:8000 --workers 3
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./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-compose up -d Creating network "myblog_default" with the default driver Creating volume "myblog_static_volume" with default driver Creating myblog_web_1 ... done Creating myblog_nginx_1 ... done
查看所有容器的状态:
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------
myblog_nginx_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp
myblog_web_1 gunicorn myblog.wsgi:applic ... Up 8000/tcp看一下日志,确认两个服务都正常:
$ docker-compose logs -f # web 输出: web_1 | [2026-05-17 14:30:01 +0000] [1] [INFO] Starting gunicorn 21.2.0 web_1 | [2026-05-17 14:30:01 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) web_1 | [2026-05-17 14:30:01 +0000] [8] [INFO] Booting worker with pid: 8 web_1 | [2026-05-17 14:30:01 +0000] [9] [INFO] Booting worker with pid: 9 web_1 | [2026-05-17 14:30:01 +0000] [10] [INFO] Booting worker with pid: 10 # nginx 输出: nginx_1 | /docker-entrypoint.sh: Configuration complete; ready for start up
访问 http://localhost(Nginx 的 80 端口),同样得到 Hello, Django & Docker!。
静态文件测试:访问 http://localhost/static/admin/css/base.css 能看到 Django admin 的样式,证明 Nginx 直接返回了静态文件。
5. 部署到真实的云服务器
假设你有一台 Ubuntu 22.04 的云服务器(阿里云、腾讯云、AWS 均可),并已通过 SSH 登录。
5.1 服务器环境准备
# 更新包索引 sudo apt update # 安装 Docker curl -fsSL https://get.docker.com | sudo sh # 启动 Docker 并设置开机自启 sudo systemctl enable docker --now # 安装 Docker Compose(独立插件方式) sudo apt install docker-compose-plugin -y # 验证 docker --version docker compose version
控制台输出示例:
$ docker --version Docker version 24.0.7, build afdd53b $ docker compose version Docker Compose version v2.21.0
5.2 上传项目代码
推荐使用 Git 管理代码:
# 在服务器上克隆项目(以 GitHub 为例) cd /opt git clone https://github.com/yourname/myblog.git cd myblog
5.3 配置环境变量(重要)
创建 .env 文件存储敏感信息,生产环境务必修改:
DJANGO_SECRET_KEY=你的超级长随机字符串 DEBUG=False ALLOWED_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
--bind 0.0.0.0:8000
--workers ${WEB_CONCURRENCY:-3}对应的 settings.py 中读取这些变量:
import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'fallback-key')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',')5.4 在服务器上启动应用
$ docker compose up -d --build [+] Building 23.4s (12/12) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 348B 0.0s ... [+] Running 3/3 ✔ Network myblog_default Created 0.1s ✔ Container myblog-web-1 Started 1.2s ✔ Container myblog-nginx-1 Started 1.5s
检查运行状态:
$ docker compose ps NAME COMMAND SERVICE STATUS PORTS myblog-nginx-1 "/docker-entrypoint.…" nginx running 0.0.0.0:80->80/tcp myblog-web-1 "gunicorn myblog.wsgi…" web running 8000/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:
- "8000"
volumes:
- static_volume:/app/staticfiles
environment:
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DEBUG=False
- DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
depends_on:
- db
command: >
gunicorn myblog.wsgi:application
--bind 0.0.0.0:8000
--workers 3
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./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_NAME=myblog DB_USER=mybloguser DB_PASSWORD=你的复杂密码
在 Django 的 settings.py 使用 dj-database-url 解析数据库连接(需加入 requirements.txt):
import dj_database_url
DATABASES = {
'default': dj_database_url.config(
default=os.environ.get('DATABASE_URL', 'sqlite:///db.sqlite3')
)
}
重新构建并启动:
$ docker compose up -d --build Creating myblog_db_1 ... done Creating myblog_web_1 ... done Creating myblog_nginx_1 ... done
查看数据库容器日志:
$ docker compose logs db db_1 | 2026-05-17 14:45:00.123 UTC [1] LOG: database system is ready to accept connections
执行数据库迁移:
$ docker compose exec web 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 {
listen 443 ssl;
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-action@v1.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/myblog
git pull
docker compose 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 项目部署到云服务器的文章就介绍到这了,更多相关Gunicorn Nginx Docker Django 项目云服务器部署内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
安装Docker Desktop报错WSL 2 installation is incomplete的问题(解决报错)
这篇文章主要介绍了安装Docker Desktop报错WSL 2 installation is incomplete的问题,解决方法很简单只需我们自己手动更新一下,我们根据提示去微软官网下载最新版的wsl2安装后即可正常打开,需要的朋友可以参考下2021-06-06
docker images,info,-d等命令报错的解决方法
最近刚接触Docker,所以在学习的过程中出现了好多的问题,百度上不好找,谷歌的话,楼主不会翻墙。后来通过各方求助和细心研究解决了一些遇到的问题,现在将遇到的问题和解决方法分享给大家,有需要的朋友们可以参考借鉴。2016-11-11
解决运行Docker镜像报错:version `GLIBC_2.32‘ not found
文章介绍了解决Docker镜像运行时因GLIBC版本不匹配导致的错误,建议使用AlpineLinux作为基础镜像,并在其中安装所需的运行时库,作者还分享了个人经验,提醒读者尝试其他方法无效后可以寻求帮助2024-12-12


最新评论