什么是 Docker?

Docker 是一个开源的容器化平台,可以将应用程序及其依赖打包到一个可移植的容器中。

核心概念

🐳 Docker 核心概念
📦 镜像 Image
只读模板
分层存储
版本标签
🏃 容器 Container
镜像实例
隔离环境
可读写层
🏠 仓库 Registry
镜像存储
版本分发
Docker Hub
⚙️ 其他组件
网络 Network
数据卷 Volume
Dockerfile

概念关系流程

📝
Dockerfile
定义镜像构建步骤
📦
镜像 Image
只读模板
🏃
容器 Container
运行实例

镜像与仓库交互

🏠
Registry
仓库
Docker Hub / 私有仓库
📦
镜像
Image
🏃
容器
Container

docker pull 从仓库拉取镜像 → docker run 从镜像启动容器 → docker push 推送镜像到仓库

概念详解

镜像 (Image)

只读模板,包含运行应用所需的代码、运行时、库和配置。

docker pull nginx:alpine

容器 (Container)

镜像的运行实例,独立、隔离的执行环境。

docker run -d -p 80:80 nginx

仓库 (Registry)

存储和分发镜像的服务,如 Docker Hub。

docker push user/image:tag

镜像分层结构

应用层 Application Layer ~10MB
依赖层 Dependencies ~50MB
运行时 Runtime (Node/Python) ~100MB
基础系统 Base OS (Alpine/Debian) ~5-150MB

每层只读,容器运行时添加可写层(Copy-on-Write)

Docker 架构图

Docker Client
docker CLI / Docker Desktop
↓ docker build/run/pull
Docker Daemon (dockerd)
监听 API 请求,管理镜像、容器、网络、卷
containerd
容器运行时
runc
OCI 运行时
Images
镜像存储
Containers
容器实例
Networks
网络
Volumes
数据卷
Host OS (Linux / macOS / Windows)

docker run 请求流程

1
用户执行命令
docker run -d nginx:alpine
2
Client 发送请求到 Daemon
通过 REST API 发送创建容器请求
3
Daemon 检查本地镜像
本地不存在则从 Registry 拉取
4
调用 containerd 创建容器
准备容器配置和 rootfs
5
containerd 调用 runc
使用 OCI 规范创建容器进程
6
容器启动运行
返回容器 ID 给用户

组件交互关系

🐳
Docker
Daemon
CLI
客户端
Registry
仓库
Images
镜像
Containers
容器
Networks
网络
Volumes
数据卷

架构说明

组件 说明
Docker Client 用户与 Docker 交互的界面,如 docker CLI 命令
Docker Daemon 后台服务,处理客户端请求,管理镜像和容器
containerd 容器运行时,管理容器生命周期
runc OCI 兼容的容器运行时,实际创建容器

安装 Docker

macOS

# Homebrew 安装
brew install --cask docker

# 或下载 Docker Desktop
# https://www.docker.com/products/docker-desktop

Windows

# 使用 winget
winget install Docker.DockerDesktop

# 或使用 Chocolatey
choco install docker-desktop

Linux (Ubuntu)

# 官方脚本安装
curl -fsSL https://get.docker.com | sh

# 添加用户到 docker 组
sudo usermod -aG docker $USER

验证安装

docker --version
docker run hello-world

Dockerfile

Dockerfile 是定义镜像构建过程的文本文件,包含一系列指令用于构建 Docker 镜像。

基础示例

# Node.js 应用
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

多阶段构建

多阶段构建可以显著减小最终镜像大小,分离构建环境和运行环境。

# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# 生产阶段
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

完整指令列表

指令 说明 示例
FROM 指定基础镜像(必须为第一条指令) FROM node:20-alpine
WORKDIR 设置工作目录 WORKDIR /app
COPY 复制文件到镜像 COPY . .
ADD 复制文件(支持 URL 和解压) ADD app.tar.gz /app
RUN 执行命令并提交结果 RUN npm install
CMD 容器启动时执行的默认命令 CMD ["node", "app.js"]
ENTRYPOINT 配置容器为可执行程序 ENTRYPOINT ["node"]
ENV 设置环境变量 ENV NODE_ENV=production
ARG 构建时的变量 ARG VERSION=1.0
EXPOSE 声明端口 EXPOSE 3000
VOLUME 创建挂载点 VOLUME /data
USER 指定运行用户 USER node
HEALTHCHECK 健康检查 HEALTHCHECK CMD curl -f http://localhost/
LABEL 添加元数据 LABEL version="1.0"

CMD vs ENTRYPOINT

特性 CMD ENTRYPOINT
目的 设置默认命令和参数 配置容器为可执行程序
覆盖方式 docker run 时可直接替换 需要 --entrypoint 参数
组合使用 作为 ENTRYPOINT 的默认参数 定义主命令
# 组合使用示例
ENTRYPOINT ["node"]
CMD ["app.js"]

# docker run myapp        → node app.js
# docker run myapp server.js → node server.js

常犯错误

❌ 错误:使用 latest 标签

FROM node:latest  # 不推荐

✅ 正确:指定版本

FROM node:20-alpine  # 推荐

latest 标签会变化,导致构建不可重复

❌ 错误:忽略缓存顺序

COPY . .
RUN npm install  # 任何文件变化都会重新执行

✅ 正确:利用缓存

COPY package*.json ./
RUN npm install
COPY . .  # 只在代码变化时才重新复制

❌ 错误:以 root 运行

FROM node:20-alpine
# 默认 root 用户
CMD ["node", "app.js"]

✅ 正确:非 root 用户

FROM node:20-alpine
RUN addgroup -g 1001 app && \
    adduser -u 1001 -G app user
USER user
CMD ["node", "app.js"]

❌ 错误:镜像过大

FROM node:20  # Debian 基础镜像 ~900MB
RUN npm install

✅ 正确:使用 Alpine

FROM node:20-alpine  # Alpine ~170MB
RUN npm install

❌ 错误:多层 RUN 指令

RUN apk add --no-cache git
RUN apk add --no-cache curl
RUN apk add --no-cache vim

✅ 正确:合并命令

RUN apk add --no-cache \
    git curl vim

❌ 错误:复制无用文件

COPY . .  # 复制所有文件

✅ 正确:使用 .dockerignore

# .dockerignore
node_modules
.git
*.md
.env

最佳实践清单

# 1. 使用特定版本的基础镜像
FROM node:20-alpine3.19

# 2. 设置工作目录
WORKDIR /app

# 3. 先复制依赖文件,利用缓存
COPY package*.json ./
RUN npm ci --only=production

# 4. 再复制源代码
COPY . .

# 5. 创建非 root 用户
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

# 6. 设置环境变量
ENV NODE_ENV=production

# 7. 声明端口
EXPOSE 3000

# 8. 切换用户
USER appuser

# 9. 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -q --spider http://localhost:3000/health || exit 1

# 10. 设置启动命令
CMD ["node", "server.js"]

构建镜像

# 构建镜像
docker build -t myapp:v1 .

# 使用 BuildKit(推荐)
DOCKER_BUILDKIT=1 docker build -t myapp:v1 .

# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:v1 .

Docker Compose

Docker Compose 是定义和运行多容器应用的工具,使用 YAML 文件配置服务、网络和数据卷。

服务架构示意

🌐
Nginx
网关
💻 Web
前端
⚡ API
后端
🗄️ DB
数据库
📦 Redis
缓存

服务启动顺序

🗄️
DB
数据库
📦
Redis
缓存
API
后端服务
💻
Web
前端服务

配置文件结构

📄 docker-compose.yml
services
image / build
ports / expose
environment
volumes
depends_on
networks
driver
internal
external
volumes
命名卷
driver
external

基本结构

# docker-compose.yml 基本结构
version: "3.8"  # 可选,新版本已不需要

services:       # 定义服务
  webapp:
    # 服务配置...

networks:       # 定义网络(可选)
  frontend:

volumes:        # 定义数据卷(可选)
  db-data:

configs:        # 定义配置(可选)
  my-config:

secrets:        # 定义机密(可选)
  db-password:

完整示例

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - NODE_ENV=production
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - frontend
      - backend
    volumes:
      - ./logs:/app/logs
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    networks:
      - backend
    restart: unless-stopped

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络,外部无法访问

volumes:
  db-data:
  redis-data:

常用命令

# 服务管理
docker compose up -d              # 后台启动所有服务
docker compose up -d --build      # 重建镜像后启动
docker compose up -d web db       # 只启动指定服务
docker compose down               # 停止并删除容器、网络
docker compose down -v            # 同时删除数据卷
docker compose down --rmi all     # 同时删除镜像

# 查看状态
docker compose ps                 # 查看服务状态
docker compose logs               # 查看所有日志
docker compose logs -f web        # 实时查看 web 服务日志
docker compose top                # 查看进程

# 服务操作
docker compose start web          # 启动服务
docker compose stop web           # 停止服务
docker compose restart web        # 重启服务
docker compose pause web          # 暂停服务
docker compose unpause web        # 恢复服务

# 执行命令
docker compose exec web sh        # 进入容器
docker compose exec db psql -U user myapp  # 在 db 容器执行命令
docker compose run --rm web npm test       # 一次性运行命令

# 其他
docker compose config             # 验证配置文件
docker compose config --services  # 列出所有服务
docker compose pull               # 拉取所有镜像
docker compose push               # 推送所有镜像

常用场景

场景 1:开发环境

services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app           # 挂载代码目录,支持热重载
      - /app/node_modules  # 防止本地 node_modules 覆盖
    environment:
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true  # 支持文件监听
    command: npm run dev

  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"     # 暴露端口便于本地连接
    environment:
      POSTGRES_DB: dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
    volumes:
      - dev-data:/var/lib/postgresql/data

