2. Docker 基础回顾:镜像分层原理、容器生命周期与常用命令
在进入缓存服务的日志收集与错误排查之前,必须重新校准 Docker 的核心基础。镜像分层机制决定了缓存镜像的构建效率与存储占用;容器生命周期管理直接影响日志采集的时机与完整性;而 docker run、exec、logs、inspect 是排查过程中使用频率最高的四个命令。本节以“快速复习 + 工程对照”的方式完成基础对齐。
2.1 Docker 镜像分层原理
Docker 镜像由只读层(Layer)堆叠而成,每一层对应 Dockerfile 中的一条指令。层被缓存并复用,这是镜像构建加速和存储优化的核心。
关键概念: 层(Layer)是 Docker 镜像的最小存储单元,采用 UnionFS(联合文件系统)技术将多个只读层挂载为一个统一视图。写操作发生在容器层(可读写层),遵循“写时复制”(Copy-on-Write, CoW)策略。
| Dockerfile 指令 | 层行为 | 缓存复用条件 |
|---|---|---|
FROM |
拉取基础镜像的所有层 | 基础镜像名称与 digest 完全匹配 |
RUN |
执行命令,生成新层(文件系统快照) | 命令字符串与上下文文件 hash 一致 |
COPY / ADD |
将构建上下文文件复制为新层 | 文件内容 hash 未变化 |
CMD / ENTRYPOINT |
仅修改元数据,不产生新层 | 始终可缓存(元数据层) |
工程启示: 在缓存服务镜像中,将
RUN apt-get update && apt-get install -y 等频繁变动的指令放在 Dockerfile 靠后位置,利用层缓存加速构建。日志采集容器应挂载宿主机目录,避免写入容器可写层导致 IO 放大。
2.2 容器生命周期管理
容器从创建到销毁经历五个核心状态。理解状态转换是正确执行日志收集和错误排查的前提。
| 状态 | 含义 | 常见触发命令 | 日志可采集性 |
|---|---|---|---|
| Created | 容器已创建但未启动 | docker create |
无日志产生 |
| Running | 容器主进程正在运行 | docker start / docker run |
stdout/stderr 实时输出 |
| Paused | 进程被 SIGSTOP 挂起 | docker pause |
日志暂停输出 |
| Exited | 主进程已终止(正常或异常) | docker stop / 进程退出 |
历史日志仍可读取 |
| Removed | 容器元数据与可写层被删除 | docker rm |
日志永久丢失(除非挂载卷) |
排查陷阱: 使用
docker run --rm 启动的容器在退出后自动进入 Removed 状态。如果容器因错误快速崩溃,日志可能来不及采集。建议在调试阶段去掉 --rm 参数,或使用 docker logs <container_id> 在容器 Exited 状态下读取历史日志。
2.3 常用 Docker 命令快速复习
以下四个命令是缓存服务冒烟测试和日志排查中使用频率最高的操作。每个命令附带工程场景说明。
2.3.1 docker run — 创建并启动容器
# 标准启动命令(前台运行,绑定端口,挂载卷,设置环境变量)
docker run -d \
--name redis-cache \
-p 6379:6379 \
-v /data/redis:/data \
-e REDIS_PASSWORD=secret \
redis:7-alpine
# 参数说明
# -d 后台运行(detached)
# --name 指定容器名称,便于后续操作
# -p 宿主机端口:容器端口
# -v 挂载卷(宿主机路径:容器路径)
# -e 设置环境变量
冒烟场景: 启动缓存服务容器后,立即执行
docker logs -f <container_name> 观察启动日志,确认服务正常初始化。若容器启动后立即退出,使用 docker run --rm -it <image> sh 进入交互式 shell 手动启动进程排查。
2.3.2 docker exec — 在运行中的容器内执行命令
# 进入容器交互式 shell
docker exec -it redis-cache sh
# 在容器内执行单条命令并返回结果
docker exec redis-cache redis-cli ping
# 参数说明
# -i 交互模式(interactive)
# -t 分配伪终端(tty)
# 组合 -it 通常一起使用,进入交互式 shell
排查场景: 当缓存服务响应异常时,使用
docker exec 进入容器内部检查进程状态、配置文件、网络连通性,无需重启容器。注意:exec 只能在 Running 状态的容器上执行。
2.3.3 docker logs — 查看容器日志输出
# 查看全部日志
docker logs redis-cache
# 实时跟踪日志(类似 tail -f)
docker logs -f redis-cache
# 查看最近 100 行日志
docker logs --tail 100 redis-cache
# 带时间戳查看日志
docker logs -t redis-cache
# 参数说明
# -f 实时跟踪(follow)
# --tail 仅显示末尾 N 行
# -t 显示时间戳
日志采集限制:
docker logs 只能捕获容器内主进程(PID 1)写入 stdout 和 stderr 的日志。如果应用将日志写入文件(如 /var/log/app.log),docker logs 无法读取。此时必须通过 docker exec 查看文件,或配置日志驱动(如 json-file、syslog、fluentd)将文件日志重定向到 stdout。
2.3.4 docker inspect — 查看容器/镜像的详细元数据
# 查看容器完整元数据(JSON 格式)
docker inspect redis-cache
# 使用 Go 模板过滤特定字段
docker inspect -f '{{.State.Status}}' redis-cache
docker inspect -f '{{.NetworkSettings.IPAddress}}' redis-cache
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}' redis-cache
# 查看镜像层信息
docker inspect redis:7-alpine
| 排查场景 | inspect 过滤表达式 | 返回信息 |
|---|---|---|
| 检查容器是否正在运行 | {{.State.Status}} |
running / exited / paused |
| 获取容器 IP 地址 | {{.NetworkSettings.IPAddress}} |
如 172.17.0.2 |
| 查看挂载卷映射 | {{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}} |
宿主机路径 → 容器路径 |
| 查看容器退出码 | {{.State.ExitCode}} |
0 正常退出,非 0 异常 |
| 查看镜像层数 | {{len .RootFS.Layers}} |
如 6(表示 6 个只读层) |
工程最佳实践: 在自动化脚本中,优先使用
docker inspect -f 配合 Go 模板提取关键字段,避免解析完整 JSON。例如,在健康检查脚本中通过 docker inspect -f '{{.State.Health.Status}}' 获取容器健康状态。
2.4 命令组合排查流程
以下是一个典型的缓存服务启动失败排查流程,展示四个命令的协同使用:
- 启动容器:
docker run -d --name my-cache redis:7-alpine - 检查状态:
docker inspect -f '{{.State.Status}}' my-cache→ 返回exited - 查看退出码:
docker inspect -f '{{.State.ExitCode}}' my-cache→ 返回1 - 读取日志:
docker logs my-cache→ 显示Fatal: can't open config file - 进入容器排查:
docker run --rm -it -v $(pwd)/redis.conf:/usr/local/etc/redis/redis.conf redis:7-alpine sh→ 手动检查配置文件权限和路径
冒烟测试集成: 在 CI/CD 流水线中,将上述排查流程封装为脚本,在缓存服务部署后自动执行状态检查、日志采集和退出码验证,实现回归冒烟的自动化。