尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

容器资源限制与监控:从 --memory 到 Prometheus

发布时间:2026/9/28 18:06:54

资讯中心
01
ARTICLE

容器资源限制与监控:从 --memory 到 Prometheus

容器资源限制与监控:从 --memory 到 Prometheus
1. 引言容器化部署早已成为后端开发的标配但很多同学对容器的资源限制仍停留在「启动时加几个参数」的层面。--memory、--cpus这些参数到底限制了什么容器被 OOM 杀掉时系统里发生了什么为什么 JVM 在容器里经常「不听话」监控面板上的指标又是从哪里来的这篇文章会从 Docker 的资源限制参数讲起完整复现一次「容器 OOM」事故带你走一遍从现象到根因的排查路径最后用 cAdvisor Prometheus Grafana 搭一套容器监控面板。全文约 4500 字建议配合命令行实际操作。2. 前置依赖阅读本文前建议先掌握以下内容对应本系列前两篇Docker 基础操作docker run、docker ps、docker exec、docker logsLinux 基础命令top、free、dmesg、cat /proc/...实验环境建议Linux 内核 5.10完整支持 cgroup v2Docker Engine 20.10至少 2 核 CPU、4 GB 内存的机器或虚拟机提示如果你用的是 macOS 或 Windows 的 Docker Desktop底层是虚拟机cgroup 版本取决于虚拟机的内核配置实验现象可能与 Linux 直装略有差异。3. 资源限制参数详解3.1 --memory内存上限--memory简写-m限制容器最多能使用的物理内存单位支持b、k、m、g。dockerrun-d--namedemo-mem--memory512m nginx这个限制是硬限制当容器内进程的内存使用超过 512 MB 时内核会触发 OOM 机制优先杀掉容器内占用内存最多的进程。3.2 --memory-swap交换分区上限--memory-swap限制容器内存 swap 的总和。它必须与--memory一起使用且值必须大于等于--memory。# 内存 512mswap 上限 256m总上限 768mdockerrun-d--namedemo-swap--memory512m --memory-swap 768m nginx# 内存 512mswap 无上限不推荐dockerrun-d--namedemo-swap2--memory512m --memory-swap-1nginx坑位提醒很多人以为--memory-swap是「swap 的上限」其实它是「内存 swap 的总上限」。如果只设--memory不设--memory-swapDocker 默认--memory-swap等于--memory的两倍即容器默认可以用 swap只是总量被限制。3.3 --cpusCPU 配额--cpus限制容器可以使用的 CPU 核数支持小数# 限制最多使用 1.5 个 CPU 核dockerrun-d--namedemo-cpu--cpus1.5nginx它底层是 cgroup 的cpu.maxcgroup v2或cpu.cfs_quota_us / cpu.cfs_period_uscgroup v1。注意--cpus是软限制容器内进程可以短暂超过配额但会被内核调度器持续压制。3.4 --pids-limit进程数上限--pids-limit限制容器内同时存在的进程/线程总数# 限制容器内最多 100 个进程dockerrun-d--namedemo-pids --pids-limit100nginx坑位提醒这个参数很容易误伤。比如 Java 应用每创建一个线程就是一个「进程」线程池开大了、或者用了 ForkJoinPool很容易撞上--pids-limit导致线程创建失败报Unable to create native thread。4. 完整复现一次容器 OOM4.1 准备一个「内存黑洞」镜像先写一个会持续吃内存的小程序。用 Python 最方便# oom_demo.pyimporttime data[]whileTrue:# 每次分配 10 MBdata.append(bx*10*1024*1024)print(fallocated{len(data)*10}MB,flushTrue)time.sleep(0.1)写一个 DockerfileFROM python:3.11-slim WORKDIR /app COPY oom_demo.py . CMD [python, oom_demo.py]构建镜像dockerbuild-toom-demo.4.2 启动容器并观察现象# 限制内存 128m不给 swapdockerrun-d--nameoom-test--memory128m --memory-swap 128m oom-demo观察容器状态dockerps-a你会看到容器状态变成Exited (137)。137 128 9即被 SIGKILL信号 9杀死。这是 OOM 杀进程的典型退出码。4.3 查看 OOM 日志dockerlogs oom-test日志会停在某个分配量附近比如allocated 120 MB allocated 130 MB然后戛然而止——进程被内核直接杀掉来不及打印任何错误。4.4 用 dmesg 定位根因OOM 的真正原因在内核日志里dmesg|grep-ioom你会看到类似这样的输出oom-kill:constraintCONSTRAINT_MEMCG,nodemask(null),cpusetdocker-xxx,mems_allowed0-1,oom_memcg/docker/xxx,task_memcg/docker/xxx,taskpython,pid1234,uid0 Memory cgroup out of memory: Killed process 1234 (python) total-vm:...kB, anon-rss:...kB, file-rss:...kB, shmem-rss:...kB, UID:0 pgtables:...kB oom_score_adj:1000关键信息解读oom_memcg/docker/xxx被 OOM 的 cgroup 是哪个容器taskpython被杀的进程oom_score_adj:1000该进程的 OOM 优先级Docker 默认给容器内进程加 1000优先被杀4.5 从现象到根因的完整路径步骤现象排查手段结论1容器退出状态Exited (137)docker ps -a进程被 SIGKILL2日志戛然而止docker logs进程被强杀无业务报错3内核记录 OOM 事件dmesg | grep -i oom内存 cgroup 超限4确认限制参数docker inspect oom-test--memory 128m触发5. docker stats 与 docker events5.1 docker stats实时资源占用dockerstats输出示例CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS a1b2c3d4e5f6 web 0.50% 256MiB / 512MiB 50.00% 1.2kB / 0B 0B / 0B 12docker stats的数据来源是 cgroup 的统计文件适合临时排查不适合长期监控。5.2 docker events事件流dockerevents--filtercontaineroom-test当容器被 OOM 杀掉时你会收到2026-09-27T08:30:00.000000000Z container die oom-test (imageoom-demo, ...)docker events能帮你捕捉容器的生命周期事件创建、启动、停止、死亡是排查「容器什么时候挂的」的好帮手。6. cgroup v2 下的差异6.1 如何确认当前 cgroup 版本stat-fc%T /sys/fs/cgroup/输出cgroup2fs→ cgroup v2输出tmpfs→ cgroup v16.2 主要差异维度cgroup v1cgroup v2内存限制文件memory.limit_in_bytesmemory.maxCPU 限制文件cpu.cfs_quota_uscpu.cfs_period_uscpu.max进程数限制pids.max独立控制器pids.max统一控制器内存压力通知需手动配置内置memory.eventsOOM 事件分散在各控制器统一在memory.events的oom字段cgroup v2 下查看容器内存限制cat/sys/fs/cgroup/memory.max查看 OOM 事件计数cat/sys/fs/cgroup/memory.events输出oom 3 oom_kill 2这比 v1 时代方便得多——oom表示触发过几次 OOM 判定oom_kill表示实际杀过几次进程。7. JVM 不感知 cgroup 的坑7.1 问题现象在容器里跑 Java 应用明明限制了--memory 512mJVM 却按宿主机内存比如 16 GB来算默认堆大小结果JVM 启动时申请了远超容器限制的堆内存容器被 OOM 杀掉JVM 连 GC 的机会都没有7.2 原因老版本 JDK8u131 之前不感知 cgroup-XX:UseContainerSupport默认关闭。JVM 通过/proc/meminfo读取的是宿主机的内存而不是容器 cgroup 的限制。7.3 解决方案方案一升级 JDK 到 8u191 或 JDK 10默认开启容器感知。方案二显式指定堆大小dockerrun-d--memory512m\-eJAVA_OPTS-Xmx256m -XX:MaxMetaspaceSize128m\my-java-app方案三使用-XX:MaxRAMPercentage按比例分配dockerrun-d--memory512m\-eJAVA_OPTS-XX:MaxRAMPercentage50.0\my-java-app坑位总结老版本 JDK 在容器里默认堆大小 宿主机内存的 1/4极易触发 OOM。升级 JDK 或显式设置堆大小是必做项。8. cAdvisor Prometheus Grafana 搭建监控面板8.1 架构总览Docker 容器cAdvisorPrometheusGrafana监控面板cAdvisor采集容器资源指标CPU、内存、网络、磁盘Prometheus时序数据库负责存储和查询指标Grafana可视化面板8.2 启动 cAdvisordockerrun-d\--namecadvisor\--restartalways\-p8080:8080\-v/:/rootfs:ro\-v/var/run:/var/run:ro\-v/sys:/sys:ro\-v/var/lib/docker/:/var/lib/docker:ro\-v/dev/disk/:/dev/disk:ro\--privileged\--device/dev/kmsg\gcr.io/cadvisor/cadvisor:latest验证curlhttp://localhost:8080/metrics|head-208.3 配置 Prometheus写一个prometheus.ymlglobal:scrape_interval:15sscrape_configs:-job_name:cadvisorstatic_configs:-targets:[cadvisor:8080]启动 Prometheusdockerrun-d\--nameprometheus\--restartalways\-p9090:9090\-v$(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml\prom/prometheus:latest验证curlhttp://localhost:9090/api/v1/status/targets8.4 启动 Grafanadockerrun-d\--namegrafana\--restartalways\-p3000:3000\grafana/grafana:latest访问http://localhost:3000默认账号密码admin/admin。8.5 配置数据源与面板在 Grafana 中Configuration → Data Sources → Add data source选择 PrometheusURL 填http://prometheus:9090如果 Grafana 和 Prometheus 在同一 Docker 网络导入官方面板 ID893cAdvisor 容器监控面板选择数据源后即可看到容器 CPU、内存、网络、磁盘的实时图表8.6 常用 PromQL 查询容器内存使用率100 * (1 - (container_memory_working_set_bytes{name} / container_spec_memory_limit_bytes{name}))容器 CPU 使用率rate(container_cpu_usage_seconds_total{name}[5m]) * 100容器 OOM 事件计数container_oom_events_total{name}9. 坑位总结9.1 swap 上限 vs 内存上限--memory-swap是「内存 swap 的总上限」不是 swap 单独的上限。只设--memory时Docker 默认给容器分配等量的 swap 额度可能导致容器在内存超限后先吃 swap 再被杀延迟了 OOM 的触发掩盖了真实的内存问题。9.2 JVM 不感知 cgroup老版本 JDK 按宿主机内存计算默认堆大小容器限制形同虚设。升级 JDK 或显式设置-Xmx/-XX:MaxRAMPercentage。9.3 --pids-limit 误伤--pids-limit限制的是容器内所有线程/进程的总数。Java 应用线程池、Python 多进程模型都容易撞上限。设置前先评估应用的线程模型别拍脑袋定一个很小的值。10. 总结本文从 Docker 资源限制参数讲起完整复现了一次容器 OOM 事故并给出了从docker ps到dmesg的排查路径。随后对比了 cgroup v1/v2 的差异剖析了 JVM 不感知 cgroup 的经典坑最后用 cAdvisor Prometheus Grafana 搭建了一套容器监控面板。核心要点回顾--memory是硬限制超限即 OOM--memory-swap是内存 swap 的总上限--cpus是软限制会被调度器持续压制--pids-limit限制进程/线程总数容易误伤容器退出码 137 SIGKILL配合dmesg定位 OOM 根因cgroup v2 用memory.events统一记录 OOM 事件老版本 JDK 不感知 cgroup必须显式设置堆大小cAdvisor Prometheus Grafana 是容器监控的标准组合下一篇文章可以聊聊 Kubernetes 的 requests/limits 与 QoS 等级看看容器资源管理在编排层面的进阶玩法。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。