资讯动态

Linux进程与日志排查:进程池、线程与进程、日志留存

发布时间:2026/9/18 7:03:51 来源:尧图企业网站定制
1. 进程与日志凭什么总被凑在一张排查表里线上出问题的时候绝大多数人第一反应是敲top看到 CPU 或者内存不对劲再去翻日志。这个动作顺序看着像本能其实背后有一条很清晰的逻辑进程回答的是现在谁在干活、干得怎么样日志回答的是过去这段时间它到底说了什么。前者是快照后者是流水账缺任何一个排查都会变成瞎猜。举个我自己遇到过的场景。某台跑批任务的机器凌晨三点告警说负载突然拉高白天看的时候一切正常。上去top一看当前占用最高的只是系统自己的几个守护进程看不出异常。这时候top已经没用了因为它只反映此刻。真正定位到问题靠的是两样东西一是sar和pidstat留下来的历史采样二是应用日志里那一串在凌晨两点五十八分开始疯狂刷的报错。日志告诉你什么时候开始的、报了什么错进程相关的采样数据告诉你那个时候是谁把 CPU 吃掉了。所以Linux 中查看进程与日志这件事本质不是教你背几个命令而是建立一套观察链路从当前状态出发顺着时间线往回找最后落到具体的那一个 PID 或者那几行日志上。1.1 为什么先看进程再看日志而不是反过来很多人习惯一上来就tail -f日志觉得日志信息最全。这个做法在日志量不大、报错很明确的时候确实快但它有个致命前提你得知道日志在哪、叫什么名字。生产环境里一台机器上跑着 nginx、Java 应用、定时任务、数据库日志散落在/var/log、应用目录、容器内部好几个地方你连找都得找半天。而进程是收敛的。ps -ef一条命令出来所有跑着的东西一目了然哪个 PID 是陌生的、哪个进程的启动时间对不上、哪个进程的父进程是被谁拉起来的信息密度极高。先扫一遍进程相当于先把嫌疑人名单列出来再去日志里找对应时间段的证词效率差好几倍。我个人养成的习惯是三步走先ps拿全局再top/pidstat定嫌疑对象最后带着 PID 和时间点去翻日志。这个顺序在九成以上的故障里都成立。1.2 进程状态背后的三种典型问题进程能反映出来的问题粗分有三类每一类对应的日志翻法完全不同。现象进程层面的表现通常去日志里找什么资源被吃满%CPU或%MEM长期高位load average超过核数慢查询、死循环、大批量任务的开始时间点进程状态卡住状态位出现D不可中断睡眠或Z僵尸磁盘 IO 报错、内核日志、父进程异常退出记录进程反复重启同一个服务每隔几十秒换一次 PID崩溃堆栈、OOM Killer 记录、退出码这张表是我自己在复盘时总结的它的价值在于先给问题归类再决定去哪本日志里翻。比如你发现状态是D那基本不用去看应用日志了直接dmesg -T看内核有没有报磁盘或者 NFS 相关的错方向对了一次就中。提示load average高不等于 CPU 忙。它统计的是运行 等待运行的进程数一个卡在磁盘 IO 上的进程同样会让 load 飙高。这时候top里 CPU 使用率可能很低但 load 是 20很多人会误判成 CPU 问题。2. ps 与 top 之外的进程观察视角ps aux和top是所有人都会的命令但真正能把它们用出花的人不多。问题在于大多数人只用了这两个命令的默认输出而默认输出往往不含你需要的列。2.1 ps 的三种用法对应三种不同的问题第一种是ps auxBSD 风格优点是有一列STAT和START能看到进程状态和启动时间。第二种是ps -efSystem V 风格优点是能清晰看到PPID父进程 ID追查这个进程是谁拉起来的特别好用。第三种是自定义列这才是真正干活用的ps -eo pid,ppid,user,stat,%cpu,%mem,etime,cmd --sort-%cpu | head -20这条命令的每个字段都有用意。%cpu和%mem按 CPU 降序排etime是进程已经运行的时长格式是天-时:分:秒stat是状态。为什么加etime因为它能帮你判断这个进程是不是刚起来的。一个跑了三天的进程突然吃满 CPU和一个刚起来十秒就吃满 CPU 的进程性质完全不同前者可能是逻辑缺陷积累后者可能是在做初始化或者进入了死循环。--sort这个参数经常被忽略。默认ps是按照 PID 排序的PID 大小和资源占用毫无关系所以默认输出里 CPU 最高的进程可能排在中间某一行。要快速找到嫌疑人必须显式排序。2.2 top 里的交互按键才是精华top打开之后很多人就盯着刷新的数字看等着它自己换。其实按几个键信息量立刻不一样。按H可以切换成显示线程这个时候原本一个 Java 进程会展开成几十行每一行是一个线程。找到那个 CPU 特别高的线程 IDTID之后可以拿这个数字去jstack里对应这是定位 Java 死循环的标准动作。按1可以展开每个 CPU 核心的使用情况。如果发现某一个核心长期 100% 而其他核心闲着那大概率是单线程瓶颈跟并行度设置有关不是整体负载问题。按P按 CPU 排序按M按内存排序按T按运行时间排序。这三个切换键我基本每次上去都要按一遍。因为默认排序是 CPU但有些问题恰恰是内存泄漏引起的光看 CPU 是看不出来的。还有一个组合值得记住top -H -p 12345直接盯住某一个 PID 的所有线程CPU 高的线程一眼就能跳出来。这比在全量 top 里翻要快得多尤其是在一台跑了几百个进程的机器上。2.3 /proc 目录所有进程信息的原始出处ps、top、pidstat这些工具数据其实都来自同一个地方/proc。这个目录是内核暴露出来的虚拟文件系统不占磁盘空间进程一退出目录就消失了。进去看一眼就明白ls /proc/12345 cat /proc/12345/status cat /proc/12345/cmdline | tr \0 ls -l /proc/12345/fdstatus里有内存占用、线程数、状态这些细节。fd目录列出的是这个进程打开的所有文件描述符链接指向的实际文件一目了然。这个目录最实用的场景是排查文件被哪个进程占用。比如你想卸载一个挂载点系统提示device is busy很多人第一反应是lsof | grep但如果系统里进程特别多lsof全量扫一遍会非常慢。这时候直接看/proc/*/fd里有没有指向那个路径的链接更快或者用fuser -m /mnt/data它是直接查内核表不走全量遍历。cmdline这一项也值得多说一句。它显示的是进程启动时的完整参数用\0分隔所以直接cat出来是连在一起的得用tr \0 转一下。用它来判断一个进程是不是被正确传参启动比看ps里被截断的COMMAND列靠谱得多——ps aux的输出在某些终端下会被截断误判成参数不对的时候不少。2.4 pgrep、pstree、pidstat 的分工这三个工具各有各的地盘用对了省很多事。pgrep -af keyword按名字或者完整命令行匹配进程返回 PID。比ps aux | grep keyword好在两点一是不会把自己这条 grep 命令也匹配进去这个坑几乎每个新手都踩过二是配合-f能匹配完整命令行适合那些名字很短、容易被误匹配的进程。pstree -p用树形结构展示进程的父子关系。排查谁启动了谁这类问题没有任何命令比它更直观。我曾经排查过一个奇怪的重复进程问题ps里看到两个一模一样的服务但不知道哪个是正主用pstree -p一看就清楚了一个是 init 系统直接拉起来的另一个是某个脚本 fork 出来的后者才是多余的。pidstat是sysstat包里带的专门做按进程的周期采样pidstat -p 12345 1 10意思是对 12345 这个进程每秒采样一次一共采十次。输出里会分别给出用户态 CPU、内核态 CPU、等待 IO 的占比。这个拆分的价值在于如果一个进程的 CPU 大部分花在内核态那问题可能出在系统调用太频繁比如大量小文件读写如果都在用户态那才是应用代码本身在算。方向一分开优化思路完全不同。3. 线程、进程与进程池看懂数量背后的含义热词里同时出现了进程池线程与进程进程通信说明这块概念上的混淆确实普遍。而在排查现场概念搞不清楚会直接导致判断错误。3.1 线程和进程在排查中的实际差别教科书上说进程是资源分配的单位线程是调度的单位。落实到排查上这句话意味着同一个进程下的多个线程共享内存所以一个线程崩了可能整个进程跟着挂而不同进程之间内存隔离一个挂了不影响另一个。这个差别带来的直接后果是日志行为不同。多线程程序里多个线程写同一个日志文件如果没做好同步日志会交错、错乱甚至丢行。你看到日志里两行莫名其妙连在一起很可能不是程序 bug而是两个线程同时写文件造成的。多进程程序反而没这个问题每个进程写自己的文件或者靠日志框架保证追加写入的原子性。所以当你看到日志内容对不上的时候第一步应该判断这个服务是多线程还是多进程模型而不是直接怀疑业务逻辑。3.2 进程池为什么会让 ps 里冒出一堆同名进程进程池的设计初衷是复用避免频繁创建销毁的开销。但它的副作用是ps -ef | grep 服务名会返回一大串看起来一模一样的进程。这里最容易犯的错是把工作进程当成主进程去 kill。比如 nginx 会有一个 master 和若干 worker你 kill 掉一个 workermaster 会立刻再拉起一个新的看起来像是杀不死。真正要停服务得 kill master然后它会给 worker 发信号让它们优雅退出。怎么区分主次看PPID。父进程是 1被 init 系统接管或者父进程是某个管理进程的通常是主进程父进程指向主进程 PID 的是它派生出来的工作进程。用pstree -p 主PID一眼就能看出这棵树的形状。还有一个更隐蔽的情况同一个服务被启动了两遍。表现就是进程池规模翻倍CPU 和内存占用也翻倍但功能看起来正常所以很难被发现。判断方法是看主进程的启动时间和监听端口——如果两个进程都监听了同一个端口那说明用到了SO_REUSEPORT这类多进程共享端口的技术是有意为之如果一个监听失败在重启循环里那就得去日志里找Address already in use这条报错。3.3 僵尸进程和孤儿进程别被名字吓到僵尸进程状态Z也叫defunct不是坏进程它其实是已经执行结束、但父进程还没来得及回收它退出状态的进程。它不占 CPU、不占内存只占一个进程表项。所以看到一两个僵尸进程完全不用慌。真正的问题是僵尸进程数量持续增长那说明父进程没有正确处理SIGCHLD子进程退出后没人回收进程表项会越积越多最终可能耗尽。查法很简单ps -eo stat,pid,ppid,cmd | awk $1 ~ /^Z/找到之后处理方式不是 kill 僵尸进程本身它已经死了kill 不掉而是处理它的父进程——要么修父进程的代码要么把父进程正常重启一次让它重新初始化并回收。孤儿进程则相反父进程先退出了它被 initPID 1接管。这种进程没什么危害只是PPID变成了 1看起来有点特别。D状态不可中断睡眠值得单独提一句因为它最容易被误判。这个状态通常是进程在等磁盘 IO 或者网络文件系统响应此时它不响应任何信号包括 kill -9。你 kill 不掉它是正常的得等 IO 返回或者恢复底层存储。遇到这种情况方向应该转向查存储层dmesg -T里经常能找到线索。4. 日志放在哪从 /var/log 到 journalctl 的完整地图知道进程是谁之后下一步就是找它的日志。Linux 上日志的分布其实有三套体系很多人只熟悉其中一套遇到另外两套就抓瞎。4.1 传统文件日志/var/log 那一堆这是最老派也最直观的一套日志就是普通文件用cat、less、tail就能看。常见的几个文件记录内容什么时候看它/var/log/messages系统级通用消息部分发行版是 syslog系统层面莫名报错/var/log/secure或auth.log登录、认证、提权记录排查异常登录、账号问题/var/log/cron定时任务的执行记录任务没跑、跑了但失败/var/log/dmesg内核环形缓冲区的快照硬件、驱动、OOM 相关注意/var/log/messages这个文件在不同发行版上名字不一样有的是syslog有的是messages有的两者都有但分工不同。这个差异经常让跨发行版操作的人踩坑判断方法是直接ls /var/log/看一眼别凭记忆敲。dmesg有个很实用的小细节默认输出的时间戳是自系统启动以来的秒数看着很痛苦。加-T参数会转成人类可读的日期时间dmesg -T | tail -50 dmesg -T | grep -i -E oom|killed process第二条命令专门抓 OOM Killer 的记录。进程突然消失、而且日志里没有任何主动退出的痕迹时第一件事就应该是查这个。OOM Killer 杀进程不会给应用留遗言只会在内核日志里留一行记录上面写着被杀的进程名、PID 和它的内存评分。4.2 systemd 体系journalctl 的用法逻辑现在的发行版基本都用 systemd日志被统一收进了 journal。它的好处是结构化、带索引、可以按服务、按时间、按优先级过滤比 grep 文件强太多。几个我每天都用的组合journalctl -u nginx.service --since 2024-01-01 08:00 --until 2024-01-01 09:00 journalctl -u myapp -f journalctl -p err -b journalctl -k -b -1第一条按服务加时间范围过滤。第二条-f是实时跟随等价于tail -f但不用你去找文件在哪。第三条-p err只显示 error 及以上级别-b限定本次启动。前两条逻辑很直白第三条才是真正省时间的——日志刷得太快的时候只留错误级别噪音立刻降下来。第四条-k -b -1是看内核日志-b -1表示上一次启动。这个参数的价值在于排查重启原因机器重启过你当前会话看到的是本次启动的日志重启那一刻发生了什么得看-b -1才有。journalctl还能输出 JSONjournalctl -u myapp -o json-pretty -n 1每条日志除了MESSAGE字段还有_PID、_COMM、_SYSTEMD_UNIT这些元数据。用jq解析之后可以做统计分析比如按 PID 分组看哪个进程报错最多。这个玩法在排查重复报错但不知道源头的问题时特别有效。有一个坑必须提醒journal 默认是持久化还是内存态取决于配置。如果/var/log/journal目录不存在日志只存在内存里重启就没了。要长期留存必须手动创建这个目录并重启服务mkdir -p /var/log/journal systemctl restart systemd-journald很多人抱怨重启前的日志怎么都找不到八成就是这个原因。4.3 应用日志文件、标准输出与容器应用日志的形态取决于它怎么部署的。直接跑在宿主机上的服务一般写在自己的目录里比如/opt/app/logs/app.log。这种日志的关键是找到路径路径通常写在配置文件里grep -r log /opt/app/conf/一般能翻出来。用容器跑的日志默认走标准输出由容器运行时接管docker logs -f --tail 100 container_name docker logs --since 30m container_name docker logs --since 2024-01-01T08:00:00 container_name--tail指定从末尾看多少行--since指定时间起点。这两个参数一定要加。不加的话一个跑了三个月的容器docker logs会把几十万行一次性吐出来终端直接卡死。这个坑我踩过一次后来养成了习惯看容器日志永远先--tail再-f跟。还有一个组合值得记docker logs -f container_name 21 | grep --line-buffered -E ERROR|Exception21把标准错误也接过来很多程序的报错走的是 stderr不接过来会漏--line-buffered保证 grep 逐行输出而不是攒一批再吐实时性才有保障。少了这个参数你会觉得日志怎么半天不刷新其实是缓冲区在作怪。如果同一个服务跑了多个副本用docker compose logs -f service_name可以合并看它会自动加上容器名前缀哪个副本报错一目了然。4.4 日志轮转为什么你的日志文件会显示包含100...日志不可能无限增长撑爆磁盘的后果是整个系统出问题。所以几乎所有发行版都自带logrotate按天或按大小切割、压缩、删除旧文件。配置一般在/etc/logrotate.d/下面/opt/app/logs/app.log { daily rotate 180 missingok notifempty compress delaycompress copytruncate }这里每个指令都值得解释一遍。daily是每天切割一次。rotate 180是保留 180 份这正好对应热词里审计日志如何留存 180 天的需求——如果按天切rotate 180就是半年的量。missingok是文件不存在时不要报错很实用避免因为日志文件还没生成就导致轮转任务告警。notifempty是空文件不切。compress是压缩旧日志省空间delaycompress是延迟一次再压缩避免刚切完的日志被压了还在被程序写。copytruncate这个指令要重点说。默认情况下 logrotate 是靠重命名 通知程序重新打开文件来工作的但有些程序不响应这个信号重命名之后它还在往原来的文件句柄里写结果就是新文件是空的写入的内容全跑到被重命名的旧文件里去了。copytruncate的做法是复制一份再清空原文件避开了这个问题代价是复制和清空之间可能丢一点点日志。如果你遇到日志切割之后内容不见了或者切割后新旧文件都对不上八成就是这个参数没配。/var/log目录下经常能看到带数字后缀的文件比如app.log.1、app.log.2.gz就是轮转的产物。.gz的是压缩过的看的时候得zcat或者zless直接cat出来是乱码——这也是一些人以为日志文件损坏了的原因。手动验证轮转配置是否正确不用等第二天logrotate -d /etc/logrotate.d/myapp-d是 debug 模式只打印它打算做什么不会真的执行。想真跑一次就用-f强制轮转。这个技巧在刚配好轮转规则的时候特别有用先 dry-run 一遍看看路径和权限对不对比等到半夜轮转失败再排查强。5. 把进程和日志串起来三条典型排查链路前面讲的是工具和知识这一段讲怎么把它们串成一条能跑通的链路。我挑三个最常见、也最能体现进程 日志配合的场景。5.1 端口或文件被占用从报错到定位报错信息一般是这样的Address already in use、dpkg: 错误: 另外一个进程已经为 dpkg 前端锁 加锁、device is busy。这三条本质上都是同一类问题——某个资源被另一个进程占着。排查链路是这样的ss -lntp | grep :8080 lsof -i :8080 fuser -v 8080/tcpss比netstat快-l是监听状态-n是数字端口-t是 TCP-p是显示进程。这条命令直接告诉你谁在监听这个端口。如果ss的输出里进程信息是空的通常是因为权限不够加sudo再看。如果是文件锁比如 dpkg 的场景那锁文件的位置是固定的/var/lib/dpkg/lock-frontend和/var/lib/dpkg/lock。判断方式很简单——先看有没有另一个包管理进程真的在跑ps aux | grep -E apt|dpkg | grep -v grep如果有等它跑完就好硬删锁文件会导致包数据库损坏。如果没有说明是上次异常退出留下的僵尸锁这时候删掉锁文件再重试是安全的。这个判断顺序很重要。看到锁就去删是很危险的习惯因为真正的并发操作被破坏之后修复成本远高于等几分钟。5.2 CPU 或内存异常的定位过程这是一个完整链路我按实际操作顺序写第一步拿到全局视图找嫌疑人ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort-%cpu | head -10第二步对嫌疑 PID 做持续观察看它是短暂尖峰还是持续高位pidstat -p 12345 1 10第三步如果是多线程服务下钻到线程top -H -p 12345拿到高 CPU 的 TID 之后把它转成十六进制再去jstack的输出里搜索对应的nid。这是 Java 服务定位热点线程的标准做法printf %x\n 12346 jstack 12345 | grep -A 30 nid0x303a第四步如果怀疑是内存问题而不是 CPU看进程的内存明细cat /proc/12345/status | grep -E VmRSS|VmSize|Threads pmap -x 12345 | tail -5VmRSS是实际占用的物理内存VmSize是虚拟内存。这两个数差距特别大的时候可能是 mmap 了很多文件不一定是真泄漏。Threads是线程数线程数持续增长基本可以确定是泄漏。第五步带着确定的时间点去翻日志journalctl -u myservice --since 10 min ago | grep -i -E error|exception|timeout这里最关键的是带着时间点去翻。没有时间范围你会在一堆日志里迷失有了时间范围再配合刚才观察到的现象CPU 尖峰、内存增长日志里的那条关键报错会自己跳出来。我还想强调一点不要一上来就 kill -9。kill -9是 SIGKILL进程没有任何机会做清理正在写的文件可能损坏正在处理的请求会直接断掉。正确顺序是先kill默认 SIGTERM给一次优雅退出的机会等几秒不行再kill -9。这个习惯在很多场景下能避免二次故障。5.3 实时对照一边跟日志一边看进程有些问题需要观察进程和日志的对应关系比如每次请求进来内存涨一点再降回去这种。这时候需要两个窗口配合。窗口一实时跟日志tail -F /opt/app/logs/app.log | grep --line-buffered -E request_id|ERROR窗口二定时采样进程状态while true; do date %H:%M:%S ps -o pid,%cpu,%mem,rss,cmd -p 12345 --no-headers sleep 2 done这样每两秒打印一次进程的资源占用和时间戳对在一起再和日志里的时间戳对照就能建立起某个操作导致资源变化的因果链。tail这里用-F而不是-f区别在于-F会在文件被轮转、被替换之后自动重新打开。日志轮转每天发生一次用-f的话轮转到零点你的跟踪就断了而且不会提示你。这个细节知道的人不多但影响很大。6. 让这套观察能力长期可用留存、采集和几个容易忽略的点排查能力不光靠临场反应还靠平时有没有把数据留住。出事的时候才发现日志只留了三天、或者根本不知道进程历史占用那就只能干瞪眼。6.1 日志留存策略怎么定留存时长取决于用途。运行日志一般保留 7 到 30 天就够因为问题通常几天内就暴露了。审计类的日志需要更久180 天是个常见要求因为很多合规场景要求能回溯半年。实现方式上单机的做法就是前面讲的logrotate把rotate设成对应的天数daily rotate 180 compress这样保留 180 个按天切割的压缩文件刚好半年。但要注意一个隐形成本180 天的日志体积可能远超预期。一个每秒写几百行日志的服务一天就是一个 GB 级别压缩之后可能还有几十 GB180 天下来磁盘直接满。所以策略上要分两档近期比如 7 天不压缩保留原始文件方便查远期压缩存储更早的转存到别的地方或者直接删除。logrotate可以用maxage配合不同的配置实现也可以分成多个配置文件处理不同目录。判断磁盘会不会被撑满可以用这个思路估算单日日志量 × 保留天数 × 压缩比文本日志压缩比通常在 5:1 到 10:1 之间。算完和剩余磁盘空间对比一下心里就有数了。6.2 交互式操作的日志容易被忽略的一环有个细节很多人不注意你在终端里敲的命令、看到输出默认是不留痕的。等到想复盘刚才那个报错到底是什么终端早就滚没了。解决办法是用script命令把会话录下来script -a /var/log/session-$(date %F).log执行之后当前 shell 的所有输入输出都会被记录到那个文件里退出时敲exit或者按CtrlD结束。-a是追加模式同一天的多次操作会累积到同一个文件。远程连接工具普遍也有保存会话日志的功能把滚动缓冲区和日志文件都打开是个好习惯。排查问题时最怕的不是查不到原因而是刚才明明看到过这个报错现在找不着了。6.3 应用层日志里最值得盯的几类系统日志记录的是系统层面发生了什么但业务问题的线索往往在应用自己的日志里。几类特别值得配置好、盯紧的第一类是慢查询日志。数据库的慢查询日志比如 MySQL 的slow_query_log记录执行时间超过阈值的语句这个阈值一般设在 1 秒左右。它的价值在于在上游服务还没报错之前提前发现性能劣化的趋势。CPU 突然高的原因十次里有三次能在慢查询日志里找到对应的时间点。第二类是错误日志。很多框架默认只输出 info 级别真正的问题被淹没了。生产环境把级别调到 warn 或 error信噪比会好很多。第三类是访问日志。它能给出请求量、响应时间、状态码分布。当进程数突然变多或者 CPU 变高时先看访问日志里请求量是不是涨了能一秒排除掉流量突增这个可能性。配置这些日志的时候有个原则日志里必须带时间戳、进程 PID 或者请求 ID。没有请求 ID 的多进程服务日志就是一堆没法串起来的碎片有了它grep request_id就能把一次请求在所有进程、所有环节留下的痕迹串成一条线。这个投入是一次性的但排查效率的提升是长期的。6.4 我在实际使用中的几点体会用这套方法排查了这些年有几个体会比较深。第一命令的组合比单个命令的熟练度更重要。会top的人很多但知道top -H -p加上pidstat加上jstack这条链路的人少得多。真正解决问题靠的是把几个工具串起来而不是把某一个工具的参数背全。第二先分类再动手比直接上手快。看到 CPU 高就往 CPU 方向查看到内存高就往内存方向查这是本能。但前面提到的load高而 CPU 低的情况、D状态进程 kill 不掉的情况都需要先判断类型否则会在错误的方向上浪费大量时间。第三留一手永远比事后补救省事。日志多留几天、会话日志开着、关键指标做个定时采样这些平时看着多余的动作在真正出事的时候价值最大。我现在的习惯是任何一台新上的机器先把日志轮转配好、journal 持久化打开、异常检测脚本挂上配置工作量不到半小时但省下的是后面无数次的抓瞎。第四不要迷信任何一个命令的输出。ps的COMMAND列会截断top的 CPU 百分比是多核情况下的相对值单核跑满在四核机器上显示 100% 而不是 25%这个口径容易误导docker logs默认只给标准输出不给标准错误。这些细节不搞清楚很容易被看起来对的数据带偏。多源交叉验证是排查里最该有的自觉。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价