2. Docker 基础回顾:Docker 镜像与容器、Dockerfile 编写、Docker Compose 入门

在微服务架构的故障排查与日志分析中,Docker 是底层运行环境的核心。理解 Docker 的三大基础概念——镜像、容器与编排——是后续进行容器级监控、日志采集和故障定位的前提。本节将系统回顾 Docker 镜像与容器的生命周期、Dockerfile 的最佳编写实践,以及 Docker Compose 在微服务环境中的入门用法。

2.1 Docker 镜像与容器:核心概念与生命周期

Docker 镜像是一个只读的、分层构建的模板,包含了运行某个应用所需的完整文件系统、依赖库、环境变量和启动命令。容器则是镜像的一个可写、可运行的实例。镜像与容器的关系类似于面向对象编程中的“类”与“对象”。

概念 特性 生命周期 典型操作
镜像 (Image) 只读、分层、可分发 构建 → 存储 → 拉取/推送 → 删除 docker build, docker pull, docker push, docker rmi
容器 (Container) 可写层、隔离、可运行 创建 → 启动 → 运行 → 停止 → 删除 docker run, docker start, docker stop, docker rm
故障排查要点: 当容器日志丢失或应用异常退出时,优先检查容器状态(docker ps -a)和镜像层缓存(docker history)。容器停止后,其可写层默认保留,但若使用 --rm 参数,退出后容器会被自动删除,日志文件也会随之消失。

下图展示了镜像分层构建与容器实例化的核心流程:

Docker 镜像分层与容器实例化流程 基础镜像层 (OS) 依赖层 (Libs) 应用层 (App) 配置层 (Config) 只读镜像层 docker run 基础镜像层 (OS) 依赖层 (Libs) 应用层 (App) 配置层 (Config) 可写容器层 运行时读写层 docker commit 基础镜像层 (OS) 依赖层 (Libs) 应用层 (App) 配置层 (Config) 新可写层 新镜像只读层

2.2 Dockerfile 编写:从基础到生产级实践

Dockerfile 是定义镜像构建过程的脚本。在微服务架构中,每个服务通常对应一个 Dockerfile。编写 Dockerfile 时,需要关注镜像体积、构建缓存、安全性和可重复性。

2.2.1 核心指令速查表

指令 作用 最佳实践
FROM 指定基础镜像 优先使用官方 Alpine 或 slim 版本,减少攻击面
RUN 执行命令并创建新层 合并多个 RUN 命令,减少层数;使用 && 连接
COPY / ADD 将文件从上下文复制到镜像 优先使用 COPY;ADD 仅用于自动解压 tar 或远程 URL
WORKDIR 设置工作目录 始终使用绝对路径,避免 cd 命令
EXPOSE 声明容器监听端口 仅作为文档用途,实际端口映射在 docker run -p 中指定
CMD / ENTRYPOINT 指定容器启动命令 ENTRYPOINT 用于固定执行程序,CMD 提供默认参数

2.2.2 生产级 Dockerfile 示例(Java Spring Boot 微服务)

# 1. 多阶段构建:编译阶段
FROM maven:3.8.6-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests

# 2. 运行阶段:使用最小化 JRE 镜像
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 从 builder 阶段复制构建产物
COPY --from=builder /app/target/*.jar app.jar
# 非 root 用户运行,提升安全性
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
故障排查提示: 如果容器启动后立即退出,检查 ENTRYPOINT 或 CMD 中的命令是否在前台运行。例如,java -jar 默认在前台运行,但若使用 nohup 或 & 将进程放入后台,容器会立即退出。日志采集时,确保应用日志输出到 stdout/stderr,而非文件。

2.3 Docker Compose 入门:微服务编排基础

Docker Compose 通过一个 docker-compose.yml 文件定义和运行多个容器。在微服务架构中,Compose 常用于本地开发环境、CI/CD 测试环境以及小型生产部署。它解决了“手动启动多个容器并配置网络、卷、环境变量”的痛点。

2.3.1 Compose 文件核心结构

version: '3.8'

services:
  # 服务 A:用户服务
  user-service:
    build: ./user-service
    ports:
      - "8081:8080"
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
    depends_on:
      - postgres
    networks:
      - backend

  # 服务 B:订单服务
  order-service:
    build: ./order-service
    ports:
      - "8082:8080"
    environment:
      - REDIS_HOST=redis
    depends_on:
      - redis
    networks:
      - backend

  # 基础设施:PostgreSQL
  postgres:
    image: postgres:15-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: microservices
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
    networks:
      - backend

  # 基础设施:Redis
  redis:
    image: redis:7-alpine
    networks:
      - backend

volumes:
  pgdata:

networks:
  backend:
    driver: bridge

2.3.2 常用 Compose 命令与故障排查场景

命令 用途 故障排查场景
docker-compose up -d 后台启动所有服务 服务启动顺序异常时,检查 depends_on 配置
docker-compose logs -f 实时查看所有服务日志 定位哪个服务启动失败或抛出异常
docker-compose ps 查看服务状态 确认容器是否处于 Up 或 Exit 状态
docker-compose exec <service> sh 进入运行中的容器 手动检查配置文件、环境变量或网络连通性
docker-compose down -v 停止并删除容器、网络、卷 清理环境后重新部署,解决数据或网络残留问题
常见陷阱: Compose 中的 depends_on 仅控制容器启动顺序,不等待服务内部就绪。例如,user-service 依赖 postgres,但 PostgreSQL 可能尚未完成初始化。建议在应用代码中添加重试机制,或使用 healthcheck 指令确保依赖服务真正可用。

下图展示了使用 Docker Compose 编排的典型微服务拓扑结构:

Docker Compose 微服务拓扑示例 API 网关 (nginx) 用户服务 订单服务 库存服务 PostgreSQL Redis MySQL

2.4 本章小结