一、Docker资源管理概述

1.1 为什么需要资源管理

当多个容器同时运行时,如果不进行资源限制,某个容器可能会占用全部硬件资源,导致其他容器无法正常工作。Docker通过Cgroups(Control Groups)技术来控制容器使用的资源配额,包括:

  • CPU控制:限制CPU使用率和权重
  • 内存控制:限制内存使用上限
  • I/O控制:限制磁盘读写速度

1.2 容器启动管理(重启策略)

为了保证容器运行的健壮性(自愈能力),Docker提供了--restart参数,让容器在退出时自动尝试重启。

四种重启策略
策略值说明适用场景
no默认策略,守护进程启动时不自动重启容器测试环境、临时容器
on-failure[:max-retries]仅当容器以非零状态退出时才重启,可限制重试次数需要容错但不希望无限重启的场景
always无论容器当前状态如何,总是在守护进程启动时启动生产环境核心服务
unless-stopped容器退出时总是重启,但守护进程启动前已停止的容器除外手动停止后不希望自动重启的场景

二、实验一:容器重启策略测试

实验目标

验证四种重启策略在不同场景下的行为差异。

实验步骤

步骤1:测试 --restart=no(默认策略)
# 创建两个容器,一个不指定策略(默认no),一个显式指定no
docker run -it --name c1 centos:7.7.1908 /bin/bash
docker run -it --name c2 --restart=no centos:7.7.1908 /bin/bash

# 查看容器重启策略配置(在inspect输出的第42行左右)
docker inspect c2

# 重启Docker服务
systemctl restart docker

# 查看容器状态:两个容器都不会自动重启
docker ps -a

# 停止所有容器
docker stop $(docker ps -aq)

# 清理容器
docker rm $(docker ps -aq)

# 清理已经停止的容器
docker container prune

预期结果:重启Docker服务后,c1和c2都处于停止状态,不会自动启动。


步骤2:测试 --restart=on-failure
# 创建两个使用on-failure策略的容器
docker run -it --name c1 --restart=on-failure centos:7.7.1908 /bin/bash
docker run -it --name c2 --restart=on-failure centos:7.7.1908 /bin/bash

# 正常退出容器(exit命令,退出码为0)
# 在容器内执行 exit

# 重启Docker服务
systemctl restart docker

# 查看容器状态
docker ps -a

# 强行关闭或杀死容器(模拟非零退出)
docker kill c1
docker stop c2

预期结果:

  • 正常退出(exit)的容器不会重启(因为退出码为0)
  • 被kill或异常退出的容器会尝试重启
  • 可以指定最大重试次数:--restart=on-failure:3

步骤3:测试 --restart=always
# 创建使用always策略的容器
docker run -it --name c1 --restart=always centos:7.7.1908 /bin/bash

# 停止容器
docker stop c1

# 重启Docker服务
systemctl restart docker

# 查看容器状态:容器会自动启动
docker ps -a

预期结果:无论容器是正常停止还是异常退出,重启Docker服务后都会自动启动。


步骤4:测试 --restart=unless-stopped
# 创建使用unless-stopped策略的容器
docker run -it --name c1 --restart=unless-stopped centos:7.7.1908 /bin/bash

# 场景1:正常退出容器
# 在容器内执行 exit

# 重启Docker服务
systemctl restart docker
docker ps -a  # 容器会自动重启

# 场景2:手动停止容器
docker stop c1

# 重启Docker服务
systemctl restart docker
docker ps -a  # 容器不会自动重启(因为是被手动停止的)

预期结果:

  • 容器异常退出或正常退出后会重启
  • 但如果是在Docker守护进程启动前就被手动停止的容器,不会自动重启

步骤5:更新正在运行的容器重启策略
# 动态修改容器的重启策略
docker update --restart=always c1

# 验证修改
docker inspect c1 | grep -A 5 RestartPolicy

三、实验二:CPU资源控制

实验目标

理解CPU权重(cpu-shares)的工作原理,验证多容器竞争CPU时的资源分配比例。

理论基础

  • CPU权重(cpu-shares):默认值为1024,只有在多个容器同时竞争CPU时才生效
  • 权重比例:容器A权重1000,容器B权重500,则A获得约66.6%的CPU时间,B获得约33.3%
  • 单容器场景:如果只有一个容器运行,权重设置无意义,它可以占用全部可用CPU

实验准备:安装stress压力测试工具

# 在宿主机上stress工具
yum -y install stress

# 查看stress帮助信息
stress --help

# 测试CPU负载(产生2个进程,持续60000秒)
stress -c 2 -v --timeout 60000s