volumes:
  dev-data:

场景 2:微服务架构

services:
  gateway:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - api
      - web
    networks:
      - frontend

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://db:5432/myapp
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend

  web:
    build: ./web
    networks:
      - frontend

  db:
    image: postgres:16-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend

  redis:
    image: redis:7-alpine
    networks:
      - backend

networks:
  frontend:
  backend:
    internal: true  # 只有内部服务可访问

场景 3:CI/CD 测试

services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgres://test:test@db:5432/test
    command: npm test

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: test
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test"]
      interval: 5s
      timeout: 5s
      retries: 10
    tmpfs:
      - /var/lib/postgresql/data  # 使用内存存储,测试更快

场景 4:多环境配置

# docker-compose.yml(基础配置)
services:
  web:
    image: myapp
    ports:
      - "3000:3000"

# docker-compose.override.yml(开发环境,自动加载)
services:
  web:
    build: .
    volumes:
      - .:/app
    environment:
      - DEBUG=true

# docker-compose.prod.yml(生产环境)
services:
  web:
    image: myapp:latest
    restart: always
    deploy:
      replicas: 3

# 使用方式:
# 开发环境: docker compose up
# 生产环境: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

常见错误

❌ 错误:端口冲突

ports:
  - "80:80"  # 本地 80 端口已被占用

✅ 解决方案

ports:
  - "8080:80"  # 使用其他本地端口

❌ 错误:服务启动顺序

depends_on:
  - db  # 只等待容器启动,不等待服务就绪

✅ 解决方案

depends_on:
  db:
    condition: service_healthy  # 等待健康检查通过

❌ 错误:数据丢失

docker compose down  # 默认不删除卷

✅ 注意事项

# 删除所有数据(危险操作)
docker compose down -v

# 生产环境建议定期备份
docker compose exec db pg_dump ...

❌ 错误:环境变量未传递

environment:
  - DB_PASS=$DB_PASSWORD  # 变量未定义时为空

✅ 解决方案

# 使用 .env 文件
# .env
DB_PASSWORD=secret

# 或设置默认值
environment:
  - DB_PASS=${DB_PASSWORD:-default}

❌ 错误:版本兼容问题

version: "2"  # 旧版格式

✅ 解决方案

version: "3.8"  # 使用最新格式
# 或直接省略 version(新版本推荐)

❌ 错误:网络隔离问题

# 服务 A 无法访问服务 B

✅ 解决方案

# 确保服务在同一网络
services:
  a:
    networks:
      - mynet
  b:
    networks:
      - mynet
networks:
  mynet:

配置文件最佳实践

# 1. 使用 .env 文件管理环境变量
# .env
COMPOSE_PROJECT_NAME=myapp
POSTGRES_PASSWORD=secret
API_KEY=xxx

# 2. 使用命名卷而非绑定挂载(生产环境)
volumes:
  db-data:  # 命名卷

# 3. 限制资源
deploy:
  resources:
    limits:
      cpus: '0.5'
      memory: 256M

# 4. 设置重启策略
restart: unless-stopped

# 5. 使用健康检查
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost/health"]
  interval: 30s
  timeout: 10s
  retries: 3

# 6. 使用网络隔离
networks:
  frontend:
  backend:
    internal: true

# 7. 使用 secrets 管理敏感数据
secrets:
  db_password:
    file: ./secrets/db_password.txt

服务配置指令详解

指令 说明 示例
image 指定镜像名称 image: nginx:alpine
build 构建配置 build: ./dir 或详细配置
ports 端口映射 ["3000:3000", "80:80"]
expose 暴露端口(仅内部) ["3000"]
environment 环境变量 KEY=value["KEY=value"]
env_file 从文件加载环境变量 env_file: .env
volumes 挂载卷 ["./data:/app/data"]
networks 连接的网络 ["frontend", "backend"]
depends_on 服务依赖 ["db", "redis"]
restart 重启策略 always / unless-stopped / on-failure
command 覆盖默认命令 ["node", "server.js"]
entrypoint 覆盖入口点 ["/app/entrypoint.sh"]
working_dir 工作目录 /app
user 运行用户 node1000:1000
healthcheck 健康检查 test, interval, timeout, retries
deploy 部署配置(Swarm) replicas, resources, update_config
profiles 服务配置文件 ["debug", "test"]
extra_hosts 添加主机名映射 "myhost:192.168.1.1"
dns 自定义 DNS ["8.8.8.8", "8.8.4.4"]
labels 添加元数据标签 "com.example.description=Web app"

变量替换

# .env 文件
DB_HOST=db
DB_PORT=5432
DB_NAME=myapp
DB_USER=${DB_USER:-admin}      # 默认值
DB_PASS=${DB_PASS:?密码未设置}  # 必须设置,否则报错

# docker-compose.yml
services:
  app:
    environment:
      - DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
    ports:
      - "${APP_PORT:-3000}:3000"  # 使用默认值

# 使用方式
DB_PASS=secret docker compose up -d
# 或
cp .env.example .env && docker compose up -d

Profiles 功能

Profiles 允许按需启动不同服务组合:

services:
  web:
    image: nginx
    ports:
      - "80:80"

  # 只在 debug profile 启用
  debug:
    image: busybox
    command: tail -f /dev/null
    profiles:
      - debug

  # 只在 test profile 启用
  selenium:
    image: selenium/standalone-chrome
    profiles:
      - test

  # 多个 profile
  adminer:
    image: adminer
    profiles:
      - debug
      - admin

# 使用方式
docker compose up -d                    # 只启动 web
docker compose --profile debug up -d    # 启动 web + debug + adminer
docker compose --profile test up -d     # 启动 web + selenium
docker compose --profile debug --profile test up -d  # 启动全部

更多应用场景

场景 5:负载均衡

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - web

  web:
    image: myapp:latest
    deploy:
      replicas: 3  # 运行 3 个副本
    environment:
      - NODE_ENV=production
    networks:
      - app-network

networks:
  app-network:

场景 6:日志收集

services:
  app:
    image: myapp
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    depends_on:
      - elasticsearch
      - logstash

  elasticsearch:
    image: elasticsearch:8-alpine
    environment:
      - discovery.type=single-node
    volumes:
      - es-data:/usr/share/elasticsearch/data

  logstash:
    image: logstash:8-alpine
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf

  kibana:
    image: kibana:8-alpine
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

volumes:
  es-data:

场景 7:消息队列

services:
  producer:
    build: ./producer
    environment:
      - RABBITMQ_HOST=rabbitmq
    depends_on:
      rabbitmq:
        condition: service_healthy

  consumer:
    build: ./consumer
    environment:
      - RABBITMQ_HOST=rabbitmq
    depends_on:
      rabbitmq:
        condition: service_healthy
    deploy:
      replicas: 3  # 多个消费者

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"   # AMQP
      - "15672:15672" # 管理界面
    healthcheck:
      test: ["CMD", "rabbitmqctl", "status"]
      interval: 10s
      timeout: 10s
      retries: 5
    volumes:
      - rabbit-data:/var/lib/rabbitmq

volumes:
  rabbit-data:

场景 8:数据库主从复制

services:
  pg-master:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_REPLICATION_USER: replicator
      POSTGRES_REPLICATION_PASSWORD: repl_pass
    volumes:
      - master-data:/var/lib/postgresql/data
    command: >
      postgres
      -c wal_level=replica
      -c max_wal_senders=3
      -c max_replication_slots=3

  pg-replica:
    image: postgres:16-alpine
    environment:
      PGUSER: replicator
      PGPASSWORD: repl_pass
      PGHOST: pg-master
    depends_on:
      - pg-master
    volumes:
      - replica-data:/var/lib/postgresql/data

volumes:
  master-data:
  replica-data:

扩展和继承

# 使用 extends 继承配置(已弃用,推荐使用 YAML 锚点)
# docker-compose.yml
x-common-config: &common-config
  restart: unless-stopped
  networks:
    - app-network
  logging:
    driver: json-file
    options:
      max-size: "10m"

services:
  api:
    <<: *common-config
    image: myapp:api
    ports:
      - "3000:3000"

  worker:
    <<: *common-config
    image: myapp:worker
    command: node worker.js

networks:
  app-network:
test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s # 6. 使用网络隔离 networks: frontend: backend: internal: true # 7. 使用 secrets 管理敏感数据 secrets: db_password: file: ./secrets/db_password.txt

常用命令

镜像管理

docker images                    # 列出镜像
docker pull nginx:alpine         # 拉取镜像
docker rmi nginx:alpine          # 删除镜像
docker tag myapp:v1 myapp:latest # 标记镜像
docker save -o app.tar myapp:v1  # 导出镜像
docker load -i app.tar           # 导入镜像

容器管理

docker ps                        # 列出运行中容器
docker ps -a                     # 列出所有容器
docker run -d -p 80:80 nginx     # 后台运行容器
docker stop container_id         # 停止容器
docker rm container_id           # 删除容器
docker exec -it container_id sh  # 进入容器
docker logs -f container_id      # 查看日志
docker cp file.txt container:/app # 复制文件到容器

系统管理

docker system df                # 查看磁盘使用
docker system prune             # 清理未使用资源
docker system prune -a          # 清理所有未使用资源
docker stats                    # 实时资源使用

网络

Docker 提供多种网络模式,满足不同应用场景:

🌐 Docker 网络
bridge
默认网络
独立 IP
端口映射
host
共享主机网络
无隔离
高性能
none
禁用网络
完全隔离
overlay
跨主机
Swarm 集群

网络类型

