限制 CPU 和内存

限制 CPU 和内存

面试题

Docker 如何限制容器的 CPU 和内存使用?参数怎么配置?底层原理是什么?

标准答案

Docker 通过 Linux 内核的 cgroups(Control Groups)机制实现资源限制。合理设置资源限制可以避免某个容器耗尽宿主机资源,是生产环境部署的基本要求。

内存限制

基本参数

# 硬限制:最大内存 512MB
docker run -d --memory=512m --name app myapp

# 软限制:尽量不超过 256MB(宿主机内存不足时才严格限制)
docker run -d --memory=512m --memory-reservation=256m myapp

# 限制内存 + Swap 总量
docker run -d --memory=512m --memory-swap=1g myapp
# memory-swap = memory + swap 之和
# 不设置时默认 memory-swap = memory * 2
# 设置 -1 表示 swap 不限

# 禁用 Swap(完全不使用交换分区)
docker run -d --memory=512m --memory-swap=512m myapp

# 限制内存常驻区域(RSS)
docker run -d --memory=512m --kernel-memory=128m myapp

关键参数说明

参数 作用 默认值
-m / --memory 内存硬上限 无限制
--memory-reservation 内存软限制 同硬限制
--memory-swap 内存+Swap 总量 等于 2×memory
--memory-swappiness Swap 倾向(0-100) 继承宿主机
--kernel-memory 内核内存上限 无限制
--oom-kill-disable 禁止 OOM Kill 不禁止

OOM(Out Of Memory)行为

# 默认情况下,内存超限会被 OOM Killer 杀掉
docker run -m 100m alpine python -c "x = ' ' * 1024**3"
# 容器会被 OOM Kill,退出码 137

# 禁止 OOM Kill(不推荐,可能导致宿主机 OOM)
docker run -m 100m --oom-kill-disable alpine stress --vm 1 --vm-bytes 200M

# OOM 优先级调整
docker run -m 100m --oom-score-adj=-500 myapp
# oom_score_adj 范围 -1000 到 1000
# 值越低,越不容易被 OOM Kill

CPU 限制

基本参数

# 限制 CPU 核数
docker run -d --cpus=1.5 myapp           # 使用 1.5 个 CPU 核心
docker run -d --cpus="2.0" myapp         # 使用 2 个 CPU 核心

# 绑定到特定 CPU 核心
docker run -d --cpuset-cpus=0,1 myapp    # 只能在 CPU 0 和 1 上运行
docker run -d --cpuset-cpus=0-3 myapp    # CPU 0-3

# CPU 份额(相对权重)
docker run -d --cpu-shares=1024 service-a  # 默认权重
docker run -d --cpu-shares=512 service-b   # 低优先级
# CPU 充足时权重无效;CPU 争抢时按权重比例分配
# service-a : service-b = 1024 : 512 = 2:1

CFS(Completely Fair Scheduler)参数

# CFS 周期和配额(底层参数,一般用 --cpus 即可)
# --cpus = --cpu-quota / --cpu-period

# 每 100ms 周期中最多使用 50ms CPU
docker run -d --cpu-period=100000 --cpu-quota=50000 myapp
# 相当于限制为 0.5 核

参数对照表

参数 作用 等价 CPU 限制
--cpus=2 使用 2 个核 CPU 时间 = 2核 × 100%
--cpus=0.5 使用半个核 CPU 时间 = 0.5核 × 100%
--cpuset-cpus=0 绑定到核 0 单核专用
--cpu-shares=2048 其他容器 1024 竞争时多获得 2 倍

组合限制示例

# 生产环境 Web 服务
docker run -d \
  --name web \
  --memory=512m \
  --memory-reservation=256m \
  --memory-swap=768m \
  --cpus=1.5 \
  --cpuset-cpus=0,1 \
  --restart always \
  nginx:alpine

# 批量任务 Worker(放宽 CPU,限制内存)
docker run -d \
  --name worker \
  --memory=2g \
  --memory-swap=2g \
  --cpus=4 \
  my-worker:latest

# 低优先级批处理
docker run -d \
  --name batch-job \
  --memory=1g \
  --cpu-shares=256 \
  --cpuset-cpus=2,3 \
  batch-processor:latest

底层原理:cgroups

# 查看容器的 cgroup 配置
# 在宿主机上
ls -l /sys/fs/cgroup/memory/docker/  # 容器 ID 对应的目录
cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes

# 查看容器的 CPU cgroup
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_period_us

最佳实践

1. 始终设置内存上限

# 不设限制的容器可能撑爆宿主机
# 跑内存密集任务时尤其重要

2. memory-reservation 设置软限制

# 正常情况下容器可使用多于软限制的内存
# 仅在宿主机内存紧张时才回收
# 防止单个容器导致整体 OOM

3. 合理设置 CPU 份额而非核数

# 对于非关键服务,用低 cpu-shares 而非固定 --cpus
# 充分利用空闲 CPU 资源,竞争时自动降级

4. 不要轻易禁用 OOM Kill

# oom-kill-disable 可能导致宿主机 OOM
# 只在极特殊场景使用(如数据库)

常见问题

Q:内存限制对 Java 应用为什么需要特殊处理?

# Java 感知不到 Docker 的 cgroup 内存限制
# 需要显式设置 JVM 堆大小
docker run -m 512m -e JAVA_OPTS="-Xmx256m -Xms128m" myapp
# Java 10+ 已支持容器感知(UseContainerSupport)

Q:–cpus=1 和 –cpuset-cpus=0 的区别是什么?

--cpus=1 限制 CPU 时间总量,但调度可以跨任意核;--cpuset-cpus=0 绑定到特定核,独占该核的时间片。

Q:如何验证限制是否生效?

# 启动受限容器
docker run -d --memory=128m --name test alpine sleep 3600

# 查看限制配置
docker inspect test | jq '.[0].HostConfig.Memory'

# 压力测试
docker exec test stress --vm 1 --vm-bytes 256M
# 容器会因 OOM 被杀掉(退出码 137)

总结

核心配置思路:内存必设上限(-m)+ 合理设置 CPU 限制(--cpus--cpu-shares)。理解 cgroups 是底层机制即可,不必深究内核细节。生产环境建议所有容器都设置资源限制,避免”坏邻居”问题。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容