Docker 容器化实战踩坑:我被网络和存储卷坑了3次后终于搞懂了
Docker 看起来简单,但是里面的坑是真不少。网络、存储卷、权限、服务间通信、构建缓存——每一个都能让新手折腾半天。先跑通再优化:先用最简单的命令把服务跑起来,再慢慢加配置多用 Docker Compose:配置文件能记录所有参数,方便排查问题看日志:大部分问题都能在日志里找到答案了解原理:知道 Docker 网络、存储卷的原理,出了问题才能快速定位希望这篇文章能帮你少踩几个坑。如果还有其他 D
Docker 容器化实战踩坑:我被网络和存储卷坑了3次后终于搞懂了
说实话,我第一次用 Docker 部署项目的时候,心里想的是“这玩意儿不就是个轻量级虚拟机吗能有多难”。结果现实给了我3个响亮的耳光子——网络连不上、存储卷写不进去、容器间互相访问不了。整整折腾了两天,才终于把这些坑填平。今天把我的血泪教训分享出来,希望你不要再踩我踩过的坑。
踩坑现场一:Docker 网络连接失败,容器 ping 不了外网
问题描述
刚开始用 Docker,我信心满满地拉了一个 nginx 镜像启动:
docker run -d nginx
容器启动了,但是访问不了。ping 百度显示 bad address 'www.baidu.com',curl 也不行。我当时就蒙了——这也太不稳定了吧?
原因分析
Docker 安装时会创建一个默认的 bridge 网络(docker0),但是这个网络有时候会出问题。最常见的原因是:
- Docker 服务没有正常启动
- 防火墙规则阻止了 docker0 网桥
- DNS 配置被宿主机覆盖
解决方案
第一步,检查 Docker 服务状态:
# 重启 Docker 服务
sudo systemctl restart docker
# 检查 Docker 网络
docker network ls
docker network inspect bridge
第二步,如果服务正常但还是不通,手动配置 DNS:
# 在启动容器时指定 DNS
docker run -d --dns 8.8.8.8 nginx
# 或者修改 Docker 配置文件
sudo vi /etc/docker/daemon.json
添加以下内容:
{
"dns": ["8.8.8.8", "114.114.114.114"]
}
然后重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
血的教训:生产环境一定要提前配置好 DNS,不要用默认的。
踩坑现场二:存储卷权限被拒绝,容器内无法写入文件
问题描述
我想把宿主机的目录挂载到容器里,用 -v 参数:
docker run -v /data/myapp:/app/data myapp:latest
结果容器启动后,应用程序报错说没有权限写入 /app/data。我检查了宿主机的目录权限是 777,应该是所有人都能写的,怎么到容器里就不行了呢?
原因分析
这个问题坑了很多人。根本原因是 容器内用户的 UID/GID 可能和宿主机不同。比如宿主机目录属于用户 A(UID 1000),但容器内进程以 root(UID 0)或者其他用户运行,导致权限不匹配。
解决方案
方法一:指定容器内用户身份运行
# 以 root 用户运行(如果应用允许)
docker run -v /data/myapp:/app/data -u root myapp:latest
方法二:修改宿主机目录权限为 777(不推荐生产环境)
chmod -R 777 /data/myapp
方法三(推荐):在 Dockerfile 中创建与宿主机相同 UID 的用户
# 在 Dockerfile 中
RUN groupadd -g 1000 appgroup && \
useradd -r -u 1000 -g appgroup appuser
USER appuser
方法四(最推荐):使用 Docker Compose 并配置用户
version: '3.8'
services:
myapp:
image: myapp:latest
volumes:
- /data/myapp:/app/data
user: "1000:1000" # 指定 UID:GID
血的教训:挂载目录前,先确认容器内进程的用户身份,尽量保持 UID 一致。
踩坑现场三:容器内无法访问宿主机上的 MySQL/Redis
问题描述
我的应用是 Docker 跑的,但是数据库 MySQL 还在宿主机上运行。我配置了 localhost:3306 连接数据库,结果容器怎么都连不上。我还以为是数据库配置问题,折腾了大半天。
原因分析
在 Docker 容器内部,localhost 指的是容器自己,不是宿主机!容器内的 localhost 是一个独立的网络命名空间,和宿主机的 localhost 完全隔离。
解决方案
方法一:使用宿主机的内网 IP
# 先查看宿主机内网 IP
hostname -I | awk '{print $1}'
# 然后在容器中使用这个 IP
docker run -e DB_HOST=192.168.1.100 myapp:latest
方法二(推荐):使用 Docker Compose 并配置 extra_hosts
version: '3.8'
services:
myapp:
image: myapp:latest
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DB_HOST=host.docker.internal
这样容器内就可以用 host.docker.internal 访问宿主机了。
方法三:如果是 Linux 系统,还可以使用 --network host 模式
docker run --network host myapp:latest
但是注意,--network host 模式下容器会共享宿主机的网络命名空间,不能再映射端口了。
血的教训:Docker 容器和宿主机是隔离的,localhost 在容器内指的是容器自己,想访问宿主机得用特殊地址。
踩坑现场四:Docker Compose 服务间互相访问不了
问题描述
我用 Docker Compose 起了两个服务:myapp 和 redis。配置了 links 或者同一个网络,但是 myapp 怎么都连不上 redis。服务名明明配置对了啊?
原因分析
Docker Compose 默认会创建一个网络,所有服务都在这个网络里,应该是能互相访问的。问题通常是:
- 服务名拼写错误
- 端口号写错
- 服务还没启动完就去连接
- 网络配置有问题
解决方案
第一步,检查服务是否在同一个网络:
docker network ls
docker network inspect myapp_default # 替换为你的网络名
第二步,检查服务是否能解析:
# 进入 myapp 容器测试
docker exec -it myapp_myapp_1 ping redis
# 或者用 nslookup
docker exec -it myapp_myapp_1 nslookup redis
第三步,检查 Docker Compose 配置:
version: '3.8'
services:
myapp:
build: .
networks:
- myapp_network
depends_on:
- redis # 确保 redis 先启动
redis:
image: redis:alpine
networks:
- myapp_network
networks:
myapp_network:
driver: bridge
关键是确保:
- 两个服务在同一个
networks下 - 使用
depends_on确保启动顺序 - 服务名要完全匹配(区分大小写)
血的教训:Docker Compose 的服务发现是基于 DNS 的,服务名就是域名,大小写敏感。
踩坑现场五:镜像构建缓存导致的问题
问题描述
我修改了代码,重新构建镜像后,容器里跑的还是旧代码!明明 Dockerfile 没变,咋回事?
原因分析
Docker 构建时会使用缓存来加速。但是有时候缓存会导致问题:
- 前面层没变,后面的层不会重新构建
- COPY 命令可能缓存了旧文件
- ADD 和 COPY 的上下文问题
解决方案
方法一:强制重新构建,不使用缓存
docker build --no-cache -t myapp:latest .
方法二:在 Dockerfile 中合理安排指令顺序
# 变化频繁的指令放后面
FROM node:18-alpine
# 变化少的指令放前面(利用缓存)
WORKDIR /app
COPY package*.json ./
RUN npm install
# 变化频繁的指令放后面
COPY . .
RUN npm run build
方法三:使用 BuildKit 构建(支持更好的缓存)
# 启用 BuildKit
export DOCKER_BUILDKIT=1
# 构建
docker build -t myapp:latest .
方法四:分别构建前后端镜像(如果是大项目)
# 先构建前端
docker build -t myapp-frontend:latest ./frontend
# 再构建后端
docker build -t myapp-backend:latest ./backend
血的教训:修改代码后构建,最好用 --no-cache 强制重新构建,或者合理安排 Dockerfile 指令顺序。
写在最后
Docker 看起来简单,但是里面的坑是真不少。网络、存储卷、权限、服务间通信、构建缓存——每一个都能让新手折腾半天。
我的建议是:
- 先跑通再优化:先用最简单的命令把服务跑起来,再慢慢加配置
- 多用 Docker Compose:配置文件能记录所有参数,方便排查问题
- 看日志:大部分问题都能在日志里找到答案
- 了解原理:知道 Docker 网络、存储卷的原理,出了问题才能快速定位
希望这篇文章能帮你少踩几个坑。如果还有其他 Docker 相关的问题,欢迎评论区交流。
相关文章推荐:
更多推荐
所有评论(0)