前言

本文档聚焦 资源限制、数据卷容器、端口映射、容器互联 四大核心章节,系统讲解如何通过 cgroups 控制容器资源使用、利用数据卷实现持久化存储、通过端口映射暴露服务,以及容器间通信机制。内容严格保留所有命令、配置参数、路径及实验步骤,便于教学与实操参考。

  1. 什么是CgroupS
  2. cgroup 对CPU限制
  3. cgroup 对内存的限制
  4. 资源限制的主要类型
  5. 资源限制几种方式
  6. 资源限制的状态查询
  7. 数据卷 容器
  8. 端口映射
  9. 容器中间互联

理论部分

4_资源限制

Docker利用Linux内核提供的 cgroups(Control Groups) 机制,实现资源隔离与限制。

4.1_cgroups四大功能

  • 资源限制:设定任务使用资源上限。
  • 优先级分配:通过权重分配CPU时间片、IO带宽。
  • 资源统计:记录CPU时长、内存用量等。
  • 任务控制:挂起、恢复或终止进程组。

4.2_CPU资源控制

① CPU使用率上限(硬限制)
参数

  • --cpus=0.5:核数,新版本友好封装
  • --cpu-period=100000:周期,值(微秒)在1千~1百万之间,和配额一起用。
  • --cpu-quota=50000:配额,周期的占比,值不超过周期。(quota = -1 表示不限制)

原理:CFS 调度器基于配额/周期计算可用 CPU 份额。
注意:在 N 核机器上,--cpus=0.5 ≈ 占用整机 0.5/N 的 CPU 能力。

② CPU权重(软限制)

  • 参数--cpu-shares=512(默认 1024)
  • 行为:仅在 CPU 争用时生效,按权重比例分配时间片。

③ CPU亲和性

  • 参数--cpuset-cpus="0,2"(从0开始,第1颗和第3颗)
  • 作用:将容器进程绑定到指定 CPU 核,提升性能稳定性。

4.3_内存使用限制

① 基础选项

  • -m, --memory=512m:限制物理内存。
  • --memory-swap=1g:限制物理内存 + swap 总量(故而不能低于物理内存限制)

② 关键规则

配置行为
-m 300m --memory-swap=1g物理内存 300MB,swap 可用 700MB
--memory-swap = -m禁用 swap,内存耗尽触发 OOM
--memory-swap = -1不限制,宿主机swap有多少就用多少。

OOM(Out-Of-Memory):内核杀掉超限容器。

4.4_磁盘 IO 控制

① 限制参数

  • --device-read-bps /dev/sda:1M :限制设备上读速率为 1 MB/s
  • --device-write-bps /dev/sda:1MB:写带宽上限
  • --device-read-iops /dev/sda:100 :限制读 IOPS(次数)
  • --device-write-iops /dev/sda:100:写 IOPS 上限

② 注意事项

  • 必须指定真实块设备路径
  • 云环境可能受底层虚拟化限制。

5_数据卷容器

5.1_数据卷

Data Volumes

  • 定义:容器内的特殊目录,可与宿主机或其他容器共享。
  • 特性
    • 数据持久化(容器删除不影响数据)。
    • 支持绑定挂载(-v /host:/container)。
    • 绕过 UnionFS,直接写入宿主机文件系统。

5.2_数据卷容器

Data Volume Container

  • 定义:专门用于提供数据卷的容器,不运行应用。
  • 用途:多个容器共享同一数据源,避免重复挂载宿主机目录。
  • 实现:通过 --volumes-from <容器名> 挂载其所有数据卷。

优势:解耦数据与应用,便于备份与迁移。

5.3_数据卷原理

共享状态

直接挂载路径(对比)

应用容器使用
--volumes-from

数据卷容器路径

第一步:创建数据卷

创建命名数据卷
docker volume create app_data

数据卷 app_data 准备就绪

运行数据卷容器
docker run -v app_data:/data
--name volume_container busybox

数据卷容器 volume_container 运行中
(通常执行 sleep infinity)

启动应用容器 A
docker run --volumes-from volume_container
--name app1 my_app

启动应用容器 B
docker run --volumes-from volume_container
--name app2 my_app

启动应用容器 C
docker run -v app_data:/data
--name app3 my_app

容器 A, B, C 均挂载
同一个数据卷 app_data

容器 A 写入 /data/file.txt

容器 B 和 C 可立即读取
/data/file.txt

实现多应用实例间
数据持久化与实时共享


