前言:掌握 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 容器:镜像是只读模板,容器是可运行的实例。
  • 镜像层:用于高效存储和构建的分层结构。
  • 镜像命令:使用 pullpushimagesrmi 管理镜像。
  • 容器生命周期:使用 createstartstoprestartrm 管理。
  • 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,亲自练习本篇学到的命令非常重要。尝试组合各种选项来熟悉它们吧!