Docker 运维入门实战:给容器加上限,别让一个服务拖垮整台机器

举报
yd_274623194 发表于 2026/10/31 15:12:01 2026/10/31
【摘要】 关键词:资源限制、内存上限、CPU 上限、重启策略、OOM、docker stats上一篇我们讲了不停机更新:一次只动一个服务,等健康了再继续,出了问题能回滚。今天把这条线的最后一块拼图补上——更新流程再稳,也挡不住一个服务自己失控:内存越吃越多、CPU 占满,最后整台机器都跟着卡,甚至把别的服务也拖死。解决办法不是等它出事再重启,而是提前给每个容器划好边界:能用多少资源、死了之后怎么办,都...

关键词:资源限制、内存上限、CPU 上限、重启策略、OOM、docker stats

上一篇我们讲了不停机更新:一次只动一个服务,等健康了再继续,出了问题能回滚。今天把这条线的最后一块拼图补上——更新流程再稳,也挡不住一个服务自己失控:内存越吃越多、CPU 占满,最后整台机器都跟着卡,甚至把别的服务也拖死。解决办法不是等它出事再重启,而是提前给每个容器划好边界:能用多少资源、死了之后怎么办,都在配置里写清楚。

本文还是接着前面的结构讲:gateway(Nginx 网关)、web(静态网页)、api(8000 端口的 Python 接口服务),并且假设健康检查已经配好——资源限制和健康检查是搭档:限制负责"别越界",健康检查负责"越界或出问题时能被发现"。

一、先看清现状:谁在吃资源

加限制之前,先看一眼现在的资源占用,别凭感觉拍数字:

docker stats --no-stream

这张表里最值得看的两列是 MEM USAGE / LIMIT 和 CPU %。MEM USAGE 是当前用了多少,LIMIT 是上限——没加限制时,这个上限就是整台主机的内存,也就是"理论上能吃光整台机器"。如果某个服务平时只用 100MB 左右,某天却涨到几个 GB,那它就是第一个需要加限制的候选。

平时也可以只看某一个服务,不用每次都刷全表:

docker stats --no-stream api

二、单机上怎么写限制:别被 deploy 误导

这里有一个新手最容易踩的坑,需要先说清楚。Compose 文档里常见的写法是:

services:
  api:
    deploy:
      resources:
        limits:
          cpus: "0.50"
          memory: 512M

但这套 deploy.resources 只在 Swarm 集群模式下生效,单机 docker compose up 会直接忽略它——写了等于没写。单机上真正生效的是下面这套写法:

services:
  api:
    mem_limit: 512m
    cpus: 0.5
    restart: unless-stopped
  web:
    mem_limit: 256m
    cpus: 0.5
    restart: unless-stopped
  gateway:
    mem_limit: 256m
    cpus: 0.5
    restart: unless-stopped

mem_limit 是内存上限,cpus 是 CPU 上限(0.5 表示最多用半个核)。数值怎么定?看 docker stats 里平时的用量,再留一倍左右的余量:平时 100MB 左右的服务,给 256MB;平时 200MB 左右的,给 512MB。限制不是越小越好——设得比正常用量还低,服务会频繁被杀,反而更不稳定。

改完配置后,同样一次只动一个服务,沿用上一篇的更新办法:

docker compose up -d --no-deps api
docker compose ps api

三、内存超了会怎样:OOM 不是报错,是直接被杀

内存限制到了之后,Linux 内核的 OOM Killer 会直接杀掉超限的进程,容器随之退出。现场看起来常常是"服务突然没了,日志里最后一条还很正常"。要确认是不是被 OOM 杀掉的,可以看容器的退出状态和系统日志:

docker inspect api --format '{{.State.OomKilled}} {{.State.ExitCode}}'
docker compose logs --tail 50 api

OomKilled 是 true,就说明是内存超限被杀,而不是代码自己退出的。这时别急着把限制调大,先想想内存涨上去的原因:是正常的流量高峰,还是某个接口在泄漏?前者调大限制合理,后者调大只是把"拖垮机器"的时间往后推了一点。

CPU 超了则温和得多:cpus 只是节流,不会杀进程,服务只是变慢。所以 CPU 限制可以设得相对宽松一点,内存限制才是保命的那一道线。

四、死了之后怎么办:重启策略选哪一个

限制能防止一个服务拖垮整机,但被杀掉的服务还得有人把它拉起来,这就是重启策略的事。四个选项,区别只在"什么时候重启":

策略 含义 适合谁
no 从不自动重启 调试时临时用
on-failure 非正常退出才重启 想区分"自己正常退出"和"崩了"时
always 只要停了就重启,连主机重启后也拉起来 希望服务永远在线时
unless-stopped 和 always 一样,但你手动停掉的就不拉起来 日常部署的首选

对 my-app 这三个服务,unless-stopped 是最省心的默认选择:机器重启后服务自己回来,平时你手动 docker compose stop api 做维护时,它也不会自作主张又爬起来。always 只有在"哪怕我手动停了,下次开机也要回来"这种场景才需要,大多数个人项目用不上。

重启策略和健康检查配合起来看才完整:重启只能保证"进程在跑",健康检查才能告诉你"服务真的能用"。上一篇讲过的验收三条——健康状态 (healthy)、日志无报错、从网关侧真实请求一次——加了限制之后同样适用,缺一不可。

五、一个容易忽略的点:限制是给单个容器的,不是整站的

mem_limit: 512m 是每个容器各自的上限,不是三个服务加起来 512MB。如果 api、web、gateway 各给 512MB,理论上三个服务一共能吃到 1.5GB。定数字时要把所有服务的上限加起来,和主机的总内存对照一下:上限之和最好留出余量,别让"每个都合法"加起来把机器吃满。网关和静态网页平时用量很小,给 256MB 绰绰有余;真正需要放宽的通常只有跑业务逻辑的 api。

结语

从单容器、Compose 编排、数据卷、反向代理和 HTTPS、日志与健康检查、滚动更新,到今天的资源限制和重启策略,这条 Docker 运维的线已经走完了:服务能对外提供、出了问题能定位、改了代码能安全更新、单个服务失控也不会拖垮整台机器。下一篇可以换一个方向:把这套东西从"手动敲命令"变成"改动自动部署",也就是用 GitHub Actions 或 GitCode 流水线做一次最简单的 CI/CD——改了代码、推上去,服务器自己更新。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。