Nginx+Tomcat多实例部署实战:单服务器多项目配置与优化指南
1. 项目概述与核心价值在中小型企业的IT基础设施演进过程中一个非常典型的场景是随着业务发展从最初单一应用的单机部署逐渐演变为需要在同一台服务器上运行多个独立的Java Web应用。这些应用可能分属不同部门或者是一个大项目下的不同微服务模块。直接的想法可能是为每个应用购买一台新服务器但这无疑会带来巨大的硬件成本和运维复杂度。更经济、更高效的做法就是在一台物理机或虚拟机上通过配置多个Tomcat实例并利用Nginx进行统一的流量分发和管理实现“一个IP多个端口多个项目”的并行部署。这听起来像是基础操作但实际操作中从端口规划、Tomcat实例隔离、到Nginx配置的精准转发每一步都有不少细节需要打磨。比如如何避免多个Tomcat实例的环境变量冲突如何优雅地管理各自的日志而不至于混乱Nginx的upstream配置怎样写才能兼顾健康检查和负载均衡哪怕目前是单机这些细节处理不好轻则应用启动失败端口占用重则服务不稳定排查问题如同大海捞针。我经历过多次从零搭建这类环境也处理过不少因为配置不当导致的线上问题。今天我就以一个实战者的角度抛开那些教科书式的理论把从服务器准备、到多个Tomcat实例的独立部署与配置、再到Nginx反向代理的完整链条连同我踩过的坑和总结的最佳实践一次性讲清楚。无论你是运维工程师、后端开发者还是需要自己搭建测试环境的技术负责人这篇教程都能给你提供一份可直接“抄作业”的详细方案。2. 环境规划与核心思路拆解在动手之前清晰的规划是成功的一半。盲目操作只会导致后续的配置混乱和难以维护。2.1 部署架构设计我们的目标架构非常明确一台Linux服务器以CentOS 7.x为例一个对外的IP地址例如192.168.1.100。在这台服务器上我们将部署两个独立的Tomcat实例分别运行项目A和项目B。Nginx作为唯一的对外入口监听标准的HTTP 80端口。用户访问不同的域名或路径由Nginx根据规则将请求转发到对应Tomcat实例的监听端口上。具体规划如下服务器IP192.168.1.100Nginx监听80端口。Tomcat实例1 (项目A)安装目录/usr/local/tomcat-8081监听8081端口应用部署在webapps/ROOT或自定义目录。Tomcat实例2 (项目B)安装目录/usr/local/tomcat-8082监听8082端口应用部署在webapps/ROOT或自定义目录。注意这里的关键是“实例隔离”。不是简单复制webapps目录而是复制整个Tomcat安装目录并修改其核心配置文件确保每个实例的端口、日志、临时文件等完全独立互不干扰。2.2 工具与软件版本选择操作系统CentOS 7.9 Minimal。选择稳定版减少未知兼容性问题。JDKOpenJDK 1.8。这是Tomcat 8/9的黄金搭档社区支持最好。建议使用yum安装方便管理。TomcatApache Tomcat 9.0.x。选择长期支持版本避免使用过于前沿或已停止维护的版本。我们将从官网下载二进制包tar.gz进行安装这样灵活性最高。NginxNginx 1.20.x。同样选择稳定版。我们将通过官方Yum仓库安装便于后续升级和管理。为什么选择源码/二进制包安装Tomcat而用Yum安装Nginx因为Tomcat需要多实例每个实例的配置尤其是端口需要定制化修改二进制包解压即用方便复制和修改。而Nginx作为反向代理配置通常集中在nginx.conf及其包含的conf.d/*.conf文件中用Yum安装服务管理更规范systemctl。3. 基础环境与Tomcat多实例部署有了规划我们开始一步步实施。首先准备基础环境然后部署两个Tomcat实例。3.1 服务器基础环境准备以root用户或具有sudo权限的用户登录服务器。更新系统并安装必要工具yum update -y yum install -y wget vim net-tools安装JDKyum install -y java-1.8.0-openjdk-devel安装完成后验证版本java -version # 输出应类似openjdk version 1.8.0_412检查JAVA_HOME通常Yum安装的OpenJDK路径是/usr/lib/jvm/java-1.8.0-openjdk。如果需要可以将其添加到环境变量但对于Tomcat来说只要java命令可用即可。3.2 部署第一个Tomcat实例端口8081我们将以第一个实例为模板详细说明步骤第二个实例则快速带过。下载并解压Tomcatcd /usr/local/src wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.85/bin/apache-tomcat-9.0.85.tar.gz tar -zxvf apache-tomcat-9.0.85.tar.gz -C /usr/local/创建第一个实例目录并复制cd /usr/local mv apache-tomcat-9.0.85 tomcat-8081 # 重命名以端口区分配置第一个实例关键步骤 进入实例目录我们需要修改conf/server.xml来改变其默认的8080端口避免冲突。cd /usr/local/tomcat-8081/conf vim server.xml找到以下三个连接器Connector配置并修改端口HTTP/1.1 Connector这是主要的应用访问端口将port8080改为port8081。Connector port8081 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /AJP Connector如果不用AJP协议通常与Nginx配合用HTTP即可可以注释掉或将其端口port8009改为18009之类的非冲突端口。Shutdown Port这是Tomcat的关闭监听端口将port8005改为port8006。这一点非常重要如果多个实例使用相同的关闭端口你将无法正常关闭任何一个实例。Server port8006 shutdownSHUTDOWN可选优化配置日志分割默认catalina.out会无限增长。建议使用logrotate服务来管理或者修改bin/catalina.sh脚本使用cronolog等工具按日期分割。一个简单的logrotate配置示例/etc/logrotate.d/tomcat-8081/usr/local/tomcat-8081/logs/catalina.out { daily rotate 30 copytruncate missingok compress delaycompress notifempty create 644 tomcat tomcat }内存调整根据应用需要修改bin/catalina.sh在文件开头添加export JAVA_OPTS-server -Xms512m -Xmx1024m -XX:MetaspaceSize128m -XX:MaxMetaspaceSize256m创建专用系统用户并授权安全最佳实践groupadd tomcat useradd -s /bin/false -g tomcat -d /usr/local/tomcat-8081 tomcat chown -R tomcat:tomcat /usr/local/tomcat-8081 chmod x /usr/local/tomcat-8081/bin/*.sh创建Systemd服务文件推荐 使用systemctl管理服务比直接运行脚本更规范。创建文件/etc/systemd/system/tomcat-8081.service[Unit] DescriptionApache Tomcat 9 Instance for Port 8081 Afternetwork.target [Service] Typeforking Usertomcat Grouptomcat EnvironmentJAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk EnvironmentCATALINA_PID/usr/local/tomcat-8081/temp/tomcat.pid EnvironmentCATALINA_HOME/usr/local/tomcat-8081 EnvironmentCATALINA_BASE/usr/local/tomcat-8081 ExecStart/usr/local/tomcat-8081/bin/startup.sh ExecStop/usr/local/tomcat-8081/bin/shutdown.sh Restarton-failure RestartSec10 [Install] WantedBymulti-user.target实操心得Typeforking和CATALINA_PID的配置是关键。Tomcat的启动脚本是forking模式的systemd需要通过PID文件来跟踪主进程。确保temp目录存在且Tomcat用户有写权限否则服务会启动失败且systemctl status看不到有用信息。启动并测试第一个实例systemctl daemon-reload systemctl start tomcat-8081 systemctl enable tomcat-8081 systemctl status tomcat-8081使用curl或在浏览器访问http://服务器IP:8081应该能看到Tomcat的默认主页。3.3 快速部署第二个Tomcat实例端口8082有了第一个实例作为模板第二个实例的部署就快很多。复制实例目录cd /usr/local cp -rp tomcat-8081 tomcat-8082 chown -R tomcat:tomcat /usr/local/tomcat-8082 # 重新授权修改第二个实例的配置cd /usr/local/tomcat-8082/conf vim server.xml修改端口将HTTP连接器端口从8081改为8082。将Shutdown端口从8006改为8007或其他未占用的端口。AJP端口也相应修改如从18009改为18010。修改第二个实例的Systemd服务文件 复制并修改服务文件/etc/systemd/system/tomcat-8082.service主要修改Description、CATALINA_PID、CATALINA_HOME、CATALINA_BASE、ExecStart、ExecStop这些路径和端口相关的变量指向tomcat-8082目录。启动并测试第二个实例systemctl daemon-reload systemctl start tomcat-8082 systemctl enable tomcat-8082 curl http://localhost:8082至此两个Tomcat实例已经在8081和8082端口独立运行。你可以将你的项目A的WAR包放到/usr/local/tomcat-8081/webapps/ROOT目录下删除原有ROOT将项目B的WAR包放到/usr/local/tomcat-8082/webapps/ROOT下然后重启对应的Tomcat服务即可。4. Nginx安装与反向代理配置现在两个后端应用已经就绪我们需要在它们前面架设Nginx作为统一入口。4.1 安装与启动Nginx配置Nginx官方Yum仓库cat /etc/yum.repos.d/nginx.repo EOF [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck1 enabled1 gpgkeyhttps://nginx.org/keys/nginx_signing.key module_hotfixestrue EOF安装并启动Nginxyum install -y nginx systemctl start nginx systemctl enable nginx firewall-cmd --permanent --add-servicehttp # 开放80端口防火墙 firewall-cmd --reload访问http://服务器IP应能看到Nginx欢迎页。4.2 配置反向代理基于域名或路径Nginx的核心配置位于/etc/nginx/nginx.conf。最佳实践是不要在主配置文件中直接修改而是在/etc/nginx/conf.d/目录下为每个站点创建独立的.conf文件这样清晰且易于管理。场景一基于不同域名的代理推荐假设我们有两个域名app-a.yourdomain.com指向项目Aapp-b.yourdomain.com指向项目B。创建项目A的配置文件vim /etc/nginx/conf.d/app-a.conf写入以下内容server { listen 80; server_name app-a.yourdomain.com; # 你的域名A # 访问日志和错误日志单独存放便于排查 access_log /var/log/nginx/app-a.access.log main; error_log /var/log/nginx/app-a.error.log warn; location / { proxy_pass http://localhost:8081; # 转发到Tomcat实例A proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 以下是一些优化参数根据实际情况调整 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering off; # 对于需要流式响应或大文件上传的应用建议关闭缓冲 } # 可选静态资源缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ { proxy_pass http://localhost:8081; expires 30d; add_header Cache-Control public, immutable; } }创建项目B的配置文件vim /etc/nginx/conf.d/app-b.conf内容类似只需修改server_name和proxy_pass的端口server { listen 80; server_name app-b.yourdomain.com; # 你的域名B access_log /var/log/nginx/app-b.access.log main; error_log /var/log/nginx/app-b.error.log warn; location / { proxy_pass http://localhost:8082; ... # 其他proxy_set_header等配置同上 } }场景二基于同一域名不同路径的代理如果只有一个域名可以通过路径来区分。例如yourdomain.com/app-a/和yourdomain.com/app-b/。配置文件/etc/nginx/conf.d/yourdomain.confserver { listen 80; server_name yourdomain.com; location /app-a/ { proxy_pass http://localhost:8081/; # 注意结尾的斜杠 proxy_set_header Host $host; ... # 其他配置 # 重写请求路径去掉前缀如果应用上下文不是/app-a # rewrite ^/app-a/(.*)$ /$1 break; } location /app-b/ { proxy_pass http://localhost:8082/; # 注意结尾的斜杠 proxy_set_header Host $host; ... # 其他配置 } # 根路径可以指向一个默认应用或静态页面 location / { root /usr/share/nginx/html; index index.html; } }关键细节proxy_pass指令中如果后端地址以斜杠结尾Nginx会将匹配到的location路径部分从请求URI中剥离后再转发。例如访问/app-a/api/user如果proxy_pass是http://localhost:8081/则转发给Tomcat的请求是/api/user。如果不加斜杠则转发为/app-a/api/user。这需要与你的应用部署上下文Context Path相匹配否则会出现404。测试配置并重载Nginxnginx -t # 测试配置文件语法确保无误 systemctl reload nginx # 平滑重载配置不影响已有连接现在当你访问http://app-a.yourdomain.com时请求会被Nginx转发到本机的8081端口即项目A访问http://app-b.yourdomain.com则转发到8082端口的项目B。对外只需要暴露80端口实现了端口的统一和应用的隔离。5. 高级配置、优化与问题排查基础部署完成后为了提升稳定性和可维护性还需要进行一些优化配置。5.1 使用Upstream模块进行负载均衡与健康检查即使目前每个后端只有一个Tomcat实例使用upstream模块也是一个好习惯它为未来扩展如增加实例和配置健康检查提供了便利。修改Nginx配置在http块内通常在/etc/nginx/nginx.conf中定义upstreamhttp { ... # 定义Tomcat服务器组 upstream backend_app_a { server localhost:8081 max_fails3 fail_timeout30s; # 未来可以在这里添加更多server实现负载均衡 # server 192.168.1.101:8081 weight2; } upstream backend_app_b { server localhost:8082 max_fails3 fail_timeout30s; } ... }然后修改各自的server块中的locationlocation / { proxy_pass http://backend_app_a; # 引用upstream名称 ... }max_fails和fail_timeout参数定义了在30秒内如果连接到该后端服务器失败3次Nginx会将其标记为不可用30秒实现了基本的被动健康检查。5.2 Tomcat应用部署与上下文路径管理如何将你的WAR包部署到Tomcat有几种方式直接复制到webapps目录将your-app.war复制到/usr/local/tomcat-8081/webapps/下Tomcat启动时会自动解压。访问路径为http://域名:端口/your-app。如果想用根路径访问可以将WAR包重命名为ROOT.war或者删除webapps/ROOT目录后将你的WAR包解压并重命名为ROOT。修改server.xml中的Context在Host标签内添加Context path docBase/path/to/your/exploded/war /并设置path来指定根上下文。这种方式更灵活但需要手动管理应用文件。使用CATALINA_BASE/conf/Catalina/localhost/目录创建一个XML文件如yourapp.xml内容为Context docBase/path/to/your/war /。这种方式支持热部署是推荐的方式。避坑指南如果你的应用是Spring Boot打包的包含内嵌容器的可执行JARspring-boot-starter-web它不能直接部署到外置Tomcat。你需要将打包方式改为war并排除内嵌的Tomcat依赖。这是一个常见的部署错误。5.3 常见问题与排查技巧实录即使按照教程操作也可能会遇到问题。这里记录几个我踩过的坑和排查思路。问题1Nginx报错502 Bad Gateway这是最常见的问题意味着Nginx无法连接到后端Tomcat。排查步骤检查Tomcat是否运行systemctl status tomcat-8081查看状态和日志journalctl -u tomcat-8081。检查端口监听netstat -tlnp | grep 8081确认Tomcat进程是否在监听8081端口。检查防火墙/SELinuxCentOS 7防火墙可能阻止了Nginx或外部访问8081端口。用firewall-cmd --list-all查看或临时关闭防火墙测试systemctl stop firewalld。SELinux也可能阻止可尝试setenforce 0临时关闭测试。检查Nginx配置确认proxy_pass地址和端口是否正确。用curl -v http://localhost:8081直接在服务器上测试后端是否可达。查看Nginx错误日志tail -f /var/log/nginx/error.log通常会有更具体的连接失败原因。问题2应用静态资源CSS, JS, 图片加载404浏览器能访问页面但样式全无控制台报资源404。原因与解决路径问题页面中资源链接使用的是绝对路径或错误的相对路径。确保你的前端资源路径正确或者使用base href/标签。Nginx配置未处理静态资源如果静态资源由Tomcat提供确保Nginx的proxy_pass配置正确且没有因为location匹配规则导致静态资源请求被错误处理。可以像前面示例一样为静态资源单独配置一个带缓存的location块。应用上下文路径不匹配如果你的应用部署在非根上下文如/myapp但前端资源请求路径没有包含这个上下文就会404。需要检查应用打包配置和Nginx的proxy_pass规则是否使用了结尾斜杠。问题3Tomcat启动失败提示端口已占用排查使用netstat -tlnp | grep 端口号查找是哪个进程占用了你配置的端口8081, 8082, 8005, 8006等。可能是另一个Tomcat实例也可能是其他应用。修改冲突的端口号。问题4Session丢失或混乱特别是在基于路径代理时原因如果应用使用Cookie存储Session ID而Cookie的Path属性默认是当前路径。当通过/app-a/访问时Cookie的Path可能是/app-a/导致切换到/app-b/时Session不共享这通常是期望行为。但如果同一个应用通过不同路径访问就会导致Session丢失。解决在Nginx的proxy_cookie_path指令中重写Cookie的Path。例如如果应用在/app-a下设置了Path为/app-a的Cookie你想让它对整个域名有效可以设置proxy_cookie_path /app-a /;。这需要谨慎评估安全性。问题5上传大文件失败或超时解决需要调整Nginx和Tomcat两端的超时和大小限制。Nginx在location块中增加client_max_body_size 100M; # 允许上传的最大body大小 proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s;Tomcat在conf/server.xml的对应Connector中增加connectionTimeout20000 maxPostSize104857600 # 100MB disableUploadTimeoutfalse uploadTimeout300000 # 5分钟日志排查黄金命令实时查看Nginx访问日志tail -f /var/log/nginx/app-a.access.log实时查看Nginx错误日志tail -f /var/log/nginx/error.log实时查看Tomcat应用日志tail -f /usr/local/tomcat-8081/logs/catalina.out或应用自身的日志文件如localhost.log,yourapp.log。查看系统服务状态systemctl status nginx和systemctl status tomcat-8081journalctl -u tomcat-8081 --since 10 minutes ago。6. 安全加固与维护建议部署完成后安全加固是必不可少的一步。Tomcat安全删除默认管理页面生产环境应删除webapps目录下的docs,examples,host-manager,manager应用减少攻击面。使用强密码如果必须使用Manager应用务必修改conf/tomcat-users.xml中的密码并限制访问IP。更新版本定期关注Tomcat安全公告及时更新到稳定版本。Nginx安全隐藏版本号在nginx.conf的http块中添加server_tokens off;。限制请求方法在不需要的location中限制HTTP方法如if ($request_method !~ ^(GET|POST|HEAD)$) { return 405; }。配置HTTPS使用Let‘s Encrypt等免费证书为域名配置HTTPS这是现代网站的标配。这涉及到在Nginx中配置SSL证书和将HTTP重定向到HTTPS。系统层面防火墙只开放必要的端口80, 443, SSH。关闭Tomcat的管理端口如8005对公网的访问。非root用户运行我们已经使用tomcat用户运行Tomcat确保Nginx也以非root用户通常是nginx运行。定期备份备份Nginx配置/etc/nginx、Tomcat配置各实例的conf目录和应用数据。监控与维护日志轮转如前所述配置logrotate防止日志撑满磁盘。进程监控可以使用supervisor或systemd的自带监控功能确保服务异常退出后能自动重启。性能监控关注服务器的CPU、内存、磁盘IO和网络流量。可以使用top,htop,iftop,iotop等命令或搭建更专业的监控系统如PrometheusGrafana。整个部署流程从规划到安全加固虽然步骤繁多但每一步都有其必要性。遵循“规划-部署-配置-优化-安全”这个流程可以帮你搭建一个结构清晰、易于维护、稳定可靠的多项目部署环境。记住配置文件是最好的文档每次修改前做好备份修改后充分测试。