機(jī)自啟:systemd配置與排障全指南)
如果你手頭有一臺(tái)跑著生產(chǎn)業(yè)務(wù)的 Linux 服務(wù)器上面部署了 Tomcat那你大概率經(jīng)歷過(guò)這種時(shí)刻機(jī)房斷電、服務(wù)器自動(dòng)重啟或者運(yùn)維例行升級(jí)內(nèi)核 reboot 之后同事過(guò)來(lái)說(shuō)“網(wǎng)站打不開(kāi)了”。SSH 上去一看MySQL、Nginx 都活著唯獨(dú) Java 進(jìn)程沒(méi)了——Tomcat 沒(méi)有開(kāi)機(jī)自啟。后來(lái)我專門整理過(guò)一套在 linux 上設(shè)置 tomcat 開(kāi)機(jī)啟動(dòng)的完整方法才把這件回頭看并不復(fù)雜、踩坑時(shí)卻相當(dāng)折磨人的事徹底理順。這篇內(nèi)容適合手里有 Linux 服務(wù)器、用 Tomcat 跑著 Java Web 項(xiàng)目、且經(jīng)受不起下次重啟后服務(wù)靜默消失的開(kāi)發(fā)或運(yùn)維朋友。文章不會(huì)只給你一段能用的配置我會(huì)把方案選型、單元文件每個(gè)參數(shù)的作用、驗(yàn)證方式、常見(jiàn)坑的完整排查鏈路以及配套的 JVM 參數(shù)、日志切割、多實(shí)例部署這些細(xì)節(jié)一并講清楚。1. 為什么 Linux 服務(wù)器上的 Tomcat 必須配置開(kāi)機(jī)自啟1.1 先說(shuō)一個(gè)讓我凌晨爬起來(lái)加班的場(chǎng)景我印象特別深有一年某個(gè)測(cè)試環(huán)境因?yàn)闄C(jī)房變更要重啟一臺(tái)老應(yīng)用服務(wù)器上面跑著 Tomcat 8.5 和幾個(gè)客戶對(duì)接用的 JSP 系統(tǒng)。重啟完成、網(wǎng)絡(luò)通了之后用客戶端工具一連——接口直接報(bào)連接超時(shí)。SSH 上去看Memcached 是起的Nginx 是起的唯獨(dú)java進(jìn)程一個(gè)都看不到Tomcat 壓根沒(méi)有跟著系統(tǒng)起來(lái)。當(dāng)時(shí)這臺(tái)機(jī)器的 Tomcat 是同事手動(dòng)startup.sh拉起來(lái)的配置里也沒(méi)有任何開(kāi)機(jī)自啟的邏輯。更要命的是沒(méi)人記得具體配置細(xì)節(jié)JAVA_HOME、CATALINA_BASE 全靠/etc/profile里的環(huán)境變量撐腰。我后來(lái)花了大半夜把環(huán)境全部摸了一遍才意識(shí)到配置 Tomcat 開(kāi)機(jī)啟動(dòng)這件事不只是寫(xiě)一行命令那么簡(jiǎn)單它需要把啟動(dòng)方式、環(huán)境變量、用戶權(quán)限、服務(wù)依賴全部理順。從那以后我在任何一臺(tái)要長(zhǎng)期跑 Tomcat 的 Linux 機(jī)器上做的第一件事就是先把開(kāi)機(jī)自啟搞定。1.2 開(kāi)機(jī)自啟的三種主流方案先做個(gè)了斷到目前為止我在不同年代、不同發(fā)行版的機(jī)器上實(shí)際用過(guò)的方案主要有三種方案原理適用場(chǎng)景維護(hù)成本systemd 服務(wù)單元通過(guò)/etc/systemd/system/tomcat.service聲明服務(wù)由 systemd 托管生命周期當(dāng)前幾乎所有主流發(fā)行版CentOS 7、Debian 8、Ubuntu 15低管理命令統(tǒng)一SysV init 腳本在/etc/init.d/放腳本通過(guò)update-rc.d或chkconfig注冊(cè)老系統(tǒng)、最小化容器、嵌入式環(huán)境中依賴順序全手動(dòng)crontab reboot用戶 crontab 里寫(xiě)reboot /path/startup.sh臨時(shí)性、實(shí)驗(yàn)性環(huán)境低但服務(wù)狀態(tài)和重啟策略幾乎沒(méi)有我見(jiàn)過(guò)不少生產(chǎn)環(huán)境的服務(wù)器還在用第三種方式就是reboot掛一條啟動(dòng)命令。你說(shuō)它不行它確實(shí)能在重啟后把 Tomcat 拉起來(lái)。但問(wèn)題在于沒(méi)有服務(wù)狀態(tài)管理如果想用systemctl查狀態(tài)、自動(dòng)重啟、看失敗次數(shù)、做資源隔離它什么都給不了。而且reboot的 cron 任務(wù)經(jīng)常踩環(huán)境變量坑——crontab 的執(zhí)行環(huán)境不是登錄式 Shell/etc/profile里定義的 JAVA_HOME 它根本讀不到啟動(dòng)一個(gè)失敗一個(gè)。SysV init 腳本在 CentOS 6 時(shí)代是絕對(duì)主力我現(xiàn)在也還會(huì)遇到基于老鏡像的機(jī)器。它的邏輯就是啟動(dòng)時(shí)按 rc.d 里的腳本順序調(diào)用#!/bin/sh從頭寫(xiě)一套 start/stop/restart 邏輯還要考慮鎖文件、PID 文件。能用但確實(shí)繁瑣而且很多長(zhǎng)期沒(méi)人維護(hù)的 init 腳本里連注釋都過(guò)期了。1.3 為什么我最終把生產(chǎn)環(huán)境的 Tomcat 全部切到 systemd如果機(jī)器發(fā)行版支持 systemd我的建議非常直接別折騰其他方案直接用 systemd。理由不復(fù)雜就三條。第一服務(wù)狀態(tài)可觀測(cè)。systemctl status tomcat看到的是active (running)、activating、failed這些明確狀態(tài)結(jié)合journalctl -u tomcat能看到服務(wù)日志定位速度比在/var/log/tomcat/里翻半天快得多。第二依賴和資源控制原生支持。我可以聲明Afternetwork.target等網(wǎng)絡(luò)就緒也可以設(shè)置MemoryLimit、CPUQuota對(duì) Tomcat 這種占內(nèi)存大頭、容易出現(xiàn)內(nèi)存泄漏的 Java 進(jìn)程這是實(shí)實(shí)在在的護(hù)欄。第三失敗重啟策略。Restarton-failure加上RestartSec讓 Tomcat 意外退出后自動(dòng)拉起。相比在 crontab 里反復(fù)跑pgrep去判斷進(jìn)程存在與否systemd 的處理方式又干凈又可靠。現(xiàn)在主流發(fā)行版幾乎都跑 systemd所以下面的配置全部圍繞 systemd 展開(kāi)同時(shí)穿插說(shuō)明一些老方案里的對(duì)應(yīng)做法。2. 手把手把 Tomcat 配成 systemd 開(kāi)機(jī)自啟服務(wù)2.1 動(dòng)手前先體檢目錄、賬號(hào)、路徑這三個(gè)事別跳過(guò)配置前我會(huì)先確認(rèn)三件事任何一件沒(méi)確認(rèn)清楚后面都會(huì)回來(lái)找麻煩。第一JDK 的真實(shí)路徑。在命令行里執(zhí)行which java readlink -f $(which java)后一條命令會(huì)把/usr/bin/java這類軟鏈接一路解析到真實(shí)路徑比如/usr/local/java/jdk-17/bin/java那 JAVA_HOME 就是/usr/local/java/jdk-17。千萬(wàn)別想當(dāng)然寫(xiě)成/usr/lib/jvm/...以readlink解析出來(lái)的為準(zhǔn)。第二Tomcat 安裝目錄和版本。通常我會(huì)用/usr/local/tomcat作為 CATALINA_HOME。要注意目錄里有沒(méi)有完整的bin、conf、lib結(jié)構(gòu)缺少文件大概率是以前解壓到一半或者路徑被改過(guò)。可以執(zhí)行/usr/local/tomcat/bin/version.sh看版本前提是 JAVA_HOME 已經(jīng)在當(dāng)前 Shell 導(dǎo)出。第三運(yùn)行賬號(hào)。強(qiáng)烈建議不要用 root 直接跑 Tomcat。一旦進(jìn)程被利用或者 war 包出問(wèn)題root 權(quán)限的 Java 進(jìn)程會(huì)造成更大的麻煩。我通常新建一個(gè)系統(tǒng)用戶useradd -r -s /bin/false tomcat chown -R tomcat:tomcat /usr/local/tomcat-r表示系統(tǒng)賬戶-s /bin/false讓它無(wú)法交互登錄對(duì) Tomcat 運(yùn)行來(lái)說(shuō)足夠。大家常問(wèn)的“Tomcat 啟動(dòng)閃退”有相當(dāng)一部分就是目錄權(quán)限不對(duì)啟動(dòng)腳本寫(xiě)不了日志或 PID 文件導(dǎo)致進(jìn)程退出。2.2 寫(xiě)一個(gè)能打的 tomcat.service 單元文件在/etc/systemd/system/tomcat.service創(chuàng)建文件這是 systemd 管理 Tomcat 的入口。我先把一份經(jīng)過(guò)生產(chǎn)驗(yàn)證的配置完整貼出來(lái)再做逐行解釋。[Unit] DescriptionApache Tomcat 9 Web Application Container Afternetwork.target Wantsnetwork.target [Service] Typeforking Usertomcat Grouptomcat EnvironmentJAVA_HOME/usr/local/java/jdk-17 EnvironmentCATALINA_HOME/usr/local/tomcat EnvironmentCATALINA_BASE/usr/local/tomcat EnvironmentCATALINA_PID/usr/local/tomcat/logs/tomcat.pid PIDFile/usr/local/tomcat/logs/tomcat.pid ExecStart/usr/local/tomcat/bin/startup.sh ExecStop/usr/local/tomcat/bin/shutdown.sh Restarton-failure RestartSec10 TimeoutStartSec60 TimeoutStopSec30 [Install] WantedBymulti-user.target這個(gè)文件里的[Unit]部分描述服務(wù)是什么、啟動(dòng)順序上的依賴[Service]部分是核心定義了進(jìn)程類型、用哪個(gè)用戶運(yùn)行、執(zhí)行什么腳本[Install]段決定了它掛載到哪個(gè)啟動(dòng)目標(biāo)multi-user.target表示這臺(tái)機(jī)器進(jìn)入正常的非圖形化運(yùn)行級(jí)別時(shí)啟動(dòng)。2.3 單元文件里這些參數(shù)每一行都在解決什么問(wèn)題很多人習(xí)慣網(wǎng)上復(fù)制一份配置能用就行出了問(wèn)題就完全不知道往哪里看。我建議把幾個(gè)關(guān)鍵參數(shù)吃透。Typeforking。Tomcat 的startup.sh啟動(dòng) Java 進(jìn)程后會(huì)立即返回退出真正的 Tomcat 主進(jìn)程是被 fork 出來(lái)的子進(jìn)程所以對(duì) systemd 來(lái)說(shuō)必須聲明Typeforking。如果不聲明或聲成simplesystemd 會(huì)把短暫駐留的啟動(dòng) Shell 當(dāng)成主服務(wù)導(dǎo)致?tīng)顟B(tài)判斷完全錯(cuò)亂。PIDFile 與 CATALINA_PID。systemd 需要跟蹤 Tomcat 實(shí)際主進(jìn)程的 PID 來(lái)管理它。Tomcat 自身通過(guò) CATALINA_PID 來(lái)寫(xiě) PID 文件。單元文件里我把 PID 指向/usr/local/tomcat/logs/tomcat.pid。這里有一個(gè)非常典型的坑很多發(fā)行版默認(rèn) Tomcat 配置里沒(méi)有導(dǎo)出 CATALINA_PID啟動(dòng)時(shí)根本沒(méi)有 PID 文件可讀systemd 在 stop 和 status 時(shí)的判斷會(huì)對(duì)不上。解決辦法就是顯式在服務(wù)里設(shè)置同時(shí)寫(xiě)上PIDFile讓 systemd 知道去哪里讀。Environment。這一步我要特別強(qiáng)調(diào)。systemd 通過(guò)ExecStart啟動(dòng)的服務(wù)不會(huì)讀取/etc/profile、~/.bashrc這些 Shell 初始化文件。不少人的 Tomcat 平時(shí)手動(dòng)啟動(dòng)沒(méi)問(wèn)題一配 systemd 就報(bào)JAVA_HOME is not defined correctly十有八九是這個(gè)原因。所以我把 JAVA_HOME、CATALINA_HOME、CATALINA_PID 統(tǒng)統(tǒng)用 Environment 寫(xiě)進(jìn)去不依賴任何全局 Shell 變量。After / Wants。Afternetwork.target表示網(wǎng)絡(luò)目標(biāo)完成后才會(huì)啟動(dòng) Tomcat避免在網(wǎng)絡(luò)接口沒(méi)有就緒時(shí)就去綁定端口。如果你的項(xiàng)目強(qiáng)依賴數(shù)據(jù)庫(kù)或 Redis也可以追加Aftermysql.service之類的聲明。注意After不產(chǎn)生啟動(dòng)依賴它只是順序約束要用Requires或Wants來(lái)真正拉起依賴服務(wù)最常見(jiàn)的組合就是Wants...加After...。Restart 策略。Restarton-failure搭配RestartSec10進(jìn)程異常退出時(shí) 10 秒后自動(dòng)重試。on-failure只處理非正常狀態(tài)如果你自己調(diào)systemctl stop tomcat它不會(huì)去重啟這也是我推薦這個(gè)值的原因。TimeoutStopSec。Tomcat 關(guān)閉時(shí)可能要優(yōu)雅停掉應(yīng)用和連接池時(shí)間給太短會(huì)被 systemd 強(qiáng)殺。我設(shè)了 30 秒大多數(shù) Web 應(yīng)用足夠。如果線上應(yīng)用有長(zhǎng)事務(wù)、連接池回收慢可以再調(diào)大一點(diǎn)。2.4 啟用自啟、驗(yàn)證生效一次做過(guò)配置寫(xiě)完以后執(zhí)行systemctl daemon-reload systemctl enable tomcat systemctl start tomcat每次修改 service 文件必須重新執(zhí)行daemon-reload這是新手最容易漏掉的動(dòng)作。enable會(huì)在/etc/systemd/system/multi-user.target.wants/下生成符號(hào)鏈接下次開(kāi)機(jī)時(shí)自動(dòng)啟動(dòng)start是立即啟動(dòng)并不是必須和enable一起執(zhí)行但先手動(dòng)啟動(dòng)一次能盡早發(fā)現(xiàn)配置錯(cuò)誤。啟動(dòng)后一定要做到驗(yàn)證閉環(huán)systemctl status tomcat systemctl is-enabled tomcat ss -lntp | grep 8080 curl -sI http://127.0.0.1:8080 | head -n 3如果看到enabled、狀態(tài)是active (running)、8080 端口監(jiān)聽(tīng)正常這一步就完成了。我自己的習(xí)慣還會(huì)再補(bǔ)一句journalctl -u tomcat -n 50 --no-pager確認(rèn)日志沒(méi)有異常然后找個(gè)時(shí)間真正 reboot 一次驗(yàn)證自啟——這一步必須有下面會(huì)解釋為什么。3. 配置自啟途中踩過(guò)的坑和完整排查鏈路3.1 環(huán)境變量丟失systemd 說(shuō)這不是它的鍋癥狀很典型手動(dòng)執(zhí)行startup.sh一切正常systemctl start tomcat后過(guò)幾秒狀態(tài)變成failedjournalctl -u tomcat里能看到類似Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is required to run this program原因就是我前面說(shuō)的systemd 啟動(dòng)的進(jìn)程不加載 Shell 環(huán)境文件。很多教程讓你把 JAVA_HOME 寫(xiě)進(jìn)/etc/profile這只會(huì)影響交互式 Shell不會(huì)影響 systemd。排查鏈路按順序走systemctl show tomcat -p Environment看 systemd 實(shí)際傳給進(jìn)程的環(huán)境變量。如果沒(méi)有 JAVA_HOME去單元文件里補(bǔ)上EnvironmentJAVA_HOME...絕對(duì)路徑以readlink -f $(which java)解析出來(lái)的為準(zhǔn)。XShell 里敲的java -version能用不代表 Tomcat 腳本能用腳本看的是 JAVA_HOME/JRE_HOME不是 PATH 里的 java 命令。3.2 PID 文件缺失狀態(tài)一直卡在 activating有一個(gè)場(chǎng)景我遇到不止一次Tomcat 其實(shí)已經(jīng)起來(lái)了8080 端口也在監(jiān)聽(tīng)業(yè)務(wù)能通但systemctl status顯示的卻是activating (start-pre)或者反復(fù)failed用systemctl stop還停不下來(lái)。根因基本可以鎖定在 PID 文件上。Typeforking加 PIDFile 時(shí)systemd 會(huì)用 PID 文件里的主進(jìn)程號(hào)來(lái)判斷服務(wù)是否成功啟動(dòng)。如果 CATALINA_PID 沒(méi)有導(dǎo)出startup.sh 不寫(xiě) PID 文件或者寫(xiě)的路徑進(jìn)程沒(méi)權(quán)限寫(xiě)就會(huì)卡在狀態(tài)判斷上。排查方式ls -l /usr/local/tomcat/logs/tomcat.pid cat /usr/local/tomcat/logs/tomcat.pid如果沒(méi)有這個(gè)文件檢查單元文件里 CATALINA_PID 是否配置如果文件存在但權(quán)限是 root檢查chown -R tomcat:tomcat /usr/local/tomcat因?yàn)?Tomcat 運(yùn)行用戶寫(xiě)不了這個(gè)文件同樣會(huì)導(dǎo)致 systemd 狀態(tài)不對(duì)。SELinux 開(kāi)啟時(shí)如果目錄上下文不對(duì)也可能拒絕寫(xiě)權(quán)限這個(gè)下面單獨(dú)說(shuō)。3.3 啟動(dòng)閃退排查先在前臺(tái)跑一次 catalina.sh配了自啟之后閃退日志往往被 systemd 收走反而不如直接前臺(tái)啟動(dòng)看得快。排查閃退我最順手的動(dòng)作是把 Tomcat 開(kāi)到前臺(tái)sudo -u tomcat /usr/local/tomcat/bin/catalina.sh runcatalina.sh run是前臺(tái)運(yùn)行日志直接打到終端報(bào)錯(cuò)一眼能看到。常見(jiàn)閃退原因端口被占用。ss -lntp | grep 8080看是不是別的服務(wù)占著或上一次啟動(dòng)的殘留進(jìn)程占著端口。war 包解壓失敗、webapps 下的應(yīng)用啟動(dòng)異常導(dǎo)致線程啟動(dòng)時(shí)報(bào)錯(cuò)退出。內(nèi)存不足或 JVM 參數(shù)非法比如-Xmx設(shè)置超出物理內(nèi)存啟動(dòng)直接報(bào)Could not reserve enough bytes for object heap。系統(tǒng)服務(wù)對(duì)進(jìn)程數(shù)量、文件句柄有限制journalctl -u tomcat里能看到句柄耗盡的跡象。定位到根因后按對(duì)應(yīng)的方式改配置改完再systemctl start tomcat驗(yàn)證。不要反復(fù)無(wú)腦重啟日志不會(huì)騙人。3.4 端口能通但外網(wǎng)訪問(wèn)不了防火墻與 SELinux 兩個(gè)兄弟自啟配好了本機(jī) curl 127.0.0.1:8080 有響應(yīng)外面怎么都訪問(wèn)不到這時(shí)候要懷疑防火墻和 SELinux。firewalld 在 CentOS/RHEL 系機(jī)器上默認(rèn)可能攔截了外部到 8080 的訪問(wèn)firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reloadSELinux 則是另一個(gè)容易忽略的環(huán)節(jié)。Tomcat 安裝到非標(biāo)準(zhǔn)目錄后進(jìn)程寫(xiě)日志、寫(xiě)臨時(shí)文件可能被 SELinux 策略擋住。先看當(dāng)前模式getenforce如果是Enforcing可以用restorecon -RF /usr/local/tomcat恢復(fù)目錄的 SELinux 上下文如果 Tomcat 確實(shí)需要連接外部網(wǎng)絡(luò)或端口可能需要設(shè)置對(duì)應(yīng)的 boolean。這里不展開(kāi)講 SELinux 策略撰寫(xiě)但要記住遇到進(jìn)程行為異常、日志中帶著Permission denied又說(shuō)不清權(quán)限問(wèn)題時(shí)八成和 SELinux 有關(guān)。注意不建議圖省事直接在生產(chǎn)上永久關(guān)閉 SELinux。造成的問(wèn)題應(yīng)該用策略去解決頂多臨時(shí)setenforce 0驗(yàn)證確認(rèn)后再定位是哪條策略。3.5 journalctl 里最值得記住的五類關(guān)鍵線索配自啟的排查基本繞不開(kāi)journalctl -u tomcat我總結(jié)過(guò)幾類最常出現(xiàn)的日志線索看到就知道往哪里查日志線索指向的問(wèn)題JAVA_HOME is not defined環(huán)境變量沒(méi)配置或路徑不對(duì)去 [Service] 段補(bǔ) Environmentpid file write failedPID 文件路徑不可寫(xiě)檢查目錄權(quán)限、SELinux 上下文Address already in use端口被占用找占用進(jìn)程或改 server.xml 端口Broken pipe、Connection reset通常是反向代理/防火墻/長(zhǎng)連接資源問(wèn)題優(yōu)先看 Nginx 和網(wǎng)絡(luò)側(cè)OutOfMemoryError、GC overhead limit exceededJVM 或堆內(nèi)存不足調(diào) CATALINA_OPTS每次排查都從日志入手按鏈路一層層往下通常能比東翻西找快一半時(shí)間。4. 開(kāi)機(jī)自啟配好之后順手把這些運(yùn)維細(xì)節(jié)也安排上4.1 JVM 參數(shù)放哪最干凈用 setenv.sh 而不是硬塞 service設(shè)置好自啟之后很多人會(huì)問(wèn)我想調(diào) JVM 堆內(nèi)存、GC 參數(shù)怎么辦兩個(gè)選擇一是全部懟在 service 文件的 Environment 里二是在/usr/local/tomcat/bin/下創(chuàng)建setenv.sh。Tomcat 每次啟動(dòng)都會(huì)自動(dòng)加載bin/setenv.sh里導(dǎo)出的環(huán)境變量。這是官方預(yù)留的擴(kuò)展機(jī)制比在 systemd 單元文件里寫(xiě)一長(zhǎng)串 Environment 干凈得多也更方便維護(hù)。我通常在 setenv.sh 里寫(xiě)export CATALINA_OPTS-Xms512m -Xmx2048m -XX:MetaspaceSize256m -XX:UseG1GC -XX:HeapDumpOnOutOfMemoryError注意兩個(gè)變量的區(qū)別CATALINA_OPTS只作用于 Tomcat 啟動(dòng)的 JVM不影響它內(nèi)部的輔助工具JAVA_OPTS會(huì)作用于所有基于 Java 的進(jìn)程包括啟動(dòng)腳本里的輔助程序。針對(duì) Tomcat 的堆和 GC 配置優(yōu)先用 CATALINA_OPTS。修改 setenv.sh 后不用重寫(xiě) servicesystemctl restart tomcat即可生效這個(gè)先后順序建議固定下來(lái)。4.2 catalina.out 無(wú)限增長(zhǎng)一個(gè) logrotate 配置解決Tomcat 的catalina.out會(huì)越滾越大尤其線上打了大量訪問(wèn)日志或異常棧時(shí)一個(gè)季度漲到幾個(gè) GB 很常見(jiàn)。有些項(xiàng)目把日志輸出在 catalina.out 里不處理遲早拖垮磁盤。我在生產(chǎn)上通常用 logrotate 做每日切割配置放在/etc/logrotate.d/tomcat/usr/local/tomcat/logs/catalina.out { daily rotate 7 copytruncate compress missingok notifempty }這里最需要注意的參數(shù)是copytruncate。因?yàn)?Java 進(jìn)程一直持有 catalina.out 這個(gè)文件句柄直接mv重命名會(huì)導(dǎo)致 Tomcat 繼續(xù)寫(xiě)舊句柄肉眼看到的是日志“消失”實(shí)際磁盤空間沒(méi)釋放。copytruncate先復(fù)制再清空原文件雖然極端情況下會(huì)丟一點(diǎn)點(diǎn)新日志但比重啟 Tomcat 去切日志劃算得多。4.3 多實(shí)例部署用模板化 service 文件省一半配置如果一臺(tái)服務(wù)器上要跑多個(gè) Tomcat 實(shí)例比如按項(xiàng)目名隔離的web1、web2完全不需要為每個(gè)實(shí)例各寫(xiě)一份 service 文件。systemd 支持模板單元tomcat.service通過(guò)%i引用實(shí)例名[Unit] DescriptionTomcat instance %i Afternetwork.target [Service] Typeforking Usertomcat Grouptomcat EnvironmentCATALINA_HOME/opt/tomcat-appserver EnvironmentCATALINA_BASE/opt/tomcat-%i EnvironmentCATALINA_PID/opt/tomcat-%i/logs/tomcat.pid PIDFile/opt/tomcat-%i/logs/tomcat.pid ExecStart/opt/tomcat-appserver/bin/startup.sh ExecStop/opt/tomcat-appserver/bin/shutdown.sh Restarton-failure [Install] WantedBymulti-user.target然后一行命令注冊(cè)并啟動(dòng)對(duì)應(yīng)實(shí)例systemctl enable tomcatweb1 systemctl start tomcatweb1需要注意多實(shí)例場(chǎng)景里CATALINA_HOME可以共用一份 Tomcat 程序但CATALINA_BASE必須每個(gè)實(shí)例各自獨(dú)立conf/、webapps/、logs/、temp/放在各自的 BASE 目錄下。這會(huì)讓自啟配置的復(fù)雜度大大下降也方便后續(xù)單獨(dú)重啟某個(gè)實(shí)例而不影響其他應(yīng)用。4.4 升級(jí) Tomcat 版本時(shí)自啟配置怎么平滑過(guò)渡Tomcat 大版本升級(jí)時(shí)最容易踩的雷就是解壓新版到新目錄然后忘了改自啟單元文件里指向 CATALINA_HOME 的路徑重啟回來(lái)發(fā)現(xiàn)啟動(dòng)的還是老版本。我的做法一般是先用軟鏈接固定路徑ln -s /usr/local/tomcat-9.0.85 /usr/local/tomcatservice 文件里始終只寫(xiě)/usr/local/tomcat升級(jí)時(shí)解壓新版目錄停服務(wù)換軟鏈接指向再啟動(dòng)。如果出問(wèn)題就回退軟鏈接整個(gè)過(guò)程可逆可控。升級(jí)前記得備份conf/server.xml、conf/web.xml和webapps下需要保留的業(yè)務(wù)應(yīng)用。Tomcat 10 把javax.*換成jakarta.*的兼容差異比較大升級(jí)時(shí)要么代碼同步遷移要么先確認(rèn)業(yè)務(wù)是否接受這個(gè)改動(dòng)。4.5 順便提一句安全加固manager 后臺(tái)別裸奔很多網(wǎng)站在 Tomcat 配置完成后conf/tomcat-users.xml里寫(xiě)死了一個(gè)manager-gui角色賬號(hào)用于后臺(tái)部署 war 包。我的建議是兩步走一是 manager 相關(guān)角色盡量不開(kāi)放公網(wǎng)訪問(wèn)至少在防火墻層或 Nginx 層限制來(lái)源 IP二是如果確實(shí)需要遠(yuǎn)程管理配合 Tomcat 的 Valve 配置做來(lái)源地址白名單而不要只靠一個(gè)弱口令頂著。這個(gè)不屬于自啟范圍但既然聊到線上的 Tomcat順手提醒總比出了安全事件再補(bǔ)要省心。5. 我總結(jié)的幾個(gè)實(shí)操習(xí)慣照著做能少走很多彎路配置和排障都講完了最后分享幾個(gè)我自己養(yǎng)成的習(xí)慣都是踩過(guò)坑后留下的。第一配置完自啟后一定要找時(shí)間重啟一次機(jī)器驗(yàn)證。很多人配完enable就覺(jué)得萬(wàn)事大吉等真重啟才發(fā)現(xiàn)某個(gè)依賴順序或環(huán)境變量在純引導(dǎo)環(huán)境里根本不生效。重啟后確認(rèn)進(jìn)程在、端口在、systemctl status是 active才算完成閉環(huán)。第二每次都先daemon-reload再改狀態(tài)。改 service 文件卻不 reloadsystemd 仍在用舊配置這會(huì)浪費(fèi)大量時(shí)間。我自己的流程是改完文件 →systemctl daemon-reload→systemctl restart tomcat→journalctl -u tomcat -n 30收尾。第三寫(xiě)文檔比記憶可靠。Tomcat 版本、JAVA_HOME 路徑、實(shí)例名、端口、依賴關(guān)系這些信息散落在不同人腦子里最危險(xiǎn)。我會(huì)在機(jī)器上放一個(gè)/etc/systemd/system/README-tomcat.txt或者同步到私有維基記錄每次變更前后的狀態(tài)和原因下次不管誰(shuí)接手都能快速接上。第四盡量用 systemd 統(tǒng)一管理減少“手藝人腳本”。手寫(xiě) shell 腳本輪詢進(jìn)程、在 rc.local 里追加啟動(dòng)命令這些方式應(yīng)急時(shí)可以但長(zhǎng)期維護(hù)下來(lái)狀態(tài)不可觀測(cè)、失敗不自愈的代價(jià)會(huì)越來(lái)越高。把 Tomcat、Nginx、MySQL 這些服務(wù)通通納入 systemd 視角后一條命令就能看清整臺(tái)機(jī)器的服務(wù)圖譜排障思路也會(huì)清晰很多。配置 Tomcat 開(kāi)機(jī)自啟本質(zhì)上并不難難的是把方案選對(duì)、把細(xì)節(jié)做扎實(shí)。希望這篇實(shí)踐筆記能讓你下次面對(duì)“重啟后服務(wù)消失”的時(shí)候不再手忙腳亂。