类型 说明 应用场景
bridge 默认网络,容器间可通过 IP 通信 单机多容器应用
host 容器共享主机网络,无网络隔离 高性能网络应用、监控
none 禁用网络 安全隔离、离线任务
overlay 跨主机网络(需 Swarm) 分布式集群应用
macvlan 容器拥有独立 MAC 地址 需要物理网络直接访问

网络架构示意图

外部网络(互联网)
宿主机网络接口 (eth0)
Docker 网桥 (docker0 / 172.17.0.1)
容器A
172.17.0.2
容器B
172.17.0.3
容器C
172.17.0.4

常用命令

# 查看网络列表
docker network ls

# 创建自定义网络
docker network create mynet

# 创建指定类型的网络
docker network create -d bridge mynet
docker network create -d host myhost

# 查看网络详情
docker network inspect mynet

# 连接容器到网络
docker network connect mynet container_name

# 断开容器网络
docker network disconnect mynet container_name

# 删除网络
docker network rm mynet

应用场景示例

场景 1:微服务通信

# 创建应用网络
docker network create app-network

# 启动服务,同一网络内可通过服务名访问
docker run -d --name api --network app-network myapi
docker run -d --name web --network app-network nginx
# web 容器可通过 http://api:8080 访问 API

场景 2:隔离不同环境

# 开发环境网络
docker network create dev-network

# 测试环境网络
docker network create test-network

# 开发和测试环境完全隔离
docker run -d --name dev-db --network dev-network postgres
docker run -d --name test-db --network test-network postgres

场景 3:DNS 解析

# 同一网络内的容器自动获得 DNS 解析
docker run -d --name redis --network mynet redis
docker run -d --name app --network mynet myapp

# app 容器内可直接使用主机名连接
# redis://redis:6379

容器与外界通讯过程

理解容器如何与外界通讯是掌握 Docker 网络的关键。

通讯架构图

┌─────────────────────────────────────────────────────────────────┐
│                         外部网络(互联网)                         │
└─────────────────────────────────────────────────────────────────┘
                              ↑↓
┌─────────────────────────────────────────────────────────────────┐
│                        宿主机 (Host)                              │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    Docker 网桥 (docker0)                   │   │
│  │                    172.17.0.1 (网关)                       │   │
│  └──────────────────────────────────────────────────────────┘   │
│         ↑                    ↑                    ↑              │
│    ┌────────┐           ┌────────┐           ┌────────┐         │
│    │ 容器 A  │           │ 容器 B  │           │ 容器 C  │         │
│    │172.17.0.2│          │172.17.0.3│          │172.17.0.4│        │
│    │ 端口 80 │           │ 端口 3000│          │ 端口 5432│        │
│    └────────┘           └────────┘           └────────┘         │
│         ↑                    ↑                    ↑              │
│    主机端口                主机端口              主机端口         │
│    8080→80               3000→3000            5432→5432         │
└─────────────────────────────────────────────────────────────────┘

1. 容器访问外部网络

# 容器访问互联网的过程:
# 1. 容器发送请求(源 IP: 172.17.0.2)
# 2. 请求经过 docker0 网桥
# 3. 通过 NAT(网络地址转换)转换为宿主机 IP
# 4. 请求发送到外部网络
# 5. 响应按原路返回

# 示例:容器内访问外部 API
docker run --rm alpine wget -qO- https://api.github.com

# 查看容器的网络配置
docker run --rm alpine ip addr show
# eth0: 172.17.0.x → docker0 网桥

# 查看宿主机的 iptables NAT 规则
sudo iptables -t nat -L -n

2. 外部访问容器(端口映射)

# 端口映射原理:
# 外部请求 → 宿主机端口 → iptables 转发 → 容器端口

# 映射单个端口
docker run -d -p 8080:80 nginx
# 宿主机 8080 → 容器 80

# 映射多个端口
docker run -d -p 8080:80 -p 8443:443 nginx

# 指定 IP 绑定
docker run -d -p 127.0.0.1:8080:80 nginx
# 只允许本地访问

# 映射 UDP 端口
docker run -d -p 53:53/udp dns-server

# 查看端口映射
docker port container_name
docker ps --format "table {{.Names}}\t{{.Ports}}"

# 验证端口监听
netstat -tlnp | grep 8080
# 或
lsof -i :8080

3. 容器间通讯

# 同一 bridge 网络内的容器通讯:

# 方式 1:通过 IP 地址
# 容器 A (172.17.0.2) → 容器 B (172.17.0.3)
curl http://172.17.0.3:3000

# 方式 2:通过容器名称(需要自定义网络)
docker network create mynet
docker run -d --name db --network mynet postgres
docker run -d --name app --network mynet myapp

# app 容器可以通过名称访问 db
# postgresql://db:5432/mydb

# 查看网络内容器的 IP
docker network inspect mynet

# 进入容器测试连通性
docker exec -it app ping db
docker exec -it app curl http://db:5432

4. 不同网络模式对比

网络模式 容器 IP 访问外部 外部访问容器 容器间通讯
bridge 独立 IP(如 172.17.0.x) ✅ 通过 NAT ✅ 需要端口映射 ✅ 通过 IP 或名称
host 共享宿主机 IP ✅ 直接访问 ✅ 直接访问容器端口 ✅ 通过 localhost
none ❌ 无法访问 ❌ 无法访问 ❌ 无法通讯
macvlan 物理网络 IP ✅ 直接访问 ✅ 直接访问 ✅ 通过物理网络

5. 网络通讯完整示例

# 场景:Web 应用 + 数据库 + Redis

# 1. 创建网络
docker network create app-network

# 2. 启动数据库(内部端口,不映射到外部)
docker run -d \
  --name db \
  --network app-network \
  -e POSTGRES_PASSWORD=secret \
  postgres:16-alpine
# 数据库只能在网络内部访问,外部无法直接连接

# 3. 启动 Redis(内部端口)
docker run -d \
  --name redis \
  --network app-network \
  redis:7-alpine

# 4. 启动 Web 应用(映射端口到外部)
docker run -d \
  --name web \
  --network app-network \
  -p 3000:3000 \
  -e DATABASE_URL=postgres://postgres:secret@db:5432 \
  -e REDIS_URL=redis://redis:6379 \
  myapp

# 通讯路径:
# 外部用户 → 宿主机:3000 → web 容器:3000
# web 容器 → db:5432(内部 DNS 解析)
# web 容器 → redis:6379(内部 DNS 解析)

# 验证通讯
docker exec -it web ping db      # Web → DB
docker exec -it web ping redis    # Web → Redis
curl http://localhost:3000        # 外部 → Web

6. 常见网络问题排查

# 问题 1:容器无法访问外部网络
# 检查 DNS
docker run --rm alpine nslookup google.com
docker run --rm alpine ping -c 3 8.8.8.8

# 问题 2:外部无法访问容器端口
# 检查端口映射
docker port container_name
# 检查防火墙
sudo ufw status
sudo iptables -L -n | grep 8080

# 问题 3:容器间无法通讯
# 确认在同一网络
docker network inspect mynet
# 检查容器网络配置
docker exec container_name ip addr show

# 问题 4:DNS 解析失败
# 使用自定义 DNS
docker run --dns 8.8.8.8 myapp

# 问题 5:端口冲突
# 查看端口占用
lsof -i :8080
netstat -tlnp | grep 8080

数据卷

数据卷用于持久化容器数据,独立于容器生命周期。

📦 Volume
Docker 管理
跨平台兼容
易于备份迁移
多容器共享
路径:/var/lib/docker/volumes/
VS
📁 Bind Mount
直接访问主机文件
开发热重载
依赖主机路径
需要绝对路径
路径:主机任意目录

存储架构关系

🐳
容器
Container
📦 Volume
Docker管理
📁 Bind Mount
主机目录
💾 tmpfs
内存存储

存储类型对比

类型 说明 应用场景
Volume Docker 管理的存储,存储在 /var/lib/docker/volumes 数据库、持久化数据、多容器共享
Bind Mount 绑定主机目录到容器 开发环境、配置文件挂载
tmpfs 存储在内存中,容器停止即消失 敏感数据、临时缓存

数据流向示意

📝
写入数据
/app/data
容器内路径
📦
Volume
mydata
Docker 管理
💾
/var/lib/docker
/volumes/mydata
宿主机存储

常用命令

# 创建数据卷
docker volume create mydata

# 列出所有数据卷
docker volume ls

# 查看数据卷详情
docker volume inspect mydata

# 删除数据卷
docker volume rm mydata

# 清理未使用的数据卷
docker volume prune

应用场景示例

场景 1:数据库持久化

# PostgreSQL 数据持久化
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:16-alpine

# 即使容器删除,数据仍然保留
docker rm -f postgres
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16-alpine
# 数据恢复

场景 2:开发环境热重载

# 挂载本地代码目录,修改立即生效
docker run -d \
  --name dev-app \
  -v $(pwd)/src:/app/src \
  -v $(pwd)/package.json:/app/package.json \
  -p 3000:3000 \
  node:20-alpine \
  npm run dev

# 本地修改代码,容器内自动更新

场景 3:配置文件管理

# 挂载配置文件
docker run -d \
  --name nginx \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v $(pwd)/html:/usr/share/nginx/html:ro \
  -p 80:80 \
  nginx:alpine

# :ro 表示只读挂载,防止容器修改配置

场景 4:多容器共享数据

# 创建共享数据卷
docker volume create shared-data

# 写入容器
docker run -d --name writer \
  -v shared-data:/data \
  alpine sh -c "echo 'hello' > /data/file.txt"

# 读取容器
docker run --name reader \
  -v shared-data:/data \
  alpine cat /data/file.txt
# 输出: hello

场景 5:敏感数据(tmpfs)

