【Docker/K8s踩坑】8个让我差点放弃容器的坑和完整解决方案
"Docker和K8s是现代后端工程师标配,但自学/上手时踩的坑比代码还多。这篇文章总结了我踩过的8个实战坑,附完整解决方案,帮你绕开弯路。"
Docker和K8s是现代后端工程师标配,
但自学/上手时踩的坑比代码还多。
这篇文章总结了我踩过的8个实战坑,
附完整解决方案,帮你绕开弯路。
一、为什么要学Docker和K8s?
先说个真实故事:
一个后端应届生入职,发现公司生产环境全是Docker容器。
简历上写着"熟练使用Docker",结果:
- 不会写Dockerfile
- 不会排查容器网络问题
- 不知道什么叫数据持久化
- K8s?完全没碰过
>
第一周就踩坑踩到怀疑人生。
这不是个案。Docker和K8s是现代后端工程师的标配,但学校不教,工作又必须会。
这篇文章总结了我踩过的8个实战坑,每个坑都有完整解决方案。
二、第一部分:Docker常见8个坑
坑①:Dockerfile写成了"万能镜像"
错误示范:
FROM ubuntu
RUN apt-get update
RUN apt-get install python
RUN pip install flask
COPY app.py /
CMD python app.py
问题在哪?
- 镜像体积巨大(Ubuntu基础镜像几百MB起步)
- 层层构建缓存失效,每次改动都要重装所有依赖
- 系统库和Python混在一起,依赖管理混乱
正确写法(多阶段构建):
# 第一阶段:构建
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt
# 第二阶段:运行(只复制产物)
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /install /usr/local/lib/python3.11/site-packages
COPY app.py .
CMD ["python", "app.py"]
效果:** 镜像从700MB降到120MB,构建速度提升3倍。
坑②:容器里的数据,删库就没了
场景:
MySQL跑在Docker容器里,跑了半年数据100GB,有一天容器崩溃重启,数据全丢了。
原因:** 容器默认使用**可写层**存储数据,容器删除数据就没了。
解决方案:使用数据卷(Volume)
# 创建数据卷
docker volume create mysql_data
# 启动容器时挂载数据卷
docker run -d \
--name mysql \
-v mysql_data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0
生产环境推荐:使用bind mount(绑定宿主机目录)
docker run -d \
--name mysql \
-v /data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0
重要:** 无论哪种方式,定期备份!
坑③:容器内时区乱8个小
场景:
日志时间对不上,数据库插入时间是UTC,代码里用的是北京时间,差了8小时。
根因:** 容器默认使用UTC时区,和中国时间差8小时。
解决方案:
FROM python:3.11-slim
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
或者启动容器时:
docker run -e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
myapp
坑④:容器无法互相访问(网络不通)
场景:
Nginx容器无法访问后端Python容器,curl报错"Connection refused"。
排查步骤:
# 1. 查看容器网络
docker network ls
# 2. 查看容器IP
docker inspect myapp | grep IPAddress
# 3. 进入容器内部测试
docker exec -it nginx ping myapp
# 4. 检查端口映射
docker port myapp
根因:** 容器没有加入同一个网络。
解决方案:创建自定义网络
# 创建网络
docker network create mynet
# 启动容器时加入网络
docker run -d --name backend --network mynet myapp
docker run -d --name nginx --network mynet nginx
# 现在nginx可以通过容器名访问backend
# nginx.conf里写:proxy_pass http://backend:8000;
核心原则:** 用自定义bridge网络,不用默认bridge。
默认bridge网络里容器只能用IP互相访问,不能用名字。
坑⑤:权限问题(Permission Denied)
场景:
代码里写文件到/app/logs/app.log,容器里报错"Permission denied"。
根因:
- Linux普通用户创建的容器内也是root
- 但某些目录挂载了宿主机目录,宿主机目录是普通用户创建的
- 权限不匹配
解决方案A:指定用户
RUN useradd -m appuser
USER appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
解决方案B:挂载时指定权限
# 挂载目录设置成777
chmod 777 /host/logs
docker run -v /host/logs:/app/logs myapp
三、第二部分:K8s(Kubernetes)常见8个坑
坑⑥:Pod反复重启(CrashLoopBackOff)
场景:
Pod刚启动就崩溃,然后重启,反复循环。
排查:
# 查看Pod状态和重启次数
kubectl get pod myapp
kubectl describe pod myapp
kubectl logs myapp --previous # 看上一个容器的日志
常见原因和解决方案:
一个常见坑:Liveness探针设置太激进
# 错误:应用启动要30秒,探针每5秒检测一次,3次失败就重启
livenessProbe:
initialDelaySeconds: 0 # 还没启动完就开始检测
periodSeconds: 5
failureThreshold: 3
# 正确:给应用足够启动时间
livenessProbe:
initialDelaySeconds: 30 # 等30秒再开始检测
periodSeconds: 10
failureThreshold: 3
坑⑦:Service访问不到(一直pending或全部endpoints失败)
场景:
创建了Service,但Pod就是访问不了,一直timeout。
排查步骤:
# 1. 确认Pod和Service在同一个namespace
kubectl get svc -A | grep myapp
kubectl get pod -A | grep myapp
# 2. 查看Service的endpoints(有没有Pod被选中)
kubectl get endpoints myapp-svc
kubectl describe svc myapp-svc | grep Selector
kubectl get pod --show-labels | grep myapp
# Service的selector
selector:
app: MyApp # 注意大小写!
labels:
app: myapp # 小写!
selector:
app: myapp # 统一小写
坑⑧:ConfigMap/Secret更新了,Pod没生效
场景:
修改了ConfigMap,但应用读取的还是旧值,重启Pod才能生效。
原因:** ConfigMap挂载为卷时,文件内容是静态挂载的,不会自动更新。
解决方案:
方案A:滚动更新Pod(推荐)
kubectl rollout restart deployment myapp
方案B:使用环境变量(支持动态更新,但需应用支持)
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: myapp-config
key: db_host
方案C:启用ConfigMap卷热更新(K8s 1.19+,需设置subPath)
# ConfigMap要加版本注解触发更新
annotations:
version: "20260101"
注意:subPath会阻止热更新,尽量不用subPath。
四、Docker/K8s自查清单
Docker部分:
- [ ] 镜像用了多阶段构建,体积最小化?
- [ ] 数据目录挂载了Volume/bind mount?
- [ ] 时区设置正确(Asia/Shanghai)?
- [ ] 容器在同一个自定义网络里?
- [ ] 权限问题处理了?
K8s部分:
- [ ] Pod反复重启?先`kubectl logs --previous`
- [ ] ConfigMap改了应用没生效?需要滚动更新Pod
- [ ] Liveness探针的initialDelaySeconds够长吗?
五、总结
记住:Docker和K8s的坑,踩过才知道疼。提前知道这些,能省你好几个深夜。
你踩过哪些Docker或K8s的坑?评论区说说,帮大家避坑。
*转发给正在配环境的后端同学,特别是刚入职要接手Docker/K8s项目的。*
更多推荐



所有评论(0)