"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项目的。*

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