# 使用内存存储敏感数据

            docker run -d \

              --name app \

              --tmpfs /run/secrets:rw,size=10m,mode=1777 \

              myapp

            

            # 数据不会写入磁盘,容器停止即消失

容器与文件系统交互原理

理解容器如何与文件系统交互,有助于更好地管理数据。

存储架构图

容器存储系统由多个层次组成,从下到上分别是:

宿主机文件系统
📁 命名卷
/var/lib/docker/volumes/mydata/_data
Docker管理的持久化存储
📁 绑定挂载
/home/user/project/src
直接映射到宿主机目录
💾 内存存储
/run/secrets
tmpfs存储(容器停止后消失)
↓ 挂载
容器文件系统
镜像层(只读)
基础系统层
依赖包层
应用代码层
容器层(可写)
运行时修改的文件存储在这里

挂载点示例: /app/data → 宿主机目录,/app/src → 代码目录

1. 镜像分层与联合文件系统

# Docker 使用联合文件系统(UnionFS)将多个层合并为一个文件系统

            

            # 查看镜像层

            docker image inspect nginx:alpine --format='{{json .RootFS.Layers}}'

            

            # 镜像层特点:

            # - 只读:镜像层不可修改

            # - 分层存储:相同基础镜像的层共享

            # - 按需加载:只加载需要的层

            

            # 容器层特点:

            # - 可写:容器运行时的所有修改都在这里

            # - 写时复制(Copy-on-Write):

            #   修改镜像中的文件时,先复制到容器层再修改

            # - 容器删除后,容器层消失

            

            # 示例:查看容器的存储驱动

            docker info | grep "Storage Driver"

            # 通常为 overlay2

2. 写时复制(Copy-on-Write)机制

# 场景:修改镜像中的文件

            

            # 1. 容器启动时,文件在镜像层(只读)

            #    /etc/nginx/nginx.conf → 镜像层

            

            # 2. 容器修改文件时:

            #    - Docker 将文件从镜像层复制到容器层

            #    - 容器层现在有一个可写副本

            #    - 镜像层的原文件保持不变

            

            # 3. 后续读取时:

            #    - 优先读取容器层的文件

            #    - 容器层没有才读取镜像层

            

            # 验证写时复制

            docker run --name test nginx:alpine

            docker exec test sh -c "ls -la /etc/nginx/nginx.conf"

            # 此时文件在镜像层

            

            docker exec test sh -c "echo 'modified' >> /etc/nginx/nginx.conf"

            # 触发写时复制,文件被复制到容器层

            

            # 查看容器层的修改

            docker diff test

            # C /etc/nginx  (C = Changed)

            # C /etc/nginx/nginx.conf

3. 三种存储类型的交互方式

# ===== 1. Volume(命名卷)=====

            # Docker 管理的存储,与容器完全解耦

            

            # 创建与挂载过程:

            # 1. docker volume create mydata

            #    → 在 /var/lib/docker/volumes/mydata/_data 创建目录

            

            # 2. docker run -v mydata:/app/data myapp

            #    → 将 /var/lib/docker/volumes/mydata/_data 挂载到容器 /app/data

            

            # 3. 容器写入 /app/data/file.txt

            #    → 文件实际写入 /var/lib/docker/volumes/mydata/_data/file.txt

            

            # 验证

            docker volume create test-vol

            docker run --rm -v test-vol:/data alpine sh -c "echo 'hello' > /data/test.txt"

            sudo ls -la /var/lib/docker/volumes/test-vol/_data/

            # 可以看到 test.txt 文件

            

            # ===== 2. Bind Mount(绑定挂载)=====

            # 直接映射主机目录到容器

            

            # 挂载过程:

            # docker run -v /host/path:/container/path myapp

            # → 将主机 /host/path 直接映射到容器 /container/path

            

            # 特点:

            # - 双向同步:主机修改 → 容器可见,容器修改 → 主机可见

            # - 性能最好(无额外抽象层)

            # - 路径依赖主机

            

            # 验证

            mkdir -p /tmp/bind-test

            echo "from host" > /tmp/bind-test/host.txt

            docker run --rm -v /tmp/bind-test:/data alpine cat /data/host.txt

            # 输出: from host

            

            docker run --rm -v /tmp/bind-test:/data alpine sh -c "echo 'from container' > /data/container.txt"

            cat /tmp/bind-test/container.txt

            # 输出: from container

            

            # ===== 3. tmpfs(内存存储)=====

            # 数据存储在内存中

            

            # 挂载过程:

            # docker run --tmpfs /data myapp

            # → 在内存中创建临时文件系统,挂载到容器 /data

            

            # 特点:

            # - 最快的存储方式(内存速度)

            # - 容器停止后数据消失

            # - 不会写入磁盘

            

            # 验证

            docker run --rm --tmpfs /data alpine sh -c "mount | grep /data"

            # 输出: tmpfs on /data type tmpfs ...

4. 挂载行为详解

# 挂载时容器目录的行为:

            

            # 情况 1:挂载到空目录

            docker run -v mydata:/app/data nginx

            # 容器 /app/data 为空 → 挂载后显示卷内容(可能为空)

            

            # 情况 2:挂载到有内容的目录

            docker run -v mydata:/usr/share/nginx/html nginx

            # 容器 /usr/share/nginx/html 有默认文件

            # 挂载后:容器原内容被隐藏,显示卷内容

            # 注意:如果卷为空,目录变空!

            

            # 情况 3:绑定挂载覆盖

            docker run -v /host/html:/usr/share/nginx/html nginx

            # 主机目录内容覆盖容器目录内容

            

            # 安全做法:先检查卷内容

            docker run --rm -v mydata:/data alpine ls -la /data/

            

            # 使用只读挂载防止意外修改

            docker run -v mydata:/app/data:ro nginx

            # 容器无法写入 /app/data

5. 数据流向示例

# 完整示例:Web 应用数据流向

            

            # 启动一个完整的 Web 应用栈

            docker run -d \

              --name web \

              -v web-code:/var/www/html \      # 网站代码(命名卷)

              -v /host/config:/etc/nginx:ro \  # 配置文件(绑定挂载,只读)

              -v web-logs:/var/log/nginx \     # 日志(命名卷)

              --tmpfs /var/cache/nginx \       # 缓存(内存)

              -p 80:80 \

              nginx:alpine

            

            # 数据流向分析:

            # 1. 用户访问 → Nginx 读取 /var/www/html/index.html

            #    → 实际读取 /var/lib/docker/volumes/web-code/_data/index.html

            

            # 2. Nginx 启动时读取配置 /etc/nginx/nginx.conf

            #    → 实际读取 /host/config/nginx.conf(主机文件)

            

            # 3. Nginx 写入日志 /var/log/nginx/access.log

            #    → 实际写入 /var/lib/docker/volumes/web-logs/_data/access.log

            

            # 4. Nginx 写入缓存 /var/cache/nginx/...

            #    → 写入内存,容器停止后消失

            

            # 验证各存储位置

            sudo ls -la /var/lib/docker/volumes/web-code/_data/

            sudo ls -la /var/lib/docker/volumes/web-logs/_data/

            ls -la /host/config/

6. 文件系统问题排查

# 问题 1:磁盘空间不足

            docker system df                    # 查看磁盘使用

            docker volume ls                    # 列出所有卷

            docker volume prune                 # 清理未使用的卷

            

            # 问题 2:权限问题

            # 容器内用户 ID 与主机不匹配

            docker run -v /host/data:/data myapp

            # 解决方案:使用相同的用户 ID

            docker run -u 1000:1000 -v /host/data:/data myapp

            

            # 问题 3:找不到挂载的文件

            # 检查挂载点

            docker inspect container_name --format='{{json .Mounts}}'

            # 或

            docker inspect container_name | grep -A 10 "Mounts"

            

            # 问题 4:绑定挂载不生效

            # 检查路径是否存在

            ls -la /host/path

            # 检查权限

            ls -la /host/path

            # 检查 Docker Desktop 设置(macOS/Windows 需要共享目录)

            

            # 问题 5:容器内文件被覆盖

            # 了解挂载行为:挂载会覆盖容器内原有内容

            # 解决:先备份数据,或使用命名卷的初始化功能

            

            # 查看容器的存储使用

            docker ps -s                        # 显示容器大小

            # SIZE:容器可写层大小

            # VIRTUAL SIZE:镜像 + 容器层总大小

7. 存储类型选择指南

需求 推荐类型 原因
数据库持久化 Volume Docker 管理,易于备份迁移
开发时代码同步 Bind Mount 实时同步,IDE 直接编辑
配置文件 Bind Mount (:ro) 版本控制,只读防误改
敏感数据 tmpfs 内存存储,不留痕迹
多容器共享 Volume 独立于容器,可共享
临时缓存 tmpfs 高速访问,自动清理

企业内部 Registry

Docker Registry 是存储和分发 Docker 镜像的服务,企业可自建私有 Registry 来管理内部镜像。

为什么需要私有 Registry?

快速搭建 Registry

# 1. 最简单的启动方式
docker run -d \
  --name registry \
  -p 5000:5000 \
  -v registry-data:/var/lib/registry \
  --restart always \
  registry:2

# 2. 使用 Docker Compose(推荐)
# docker-compose.yml
services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
    volumes:
      - registry-data:/var/lib/registry
    restart: always

volumes:
  registry-data:

推送和拉取镜像

# 1. 标记镜像指向私有仓库
docker tag myapp:v1 localhost:5000/myapp:v1

# 2. 推送镜像
docker push localhost:5000/myapp:v1

# 3. 拉取镜像
docker pull localhost:5000/myapp:v1

# 4. 查看仓库中的镜像
curl http://localhost:5000/v2/_catalog
curl http://localhost:5000/v2/myapp/tags/list