# 另一个终端观察CPU使用情况
top

实验步骤

步骤1:创建两个容器并设置不同CPU权重
# 先清理之前的容器
docker stop c1
docker kill c2
docker ps -a

# 正常退出容器(如果需要)
docker attach c1
exit

# 重启Docker服务确保环境干净
systemctl restart docker
docker ps -a

# 创建docker10容器:权重512,绑定到CPU0和CPU1
docker run -itd --name docker10 --cpuset-cpus 0,1 --cpu-shares 512 centos:7.7.1908 /bin/bash

# 创建docker20容器:权重1024,绑定到CPU0和CPU1
docker run -itd --name docker20 --cpuset-cpus 0,1 --cpu-shares 1024 centos:7.7.1908 /bin/bash

参数说明:

  • --cpuset-cpus 0,1:限制容器只能使用CPU0和CPU1核心
  • --cpu-shares 512:设置CPU权重为512(docker10)
  • --cpu-shares 1024:设置CPU权重为1024(docker20)
  • -itd:交互式、伪终端、后台运行

步骤2:验证容器CPU配置
# 进入docker10容器
docker attach docker10

# 查看容器能使用的CPU核心
cat /sys/fs/cgroup/cpuset/cpuset.cpus
# 输出:0-1

# 查看CPU权重设置
cat /sys/fs/cgroup/cpu/cpu.shares
# 输出:512

# 退出容器(Ctrl+P+Q 分离,不要直接exit)

# 进入docker20容器
docker attach docker20

# 同样验证配置
cat /sys/fs/cgroup/cpuset/cpuset.cpus
cat /sys/fs/cgroup/cpu/cpu.shares
# 输出:1024

步骤3:安装stress并进行CPU压测

注意:以下操作需要在两个容器中分别执行

# ===== 在docker10容器中执行 =====
docker attach docker10

# 换源(加速下载)
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo

# 安装stress
yum -y install stress

# 开始CPU压测:产生2个CPU密集型进程,持续12000秒
stress -c 2 -v --timeout 12000s


# ===== 在docker20容器中执行 =====
docker attach docker20

# 换源
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo

# 安装stress
yum -y install stress

# 开始CPU压测
stress -c 2 -v --timeout 12000s

stress参数说明:

  • -c 2:产生2个进程,每个进程反复调用sqrt()计算随机数的平方根,消耗CPU
  • -v:显示详细运行信息
  • --timeout 12000s:持续运行12000秒(约3.3小时)

步骤4:观察CPU使用率分布
# 在宿主机上打开新终端,使用top观察
top

# 或使用htop(更直观)
htop

预期结果:

  • docker10(权重512)约占33.3%的CPU时间
  • docker20(权重1024)约占66.6%的CPU时间
  • 两者比例为 1:2,与权重比例一致

步骤5:动态修改CPU权重
# 将docker20的权重从1024改为2048
docker update docker20 --cpu-shares 2048

# 重新观察top中的CPU占比变化
# 此时docker10:docker20 = 512:2048 = 1:4
# docker10约占20%,docker20约占80%

四、实验三:内存资源控制

实验目标

验证Docker对容器内存使用的限制机制。

实验步骤

步骤1:启动限制内存的容器
# 启动容器,限制最大使用内存为256MB
docker run -it -m 256m --name docker30 centos:7.7.1908 /bin/bash

参数说明:

  • -m 256m:限制容器最大使用256MB内存

步骤2:查看内存限制配置
# 在容器内查看内存限制(单位:字节)
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
# 输出:268435456(即256 * 1024 * 1024 = 268435456字节)

步骤3:进行内存压力测试
# 在容器内安装stress
yum -y install stress

# 产生2个内存分配进程,持续60000秒
# -m 2:产生2个进程,每个进程不断调用malloc()和free()分配释放内存
stress -m 2 -v --timeout 60000s

预期结果:

  • stress会尝试分配大量内存
  • 当超过256MB限制时,容器会被OOM Killer终止或stress报错
  • 可以通过dmesg查看OOM日志

步骤4:观察内存使用情况
# 在宿主机上使用top观察
top

步骤5:综合限制(CPU + 内存)
# 同时限制CPU核心、CPU权重和内存
docker run -itd --name docker40 \
  --cpuset-cpus 0,1 \
  --cpu-shares 512 \
  -m 1024m \
  centos:7.7.1908 /bin/bash

说明:此容器被限制为:

  • 只能使用CPU0和CPU1
  • CPU权重为512
  • 最大内存使用1024MB

五、实验四:I/O资源控制

实验目标

