引言
Docker 作为容器技术的事实标准,已经成为现代运维工程师必须掌握的核心技能。它通过提供一致的运行环境,彻底解决了"在我的机器上可以运行"的经典问题。本文将深入剖析 Docker 的各个核心组件,从底层原理到实际应用,帮助运维工程师全面理解并高效使用 Docker。
Docker 的强大之处不仅在于容器本身,更在于其完整的生态系统:从 Dockerfile 定义环境,到镜像构建和分发,再到容器运行和编排,每个环节都精心设计,相互协作。让我们逐一深入了解。
一、Dockerfile:环境定义的起点
什么是 Dockerfile
Dockerfile 是 Docker 的核心基石,它是一个文本文件,包含了构建 Docker 镜像所需的所有指令。通过 Dockerfile,我们可以用代码的形式定义应用程序的运行环境,实现基础设施即代码(Infrastructure as Code)。
为什么需要 Dockerfile
在传统运维中,环境配置往往依赖手工操作和文档,容易出错且难以复现。Dockerfile 解决了这些问题:
- 可重复性:同一个 Dockerfile 可以在任何地方构建出完全相同的环境
- 版本控制:Dockerfile 可以纳入 Git 管理,追溯环境变更历史
- 标准化:团队成员使用统一的环境定义,避免"环境不一致"问题
Dockerfile 实战示例
下面是一个 Python Flask 应用的 Dockerfile 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 1. 基础镜像选择
FROM python:3.11-slim
# 2. 设置工作目录
WORKDIR /app
# 3. 复制依赖文件并安装(利用缓存机制)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. 复制应用代码
COPY . .
# 5. 暴露端口
EXPOSE 5000
# 6. 设置环境变量
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# 7. 定义启动命令
CMD ["flask", "run", "--host=0.0.0.0"]
|
关键指令说明
| 指令 | 作用 | 运维最佳实践 |
|---|
FROM | 指定基础镜像 | 选择官方镜像,使用固定版本标签(避免 latest) |
WORKDIR | 设置工作目录 | 使用绝对路径,保持一致性 |
COPY | 复制文件到镜像 | 先复制依赖文件,后复制代码,优化缓存 |
RUN | 执行命令构建层 | 合并多个命令减少层数:RUN apt-get update && apt-get install -y ... |
ENV | 设置环境变量 | 集中管理配置,便于后期调整 |
CMD / ENTRYPOINT | 定义容器启动命令 | ENTRYPOINT 固定命令,CMD 提供默认参数 |
实际应用场景
场景:为生产环境构建优化的 Nginx 镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| FROM nginx:1.25-alpine
# 删除默认配置
RUN rm /etc/nginx/conf.d/default.conf
# 复制自定义配置
COPY nginx.conf /etc/nginx/nginx.conf
COPY conf.d/ /etc/nginx/conf.d/
# 复制静态文件
COPY html/ /usr/share/nginx/html/
# 设置非 root 用户运行(安全加固)
RUN chown -R nginx:nginx /usr/share/nginx/html
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
|
二、镜像分层:效率的秘密
分层结构原理
Docker 镜像采用**分层存储(Layered Storage)**机制,每条 Dockerfile 指令都会创建一个新的只读层。这种设计带来了巨大的优势:
- 缓存加速:构建时,未修改的层会被复用
- 存储优化:多个镜像可以共享相同的基础层
- 传输高效:推送/拉取镜像时只传输变化的层
查看镜像分层
1
2
3
4
5
6
7
8
| # 查看镜像历史(每一层)
docker history myapp:latest
# 详细查看镜像元数据
docker inspect myapp:latest
# 查看镜像大小和层数
docker images myapp:latest
|
优化镜像大小的实战技巧
❌ 不好的做法(产生多个层,镜像臃肿):
1
2
3
4
5
| FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y python3-pip
RUN apt-get clean
|
✅ 优化后(合并命令,清理缓存):
1
2
3
4
5
| FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
|
多阶段构建(Multi-Stage Build)
对于编译型语言,多阶段构建可以大幅减小最终镜像体积:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 第一阶段:编译
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 myapp
# 第二阶段:运行
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
|
效果对比:
- 构建镜像:~800MB(包含 Go 编译环境)
- 最终镜像:~10MB(仅包含二进制文件和基础系统)
三、容器:轻量级的运行实例
容器的本质
容器是镜像的运行时实例。与虚拟机不同,容器共享宿主机的内核,通过 Linux 内核的两大特性实现隔离:
- Namespaces(命名空间):隔离进程、网络、挂载点、用户等
- Cgroups(控制组):限制和分配资源(CPU、内存、IO)
容器生命周期管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # 1. 运行容器(基础)
docker run -d --name myapp -p 8080:80 nginx:latest
# 2. 运行容器(完整参数,生产环境)
docker run -d \
--name webapp \
--restart unless-stopped \ # 自动重启策略
--memory="512m" \ # 限制内存
--cpus="1.5" \ # 限制 CPU
-p 8080:80 \ # 端口映射
-v /data/webapp:/app/data \ # 数据卷挂载
-e DB_HOST=postgres.local \ # 环境变量
--network mynet \ # 自定义网络
--health-cmd="curl -f http://localhost/ || exit 1" \
--health-interval=30s \
myapp:v1.2.3
# 3. 查看容器状态
docker ps # 运行中的容器
docker ps -a # 所有容器
docker stats # 资源使用情况
# 4. 容器操作
docker logs -f myapp # 查看日志
docker exec -it myapp /bin/bash # 进入容器
docker stop myapp # 停止容器
docker rm myapp # 删除容器
|
实际运维场景:资源控制
场景:限制容器资源防止单个应用影响整体系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 严格限制资源的数据库容器
docker run -d \
--name mysql-prod \
--memory="2g" \
--memory-reservation="1.5g" \
--cpus="2" \
--cpu-shares=1024 \
--restart always \
-v /data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-p 3306:3306 \
mysql:8.0
# 监控资源使用
docker stats mysql-prod --no-stream
|
容器网络模式
| 网络模式 | 说明 | 使用场景 |
|---|
bridge(默认) | 容器使用独立的网络栈,通过虚拟网桥通信 | 单机多容器应用 |
host | 容器共享宿主机网络栈 | 需要高性能网络的应用(如监控) |
none | 容器无网络接口 | 高安全要求的批处理任务 |
container | 共享另一个容器的网络 | Sidecar 模式 |
| 自定义网络 | 用户定义的网络,支持 DNS 解析 | 微服务架构 |
创建自定义网络示例:
1
2
3
4
5
6
7
8
9
| # 创建自定义桥接网络
docker network create --driver bridge --subnet 172.20.0.0/16 myapp-net
# 运行容器并连接到自定义网络
docker run -d --name web --network myapp-net nginx
docker run -d --name api --network myapp-net myapi:latest
# 容器间可以通过容器名访问
# 在 web 容器中:curl http://api:8080/health
|
四、Docker Registry:镜像分发中心
什么是 Registry
Docker Registry 是存储和分发 Docker 镜像的服务。最常用的公共 Registry 是 Docker Hub,企业通常会搭建私有 Registry。
Docker Hub 使用
1
2
3
4
5
6
7
8
9
10
11
| # 1. 登录 Docker Hub
docker login
# 2. 标记镜像
docker tag myapp:latest username/myapp:v1.0.0
# 3. 推送镜像
docker push username/myapp:v1.0.0
# 4. 拉取镜像
docker pull username/myapp:v1.0.0
|
搭建私有 Registry
方案一:使用官方 Registry 镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 1. 启动私有 Registry
docker run -d \
--name registry \
--restart always \
-p 5000:5000 \
-v /data/registry:/var/lib/registry \
registry:2
# 2. 推送镜像到私有 Registry
docker tag myapp:latest localhost:5000/myapp:v1.0.0
docker push localhost:5000/myapp:v1.0.0
# 3. 从私有 Registry 拉取
docker pull localhost:5000/myapp:v1.0.0
|
方案二:使用 Harbor(企业级方案)
Harbor 提供了更强大的功能:
- 基于角色的访问控制(RBAC)
- 镜像漏洞扫描
- 镜像签名和验证
- 镜像复制(多地域同步)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 使用 Docker Compose 部署 Harbor
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xvf harbor-offline-installer-v2.10.0.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
# 编辑配置
vim harbor.yml
# 修改 hostname、端口、数据存储路径等
# 安装
sudo ./install.sh
# 登录 Harbor
docker login harbor.example.com
|
五、数据持久化:Volumes 与挂载
为什么需要 Volumes
容器的文件系统是临时的,容器删除后数据会丢失。Volumes 提供了独立于容器生命周期的持久化存储。
三种挂载方式
1
2
3
4
5
6
7
8
9
| # 1. Volume(推荐):由 Docker 管理
docker volume create mydata
docker run -v mydata:/app/data myapp
# 2. Bind Mount:挂载宿主机目录
docker run -v /host/path:/container/path myapp
# 3. tmpfs Mount:挂载内存(临时数据)
docker run --tmpfs /tmp myapp
|
实际应用场景
场景 1:MySQL 数据持久化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 创建专用数据卷
docker volume create mysql-data
# 运行 MySQL 并挂载数据卷
docker run -d \
--name mysql \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0
# 查看数据卷信息
docker volume inspect mysql-data
# 备份数据卷(重要!)
docker run --rm \
-v mysql-data:/data \
-v /backup:/backup \
alpine tar czf /backup/mysql-$(date +%Y%m%d).tar.gz /data
|
场景 2:Nginx 日志和配置分离
1
2
3
4
5
6
7
| docker run -d \
--name nginx \
-v /etc/nginx/conf.d:/etc/nginx/conf.d:ro \ # 只读挂载配置
-v /var/log/nginx:/var/log/nginx \ # 日志持久化
-v /data/www:/usr/share/nginx/html:ro \ # 静态文件
-p 80:80 \
nginx:alpine
|
六、Docker Compose:多容器应用编排
什么是 Docker Compose
Docker Compose 是一个用于定义和运行多容器应用的工具。通过一个 YAML 文件,可以配置应用的所有服务,然后使用一条命令启动所有服务。
完整的 WordPress 应用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| version: '3.8'
services:
# MySQL 数据库服务
db:
image: mysql:8.0
container_name: wordpress-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
volumes:
- db-data:/var/lib/mysql
networks:
- wordpress-net
deploy:
resources:
limits:
memory: 1G
cpus: '1'
# WordPress 应用服务
wordpress:
image: wordpress:latest
container_name: wordpress-app
restart: unless-stopped
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
volumes:
- wp-data:/var/www/html
networks:
- wordpress-net
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: wordpress-nginx
restart: unless-stopped
depends_on:
- wordpress
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
networks:
- wordpress-net
volumes:
db-data:
wp-data:
networks:
wordpress-net:
driver: bridge
|
Compose 常用命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f wordpress
# 停止服务
docker-compose stop
# 停止并删除容器(保留数据卷)
docker-compose down
# 停止并删除所有资源(包括数据卷)
docker-compose down -v
# 重启服务
docker-compose restart wordpress
# 扩展服务实例
docker-compose up -d --scale wordpress=3
|
生产环境最佳实践
使用 .env 文件管理配置:
1
2
3
4
5
6
| # .env 文件
DB_ROOT_PASSWORD=changeme
DB_NAME=wordpress
DB_USER=wpuser
DB_PASSWORD=secret123
WP_VERSION=6.4-php8.2
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # docker-compose.yml
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
wordpress:
image: wordpress:${WP_VERSION}
environment:
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
|
七、容器编排:从 Docker 到 Kubernetes
为什么需要编排
当应用规模扩大时,手动管理容器变得不现实。容器编排解决了以下问题:
- 自动故障转移:容器崩溃时自动重启
- 负载均衡:流量自动分配到多个容器
- 滚动更新:零停机更新应用
- 自愈能力:自动替换不健康的容器
- 弹性伸缩:根据负载自动增减容器数量
Docker Swarm 快速入门
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 1. 初始化 Swarm 集群(管理节点)
docker swarm init --advertise-addr 192.168.1.100
# 2. 添加工作节点(在其他机器上执行)
docker swarm join --token <TOKEN> 192.168.1.100:2377
# 3. 部署服务
docker service create \
--name web \
--replicas 3 \
--publish 80:80 \
nginx:alpine
# 4. 查看服务
docker service ls
docker service ps web
# 5. 扩展服务
docker service scale web=5
# 6. 滚动更新
docker service update --image nginx:latest web
|
Kubernetes 简介
Kubernetes(K8s)是目前最流行的容器编排平台,提供了更强大的功能:
核心概念对比:
| Docker Compose | Kubernetes |
|---|
| Service | Service + Deployment |
| Container | Pod |
| docker-compose.yml | YAML Manifests |
| 单机或 Swarm | 分布式集群 |
简单的 Kubernetes 部署示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| # deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
limits:
memory: "128Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
targetPort: 80
|
1
2
3
4
5
6
| # 部署到 Kubernetes
kubectl apply -f deployment.yaml
# 查看状态
kubectl get pods
kubectl get services
|
八、Docker CLI 与 Daemon
架构概述
Docker 采用客户端-服务器架构:
- Docker CLI:用户使用的命令行工具
- Docker Daemon (dockerd):后台服务,负责构建、运行和管理容器
- REST API:CLI 与 Daemon 之间的通信接口
常用 CLI 命令速查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # === 镜像管理 ===
docker build -t myapp:v1 . # 构建镜像
docker images # 列出镜像
docker rmi myapp:v1 # 删除镜像
docker pull nginx:alpine # 拉取镜像
docker push myapp:v1 # 推送镜像
docker tag myapp:v1 myapp:latest # 镜像打标签
# === 容器管理 ===
docker run -d nginx # 运行容器
docker ps # 查看运行中容器
docker stop <container-id> # 停止容器
docker start <container-id> # 启动容器
docker restart <container-id> # 重启容器
docker rm <container-id> # 删除容器
# === 调试与运维 ===
docker logs -f <container-id> # 查看日志
docker exec -it <container-id> bash # 进入容器
docker inspect <container-id> # 查看详细信息
docker stats # 资源使用统计
docker top <container-id> # 查看容器内进程
# === 清理命令 ===
docker system prune # 清理未使用资源
docker image prune # 清理未使用镜像
docker volume prune # 清理未使用数据卷
docker container prune # 清理已停止容器
|
配置 Docker Daemon
1
2
| # 编辑配置文件
sudo vim /etc/docker/daemon.json
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"registry-mirrors": ["https://mirror.example.com"],
"insecure-registries": ["192.168.1.100:5000"],
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
}
}
|
1
2
| # 重启 Docker 服务
sudo systemctl restart docker
|
九、其他容器运行时
Containerd
Containerd 是 Docker 拆分出的核心容器运行时,专注于容器生命周期管理。Kubernetes 已将其作为默认运行时。
1
2
3
4
5
| # 使用 containerd 运行容器(需要 nerdctl 工具)
sudo nerdctl run -d nginx:alpine
# 查看容器
sudo nerdctl ps
|
Podman
Podman 是 Red Hat 推出的无守护进程容器工具,与 Docker CLI 兼容,但不需要 root 权限。
1
2
3
4
5
6
7
8
| # Podman 命令与 Docker 几乎相同
podman run -d nginx:alpine
podman ps
podman images
# 支持 Pod 概念(类似 Kubernetes)
podman pod create --name mypod -p 8080:80
podman run -d --pod mypod nginx
|
对比:
| 特性 | Docker | Podman | Containerd |
|---|
| 守护进程 | 需要 | 不需要 | 需要 |
| Root 权限 | 需要 | 可选 | 需要 |
| Docker Compose 支持 | 原生 | 通过 podman-compose | 不支持 |
| Kubernetes 集成 | CRI-Docker | CRI-O | 原生 |
十、运维最佳实践
1. 安全加固
1
2
3
4
5
6
7
8
9
10
11
| # 以非 root 用户运行容器
docker run --user 1000:1000 myapp
# 只读文件系统
docker run --read-only --tmpfs /tmp myapp
# 禁用特权模式
docker run --security-opt=no-new-privileges myapp
# 限制容器能力
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
|
2. 资源限制
1
2
3
4
5
6
7
8
9
10
| # 完整的资源限制示例
docker run -d \
--name prod-app \
--memory="1g" \
--memory-reservation="750m" \
--memory-swap="1g" \
--cpus="1.5" \
--cpu-shares=1024 \
--pids-limit=100 \
myapp:latest
|
3. 健康检查
1
2
3
| # Dockerfile 中定义健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost/health || exit 1
|
4. 日志管理
1
2
3
4
5
6
| # 配置日志驱动和轮转
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=5 \
myapp
|
5. 镜像扫描
1
2
3
4
| # 使用 Trivy 扫描镜像漏洞
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image myapp:latest
|
总结
Docker 通过其完善的组件生态,构建了从开发到生产的完整容器化解决方案:
- Dockerfile 实现了环境的代码化定义
- 分层镜像 带来了构建和分发的高效性
- 容器 提供了轻量级的隔离运行环境
- Registry 解决了镜像的分发和版本管理
- Volumes 确保了数据的持久化
- Compose 简化了多容器应用的管理
- 编排工具 实现了大规模容器的自动化运维
作为运维工程师,掌握 Docker 不仅仅是学习命令,更重要的是理解其背后的设计理念:
- 不可变基础设施:镜像一经构建即不可变
- 一次构建,到处运行:环境一致性
- 资源高效利用:共享内核,轻量级隔离
- 自动化优先:从构建到部署的完整自动化
随着云原生技术的发展,Docker 已经与 Kubernetes 等编排工具深度集成,成为现代分布式系统的基石。持续学习和实践,将 Docker 与 CI/CD、监控、日志等系统整合,才能发挥其最大价值。
参考资料