4. Docker进阶

Docker 基础命令用熟了,你可能会觉得——嗯,不就是跑个容器嘛。但真正到了生产环境,你会发现事情没那么简单。容器之间怎么通信?数据怎么持久化?多个服务怎么一起启动?日志丢了怎么办?

这些问题,就是今天要聊的核心。我刚开始接触 Docker 时,也在这几个坑里摔过。今天我把经验掰开揉碎,咱们一个一个过。

本章知识体系速览:网络模式决定容器间通信方式;数据卷解决持久化问题;Compose 编排多容器应用;日志管理让你能排查问题。四者缺一不可。

Docker 进阶四大核心模块 Docker 进阶 网络模式 数据卷管理 Compose 编排 日志管理 每个模块都对应一个生产环境中的实际问题

4.1 Docker 网络模式:容器间通信的基石

Docker 的网络模式,说白了就是决定容器怎么连上别人、怎么被别人连。我见过不少新手,容器跑起来了,但两个容器之间互相访问不了,急得团团转。其实搞懂几种模式就通了。

4.1.1 五种网络模式对比

模式 命令参数 特点 适用场景
bridge --network bridge 默认模式,容器通过虚拟网桥通信 单机多容器通信
host --network host 容器直接使用宿主机网络栈 性能敏感型应用
none --network none 无网络,完全隔离 安全沙箱、离线计算
container --network container:ID 共享另一个容器的网络栈 Sidecar 模式
overlay --network overlay 跨宿主机容器通信 Swarm 集群

我个人习惯,开发环境用 bridge 就够了。但如果你跑的是 Nginx 这类需要极致网络性能的服务,host 模式会更合适。为什么?因为 bridge 模式会经过 Docker 的 NAT 转发,有轻微性能损耗。

小技巧:使用 docker network create mynet 创建自定义 bridge 网络。自定义网络支持容器名直接解析,不用再记 IP 地址。我在项目中就用这个方式,让前端容器通过 http://backend:8080 直接访问后端,清爽得很。

4.1.2 实战:自定义网络下的容器互联

# 创建自定义网络
docker network create app-net

# 启动两个容器,加入同一网络
docker run -d --name web --network app-net nginx
docker run -d --name api --network app-net my-api:latest

# 在 web 容器中测试连通性
docker exec web curl http://api:8080/health

你看,就这么简单。容器名就是主机名,Docker 内置的 DNS 会自动解析。我曾经在排查一个微服务调用超时问题时,发现就是因为用了默认 bridge 网络,容器之间只能靠 IP 通信,IP 一变就断了。换成自定义网络后,问题迎刃而解。

4.2 Docker 数据卷管理:让数据不随容器消失

容器是无状态的——这句话你肯定听过。但数据库、日志文件、配置文件这些,总不能每次重启都丢吧?数据卷就是干这个的。

4.2.1 三种数据持久化方式

  • Volume(数据卷):由 Docker 管理,存放在 /var/lib/docker/volumes/。推荐使用。
  • Bind mount(绑定挂载):将宿主机目录直接挂载到容器。方便调试,但安全性稍差。
  • tmpfs mount(临时挂载):数据存在内存中,容器停止即消失。适合敏感数据。

注意:Bind mount 会覆盖容器内的目录。我曾经犯过一个错——把宿主机的空目录挂载到 /etc/nginx/conf.d,结果 Nginx 配置全没了,服务直接挂掉。所以挂载前一定要确认目录内容。

4.2.2 Volume 操作实战

# 创建数据卷
docker volume create mysql-data

# 启动 MySQL 并挂载数据卷
docker run -d \
  --name mysql \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  mysql:8.0

# 查看数据卷信息
docker volume inspect mysql-data

# 备份数据卷
docker run --rm -v mysql-data:/source -v $(pwd):/backup alpine tar czf /backup/mysql-backup.tar.gz -C /source .

备份那行命令,我解释一下:启动一个临时 Alpine 容器,挂载数据卷和当前目录,然后用 tar 打包。用完即删,干净利落。我在生产环境就用这个脚本做每日备份,从来没出过问题。

