文章目录
- [第 4 章 docker容器](#第 4 章 docker容器)
-
- [4.1 如何运行容器?](#4.1 如何运行容器?)
- [4.2 两种进入容器的方法](#4.2 两种进入容器的方法)
-
- [docker attach](#docker attach)
-
- [示例:attach 到容器](#示例:attach 到容器)
- [docker exec](#docker exec)
-
- [示例:exec 进入容器](#示例:exec 进入容器)
- [attach VS exec 区别](#attach VS exec 区别)
- 查看容器日志(无需进入容器)
- [4.3 运行容器的最佳实践](#4.3 运行容器的最佳实践)
- [4.4 容器常用操作](#4.4 容器常用操作)
-
- [stop/start/restart 容器](#stop/start/restart 容器)
-
- [停止容器(docker stop)](#停止容器(docker stop))
- [启动停止的容器(docker start)](#启动停止的容器(docker start))
- [重启容器(docker restart)](#重启容器(docker restart))
- 自动重启容器(--restart)
- [pause/unpause 容器](#pause/unpause 容器)
-
- [暂停容器(docker pause)](#暂停容器(docker pause))
- [恢复容器(docker unpause)](#恢复容器(docker unpause))
- [删除容器(docker rm)](#删除容器(docker rm))
- [4.5 一张图搞懂容器所有操作](#4.5 一张图搞懂容器所有操作)
- [4.6 限制容器对内存的使用](#4.6 限制容器对内存的使用)
- [4.7 限制容器对 CPU 的使用](#4.7 限制容器对 CPU 的使用)
-
- [CPU 份额说明](#CPU 份额说明)
- [示例:CPU 份额配置实验](#示例:CPU 份额配置实验)
-
- [步骤 1:启动高权重容器(容器 A)](#步骤 1:启动高权重容器(容器 A))
- [步骤 2:启动低权重容器(容器 B)](#步骤 2:启动低权重容器(容器 B))
- [步骤 3:观察 CPU 占用情况](#步骤 3:观察 CPU 占用情况)
- [步骤 4:暂停容器 A,观察容器 B](#步骤 4:暂停容器 A,观察容器 B)
- [4.8 export 和 import 容器](#4.8 export 和 import 容器)
- [4.9 实现容器的底层技术](#4.9 实现容器的底层技术)
-
- cgroup
-
- [cgroup 目录结构](#cgroup 目录结构)
- [示例:查看容器 cgroup 配置](#示例:查看容器 cgroup 配置)
- namespace
-
- [1. Mount namespace](#1. Mount namespace)
- [2. UTS namespace](#2. UTS namespace)
- [3. IPC namespace](#3. IPC namespace)
- [4. PID namespace](#4. PID namespace)
- [5. Network namespace](#5. Network namespace)
- [6. User namespace](#6. User namespace)
- 容器常用操作命令
第 4 章 docker容器
4.1 如何运行容器?
上一章我们学习了如何构建 Docker 镜像,并通过镜像运行容器。本章将深入讨论容器:学习容器的各种操作,容器各种状态之间如何转换,以及实现容器的底层技术。
运行容器
docker run = docker create + docker start

容器启动时执行的命令可通过三种方式指定:
- CMD 指令
- ENTRYPOINT 指令
- docker run 命令行中指定
示例 1:创建并启动容器
[root@docker ~]# docker create ubuntu # 使用ubuntu镜像创建容器
eb1aa0ca86b2d49250ebe64913af50e88482ad68b9c3e61ef8f8da9c24b00f7a # 新创建的容器长ID
[root@docker ~]# docker ps -a # create 的容器状态为 Created
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eb1aa0ca86b2 ubuntu "/bin/bash" 2 seconds ago Created quizzical_goldwasser
[root@docker ~]# docker start eb1aa0ca86b2 # 启动容器
eb1aa0ca86b2
[root@docker ~]# docker ps -a # 容器启动后又退出(无持续运行命令)
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eb1aa0ca86b2 ubuntu "/bin/bash" 23 seconds ago Exited (0) 3 seconds ago quizzical_goldwasser
示例 2:启动容器并执行命令
[root@docker ~]# docker run ubuntu pwd # 创建容器并执行pwd命令
/
容器启动时执行 pwd,返回容器中的当前目录 /。
让容器长期运行
容器的生命周期依赖于启动时执行的命令,命令结束容器即停止。要让容器长期运行,需执行持续运行的命令。
示例:通过循环命令保持容器运行
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ; echo hahaha; done"
2a0bfa267fe146753b4fc8b23d55b08fbe3a5f9b5e093de6885133f6bbd20c56
-d:后台启动容器- 循环命令让 bash 不退出,容器持续运行
查看运行中的容器:
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a0bfa267fe1 ubuntu "/bin/bash -c 'while..." 7 minutes ago Up 7 minutes vigorous_pike
示例:运行后台服务容器(httpd)
[root@docker ~]# docker run --name "my_http_server" -d httpd
7a8c3e30bbdb9103d9a134212d0c1fde809d312120183a413d73592ee85b210c
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7a8c3e30bbdb httpd "httpd-foreground" 11 seconds ago Up 10 seconds 80/tcp my_http_server
--name:指定容器名称,方便后续操作- httpd 镜像通过 CMD 指定默认启动命令
httpd-foreground,持续运行提供服务

4.2 两种进入容器的方法
我们经常需要进到容器里去做一些工作,比如查看日志、调试、启动其他进程等。有两种方法进入容器:attach 和 exec。
docker attach
通过 docker attach 可以 attach 到容器启动命令的终端,跟随容器启动命令的输出。
示例:attach 到容器
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ; echo I_am_in_container ; done"
dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6
[root@docker ~]# docker attach dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6
I_am_in_container
I_am_in_container
I_am_in_container
注:可通过 Ctrl+p + Ctrl+q 组合键退出 attach 终端,不终止容器。
docker exec
通过 docker exec 可在容器中打开新的终端,启动新进程,是更常用的进入容器方式。
示例:exec 进入容器

-it:以交互模式打开伪终端(pseudo-TTY),执行 bash- 容器的 hostname 为其 "短 ID"
- 可在容器中执行常规 Linux 命令,如
ps查看进程
attach VS exec 区别
- attach 直接进入容器启动命令的终端,不启动新进程
- exec 在容器中打开新终端,可启动新进程
- 查看启动命令输出用 attach,其他操作(调试、执行命令)用 exec
查看容器日志(无需进入容器)
[root@docker ~]# docker logs -f dc508b94447f
I_am_in_container
I_am_in_container
I_am_in_container
-f:类似tail -f,持续打印新日志
下一节聊聊运行容器的最佳实践。
4.3 运行容器的最佳实践
按用途容器大致可分为两类:服务类容器和工具类容器。
1. 服务类容器
以 daemon 形式运行,对外提供服务(如 web 服务器、数据库),适合用 -d 后台启动,需调试时用 exec -it 进入。
示例:后台运行 httpd 服务容器
[root@docker ~]# docker run -d --name my_web -p 80:80 httpd
2. 工具类容器
提供临时工作环境,通常以 run -it 方式运行,用完即停。
示例:运行 busybox 工具容器
[root@docker ~]# docker run -it busybox
/ # wget www.baidu.com
Connecting to www.baidu.com (223.109.82.6:80)
saving to 'index.html'
index.html 100% |********************| 2381 0:00:00 ETA
'index.html' saved
/ # exit
[root@docker ~]#
run -it:容器启动后直接进入交互终端- 执行
exit退出终端,容器同时停止 - 工具类容器常用基础镜像(busybox、debian、ubuntu 等)
容器运行小结
核心知识点
- 容器启动命令(CMD/ENTRYPOINT/docker run 指定)结束时,容器停止
- 通过
-d后台启动容器 - 通过
exec -it进入容器执行命令
指定容器的三种方式
- 短 ID(如
dc508b9) - 长 ID(如
dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6) - 容器名称(如
my_web),可通过--name命名,用docker rename重命名
下一节讨论容器的其他操作,比如 stop, restart, pause, delete。
4.4 容器常用操作
前面讨论了如何运行容器,本节学习容器的其他常用操作。
stop/start/restart 容器
停止容器(docker stop)

-
docker stop本质是向容器进程发送SIGTERM信号,允许容器优雅退出 -
快速停止容器用 docker kill(发送 SIGKILL 信号)

启动停止的容器(docker start)

docker start保留容器第一次启动时的所有参数
重启容器(docker restart)

- 等效于
docker stop + docker start
自动重启容器(--restart)
启动容器时设置 --restart,容器异常退出时自动重启:

--restart=always:无论容器因何种原因退出(包括正常退出),均自动重启--restart=on-failure:3:仅当启动进程退出代码非 0 时重启,最多重启 3 次
pause/unpause 容器
暂停容器(docker pause)

恢复容器(docker unpause)

- 暂停状态的容器不占用 CPU 资源,仅保留内存数据
删除容器(docker rm)
删除停止的容器

[root@docker ~]# docker rm 35af7150bd17 9769bb915803
35af7150bd17
9769bb915803
批量删除所有退出的容器

[root@docker ~]# docker rm -v $(docker ps -aq -f status=exited)
-v:删除容器关联的匿名数据卷docker ps -aq -f status=exited:筛选所有状态为 "已退出" 的容器 ID

强制删除运行中的容器
[root@docker ~]# docker rm -f $(docker ps -aq)
-f:强制删除,先停止容器再删除(慎用!)
注意:docker rm 删除容器,docker rmi 删除镜像,避免混淆。
4.5 一张图搞懂容器所有操作
前面我们已经讨论了容器的各种操作,对容器的生命周期有了大致的理解,下面这张状态机很好地总结了容器各种状态之间是如何转换的。


补充说明
容器创建与启动:docker create 创建容器(状态为 Created),docker start 启动容器(状态为 Running);docker run 是二者的组合。

自动重启触发条件:仅当容器的启动进程退出(正常退出或异常退出,如 OOM)时,--restart 策略生效;docker stop/docker kill 导致的退出不会自动重启。

如果掌握了前面的知识,要看懂这张图应该不难。
好了,容器操作就讨论到这里,下一节我们将学习如何限制容器对资源的使用。
4.6 限制容器对内存的使用
cgroup 简介
docker 通过 cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。
cgroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源 (如 cpu、memory、磁盘 IO 等等) 的机制,被 LXC、docker 等很多项目用于实现进程资源控制。
核心 cgroup 子系统(用于容器资源控制):
- blkio:限制块设备 IO
- cpu:CPU 调度与配额控制
- cpuacct:CPU 资源使用统计
- cpuset:多核心 CPU 核心分配
- devices:设备访问控制
- freezer:暂停 / 恢复进程组
- memory:内存使用限制与统计
stress 工具
stress 是模拟压力测试的工具,可模拟 CPU、内存等资源高占用场景,用于测试容器资源限制效果。
构建 stress 镜像(Dockerfile)
[root@docker dockerfile]# vim Dockerfile
# Version 1
FROM ubuntu
MAINTAINER gaoqd "6946630@qq.com"
RUN apt-get -y update && apt-get -y install stress
ENTRYPOINT ["/usr/bin/stress"]
构建镜像
[root@docker ~]# docker build -t ubuntu-with-stress .
内存限额
Docker 通过两组参数控制容器内存使用:
-m或--memory:设置物理内存使用限额(如 100M、2G)--memory-swap:设置物理内存 + swap 总限额
内存限额规则
- 仅指定
-m 200M:--memory-swap默认为-m的 2 倍(即 400M,含 200M 物理内存 + 200M swap) --memory-swap=300M -m 200M:允许使用 200M 物理内存 + 100M swap--memory-swap=0或不设置:--memory-swap默认为-m的 2 倍--memory-swap=-1:物理内存受限,swap 不受限(使用宿主机所有可用 swap)--memory-swap与-m相等:容器不能使用 swap
示例 1:内存限额内运行

[root@docker ~]# docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 280M -v
--vm 1:启动 1 个内存工作线程--vm-bytes 280M:每个线程分配 280M 内存(在 300M 总限额内)- 运行结果:线程正常分配 / 释放内存,循环执行
示例 2:超出内存限额

[root@docker ~]# docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 310M
- 分配内存超出 300M 限额,stress 线程被 kill,容器退出
下一节我们学习如何限制容器对 CPU 资源的使用。
4.7 限制容器对 CPU 的使用
默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。
Docker 可以通过 -c 或 --cpu-shares 设置容器使用 CPU 的权重。如果不指定,默认值为 1024。
CPU 份额说明
--cpu-shares是弹性权重,非绝对限额- 仅当多个容器竞争 CPU 资源时,权重才生效
- 单个容器时,即使权重为 50,也可独占 host 所有 CPU
- 权重比例决定 CPU 时间片分配比例(如容器 A 权重 1024,容器 B 权重 512,A 获得的 CPU 时间片是 B 的 2 倍)
示例:CPU 份额配置实验
步骤 1:启动高权重容器(容器 A)
[root@docker ~]# docker run --name "container_A" -it -c 1024 ubuntu-with-stress --cpu 4 -v
-
--cpu 4:启动 4 个 CPU 工作线程(与 host CPU 核心数一致,充分占用 CPU) -
查看容器 cgroup 配置:
[root@docker ~]# cat /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares 1024
步骤 2:启动低权重容器(容器 B)
[root@docker ~]# docker run --name "container_B" -it -c 512 ubuntu-with-stress --cpu 4 -v
-
查看容器 cgroup 配置:
[root@docker ~]# cat /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares 512
步骤 3:观察 CPU 占用情况
在 host 执行 top 或 docker stats:


- 容器 A CPU 占用率约为容器 B 的 2 倍,符合权重比例
步骤 4:暂停容器 A,观察容器 B
[root@docker ~]# docker pause container_A


- 容器 A 暂停后,容器 B 独占 CPU,使用率接近 100%(单核心)或更高(多核心)
4.8 export 和 import 容器
export - 容器导出
将容器的文件系统导出为 tar 包(不含镜像元数据和历史记录,仅保存容器当前状态)。
命令格式
[root@docker ~]# docker export --help
Usage: docker export [OPTIONS] CONTAINER
Export a container's filesystem as a tar archive
Aliases:
docker container export, docker export
Options:
-o, --output string Write to a file, instead of STDOUT
示例:导出容器
# 创建测试容器
[root@docker ~]# docker run -d --name httpd1 httpd
e4f0a329c4df50ef0afb0bf21e22edc20e5a24c03ae64c6340aa2992d6e32525
# 导出容器为 tar 包
[root@docker ~]# docker export httpd1 -o myhttpd.tar
[root@docker ~]# ls
myhttpd.tar
import - 容器 tar 包导入
将 export 导出的 tar 包导入为新镜像(无元数据,视为全新镜像)。
命令格式
[root@docker ~]# docker import --help
Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
Import the contents from a tarball to create a filesystem image
Aliases:
docker image import, docker import
Options:
-c, --change list Apply Dockerfile instruction to the created image
-m, --message string Set commit message for imported image
--platform string Set platform if server is multi-platform capable
示例:导入容器 tar 包
# 导入为新镜像
[root@docker ~]# docker import myhttpd.tar myweb:v1
sha256:9fcb90561d2014130ef19f9dd8af5efd7a0a4a2154907bc818ac6b887f13755c
# 查看导入的镜像
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myweb v1 9fcb90561d20 3 seconds ago 146MB
<none> <none> 9ae4699fa217 32 seconds ago 146MB
docker save 和 docker export 对比
| 特性 | docker save | docker export |
|---|---|---|
| 操作对象 | 镜像 | 容器 |
| 包含内容 | 镜像元数据、历史记录、所有层 | 仅容器文件系统(当前状态) |
| 导入结果 | 恢复为原镜像(含元数据) | 生成新镜像(无元数据) |
| 适用场景 | 镜像备份、迁移 | 容器状态快照、快速分享容器文件系统 |
注意事项
docker export导出的 tar 包不能用docker load导入docker save导出的 tar 包不能用docker import导入(需解压后手动处理)
4.9 实现容器的底层技术
为了更好地理解容器的特性,本节我们将讨论容器的底层实现技术。
cgroup 和 namespace 是最重要的两种技术。cgroup 实现资源限额,namespace 实现资源隔离。
cgroup
cgroup 全称 Control Group。Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。Docker 的 --cpu-shares、-m、--device-write-bps 等参数本质是配置 cgroup。
cgroup 目录结构
cgroup 配置文件存储在 /sys/fs/cgroup 下,每个子系统对应一个目录(如 cpu、memory、blkio)。
示例:查看容器 cgroup 配置
# 启动带 CPU 限额的容器
[root@docker ~]# docker run -it --cpu-shares 512 ubuntu-with-stress -c 1 -v
# 查看容器长ID
[root@docker ~]# docker ps --no-trunc
# 查看 CPU cgroup 配置
[root@docker ~]# ls /sys/fs/cgroup/cpu/docker/<容器长ID>/
cpu.cfs_period_us cpu.cfs_quota_us cpu.shares cpu.stat ...
[root@docker ~]# cat /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares
512
cpu.shares保存--cpu-shares的配置值- 其他文件对应 CPU 相关的其他限额配置(如
cpu.cfs_quota_us限制 CPU 配额)
namespace
Linux 通过 namespace 管理 host 全局资源,让每个容器认为自己独占资源,实现容器间资源隔离。
Linux 提供六种 namespace,对应六种资源隔离:
1. Mount namespace
- 让容器拥有独立的文件系统视图(独立的
/目录) - 容器内的
mount/umount操作仅在容器内生效,不影响 host 和其他容器
2. UTS namespace
-
让容器拥有独立的 hostname 和域名
-
可通过
-h或
--hostname指定容器 hostname:
[root@docker ~]# docker run -h myhost -it ubuntu root@myhost:/# hostname myhost
3. IPC namespace
- 让容器拥有独立的共享内存、信号量(semaphore)和消息队列
- 容器间 IPC 隔离,不能直接通信(需通过网络等方式)
4. PID namespace
-
让容器拥有独立的 PID 空间(容器内 PID=1 的进程不是 host 的 init 进程)
-
容器内只能看到自身的进程,看不到 host 和其他容器的进程:
[root@docker ~]# docker exec -it a80f1f8b692c bash root@a80f1f8b692c:/# ps axf PID TTY STAT TIME COMMAND 1 pts/0 Ss 0:00 /bin/bash 17 pts/1 Ss 0:00 bash 25 pts/1 R+ 0:00 \_ ps axf
5. Network namespace
- 让容器拥有独立的网卡、IP、路由和端口
- 每个容器默认有独立的网络栈,通过网桥、端口映射等实现网络通信(后续章节详细讨论)
6. User namespace
-
让容器拥有独立的用户空间(容器内创建的用户仅在容器内可见)
[root@docker ~]# docker exec -it a80f1f8b692c bash root@a80f1f8b692c:/# useradd gaoqd root@a80f1f8b692c:/# exit [root@docker ~]# su - gaoqd su: user gaoqd does not exist -
cgroup:限制容器对 CPU、内存、IO 等资源的使用量
-
namespace:隔离容器的文件系统、网络、PID、用户等资源
-
二者结合实现了容器 "轻量级虚拟化" 的核心特性(高效、隔离)
本章首先通过大量实验学习了容器的各种操作以及容器状态之间如何转换,然后讨论了限制容器使用 CPU、内存和 Block IO 的方法,最后学习了实现容器的底层技术:cgroup 和 namespace。
容器常用操作命令
- create:创建容器
- run:创建并启动容器
- pause:暂停容器
- unpause:恢复容器运行
- stop:发送 SIGTERM 停止容器
- kill:发送 SIGKILL 快速停止容器
- start:启动停止的容器
- restart:重启容器
- attach:attach 到容器启动进程的终端
- exec:在容器中启动新进程(常用
-it进入终端) - logs:查看容器启动进程的日志(
-f持续打印) - rm:删除容器
a80f1f8b692c bash
root@a80f1f8b692c:/# useradd gaoqd
root@a80f1f8b692c:/# exit
root@docker \~\]# su - gaoqd su: user gaoqd does not exist - cgroup:限制容器对 CPU、内存、IO 等资源的使用量 - namespace:隔离容器的文件系统、网络、PID、用户等资源 - 二者结合实现了容器 "轻量级虚拟化" 的核心特性(高效、隔离) 本章首先通过大量实验学习了容器的各种操作以及容器状态之间如何转换,然后讨论了限制容器使用 CPU、内存和 Block IO 的方法,最后学习了实现容器的底层技术:cgroup 和 namespace。 ### 容器常用操作命令 - create:创建容器 - run:创建并启动容器 - pause:暂停容器 - unpause:恢复容器运行 - stop:发送 SIGTERM 停止容器 - kill:发送 SIGKILL 快速停止容器 - start:启动停止的容器 - restart:重启容器 - attach:attach 到容器启动进程的终端 - exec:在容器中启动新进程(常用 `-it` 进入终端) - logs:查看容器启动进程的日志(`-f` 持续打印) - rm:删除容器