验证Docker对容器磁盘I/O读写速度的限制。

理论基础

硬盘读写速度是有限的,如果某些容器长时间大量读写数据,会影响其他容器的I/O性能。通过--device-read-bps和--device-write-bps可以限制容器的读写速度。


实验步骤

步骤1:测试默认I/O读写速度
# 启动一个无限制的容器
docker run -it centos:7.7.1908 /bin/bash

# 测试写入速度:写入10MB数据
time dd if=/dev/zero of=file1 bs=1M count=10

# 示例输出:
# 10+0 records in
# 10+0 records out
# 10485760 bytes (10 MB) copied, 0.00921237 s, 1.1 GB/s

说明:

  • if=/dev/zero:输入源为零设备(产生全0数据)
  • of=file1:输出到file1文件
  • bs=1M:每次读写1MB
  • count=10:共读写10次,总计10MB
  • time:测量执行时间
  • 未限制时速度可达1.1 GB/s(取决于磁盘性能)

步骤2:限制I/O写入速度
# 启动容器,限制写入速度为1MB/s
docker run -it --device-write-bps /dev/sda:1m centos:7.7.1908 /bin/bash

# 测试写入速度(使用direct IO,绕过缓存)
time dd if=/dev/zero of=file1 bs=1M count=10 oflag=direct

# 示例输出:
# 10+0 records in
# 10+0 records out
# 10485760 bytes (10 MB) copied, 9.95536 s, 1.1 MB/s

参数说明:

  • --device-write-bps /dev/sda:1m:限制对/dev/sda设备的写入速度为1MB/s
  • oflag=direct:使用直接IO方式,不走系统缓存,直接从内存写入硬盘,能真实反映磁盘限速效果

对比结果:

  • 未限制:1.1 GB/s(约10ms完成)
  • 限制1MB/s:1.1 MB/s(约10秒完成)
  • 速度降低了约1000倍,符合预期

步骤3:使用iotop监控I/O
# 在另一台机器或宿主机上安装iotop
yum -y install iotop

# 运行iotop观察各进程的I/O使用情况
iotop

# 按'o'键只显示有I/O活动的进程

步骤4:使用iostat监控磁盘负载
# 安装sysstat工具包
yum -y install sysstat

# 监控磁盘I/O统计信息
iostat -dmx 2

# 关注 %util 列:表示磁盘负载的使用率
# 接近100%表示磁盘已满负荷运行

iostat输出关键字段:

  • %util:磁盘利用率,接近100%表示磁盘饱和
  • await:平均等待时间(毫秒)
  • svctm:平均服务时间(毫秒)

步骤5:限制读取速度
# 同时限制读取和写入速度
docker run -it \
  --device-read-bps /dev/sda:5m \
  --device-write-bps /dev/sda:1m \
  centos:7.7.1908 /bin/bash

说明:

  • --device-read-bps /dev/sda:5m:限制读取速度为5MB/s
  • --device-write-bps /dev/sda:1m:限制写入速度为1MB/s

步骤6:设置容器自动释放资源
# 启动容器,30秒后自动删除
docker run -itd --rm --name c111 centos:7.7.1908 sleep 30

# 30秒后容器会自动停止并被删除
docker ps -a  # 30秒后查看,c111已不存在

说明:

  • --rm:容器停止后自动删除
  • sleep 30:容器运行30秒后退出
  • 适用于临时测试任务,避免残留容器占用资源

六、实验总结

6.1 四种资源管理策略对比

资源类型关键参数作用典型应用场景
重启策略--restart控制容器异常退出后的行为生产环境服务自愈
CPU控制--cpu-shares --cpuset-cpus限制CPU使用权重和核心绑定多容器公平共享CPU
内存控制-m / --memory限制最大内存使用量防止内存泄漏耗尽宿主机
I/O控制--device-read-bps --device-write-bps限制磁盘读写速度防止某个容器独占磁盘I/O

6.2 关键注意事项

  1. CPU权重只在竞争时生效:单个容器运行时,权重设置无意义
  2. 内存限制要严格测试:设置过小可能导致容器频繁被OOM Kill
  3. I/O限制需使用direct IO验证:绕过缓存才能看到真实限速效果
  4. 重启策略选择要谨慎:生产环境推荐always或unless-stopped
  5. 资源限制可以组合使用:如同时限制CPU、内存和I/O

6.3 常用监控命令

# 实时监控CPU和内存
top
htop

# 实时监控I/O
iotop

# 磁盘I/O统计
iostat -dmx

# 查看容器资源使用情况
docker stats

# 查看容器详细配置
docker inspect <container_name>

更多推荐