ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ubuntu 22.04日志管理实战:从journalctl到logrotate与集中采集

Ubuntu 22.04日志管理实战:从journalctl到logrotate与集中采集 接手一台Ubuntu 22.04服务器之后我建议你第一件事就是搞清楚这台机器上的日志到底怎么存、怎么查。很多以为自己“运气不好”的故障——服务莫名退出、磁盘空间突然告警、半夜被入侵、应用响应变慢其实答案早就躺在日志里了。只是大多数人没时间、没习惯、没用对工具去读它。这篇文章不聊空泛的理论直接围绕Ubuntu 22.04的日志处理讲透日志从哪来、在哪个文件里、用哪些命令能快速翻到想要的内容、怎么把日志集中采集出去、怎么防止日志把磁盘塞爆以及我在实际运维中踩过的坑。无论你是刚上手Ubuntu的新人还是从CentOS迁移过来的老运维这篇文章都能让你少走弯路。先说结论Ubuntu 22.04的日志体系和老版本区别非常大理解它的“双轨制”是第一步。1. 日志从哪来、到哪去1.1 Ubuntu 22.04的日志体系三股流量很多人上来就习惯去翻/var/log/messages结果发现文件不存在就开始怀疑系统有问题。其实不是坏了而是Ubuntu没有这个文件。Ubuntu 22.04的日志体系可以粗略分成三股流量第一股是内核和系统服务的日志由systemd-journald接管。所有通过systemd启动的服务几乎涵盖所有系统服务它们的标准输出、标准错误、以及调用syslog接口打印的日志最终都被journald收集存成二进制的journal文件放在/var/log/journal/目录下。你直接用cat去读这些文件会看到乱码必须用journalctl命令来查。第二股是传统文本日志由rsyslog处理。journald虽然收日志但它本身不做复杂的文件落盘和转发真正把日志写到/var/log/syslog、/var/log/auth.log这些文本文件里的是rsyslog。它从journald读取日志再根据规则分发到对应文件。第三股是应用自己写的日志比如Nginx的access.log、MySQL的error.log、Java应用的日志文件。这类日志和系统日志基本无关由应用自行管理通常也放在/var/log下或者应用自己的目录里。理解这三股流量是处理Ubuntu 22.04日志的基础。我见过不少人在/var/log/syslog里找不到某个服务的日志就以为系统出问题了其实那个服务的日志可能只进了journald根本没被rsyslog落到文件里。说起来Ubuntu 22.04时代默认日志采集链路大致是这样的进程写日志 → journald收集 → rsyslog监听journal → 写入/var/log下的文本文件。中间任何一环配置变化都会影响最终结果。1.2 /var/log核心文件一览Ubuntu 22.04的/var/log目录里我平时最常看这些文件文件路径内容说明/var/log/syslog系统全局日志很多服务都会写到这里排查问题优先看它/var/log/auth.log登录认证日志包括SSH登录、sudo提权记录安全检查必看/var/log/kern.log内核日志硬件、驱动、内核模块问题看这里/var/log/dpkg.log软件包安装和升级记录排查“软件莫名其妙没了”的问题/var/log/apt/apt更新日志目录含history.log和term.log/var/log/boot.log开机时的服务启动信息/var/log/nginx/Nginx访问和错误日志安装Nginx后出现/var/log/mysql/MySQL错误日志安装MySQL后出现/var/log/journal/journald的二进制日志目录用journalctl查询一个很实用的习惯是不确定某个服务日志在哪的时候用ls -lt /var/log/ | head看看哪些文件最近有更新基本就能锁定目标。2. 用journalctl翻日志比你想的更好用2.1 至少要学会的6种journalctl查看方式journalctl是Ubuntu 22.04上最核心的日志查看命令也是很多人觉得最不直观的命令。我日常使用频率最高的是下面这些组合查看本次开机之后的全部日志journalctl -b查看上次开机的日志journalctl -b -1查看某个Unit服务的日志这是排查服务问题的王牌命令journalctl -u ssh.service看某个服务的最近100行日志并且持续跟随输出调试服务时开着它很舒服journalctl -u nginx.service -f -n 100只看内核日志相当于dmesg加了时间戳和持久化journalctl -k看过去1小时内的日志journalctl --since 1 hour ago我特别喜欢_PID、_COMM这些字段的过滤方式比如我只想看到某个进程的崩溃信息journalctl _COMMjava --since 2024-01-01 --until 2024-01-02这里的关键是journal日志里每个条目都有丰富的元数据包括进程号、用户ID、来源Unit、时间戳等。你用journalctl -o verbose就能看到每条日志的完整字段再配合_PID、_UID、_SYSTEMD_UNIT过滤精度极高。2.2 “无日志文件夹”是怎么回事很多人搜过这么一个问题某服务的日志文件夹不存在到处找不到日志。我刚开始用systemd的时候也困惑过。一个systemd服务如果配置文件里没有指定StandardOutput和StandardError那么它打印到标准输出和标准错误的内容默认全被journald收走了不会生成/var/log/xxx.log文件。这不是Bug而是设计选择。所以当你发现“无日志文件夹”时第一反应应该是journalctl -u 你的服务名日志大概率都在这里。如果你就是想要独立的文件日志可以在service文件里显式指定[Service] StandardOutputfile:/var/log/myapp/app.log StandardErrorfile:/var/log/myapp/app.log但要注意journald依然会同时收集一份所以要控制好日志量。还有就是如果用Docker容器里服务打印到stdout的日志默认由Docker接管不走journald别搞混了。2.3 查看crontab执行日志的两种办法我经常在服务器上排查定时任务为什么没跑发现很多人不知道crontab日志在哪。这里容易有两个坑一是cron的日志默认落在了syslog里但容易被忽略二是journald里也有但得用对过滤条件。第一种办法直接搜sysloggrep CRON /var/log/syslog第二种办法用journalctl过滤journalctl _COMMcron --since today如果发现日志量太大可以单独给cron一把椅子。在/etc/rsyslog.d/下新建一个配置文件比如50-cron.conf写上cron.* /var/log/cron.log重启rsyslog之后cron日志就单独落到文件里了。如果你用的是系统自带的systemd timer那又是另一套查看方式systemctl list-timers看定时器状态journalctl -u 定时器名字查看执行细节。3. 把日志统一采集起来单机翻日志是基本功但真正上班之后你很快会遇到更现实的问题几十台服务器的日志太分散每台都ssh上去journalctl会疯掉的。这时候就需要把日志统一采集到一个平台。Ubuntu 22.04上最常见的两种方式我要展开讲讲。3.1 用filebeat做JSON化采集Elastic公司出的filebeat是目前最常见的轻量日志采集器在Ubuntu 22.04上配置不算复杂但有几个细节值得注意。安装方式我习惯直接用Elastic官方仓库curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add - echo deb https://artifacts.elastic.co/packages/8.x/apt stable main | sudo tee /etc/apt/sources.list.d/elastic-8.x.list sudo apt update sudo apt install filebeat然后修改/etc/filebeat/filebeat.yml。一个最小可用的配置大概是这样的filebeat.inputs: - type: filestream id: my-application enabled: true paths: - /var/log/myapp/*.log fields: service: myapp hostname: ${HOSTNAME} fields_under_root: true output.elasticsearch: hosts: [192.168.1.10:9200] username: elastic password: changeme配置好之后启动并设置开机自启sudo systemctl enable --now filebeat这里我要提醒一个坑filebeat的旧版配置用- type: log新版本推荐filestream两者参数不兼容。网上90%的老教程还在用logtype照着抄到8.x版本上会直接启动失败。报错信息通常会说unknown key。另外filebeat默认会记录读取进度到/var/lib/filebeat/registry/所以第一次采集会从头读之后只读新增。如果你改过paths想重新采集记得把registry目录清掉sudo rm -rf /var/lib/filebeat/registry sudo systemctl restart filebeat3.2 用rsyslog做远程转发如果你不想引入Java环境、不想维护Elasticsearch集群只想把多台Ubuntu的日志集中收集到一台机器上rsyslog远程转发是更轻量的方案。Ubuntu 22.04自带rsyslog只需要两端做配置。接收端日志服务器在/etc/rsyslog.conf里打开模块module(loadimudp) input(typeimudp port514) module(loadimtcp) input(typeimtcp port514)如果想按发送方主机名区分日志可以加一行模板规则。比如在/etc/rsyslog.d/60-template.conf里写template(namePerHostLog typestring string/var/log/remote/%HOSTNAME%/%programname%.log)发送端在/etc/rsyslog.d/60-forward.conf里写*.* 192.168.1.10:514重启rsyslog服务后日志就转发过去了。注意这里的协议单个 表示UDP两个 表示TCP。UDP快但有丢包风险TCP稳但占用稍高内网采集用TCP更可靠。我自己的习惯是小规模比如10台以内用rsyslog就够省心、不占资源。规模大了、需要全文检索和可视化再上filebeat Elasticsearch。3.3 到底选哪个filebeat和rsyslog对比对比项filebeatrsyslog远程转发资源占用中等Go语言实现单实例约20-40MB内存低原生进程内存占用可忽略功能支持多输出、多条件过滤、内置模块侧重转发和简单过滤规则较繁琐扩展性配合ES、Logstash、Kibana能力强配合远端rsyslog简单集中缺乏可视化上手难度中等配置文件YAML清晰中等配置语法老派需要查文档适用场景中等规模以上、需要搜索和可视化小规模、只要集中存储即可无论选哪个都建议在采集端就把日志结构化。比如应用能输出JSON最好实在不行也至少保证一条日志一行不要用多行日志否则采集、解析、检索全部难受。4. 日志轮转与清理别让日志把磁盘撑爆4.1 logrotate的工作原理日志如果只增不减任何机器都扛不住。Ubuntu 22.04自带的logrotate工具就是为了解决这个问题。它的核心思想很简单定期把当前日志文件改名、压缩或删除然后让应用重新写入新文件。logrotate的配置分为两部分全局配置/etc/logrotate.conf和各应用自己的配置/etc/logrotate.d/下的文件。系统每天通过cron定时执行一次/etc/cron.daily/logrotate就是入口。它的执行逻辑大概是读取配置 → 判断是否需要轮转按天数或大小→ 执行轮转 → 执行postrotate脚本通常是通知应用重开日志文件比如systemctl reload nginx→ 把旧文件按序改名、压缩 → 超过保留数量的删除。4.2 定制一个logrotate配置我举一个实际例子。假设你的应用myapp每天会产生100MB日志你想保留7天每天轮转一次并开启压缩。在/etc/logrotate.d/myapp里写/var/log/myapp/*.log { daily rotate 7 compress delaycompress missingok notifempty dateext su myapp myapp postrotate systemctl reload myapp endscript }这里逐条说明daily每天轮转一次。如果要按大小换成size 100M两者选一个。rotate 7保留7个旧文件第8个会被删掉。compress轮转后的旧文件压缩成gz格式。delaycompress比较巧妙表示压缩延迟到下一轮也就是最新那个旧文件不压等下轮一起压。这么做主要防止像Nginx这类还在往旧文件写日志的应用出问题。missingok日志文件不存在时不要报错避免黑屏刷错误。notifempty文件为空就不轮转。dateext轮转后的文件名带上日期比如app.log-20240101.gz比默认的.1、.2后缀直观得多。su myapp myapp以指定用户执行防止权限问题导致轮转失败。postrotate轮转完成后重载应用让它重新打开新的日志文件。算一笔账每天100MB保留7天且压缩。压缩率按0.2算日志占用大约是7个旧日志压缩后 ≈ 100MB × 0.2 × 7 ≈ 140MB加上当天原始日志100MB总共不到250MB。如果不开压缩那就是800MB差距巨大。所以我强烈建议所有能开压缩的日志都开压缩。反正解压也只是日志平台采集端的举手之劳。4.3 手动清空和滚动日志的注意事项线上总会遇到一些奇葩场景某个应用不写日志到文件而是持续刷屏日志文件已经几个GB了logrotate因为rotate数量还没到或者应用不配合一直没转。这时候你需要手动干预。最简单的做法是清空文件而不是删除 /var/log/myapp/big.log这个命令用Shell重定向直接清空文件内容。我特别强调不要用rm删除日志文件特别当应用还持有文件句柄时。删除后应用会继续往那个已经“消失”的inode上写数据磁盘空间不会释放而且新文件根本不会创建。这是Linux下最常见的“明明删了日志磁盘空间还是满”的原因。正确清空一个正在被写的大日志还有一种更自动化的方式truncate -s 0 /var/log/myapp/big.log甚至想保留部分内容可以truncate -s 100M /var/log/myapp/big.log这样文件会缩短到100MB之后的日志继续追加。应用进程完全无感知也不需要重启。不过要注意有些应用会记录自己在文件中的偏移位置比如tail -c这种突然截断可能导致它继续从旧偏移写产生文件空洞。我遇到过Java应用配合log4j时truncate后再往里写会出现一大串NUL字符。稳妥的做法还是truncate之后观察一下日志是否正常增长如果异常就重启应用。5. 常见问题与排查技巧5.1 journald日志把磁盘占满这是我在Ubuntu 22.04上见过最多的问题。journald默认会把日志存在/var/log/journal里而且没有做严格的大小限制如果某个服务疯狂刷日志journal文件能涨到几个GB甚至占满整个根分区。最直接的查看方式journalctl --disk-usage然后你可以手动清理journalctl --vacuum-size200M这会把日志总量压缩到200MB以内只保留更近的日志。还可以按时间清理只保留最近7天journalctl --vacuum-time7d但从根本上解决问题还是要修改journald的配置。编辑/etc/systemd/journald.conf找到这几行并改成SystemMaxUse200M SystemMaxFileSize50M MaxRetentionSec7daySystemMaxUse是journal能够占用的最大磁盘总量SystemMaxFileSize是单个journal文件上限MaxRetentionSec是日志最大保留时间。改完重启服务sudo systemctl restart systemd-journald注意重启journald会丢掉内存中的部分日志但磁盘上已有的journal仍在。这里有个顺带坑改配置后建议顺手journalctl --verify检查一下journal文件的完整性我遇到过断电后journal文件损坏执行此命令能提前发现。5.2 日志时间戳和实际时间对不上刚装完Ubuntu 22.04很多人发现日志时间和当前时间差8小时这通常不是日志系统坏了而是时区没配。systemd时代系统时钟管理已经统一了配置方法如下sudo timedatectl set-timezone Asia/Shanghai设置完用timedatectl查看确认。之后journald和rsyslog记录的日志时间戳就会跟随系统时区。但这里有个深坑journald内部存储的时间戳实际上是UTC时间只展示的时候转换成本地时间。所以如果你把系统时区改了之前记录的日志展示时间也会跟着变内容不会错乱。如果你用的是Docker容器容器里的日志时间出问题又是一种解法启动容器时必须挂载时区文件docker run -v /etc/localtime:/etc/localtime:ro ...否则容器默认UTC日志自然比宿主机快8小时。5.3 filebeat采集不到日志遇到filebeat装了但采集不到日志我通常按这个顺序排查第一步看进程有没有起来systemctl status filebeat第二步看filebeat的日志有没有报错journalctl -u filebeat -n 50第三步测试能否连上目标curl -s http://192.168.1.10:9200第四步看是否有权限问题filebeat默认以root运行但如果你在配置里指定了user: myapp那它读不了/var/log/myapp/下权限600的日志文件。还有一个我踩过的坑paths里写的是/var/log/myapp/*.log但应用实际生成的日志后缀是log.20240101不匹配导致filebeat永远扫不到。解决方法是把通配符改成/var/log/myapp/*或者指定具体文件名。5.4 重启日志服务导致日志丢失很多人不知道restart rsyslog和restart systemd-journald的区别。journald重启会丢失一部分正在内存中的日志因为这些日志还没落盘。所以不要没事就重启journald尤其是排查问题的时候重启一次可能就把关键线索弄丢了。相比之下rsyslog重启影响小一些因为它只是读取journal再写文件已经在文件里的日志不丢但journald那边没落盘的依然丢。如果确实需要清理日志且不想丢关键信息建议先执行journalctl --flush把内存日志强制写入磁盘再进行重启。另外每次大版本升级之后我建议抽查几个服务的日志轮转是否正常。Ubuntu的自动更新可能替换了logrotate版本或者某些包自带的配置覆盖了你定制的内容。我遇到过升级后/etc/logrotate.d/nginx配置被整体替换结果自定义的dateext和保留数量全丢了日志又开始越拉越长。日志处理这件事说大不大说小不小。它不产生业务价值但业务出了问题第一个要找的就是它。我在Ubuntu 22.04上摸索了几个月踩过不少坑之后最大的体会是先把journald弄明白把rsyslog的转发和logrotate的轮转配好最后再考虑上采集平台。大多数中小规模的日志问题靠这几样就能解决。最后再分享一个小技巧如果不知道日志量增长有多快可以用这个命令快速估算今天写了多少日志journalctl --since today | wc -l数字一旦稳定增长你就知道该去把轮转和采集配好了。
返回列表