配置 HTTPS(生产必需)

# 方案 1:使用 Nginx 反向代理 + Let's Encrypt

# docker-compose.yml
services:
  registry:
    image: registry:2
    volumes:
      - registry-data:/var/lib/registry
    restart: always

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - registry

volumes:
  registry-data:

# nginx.conf 示例
# server {
#     listen 443 ssl;
#     server_name registry.example.com;
#     ssl_certificate /etc/nginx/certs/fullchain.pem;
#     ssl_certificate_key /etc/nginx/certs/privkey.pem;
#     client_max_body_size 0;  # 不限制上传大小
#     location / {
#         proxy_pass http://registry:5000;
#     }
# }

配置认证

# 1. 创建用户密码文件
docker run --rm \
  --entrypoint htpasswd \
  httpd:2 -Bbn admin password123 > htpasswd

# 2. 启动带认证的 Registry
docker run -d \
  --name registry \
  -p 5000:5000 \
  -v registry-data:/var/lib/registry \
  -v $(pwd)/htpasswd:/etc/docker/registry/htpasswd \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/htpasswd \
  registry:2

# 3. 登录私有仓库
docker login localhost:5000
# 输入用户名和密码

# 4. 推送镜像(需要先登录)
docker push localhost:5000/myapp:v1

# 5. 退出登录
docker logout localhost:5000

完整生产配置

# docker-compose.yml - 生产环境完整配置
services:
  registry:
    image: registry:2
    volumes:
      - registry-data:/var/lib/registry
      - ./config.yml:/etc/docker/registry/config.yml:ro
    environment:
      - REGISTRY_HTTP_ADDR=0.0.0.0:5000
      - REGISTRY_LOG_LEVEL=info
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`registry.example.com`)"
      - "traefik.http.routers.registry.tls.certresolver=letsencrypt"
    restart: always
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:5000/v2/"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 可选:Web 管理界面
  registry-ui:
    image: joxit/docker-registry-ui:latest
    ports:
      - "8080:80"
    environment:
      - REGISTRY_TITLE=My Private Registry
      - REGISTRY_URL=http://registry:5000
      - DELETE_IMAGES=true
      - SHOW_CONTENT_DIGEST=true
    depends_on:
      - registry

  # 可选:镜像清理服务
  registry-gc:
    image: registry:2
    entrypoint: ["bin/registry", "garbage-collect", "/etc/docker/registry/config.yml"]
    volumes:
      - registry-data:/var/lib/registry
      - ./config.yml:/etc/docker/registry/config.yml:ro

volumes:
  registry-data:

客户端配置

# 配置 Docker 客户端信任私有仓库

# 1. 配置 insecure-registry(仅用于 HTTP,不推荐生产)
# /etc/docker/daemon.json
{
  "insecure-registries": ["registry.example.com:5000"]
}

# 重启 Docker
sudo systemctl restart docker

# 2. 对于自签名证书,需要信任证书
sudo mkdir -p /etc/docker/certs.d/registry.example.com
sudo cp ca.crt /etc/docker/certs.d/registry.example.com/

# 3. 企业环境建议使用企业 CA 证书
# 或使用 Let's Encrypt 等受信任的证书

镜像管理操作

# 查看所有仓库
curl -u admin:password123 http://localhost:5000/v2/_catalog

# 查看某镜像的所有标签
curl -u admin:password123 http://localhost:5000/v2/myapp/tags/list

# 删除镜像(需要配置 DELETE_IMAGES=true)
curl -X DELETE -u admin:password123 \
  http://localhost:5000/v2/myapp/manifests/sha256:xxx

# 获取镜像 digest
curl -I -u admin:password123 \
  -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
  http://localhost:5000/v2/myapp/manifests/v1

# 清理未引用的层(垃圾回收)
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml

备份与迁移

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

# 恢复数据
docker run --rm \
  -v registry-data:/data \
  -v $(pwd)/backup:/backup \
  alpine sh -c "cd /data && tar xzf /backup/registry-backup.tar.gz"

# 迁移到新服务器
# 1. 停止原 Registry
docker stop registry

# 2. 打包数据卷
docker run --rm \
  -v registry-data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/registry-data.tar.gz -C /data .

# 3. 复制到新服务器并解压
scp registry-data.tar.gz user@new-server:/tmp/
ssh user@new-server
docker volume create registry-data
docker run --rm \
  -v registry-data:/data \
  -v /tmp:/backup \
  alpine sh -c "cd /data && tar xzf /backup/registry-data.tar.gz"

Harbor - 企业级 Registry

对于大型企业,推荐使用 Harbor,它提供了更丰富的功能:

# Harbor 功能特点:
# - Web 管理界面
# - 基于角色的访问控制(RBAC)
# - 漏洞扫描(Trivy 集成)
# - 镜像签名(Notary)
# - 镜像复制(跨区域同步)
# - 垃圾回收自动化
# - 审计日志

# 快速安装 Harbor
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzf harbor-offline-installer-v2.10.0.tgz
cd harbor

# 配置 harbor.yml
cp harbor.yml.tmpl harbor.yml
# 编辑 harbor.yml,设置 hostname、密码等

# 安装
./install.sh

# 或使用 Docker Compose
docker compose -f docker-compose.yml up -d

# 访问:https://your-harbor-domain
# 默认用户名:admin
# 默认密码:Harbor12345

Registry 方案对比

方案 复杂度 功能 适用场景
Docker Registry ⭐ 简单 基础存储分发 小型团队、测试环境
Registry + Nginx ⭐⭐ 中等 HTTPS、认证 中型团队、生产环境
Harbor ⭐⭐⭐ 复杂 全功能企业级 大型企业、合规要求
Nexus ⭐⭐⭐ 复杂 多格式仓库 统一制品管理

安全最佳实践

✅ 使用非 root 用户

RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup appuser
USER appuser

✅ 最小化镜像

FROM alpine:3.19
# 或使用 distroless
FROM gcr.io/distroless/static

✅ 健康检查

HEALTHCHECK --interval=30s \
  CMD wget -q --spider \
  http://localhost:3000/health

Docker 周边工具

Docker 生态系统拥有丰富的周边工具,覆盖管理、监控、安全、开发等多个领域,帮助提升工作效率。

🔍 Docker Scout - 安全扫描工具

什么是 Docker Scout?

Docker Scout 是 Docker 官方的安全分析工具,可以扫描你的镜像,找出其中的安全漏洞,并告诉你如何修复。

为什么需要它?

  • 你的镜像可能包含有漏洞的软件包
  • 新漏洞每天都在被发现,需要定期检查
  • 生产环境必须确保安全合规

如何使用?

# 1. 快速查看镜像安全概况
docker scout quickview nginx:latest

# 2. 详细漏洞扫描
docker scout cves myapp:latest

# 3. 只显示高危漏洞
docker scout cves --severity HIGH,CRITICAL myapp:latest

# 4. 生成软件物料清单(SBOM)
docker scout sbom --format spdx-json --output sbom.json myapp:latest

# 5. 比较两个镜像的安全差异
docker scout compare --from myapp:v1 --to myapp:v2

# 6. 在 CI/CD 中自动阻断不安全镜像
docker scout cves --exit-code 1 --severity HIGH,CRITICAL myapp:latest

初学者常见问题

Q: 扫描结果显示很多漏洞,我该怎么办?
A: 优先修复 CRITICAL 和 HIGH 级别的漏洞,Docker Scout 会给出具体的修复建议。

Q: 每次构建都需要扫描吗?
A: 建议在 CI/CD 流程中加入自动扫描,确保不安全的镜像不会部署到生产环境。

🤖 Ask Gordon - Docker AI 助手

什么是 Ask Gordon?

Ask Gordon 是 Docker 内置的 AI 助手,你可以用自然语言与它对话,让它帮你编写 Dockerfile、诊断问题、优化配置。

为什么需要它?

  • 不懂 Dockerfile 语法?让 AI 帮你写
  • 遇到错误不知道怎么解决?问 Gordon
  • 想知道最佳实践?直接问

如何使用?

# 启动 Ask Gordon(需要 Docker Desktop)
docker ai

# 示例对话:
You: 帮我为 Node.js 应用写一个 Dockerfile
Gordon: 好的,这是为你生成的 Dockerfile...
[生成优化的 Dockerfile]

You: 我的容器启动后立即退出了,怎么回事?
Gordon: 让我帮你分析可能的原因...
[给出诊断建议]

You: 如何减小镜像大小?
Gordon: 这里有一些优化建议...
[列出具体的优化方法]

常见使用场景

# 场景 1:生成 Dockerfile
"帮我为一个 Python Flask 应用创建 Dockerfile"

# 场景 2:诊断问题
"容器报错 'connection refused',如何排查?"

# 场景 3:优化建议
"这个 Dockerfile 有什么可以优化的地方?"

# 场景 4:学习概念
"什么是多阶段构建?为什么要用它?"

📦 Dockerizer - 自动容器化工具

什么是 Dockerizer?

Dockerizer 是一个 AI 驱动的工具,可以自动分析你的项目代码,生成 Dockerfile 和 docker-compose.yml,无需手动编写。

为什么需要它?

  • 不知道如何为项目写 Dockerfile?自动生成
  • 不确定最佳实践?AI 帮你优化
  • 项目结构复杂?自动识别依赖

如何使用?

# 1. 安装 Dockerizer
npm install -g dockerizer

# 2. 在项目目录中初始化
cd my-project
npx dockerizer init

# Dockerizer 会:
# - 分析 package.json / requirements.txt 等依赖文件
# - 检测项目类型(Node.js / Python / Java 等)
# - 生成优化的 Dockerfile
# - 生成 docker-compose.yml(可选)

