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 怎么就能直接访问 mysqlredis 这个主机名?

答案就是 Docker 网络

Docker Compose 会自动创建一个默认网络,所有服务都加入这个网络。Docker 内置的 DNS 解析会把服务名解析成容器 IP。说白了,就是容器间可以通过服务名直接通信。

3.2.1 三种网络模式

  • bridge(默认):单机容器互联。适合开发环境。
  • host:容器直接使用宿主机网络。性能好,但隔离性差。
  • overlay:跨主机通信。Swarm 模式下用,生产环境常见。

我个人习惯,开发用 bridge,生产用 overlay。别图省事用 host 模式,安全风险太大。

我曾经踩过的坑:两个 Compose 项目默认网络是隔离的。想让它们通信?要么手动指定同一个外部网络,要么用 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 会管好。

避坑指南:我曾经在升级 MySQL 镜像时,忘了指定卷。新容器启动后,数据全没了。还好有备份……从那以后,我每次操作前都会先 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 + 认证

我建议至少做三件事:

  1. 配置 HTTPS:用 Nginx 反向代理加 Let's Encrypt 证书。
  2. 添加认证:用 htpasswd 生成账号密码。
  3. 限制存储:设置 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:
注意:Docker 默认只信任 HTTPS 仓库。如果你用 HTTP,需要在每个客户端配置 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 流水线。到时候你会发现,前面学的每一块都有用。

嗯,今天就到这。有什么问题,欢迎在评论区交流。