容器资源限制不是摆设
跑个 Docker 容器,顺手就用默认配置,结果线上服务一跑起来,某个 Java 应用直接吃满主机内存,把别的服务全挤下去了。这种情况在小团队里太常见了。别以为容器天然隔离,不设限就跟裸奔差不多。
其实从启动容器那一刻起,就得考虑 CPU 和内存的分配。Docker、Kubernetes 这些平台都提供了精细的控制手段,关键是怎么用到位。
CPU 控制:别让一个容器抢走全部算力
假设你有台 4 核服务器,跑了三个服务容器:API 网关、日志处理、后台任务。如果不做限制,其中一个计算密集型任务可能占满所有 CPU 时间,导致接口响应变慢。
Docker 提供了 --cpus 参数,可以按比例分配。比如给 API 网关分 1.5 核:
docker run -d --name api-gateway --cpus=1.5 my-api:latest这个值其实是 CPU 时间的权重比例,并非硬性隔离。系统空闲时,容器仍可短暂 burst 超过限额,但长期占用会被调度器压制。
更细粒度的控制可以用 --cpu-shares,它设定的是相对权重。比如:
docker run -d --name logger --cpu-shares 512 my-logger而主服务设为 1024,那么在 CPU 竞争时,后者能拿到两倍于前者的执行时间。
内存限制:防止“内存杀手”出现
最怕哪种容器?那种默默增长内存使用,直到触发 OOM(Out of Memory)被系统杀掉的。尤其是 Java 应用,JVM 堆没设好,容器内存超限是常事。
用 -m 或 --memory 可以硬性限制容器最大可用内存。例如限制为 512MB:
docker run -d --name worker -m 512m my-worker:latest这时候即使应用想申请更多内存,也会被 cgroup 拦下,不会影响宿主机和其他容器。
配合使用 --memory-swap 可以控制是否允许使用交换空间。设成和 memory 一样大小,就等于禁用 swap:
docker run -d --name worker -m 512m --memory-swap 512m my-worker对延迟敏感的服务,建议禁用 swap,避免因换页导致响应抖动。
Kubernetes 中的资源配置
在 K8s 里,这些控制通过 Pod 的 resources 字段实现。比如:
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: my-frontend
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"这里 requests 是调度依据,limits 是运行上限。Kubelet 会根据 limits 设置 cgroup 规则。
特别注意:Java 应用在容器里跑时,JVM 不一定能感知到容器内存限制。建议加上 -XX:+UseContainerSupport 参数,或者显式设置 -Xmx,比如 -Xmx400m,留点空间给堆外内存。
监控与调整不能少
设了限不代表万事大吉。得用 Prometheus + Grafana 搭套监控,看各个容器的实际 CPU 和内存走势。经常发现某些服务申请了 1G 内存,实际只用 200M,白白浪费调度资源。
反过来,也有设得太紧的,频繁触发接近 limit 的告警。这时候就得结合日志和性能数据,逐步调优。
资源限制不是一次性的配置,而是随着业务变化持续调整的过程。上线前压测,上线后观察,才能找到最合适的配额。