Docker 容器化实战踩坑:我被这些问题坑了3次后总结的保命指南
Docker 确实是现代开发和运维的神器,但它也不是万能的。该踩的坑一个都跑不了。权限问题优先排查,容器内外用户要对齐数据持久化是底线,没挂载数据卷就等着哭网络配置要提前规划,别等部署时抓瞎镜像优化要从第一天做起,别等体积爆炸才后悔希望这份踩坑指南能帮你少走弯路。Docker 官方文档:https://docs.docker.com/Docker Compose 最佳实践Kubernetes 入门
Docker 容器化实战踩坑:我被这些问题坑了3次后总结的保命指南
说实话,我第一次用 Docker 的时候,完全是被"一键部署"的宣传给骗进来的。什么"构建一次,到处运行",听起来挺美。结果呢?现实给了我三记响亮的耳光——容器起不来、权限被拒绝、数据全丢光。今天就把这些坑踩了个遍,总结成这份保命指南。
坑一:容器起不来,exit code 127 是个什么鬼?
问题现场
我兴冲冲地写好了 Dockerfile,构建镜像后运行:
docker build -t myapp .
docker run myapp
结果输出:
bash: myapp: command not found
exit code 127
原因分析
exit code 127 表示"命令找不到"。问题出在 Dockerfile 的 CMD 指令:
# 错误写法
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3
CMD myapp # 容器里根本没有这个命令!
容器内的环境和你本地完全不同,你宿主机上的可执行文件路径在容器里根本不存在。
解决方案
方法一:使用完整路径
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3
WORKDIR /app
COPY . .
CMD ["/usr/bin/python3", "app.py"] # 使用完整路径
方法二:在容器内安装你的应用
# 推荐:构建时就把应用装进去
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
方法三:使用 ENTRYPOINT + ARG
FROM python:3.9
WORKDIR /app
COPY . .
ENTRYPOINT ["python"]
CMD ["app.py"]
教训
容器不是虚拟机,它只认容器内的路径。写 CMD 之前,先想想这个命令在容器里能不能找到。
坑二:Permission Denied?Docker 权限拒绝的崩溃现场
问题现场
我在容器里挂载了一个本地目录,准备读写文件:
docker run -v /home/data:/app/data myapp
结果:
PermissionError: [Errno 13] Permission denied: '/app/data/log.txt'
原因分析
Docker 容器的默认运行用户是 root(或者在 Kubernetes 里是特定用户),但宿主机目录可能是普通用户创建的。容器内 root 写不进去?这不太对。
更常见的情况是:SElinux 或 AppArmor 限制了容器的操作。
解决方案
方法一:关闭 SElinux(临时)
# 临时禁用
setenforce 0
# 永久禁用(编辑 /etc/selinux/config)
SELINUX=disabled
方法二:使用 😒 或 :Z 挂载参数
# :z - 多个容器共享
# :Z - 私有(每个容器独立)
docker run -v /home/data:/app/data:Z myapp
方法三:运行时指定用户
# 创建普通用户并授权
docker run -u 1000:1000 -v /home/data:/app/data myapp
方法四:Dockerfile 中创建用户
FROM ubuntu:20.04
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
RUN chown -R appuser:appgroup /app
USER appuser
COPY --chown=appuser:appgroup . .
CMD ["python", "app.py"]
教训
容器权限不是小事,生产环境千万不要用 root。创建专用用户,文件权限要对齐。
坑三:重启容器后数据全没了?数据卷挂载的致命错误
问题现场
我部署了一个 MySQL 容器,跑得好好的:
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8
某天服务器重启,我重新启动容器:
docker start mysql
结果:数据库是空的,所有数据都没了!
原因分析
问题在于:我根本没有挂载数据卷!容器内的数据存储在 /var/lib/mysql,但这只是容器内部的文件系统。容器一删,数据就没。
# 错误:没有 -v 挂载
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8
# 正确:载宿挂主机目录
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /home/mysql/data:/var/lib/mysql \
mysql:8
解决方案
方法一:宿主机目录挂载
# 创建数据目录
mkdir -p /home/mysql/data
# 启动时挂载
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=mydb \
-v /home/mysql/data:/var/lib/mysql \
mysql:8
方法二:使用 Docker Volume
# 创建数据卷
docker volume create mysql_data
# 启动时挂载
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql_data:/var/lib/mysql \
mysql:8
方法三:docker-compose 一键部署
version: '3.8'
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: mydb
volumes:
- mysql_data:/var/lib/mysql
restart: always
volumes:
mysql_data:
教训
重要的事情说三遍:生产环境一定要挂载数据卷!数据卷!数据卷! 没有持久化,容器就是一次性用品。
坑四:端口冲突?容器网络配置的连环坑
问题现场
我启动了两个 Nginx 容器:
docker run -d -p 80:80 --name web1 nginx
docker run -d -p 80:80 --name web2 nginx
第二个容器启动失败:
Error response from daemon: driver failed programming external connectivity on endpoint web2: bind: address already in use
原因分析
端口被占用了。同一个宿主机上,两个容器不能同时绑定同一个端口。
解决方案
方法一:使用不同宿主机端口
docker run -d -p 80:80 --name web1 nginx
docker run -d -p 8080:80 --name web2 nginx # 映射到 8080
方法二:使用 docker-compose 自动分配
version: '3.8'
services:
web1:
image: nginx
ports:
- "80:80"
web2:
image: nginx
ports:
- "8080:80"
方法三:使用 --network 搭建内部网络
# 创建网络
docker network create mynet
# 启动容器加入网络
docker run -d --network mynet --name web1 nginx
docker run -d --network mynet --name web2 nginx
# 容器间通过容器名通信
docker exec web1 curl http://web2
方法四:Docker 内置网络模式
# host 模式(不推荐,会冲突)
docker run -d --network host nginx
# bridge 模式(默认)
docker run -d nginx
# none 模式(无网络)
docker run -d --network none nginx
教训
端口规划要提前做好,生产环境建议用 docker-compose 统一管理网络。
坑五:镜像太大?构建优化的实战技巧
问题现场
我构建了一个 Node.js 镜像,体积居然 1.2GB:
docker images
REPOSITORY TAG SIZE
myapp latest 1.2GB
这要是部署到服务器,下载镜像就能把人等疯。
原因分析
- 基础镜像太大(用了个 ubuntu 全家桶)
- 把 node_modules、.git 等无关文件一起 COPY 进去了
- 没有清理构建缓存和临时文件
解决方案
方法一:使用 alpine 精简镜像
# 原始镜像
# FROM node:18 # 900MB+
# 优化后
FROM node:18-alpine # 170MB
方法二:使用多阶段构建
# 第一阶段:构建
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:运行(只复制必要文件)
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
方法三:.dockerignore 排除无关文件
# .dockerignore
node_modules
.git
.DS_Store
*.log
.env
.vscode
方法四:按需安装依赖
FROM python:3.9-slim
WORKDIR /app
# 只复制依赖文件
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再复制源码
COPY . .
CMD ["python", "app.py"]
最终效果
优化前:1.2GB
优化后:约 150MB
教训
镜像体积直接影响部署速度,能用 slim 就不用 full,能用 alpine 就不用 ubuntu。
写在最后
Docker 确实是现代开发和运维的神器,但它也不是万能的。该踩的坑一个都跑不了。
我的经验是:
- 权限问题优先排查,容器内外用户要对齐
- 数据持久化是底线,没挂载数据卷就等着哭
- 网络配置要提前规划,别等部署时抓瞎
- 镜像优化要从第一天做起,别等体积爆炸才后悔
希望这份踩坑指南能帮你少走弯路。如果觉得有用,点个赞再走?
推荐阅读:
- Docker 官方文档:https://docs.docker.com/
- Docker Compose 最佳实践
- Kubernetes 入门实战(进阶必学)
更多推荐
所有评论(0)