Docker & Kubernetes 完全掌握 第2篇: Docker 镜像与容器管理
Docker & Kubernetes Complete Guide Part 2: Docker Images and Container Management
前言:掌握 Docker 核心概念
在第1篇中我们学习了 Docker 的基本概念和安装方法。本篇将深入介绍在实际工作中有效使用 Docker 必须了解的镜像(Image)和容器(Container)的核心概念和管理方法。
准确理解镜像和容器的关系,掌握各种命令选项,可以更高效地使用 Docker。特别是 docker run 的各种选项在实际工作中非常常用,必须熟练掌握。
1. 镜像与容器的区别
1.1 什么是镜像(Image)?
Docker 镜像是用于创建容器的只读(Read-Only)模板。它包含运行应用程序所需的一切(代码、运行时、库、环境变量、配置文件等)。
比喻:镜像类似于程序的"安装文件"或"类定义"。是运行前的状态。
1.2 什么是容器(Container)?
Docker 容器是镜像的可运行实例。它基于镜像创建,拥有自己的文件系统、网络和进程空间。
比喻:容器类似于程序的"正在运行的进程"或"对象实例"。是实际运行的状态。
1.3 镜像与容器的关系
┌─────────────────────────────────────────────────────────────┐
│ Docker Image │
│ (只读模板) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Application Code + Dependencies + Configuration │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ docker run
▼
┌─────────────────────────────────────────────────────────────┐
│ Docker Container 1 │
│ (运行实例) │
│ ┌───────────────────────┐ ┌───────────────────────────┐ │
│ │ Image Layers (RO) │ │ Writable Layer (RW) │ │
│ │ (只读) │ │ (可写) │ │
│ └───────────────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ docker run (使用相同镜像)
▼
┌─────────────────────────────────────────────────────────────┐
│ Docker Container 2 │
│ (独立实例) │
└─────────────────────────────────────────────────────────────┘
1.4 核心区别总结
| 区分 | 镜像 (Image) | 容器 (Container) |
|---|---|---|
| 状态 | 静态 (Static) | 动态 (Dynamic) |
| 可修改性 | 只读 (Immutable) | 可写 (Mutable) |
| 存储位置 | 本地或仓库 | 主机内存/磁盘 |
| 创建方式 | Dockerfile 构建或 pull | docker run/create |
| 关系 | 一个镜像 = 多个容器 | 一个容器 = 一个镜像 |
2. 镜像层概念
2.1 什么是层(Layer)?
Docker 镜像由多个只读层组成。每一层只存储与前一层的差异(delta)。这种结构使用联合文件系统(Union File System, UnionFS)来实现。
┌─────────────────────────────────────────┐
│ Container Layer (R/W) │ ← 运行容器时添加
├─────────────────────────────────────────┤
│ Layer 4: CMD/ENTRYPOINT │ ← 镜像层
├─────────────────────────────────────────┤
│ Layer 3: Application Code │
├─────────────────────────────────────────┤
│ Layer 2: Dependencies │
├─────────────────────────────────────────┤
│ Layer 1: Base OS (Ubuntu) │
└─────────────────────────────────────────┘
2.2 层的优势
- 存储空间效率化:相同的层可以在多个镜像之间共享,防止重复存储。
- 快速构建:只重新构建更改的层,缩短构建时间。
- 快速部署:已存在的层不需要重新下载。
- 缓存:可以在构建过程中有效利用缓存。
2.3 查看镜像层
# 查看镜像的层历史
docker history nginx
# 输出示例:
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) COPY file:e57eef017a414ca7… 4.62kB
<missing> 2 weeks ago /bin/sh -c set -x && groupadd --system -… 112MB
...
# 查看镜像详细信息(包括层)
docker inspect nginx
# 只提取特定字段(层列表)
docker inspect --format='{{range .RootFS.Layers}}{{println .}}{{end}}' nginx
3. 镜像管理命令
3.1 docker pull - 下载镜像
从 Docker Hub 或其他仓库下载镜像。
# 基本用法(latest 标签)
docker pull nginx
# 指定特定版本(标签)
docker pull nginx:1.25.3
docker pull nginx:alpine # 基于 Alpine Linux 的轻量版本
# 从特定仓库 pull
docker pull gcr.io/google-containers/nginx
# pull 所有标签(注意:占用大量空间)
docker pull -a nginx
# 查看 pull 进度
docker pull ubuntu:22.04
# 22.04: Pulling from library/ubuntu
# 2ab09b027e7f: Pull complete
# Digest: sha256:...
# Status: Downloaded newer image for ubuntu:22.04
3.2 docker images - 查看镜像列表
# 所有镜像列表
docker images
docker image ls # 相同命令
# 输出示例:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 weeks ago 187MB
nginx alpine 8e75cbc5b25c 2 weeks ago 41.1MB
ubuntu 22.04 174c8c134b2a 3 weeks ago 77.8MB
mysql 8.0 3218b38490ce 4 weeks ago 516MB
# 只过滤特定镜像
docker images nginx
docker images "nginx:*"
# 只输出镜像 ID
docker images -q
# 显示 Dangling 镜像(没有标签的镜像)
docker images -f "dangling=true"
# 显示详细信息
docker images --no-trunc # 完整镜像 ID
docker images --digests # 包含摘要
# 以特定格式输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
3.3 docker push - 上传镜像
# 登录 Docker Hub
docker login
# 镜像打标签(Docker Hub 格式: username/repository:tag)
docker tag my-app:latest myusername/my-app:v1.0
# push 镜像
docker push myusername/my-app:v1.0
# push 所有标签
docker push myusername/my-app --all-tags
# push 到其他仓库
docker tag my-app:latest registry.example.com/my-app:v1.0
docker push registry.example.com/my-app:v1.0
3.4 docker rmi - 删除镜像
# 删除镜像(名称:标签 或 镜像 ID)
docker rmi nginx:alpine
docker rmi a6bd71f48f68
# 强制删除(即使有容器在使用)
docker rmi -f nginx
# 同时删除多个镜像
docker rmi nginx:alpine mysql:8.0 redis:latest
# 删除所有 Dangling 镜像
docker image prune
# 删除所有未使用的镜像
docker image prune -a
# 删除符合特定条件的镜像
docker image prune -a --filter "until=24h" # 超过24小时的镜像
3.5 其他镜像命令
# 镜像详细信息
docker inspect nginx
# 保存镜像(导出为 tar 文件)
docker save -o nginx.tar nginx:latest
docker save nginx:latest > nginx.tar
# 加载镜像(从 tar 文件)
docker load -i nginx.tar
docker load < nginx.tar
# 添加镜像标签
docker tag nginx:latest my-nginx:v1.0
# 从容器创建镜像(提交更改)
docker commit my-container my-new-image:v1.0
4. 容器生命周期
4.1 容器状态转换图
docker create
[Image] ─────────────────────► [Created]
│
│ docker start
▼
[Running] ◄────────┐
│ │
docker stop │ │ docker restart
docker kill │ │
▼ │
[Stopped] ─────────┘
│
│ docker rm
▼
[Removed]
4.2 docker create - 创建容器
docker create 只创建容器而不启动。
# 创建容器(不启动)
docker create --name my-nginx nginx
# 查看创建的容器
docker ps -a
# STATUS: Created
4.3 docker start - 启动容器
# 启动容器
docker start my-nginx
# 同时启动多个容器
docker start container1 container2 container3
# 以交互模式启动(连接标准输入)
docker start -i my-container
# 将输出连接到终端
docker start -a my-container
4.4 docker stop - 停止容器
# 正常终止容器(SIGTERM 信号)
docker stop my-nginx
# 指定超时时间(默认10秒)
docker stop -t 30 my-nginx # 等待30秒后强制终止
# 同时停止多个容器
docker stop container1 container2
# 停止所有运行中的容器
docker stop $(docker ps -q)
4.5 docker restart - 重启容器
# 重启容器
docker restart my-nginx
# 指定超时时间
docker restart -t 5 my-nginx
4.6 docker rm - 删除容器
# 删除已停止的容器
docker rm my-nginx
# 强制删除(即使正在运行)
docker rm -f my-nginx
# 同时删除卷
docker rm -v my-nginx
# 删除所有已停止的容器
docker container prune
# 删除所有容器(强制)
docker rm -f $(docker ps -aq)
4.7 docker kill - 强制终止容器
# 强制终止(SIGKILL)
docker kill my-nginx
# 发送特定信号
docker kill -s SIGINT my-nginx
docker kill -s SIGHUP my-nginx
5. docker run 选项详解
docker run 一次性执行 docker create + docker start。这是最常用的命令,必须熟悉各种选项。
5.1 基本选项
# 基本格式
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 基本运行
docker run nginx
# 指定名称(--name)
docker run --name my-nginx nginx
# 后台运行(-d, --detach)
docker run -d nginx
# 交互模式(-i, -t, -it)
docker run -it ubuntu bash
# -i: 启用标准输入(stdin)
# -t: 分配虚拟终端(TTY)
# 容器退出时自动删除(--rm)
docker run --rm nginx curl localhost
5.2 端口映射(-p, --publish)
# 主机端口:容器端口
docker run -d -p 8080:80 nginx
# 主机的 8080 端口 → 容器的 80 端口
# 多个端口映射
docker run -d -p 8080:80 -p 443:443 nginx
# 只绑定到特定 IP
docker run -d -p 127.0.0.1:8080:80 nginx
# 只指定容器端口(主机端口自动分配)
docker run -d -p 80 nginx
docker port my-nginx # 查看分配的端口
# 自动映射所有暴露的端口(-P)
docker run -d -P nginx
# 指定协议
docker run -d -p 8080:80/tcp -p 53:53/udp my-app
5.3 卷挂载(-v, --volume, --mount)
# 挂载主机目录(Bind Mount)
docker run -d -v /host/path:/container/path nginx
docker run -d -v $(pwd)/html:/usr/share/nginx/html nginx
# 只读挂载
docker run -d -v /host/path:/container/path:ro nginx
# 使用命名卷(Named Volume)
docker volume create my-data
docker run -d -v my-data:/app/data mysql
# --mount 选项(更明确)
docker run -d --mount type=bind,source=/host/path,target=/container/path nginx
docker run -d --mount type=volume,source=my-data,target=/app/data mysql
# 匿名卷
docker run -d -v /app/data mysql
# 共享其他容器的卷(--volumes-from)
docker run -d --volumes-from my-container backup-container
5.4 环境变量(-e, --env)
# 设置环境变量
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql
docker run -d -e "MY_VAR=hello world" my-app
# 多个环境变量
docker run -d \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=mydb \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
mysql
# 从文件读取环境变量(--env-file)
# .env 文件:
# MYSQL_ROOT_PASSWORD=secret
# MYSQL_DATABASE=mydb
docker run -d --env-file .env mysql
# 传递主机环境变量
docker run -d -e MY_VAR my-app # 使用主机的 MY_VAR 值
5.5 网络选项(--network)
# 默认 bridge 网络(default)
docker run -d nginx
# 使用主机网络(容器直接使用主机网络)
docker run -d --network host nginx
# 无网络
docker run -d --network none nginx
# 用户定义网络
docker network create my-network
docker run -d --network my-network --name web nginx
docker run -d --network my-network --name db mysql
# 设置网络别名
docker run -d --network my-network --network-alias web-alias nginx
5.6 其他有用选项
# 设置工作目录(-w, --workdir)
docker run -w /app node npm install
# 指定用户(-u, --user)
docker run -u 1000:1000 nginx
docker run -u root nginx
# 设置主机名(-h, --hostname)
docker run -h my-hostname nginx
# DNS 设置
docker run --dns 8.8.8.8 nginx
# 重启策略(--restart)
docker run -d --restart always nginx # 始终重启
docker run -d --restart unless-stopped nginx # 除非手动停止否则重启
docker run -d --restart on-failure:3 nginx # 失败时最多重启3次
# 权限设置(--privileged, --cap-add, --cap-drop)
docker run --privileged nginx # 所有权限
docker run --cap-add SYS_ADMIN nginx
docker run --cap-drop NET_RAW nginx
# 设备访问(--device)
docker run --device /dev/sda:/dev/xvdc nginx
# 只读根文件系统
docker run --read-only nginx
6. docker exec 和 attach
6.1 docker exec - 在运行中的容器中执行命令
docker exec 在已运行的容器中启动新的进程。
# 基本用法
docker exec my-nginx ls -la
# 交互式 shell 连接(最常用)
docker exec -it my-nginx bash
docker exec -it my-nginx sh # 没有 bash 的容器
# 以特定用户执行
docker exec -u root my-nginx whoami
# 指定工作目录
docker exec -w /etc/nginx my-nginx cat nginx.conf
# 设置环境变量
docker exec -e MY_VAR=value my-nginx printenv MY_VAR
# 在后台执行
docker exec -d my-nginx touch /tmp/test.txt
6.2 docker attach - 连接到运行中的容器
docker attach 将标准输入输出连接到容器的主进程(PID 1)。
# 连接到容器
docker attach my-container
# 设置分离键(默认: Ctrl+P, Ctrl+Q)
docker attach --detach-keys="ctrl-c" my-container
# 只查看输出(禁用输入)
docker attach --no-stdin my-container
# 禁用信号传递
docker attach --sig-proxy=false my-container
6.3 exec vs attach 区别
| 项目 | docker exec | docker attach |
|---|---|---|
| 连接目标 | 创建新进程 | 连接到主进程(PID 1) |
| 使用场景 | 调试、管理任务 | 查看日志、交互式应用 |
| 退出时影响 | 不影响容器 | Ctrl+C 可能终止容器 |
| 多连接 | 可以多个会话 | 共享相同输出 |
| 推荐情况 | 大多数情况推荐 | 只在特殊情况使用 |
# 实际推荐的模式
# 1. 调试时
docker exec -it my-container bash
# 2. 查看日志时(替代 attach)
docker logs -f my-container
# 3. 安全使用 attach
docker attach --sig-proxy=false my-container
# 或使用 Ctrl+P, Ctrl+Q 分离
7. 容器日志查看(docker logs)
7.1 基本日志命令
# 输出全部日志
docker logs my-nginx
# 实时日志流(-f, --follow)
docker logs -f my-nginx
# 只输出最后 N 行(--tail)
docker logs --tail 100 my-nginx
# 包含时间戳(-t, --timestamps)
docker logs -t my-nginx
# 特定时间之后的日志(--since)
docker logs --since 2026-01-22T10:00:00 my-nginx
docker logs --since 1h my-nginx # 1小时内
docker logs --since 30m my-nginx # 30分钟内
# 到特定时间为止的日志(--until)
docker logs --until 2026-01-22T12:00:00 my-nginx
# 组合示例
docker logs -f --tail 50 -t my-nginx
7.2 日志驱动设置
# 查看日志驱动
docker inspect --format='{{.HostConfig.LogConfig.Type}}' my-nginx
# JSON 文件日志驱动(默认)
docker run -d --log-driver json-file nginx
# 日志选项设置
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
# syslog 驱动
docker run -d --log-driver syslog nginx
# 禁用日志
docker run -d --log-driver none nginx
# 查看实际日志文件位置
docker inspect --format='{{.LogPath}}' my-nginx
7.3 日志管理最佳实践
# 1. 限制日志大小(生产环境必须)
docker run -d \
--log-opt max-size=10m \
--log-opt max-file=5 \
nginx
# 2. 查看每个容器的日志文件
ls -la /var/lib/docker/containers/[CONTAINER_ID]/
# 3. 删除日志(注意:运行中删除可能会出问题)
truncate -s 0 $(docker inspect --format='{{.LogPath}}' my-nginx)
# 4. 与 grep 一起使用
docker logs my-nginx 2>&1 | grep "error"
# 5. 只输出特定流
docker logs my-nginx 2>/dev/null # 只输出 stdout
docker logs my-nginx 2>&1 1>/dev/null # 只输出 stderr
8. 资源限制(--memory, --cpus)
8.1 内存限制
# 内存限制(--memory, -m)
docker run -d -m 512m nginx
docker run -d --memory 1g nginx
docker run -d --memory 256m nginx
# 内存 + 交换空间限制
docker run -d -m 512m --memory-swap 1g nginx
# 禁用交换
docker run -d -m 512m --memory-swap 512m nginx
# 内存预留(软限制)
docker run -d -m 1g --memory-reservation 512m nginx
# 禁用 OOM Killer(不推荐)
docker run -d -m 512m --oom-kill-disable nginx
8.2 CPU 限制
# CPU 数量限制(--cpus)
docker run -d --cpus 1.5 nginx # 1.5 个 CPU
docker run -d --cpus 0.5 nginx # 0.5 个 CPU(50%)
# CPU 共享权重(--cpu-shares)
docker run -d --cpu-shares 512 nginx # 默认值: 1024
# 相对权重(仅在竞争时生效)
# 固定到特定 CPU 核心(--cpuset-cpus)
docker run -d --cpuset-cpus 0,1 nginx # 只使用 CPU 0, 1
docker run -d --cpuset-cpus 0-3 nginx # 使用 CPU 0~3
# CPU 周期限制(高级)
docker run -d --cpu-period 100000 --cpu-quota 50000 nginx
# 100ms 中使用 50ms = 50% CPU
8.3 综合示例
# 生产环境资源限制示例
docker run -d \
--name production-app \
--memory 2g \
--memory-reservation 1g \
--cpus 2 \
--restart unless-stopped \
--log-opt max-size=50m \
--log-opt max-file=5 \
-p 8080:80 \
my-app:latest
# 实时监控资源使用
docker stats
# 只监控特定容器
docker stats my-nginx
# 一次性查看资源使用
docker stats --no-stream
# 以特定格式输出
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
8.4 更改运行中容器的资源
# 更改内存限制
docker update --memory 1g --memory-swap 2g my-container
# 更改 CPU 限制
docker update --cpus 2 my-container
# 更改重启策略
docker update --restart unless-stopped my-container
# 同时更改多个选项
docker update -m 2g --cpus 1.5 my-container
9. 总结与下一步
本篇深入学习了 Docker 核心的镜像和容器。
核心要点
- 镜像 vs 容器:镜像是只读模板,容器是可运行的实例。
- 镜像层:用于高效存储和构建的分层结构。
- 镜像命令:使用
pull、push、images、rmi管理镜像。 - 容器生命周期:使用
create、start、stop、restart、rm管理。 - docker run 选项:
-d、-p、-v、-e、--name等是核心选项。 - exec vs attach:大多数情况下使用
exec。 - 日志管理:
docker logs和日志驱动设置很重要。 - 资源限制:使用
--memory、--cpus实现稳定运营。
下一篇预告
第3篇将介绍 Dockerfile 编写和镜像构建:
- Dockerfile 基本语法
- 主要命令(FROM、RUN、COPY、ADD、CMD、ENTRYPOINT 等)
- 多阶段构建
- 镜像优化技巧
- 最佳实践
要在实际工作中有效使用 Docker,亲自练习本篇学到的命令非常重要。尝试组合各种选项来熟悉它们吧!