Cookiecutter Django K8s 部署实战:4 步把 Compose 生产栈搬进 Kubernetes 集群
【免费下载链接】cookiecutter-djangoCookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.项目地址: https://gitcode.com/GitHub_Trending/co/cookiecutter-django
Docker Compose 单机跑得挺顺,但机器一挂服务全挂,想横向扩容也没抓手。你已经用 Cookiecutter Django 生成了生产栈,现在要做的是把容器化部署迁到 Kubernetes 集群。这篇文章按"镜像 → 有状态服务 → 流量入口 → 自愈"的决策链展开,帮你一次搞定迁移。
复用 Compose 生产镜像:多阶段 Dockerfile 已经为集群做好准备
很多人以为上 K8s 要重写 Dockerfile,其实不用。模板里的 compose/production/django/Dockerfile 本身就是为生产设计的多阶段构建:node 阶段编译前端,uv 阶段锁定依赖装进 venv,运行阶段只带python:3.14-slim和必要的系统库,而且全程以非 root 的 django 用户运行。
镜像的入口逻辑藏在两个脚本里,值得花两分钟看懂:
# compose/production/django/entrypoint wait-for-it "${POSTGRES_HOST}:${POSTGRES_PORT}" -t 30 exec "$@" # compose/production/django/start(节选) python /app/manage.py collectstatic --noinput exec gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/appwait-for-it等数据库就绪再起进程,/start先收静态文件再用 gunicorn 监听5000 端口——后面配 Ingress 和探针都用得到这个端口号。
再看配置怎么进容器。打开 config/settings/base.py 会看到数据库和缓存全部走环境变量:
# config/settings/base.py(节选) if os.getenv("DATABASE_URL"): DATABASES = {"default": env.db("DATABASE_URL")} # ... REDIS_URL = env("REDIS_URL", default="redis://redis:6379/0")镜像本身无状态,所有运行时配置全靠环境变量注入。这意味着迁移时镜像原样推进镜像仓库即可,Deployment 里只改 env,一行 Python 代码都不用动。
PostgreSQL 与 Redis 有状态服务:把 DATABASE_URL 和 REDIS_URL 指到 K8s Service
Compose 里的postgres服务是官方镜像加一套维护脚本(backup / restore / rmbackup 都在 compose/production/postgres/maintenance/),redis直接跑docker.io/redis:7.2。到了集群里,这两者不该再跟 Django 挤在同一批 Pod 里:
- PostgreSQL 用 StatefulSet + PVC,或直接换云厂商托管数据库;
- Redis 同理,小项目用一个 StatefulSet 就够,大项目上托管实例。
关键优势是模板没有把任何主机名写死:DATABASES优先读DATABASE_URL,兜底读POSTGRES_HOST等变量;REDIS_URL默认值redis://redis:6379/0里的redis只是 Compose 的服务名。K8s Service 本质就是集群内 DNS,把这两个值换成 Service 名就行:
env: - name: DATABASE_URL valueFrom: { secretKeyRef: { name: db-secret, key: database-url } } - name: REDIS_URL valueFrom: { secretKeyRef: { name: db-secret, key: redis-url } }两种部署的唯一区别,就是这两个变量指向的主机名不同。密码、密钥一律放 Secret,别硬编码。另外注意 config/settings/production.py 里CONN_MAX_AGE默认 60 秒,gunicorn 多 worker 会复用连接池,这在集群里是好事,不用改。
上集群后先进 Pod 跑一次迁移验证连通性:
kubectl exec -it django-pod -- python /app/manage.py migrate去掉集群内的 Traefik:Ingress 管路由,cert-manager 管证书
Compose 方案里的 traefik 服务负责 80/443 端口和 Let's Encrypt 的 httpChallenge(见 compose/production/traefik/traefik.yml)。上 K8s 后这套东西可以整体退役:Ingress 管路由,cert-manager 负责证书自动续期,集群里不需要再自己养一个反向代理。
好消息是 Django 侧的"前面有代理"配置模板已经替你写好了:
# config/settings/production.py(节选) SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True)只要 Ingress 正确传递了X-Forwarded-Proto,HTTPS 强制跳转会自动生效。Ingress 最小配置:
metadata: name: myapp annotations: cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: - hosts: [example.com] secretName: myapp-tls两个容易踩的坑:
ALLOWED_HOSTS读的是DJANGO_ALLOWED_HOSTS环境变量,新域名必须加进去,否则全站 400;- 原 traefik.yml 里有个
hostsProxyHeaders: ['X-CSRFToken']的 CSRF 中间件,换 Ingress 后记得加等价的 header 透传,否则带 CSRF Token 的 AJAX 请求会被拒。
3 行探针 YAML:gunicorn 卡死后 K8s 集群自动重启 Pod
探针是集群派给每个容器的"自动体检仪":liveness 失败就重启容器,readiness 失败就从 Service 摘流量。模板没有独立的/health/路由,所以直接用首页做就绪检查,存活检查用 TCP 端口即可:
readinessProbe: httpGet: { path: /, port: 5000 } initialDelaySeconds: 10 livenessProbe: tcpSocket: { port: 5000 } periodSeconds: 10就绪探针查首页、存活探针查 TCP 5000,两者都别填 80 端口——那是 Ingress 层的事,Pod 只监听 5000。再给容器加上resources的 requests/limits,流量高峰时 HPA 就能依据 CPU 指标自动加副本,这就是 Compose 给不了的弹性。
上线前建议先在集群里跑一轮容器化测试,确认视图、API、缓存读写全部通过:
kubectl exec -it django-pod -- python /app/manage.py test上线前检查清单:逐条打勾再放流量
- 镜像已推入仓库,Deployment 里的 tag 与 Compose 验证过的一致
DATABASE_URL/REDIS_URL指向集群内 Service 名,而非 localhostDJANGO_ALLOWED_HOSTS已包含实际对外域名- 探针端口是 5000,Pod 能在 1 分钟内进入 Ready
migrate和collectstatic都已执行成功,静态资源可访问- 访问 HTTP 域名会被 301 到 HTTPS(
SECURE_SSL_REDIRECT生效) - CPU / 内存的 requests 与 limits 已设置,HPA 目标值合理
Compose 编排的细节可以再对照 docs/3-deployment/deployment-with-docker.rst 里的说明。清单全部打勾后,切 DNS 到 Ingress,然后删掉那台单机——集群剩下的事,它会自己管。
【免费下载链接】cookiecutter-djangoCookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.项目地址: https://gitcode.com/GitHub_Trending/co/cookiecutter-django
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考