Docker 容器化技术完整指南

Docker 学习笔记 -- 从入门到生产实践

分类:容器化技术 / DevOps

核心主题:Docker 容器化技术从基础到高级的全面指南

主要内容:全面系统地讲解 Docker 的安装配置、镜像管理、容器操作、Dockerfile 编写、Docker Compose 编排、网络配置、数据卷管理、安全加固、CI/CD 集成以及生产环境部署最佳实践。

关键词:Docker, 容器, 镜像, Dockerfile, Docker Compose, 数据卷, 网络, DevOps, CI/CD, 微服务

关联笔记:Claude Code CLI 完整参考手册(配合 Docker 部署使用)

一、Docker 基础概念

1.1 什么是 Docker

Docker 是一个开源的容器化平台,它允许开发者将应用程序及其所有依赖项打包到一个轻量级、可移植的容器中,然后在任何支持 Docker 的系统上运行。与传统的虚拟机不同,Docker 容器共享宿主操作系统的内核,无需为每个应用安装完整的操作系统,因此启动速度更快(毫秒级)、资源消耗更低、部署密度更高。

Docker 于 2013 年由 Docker 公司推出,基于 Linux 内核的 cgroups(控制组)和 Namespaces(命名空间)技术实现资源隔离和进程隔离。短短几年间,Docker 已成为云原生计算基金会(CNCF)的重要组成部分,彻底改变了软件开发、交付和部署的方式。

容器 vs 虚拟机

理解容器和虚拟机的区别是学习 Docker 的第一步。虚拟机通过 Hypervisor 虚拟化硬件,每个虚拟机包含完整的操作系统,因此体积庞大(通常数 GB)、启动缓慢(分钟级)。而容器仅打包应用及其依赖,共享宿主内核,体积小巧(通常数十 MB)、启动迅速(毫秒级)。

核心差异对比:虚拟机提供硬件级隔离,安全性更高,但资源开销大;容器提供进程级隔离,资源效率高,但隔离性相对较弱。在实际生产环境中,两者常常配合使用——虚拟机提供基础设施层的隔离,容器在虚拟机上运行应用。

1.2 Docker 核心架构

Docker 采用 C/S(客户端-服务器)架构,主要由以下组件组成:Docker 客户端(docker CLI)、Docker 守护进程(dockerd)、Docker 镜像(Image)、Docker 容器(Container)和 Docker 仓库(Registry)。客户端通过 REST API 与守护进程通信,守护进程负责管理镜像、容器、网络和数据卷等资源。

┌─────────────────────────────────────────────────┐
│ Docker 架构图 │
├─────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ Docker │ │ Docker Daemon │ │
│ │ Client │───→│ (dockerd) │ │
│ │ (CLI) │ │ ┌─────┐ ┌─────┐ │ │
│ └──────────┘ │ │Img1 │ │Img2 │ │ │
│ │ └─────┘ └─────┘ │ │
│ ┌──────────┐ │ ┌─────┐ ┌─────┐ │ │
│ │ Registry │────→│ │Ctn1 │ │Ctn2 │ │ │
│ │ (Hub) │ │ └─────┘ └─────┘ │ │
│ └──────────┘ │ ┌───Net────┐ │ │
│ │ │ Volumes │ │ │
│ │ └──────────┘ │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────┘

1.3 Docker 镜像与容器的关系

镜像是容器的模板,容器是镜像的运行实例。如果把镜像类比为面向对象编程中的"类",那么容器就是"对象实例"。镜像是只读的,容器在镜像之上添加一个可写层(Container Layer)。这种分层存储机制(Union File System)使得多个容器可以共享同一镜像,同时拥有各自独立的可写空间。

重要概念:镜像分层(Layer)是 Docker 的核心设计之一。每个 Dockerfile 指令都会创建一个新的镜像层,这些层是只读的且可被多个镜像共享。当拉取镜像时,已存在于本地的层不会被重复下载。这意味着如果你有多个基于 Ubuntu 的镜像,Ubuntu 基础层只会被下载和存储一次,极大地节省了磁盘空间和网络带宽。

二、安装与配置

2.1 各平台安装方式

Docker 支持主流操作系统,包括 Linux、macOS 和 Windows。在 Linux 上推荐通过包管理器安装 Docker Engine,在 macOS 和 Windows 上推荐安装 Docker Desktop,后者提供了一个包含 Docker Engine、Docker CLI、Docker Compose 和 Kubernetes 的完整开发环境。

# Ubuntu / Debian 安装 Docker Engine sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io # CentOS / RHEL 安装 Docker Engine sudo yum install -y yum-utils sudo yum-config-manager --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable docker # 验证安装 docker version docker info docker run hello-world

安装验证小技巧

安装完成后,运行 docker run hello-world 可以验证 Docker 是否正确安装并能够正常拉取和运行镜像。如果看到包含 "Hello from Docker!" 的消息,说明安装成功。这个命令会从 Docker Hub 拉取一个测试镜像并在容器中运行,是初学者验证环境的首选命令。

2.2 非 root 用户管理

默认情况下,docker 命令需要 root 权限或使用 sudo。为了避免每次执行 Docker 命令都加 sudo,可以将用户加入 docker 用户组。请注意,加入 docker 组等同于拥有 root 权限,因为它允许用户直接访问 Docker 守护进程。

# 将当前用户加入 docker 用户组 sudo usermod -aG docker $USER # 重新登录或执行以下命令使组变更生效 newgrp docker # 验证是否无需 sudo 即可运行 docker ps

安全警告