4.3 Docker Compose:一键拉起多容器应用

一个现代应用,通常包含前端、后端、数据库、缓存、消息队列……一个个手动启动?太累了。Compose 就是用来解决这个问题的。

4.3.1 docker-compose.yml 结构

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - api

  api:
    build: ./api
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - DB_PORT=3306
    depends_on:
      - db

  db:
    image: mysql:8.0
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root123

volumes:
  mysql-data:

这个文件定义了三个服务:web、api、db。注意 depends_on 字段,它控制启动顺序。但说实话,depends_on 只保证容器启动顺序,不保证服务就绪。比如 db 容器启动了,但 MySQL 还没完全初始化好,api 连上去就会报错。

我的做法:在 api 的启动脚本里加一个等待逻辑,循环检测数据库端口是否可达。或者用 dockerize 这类工具,等依赖服务就绪后再启动主进程。别问我怎么知道的——线上事故教会我的。

4.3.2 Compose 常用命令

命令 作用 说明
docker compose up -d 后台启动所有服务 最常用
docker compose down 停止并删除容器和网络 不会删除数据卷
docker compose logs -f 实时查看所有服务日志 调试利器
docker compose exec web sh 进入某个容器 类似 docker exec
docker compose build 重新构建镜像 代码变更后使用

4.4 Docker 日志管理:出了问题别抓瞎

容器跑起来了,服务也正常。突然有一天,用户反馈页面打不开。你第一件事是什么?看日志。但 Docker 的日志机制,跟传统方式不太一样。

4.4.1 日志驱动

Docker 默认使用 json-file 驱动,把 stdout 和 stderr 写到 JSON 文件里。但生产环境,我建议换成 journald 或直接对接外部日志系统。

# 查看当前日志驱动
docker info --format '{{.LoggingDriver}}'

# 启动容器时指定日志驱动
docker run -d \
  --log-driver journald \
  --log-opt tag="{{.Name}}/{{.ID}}" \
  nginx

# 限制日志文件大小和数量
docker run -d \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx

为什么要限制日志大小?我遇到过一台服务器因为容器日志写满磁盘,导致整个宿主机挂掉。从那以后,max-size 和 max-file 成了我启动容器的标配参数。

4.4.2 查看日志的实用技巧

# 查看最近 100 行
docker logs --tail 100 my-container

# 实时跟踪
docker logs -f my-container

# 按时间过滤
docker logs --since 2024-01-01T00:00:00 --until 2024-01-02T00:00:00 my-container

# 加上时间戳
docker logs -t my-container

核心原则:容器内应用不要把日志写到文件里,一律输出到 stdout/stderr。Docker 会自动捕获。如果你在容器里写日志文件,用 docker logs 是看不到的,排查问题会非常痛苦。

4.4.3 集中式日志方案

单机用 docker logs 还行,集群环境就不够用了。我常用的方案是 ELK 或 Loki + Grafana。简单说就是:

  • 容器日志输出到 stdout
  • Filebeat 或 Promtail 采集日志
  • 发送到 Elasticsearch 或 Loki
  • Kibana 或 Grafana 展示

这套方案我在多个项目里验证过,日均处理几 GB 日志毫无压力。如果你刚开始接触,可以先从 Loki + Promtail 入手,轻量级,跟 Docker 集成也方便。

避坑提醒:千万别把日志直接挂载到宿主机目录然后用 logrotate 轮转。Docker 的 json-file 驱动有自己的轮转机制,外部操作容易导致日志文件损坏。我曾经因为这个丢过半天日志,排查问题全靠猜。

好了,Docker 进阶的四个核心模块就聊到这儿。网络模式让你知道容器怎么连;数据卷让数据有了归宿;Compose 让多容器管理变得优雅;日志管理让你在出问题时能快速定位。这些技能,每一个都是生产环境的必修课。下次遇到容器通信失败、数据丢失、服务起不来、日志找不到——回头看看这章,应该能找到答案。