运维新手必避的5大服务器配置坑,第3个90%人中招
|
去年8月,凌晨三点十七分,我盯着屏幕右下角那个跳动的「23:57」——哦不对,是凌晨,时区切错了。测试机桌上排成一排,四台,三台亮着,一台黑着,黑的那台电源灯是绿的,但SSH死活不通,我拿手指甲刮了刮网口金属片,没灰,但莫名就想去刮。 第3个坑:systemd服务单元里写ExecStart=/usr/bin/java -jar app.jar,后面不跟--spring.profiles.active=prod,也不加Restart=always,更不设WorkingDirectory。90%人中招。我敢打包票。不是统计,是去年八月那一周,我们收了七份上线工单,五份在UAT环境跑两小时后静默退出,查journalctl -u xxx | tail -20,全是「Process exited, code=exited, status=143」——连OOM都不是,是java进程被kill -15之后,JVM连日志都懒得刷一行就躺平了。 我原以为是Java版本问题,把OpenJDK 11换成17,结果第二天监控告警又响,还是同一台机器。后来发现是systemd把进程挂到cgroup v1 root.slice下面,而应用自己启了个临时线程池,没配ThreadFactory.setName,结果在systemd kill前0.3秒,这线程池往/dev/stdout打了一串乱码,日志文件里只留下半行「[main] c.c.a.App : Startin」,然后戛然而止。 凉了的外卖还搁在键盘右边,铝箔盒底儿朝天,里面剩两块土豆,浮着一层油。我没动它。产品经理站在工位隔断外头,左手捏着iPad,右手食指悬在AirDrop图标上空没点下去,说:“就这么点东西,测完就能上。”他衬衫袖子挽到小臂中间,露出一块浅褐色胎记,像块陈年咖啡渍。我没接话,点了三下Enter,重启服务。没用。第四次我删掉整个[Unit]段,重写,把Type=notify改成simple,才活过来。但没人教新人这个——没人告诉ta,notify不是让你去写RuntimeDirectory=,而是逼你在代码里调System.exit()之前,先发个sd_notify("READY=1")。写了等于白写,因为Spring Boot默认压根不集成libsystemd。 UI走查时的沉默。那天下班前最后一轮,产品、前端、测试围在会议室白板前,我蹲在投影仪线缆堆里改Ansible playbook。他们讨论按钮圆角该是4px还是6px,声音很轻,但“6”字飘出来的时候,我手机在裤兜里震了一下——地铁上掏手机复现这个问题,是在回龙观站进站闸机刚刷完卡、还没抬脚那半秒。我划开微信,是SRE群里有人贴图:某银行客户环境,同样配置,同样静默挂,只不过人家用的是Zing JVM,所以exit时还多吐一行「[INFO] Zing GC paused for 0ms」,成了救命稻草。可这有啥用?Zing要钱,我们用OpenJDK——但OpenJDK不告诉你,-XX:+ExitOnOutOfMemoryError 这种参数,遇到systemd kill -15也照旧失效。 失败案例堆得比我工位抽屉还满:新同事给Redis配maxmemory-policy volatile-lru,结果线上缓存穿透瞬间打穿数据库;另一个给Nginx upstream写ip_hash却忘了keepalive timeout和upstream timeout差了三倍,连接池疯涨;还有个在/etc/crontab里直接写 root curl -s http://localhost:8080/health > /dev/null,结果每天00:00:00整点,所有实例同时发探针,SLB层直接熔断。这些都没第3个坑隐蔽——它不报错,不报警,不丢数据,就是凌晨四点你醒来摸手机看企业微信,弹出三条告警,点开,全是一模一样的log tail:“Started Application in 3.2 seconds (JVM running for 3.2)”,然后就没然后了。
文章配图,仅供参考 优胜劣汰?对。真不是狠话。去年八月我们团队转正三人,两人留,一人没过试用期。留下的俩,一个改完systemd unit之后,顺手把journald rate limit从1000/30s提到5000/30s,防日志淹没;另一个在CI阶段就把systemd-analyze verify xxx.service跑进流水线。淘汰的那个?他重装了三次系统,最后发现是SELinux开着,而他写的selinux-policy-modules根本没编译进内核模块——但他始终觉得是Java问题。这事我不说了,说多了像刻薄。但你说这算不算筛选?算。系统不筛你,时间筛,时间不筛,客户筛。 上周我又看到个case:K8s里用Deployment跑Spring Boot,initContainer里chown -R 1001:1001 /app,结果pod启动后主容器进不去,因为Java进程以user 1001跑,但jvm.pid写在/tmp里,而/tmp挂载时用了noexec,nodev——但这不是重点。重点是,这人在GitHub issue里抄别人答案,把startupProbe改成exec: ["sh", "-c", "ps aux | grep java"],硬扛三分钟才探测成功。我截图发给运维老李,他回我三个字:“再练练。” 这事我暂时没法往下推了。我们组正在把systemd模板拆成三类:通用型、Spring Boot型、Netty专用型,每个都带注释,注释第一行写「别删WorkingDirectory,否则log可能落到/root目录下」。但我知道,只要文档没变成pre-commit hook里的check,就永远有人跳进去。我试过用shellcheck做CI校验,结果新同事把ExecStart=那一行用\\折行,shellcheck直接绕过去了。这我能咋办?让他背systemd.directives手册?算了。明天早会我说一句:“所有java服务,先配RestartSec=5,不管有没有Restart=always”。就这一句。说完我喝一口凉透的美式,杯子底儿有圈褐色渍,跟产品经理小臂上那块胎记差不多深。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


17年云运维老兵亲授:SQL Server运维避坑实战指南
嵌入式工程师建站实战:裸机到高可用全链路运维
SQL Server DBA实战手册:微服务时代的数据库运维精要
网站上线流量归零?运维盲区正 silently 吞食用户
网站运维者必知的5大云安全防护盲区
安全防线崩塌?3大运维盲区正吞噬防御体系
Linux运维老手也常踩的5个Unix环境搭建坑
