docker是ci/cd落地的基础设施层,通过镜像固化环境与依赖消除“本地能跑、上线就崩”问题;关键在合理利用分层缓存、多阶段构建、规范镜像命名及tag管理,并确保k8s中端口暴露、环境变量注入和dns配置正确。

Docker在DevOps中不是“加分项”,而是让CI/CD管道真正跑通的基础设施层——它把环境、依赖、配置、启动逻辑全部固化进镜像,让“开发能跑,测试能过,上线就崩”这种经典故障直接消失。
为什么Dockerfile写完一构建就失败?关键在分层缓存和指令顺序
很多人以为Dockerfile只是“按顺序执行命令”,但实际构建过程高度依赖Layer缓存机制。一旦某层失效(比如RUN pip install -r requirements.txt因requirements.txt内容变更而重建),其后所有层都会重新执行,导致构建变慢、镜像体积失控,甚至因缓存误用引入不一致依赖。
- 把变动频繁的指令(如代码复制、依赖安装)尽量往后放,稳定部分(如基础镜像、系统包安装)靠前
- 避免用
COPY . /app后再RUN pip install——这会让每次代码改一行都触发重装全部Python包 - 用多阶段构建(
FROM python:3.11 AS builder)分离构建环境与运行环境,最终镜像里不带gcc、pip等编译工具 - 检查
docker build --no-cache是否真能复现问题;若能,说明是缓存掩盖了真实错误(比如网络超时、源地址失效)
CI流程里镜像推不上去 registry?先确认认证和命名规范
CI服务器(如GitLab Runner、Jenkins Agent)默认没有Docker守护进程权限,且私有registry往往要求显式登录+严格命名。常见报错denied: requested access to the resource is denied或unauthorized: authentication required,90%不是权限问题,而是镜像tag没对上。
- 确保
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY在build前执行(注意:密码不能明文写死,必须用CI变量) - 镜像名必须含完整registry地址和命名空间,例如
registry.example.com/myteam/myapp:git-$CI_COMMIT_SHORT_SHA,不能只写myapp:latest - GitLab CI中
$CI_REGISTRY_IMAGE变量已预置好命名空间,推荐用$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG或$CI_REGISTRY_IMAGE:dev - 别用
latest作为生产部署tag——它无法追溯、不可重现、违反不可变交付原则
Kubernetes部署总连不上服务?问题大概率出在端口和环境变量传递
Docker容器本身不暴露端口,Kubernetes的Service也不自动转发任意端口。很多团队卡在“容器日志显示启动成功,但curl http://pod-ip:5000超时”,其实是没理解“容器端口声明”和“宿主机/Service端口映射”的两层关系。
-
EXPOSE 5000在Dockerfile里只是文档性声明,不影响实际网络行为;真正起作用的是Pod定义里的containers[].ports[].containerPort - 应用必须监听
0.0.0.0:5000(而非127.0.0.1:5000),否则K8s流量进不来 - 数据库地址、API网关URL这类配置,必须通过
env:字段注入,而不是写死在代码里;K8s不支持自动替换config.py中的字符串 - 如果应用依赖DNS解析服务(如连接
redis.default.svc.cluster.local),确保集群CoreDNS正常,且Pod的dnsPolicy没被覆盖为Default
最常被跳过的一步是:验证镜像在本地docker run能跑通再进CI。很多线上问题其实在docker run -it --rm -p 5000:5000 myimage这一步就能暴露——比如缺少libc、监听地址写错、健康检查路径不存在。别让CI替你做本该在本地完成的验证。










