Docker 容器化技术完整指南
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 的完整开发环境。
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
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 守护进程。
sudo usermod -aG docker $USER
newgrp docker
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"
],
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"max-concurrent-downloads": 10,
"experimental": true,
"iptables": true,
"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
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 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 导出的是容器的文件系统(扁平化,不含分层和历史信息)。
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
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
docker run -p 80:80/tcp -p 443:443/tcp nginx
docker run -p 3000-3005:3000-3005 app
docker run -P nginx
docker port web
端口冲突处理
当宿主机端口已被占用时,docker run 会报错退出。解决方法:①改用其他宿主机端口;②停止占用端口的进程或其他容器;③使用 Docker NAT 网络模式。推荐在启动容器前使用 ss -tlnp | grep :80 或 netstat -tlnp | grep :80 检查端口占用情况。对于生产环境,建议使用专门的端口管理工具或服务发现机制。
4.3 容器日志管理
Docker 自动捕获容器的标准输出(stdout)和标准错误(stderr),并将其作为日志记录。日志驱动(Log Driver)决定了日志的存储和转发方式。默认的 json-file 驱动将日志以 JSON 格式存储在宿主机上,每个容器一个日志文件。在生产环境中,建议将日志转发到集中的日志系统(如 Elasticsearch、Splunk、Graylog 等)。
docker logs web
docker logs --tail 100 web
docker logs -f web
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
docker run -d --cpus="1.5" nginx
docker run -d --cpuset-cpus="0,2" nginx
docker run -d --cpu-shares=512 nginx
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 | 指定 shell | SHELL ["/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 开始一个新的构建阶段,可以从前面的阶段复制文件。这一技术的最大优势是大幅减小最终镜像的体积——你可以在第一个阶段安装所有编译工具和依赖来构建应用,然后在最终阶段只复制编译产物,完全丢弃构建工具链。
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 .
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]
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:alpine 或 node:alpine 作为构建阶段基础镜像,使用 alpine 或 distroless 作为运行阶段基础镜像。
5.3 编写 Dockerfile 的最佳实践
编写高质量的 Dockerfile 不仅能提高构建效率,还能提升运行时的安全性和性能。以下是被广泛认可的最佳实践:首先是减少层数——合理合并 RUN 指令,但不必过度优化,Docker 官方建议每层包含逻辑相关的操作;其次是优化缓存使用——将变化频率从低到高排列指令顺序;再次是使用特定版本标签而非 latest——构建可复现的镜像;最后是以非 root 用户运行应用——提升容器安全性。
FROM node:18-alpine AS base
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 定义持久化数据存储。
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 配置的标准做法。
DB_PASSWORD=secure_password_123
API_KEY=sk-abcdef123456
DEBUG=false
services:
app:
image: myapp
environment:
- DB_PASSWORD=${DB_PASSWORD}
- API_KEY=${API_KEY}
- DEBUG=${DEBUG:-false}
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 | 直接连接物理网络 | 优秀 | 遗留应用、网络监控 |
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
sudo iptables -t nat -L -n | grep docker
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 将宿主机的任意目录挂载到容器中,灵活性更高但可控性较差。
docker volume create my-data
docker run -v my-data:/var/lib/mysql mysql:8
docker run -v /var/lib/mysql mysql:8
docker run -v $(pwd):/app -w /app node npm run dev
docker run -v $(pwd)/config:/etc/config:ro nginx
docker run --tmpfs /app/temp nginx
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 本身提供了一些安全机制,但默认配置可能不满足生产环境的严格安全要求。以下是一些基本的安全加固措施:
docker run -d --user 1000:1000 nginx
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 image nginx:latest
trivy image --severity HIGH,CRITICAL myapp:latest
docker scout quickview nginx:latest
docker scout cves nginx:latest
docker scout recommendations nginx:latest
clairctl analyze myapp:latest
- 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 工作流示例:
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 配置:
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 支持多种日志驱动,可以将日志直接发送到集中的日志平台。对于监控和告警,推荐使用以下技术栈组合。
services:
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
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 测试 | 无 | 中 | 高 | 功能对比验证 |
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)。
[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 system df
docker system prune -a --volumes
docker image prune -a --filter "until=24h"
docker container prune --filter "until=24h"
{
"data-root": "/mnt/docker-data"
}
13.3 容器网络连通性问题
症状:容器 A 无法通过容器名称访问容器 B。原因:常见原因包括:两个容器不在同一个自定义网络中、使用了默认 bridge 网络(不支持 DNS 解析)、容器 B 未正确启动或端口监听错误。解决方法:确保容器在同一自定义网络中、检查容器启动状态、验证容器内服务是否正常监听端口。
docker network ls
docker network inspect app-network
docker inspect container-a --format '{{.NetworkSettings.Networks}}'
docker network connect app-network container-a
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 知识融入更广泛的云原生生态。
"容器化不是关于运行什么,而是关于如何交付。它是软件供应链中标准化和自动化的关键推动力。"