将用户添加到 docker 组等同于授予该用户无密码的 root 访问权限。这是因为用户可以挂载宿主机的任何目录、运行特权容器,从而获得主机的完全控制权。在生产服务器上,应避免将非管理员用户加入 docker 组。建议通过 Docker Context 和远程 API 进行权限管理,或使用 Rootless Docker 模式运行。

2.3 Docker 配置调优

Docker 守护进程的配置文件位于 /etc/docker/daemon.json(Linux)或 Docker Desktop 的设置界面中。合理配置可以显著提升性能、安全性和稳定性。以下是一些常用的配置项:镜像加速器(Registry Mirrors)可以加速从 Docker Hub 拉取镜像的速度;存储驱动(Storage Driver)的选择影响容器写入性能;日志驱动(Log Driver)配置控制容器日志的收集和管理。

{ // 配置镜像加速器(国内必配) "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com" ], // 存储驱动推荐使用 overlay2 "storage-driver": "overlay2", // 日志配置 - 限制容器日志大小 "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, // 限制同时运行的容器数量 "max-concurrent-downloads": 10, // 启用实验性功能 "experimental": true, // 配置 iptables 规则 "iptables": true, // 启用 live-restore(守护进程重启时保持容器运行) "live-restore": true }

镜像加速配置

在中国大陆地区,直接从 Docker Hub 拉取镜像速度缓慢甚至不可用。配置镜像加速器是解决这一问题的标准做法。国内常用的加速器包括阿里云(需注册获取专属地址)、中科大镜像、网易镜像等。配置后重启 Docker 服务即可生效:sudo systemctl restart docker

三、Docker 镜像管理

3.1 镜像基本操作

镜像是 Docker 的核心概念之一,它包含了运行应用所需的一切:代码、运行时、系统工具、库和设置。镜像管理主要涉及拉取(pull)、查看(ls/images)、删除(rmi)、标记(tag)和推送(push)等操作。Docker Hub 是官方的公共镜像仓库,包含了数以万计的官方和社区维护的镜像。

# 搜索镜像 docker search nginx docker search --filter=stars=100 ubuntu # 拉取镜像 docker pull nginx:latest docker pull ubuntu:22.04 docker pull python:3.11-slim # 列出本地镜像 docker images docker image ls docker image ls --filter reference="nginx*" # 查看镜像详情 docker inspect nginx:latest docker history nginx:latest # 查看镜像分层历史 # 标记镜像 docker tag nginx:latest myregistry.io/nginx:v1.0 # 删除镜像 docker rmi nginx:latest docker image prune # 删除悬空镜像(dangling images) docker image prune -a # 删除所有未使用的镜像

3.2 镜像分层与缓存机制

Docker 镜像由多个只读层叠加而成。每个 Dockerfile 指令生成一个层,层与层之间通过联合文件系统(OverlayFS、AUFS 等)组合。这种分层结构带来了重要的缓存优化:当构建镜像时,Docker 会尝试复用已有的层。如果某层对应的指令和上下文没有变化,Docker 会直接从缓存中读取该层,跳过重新执行。这一机制极大加速了镜像构建过程,特别是对于依赖安装等耗时操作。

分层缓存的核心规则:如果 Dockerfile 的某条指令发生了变化(或其输入文件发生了变化),该指令及之后所有指令的缓存都会失效。因此,优化 Dockerfile 的核心策略之一是将不常变化的指令(如安装系统依赖)放在前面,将频繁变化的指令(如复制源代码)放在后面。这样可以最大化利用缓存,显著缩短构建时间。

3.3 镜像仓库管理

除了官方 Docker Hub,你还可以使用私有仓库或第三方云厂商的镜像仓库来存储和管理镜像。Docker 支持多种镜像仓库,包括 Harbor、AWS ECR、Google Container Registry、阿里云容器镜像服务等。登录仓库后,你可以推送和拉取私有镜像,实现团队内部或企业内部共享。

# 登录 Docker Hub docker login docker login -u username # 登录私有仓库 docker login myregistry.example.com # 推送镜像到仓库 docker tag myapp:latest myregistry.example.com/myapp:v1.0 docker push myregistry.example.com/myapp:v1.0 # 从私有仓库拉取镜像 docker pull myregistry.example.com/myapp:v1.0 # 登出 docker logout

3.4 镜像导出与导入

在网络受限的环境中,Docker 镜像的导出和导入功能非常实用。你可以将镜像保存为 tar 文件,传输到目标机器后再加载。需要注意的是,docker save 保存的是完整的分层结构,而 docker export 导出的是容器的文件系统(扁平化,不含分层和历史信息)。

# 将镜像保存为 tar 文件 docker save -o nginx.tar nginx:latest # 压缩保存(推荐,节省空间) docker save nginx:latest | gzip > nginx.tar.gz # 加载镜像 docker load -i nginx.tar # 导出容器文件系统 docker export -o mycontainer.tar my_container # 导入为镜像 docker import mycontainer.tar myapp:imported

save vs export 的选择

如果需要完整保留镜像的标签、分层和历史信息(例如用于部署分发),使用 docker save。如果只需要获取容器的当前文件系统快照(例如用于迁移或备份),使用 docker export。注意:export 导出的文件无法通过 docker load 加载,必须使用 docker import,且导入后原有镜像的 CMD、ENTRYPOINT 等元信息会丢失。

四、Docker 容器管理

4.1 容器生命周期

容器在 Docker 中经历创建、启动、运行、停止、删除等阶段。理解容器的生命周期有助于正确管理容器的状态。容器的状态包括 created(已创建)、running(运行中)、paused(暂停中)、exited(已退出)和 dead(已死亡)。

# 创建并启动容器 docker run -d --name web nginx:latest docker run -it --name shell ubuntu:22.04 /bin/bash docker run --rm -p 8080:80 nginx:latest # 退出后自动删除 # 管理容器 docker ps # 列出运行中的容器 docker ps -a # 列出所有容器(包含已停止的) docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}" # 自定义输出格式 # 控制容器状态 docker start web docker stop web docker restart web docker pause web docker unpause web docker kill web # 强制停止 docker rm web # 删除容器 docker rm -f web # 强制删除运行中的容器 docker container prune # 删除所有已停止的容器 # 进入运行中的容器 docker exec -it web /bin/bash docker exec -it web sh # 适用于 Alpine 等基础镜像 docker attach web # 附加到容器主进程

关键命令说明:docker run 的常用选项非常丰富:-d 后台运行(detached)、-it 交互式终端、--rm 容器退出时自动删除、--name 指定容器名称、-p 端口映射(宿主机端口:容器端口)、-v 挂载数据卷、-e 设置环境变量、--network 指定网络模式、--restart 设置重启策略。正确组合这些选项可以应对大多数容器运行场景。

4.2 端口映射详解

Docker 容器拥有独立的网络命名空间,容器内部的服务默认只能通过容器 IP 访问。要让外部访问容器内的服务,需要将宿主机的端口映射到容器的端口。Docker 使用 iptables(Linux)或通过 Docker Desktop 的端口转发机制实现端口映射。可以同时映射多个端口,也可以映射到不同的宿主机端口。

# 端口映射的各种方式 docker run -p 80:80 nginx # 宿主机:容器端口映射 docker run -p 8080:80 nginx # 映射到宿主机的不同端口 docker run -p 127.0.0.1:8080:80 nginx # 绑定到特定 IP docker run -p 80:80/tcp -p 443:443/tcp nginx # 多端口映射 docker run -p 3000-3005:3000-3005 app # 端口范围映射 docker run -P nginx # 随机映射暴露端口(EXPOSE) # 查看端口映射 docker port web

端口冲突处理

当宿主机端口已被占用时,docker run 会报错退出。解决方法:①改用其他宿主机端口;②停止占用端口的进程或其他容器;③使用 Docker NAT 网络模式。推荐在启动容器前使用 ss -tlnp | grep :80netstat -tlnp | grep :80 检查端口占用情况。对于生产环境,建议使用专门的端口管理工具或服务发现机制。

4.3 容器日志管理

Docker 自动捕获容器的标准输出(stdout)和标准错误(stderr),并将其作为日志记录。日志驱动(Log Driver)决定了日志的存储和转发方式。默认的 json-file 驱动将日志以 JSON 格式存储在宿主机上,每个容器一个日志文件。在生产环境中,建议将日志转发到集中的日志系统(如 Elasticsearch、Splunk、Graylog 等)。

# 查看容器日志 docker logs web docker logs --tail 100 web # 只显示最后 100 行 docker logs -f web # 实时跟踪日志(类似 tail -f) docker logs --since 2024-01-01 web # 查看某个时间点之后的日志 docker logs -t web # 显示时间戳 # 使用不同的日志驱动 docker run --log-driver syslog --log-opt syslog-address=udp://192.168.1.1:514 myapp docker run --log-driver awslogs --log-opt awslogs-group=my-group myapp # 清理日志文件(直接清空,不删除容器) truncate -s 0 /var/lib/docker/containers/*/*-json.log

4.4 容器资源限制

默认情况下,Docker 容器可以无限制地使用宿主机的 CPU、内存和磁盘资源。为了防止单个容器耗尽系统资源,Docker 提供了多种资源限制机制。合理设置资源限制是生产环境中确保系统稳定性的重要手段。

# 限制内存使用 docker run -d --memory="512m" --memory-swap="1g" nginx docker run -d --memory="256m" --memory-reservation="128m" nginx # 限制 CPU 使用 docker run -d --cpus="1.5" nginx # 限制最多使用 1.5 个 CPU 核心 docker run -d --cpuset-cpus="0,2" nginx # 限定使用 CPU 核心 0 和 2 docker run -d --cpu-shares=512 nginx # 设置 CPU 权重(默认 1024) # 限制磁盘 I/O docker run -d --device-read-bps=/dev/sda:50mb nginx docker run -d --device-write-bps=/dev/sda:50mb nginx # 查看容器资源使用 docker stats docker stats web db # 查看指定容器 docker stats --no-stream # 单次查看(不持续监控)

资源限制最佳实践

①始终为容器设置内存限制(--memory),防止内存泄漏导致宿主机 OOM。②对于 CPU 密集型应用,使用 --cpus 而非 --cpu-shares,前者提供硬限制。③使用 docker stats 在生产环境下持续监控资源使用情况,据此调整限制参数。④为 Docker 守护进程本身设置全局默认资源限制(在 daemon.json 中配置)。

五、Dockerfile 编写

5.1 Dockerfile 指令详解

Dockerfile 是一个文本文件,包含了一系列构建镜像的指令。每一条指令都会在镜像中创建一个新层。掌握 Dockerfile 的编写是使用 Docker 的核心技能。以下逐一介绍每个重要指令的用途和最佳实践。

指令用途示例
FROM指定基础镜像FROM node:18-alpine
WORKDIR设置工作目录WORKDIR /app
COPY复制文件COPY package.json ./
ADD复制文件(支持URL和解压)ADD app.tar.gz /app
RUN执行命令RUN npm install
CMD默认命令CMD ["node", "app.js"]
ENTRYPOINT入口点ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE声明端口EXPOSE 3000
ENV设置环境变量ENV NODE_ENV=production
ARG构建参数ARG VERSION=1.0
VOLUME声明挂载点VOLUME /data
USER指定用户USER node
LABEL添加元数据LABEL version="1.0"
HEALTHCHECK健康检查HEALTHCHECK CMD curl -f http://localhost || exit 1
SHELL指定 shellSHELL ["/bin/bash", "-c"]

CMD vs ENTRYPOINT 的区别:CMD 提供默认命令和参数,可以在 docker run 时被覆盖。ENTRYPOINT 设置容器的主命令,不易被覆盖。典型用法是:ENTRYPOINT 设置为固定命令(如 nginx),CMD 提供默认参数(如 ["-g", "daemon off;"])。当两者联合使用时,CMD 的内容会作为参数传递给 ENTRYPOINT。如果需要在 docker run 时覆盖 ENTRYPOINT,使用 --entrypoint 选项。

5.2 多阶段构建

多阶段构建(Multi-stage Build)是 Dockerfile 的高级特性,允许在一个 Dockerfile 中使用多个 FROM 语句。每个 FROM 开始一个新的构建阶段,可以从前面的阶段复制文件。这一技术的最大优势是大幅减小最终镜像的体积——你可以在第一个阶段安装所有编译工具和依赖来构建应用,然后在最终阶段只复制编译产物,完全丢弃构建工具链。

# Go 应用多阶段构建示例 # 第一阶段:编译 FROM golang:1.21 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o server . # 第二阶段:运行(镜像体积从 1GB 降到 15MB) FROM alpine:3.18 RUN apk --no-cache add ca-certificates tzdata WORKDIR /app COPY --from=builder /app/server . EXPOSE 8080 CMD ["./server"]
# Node.js 应用多阶段构建 FROM node:18 AS build WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:18-alpine RUN apk add --no-cache tini WORKDIR /app COPY --from=build /app/node_modules ./node_modules COPY . . EXPOSE 3000 ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"]

多阶段构建的优势

①大幅减小镜像体积(通常减少 80%-95%)。②安全性提升——最终镜像中不包含编译工具和源代码。③构建速度优化——每个阶段的缓存独立管理。④无需复杂的构建脚本和构建后清理。一个常见的实践是使用 golang:alpinenode:alpine 作为构建阶段基础镜像,使用 alpinedistroless 作为运行阶段基础镜像。

5.3 编写 Dockerfile 的最佳实践

编写高质量的 Dockerfile 不仅能提高构建效率,还能提升运行时的安全性和性能。以下是被广泛认可的最佳实践:首先是减少层数——合理合并 RUN 指令,但不必过度优化,Docker 官方建议每层包含逻辑相关的操作;其次是优化缓存使用——将变化频率从低到高排列指令顺序;再次是使用特定版本标签而非 latest——构建可复现的镜像;最后是以非 root 用户运行应用——提升容器安全性。

# 一个遵循最佳实践的 Node.js Dockerfile FROM node:18-alpine AS base # 使用非 root 用户 RUN addgroup app && adduser -S -G app app # 安装生产依赖(利用缓存优化) FROM base AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force # 最终阶段 FROM base WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN chown -R app:app /app USER app EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 CMD ["node", "server.js"]

Dockerfile 编写核心要点

①选择合适的基础镜像——优先使用官方镜像的 Alpine 或 Slim 变体。②合理组织指令顺序——稳定指令在前,易变指令在后。③使用 .dockerignore 排除不需要的文件。④多阶段构建大幅缩小镜像体积。⑤使用 HEALTHCHECK 提供容器健康检测。⑥指定 EXPOSE 声明应用端口(文档化目的)。⑦以非 root 用户运行,最小化攻击面。⑧使用固定版本标签而非 latest,确保构建可复现。

六、Docker Compose

6.1 Compose 概述

Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件(docker-compose.yml),你可以配置应用的所有服务、网络和数据卷,然后使用一条命令就可以创建和启动所有服务。Compose 适用于开发环境、测试环境和单机部署的生产环境,是 Docker 生态系统中容器编排的基础工具。

6.2 docker-compose.yml 文件结构

Compose 文件的核心由 services(服务)、networks(网络)和 volumes(数据卷)三部分组成。services 定义每个容器的配置(类似 docker run 的参数),networks 定义容器间的通信网络,volumes 定义持久化数据存储。

# docker-compose.yml 完整示例 - Web 应用 + 数据库 + Redis version: "3.8" services: web: build: . image: myapp:latest ports: - "8080:3000" environment: - NODE_ENV=production - DB_HOST=db - REDIS_HOST=redis depends_on: db: condition: service_healthy redis: condition: service_started volumes: - uploads:/app/uploads networks: - frontend - backend restart: unless-stopped deploy: resources: limits: cpus: "0.5" memory: 512M db: image: postgres:15-alpine volumes: - pgdata:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql environment: - POSTGRES_DB=myapp - POSTGRES_USER=myapp - POSTGRES_PASSWORD=${DB_PASSWORD} healthcheck: test: ["CMD-SHELL", "pg_isready -U myapp"] interval: 10s timeout: 5s retries: 5 networks: - backend restart: unless-stopped redis: image: redis:7-alpine volumes: - redis-data:/data command: redis-server --appendonly yes networks: - backend restart: unless-stopped volumes: pgdata: uploads: redis-data: networks: frontend: backend: internal: true # 后端网络禁止外部访问

6.3 Compose 常用命令

Docker Compose 提供了一套简洁的命令来管理多容器应用。理解这些命令是日常使用 Compose 的基础。注意新版 Docker 已将 Compose 作为 Docker CLI 的子命令集成:docker compose(无连字符)而不是独立的 docker-compose

# 启动所有服务 docker compose up -d # 构建镜像并启动 docker compose up -d --build # 停止并移除所有容器 docker compose down # 停止并移除容器、网络、数据卷 docker compose down -v # 查看运行中的服务 docker compose ps # 查看日志 docker compose logs -f web # 执行命令 docker compose exec web node scripts/migrate.js # 重建特定服务 docker compose up -d --no-deps --build web # 查看服务配置 docker compose config # 拉取所有镜像 docker compose pull # 重启服务 docker compose restart web

6.4 环境变量与配置

Compose 文件支持多种方式注入环境变量,包括 .env 文件、Shell 环境变量和内联设置。Compose 会在项目目录下自动加载 .env 文件,也可以使用 --env-file 指定其他文件。通过环境变量传递敏感信息(如密码、API 密钥)是管理 Docker 配置的标准做法。

# .env 文件 DB_PASSWORD=secure_password_123 API_KEY=sk-abcdef123456 DEBUG=false # docker-compose.yml 中使用变量 services: app: image: myapp environment: - DB_PASSWORD=${DB_PASSWORD} - API_KEY=${API_KEY} - DEBUG=${DEBUG:-false} # 默认值 # 使用不同环境的 .env 文件 docker compose --env-file .env.production up -d

安全注意事项

①永远不要将包含敏感信息的 .env 文件提交到版本控制,确保已添加到 .gitignore。②在团队中使用 .env.example 模板来记录所需的变量名。③生产环境建议使用 Docker Secrets 或外部密钥管理服务(如 Vault、AWS Secrets Manager)。④避免在 Compose 文件中硬编码密码,始终使用变量引用。

七、Docker 网络

7.1 网络模式

Docker 提供多种网络模式,每种模式适用于不同的场景。理解网络模式是设计 Docker 网络架构的基础。主要模式包括:bridge(桥接网络,默认)、host(主机网络,容器直接使用宿主机网络栈)、none(无网络)、overlay(跨主机覆盖网络,用于 Swarm 模式)和 macvlan(为容器分配 MAC 地址)。

网络模式隔离性性能适用场景
bridge(默认)容器间隔离中等单机多容器通信
host无隔离最佳(无 NAT)高性能网络服务
none完全隔离N/A安全敏感、离线计算
overlay跨主机隔离较低(有封装开销)Docker Swarm 集群
macvlan直接连接物理网络优秀遗留应用、网络监控
# 创建自定义 bridge 网络 docker network create --driver bridge \ --subnet 172.20.0.0/16 \ --gateway 172.20.0.1 \ --ip-range 172.20.0.0/24 \ my-network # 在自定义网络运行容器 docker run -d --network my-network --name web nginx docker run -d --network my-network --ip 172.20.0.10 --name db postgres # 将已有容器连接到网络 docker network connect my-network existing-container # 断开容器与网络的连接 docker network disconnect my-network existing-container # 网络管理 docker network ls docker network inspect my-network docker network prune

7.2 容器间通信

在自定义 bridge 网络中,Docker 内置了 DNS 解析功能,容器可以通过服务名称互相访问。这意味着你不需要记住容器 IP 地址,只需使用容器名即可。例如,一个名为 db 的容器可以通过 http://db:5432 被同一网络中的其他容器访问。这是 Docker Compose 中服务间通信的基础。

重要:默认 bridge 网络(docker0)与自定义 bridge 网络的一个重要区别是:在默认 bridge 网络中,容器只能通过 IP 地址通信(不提供 DNS 解析);而在自定义 bridge 网络中,Docker 内置的 DNS 解析器会自动将容器名称解析为 IP 地址。因此,生产环境中始终建议使用自定义 bridge 网络。

7.3 网络排错工具

容器网络问题的排查需要一些基本的网络诊断工具。以下命令和技巧可以帮助你快速定位网络问题:

# 检查容器网络配置 docker inspect web --format '{{json .NetworkSettings.Networks}}' # 在容器内执行网络诊断 docker exec web ping db docker exec web nslookup db docker exec web curl -v http://db:5432 # 查看容器端口映射 docker port web # 检查 iptables 规则(Linux) sudo iptables -t nat -L -n | grep docker # 查看网络流量(使用 nsenter) docker inspect web --format '{{.State.Pid}}' sudo nsenter -t $(docker inspect web --format '{{.State.Pid}}') -n tcpdump

八、Docker 数据卷

8.1 数据持久化方案

Docker 容器默认是"无状态"的——容器被删除时,其内部的所有数据也会被删除。数据卷(Volume)和绑定挂载(Bind Mount)提供了数据持久化的机制。Volume 由 Docker 管理,存储在宿主机文件系统的特定位置(/var/lib/docker/volumes/),是推荐的数据持久化方式。Bind Mount 将宿主机的任意目录挂载到容器中,灵活性更高但可控性较差。

# 使用 Volume(推荐) docker volume create my-data docker run -v my-data:/var/lib/mysql mysql:8 # 匿名 Volume(Docker 自动生成名称) docker run -v /var/lib/mysql mysql:8 # 使用 Bind Mount(开发环境常用) docker run -v $(pwd):/app -w /app node npm run dev # 只读挂载 docker run -v $(pwd)/config:/etc/config:ro nginx # 使用 tmpfs 挂载(内存中,速度快但数据不持久) docker run --tmpfs /app/temp nginx # Volume 管理 docker volume ls docker volume inspect my-data docker volume prune docker volume rm my-data

Volume vs Bind Mount 的选择

①使用 Volume 的场景:数据库持久化、配置文件管理、容器间共享数据、需要备份和迁移的数据。Volume 的优点是 Docker 完全管理、跨平台一致、支持 Volume Driver 扩展。②使用 Bind Mount 的场景:开发环境热重载(Hot Reload)、向容器注入配置文件、读取宿主机上的日志或 socket 文件。Bind Mount 的优点是灵活、可以使用任意路径,但跨平台体验不一致(Windows 路径问题)。

8.2 数据卷容器与备份

数据卷容器(Volume Container)和数据备份是运维中的常见需求。数据卷容器是一种模式:创建一个专门用于挂载数据卷的容器,其他容器通过 --volumes-from 共享其数据卷。备份数据卷可以通过在临时容器中挂载卷并打包来实现。

# 创建数据卷容器 docker create -v /data --name data-container alpine # 其他容器共享数据卷 docker run --volumes-from data-container --name app1 nginx docker run --volumes-from data-container --name app2 nginx # 备份数据卷 docker run --rm \ -v my-data:/source:ro \ -v $(pwd):/backup \ alpine tar czf /backup/my-data-$(date +%Y%m%d).tar.gz -C /source . # 恢复数据卷 docker run --rm \ -v my-data:/target \ -v $(pwd):/backup \ alpine tar xzf /backup/my-data-20240101.tar.gz -C /target

九、Docker 安全实践

9.1 安全基线配置

容器安全是一个系统性工程,涉及镜像安全、运行时安全、宿主机安全和网络安全等多个层面。Docker 本身提供了一些安全机制,但默认配置可能不满足生产环境的严格安全要求。以下是一些基本的安全加固措施:

# 以非 root 用户运行容器 docker run -d --user 1000:1000 nginx # 移除容器能力(Capability) docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE nginx # 设置只读根文件系统 docker run -d --read-only --tmpfs /tmp --tmpfs /var/run nginx # 启用安全选项 docker run -d \ --security-opt=no-new-privileges:true \ --security-opt=seccomp=seccomp-profile.json \ --security-opt=apparmor=my-apparmor-profile \ nginx # 限制容器访问 docker run -d --pids-limit=100 nginx # 限制进程数 docker run -d --restart=on-failure:5 nginx # 限制重启次数

9.2 镜像安全扫描

镜像可能包含已知漏洞、恶意软件或配置不当。定期对镜像进行安全扫描是保障容器安全的重要环节。Docker Desktop 集成了 Snyk 扫描功能,同时社区也有多种开源扫描工具可供选择:

# Trivy 镜像扫描(推荐的开源工具) trivy image nginx:latest trivy image --severity HIGH,CRITICAL myapp:latest # Docker Scout(Docker 官方) docker scout quickview nginx:latest docker scout cves nginx:latest docker scout recommendations nginx:latest # Clair 扫描 clairctl analyze myapp:latest # 在 CI/CD 中集成安全扫描 # GitHub Actions 示例 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: myapp:latest format: table exit-code: 1 severity: CRITICAL,HIGH

安全红线

①绝对不要使用 --privileged 标志运行容器(除非极特殊场景),它会赋予容器所有宿主机的内核能力。②不要以 root 用户运行容器中的应用进程。③避免使用 latest 标签——使用特定版本标签确保镜像的可追溯性。④定期更新基础镜像(尤其是 Alpine 和 Distroless 的基础镜像)。⑤在 CI/CD 流水线中集成镜像安全扫描,阻止包含高危漏洞的镜像进入生产环境。

十、Docker 与 CI/CD 集成

10.1 Docker 在 CI/CD 中的角色

Docker 在 CI/CD 流水线中扮演着核心角色:它提供了可重复的构建环境、一致的测试环境和可靠的部署单元。通过 Docker,开发者可以确保"在我机器上可以运行"变成"在任何地方都可以运行"。CI/CD 中的 Docker 实践通常包括镜像构建、安全扫描、自动化测试和部署到目标环境。

10.2 GitHub Actions + Docker

GitHub Actions 提供了原生的 Docker 支持,可以轻松实现镜像构建、推送和部署的自动化流水线。以下是一个完整的 CI/CD 工作流示例:

# .github/workflows/docker-deploy.yml name: Build and Deploy on: push: branches: [main] pull_request: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: | myrepo/myapp:${{ github.sha }} myrepo/myapp:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Scan for vulnerabilities uses: aquasecurity/trivy-action@master with: image-ref: myrepo/myapp:${{ github.sha }} exit-code: 1 severity: CRITICAL - name: Deploy to server uses: appleboy/ssh-action@v1.0.0 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_KEY }} script: | docker pull myrepo/myapp:${{ github.sha }} docker compose down sed -i "s|IMAGE_TAG|${{ github.sha }}|g" docker-compose.yml docker compose up -d docker image prune -f

10.3 GitLab CI + Docker

GitLab CI 同样提供了完善的 Docker 集成支持,特别适合自托管的 Docker 运行环境。以下是一个典型的 GitLab CI 配置:

# .gitlab-ci.yml image: docker:24.0.5 services: - docker:24.0.5-dind variables: DOCKER_TLS_CERTDIR: "/certs" IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA stages: - build - test - deploy before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY build: stage: build script: - docker build -t $IMAGE_TAG . - docker push $IMAGE_TAG test: stage: test script: - docker run --rm $IMAGE_TAG npm test deploy: stage: deploy script: - docker pull $IMAGE_TAG - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest - docker push $CI_REGISTRY_IMAGE:latest - apt-get update && apt-get install -y sshpass - sshpass -p $SSH_PASSWORD ssh deploy@server "cd /app && docker compose pull && docker compose up -d" only: - main

十一、Docker 生产环境部署

11.1 部署架构模式

Docker 在生产环境中的部署模式取决于应用的规模、可用性要求和团队的技术栈。常见的架构模式包括:单机多容器(适用于小型应用或开发环境)、Docker Swarm(Docker 原生集群方案,配置简单)、Kubernetes(业界标准的容器编排平台,功能强大但复杂度高)、以及托管容器服务(如 AWS ECS、Google Cloud Run、阿里云 ACK 等)。

选择建议:对于初创项目或中小规模团队,推荐使用 Docker Compose + 单机部署起步,可以快速上线。当应用需要水平扩展和高可用时,迁移到 Kubernetes 或托管容器服务。Docker Swarm 作为过渡方案适合已有 Docker 基础且需要简单集群的场景。对于大型企业或需要复杂调度策略的场景,Kubernetes 是事实上的标准选择。

11.2 日志与监控

生产环境中的 Docker 应用需要完善的日志收集和性能监控方案。Docker 支持多种日志驱动,可以将日志直接发送到集中的日志平台。对于监控和告警,推荐使用以下技术栈组合。

# docker-compose 日志与监控栈 services: # 日志收集(ELK Stack) filebeat: image: docker.elastic.co/beats/filebeat:8.11.0 volumes: - /var/lib/docker/containers:/var/lib/docker/containers:ro - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro user: root restart: unless-stopped # 指标收集(Prometheus + cAdvisor) cadvisor: image: gcr.io/cadvisor/cadvisor:latest ports: - "8080:8080" volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro - /dev/disk/:/dev/disk:ro privileged: true restart: unless-stopped prometheus: image: prom/prometheus ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus restart: unless-stopped # 可视化 grafana: image: grafana/grafana ports: - "3000:3000" volumes: - grafana-data:/var/lib/grafana restart: unless-stopped

11.3 常见部署策略

在生产环境中更新容器化应用时,需要选择合适的部署策略以最小化停机时间。以下是几种常见的部署策略及其 Docker 实现方式。

部署策略停机时间回滚速度复杂度适用场景
滚动更新无(逐步替换)大多数 Web 应用
蓝绿部署无(瞬间切换)极快关键业务系统
金丝雀发布大规模用户验证
A/B 测试功能对比验证
# 蓝绿部署示例脚本 #!/bin/bash # 部署新版本(蓝色→绿色) docker compose -f docker-compose.green.yml up -d # 等待新版本就绪 sleep 10 # 验证健康检查 if curl -f http://localhost:8081/health; then # 切换流量到新版本 docker compose -f docker-compose.blue.yml scale web=0 echo "已切换到绿色版本" # 保留旧版本用于快速回滚 docker compose -f docker-compose.blue.yml up -d --scale web=1 else echo "新版本健康检查失败,回滚" docker compose -f docker-compose.green.yml down exit 1 fi

优雅关闭容器

当 Docker 停止容器时,它会向容器的主进程发送 SIGTERM 信号,等待一段默认时间(10秒)后发送 SIGKILL。应用应当捕获 SIGTERM 信号并执行清理操作(如完成当前请求、关闭数据库连接)。可以通过 --stop-timeout 调整等待时间。一个正确的优雅关闭实现可以减少用户感知的停机时间并避免数据丢失。

十二、Docker CLI 命令速查表

12.1 容器管理命令

命令说明常用选项
docker run创建并启动容器-d, -it, --rm, --name, -p, -v, -e, --network
docker ps列出容器-a, -q, --filter, --format
docker stop停止容器-t (等待时间)
docker start启动已停止的容器-i (交互模式)
docker restart重启容器-t (等待时间)
docker rm删除容器-f (强制), -v (删除数据卷)
docker exec在容器中执行命令-it, -d, -e, -w
docker logs查看容器日志-f, --tail, --since, -t
docker inspect查看容器详细信息--format (格式化输出)
docker stats查看资源使用情况--no-stream, --all

12.2 镜像管理命令

命令说明常用选项
docker images列出本地镜像--filter, --format, -q
docker pull拉取镜像--platform (指定架构)
docker push推送镜像需先登录仓库
docker build构建镜像-t, -f, --no-cache, --platform
docker rmi删除镜像-f (强制)
docker tag标记镜像tar格式
docker save导出镜像为 tar-o (输出文件)
docker load加载 tar 镜像-i (输入文件)
docker history查看镜像构建历史显示每层的信息
docker image prune清理未使用的镜像-a (全部), -f (强制)

12.3 系统管理命令

命令说明常用选项
docker info显示系统信息查看存储驱动、镜像数、容器数
docker version显示版本信息客户端和服务端版本
docker system df查看磁盘使用情况-v (详细信息)
docker system prune清理所有未使用的资源-a (全部), --volumes
docker login登录镜像仓库-u, -p
docker logout登出镜像仓库
docker events实时监听 Docker 事件--filter

十三、常见问题与排错

13.1 镜像拉取失败

症状:docker pull 超时或返回 "net/http: TLS handshake timeout" 错误。原因:通常是由于网络问题或 Docker Hub 被墙。解决方法:配置镜像加速器、使用 VPN 或代理、尝试更换 DNS 服务器(如 8.8.8.8)。

# 配置 Docker 代理 # 创建 /etc/systemd/system/docker.service.d/proxy.conf [Service] Environment="HTTP_PROXY=http://proxy.example.com:8080" Environment="HTTPS_PROXY=http://proxy.example.com:8080" Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com" # 重新加载并重启 sudo systemctl daemon-reload sudo systemctl restart docker

13.2 Docker 磁盘空间不足

症状:容器启动失败,错误信息包含 "no space left on device" 或 "disk quota exceeded"。原因:Docker 默认将镜像、容器和数据卷存储在 /var/lib/docker 目录下,未使用的镜像和日志文件会不断累积。解决方法:定期清理未使用的资源、配置日志轮转、将数据目录迁移到更大容量的磁盘。

# 查看 Docker 磁盘使用情况 docker system df # 一键清理(慎用 -a 会删除所有未使用的镜像) docker system prune -a --volumes # 更安全的清理方式 docker image prune -a --filter "until=24h" # 删除 24 小时前的未使用镜像 docker container prune --filter "until=24h" # 删除 24 小时前退出的容器 # 更改 Docker 数据目录 # 编辑 /etc/docker/daemon.json { "data-root": "/mnt/docker-data" }

13.3 容器网络连通性问题

症状:容器 A 无法通过容器名称访问容器 B。原因:常见原因包括:两个容器不在同一个自定义网络中、使用了默认 bridge 网络(不支持 DNS 解析)、容器 B 未正确启动或端口监听错误。解决方法:确保容器在同一自定义网络中、检查容器启动状态、验证容器内服务是否正常监听端口。

# 网络排错通用步骤 # 1. 确认网络配置 docker network ls docker network inspect app-network # 2. 确认容器连接状态 docker inspect container-a --format '{{.NetworkSettings.Networks}}' # 3. 连接到同一网络 docker network connect app-network container-a # 4. 测试通信 docker exec container-a ping container-b docker exec container-a curl -v http://container-b:8080/health

13.4 容器启动后立即退出

症状:容器启动后立即进入 Exited 状态。原因:容器的前台进程结束导致容器退出。Docker 容器必须有一个前台进程持续运行,如果进程执行完毕或出错退出,容器就会停止。解决方法:检查应用的启动命令是否正确、查看容器日志定位具体错误、确保应用以非 daemon 模式在前台运行。

# 排查步骤 docker logs my-container # 查看退出日志 docker inspect my-container --format '{{.State.ExitCode}}' # 查看退出码 # 以交互模式测试,便于调试 docker run -it --entrypoint sh my-image # 进入容器后手动启动应用,观察错误输出

十四、核心总结

Docker 学习要点总结

  • 容器化核心理念:Docker 通过容器将应用及其依赖打包,实现"一次构建,到处运行"。与虚拟机相比,容器共享宿主机内核,启动更快、资源占用更少。
  • 镜像管理:镜像是只读模板,由多层构成。使用 docker pull 拉取、docker build 构建、docker push 分享。多阶段构建可将最终镜像体积缩小 90% 以上。
  • 容器管理:容器是镜像的运行实例。通过 docker run 创建,docker exec 进入,docker logs 查看日志,docker stats 监控资源。合理设置资源限制是生产环境的基本要求。
  • Dockerfile 编写:核心指令包括 FROM、WORKDIR、COPY、RUN、CMD、ENTRYPOINT、EXPOSE、ENV、USER。遵循"小基础镜像、缓存优化、非 root 用户、多阶段构建"四大原则。
  • Docker Compose:通过 YAML 定义多容器应用,一条命令启动整个栈。支持环境变量注入、健康检查、依赖管理和资源限制,是单机多容器部署的首选方案。
  • 网络:理解 bridge、host、overlay 等网络模式的适用场景。始终使用自定义 bridge 网络,利用 Docker DNS 实现服务发现。
  • 数据持久化:使用 Volume 管理数据库等需要持久化的数据,Bind Mount 适合开发环境热重载。定期备份数据卷。
  • 安全:最小权限原则——以非 root 用户运行、移除不必要的能力、使用只读文件系统、在 CI/CD 中集成镜像安全扫描。
  • CI/CD 集成:Docker 提供了一致的构建和部署单元。在 CI/CD 流水线中集成镜像构建、安全扫描、自动化测试和自动部署,实现端到端的自动化交付。
  • 生产部署:根据应用规模选择合适的编排工具(Docker Compose → Swarm → Kubernetes)。实施健康检查、日志收集、监控告警和优雅关闭。

一句话总结:Docker 容器化技术是现代 DevOps 实践的基石,它通过标准化应用打包、简化部署流程、提升环境一致性,从根本上改变了软件从开发到上线的整个生命周期。掌握 Docker 是每一位后端开发、运维和 DevOps 工程师的必备技能,也是学习 Kubernetes、云原生和微服务架构的基础。

学习路径建议

Docker 的学习路径建议遵循"从实践出发,逐步深入"的原则:

  • 第一阶段(入门):掌握 Docker 安装、常用命令(run、ps、exec、logs、stop、rm),理解镜像和容器的关系。
  • 第二阶段(进阶):编写 Dockerfile(重点掌握多阶段构建和缓存优化)、使用 Docker Compose 编排多容器应用。
  • 第三阶段(高级):网络配置与安全加固、Docker Swarm 集群管理、CI/CD 集成、生产环境监控与日志收集。
  • 第四阶段(生态):学习 Kubernetes 编排、Service Mesh、Helm Charts 等云原生技术,将 Docker 知识融入更广泛的云原生生态。

"容器化不是关于运行什么,而是关于如何交付。它是软件供应链中标准化和自动化的关键推动力。"

相关学习资源

Claude Code CLI 完整参考手册 —— 学习如何使用 Claude Code 与 Docker 配合进行自动化开发

▶ Docker 官方文档:https://docs.docker.com/

▶ Docker Hub 镜像搜索:https://hub.docker.com/

▶ Docker Compose 文件参考:Compose File Reference