
如果你的服务器磁盘又被Nacos日志撑爆了别急着扩容先看看logs目录里是不是堆了一堆几十天甚至几个月前的老日志。我维护Nacos集群这几年最常处理的不是功能问题而是磁盘告警。Nacos作为注册中心和配置中心启动后会在logs目录持续写入主日志、访问日志、配置日志、命名服务日志长时间不处理几十GB很常见。所以我很早就写了一个“保留最近14天日志”的清理脚本放在cron里定期跑。这篇文章就把它完整拆开来讲包括脚本代码、设计思路、部署方式还有我踩过的那些坑。1. 先看看Nacos的日志到底堆在哪1.1 日志目录和典型文件Nacos默认把日志写到${nacos.home}/logs目录下如果你是用官方发行包直接启动的一般就在/home/nacos/logs或者/opt/nacos/logs。我见过不少人把Nacos部署在自定义路径下找日志找半天其实看启动脚本或者application.properties里的配置就能确定。这个目录下比较常见的文件有这么几类文件/目录作用nacos.logNacos主日志框架启动、异常堆栈、核心业务链路基本都在这里access_log.YYYY-MM-DD.logHTTP访问日志按天滚动请求量大时增长很快naming.log服务注册与发现的日志config.log配置中心相关日志配置发布、监听、推送都会有记录admin.log控制台操作后台日志client.log客户端请求相关日志某些版本比较明显start.out启动脚本输出一般不会太大但也会积累gc.log/heapdumpJVM垃圾回收日志和堆转储文件出现问题排查时才会有第一次去看Nacos日志目录的人往往会吓一跳nacos.log可能有几个GBaccess_log文件按天能生成几百MB几个月不清理几十GB完全不夸张。磁盘满了之后轻则日志写不进去重则Nacos直接异常注册中心挂掉会影响所有接入方比业务服务宕机还麻烦。1.2 为什么默认不清理会出事很多中间件默认不会帮你去滚动清理日志。Nacos主要依赖logback和Tomcat AccessLog日志文件滚动策略默认只是按天生成新的access_log文件并不会自动删除历史文件。也就是说只要服务一直跑日志文件只会越来越多。更隐蔽的问题是JVM层面。如果日志文件一直增长日志写入本身会占用CPU和IO磁盘使用率达到100%以后Nacos写日志时会出现IO错误进而影响配置推送、服务注册这些关键功能。网上搜Nacos故障不少都跟“磁盘满”有关。所以日志清理不是可做可不做的事而是生产环境里一个很基础的运维动作。1.3 保留14天这个周期怎么定“保留最近14天”不是拍脑袋定的。在绝大多数运维场景里线上问题的排查周期一般不会超过一周到两周超过14天的日志基本没有实时排查价值。如果业务方需要更长留档比如等保审计要求30天、90天那就把脚本里的参数改成30或90都是可行的。我自己的习惯是14天做一个平衡点既能覆盖大部分“出问题往前查日志”的时间窗口又能把磁盘空间压在一个可控范围。比如一个日均产生1GB日志的Nacos节点14天大约占用14GB对于一个独立的日志分区来说完全可以接受。如果你需要追溯更久最好不依赖单机文件而是把日志接入ELK或者其他日志平台那就不是这里要讨论的方案了。2. 清理脚本的整体设计思路2.1 是选择logrotate还是自研脚本第一反应可能有人会说Linux系统不是自带logrotate吗直接配置一下不就行了。确实logrotate可以做轮转和删除但它更适合处理那些有统一规范、路径固定、按固定pattern命名的日志文件。Nacos的日志文件命名、滚动方式在不同版本、不同部署方式下不太一致而且access_log是按日期生成的nacos.log又是单一文件持续写入配置logrotate规则需要写好几条权限、压缩、olddir等参数挺多维护成本并不低。自研脚本的好处是直观、可控、好扩展。它可以做到只清理Nacos的logs目录、不误伤其他目录保留最近14天修改的文件提供dry-run模式先看效果留出环境变量可以灵活调整目录和天数甚至能在清理前自动统计释放空间。我最终采用的是自研bash脚本因为生产环境里bash是最通用的不需要额外装Python依赖放到任何一台Linux机器上都能跑。2.2 核心删除逻辑find -mtime 14脚本的核心就一个find命令。它的逻辑是找出logs目录下所有普通文件然后筛选出mtime超过14天的文件最后打印并删除。find -mtime按天粒度判断比如-mtime 14表示文件内容最后修改时间在14个24小时之前。这里有个细节必须说清楚find -mtime 14不等于“文件创建超过14天”也不等于“文件名称里的日期超过14天”。它以文件inode的修改时间为准判断粒度是“天”而且具体边界行为和find版本有关。比如某个文件最后一次写入时间在13天23小时前-mtime 14可能不会匹配它而一个14天前凌晨修改、当前时间是下午的文件匹配结果也不同。实际场景里我们不需要精确到秒所以这个粒度的偏差完全可以接受。如果你希望更精确可以用-mmin 20160表示20160分钟前修改不过一般没必要。另一个常见的坑是直接对Nacos当前正在写入的日志文件执行删除。Nacos的nacos.log、config.log等都是进程长期持有的文件句柄find -mtime 14原则上不会删掉这些“最近还在写”的文件因为只要进程在写文件mtime就是新的。但如果服务已经停止或者某个日志文件已经很长时间没有新的写入记录它就会被识别为“旧文件”然后被删掉。如果Nacos进程还在删掉后又重新创建同名文件问题不大但如果Nacos进程已经停止删除也是安全的。真正要小心的是删除一个进程仍在写、但文件mtime因为写入频率极低而“看起来旧”的文件。所以脚本里建议加一层排除规则把当前活跃的日志文件名显式排除掉。2.3 加一层“未雨绸缪”的保护机制我在脚本里加了三层保护机制分别是dry-run模拟运行、文件列表输出、磁盘占用前后对比。dry-run模式是必须的尤其是第一次在生产执行之前一定要先跑一遍只看不删的模式确认列出来的文件都是该清理的。文件列表输出可以直接定位哪些文件会被删避免误删到近期还在用的归档。磁盘占用前后对比可以用来验证清理效果也能让你对每个节点每天产生多少日志有个量化认知。另外脚本开头用set -euo pipefail一旦中间有命令出错就会立即退出避免管道下出现半截执行的情况。脚本还会检查日志目录是否存在、环境变量是否合法尽量做到“拿到就能用”。3. 可直接复制的清理脚本实现3.1 脚本完整代码下面这个脚本我已经用在多套Nacos集群上直接保存为clean_nacos_logs.sh即可。#!/usr/bin/env bash set -euo pipefail # # Nacos 日志清理脚本保留最近14天日志 # 用法 # DRY_RUNtrue ./clean_nacos_logs.sh # 只打印不删除 # DRY_RUNfalse ./clean_nacos_logs.sh # 实际清理 # 建议通过 crontab 定时执行 # # 日志目录可通过环境变量覆盖 NACOS_LOG_DIR${NACOS_LOG_DIR:-/home/nacos/logs} # 保留天数 RETAIN_DAYS${RETAIN_DAYS:-14} # dry-run 开关默认 true避免误删 DRY_RUN${DRY_RUN:-true} # 当前正在写入的活跃日志文件名清理时排除 ACTIVE_LOG_NAMES( nacos.log naming.log config.log admin.log client.log ) if [[ ! -d $NACOS_LOG_DIR ]]; then echo [ERROR] 日志目录不存在: $NACOS_LOG_DIR 2 exit 1 fi # 构造排除参数 EXCLUDE_ARGS() for name in ${ACTIVE_LOG_NAMES[]}; do EXCLUDE_ARGS(! -name $name) done # 计算清理前后目录占用 before_size$(du -sh $NACOS_LOG_DIR | cut -f1) if [[ $DRY_RUN true ]]; then echo [INFO] DRY-RUN 模式以下文件将被清理 echo [INFO] 日志目录: $NACOS_LOG_DIR echo [INFO] 保留天数: $RETAIN_DAYS echo --------------------------------------------- # 仅打印文件列表 find $NACOS_LOG_DIR -type f -mtime ${RETAIN_DAYS} ${EXCLUDE_ARGS[]} \ -printf %TY-%Tm-%Td %TH:%TM %10s %p\n | sort echo --------------------------------------------- echo [INFO] 当前占用: $before_size echo [INFO] 执行命令: DRY_RUNfalse $0 else echo [INFO] 开始清理 $NACOS_LOG_DIR 下超过 $RETAIN_DAYS 天的日志文件 # 打印并删除匹配的文件 find $NACOS_LOG_DIR -type f -mtime ${RETAIN_DAYS} ${EXCLUDE_ARGS[]} \ -printf 删除 %TY-%Tm-%Td %TH:%TM %10s %p\n \ -delete after_size$(du -sh $NACOS_LOG_DIR | cut -f1) echo [INFO] 清理完成日志目录占用: $before_size - $after_size fi3.2 关键参数怎么配脚本最上方几个变量是给你改的。NACOS_LOG_DIR就是Nacos的logs目录改成你的实际路径RETAIN_DAYS表示保留最近几天默认14DRY_RUN默认开启模拟这样就算有人不小心直接执行了脚本也不会把日志删掉。ACTIVE_LOG_NAMES数组里列的是当前正在写入的文件名。把这些文件排除在清理范围之外可以避免“进程还在写这个文件却被find判定成旧文件删除后磁盘空间不释放”的情况。不同Nacos版本日志文件名略有差异你可以根据实际目录清单再补充。比如如果有cluster.log、distro.log之类的文件也加入这个数组即可。3.3 先跑一遍模拟确认删除范围拿到脚本后第一步不是直接执行而是先给脚本加上可执行权限然后用dry-run模式跑一遍chmod x clean_nacos_logs.sh DRY_RUNtrue ./clean_nacos_logs.sh输出会列出一堆文件。仔细看这些文件的时间戳和大小确认它们确实是你想清理的旧日志。如果在模拟列表里看到不应该被删的文件先停下来检查是不是日志文件命名、排除规则或者RETAIN_DAYS设置出了问题。我第一次在生产环境跑的时候模拟列表里出现了一个目录叫history里面是几个月前手动归档的日志本来不想删。后来才意识到find默认会递归子目录而我当时没有加排除目录的逻辑。如果你也有类似不想清理的子目录可以在find命令里加-path $NACOS_LOG_DIR/history -prune -o这类的排除参数。3.4 部署到crontab定期执行确认模拟结果没问题后就可以用真实模式执行一次然后配置crontab。推荐每天凌晨3点左右执行避开业务高峰和Nacos的定时任务时间。crontab配置示例0 3 * * * DRY_RUNfalse NACOS_LOG_DIR/home/nacos/logs RETAIN_DAYS14 /home/nacos/scripts/clean_nacos_logs.sh /var/log/clean_nacos_logs.log 21注意几点脚本和日志路径最好用绝对路径cron执行时的PATH可能很短DRY_RUNfalse要放在命令前作为环境变量传递给脚本重定向日志要保证/var/log目录可写或者改成nacos用户有权限的路径。这里多说一句crontab里执行的脚本环境变量和交互式终端里不一样。我遇到过明明脚本在本机手动执行没问题放cron里却一直找不到find命令的情况最后发现是PATH里没有/usr/bin。脚本第一行的shebang已经指定了bash但环境变量仍需在cron命令里显式给全或者在脚本开头重新export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。这样更稳妥。4. 实操过程从磁盘告警到自动清理4.1 第1步磁盘占用体检在配置自动清理之前先对Nacos节点做个“体检”。这一步是为了搞清楚你面对的日志量级有多大以及清理后能释放多少空间。我一般这样操作# 查看磁盘整体使用率 df -h # 查看Nacos logs目录总大小 du -sh /home/nacos/logs # 按大小排序看看哪个文件最占空间 du -h /home/nacos/logs/* | sort -rh | head -30如果发现某个access_log.*.log已经几个GB或者nacos.log超过2GB说明这个节点日志滚动频率很高必须及时清理。你也可以顺手统计一下14天前的日志文件总大小用于预估清理收益。一条命令就能算find /home/nacos/logs -type f -mtime 14 -printf %s\n | awk {sum $1} END {printf 可释放约 %.2f GB\n, sum/1024/1024/1024}这是我在一个日活比较高的Nacos集群上看到的真实情况logs目录20GB左右其中14天前的文件占了约11.5GB。跑完清理脚本目录从20GB降到8.5GB效果非常明显。4.2 第2步模拟运行观察结果体检之后我会先跑dry-run。这一步不只是看一遍文件列表还要核对几个细节文件清单里有没有近期问题的排查归档有没有同事手动放进去的分析文件有没有自定义扩展模块生成的日志如果一切正常再跑一次带统计的dry-run确认可释放文件总大小符合预期。这里可以用一个临时变量观察不会影响正式脚本DRY_RUNtrue /home/nacos/scripts/clean_nacos_logs.sh模拟输出的文件如果太多可以重定向到文件里慢慢看或者用grep过滤某一天的文件。总之这一步越仔细后面误删的概率越低。4.3 第3步生产执行与前后对比确认完毕后切到真实模式执行一次DRY_RUNfalse /home/nacos/scripts/clean_nacos_logs.sh脚本会输出每一行“删除”记录同时给出前后占用对比。如果执行过程中没有报错并且“日志目录占用”一栏显示明显下降说明清理成功。这里要特别提醒真实模式执行前确保脚本有足够的权限去删除文件。如果Nacos是用nacos用户启动的日志目录属主和属组也是nacos那么你用root跑脚本当然能删但如果你想用nacos用户本身跑cron需要保证nacos对日志目录有写权限。否则find命令会报Permission denied虽然不会崩但部分文件会漏删。4.4 第4步配置自动清理生产执行没问题后我就配置crontab。如果Nacos部署在多台机器上我会让每台机器都放一份同样的脚本各自独立清理互不影响。线上推荐配置0 3 * * * cd /home/nacos/scripts DRY_RUNfalse NACOS_LOG_DIR/home/nacos/logs RETAIN_DAYS14 ./clean_nacos_logs.sh /var/log/clean_nacos_logs.log 21配好之后等第二天早上看一眼/var/log/clean_nacos_logs.log确认有新的执行记录。如果你希望执行后能收到通知可以在脚本末尾加一行curl调用把清理结果POST到企业微信机器人、钉钉机器人或者邮件网关。注意不要把日志内容原文发出去只要发送“哪个节点、清理前后大小、清理文件数量”即可。5. 常见问题脚本跑了但空间没释放怎么办5.1 为什么磁盘空间还是没释放这是最经典的问题。你删了文件df -h一看磁盘空间竟然没有变化或者只减少了一点点。原因不是find没匹配到文件而是某个被删的文件仍被Nacos进程持有文件描述符。Linux下删除文件只是unlink如果进程还开着这个文件文件内容不会真正消失直到文件句柄被释放。如果误删了当前活跃日志或者Nacos进程绑定了一堆历史日志文件就会出现这种情况。排查方法lsof L1 | grep /home/nacos/logs或者find /proc/*/fd -lname /home/nacos/logs* 2/dev/null | head -20看到deleted标记的就是已经被删除但仍占用空间的文件。处理方式取决于场景如果是nacos.log这类活跃文件直接重启Nacos服务如果是历史文件其实不用管Nacos不会一直占着不放但为了立刻释放空间也可以重启一次。注意重启前先确认其他节点是否正常避免引起服务抖动。5.2 为什么cron任务没有按预期执行遇到cron不执行我一般按三步排查。第一步看crontab服务状态systemctl status crond或service cron status确认服务在跑。第二步看脚本日志如果/var/log/clean_nacos_logs.log是空的说明脚本根本没被调用大概率是crontab语法或路径问题。第三步单独把cron命令行复制出来在终端里直接执行。如果手动执行成功而cron里失败多半是环境变量问题。在脚本开头加上export PATH就能解决大部分问题。另外我习惯在crontab里显式写/bin/bash /home/nacos/scripts/clean_nacos_logs.sh避免系统默认shell解析差异。5.3 误删了正在写入的日志怎么办如果脚本不小心把Nacos当前正在写的日志文件删了比如因为你没有把某个新出现的日志文件名加入ACTIVE_LOG_NAMESNacos进程会继续往已删除的inode写入不再生成同名文件。从外部看文件名还在不在取决于是否重新创建但磁盘空间可能一直不释放。这时候最简单的处理是重启Nacos服务。服务重启后会重新创建日志文件磁盘空间也会被释放。如果不想重启可以尝试用kill -HUP让logback重新初始化但不同Nacos版本对HUP信号的处理机制不一致不一定能触发日志文件重建。我最推荐的还是重启一次虽然听起来粗暴但在日志文件被误删的场景下这是最确定有效的手段。5.4 容器环境下怎么处理Nacos跑在Docker容器里时日志通常挂载在宿主机目录上。清理脚本有两种跑法一种是进入容器内跑但容器镜像里可能没有bash、find或者权限受限另一种是直接在宿主机上对挂载目录执行清理这也是我更推荐的方式。如果日志目录挂载方式是/home/docker/nacos/logs:/home/nacos/logs宿主机上直接执行脚本指定NACOS_LOG_DIR/home/docker/nacos/logs即可。不过要注意容器内和宿主机的时区是否一致。如果容器的TZ没有设置日志文件时间戳可能显示UTC时间find -mtime也会受系统时区影响。解决方法是启动容器时设置TZAsia/Shanghai或者把脚本里的时间相关操作都基于宿主机的实际时间来判断反正容器日志文件的时间戳最终会由宿主机inode记录。6. 我踩过坑之后的一些经验6.1 关于活跃日志排除列表脚本里的ACTIVE_LOG_NAMES不是一成不变的。Nacos版本升级后日志文件名可能调整。比如我之前遇到过某个版本多了一个protocol.log因为没加进排除列表差点被当成旧日志清理掉。建议每次升级Nacos后都去logs目录看一眼当前有哪些文件正在持续更新同步更新脚本的排除数组。更稳妥的做法是可以不硬编码文件名而是用“最近24小时内是否被修改”来判断活动文件。比如在find命令里加入! -mtime -1表示“排除最后修改时间在1天之内的文件”。这样即使出现新的日志文件也不会被误删。不过这会改变筛选逻辑你想保留14天的日志但“排除最近1天修改的活跃文件”和“保留14天日志”是两套标准某些恰好超过14天但今天有微量写入的滚动文件会被排除。实际用下来我更倾向于“显式排除活跃文件 最近的写入时间兜底”的组合而不是只依赖单一条件。6.2 清理前最好做一次空间统计我后来给脚本加了一个小功能清理前把将要删除的文件总大小先统计出来写入日志。这样每次执行后我能看到“预计释放多少”“实际释放多少”对后续磁盘容量规划很有帮助。你要想加可以用类似find $NACOS_LOG_DIR -type f -mtime ${RETAIN_DAYS} ${EXCLUDE_ARGS[]} -printf %s\n | awk {s$1} END {printf 可释放 %.2f MB\n, s/1024/1024}把它放在dry-run和真实执行两个分支的前面即可。这不算复杂但能让你对集群日志增长速度有量化的认知不会等到磁盘报警了才想起来查看。6.3 不要总依赖脚本也要关注日志滚动配置清理脚本毕竟是一种事后补救。如果想从根本上减少磁盘压力可以同时调整Nacos的日志滚动策略。比如access_log按天生成可以把它改成按大小和数量双维度滚动Nacos主日志默认可能不会频繁滚动也可以通过logback配置增加文件大小上限和保留历史文件数量。不过这些改动要结合实际日志排查需求不要为了省空间把日志滚动得过于激进否则出问题找日志时反而无从下手。另外如果Nacos节点非常多日志清理没必要一台台手动配。可以用Ansible、SaltStack这类工具把脚本分发到所有节点统一配置cron。这样一次调整整组机器都生效省下来的时间足够去处理真正复杂的问题了。我的习惯是每个月检查一次各节点的清理日志确认脚本一直在正常执行顺便看看有没有突然超出预期的日志增长。如果你也打算在生产环境用这个方案建议先从一两个节点开始跑两天确认没有误删、空间也正常释放之后再推广到全集群。稳定的清理策略胜过任何临时抱佛脚的手工操作。