
1. 項目概述與核心價值在中小型企業的實際運維場景里或者個人開發者手頭只有一臺性能尚可的服務器時我們常常會面臨一個非常現實的需求如何在這一臺物理或云服務器上同時運行多個基于Tomcat的Java Web應用。這可能是為了隔離不同客戶的項目、區分測試與生產環境或者僅僅是最大化利用服務器資源。直接在一份Tomcat里部署多個應用雖然可行但在日志管理、應用重啟、資源隔離和版本依賴方面會帶來諸多不便。因此學會在一臺服務器上部署多個獨立的Tomcat實例每個實例承載一個或多個項目是一項非常實用且基礎的服務器管理技能。這篇文章我將結合自己多年的部署經驗為你拆解從零開始在一臺Linux服務器以CentOS 7為例上部署多個Tomcat及項目的完整流程。整個過程會涵蓋環境準備、Tomcat實例的獨立配置、項目部署、服務化管理以及最重要的——端口沖突的解決與日常運維技巧。無論你是剛接觸服務器部署的開發者還是需要優化現有部署結構的運維人員這篇手把手的圖文指南都能提供清晰的路徑。2. 環境準備與規劃在開始動手之前清晰的規劃是避免后續混亂的關鍵。我們需要從軟件環境和部署結構兩方面做好準備。2.1 基礎軟件環境檢查與安裝首先確保你的服務器已經具備了運行Java Web應用的基礎環境。通過SSH連接到你的服務器執行以下檢查與安裝步驟。Java環境JDKTomcat運行依賴于Java環境。推薦使用JDK 8或JDK 11這些長期支持版本。# 檢查是否已安裝Java及其版本 java -version如果未安裝可以使用yum進行安裝以OpenJDK 8為例# 搜索可用的JDK包 yum search java-1.8.0-openjdk # 安裝JDK開發包包含JRE yum install -y java-1.8.0-openjdk-devel安裝后再次驗證java -version和javac -version。規劃安裝目錄為了管理清晰建議為所有Tomcat實例創建一個統一的父目錄。例如我習慣在/opt下創建tomcats目錄。mkdir -p /opt/tomcats cd /opt/tomcats后續下載的Tomcat壓縮包和解壓后的各個實例都將放在這個目錄下。2.2 部署結構規劃這是核心步驟。我們需要決定部署多少個Tomcat實例以及如何為它們分配唯一的標識和資源。確定實例數量與命名假設我們需要部署兩個Tomcat實例分別運行“客戶門戶系統”和“內部管理系統”。我們可以這樣命名tomcat-portal: 對應客戶門戶。tomcat-internal: 對應內部系統。 清晰的命名有助于后續的腳本編寫和日志查看。端口規劃表至關重要單個Tomcat默認使用8080端口HTTP、8005端口SHUTDOWN和8009端口AJP。多實例必須避免端口沖突。我們需要為每個實例規劃一套唯一的端口。建議制作一個表格實例名稱HTTP端口SHUTDOWN端口AJP端口備注tomcat-portal808080058009默認實例可保留tomcat-internal808180068010第二個實例全部遞增tomcat-app3808280078011預留第三個實例端口注意在生產環境中如果服務器內存充足建議為每個實例的SHUTDOWN端口也設置不同的值這是Tomcat官方推薦的做法可以防止誤操作關閉錯誤的實例。AJP端口通常在使用Apache HTTPD等前端代理時才需要如果只用Tomcat可以暫時不修改但為了規范性建議一并修改。目錄結構預覽規劃好后的/opt/tomcats目錄結構將如下所示/opt/tomcats/ ├── apache-tomcat-9.0.xx.tar.gz # 下載的原始壓縮包 ├── tomcat-portal/ # 實例一目錄 │ ├── bin/ # 啟動腳本等 │ ├── conf/ # 配置文件需修改 │ ├── logs/ # 實例專屬日志 │ ├── webapps/ # 部署項目war包在此 │ └── ... └── tomcat-internal/ # 實例二目錄 ├── bin/ ├── conf/ ├── logs/ ├── webapps/ └── ...3. 下載、解壓與多實例配置有了清晰的規劃我們就可以開始具體的配置工作了。3.1 下載與解壓Tomcat首先從Apache官網下載Tomcat的二進制壓縮包tar.gz格式。以Tomcat 9為例# 進入規劃好的目錄 cd /opt/tomcats # 使用wget下載請替換為官網最新的穩定版鏈接 wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.xx/bin/apache-tomcat-9.0.xx.tar.gz # 解壓壓縮包 tar -zxvf apache-tomcat-9.0.xx.tar.gz # 解壓后得到目錄 apache-tomcat-9.0.xx3.2 創建第一個Tomcat實例tomcat-portal我們將解壓后的目錄復制一份作為我們的第一個實例并以其命名。# 復制并重命名創建第一個實例 cp -r apache-tomcat-9.0.xx tomcat-portal現在進入tomcat-portal的配置目錄開始修改端口。cd tomcat-portal/conf # 使用vi或nano編輯 server.xml vi server.xml在server.xml文件中找到以下三個關鍵連接器Connector配置HTTP/1.1 Connector(默認端口8080):Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /因為我們規劃tomcat-portal使用默認端口所以這里不需要修改。如果你想讓它使用其他端口比如8080被占用可以在這里修改。AJP Connector(默認端口8009):Connector port8009 protocolAJP/1.3 redirectPort8443 /同樣按規劃我們保留8009不需要修改。Server Port(SHUTDOWN端口默認8005):Server port8005 shutdownSHUTDOWN這是Tomcat監聽關閉指令的端口。按規劃保留8005不需要修改。第一個實例配置完成。它將繼續使用Tomcat的默認端口組8080, 8005, 8009。3.3 創建并配置第二個Tomcat實例tomcat-internal現在創建第二個實例并修改其所有端口避免與第一個實例沖突。# 回到tomcats根目錄 cd /opt/tomcats # 復制原始解壓目錄創建第二個實例 cp -r apache-tomcat-9.0.xx tomcat-internal # 進入其配置目錄 cd tomcat-internal/conf vi server.xml在第二個實例的server.xml中根據我們之前的規劃表進行修改修改HTTP端口為8081:Connector port8081 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /修改AJP端口為8010:Connector port8010 protocolAJP/1.3 redirectPort8443 /修改SHUTDOWN端口為8006:Server port8006 shutdownSHUTDOWN保存并退出編輯器。至此第二個實例的端口配置已完成它將在8081端口提供HTTP服務。3.4 配置技巧與注意事項環境變量隔離每個Tomcat實例的bin/catalina.sh腳本會讀取CATALINA_HOME和CATALINA_BASE環境變量。在我們這種復制目錄的部署方式中每個實例的目錄本身就是其CATALINA_BASE而CATALINA_HOME可以指向原始解壓目錄或留空腳本會自動處理。這種結構實現了天然的隔離。內存參數調整如果服務器內存有限或者應用本身比較吃內存需要為每個Tomcat實例單獨設置JVM內存參數。編輯每個實例bin/catalina.sh文件在文件開頭附近注釋之后添加類似如下的設置# 在 bin/catalina.sh 中添加 export JAVA_OPTS-server -Xms512m -Xmx1024m -XX:MetaspaceSize128m -XX:MaxMetaspaceSize256m這樣可以為該實例設定初始堆內存512MB最大堆內存1024MB。請根據服務器總內存和應用需求合理分配確保所有實例總和不超過物理內存。日志目錄每個實例的logs目錄是獨立的這非常有利于排查問題。你可以通過tail -f tomcat-portal/logs/catalina.out來單獨跟蹤某個應用的輸出。4. 項目部署與啟動測試配置好Tomcat實例后下一步就是將我們的Web應用通常是WAR包部署進去并測試啟動。4.1 部署WAR包假設我們有兩個WAR包customer-portal.war和internal-system.war。# 將WAR包分別放入對應實例的webapps目錄下 # 可以使用SCP等工具從本地上傳這里假設已上傳到服務器/tmp目錄 cp /tmp/customer-portal.war /opt/tomcats/tomcat-portal/webapps/ cp /tmp/internal-system.war /opt/tomcats/tomcat-internal/webapps/Tomcat在啟動時會自動解壓webapps目錄下的WAR文件。你也可以將WAR包解壓成一個目錄再放入這樣可以預先修改一些配置文件。4.2 啟動Tomcat實例我們進入每個實例的bin目錄使用腳本來啟動。# 啟動第一個實例 (tomcat-portal) cd /opt/tomcats/tomcat-portal/bin ./startup.sh # 查看啟動日志確認無報錯 tail -f ../logs/catalina.out # 按 CtrlC 退出日志跟蹤 # 啟動第二個實例 (tomcat-internal) cd /opt/tomcats/tomcat-internal/bin ./startup.sh tail -f ../logs/catalina.out4.3 驗證部署結果啟動完成后通過瀏覽器或curl命令訪問服務驗證部署是否成功。# 驗證第一個實例端口8080 curl http://localhost:8080/customer-portal/ # 或者訪問服務器IP: http://你的服務器IP:8080/customer-portal/ # 驗證第二個實例端口8081 curl http://localhost:8081/internal-system/如果看到應用的首頁或者正常的HTTP響應而非404錯誤說明部署成功。4.4 防火墻配置如果服務器開啟了防火墻如firewalld需要放行我們使用的端口。# 放行8080和8081端口 firewall-cmd --permanent --add-port8080/tcp firewall-cmd --permanent --add-port8081/tcp # 重載防火墻規則 firewall-cmd --reload # 查看已放行的端口 firewall-cmd --list-ports5. 將Tomcat配置為系統服務Systemd使用startup.sh和shutdown.sh手動管理在服務器重啟后不會自動啟動也不便于集中管理。最佳實踐是將每個Tomcat實例配置為獨立的系統服務Systemd Service。5.1 為每個實例創建Service文件為tomcat-portal創建服務文件vi /etc/systemd/system/tomcat-portal.service將以下內容粘貼進去請務必根據你的實際路徑修改CATALINA_HOME和JAVA_HOME[Unit] DescriptionApache Tomcat 9 Instance - Portal Aftersyslog.target network.target [Service] Typeforking # 設置環境變量JAVA_HOME指向你的JDK安裝目錄 EnvironmentJAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk # CATALINA_HOME 可以指向原始Tomcat目錄也可以指向實例目錄。這里指向實例目錄。 EnvironmentCATALINA_HOME/opt/tomcats/tomcat-portal EnvironmentCATALINA_BASE/opt/tomcats/tomcat-portal EnvironmentCATALINA_PID${CATALINA_BASE}/temp/tomcat.pid # 可以在這里設置JVM參數覆蓋catalina.sh中的設置 EnvironmentJAVA_OPTS-server -Xms512m -Xmx1024m -Djava.awt.headlesstrue # 啟動命令 ExecStart${CATALINA_HOME}/bin/startup.sh # 停止命令 ExecStop${CATALINA_HOME}/bin/shutdown.sh Userroot Grouproot UMask0007 RestartSec10 Restartalways [Install] WantedBymulti-user.target同理為tomcat-internal創建服務文件/etc/systemd/system/tomcat-internal.service內容基本相同只需修改Description、CATALINA_HOME、CATALINA_BASE以及JAVA_OPTS如果需要不同的內存配置。5.2 啟用并管理服務創建好服務文件后重新加載systemd配置然后啟用并啟動服務。# 重新加載systemd配置 systemctl daemon-reload # 啟用tomcat-portal服務開機自啟 systemctl enable tomcat-portal.service # 啟動tomcat-portal服務 systemctl start tomcat-portal.service # 查看服務狀態 systemctl status tomcat-portal.service # 對tomcat-internal執行同樣操作 systemctl enable tomcat-internal.service systemctl start tomcat-internal.service systemctl status tomcat-internal.service現在你可以使用標準的systemctl命令來管理你的Tomcat實例了systemctl start|stop|restart|status tomcat-portaljournalctl -u tomcat-portal -f# 查看該服務的實時日志6. 高級配置、優化與日常運維部署完成并能正常運行只是第一步要讓多實例環境穩定、高效還需要一些進階配置和運維技巧。6.1 會話共享與負載均衡考慮雖然本文重點是多實例部署在同一服務器但了解其擴展方向很重要。如果未來流量增長需要橫向擴展這兩個實例可以很容易地加入到負載均衡器如Nginx后面。在Nginx中可以這樣配置 upstreamhttp { upstream backend_servers { # 指向同一臺機器的不同端口 server 127.0.0.1:8080 weight1; server 127.0.0.1:8081 weight1; # 如果未來加了新服務器直接添加即可 # server 192.168.1.102:8080 weight1; } server { listen 80; server_name yourdomain.com; location / { proxy_pass http://backend_servers; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }注意如果應用使用了Session默認情況下Session存儲在各自Tomcat的內存中在負載均衡時會導致用戶會話丟失。此時需要配置Session共享常見方案有Session粘滯Sticky Session在負載均衡器設置讓同一用戶的請求始終轉發到同一臺Tomcat。Session復制在Tomcat的server.xml中配置Cluster讓多個Tomcat實例間同步Session數據對網絡和性能有影響。外部Session存儲將Session存入Redis或數據庫等外部存儲這是最推薦的方式徹底解耦。Spring Boot項目可以輕松集成Spring Session來實現。6.2 日志管理與切割Tomcat默認的日志不會自動切割長期運行會導致catalina.out文件巨大。我們可以使用Linux自帶的logrotate工具來管理。創建日志切割配置文件vi /etc/logrotate.d/tomcat-portal內容如下/opt/tomcats/tomcat-portal/logs/catalina.out { copytruncate daily rotate 30 compress missingok dateext size 100M create 644 root root }同樣為tomcat-internal創建一份。這樣配置后日志會按天或大小100MB進行切割保留30份舊的會被壓縮。6.3 性能監控與JVM調優對于生產環境監控是必不可少的。啟用JMX遠程監控在某個實例的bin/catalina.sh的JAVA_OPTS中添加JMX參數即可使用JConsole或VisualVM進行遠程監控。export JAVA_OPTS$JAVA_OPTS -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port9090 \ -Dcom.sun.management.jmxremote.sslfalse \ -Dcom.sun.management.jmxremote.authenticatefalse \ -Djava.rmi.server.hostname你的服務器IP安全警告上述配置關閉了SSL和認證僅適用于內網測試環境。生產環境務必啟用SSL和強密碼認證。GC日志分析在JAVA_OPTS中添加GC日志參數有助于分析內存使用情況和垃圾回收問題。export JAVA_OPTS$JAVA_OPTS -XX:PrintGCDetails -XX:PrintGCDateStamps -XX:PrintGCTimeStamps -Xloggc:../logs/gc.log6.4 自動化部署腳本對于頻繁更新的項目可以編寫簡單的Shell腳本實現一鍵停止、備份、部署、啟動。創建一個deploy-portal.sh腳本#!/bin/bash TOMCAT_HOME/opt/tomcats/tomcat-portal WAR_SOURCE/data/new-releases/customer-portal.war BACKUP_DIR/data/backups/$(date %Y%m%d_%H%M%S) echo “停止Tomcat服務...” systemctl stop tomcat-portal echo “備份舊應用...” mkdir -p $BACKUP_DIR cp -r $TOMCAT_HOME/webapps/customer-portal* $BACKUP_DIR/ 2/dev/null || true echo “清理工作目錄...” rm -rf $TOMCAT_HOME/webapps/customer-portal* rm -rf $TOMCAT_HOME/work/Catalina/localhost/customer-portal echo “部署新WAR包...” cp $WAR_SOURCE $TOMCAT_HOME/webapps/ echo “啟動Tomcat服務...” systemctl start tomcat-portal echo “等待應用啟動...” sleep 30 # 可以添加一個健康檢查例如curl應用的健康端點 if curl -s --max-time 10 http://localhost:8080/customer-portal/health /dev/null; then echo “部署成功” else echo “部署可能存在問題請檢查日志$TOMCAT_HOME/logs/catalina.out” fi記得給腳本執行權限chmod x deploy-portal.sh。7. 常見問題排查與解決方案實錄在實際操作中你幾乎一定會遇到一些問題。這里記錄了幾個最常見的問題和我的排查思路。7.1 端口沖突問題問題現象啟動第二個Tomcat實例時失敗日志中報java.net.BindException: Address already in use。排查步驟確認錯誤端口查看日志明確是哪個端口被占用例如8081、8006。查找占用進程使用netstat或lsof命令。# 查看8081端口被誰占用 netstat -tlnp | grep :8081 # 或 lsof -i :8081分析原因被其他服務占用可能是其他軟件如Nginx、另一個Tomcat占用了該端口。如果是修改沖突方的端口或停止該服務。被自己的僵尸進程占用有時Tomcat非正常關閉進程未完全退出。使用ps -ef | grep tomcat找到殘留進程用kill -9 PID強制結束。配置未生效確認修改的是正確的server.xml文件并且重啟了Tomcat。7.2 應用啟動失敗但Tomcat本身正常問題現象訪問應用URL返回404或500錯誤但Tomcat默認首頁http://ip:port可以訪問。排查步驟檢查部署位置確認WAR包或應用目錄是否在正確的webapps目錄下。查看應用專屬日志Tomcat會在logs目錄下為每個應用生成一個日志文件如localhost_customer-portal_yyyy-MM-dd.log。這里是應用初始化失敗信息的第一現場。查看Catalina日志重點查看catalina.out和localhost.log尋找SEVERE或ERROR級別的日志通常會有詳細的堆棧跟蹤。常見原因數據庫連接失敗檢查應用配置的數據庫IP、端口、用戶名密碼。依賴缺失檢查WEB-INF/lib下是否有所有必需的JAR包。對于Spring Boot項目通常一個可執行JAR或WAR包已包含所有依賴。內存不足檢查catalina.out是否有OutOfMemoryError。需要調整JAVA_OPTS中的-Xmx參數。文件權限問題確保Tomcat進程用戶如root或tomcat用戶對應用目錄、臨時目錄有讀寫權限。7.3 服務無法開機自啟問題現象配置了systemd服務并enable了但服務器重啟后Tomcat沒有啟動。排查步驟檢查服務狀態systemctl status tomcat-portal看是否顯示active (running)。查看服務日志journalctl -u tomcat-portal -xe這里會有詳細的啟動失敗信息。常見原因依賴服務未就緒在tomcat-portal.service的[Unit]部分可以增加對網絡或數據庫的依賴例如Afternetwork.target mysqld.service。環境變量問題systemd服務文件中的JAVA_HOME路徑可能不正確。使用which java和readlink -f $(which java)找到準確的JAVA_HOME路徑。啟動超時如果應用啟動非常慢可能超過systemd默認的超時時間默認90秒。可以在[Service]部分增加TimeoutStartSec300來延長等待時間。7.4 如何優雅地更新應用直接替換WAR包Tomcat會嘗試熱部署但在復雜應用中容易出錯。我推薦的流程是通過systemctl停止服務systemctl stop tomcat-portal。備份舊應用和日志可選但建議。刪除webapps下的舊應用目錄和WAR包同時刪除work/Catalina/localhost下對應的緩存目錄。放入新的WAR包。通過systemctl啟動服務systemctl start tomcat-portal。通過tail -f監控日志直到看到應用啟動成功的標志。這種“停止-清理-部署-啟動”的模式比熱部署更穩定對于絕大多數應用來說幾十秒的停機時間是可以接受的。如果要求零停機則需要設計藍綠部署或滾動更新方案這通常需要結合負載均衡器和更復雜的腳本。