6_端口映射

容器内部服务默认无法被外部访问,需通过端口映射暴露。

6.1_随机端口映射(-P)

  • 自动将容器 EXPOSE 的端口映射到宿主机 32768~65535 随机端口。
  • 适用于无需固定端口的场景。

6.2_指定端口映射(-p)

  • 手动指定映射关系:-p <宿主端口>:<容器端口>
  • 支持协议指定:-p 8080:80/tcp
  • 注意:宿主端口不能低于 60(Compose 限制)。

原理:Docker 在 iptables 中添加 DNAT 规则实现端口转发。


7_容器互联

容器互联指容器间通过网络相互通信。

7.1_–link 方式(已弃用)

语法--link <源容器>:<别名>
机制

  • /etc/hosts 添加源容器 IP 记录。
  • 设置环境变量(如 WEB1_PORT=tcp://172.17.0.2:80)。
  • 局限:单向连接,不支持 DNS 动态解析。

宿主机 Docker Engine

--link 机制

成功解析为 172.17.0.2

--link 内部机制

etc/hosts 更新
172.17.0.2 web1

环境变量注入
WEB1_PORT等变量/

源容器: web1
IP: 172.17.0.2

接收容器: web2
IP: 172.17.0.3

步骤1: docker run --name web1

步骤2: docker run --name web2 --link web1:web1

步骤3: docker exec -it web2 ping web1

域名解析

连通成功 Ping OK

7.2_自定义网络(推荐)

  • 创建自定义 bridge 网络:docker network create mynet
  • 容器加入同一网络后,自动支持 DNS 解析(通过容器名通信)。
  • 优势:双向通信、动态发现、无需修改 /etc/hosts

生产建议:始终使用自定义网络替代 --link


实验部分

1_资源限制实验

1.1_CPU 限制

① 限制 CPU 使用率
方法一:直接更改 cgroup 配置

  • 查看容器ID
docker inspect -f '{{.Id}}' 容器名
  • 修改CFS调度器文件
cd /sys/fs/cgroup/cpu/docker/<容器ID>/
cat cpu.cfs_period_us	# 默认会输出100000(周期值,微秒)
cat cpu.cfs_quota_us	# 默认会输出-1(配额值,-1表示不限制)

# 修改配额值
echo 50000 > cpu.cfs_quota_us

方法二:使用docker命令指定周期值和配额值

docker run -itd --name c1 --cpu-quota 100000 centos:7 /bin/bash

# 查看容器的CPU配额
docker inspect -f '{{.HostConfig.CpuQuota}}' 容器名或ID
# 查看容器的CPU配额
docker inspect -f '{{.HostConfig.CpuPeriod}}' 容器名或ID

方法三:使用docker命令指定CPU(s)核数

docker run -itd --name c2 --cpus="0.5" centos:7 /bin/bash

--cpus="0.5":限制容器最多使用 0.5 个 CPU 核心

② 设置 CPU 权重

docker run -itd --name c1 --cpu-shares 512 centos:7
docker run -itd --name c2 --cpu-shares 1024 centos:7

# 如果容器已存在,可修改CPU权重,需重启容器
docker update --cpu-shares=512 c1
docker update --cpu-shares=1024 c2

# 查看容器的CPU权重
docker inspect -f '{{.HostConfig.CpuShares}}' 容器名或ID

--cpu-shares:设置 CPU 时间片权重(默认 1024),争用时 c2 获得约 2 倍于 c1 的 CPU 时间

③ 绑定 CPU 核

  • 查看宿主机CPU核
# 检查可用的 CPU
cat /sys/fs/cgroup/cpuset/cpuset.cpus

# 查看核心数
nproc
lscpu | grep "Socket"
  • 容器绑定CPU核
docker run -itd --name c1 --cpuset-cpus "1,3" centos:7 /bin/bash
docker update --cpuset-cpus="1,3" c1

--cpuset-cpus "1,3":将容器进程绑定到宿主机的第 1 和第 3 个 CPU 核(从 0 开始计数)

  • 查看容器绑定的CPU核
docker inspect -f '{{.HostConfig.CpusetCpus}}' c1

④ 压力测试
方法一:使用死循环脚本

  • 准备一个死循环脚本cpu.sh
#!/bin/bash
i=0
while true; do let i++; done
  • 拷贝到容器并后台运行该脚本
docker cp cpu.sh c1:/opt/
docker exec -itd c1 /bin/bash /opt/cpu.sh

方法二:使用stress压测工具

# 进入容器
docker exec -it c1 bash
# 安装 stress
yum install -y epel-release
yum install -y stress
# 生成 CPU 负载
stress -c 4

stress -c 4:启动 4 个 worker 进程进行平方根计算,模拟 CPU 密集型负载

⑤ 验证

# 实时监测
docker stats

# 静态输出
docker stats --no-stream c1 c2
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O         BLOCK I/O        PIDS
e2d845b42569   c2        51.36%    534.7MiB / 3.7GiB   14.11%    105MB / 704kB   442kB / 50.5MB   7
3e1540bd88a7   c1        102.25%   1MiB / 3.7GiB       0.03%     698B / 0B       0B / 0B          7
  • 宿主机观察各核利用率
top -d 1

docker stats:实时显示容器资源使用情况;top1 键显示每个 CPU 核的使用率

  • 查看 cgroup 配置
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu/docker/<容器ID>/.....

1.2_内存限制

  • 查看容器当前内存限制
docker inspect -f '内存: {{.HostConfig.Memory}} 内存交换: {{.HostConfig.MemorySwap}}' <容器名/ID>

run创建容器加-m时,--memory-swap=缺省默认值是容器物理内存的2倍。如果不加-m,则--memory-swap=为0。

① 限制物理内存

docker run -itd --name memtest -m 512m centos:7 /bin/bash

-m 512m:限制容器最大使用 512MB 物理内存

② 限制内存+swap

docker update -m 300m --memory-swap=1g memtest

--memory-swap=1g:物理内存 + swap 总量不超过 1GB,因此 swap 可用 = 700MB

③ 验证

  • 实时监控
docker stats
  • 查看 cgroup 限制
cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes
  • 模拟 OOM
stress --vm 1 --vm-bytes 600M   # 超过 512m 限制
docker exec c1 stress --vm 2 --vm-bytes 800M --timeout 30s

stress --vm 1 --vm-bytes 600M:分配 600MB 虚拟内存触发 OOM;dmesg | tail 查看内核日志确认 kill 事件

  • 查看 OOM 日志
dmesg -T | tail
watch -n 1 'dmesg -T | tail -5'

dmesg -T:内核日志,-T:人类可读的时间戳

1.3_磁盘 IO 限制

① 限制写速

docker run -it --name iotest --device-write-bps /dev/sda:1MB centos:7 /bin/bash

--device-write-bps /dev/sda:1MB:限制对 /dev/sda 设备的写入带宽为 1MB/s

② 验证写速

# 在容器内执行
dd if=/dev/zero of=test.out bs=1M count=10 oflag=direct

dd:数据复制工具;oflag=direct:绕过文件系统缓存直写磁盘;输出应显示 ~1.0 MB/s


2_数据卷容器实验

2.1_创建与挂载数据卷

① 挂载宿主机目录

  • 建一个nginx容器,并拷贝容器里nginx存放数据的目录。
docker cp nginx:/etc/nginx/ /etc/
docker cp nginx:/var/log/nginx/ /var/log/
docker cp nginx:/usr/share/nginx/html/ /var/www/
# 或者直接在宿主机yum安装一个nginx,前提是版本一致。
  • 再重新建一个nginx容器,挂载数据卷。
docker run -v /etc/nginx:/etc/nginx \
		   -v /var/log/nginx:/var/log/nginx \
		   -v /var/www/html:/usr/share/nginx/html \
		   -p 80:80 -d --name web1 nginx:latest 

-v /var/www/html:/usr/share/nginx/html:将宿主机 /var/www/html 挂载到容器 /usr/share/nginx/html

② 在容器中写入数据

echo "this is web1" > /var/www/html/index.html

③ 验证宿主机数据

curl http://localhost

输出:this is web1,证明数据同步

2.2_创建数据卷容器

① 创建专用数据卷容器

docker run --name web2 -v /data1 -v /data2 -it centos:7 /bin/bash

-v /data1 -v /data2:在容器内创建两个匿名数据卷(不绑定宿主机路径)

② 在数据卷容器中写入数据

echo "this is web2" > /data1/abc.txt
echo "THIS IS WEB2" > /data2/ABC.txt

2.3_共享数据卷

① 创建新容器并挂载数据卷

docker run -it --volumes-from web2 --name web3 centos:7 /bin/bash

--volumes-from web2:挂载 web2 容器的所有数据卷

② 验证数据共享

cat /data1/abc.txt
cat /data2/ABC.txt

输出:

this is web2
THIS IS WEB2

3_端口映射实验

3.1_随机端口映射

① 启动容器

docker run -d --name test1 -P nginx

-P:自动映射容器暴露的端口(如 80)到宿主机随机高端口

② 查看映射

docker ps -a

输出示例:0.0.0.0:49170->80/tcp

③ 访问服务

浏览器访问:http://<宿主IP>:49170

3.2_指定端口映射

① 启动容器

docker run -d --name test2 -p 43000:80 nginx

-p 43000:80:将宿主机 43000 端口映射到容器 80 端口

② 查看映射

docker ps -a

输出示例:0.0.0.0:43000->80/tcp

③ 访问服务

浏览器访问:http://<宿主IP>:43000


4_容器互联实验

4.1_–link 方式(传统方法)

① 创建源容器

docker run -itd -P --name web1 centos:7 /bin/bash

② 创建接收容器并链接

docker run -itd -P --name web2 --link web1:web1 centos:7 /bin/bash

--link web1:web1:将 web1 容器暴露给 web2,并设置别名为 web1

③ 测试连通性

docker exec -it web2 bash
cat /etc/hosts
ping web1

成功 ping 通,证明 --link 生效

4.2_自定义网络方式(推荐)

① 创建自定义网络

docker network create mynet

② 启动容器并加入网络

docker run -itd --name app1 --network mynet centos:7 /bin/bash
docker run -itd --name app2 --network mynet centos:7 /bin/bash

③ 测试 DNS 解析

docker exec -it app1 ping app2

成功 ping 通,证明自定义网络自动解析容器名


结语

资源限制要点:CPU 用 --cpus(硬限)或 --cpu-shares(权重),内存必须设 -m 并谨慎配置 swap,IO 限制需指定真实设备。
数据持久化方案:简单场景用 -v 绑定挂载,多容器共享用数据卷容器(--volumes-from)。
服务暴露方式:开发用 -P 随机映射,生产用 -p 指定端口。
容器通信演进--link 已过时,自定义网络是唯一推荐方案,支持 DNS 自动解析。

[!question] --cpu-shares--cpus 的本质区别是什么?
--cpu-shares 是相对权重(争用时按比例分配),非硬限制;--cpus 是绝对上限(硬限制),即使 CPU 空闲也不可超用。

[!question] 如何避免容器因内存超限被 OOM kill?

  1. 合理设置 -m;2. 限制 --memory-swap;3. 应用层实现内存监控与优雅降级;4. 配置容器重启策略(--restart=on-failure)。

[!question] 在云服务器上设置 --device-write-bps 为何无效?
云盘(如 AWS EBS、阿里云云盘)的 IO 性能由底层虚拟化层控制,容器内的 cgroups 限制可能被覆盖或忽略,建议在物理机验证。

[!question] 为什么设置 --memory-swap = -m 会禁用 swap?
此配置表示“物理内存 + swap 总量 = 物理内存限制”,即 swap 可用量为 0,从而禁用 swap,内存超限直接 OOM。

[!question] 数据卷容器与普通容器挂载数据卷有何区别?
数据卷容器专用于提供数据卷,生命周期独立于应用容器;普通容器挂载的数据卷随容器删除而丢失(除非使用命名卷)。

[!question] -P-p 的本质区别是什么?
-P 仅映射 Dockerfile 中 EXPOSE 声明的端口,且端口随机;-p 可映射任意端口,且可手动指定宿主端口。

[!question] 为何推荐使用自定义网络而非 --link
--link 需静态配置、单向连接、不支持动态发现;自定义网络提供双向 DNS 解析、动态 IP 分配、更安全的隔离。

扩展

容器存储的生命周期

绑定挂载

命名卷

数据卷容器

读操作

写操作

备份

恢复

开始

选择数据管理方式

绑定挂载 Bind Mount

命名卷 Named Volume

数据卷容器 Volume Container

宿主机路径: /host/path

直接映射宿主机目录

文件双向实时同步

应用容器使用

Docker 管理存储位置

自动创建和数据迁移

容器间共享数据

创建专用数据卷容器

定义数据卷路径

其他容器 --volumes-from

多容器共享数据

数据操作类型

从数据卷读取

写入数据卷

备份数据卷

恢复数据卷

数据持久化存储

数据备份流程

数据恢复流程

生命周期结束

是否删除数据卷

手动删除卷 docker volume rm

数据保留供后续使用

结束

更多推荐