3、Docker进阶:Docker Compose多容器编排、Docker网络与存储、镜像仓库搭建
好,咱们继续往前走。上一章我们把 Docker 的基础操作捋了一遍,你大概已经能跑个单容器应用了。但说实话,真实项目里哪有只跑一个容器的?
我刚开始用 Docker 的时候,就犯过这个错——把所有东西塞进一个容器里。数据库、后端、前端、Redis,全挤在一起。结果呢?想升级个 Redis 版本,得把整个容器停了重建。那叫一个痛苦。
所以这一章,咱们聊聊真正的「实战姿势」:多容器怎么编排、网络怎么打通、数据怎么持久化,还有镜像仓库怎么自己搭。
3.1 Docker Compose:一键拉起整个应用栈
你想想看,一个典型的 Web 应用,至少需要:
- 一个 Nginx 做反向代理
- 一个后端服务(比如 Node.js 或 Go)
- 一个数据库(MySQL 或 PostgreSQL)
- 一个缓存(Redis)
手动一个个 docker run?那得敲多少命令。而且每次启动都要记住端口映射、挂载卷、网络配置……我记性可没那么好。
Docker Compose 就是来解决这个问题的。
它用一个 YAML 文件描述整个应用栈。一个 docker-compose up,全部搞定。
3.1.1 一个真实的 docker-compose.yml
来看个例子。这是我之前一个项目的简化版:
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- backend
backend:
build: ./backend
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: myapp
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7-alpine
volumes:
mysql_data:
嗯,这里要注意几个关键点:
- depends_on:控制启动顺序。但说实话,它只保证容器启动顺序,不保证服务就绪。我踩过这个坑——后端启动时数据库还没准备好,连接失败。解决方案是加个
wait-for-it.sh脚本。 - volumes:命名卷
mysql_data用来持久化数据库文件。容器删了数据还在。 - environment:环境变量传配置。千万别把密码写死在代码里。
.env 文件,用 env_file 引入。这样既安全又清晰。
3.1.2 常用 Compose 命令
| 命令 | 作用 | 备注 |
|---|---|---|
docker-compose up -d |
后台启动所有服务 | 最常用 |
docker-compose down |
停止并删除容器、网络 | 不会删卷 |
docker-compose logs -f |
实时查看日志 | 调试利器 |
docker-compose exec backend sh |
进入容器内部 | 排查问题用 |
我曾经在生产环境排查问题时,全靠 docker-compose logs -f 实时追踪日志。比 SSH 进服务器一个个翻日志文件快多了。
3.2 Docker 网络:容器间怎么通信?
你可能会问:上面那个 Compose 文件里,backend 怎么就能直接访问 mysql 和 redis 这个主机名?
答案就是 Docker 网络。
Docker Compose 会自动创建一个默认网络,所有服务都加入这个网络。Docker 内置的 DNS 解析会把服务名解析成容器 IP。说白了,就是容器间可以通过服务名直接通信。
3.2.1 三种网络模式
- bridge(默认):单机容器互联。适合开发环境。
- host:容器直接使用宿主机网络。性能好,但隔离性差。
- overlay:跨主机通信。Swarm 模式下用,生产环境常见。
我个人习惯,开发用 bridge,生产用 overlay。别图省事用 host 模式,安全风险太大。
network_mode: "service:xxx"。别问我怎么知道的——那次联调搞了一下午。
3.2.2 自定义网络
有时候你需要更精细的控制。比如让前端服务在一个网络,后端和数据库在另一个网络:
networks:
frontend:
backend:
services:
nginx:
networks:
- frontend
backend:
networks:
- frontend
- backend
mysql:
networks:
- backend
这样 Nginx 只能访问 backend,不能直接访问数据库。安全多了。
3.3 Docker 存储:数据不能丢
容器是「无状态」的——这话你肯定听过。但数据库呢?用户上传的文件呢?日志呢?这些必须有地方存。
Docker 提供了三种存储方式:
3.3.1 三种存储方式对比
| 方式 | 特点 | 适用场景 |
|---|---|---|
| Volume(卷) | 由 Docker 管理,存在宿主机特定目录 | 数据库、持久化数据 |
| Bind Mount(绑定挂载) | 直接映射宿主机目录 | 开发时热重载、配置文件 |
| tmpfs | 存在内存中,容器停止即消失 | 敏感信息、临时缓存 |
我个人的建议:生产环境优先用 Volume。Bind Mount 虽然方便,但容易搞乱宿主机目录权限。我见过有人把宿主机根目录挂进去的……嗯,后果很严重。
3.3.2 实战:MySQL 数据持久化
回到刚才的 Compose 文件。MySQL 的数据卷是这样配置的:
volumes:
mysql_data:/var/lib/mysql
这个 mysql_data 是命名卷。你可以用 docker volume inspect mysql_data 查看它在宿主机上的实际路径。但说实话,你不需要关心它具体在哪——Docker 会管好。
docker volume ls 确认一下。
3.4 镜像仓库:自己搭一个私服
镜像从哪来?Docker Hub 当然方便,但企业里谁敢把私有代码推上去?
自己搭一个私有仓库,是云原生架构的必修课。
3.4.1 用 Registry 搭建私有仓库
Docker 官方提供了 registry 镜像,一行命令就能跑起来:
docker run -d -p 5000:5000 --name registry registry:2
然后你就可以推送镜像了:
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest
拉取也一样:
docker pull localhost:5000/myapp:latest
就这么简单。但生产环境肯定不能这么裸奔。
3.4.2 生产级配置:HTTPS + 认证
我建议至少做三件事:
- 配置 HTTPS:用 Nginx 反向代理加 Let's Encrypt 证书。
- 添加认证:用 htpasswd 生成账号密码。
- 限制存储:设置
REGISTRY_STORAGE_DELETE_ENABLED: true,定期清理旧镜像。
一个完整的 docker-compose 配置大概长这样:
version: '3.8'
services:
registry:
image: registry:2
volumes:
- registry_data:/var/lib/registry
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_STORAGE_DELETE_ENABLED: "true"
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- registry
volumes:
registry_data:
insecure-registries。我建议别省这个功夫——HTTPS 是底线。
3.4.3 Harbor:企业级镜像仓库
如果 Registry 太简陋,可以试试 Harbor。它提供了:
- Web UI 管理界面
- 镜像复制(跨机房同步)
- 漏洞扫描
- RBAC 权限控制
我目前在用的就是 Harbor。说实话,小团队用 Registry 就够了。但如果你要管几十个团队、上千个镜像,Harbor 是更好的选择。
3.5 本章小结
这一章我们聊了:
- 用 Docker Compose 编排多容器应用
- Docker 网络的三种模式及自定义网络
- Volume、Bind Mount、tmpfs 三种存储方式
- 搭建私有镜像仓库(Registry 和 Harbor)
下一章,我们会把这些知识串起来,搭建一个完整的 CI/CD 流水线。到时候你会发现,前面学的每一块都有用。
嗯,今天就到这。有什么问题,欢迎在评论区交流。