# 3. 分析现有项目
npx dockerizer analyze

# 4. 生成多阶段构建的 Dockerfile
npx dockerizer init --multi-stage

# 5. 为特定框架生成
npx dockerizer init --framework nextjs
npx dockerizer init --framework django

生成示例

# Dockerizer 自动生成的 Dockerfile 示例
# 项目类型:Node.js Express

# --- 基础阶段 ---
FROM node:20-alpine AS base
WORKDIR /app

# --- 依赖阶段 ---
FROM base AS deps
COPY package*.json ./
RUN npm ci --only=production

# --- 运行阶段 ---
FROM base
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]

🖥️ Dockge - 可视化管理界面

什么是 Dockge?

Dockge 是一个现代化的 Docker Compose 可视化管理界面,让你通过网页界面管理容器,无需记住复杂的命令。

为什么需要它?

  • 不喜欢命令行?用图形界面操作
  • 想查看容器日志?一键查看
  • 需要管理多个项目?统一界面管理

如何安装?

# 方法 1:Docker 命令(推荐)
docker run -d \
  --name dockge \
  -p 5001:5001 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v ~/dockge/data:/app/data \
  louislam/dockge:latest

# 方法 2:Docker Compose
services:
  dockge:
    image: louislam/dockge:latest
    ports:
      - "5001:5001"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
    restart: unless-stopped

# 访问界面
# 打开浏览器:http://localhost:5001

主要功能

# 1. 管理 Compose 项目
# - 查看所有服务状态
# - 启动/停止/重启服务
# - 查看实时日志

# 2. 编辑 compose 文件
# - 内置 YAML 编辑器
# - 语法高亮
# - 自动补全

# 3. 容器终端
# - 直接在网页中进入容器
# - 执行命令

# 4. 资源监控
# - CPU/内存使用情况
# - 网络流量

🧠 Docker GenAI Stack - AI 应用开发栈

什么是 Docker GenAI Stack?

Docker GenAI Stack 是 Docker 官方提供的生成式 AI 开发模板,包含运行大语言模型所需的所有组件。

为什么需要它?

  • 想本地运行 AI 模型?一键部署
  • 开发 AI 应用?提供完整模板
  • 学习大语言模型?开箱即用

如何使用?

# 1. 克隆项目
git clone https://github.com/docker/genai-stack
cd genai-stack

# 2. 启动服务
docker compose up -d

# 3. 访问应用
# 打开浏览器:http://localhost:8501

# 包含的组件:
# - Ollama:本地运行大语言模型
# - LangChain:AI 应用开发框架
# - Neo4j:图数据库(可选)
# - Streamlit:Web 界面

运行 AI 模型示例

# 在容器中运行 Ollama
docker exec -it ollama ollama pull llama2
docker exec -it ollama ollama run llama2

# 或者使用 Docker 直接运行 Ollama
docker run -d \
  --name ollama \
  -p 11434:11434 \
  -v ollama-data:/root/.ollama \
  ollama/ollama

# 拉取并运行模型
docker exec -it ollama ollama pull llama2
docker exec -it ollama ollama pull codellama
docker exec -it ollama ollama pull mistral

🔒 Trivy AI 增强版 - 漏洞扫描工具

什么是 Trivy?

Trivy 是一个全面的安全扫描器,可以扫描容器镜像、文件系统、Git 仓库,发现漏洞、配置问题和敏感信息泄露。AI 增强版提供智能修复建议。

为什么需要它?

  • Docker Scout 的开源替代方案
  • 扫描更全面(漏洞+配置+密钥)
  • AI 提供智能修复建议

如何安装和使用?

# 安装 Trivy
# macOS
brew install trivy

# Windows (使用 Scoop)
scoop install trivy

# Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# 基本使用
# 1. 扫描镜像
trivy image nginx:latest

# 2. 扫描本地文件系统
trivy fs .

# 3. 扫描配置文件
trivy config .

# 4. 扫描多种类型
trivy image --scanners vuln,secret,misconfig myapp:latest

# 5. 输出 JSON 格式
trivy image --format json --output report.json myapp:latest

# 6. 只显示高危漏洞
trivy image --severity HIGH,CRITICAL myapp:latest

CI/CD 集成示例

# GitHub Actions
name: Security Scan
on: [push]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          severity: 'HIGH,CRITICAL'
          exit-code: '1'  # 发现漏洞时失败

扫描结果解读

trivy image myapp:latest

# 输出示例:
# ┌──────────────┬───────────────┬──────────────┬─────────────┐
# │   Library    │ Vulnerability │  Severity    │   Version   │
# ├──────────────┼───────────────┼──────────────┼─────────────┤
# │ openssl      │ CVE-2024-1234 │ CRITICAL     │ 1.1.1k      │
# │ node         │ CVE-2024-5678 │ HIGH         │ 18.0.0      │
# └──────────────┴───────────────┴──────────────┴─────────────┘

# 解读:
# - CRITICAL: 必须立即修复
# - HIGH: 应尽快修复
# - MEDIUM: 计划修复
# - LOW: 可忽略

工具对比与选择指南

工具 主要用途 适合人群 难度
Docker Scout 官方安全扫描 所有 Docker 用户 ⭐ 简单
Ask Gordon AI 助手、学习指导 初学者 ⭐ 简单
Dockerizer 自动生成配置 不想手写配置的人 ⭐ 简单
Dockge 可视化管理 喜欢图形界面的人 ⭐ 简单
GenAI Stack AI 应用开发 AI 开发者 ⭐⭐ 中等
Trivy 全面安全扫描 安全运维人员 ⭐⭐ 中等

🖥️ 可视化管理工具

Portainer - 最流行的管理界面

Portainer 是一个轻量级的 Docker 管理界面,提供直观的 Web UI 来管理容器、镜像、网络和卷。

# 快速启动
docker run -d \
  --name portainer \
  -p 9443:9443 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer-data:/data \
  --restart always \
  portainer/portainer-ce:latest

# 访问:https://localhost:9443
# 首次访问需要设置管理员密码

# Docker Compose 方式
services:
  portainer:
    image: portainer/portainer-ce:latest
    ports:
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer-data:/data
    restart: always

volumes:
  portainer-data:

主要功能:容器管理、镜像管理、网络配置、卷管理、用户权限控制、Stack 管理、模板部署

Lazydocker - 终端交互式管理

Lazydocker 是一个终端 UI 工具,用键盘即可管理 Docker,适合喜欢命令行的用户。

# 安装(macOS)
brew install jesseduffield/lazydocker/lazydocker

# 安装(Linux)
curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash

# 安装(Windows)
scoop install lazydocker

# 或使用 Docker 运行
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  lazyteam/lazydocker

# 启动
lazydocker

# 快捷键:
# - 上/下:选择项目
# - Enter:查看详情
# - x:停止/删除
# - l:查看日志
# - s:进入容器 Shell
# - ?:帮助

ctop - 容器实时监控

ctop 提供实时的容器资源监控视图,类似 top 命令但专为容器设计。

# 安装(macOS)
brew install ctop

# 安装(Linux)
sudo wget https://github.com/bcicen/ctop/releases/download/v1.0.7/ctop-1.0.7-linux-amd64 -O /usr/local/bin/ctop
sudo chmod +x /usr/local/bin/ctop

# 使用 Docker 运行
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  quay.io/vektorlab/ctop:latest

# 启动
ctop

# 快捷键:
# - a:切换显示所有/运行中容器
# - l:查看容器日志
# - s:进入容器 Shell
# - e:显示环境变量
# - h:帮助

🔧 开发辅助工具

Dive - 镜像分析工具

Dive 帮助你分析 Docker 镜像每一层的内容,找出可以优化的地方,减小镜像体积。

# 安装(macOS)
brew install dive

# 安装(Linux)
wget https://github.com/wagoodman/dive/releases/download/v0.12.0/dive_0.12.0_linux_amd64.deb
sudo apt install ./dive_0.12.0_linux_amd64.deb

# 分析镜像
dive nginx:alpine

# 分析本地构建的镜像
dive myapp:latest

# 在 CI 中使用
dive --ci myapp:latest

# 输出信息:
# - 每层的文件变更
# - 可能的优化建议
# - 镜像效率评分
# - 重复文件检测

Hadolint - Dockerfile 语法检查

Hadolint 是 Dockerfile 的 lint 工具,检查语法错误和最佳实践违反。

# 安装(macOS)
brew install hadolint

# 安装(Linux)
wget -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
chmod +x hadolint
sudo mv hadolint /usr/local/bin/

# 使用 Docker 运行
docker run --rm -i hadolint/hadolint < Dockerfile

# 检查 Dockerfile
hadolint Dockerfile

# 只显示错误(忽略警告)
hadolint --ignore DL3008 Dockerfile

# 输出 JSON 格式
hadolint -f json Dockerfile

# 常见规则:
# DL3000: 使用绝对 WORKDIR
# DL3001: 不要使用 MAINTAINER(已废弃)
# DL3003: 不要使用 WORKDIR 切换目录
# DL3004: 不要以 root 用户构建
# DL3008: 固定 apt 包版本
# DL3015: 避免使用 --no-install-recommends 以外的选项

# VS Code 集成
# 安装扩展:exiasr.hadolint

DevContainer - 开发容器化

DevContainer 让你在容器中进行开发,确保开发环境一致性,支持 VS Code 和 GitHub Codespaces。

# .devcontainer/devcontainer.json
{
  "name": "My Dev Container",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {
      "version": "20"
    },
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-sqlite",
        "esbenp.prettier-vscode"
      ]
    }
  },
  "forwardPorts": [3000],
  "postCreateCommand": "npm install",
  "remoteUser": "vscode"
}

