
如果你也在用 Intel N100 这类小主机跑一堆常驻服务并且被某个“EOS 日志”折腾到半夜那这篇文章应该能帮到你。我最近刚好完成了一次 N100 真机上的日志排查把从系统日志、容器日志到采集链路、检索分析的过程完整走了一遍。标题写的是“[EOS 日志] 1 N100 真机排查”其实就是我自己的系列记录第一篇下面这些内容都是这次实际排查里沉淀下来的适合正在用类似低功耗小主机跑 Docker、自己做日志采集和排障的人参考。1. 背景交代一台小主机和一个代号 EOS 的服务1.1 N100 这台机器的定位和脾气N100 是 Intel 推出的低功耗处理器4 核 4 线程TDP 只有 6W 到 15W 左右放在迷你主机里非常省电。这两年很多软路由、NAS、家庭服务器都用了这颗芯片市面上各种 N100 准系统也特别多。我手上这台是 16GB 内存、512GB NVMe 的版本装了 Debian 12日常用 Docker 跑服务包括内网穿透、下载工具、监控面板还有今天要说的这个 EOS。N100 的性能怎么说呢日常跑十几个容器没问题但前提是别让任何容器长期跑满 CPU。这颗 U 的单核性能放在现在也只能算中等偏下一旦某个服务的日志量突然暴涨磁盘 IO 和 CPU 占用会立刻被打满整台机器都会卡。很多人在虚拟机里排查问题都挺顺利一放到真机上就复现不了其实是因为真机上的资源竞争、散热、功耗限制都是虚拟机里模拟不出来的。这也是我坚持在真机上做排查的原因。1.2 EOS 是什么日志为什么难搞先说明一下我这里的 EOS 是内部项目代号一个常驻的后台服务具体业务不重要重要的是它产生的日志特别有代表性。EOS 进程同时往三个地方写日志标准输出、磁盘文件、还有一部分内部模块的独立日志文件。日志格式也不统一有的行带时间戳有的行是一段多行堆栈有的模块用 JSON 输出有的模块就是纯文本。这种“混合输出”的日志恰恰是日常运维里最常见的。日志这个东西平时没人看出事的时候所有人都在看。但问题在于如果平时没把采集和检索链路搭好真到排查的时候你只能一台机器接一台机器地 ssh 上去tail、grep、awk 轮着来。EOS 的日志分布在好几个目录、好几个容器里手工翻非常痛苦。我这次做“真机排查”第一步就是把日志链路理清楚然后再谈怎么定位问题。1.3 为什么要写“真机排查”这个系列我做记录的习惯是把一次完整的排查过程拆成编号系列这次是第 1 篇重点就是“从零到一”把日志采集、检索、分析这条路走通。后面几篇可能会分别写具体的问题定位、告警配置、容量规划这些。之所以强调“真机”是因为很多坑只有真机上才会遇到比如磁盘 IO 竞争、容器日志被滚动删除、OOM Killer 杀进程、时区错乱等。如果你也准备在小主机上长期跑服务这篇文章里关于日志采集和系统排障的部分可以直接拿去用。2. 开始之前日志采集链路的选型思路2.1 先想清楚日志到底要采到哪里去手动 ssh 上去看日志只适合临时应急长期来看必须把日志集中到一个地方。集中式日志方案无非几个选择ELKElasticsearch Logstash Kibana、EFKElasticsearch Filebeat Kibana、Loki Grafana或者更轻量的 Vector ClickHouse 之类的组合。选型的核心取决于两个问题日志量有多大机器内存有多少。ELK 全家桶功能最强但 Elasticsearch 对内存的要求很现实N100 只有 16GB 内存还要跑其他 Docker 容器给 ES 分 2GB 都嫌多。而且 ES 的索引一旦膨胀磁盘和内存都受不了。我最后选了 Loki Grafana Promtail 这套组合。Loki 的设计思路和 ES 完全不同它只索引日志的标签label不索引日志内容所以内存占用远低于 ES。加上 Grafana 我本来就在用Promtail 也足够轻量正好匹配 N100 这种小主机的资源环境。当然Filebeat 依然是很多场景下靠谱的选择它和 Elasticsearch 配合更紧密生态也更成熟。如果你的服务器内存充足、日志量又大需要全文检索和复杂的聚合分析那 ELK 依然是首选。但如果你和我一样用低功耗小主机、跑十几个容器只想快速检索和可视化我建议认真考虑 Loki。2.2 几个采集器之间的对比我简单整理了一个表可以直观看看当前主流的日志采集器差别采集器语言/资源占用多行合并适用场景缺点FilebeatGo / 较低支持multiline轻量采集搭配 ES 使用复杂处理能力弱依赖 ES 或其输出端PromtailGo / 较低支持multiline搭配 Loki和 Grafana 深度集成只有 Loki 一个主要输出方向LogstashJRuby / 高支持multiline复杂清洗、加工、转换内存开销大N100 上别碰FluentdRuby / 中高支持Kubernetes 生态常用插件丰富但性能一般资源占用偏高VectorRust / 极低支持高性能采集和转发配置语法和生态相对新我这次选的是 Promtail因为它和 Loki 配合最自然。Promtail 支持自动发现 Docker 容器日志、给日志打上容器名和 job 标签这正好符合我“所有容器日志自动进 Loki”的需求。如果你习惯用 Filebeat也可以把 Filebeat 的输出端配置为 Loki只是需要额外装 Loki 的 output plugin比较折腾不如直接用 Promtail 来得省事。2.3 Docker 日志的默认行为其实很坑在容器环境下应用如果直接把日志写到 stdoutDocker 默认会把它收进 json-file 格式的日志文件里。这个机制本身没问题但默认配置下日志文件无限增长不会自动轮转也不会自动清理。我见过不少人跑了一个月才发现/var/lib/docker/containers/下面某个容器日志已经占了几十 GB直接撑爆磁盘。更坑的是 Docker 的 json-file 日志一旦被轮转或截断旧日志就真的没了容器重启也会丢失之前的日志。所以我这次在 docker-compose 里给所有服务都加了明确的 logging 配置限制单个日志文件大小和保留数量。下面是通用配置基本可以直接抄services: eos: image: eos-service:latest logging: driver: json-file options: max-size: 50m max-file: 3这样配置之后单个容器日志最多占 150MB超过的旧日志会被 Docker 自动清理。但它也意味着如果容器频繁重启你可能只能看到最近几次的日志所以后面我把 EOS 的关键日志同时落盘到宿主机文件再用 Promtail 采集宿主机的文件路径这样日志不会因为容器重建而丢。3. 实操记录从发现日志异常到定位问题的完整流程3.1 第一步先查系统日志和容器状态我这次排查的起因是 EOS 服务每隔一段时间就会崩一次容器状态显示 Exited。重启之后能恢复但过几个小时又挂。所以第一步不是看应用日志而是先看系统日志确认是不是资源问题。N100 上 Linux 的系统日志都在 journald 里我习惯直接用下面的命令# 查看最近的内核日志重点关注 OOM、IO error、温度相关记录 sudo dmesg -T | tail -n 100 # 查看 EOS 服务相关的 systemd 日志 sudo journalctl -u eos --since 1 hour ago --no-pager # 查看 Docker 容器最近的日志 docker logs eos --tail 200 --timestampsdmesg 是排查内核级问题的第一入口。如果你看到类似Out of memory: Killed process或者oom-kill的字样基本可以断定是内存不足导致进程被杀。docker logs则是看应用自身输出的最快路径适合快速确认容器是否正常启动、有没有报错。这次排查中我一开始就在 dmesg 里看到了 OOM 相关的记录说明容器被杀不是 EOS 自己的逻辑问题而是 N100 这 16GB 内存真的不够用了。但 OOM 只是一个结果我还得搞清楚是谁把内存吃满了所以继续往下查。3.2 第二步ETL 式的日志盘点先搞清楚有哪些日志源既然要做日志采集就不能只盯一个文件。我把 EOS 相关的日志源全部盘了一遍包括Docker 容器 stdout 日志存于/var/lib/docker/containers/EOS 应用自身的文件日志比如/opt/eos/logs/下的 app.log、error.logN100 系统日志包括auth.log、syslog、kern.log慢查询日志如果 EOS 依赖数据库还需要看看 SQL 慢日志把这几个来源列清楚后我用一个简单命令确认了各类日志文件的体积和写入频率# 查看日志目录大小和最近修改时间 sudo du -sh /opt/eos/logs/* ls -lah /opt/eos/logs/对日志做“盘点”这一步很多人会忽略直接配置一个路径就开始采集。但实际排查时你会发现 EOS 某些异常日志只写到单独模块的日志文件里stdout 里根本看不到。只采 Docker 日志会造成信息缺失。所以我建议在做集中式日志采集之前先花十分钟把所有可能的日志文件位置、日志格式、日志量级摸清楚这能帮你避免“查了半天发现日志没采全”的尴尬。3.3 第三步把 EOS 日志输出到宿主机保证不丢前面提到 Docker 的 json-file 日志会在容器重启后轮转为了保住 EOS 的关键日志我直接把应用日志目录挂载到了宿主机services: eos: image: eos-service:latest volumes: - /opt/eos/logs:/var/log/eos这样 EOS 写入/var/log/eos/的内容实际落在宿主机的/opt/eos/logs/。Promtail 配置里直接采集宿主机路径就不用依赖 Docker 日志驱动了。挂载目录还能顺便解决一个问题多行日志是完整写入文件的不会像 Docker json-file 那样把每条日志强行拉成一行多行堆栈会被保留更适合排查。当然也可以用 Docker 提供的多行合并机制来处理但 Promtail 的 multiline 配置也很简单。我是在 Promtail 里针对error.log开启多行合并的这样 Java 之类的堆栈信息不会被打散。3.4 第四步Promtail 配置实战下面是这次 Promtail 的核心配置片段它同时采集两个来源Docker 容器日志自动发现和挂载到宿主机的文件日志。注意看pipeline_stages里的多行合并和标签设置server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /tmp/positions.yaml clients: - url: http://127.0.0.1:3100/loki/api/v1/push scrape_configs: - job_name: docker docker_sd_configs: - host: unix:///var/run/docker.sock refresh_interval: 5s relabel_configs: - source_labels: [__meta_docker_container_name] regex: /(.*) target_label: container - job_name: eos-files static_configs: - targets: [localhost] labels: job: eos __path__: /opt/eos/logs/**/*.log pipeline_stages: - match: selector: {jobeos, filename/opt/eos/logs/error.log} stages: - multiline: firstline: ^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}Promtail 的配置思路很简单定义采集任务告诉它文件路径然后由 relabel 或 static label 给日志打上标签。在这里我给 EOS 文件日志打了jobeos标签还按文件名区分了不同日志。之后在 Grafana 里查询就非常方便可以这样过滤{jobeos} | ERROR这条查询语句的意思是在所有 EOS 日志里搜索包含“ERROR”的日志行。Loki 的 LogQL 语法对用过 PromQL 的人很友好支持精确匹配、正则匹配、过滤关键字、聚合统计等。排查时我还会用下面这种格式统计错误日志速率sum by (level) (rate({jobeos} | ERROR [5m]))3.5 第五步在 Grafana 里做日志分析和可视化日志进到 Loki 之后我直接在 Grafana 里加了 Loki 数据源然后用 Explore 页面做检索。页面左侧可以选标签右侧直接输入 LogQL非常直观。排查期间我用得最多的三个查询目的LogQL 语句看 EOS 所有错误日志{jobeos}按容器名过滤{containereos} | json看最近 1 小时日志量变化sum(rate({jobeos}[1h]))Grafana 的 Explore 页面有个很实用的功能直接把日志查询结果按时间生成图表还能一键把某段日志展开看上下文。排查 OOM 问题的时候我用rate({jobeos}[5m])画了张图发现 EOS 日志量在崩溃前十分钟有一个明显的尖峰结合时间戳就锁定了问题发生的时间窗口。4. 这次排查踩到的坑和排查技巧4.1 问题一日志时间慢了 8 个小时第一次部署 Promtail 后我在 Grafana 里看到的日志时间戳全部比实际时间慢了 8 小时。这个坑非常经典根源是 Docker 容器默认使用 UTC 时区而宿主机用的是 CST东八区。虽然日志内容里的业务时间是对的但因为容器里日志文件的 mtime 和 Docker 日志驱动记录的时间都是 UTC导致 Loki 里显示的采集时间不对。解决办法有两个一是容器里把时区改成 Asia/Shanghai二是在 Promtail 里做时间偏移。我更推荐前者因为改了容器时区之后所有依赖时间的组件都保持一致。做法是在 docker-compose 里加环境变量services: eos: environment: - TZAsia/Shanghai如果是 Debian/Ubuntu 镜像可以再挂载/etc/localtime和/etc/timezone但加TZ环境变量已经能覆盖大多数情况。4.2 问题二日志量海啸导致磁盘 IO 被打满排查过程中发现 EOS 在异常时会疯狂打日志每分钟的输出量达到了几百 MB 级别Promtail 采集时直接把磁盘 IO 占满N100 整机卡顿到 ssh 都敲不动命令。这也是低功耗小主机最常见的悲剧应用日志刷屏同时把 IO 和 CPU 一起拖垮。解决办法是给日志采集和清理加双层保险。第一层是前面提到的 Docker logging 限制第二层是用 logrotate 定期轮转宿主机挂载的日志文件/opt/eos/logs/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }copytruncate是这里的关键参数。它先复制日志文件再清空原文件这样 Promtail 不会因文件被移动而丢失采集位置应用进程也不会因为日志文件被删除而继续往旧 inode 里写。不过要注意copytruncate在极端高并发写入下可能丢失极少量日志如果要求不丢日志可以考虑create模式配合应用 reopen 才能真正完美解决但对大多数场景来说copytruncate已经够了。4.3 问题三容器重启后日志断档查不到崩溃前状态EOS 容器崩掉之后我发现 Grafana 里有一段日志空白正好是崩溃前几十秒。原因很简单容器被 OOM Killer 杀掉之前Docker 的日志缓冲还没来得及 flush而 json-file 又是按行写的缓冲里的日志在进程被杀后就没有落盘。加上我之前给 Docker 设置了max-size: 50m旧日志被滚动掉了一部分又丢了一层。后来我把关键日志通过挂载目录持久化到宿主机再配合 Promtail 采集宿主机文件这个问题才算真正解决。因为应用直接写到文件系统的日志就算进程被杀已经写进去的内容依然在。排查时我建议优先确保关键服务的关键日志落盘不要只依赖 Docker 的 stdout 日志。4.4 问题四OOM Killer 杀进程N100 内存到底哪里不够回到最初的问题EOS 为什么反复崩溃dmesg 已经指向 OOM我就用下面的命令查了当时的内存状态# 看系统内存整体情况 free -h # 查 dmesg 里的 OOM 记录 sudo dmesg -T | grep -i oom # 查看进程内存占用排名 ps aux --sort-%mem | head -20排查发现N100 的 16GB 内存里Docker 容器加起来占了大约 10GB其中 EOS 容器由于日志量过大、内部缓存没及时清理占了快 5GB再加上文件系统缓存和其他系统进程内存就爆了。OOM Killer 选中的往往是占用内存最多、且 oom_score 最高的进程EOS 不幸中招。解决办法不是简单加内存N100 不少型号最大 16GB 或 32GB扩展空间有限而是做三层优化给 EOS 容器设置内存上限限制它不能超过 3GB开启 swap让内存吃紧时可以借磁盘空间顶一下优化 EOS 自身的日志输出逻辑避免高峰期刷屏。docker-compose 里对应配置如下services: eos: mem_limit: 3g memswap_limit: 4g restart: unless-stopped加上内存上限之后EOS 容器即使发生内存泄漏也只影响自己不会把宿主机拖垮。这是一个非常重要的隔离思路尤其在多容器共用一台小主机的时候。4.5 常见问题速查表整理一下这次排查中遇到的高频问题方便你直接对照现象可能原因快速处理日志时间相差 8 小时容器时区是 UTC设置TZAsia/Shanghai容器频繁被杀OOM Killer 触发dmesg 查 oom、给容器加 mem_limit日志文件无限增大未配置日志轮转Docker logging limits logrotate容器重启后日志丢了json-file 轮转 / 缓冲未 flush挂载目录持久化日志用 Promtail 采文件日志采集占用过高日志量太大、采集器配置过频增加日志采样、调整刷新时间、限制单文件大小日志里搜不到堆栈多行日志被拆成多行索引配置 multiline 合并第一行规则5. 排查后的优化与后续建议5.1 给 EOS 日志体系做的几个改进这次排查完成后我给 EOS 的日志体系做了几项改进。首先是规范日志格式统一要求 EOS 输出 JSON 结构化日志字段至少包含timestamp、level、module、message这样在 Loki 里可以用 LogQL 的\| json直接提取字段过滤效率高很多。其次是加入告警规则在 Grafana 里针对{jobeos} | FATAL这类日志设了 alert一旦出现严重错误就通知我不用等用户来报。第三是控制日志输出级别生产环境默认 INFO排查时临时开 DEBUG避免平时就刷大量无用日志。5.2 对 N100 同类小主机的运维建议如果你也用 N100 或者类似的小主机跑服务我作为一个刚吃过亏的人给你几个实在的建议。给每个 Docker 容器都设置内存限制这样单个容器异常不会拖垮整台机器。系统层面至少开 2GB swap虽然性能不如内存但能有效减少 OOM 概率。日志采集器不要和业务容器共享 /var/lib/docker 所在的磁盘有条件的话用独立目录挂载。注意定期查看磁盘占用N100 这类机器通常只有一块 NVMe 或者 SATA 盘几百 GB 空间很容易被日志和镜像占满。散热也要留意。N100 小主机很多是被动散热长时间高负载运行会降频排查性能问题的时候如果发现 CPU 频率远低于标称值先看看机器温度是不是过高。我这次排查时就顺带加了温度监控用sensors命令配合 Grafana 展示比裸奔强很多。5.3 最后分享一个实用小技巧还有一个非常实用的小技巧适用于任何 N100 排查场景在 ssh 进主机之后养成先跑uptime free -h df -h ps aux --sort-%mem | head -15的习惯。一行命令几秒钟就能拿到系统负载、内存、磁盘、进程占用的全貌很多问题的方向一下子就有了。排查问题不要一上来就翻应用日志先看资源再看日志最后定位代码这个顺序能省下大量时间。这次“EOS 日志 N100 真机排查”的记录就写到这里。日志排查本质上是把看不见的系统行为变成看得见的证据链真机上踩过的坑越多后面的路就越顺。我后续还会继续记录这个系列里具体告警规则和日志量治理的做法希望能对同样在用 N100 跑服务的人有点帮助。