小内存 ARM64 主机上自建服务的五类高频故障与排查顺序
为什么要写这篇
我长期在一台 ARM64 的小机器上跑一些自建服务:反向代理、对象存储、监控面板、定时任务。机器内存只有 7.4G,swap 早就吃掉了三四个 G。这类环境里翻车往往不是代码写错,而是资源某天突然不够、某个服务默默换了个端口、某个容器重启后 DNS 失效。这篇把最近半年反复遇到的几类问题整理成可复用的排查顺序,都是可以直接敲命令验证的。
一、先看内存,再看 CPU
大多数「服务突然变慢」的现场,第一反应是看 CPU,但实际上容器化之后更常见的是内存压力导致的连锁反应:某个进程被 OOM killer 干掉,systemd 或容器运行时把它重启,重启期间上游全部 502。判据不在负载,而在回收行为:
- free -h 里 available 是否已经接近 0,swap 是否持续被吃;
- dmesg -T | grep -i oom 是否出现被杀记录;
- systemctl status 里某个 service 的 Restart 计数是否在涨。
三条里任何一条成立,就应该先把占用最大的容器停掉,再看业务是否恢复,而不是先加机器。加机器只是把问题推迟一周。
二、容器 host 网络下的 DNS 是独立故障点
容器用 host 网络模式时很容易忽略一件事:容器内的 /etc/resolv.conf 由运行时注入,如果注入失败,文件可能是空的,只剩一行 ::1。这时所有对外请求都会表现为超时或 503,但容器内进程自己完全不知道自己解析不了域名。
判据只有一条:进容器 cat /etc/resolv.conf,看不到有效的 nameserver 就是这个病。修法不是改业务,而是重建容器时显式传 DNS:
docker run --dns 223.5.5.5 --dns 119.29.29.29 ...
验证要分层:先在容器内 curl 一个域名,再在宿主上 curl 容器端口。两层都通过,才排除「网络不通」这一类。
三、端口被占用的排查顺序
应用启动失败报「address already in use」时,先确认监听的是哪个地址,而不是直接杀掉占用的进程,因为占用的可能正是你要保留的那个服务。
- ss -tlnp 过滤目标端口,看 PID 与绑定的地址是 0.0.0.0 还是 127.0.0.1;
- 绑在回环上的服务,从别的机器当然连不上,这属于配置问题不是端口冲突;
- 确认进程身份后再决定换端口;换端口后一定要同步改反向代理的 upstream,否则前端仍然 502。
四、反向代理后面加一层,日志要分开看
典型结构是外层反代做 TLS 和域名分流,内层应用直接跑在容器里。出问题时最容易被误导的是:外层日志显示 502,于是去看应用日志,发现应用根本没收到请求。
这时候要看的是外层反代到上游那一段:upstream 地址对不对、容器是否在监听、容器网络与宿主的可达性。一个快速判据是在宿主上直接 curl 上游地址,如果宿主能通而反代不能通,问题就在反代的网络命名空间或 upstream 配置,与应用无关。
五、定时任务的三个必备件
自建定时任务最容易埋的坑是「静默失败」:任务跑了,但什么都没做,也没人知道。我现在要求每个定时任务至少有三样东西:
- 一个锁,防止上一轮没跑完下一轮又起来;
- 一份结构化结果文件,记录每个步骤的状态与失败原因;
- 一个投递通道(邮件或 webhook),失败时必须有人能看到。
三样里最容易被省掉的是第二样,而没有它,失败就只能写成一句「今天没成功」,回头查什么都要重新跑一遍。把每一步的原始证据落盘,比写一堆重试逻辑有用得多。
六、小结
小机器上的稳定性,大部分不取决于装了多少个监控面板,而取决于三件事:能不能快速判断是内存还是网络、能不能分辨容器内和宿主两层、失败时有没有留下可核对的证据。把这三件事做到,问题依然会出现,但每次都能在几分钟内定位到具体环节。
- 点赞
- 收藏
- 关注作者
评论(0)