# 或使用 Docker Compose
# .devcontainer/docker-compose.yml
services:
  app:
    build: 
      context: ..
      dockerfile: Dockerfile.dev
    volumes:
      - ../..:/workspaces:cached
    command: sleep infinity
    environment:
      - DATABASE_URL=postgres://db:5432/dev

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: dev
      POSTGRES_PASSWORD: dev

# VS Code 命令面板:
# Dev Containers: Reopen in Container
# Dev Containers: Rebuild Container

⚙️ 自动化工具

Watchtower - 自动更新容器

Watchtower 监控运行中的容器,当镜像有更新时自动拉取并重启容器。

# 基本启动
docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower

# 指定检查间隔(秒)
docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e WATCHTOWER_POLL_INTERVAL=3600 \
  containrrr/watchtower

# 只监控特定容器
docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower myapp nginx

# Docker Compose
services:
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_POLL_INTERVAL=3600
      - WATCHTOWER_CLEANUP=true        # 删除旧镜像
      - WATCHTOWER_INCLUDE_STOPPED=true # 包括停止的容器
      - WATCHTOWER_NOTIFICATIONS=email  # 发送通知
    restart: always

# 通知配置(邮件示例)
environment:
  - WATCHTOWER_NOTIFICATIONS=email
  - WATCHTOWER_NOTIFICATION_EMAIL_FROM=sender@example.com
  - WATCHTOWER_NOTIFICATION_EMAIL_TO=admin@example.com
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.example.com
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=user
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=pass

Skaffold - Kubernetes 开发工具

Skaffold 是 Google 开发的命令行工具,简化 Kubernetes 应用的持续开发。

# 安装(macOS)
brew install skaffold

# 安装(Linux)
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
chmod +x skaffold
sudo mv skaffold /usr/local/bin

# 初始化项目
skaffold init

# 开发模式(文件变化自动重新部署)
skaffold dev

# 构建并部署
skaffold run

# 清理部署
skaffold delete

# skaffold.yaml 示例
apiVersion: skaffold/v4beta9
kind: Config
build:
  artifacts:
    - image: myapp
      docker:
        dockerfile: Dockerfile
manifests:
  rawYaml:
    - k8s/*.yaml
deploy:
  kubectl: {}

Tilt - 本地 Kubernetes 开发

Tilt 为 Kubernetes 微服务开发提供即时反馈,支持多服务同时开发。

# 安装(macOS/Linux)
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash

# 安装(macOS with Homebrew)
brew install tilt-dev/tap/tilt

# 启动 Tilt
tilt up

# Tiltfile 示例
# Tiltfile
docker_build('myapp', '.')
k8s_yaml('k8s/')
k8s_resource('myapp', port_forwards=3000)

# 多服务示例
docker_build('api', './api')
docker_build('web', './web')
k8s_yaml('k8s/')
k8s_resource('api', port_forwards=8080)
k8s_resource('web', port_forwards=3000)

# 特点:
# - 实时日志查看
# - 资源监控面板
# - 文件变化自动构建
# - 支持 live_update(不重建镜像更新代码)

🌐 网络与代理

Traefik - 云原生边缘路由器

Traefik 是现代化的反向代理和负载均衡器,自动发现 Docker 服务并配置路由。

# docker-compose.yml
services:
  traefik:
    image: traefik:v3.0
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./certs:/etc/certs:ro
    restart: always

  whoami:
    image: traefik/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=web"

# traefik.yml
api:
  dashboard: true
  insecure: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /etc/certs/acme.json
      httpChallenge:
        entryPoint: web

📊 监控与日志

Prometheus + Grafana - 监控方案

完整的 Docker 监控方案,Prometheus 收集指标,Grafana 可视化展示。

# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8081:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro

  node-exporter:
    image: prom/node-exporter:latest
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro

volumes:
  grafana-data:

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

# 导入 Grafana Dashboard
# 推荐 ID: 11600 (Docker and system monitoring)
# 或 893 (Node Exporter Full)

Dozzle - 轻量级日志查看

Dozzle 是一个轻量级的容器日志查看工具,实时显示容器日志。

# 快速启动
docker run -d \
  --name dozzle \
  -p 9999:8080 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  amir20/dozzle:latest

# Docker Compose
services:
  dozzle:
    image: amir20/dozzle:latest
    ports:
      - "9999:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DOZZLE_LEVEL=info
      - DOZZLE_TAILSIZE=300

# 访问:http://localhost:9999
# 特点:
# - 实时日志流
# - 多容器同时查看
# - 日志搜索过滤
# - 无需配置

🔐 安全加固工具

Docker Bench for Security

Docker 官方安全检查脚本,检查主机和 Docker 配置是否符合 CIS 安全基准。

# 运行检查
docker run --rm \
  --net host \
  --pid host \
  --cap-add audit_control \
  -v /var/lib:/var/lib \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /usr/lib/systemd:/usr/lib/systemd \
  -v /etc:/etc \
  --label docker_bench_security \
  docker/docker-bench-security

# 输出分类:
# [INFO] 检查项
# [PASS] 通过
# [WARN] 警告(需要关注)
# [FAIL] 失败(需要修复)
# [NOTE] 说明

# 常见修复建议:
# 1. 设置日志驱动
#    --log-driver json-file --log-opts max-size=10m

# 2. 禁用 inter-container communication
#    --icc=false

# 3. 启用 user namespace
#    /etc/docker/daemon.json: {"userns-remap": "default"}

# 4. 限制容器 capabilities
#    --cap-drop ALL --cap-add CHOWN

# 5. 使用 seccomp profile
#    --security-opt seccomp=default.json

Clair - 镜像漏洞扫描

Clair 是 CoreOS 开发的开源镜像漏洞扫描器,可集成到 CI/CD 流程。

# 使用 Clair 的客户端工具 clair-scanner
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $(pwd)/reports:/reports \
  arminc/clair-local-scan:v2.0.8 \
  --clair http://clair:6060 \
  --report /reports/$(basename $IMAGE).json \
  myapp:latest

# 或使用 Clair V4
services:
  clair:
    image: quay.io/coreos/clair:v4.5.0
    ports:
      - "6060:6060"
      - "6061:6061"
    volumes:
      - ./config.yaml:/etc/clair/config.yaml

# 与 Harbor 集成
# Harbor 内置 Clair 扫描器
# 在 Harbor 配置中启用漏洞扫描即可

🛠️ 完整工具清单

类别 工具 用途
可视化管理 Portainer Web 管理界面
Lazydocker 终端 UI 管理
Dockge Compose 项目管理
监控 ctop 容器资源监控
Prometheus + Grafana 完整监控方案
日志 Dozzle 轻量级日志查看
开发 Dive 镜像层分析
Hadolint Dockerfile 检查
DevContainer 容器化开发环境
自动化 Watchtower 自动更新容器
Skaffold/Tilt K8s 开发流程
网络 Traefik 反向代理/负载均衡
安全 Docker Scout 官方安全扫描
Trivy 漏洞扫描
Docker Bench 安全基准检查
AI 相关 Ask Gordon AI 助手
GenAI Stack AI 应用开发栈

Docker 替代品

虽然 Docker 是最流行的容器化平台,但市面上还有其他优秀的替代方案,各有特点和适用场景。

容器运行时替代品

Podman - 无守护进程的容器引擎

Podman 是 Red Hat 开发的容器引擎,与 Docker 完全兼容,但不需要守护进程,支持 rootless 模式。

核心特点
# 与 Docker 的主要区别:
# 1. 无守护进程(daemonless)
# 2. 默认 rootless 运行(更安全)
# 3. 支持 Kubernetes YAML
# 4. 兼容 Docker CLI 命令

# 安装(macOS)
brew install podman
podman machine init
podman machine start

# 安装(Linux - Ubuntu)
sudo apt update
sudo apt install podman

# 安装(Linux - Fedora/RHEL)
sudo dnf install podman
基本使用
# 命令与 Docker 几乎相同
podman pull nginx:alpine
podman run -d -p 8080:80 nginx
podman ps
podman images
podman exec -it container_id sh
podman logs container_id

# 构建镜像
podman build -t myapp:v1 .

# 使用 Docker Compose 兼容工具
podman-compose up -d

# 生成 Kubernetes YAML
podman generate kube mycontainer > pod.yaml

# 从 Kubernetes YAML 部署
podman play kube pod.yaml
Podman vs Docker
特性 Docker Podman
守护进程 需要 dockerd 无守护进程
Root 权限 默认需要 root 默认 rootless
Docker 兼容 - CLI 完全兼容
Kubernetes 集成 需要额外工具 原生支持
安全性 中等 更高(rootless)
别名配置
# 在 ~/.bashrc 或 ~/.zshrc 添加别名
alias docker=podman
alias docker-compose=podman-compose

# 这样可以直接使用 Docker 命令习惯

containerd - 工业级容器运行时

containerd 是从 Docker 分离出来的容器运行时,专注于运行容器,被 Kubernetes 作为默认运行时。

# 安装(Linux)
wget https://github.com/containerd/containerd/releases/download/v1.7.13/containerd-1.7.13-linux-amd64.tar.gz
tar xzf containerd-1.7.13-linux-amd64.tar.gz
sudo mv bin/* /usr/local/bin/

# 创建 systemd 服务
sudo mkdir -p /usr/local/lib/systemd/system/
sudo cat > /usr/local/lib/systemd/system/containerd.service << EOF
[Unit]
Description=containerd container runtime
After=network.target

[Service]
ExecStart=/usr/local/bin/containerd
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now containerd

# 使用 ctr 命令管理容器
ctr images pull docker.io/library/nginx:alpine
ctr run -d docker.io/library/nginx:alpine mynginx
ctr containers list
ctr tasks list
适用场景
  • Kubernetes 集群节点运行时
  • 需要轻量级容器运行时的场景
  • 嵌入式或边缘计算环境

CRI-O - Kubernetes 专用运行时

CRI-O 是专门为 Kubernetes 设计的轻量级容器运行时,实现 CRI(Container Runtime Interface)。

# 安装(Fedora/RHEL)
sudo dnf install cri-o

# 安装(Ubuntu)
sudo apt install cri-o

# 启动服务
sudo systemctl enable --now crio

# CRI-O 专为 Kubernetes 优化
# 不提供独立 CLI,通过 crictl 管理

# 安装 crictl
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.29.0/crictl-v1.29.0-linux-amd64.tar.gz
tar xzf crictl-v1.29.0-linux-amd64.tar.gz
sudo mv crictl /usr/local/bin/

# 常用命令
crictl pull nginx:alpine
crictl images
crictl pods
crictl ps
crictl logs container_id

镜像构建工具

Kaniko - 无 Docker 构建镜像

Kaniko 可以在不需要 Docker 守护进程的情况下构建镜像,适合 CI/CD 环境。

# 在 Kubernetes 中使用
apiVersion: v1
kind: Pod
metadata:
  name: kaniko
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--dockerfile=Dockerfile"
    - "--context=git://github.com/user/repo"
    - "--destination=user/image:tag"
    volumeMounts:
    - name: docker-config
      mountPath: /kaniko/.docker
  volumes:
  - name: docker-config
    configMap:
      name: docker-config

# 在容器中运行
docker run -it \
  -v $(pwd):/workspace \
  -v ~/.docker/config.json:/kaniko/.docker/config.json \
  gcr.io/kaniko-project/executor:latest \
  --dockerfile=Dockerfile \
  --context=/workspace \
  --destination=myregistry/myapp:v1

Buildah - 无守护进程构建

Buildah 是 Podman 的姐妹项目,专注于构建 OCI 镜像,无需 root 权限。

# 安装(Linux)
sudo apt install buildah  # Ubuntu/Debian
sudo dnf install buildah  # Fedora/RHEL

# 从 Dockerfile 构建
buildah build -t myapp:v1 .

# 交互式构建
container=$(buildah from alpine)
buildah run $container -- apk add nginx
buildah config --cmd "nginx -g 'daemon off;'" $container
buildah commit $container myapp:nginx

# 推送镜像
buildah push myapp:v1 docker://registry.example.com/myapp:v1

# 无需 Dockerfile 的构建方式
buildah from alpine
buildah copy mycontainer app.py /app/
buildah run mycontainer -- pip install flask
buildah commit mycontainer myapp:latest

BuildKit - Docker 下一代构建器

BuildKit 是 Docker 的新一代构建引擎,性能更强,支持并行构建和更好的缓存。

# 启用 BuildKit
export DOCKER_BUILDKIT=1
docker build -t myapp:v1 .

# 使用 buildx(Docker 内置)
docker buildx version

# 创建多平台构建器
docker buildx create --name mybuilder --use
docker buildx inspect --bootstrap

# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:v1 .

# 直接使用 BuildKit daemon
# 安装
wget https://github.com/moby/buildkit/releases/download/v0.12.5/buildkit-v0.12.5.linux-amd64.tar.gz
tar xzf buildkit-v0.12.5.linux-amd64.tar.gz
sudo mv bin/* /usr/local/bin/

# 启动服务
sudo buildkitd &

# 使用 buildctl 构建
buildctl build \
  --frontend dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=docker,name=myapp:v1 | docker load

容器编排平台

Kubernetes - 容器编排标准

Kubernetes 是容器编排的事实标准,适合大规模生产环境。

# 本地开发环境选择
# 1. minikube
minikube start
minikube dashboard

# 2. kind(Kubernetes in Docker)
kind create cluster
kind delete cluster

# 3. k3s(轻量级 Kubernetes)
curl -sfL https://get.k3s.io | sh -

# 4. k3d(k3s in Docker)
k3d cluster create mycluster

# 基本概念
# Pod: 最小部署单元
# Deployment: 管理 Pod 副本
# Service: 网络服务发现
# ConfigMap/Secret: 配置管理
# Ingress: HTTP 路由

# 示例部署
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pods,svc

# YAML 部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:v1
        ports:
        - containerPort: 3000

Nomad - HashiCorp 编排工具

Nomad 是 HashiCorp 开发的简单灵活的工作负载编排器。

# 安装(macOS)
brew install nomad

# 安装(Linux)
wget https://releases.hashicorp.com/nomad/1.7.7/nomad_1.7.7_linux_amd64.zip
unzip nomad_1.7.7_linux_amd64.zip
sudo mv nomad /usr/local/bin/

# 启动开发模式
nomad agent -dev

# 运行 Docker 任务
job "webapp" {
  datacenters = ["dc1"]
  type = "service"

  group "web" {
    count = 2

    task "server" {
      driver = "docker"
      config {
        image = "nginx:alpine"
        port_map {
          http = 80
        }
      }
      resources {
        cpu    = 500
        memory = 256
        network {
          mbits = 10
          port "http" {}
        }
      }
    }
  }
}

# 运行作业
nomad job run webapp.nomad
nomad job status webapp

Docker Swarm - Docker 原生编排

Docker Swarm 是 Docker 内置的编排功能,简单易用,适合中小规模集群。

# 初始化 Swarm
docker swarm init --advertise-addr 

# 添加工作节点
docker swarm join-token worker

# 部署服务
docker service create \
  --name web \
  --replicas 3 \
  --publish 80:80 \
  nginx:alpine

# 管理服务
docker service ls
docker service ps web
docker service scale web=5
docker service update --image nginx:latest web

# 使用 Stack 部署
# docker-compose.yml
version: "3.8"
services:
  web:
    image: nginx
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"

# 部署 Stack
docker stack deploy -c docker-compose.yml myapp
docker stack ls
docker stack services myapp

开发环境工具

Dagger - 可编程 CI/CD

Dagger 使用代码定义 CI/CD 流水线,在容器中运行,无需专有 CI 服务器。

# 安装
curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
dagger version

# 使用 Go SDK 编写流水线
# main.go
package main

import (
    "context"
    "dagger.io/dagger"
)

func main() {
    ctx := context.Background()
    client, _ := dagger.Connect(ctx)
    defer client.Close()

    // 获取源代码
    source := client.Host().Directory()

    // 构建镜像
    image := client.Container().
        Build(source)

    // 运行测试
    _, err := image.
        WithExec([]string{"go", "test", "./..."}).
        ExitCode(ctx)
}

# 运行
dagger run go run main.go

Earthly - Make + Docker

Earthly 结合 Makefile 和 Dockerfile 的优点,提供可重复的构建。

# 安装(macOS)
brew install earthly/earthly/earthly

# 安装(Linux)
sudo /bin/sh -c 'wget https://github.com/earthly/earthly/releases/download/v0.8.0/earthly-linux-amd64 -O /usr/local/bin/earthly && chmod +x /usr/local/bin/earthly'

# Earthfile
VERSION 0.8

FROM golang:1.22-alpine

build:
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    RUN go build -o app .
    SAVE ARTIFACT app

docker:
    FROM alpine:3.19
    COPY +build/app /app
    ENTRYPOINT ["/app"]
    SAVE IMAGE myapp:latest

test:
    FROM +build
    RUN go test ./...

# 执行构建
earthly +build
earthly +docker
earthly +test

替代品对比总结

类别 工具 主要优势 适用场景
容器运行时 Podman 无守护进程、rootless 安全要求高的环境
containerd 轻量、稳定 Kubernetes 节点
CRI-O K8s 原生 Kubernetes 专用
镜像构建 Kaniko 无 Docker 构建 CI/CD 流水线
Buildah rootless 构建 安全构建环境
BuildKit 高性能、多平台 现代 Docker 构建
容器编排 Kubernetes 行业标准、功能强大 大规模生产环境
Nomad 简单灵活 混合工作负载
Docker Swarm Docker 原生、易用 中小规模集群

如何选择?

继续使用 Docker

  • 个人开发和学习
  • 中小团队项目
  • 需要最广泛的社区支持
  • 使用 Docker Desktop 的 macOS/Windows 用户

考虑 Podman

  • 需要更高的安全性
  • Linux 服务器环境
  • 想要 rootless 容器
  • 企业级 Red Hat 环境

考虑 Kubernetes

  • 大规模微服务架构
  • 需要自动扩缩容
  • 多云/混合云部署
  • 企业生产环境

考虑 Nomad

  • 需要编排多种工作负载
  • 想要更简单的操作
  • 已有 HashiCorp 工具栈
  • 边缘计算场景

实战案例

CI/CD 容器化流程

📝
代码提交
Git Push
🔨
构建镜像
docker build
🔍
安全扫描
Trivy/Scout
📤
推送仓库
Registry
🚀
部署上线
K8s/Swarm

开发环境架构

💻 开发机 (IDE + 热重载)
🐳 Docker Compose (多服务编排)
App
:3000
DB
:5432
Redis
:6379

案例 1:Web 应用

选择基础镜像
FROM node:20-alpine
设置工作目录
WORKDIR /app
安装依赖
COPY package*.json ./ → RUN npm ci
复制代码
COPY . .
启动应用
EXPOSE 3000 → CMD ["node", "server.js"]
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

案例 2:Python 应用

# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]

案例 3:完整开发环境

💻
App
:3000
🗄️ DB
:5432
📦 Redis
:6379
# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/dev
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: dev
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - db-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  db-data: