ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot多环境配置与部署实战:从jar包到Docker

SpringBoot多环境配置与部署实战:从jar包到Docker 1. 部署形态先理清楚jar包、war包还是镜像1.1 为什么说部署方案首先要看你的运行环境SpringBoot项目部署我自己刚入行那会儿也天真地以为“打包扔服务器上就能跑”结果被环境折腾得够呛。其实部署形态这件事很大程度上取决于你的运行环境和团队基础设施。先说说最常见的三种形态。第一种是打jar包直接跑这也是SpringBoot默认推荐的方式。因为SpringBoot内嵌了Tomcat所以打出来的jar是个可执行jar服务器上只要有JDK就能跑不需要额外装Tomcat或者配置Web容器。这种方式的优点就是简单、依赖少、版本一致性好特别适合微服务场景下几十个服务要快速部署的情况。你不需要关心服务器上Tomcat是8还是9因为Web容器跟着应用走。第二种是打war包丢到外置Tomcat里这种在传统企业里仍然很常见。为什么要用war包呢一个典型场景就是公司运维已经有一套成熟的Tomcat集群有统一的启动脚本、端口管理、日志采集方案如果改为内嵌模式就要改动运维侧的整套流程。还有就是有些遗留系统需要和其他Java Web应用共用同一个Tomcat实例。这种情况你把SpringBoot项目的打包方式改成war并且让启动类继承SpringBootServletInitializer就可以像部署传统Web项目一样用了。第三种是打成Docker镜像容器化部署。这种形态的好处是环境隔离彻底、扩容方便开发环境和生产环境用的镜像是同一份不会出现“我在我机器上跑得好好的”这种问题。适合上了K8s或者至少用了Docker Compose做编排的团队。三种形态没有绝对的好坏关键是看场景。我给一个选型参考如果你是个人项目或者小团队服务器就一两台直接打jar包配合systemd就够了如果公司运维体系成熟、已经有很多传统Java Web项目在共用Tomcat那war包能少很多沟通成本如果团队已经有了容器化基础设施那就老老实实走镜像路线别绕路。1.2 打包前需要搞定的几项准备不管用哪种形态部署构建之前都有几件事要确认清楚不然打包打一半出问题很浪费时间。第一个是JDK版本。现在SpringBoot 2.x对应Java 8或11都能跑SpringBoot 3.x就必须要Java 17了。所以你在pom.xml里指定的Java版本和服务器上装的JDK版本必须匹配。这里有个常见误区本地用IDEA能跑起来不代表服务器上也能跑因为你本地可能是1.8服务器上装的是11而项目里用了比较老的依赖在更高级别的JDK下可能出现兼容问题。反过来也一样本地跑得好好的服务器上JDK版本太低直接启动不了。建议在pom.xml的properties里把java.version固定好并且在服务器上安装对应版本JDK别用系统自带的OpenJDK版本有时候会对不上。第二个是Maven仓库依赖。如果你的项目依赖了公司内部的私有构件那打包服务器或者你自己电脑的Maven settings.xml里必须配置私服地址和账号。我遇到过团队新成员拉不下来依赖就是因为私服配置没同步。另外很多依赖第一次下载很慢建议用阿里云镜像源别默认中央仓库硬扛。第三个是测试和代码检查的问题。构建时如果执行了test和lint可能会因为一些环境问题导致构建失败。比如测试用例连了本地数据库打包服务器上没有这个库就直接报错。所以个人实践时我会在打包命令里加上-DskipTests跳过测试只是编译测试代码但不执行或者更彻底一点用-Dmaven.test.skiptrue连测试代码都不编译。不过这只适合“构建部署”这个环节CI/CD流程里最好还是完整跑一遍测试这个要分清楚。第四个是资源文件。SpringBoot默认会把src/main/resources下的文件都打进包里比如application.yml、mapper XML文件、静态资源等等。如果你改了某个配置文件打包前一定要先clean一下再package不然旧文件会被缓存下来。我见过不止一次改了配置但部署后没生效最后发现是没clean导致的。2. 多环境配置的核心逻辑一套代码多套环境2.1 Spring Boot的profile机制到底解决什么问题开发完一个项目你至少要面对三个环境本地开发环境dev、测试环境test、生产环境prod。这三个环境里的数据库地址、Redis地址、日志级别、文件存储路径、第三方接口地址基本都不一样的。如果每次发布前手动改一遍配置文件那这个流程迟早要出大事故。SpringBoot的profile机制就是为了解决这个问题。它的核心思路非常朴素一套代码多套配置运行时按需激活。具体操作上你的资源目录里会有这么几个文件src/main/resources/ ├── application.yml ├── application-dev.yml ├── application-test.yml └── application-prod.ymlapplication.yml里放的是所有环境都一样的公共配置比如应用名、端口、编码之类。各个环境的差异配置放到对应的application-{profile}.yml里比如application-dev.yml里配本机的数据库连接application-prod.yml里配生产库的连接。然后application.yml里指定默认激活哪个profilespring: profiles: active: dev这样你在本地直接启动加载的就是application.ymlapplication-dev.yml的合并结果。发到生产环境时通过启动参数把profile切到prod加载的就是application.ymlapplication-prod.yml而dev的配置完全不会被加载。这里有个细节很多人一开始不太理解profile文件不是“覆盖”公共文件而是“合并”。SpringBoot的PropertiesPropertySourceLoader在加载时会做合并profile配置文件里的属性优先级高于默认配置文件。所以在dev配置里你只需要写需要改动的那些属性公共属性留在application.yml里就行了。另外SpringBoot的配置文件可以有多个来源这里列出优先级从高到低命令行参数最高→SPRING_APPLICATION_JSON→ Servlet参数 → JNDI → Java系统属性如-Dspring.profiles.activeprod→ 操作系统环境变量 →application-{profile}.yml→application.yml→ 项目内配置。这个优先级顺序很关键因为后面部署时我们要利用这个机制让命令行或者环境变量来覆盖配置文件里的默认值。2.2 激活环境的五种姿势与推荐场景spring.profiles.active这个属性有多个地方可以设置我实际工作中用过的就有五种方式这里一一列出来大家根据场景选择第一种在application.yml里写死spring: profiles: active: dev这是最偷懒的方式适合本地开发调试。但生产环境如果用这个每次发版前都要改文件风险很高我不推荐在正式流程里这么做。第二种启动参数指定命令行java -jar app.jar --spring.profiles.activeprod这是最推荐的方式之一。因为命令行的优先级最高不管application.yml里写的是什么这个参数都能覆盖。而且这个参数写在启动脚本里代码里完全不用动发版时改脚本就行或者直接在CI/CD流程里传进去。第三种JVM系统属性指定java -jar -Dspring.profiles.activeprod app.jar注意这里-D要放在-jar前面这是JVM参数而不是程序参数。效果和第二种一样只是传递方式不同。实测中这种方式有个小坑如果你用的是java -jar app.jar -Dspring.profiles.activeprod把-D放后面那么这个参数会变成程序入参而不是JVM系统属性SpringApplication也能识别但需要注意参数顺序避免混淆。第四种操作系统环境变量export SPRING_PROFILES_ACTIVEprod java -jar app.jarSpringBoot对配置项有松散绑定机制所以spring.profiles.active这个点分式属性对应环境变量就是大写加下划线SPRING_PROFILES_ACTIVE。这种方式在Docker部署时尤其好用因为很容易在容器编排工具里设置环境变量而不用改命令。第五种用启动类的代码指定SpringApplication app new SpringApplication(Application.class); app.setAdditionalProfiles(prod); app.run(args);这种写代码的方式不太灵活一般不推荐除非你有动态计算profile的需求比如读取某个配置中心的开关来决定激活哪个profile。综合来看我的实践组合是本地用第一种测试和生产环境用第二种或第四种。特别是容器化部署直接在docker-compose的environment里设置SPRING_PROFILES_ACTIVE配置改动非常清晰。2.3 多环境配置文件里到底放什么东西很多人对profile的理解停留在“每个环境一个配置文件”这个层面但具体往文件里塞哪些内容、怎么保持文件结构清晰这里面的门道还是挺多的。我建议按这个思路来拆分公共配置所有环境都一样放application.yml差异配置按环境分开。典型的公共配置包括应用名称、端口如果你所有环境都用同一个端口可以放这里、编码、日志格式、以及一些框架级别的不变配置。差异配置通常有这些类数据源连接dev连本地MySQLtest连测试库prod连生产库Redis、MQ等中间件地址不同环境的IP和端口基本都不一样日志级别dev和test可以打DEBUGprod必须打INFO或WARN文件存储路径本地用相对路径服务器上用绝对路径第三方接口地址比如支付回调地址、短信网关地址开关类配置某些功能在dev环境要开启在prod可能要关闭举个例子application-dev.yml大概长这样server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall_dev?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: root123 redis: host: localhost port: 6379 logging: level: com.example.mall: DEBUGapplication-prod.yml则是对应的一组生产值server: port: 8080 spring: datasource: url: jdbc:mysql://10.0.0.5:3306/mall_prod?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: mall_app password: proD_2024_... redis: host: 10.0.0.6 port: 6379 logging: level: com.example.mall: INFO这里要注意一个细节数据库密码等敏感信息不应该明文写在配置文件里尤其是prod环境的。你可以用Jasypt做配置加密或者使用配置中心如Nacos、Apollo来管理。在下面的常见问题里我还会专门展开讲配置安全这块。3. 从本地到服务器的完整落地流程3.1 Linux服务器部署前的环境准备项目开发完了多环境配置也写好了接下来就是真正往服务器上扔了。这一节我以最经典的“jar包 systemd”方案为例讲Linux服务器上的完整落地过程。先准备JDK环境。我是用tar.gz手动安装的方式这样对版本控制最精确。先到官网下载JDK 8或JDK 17的tar包根据你的项目要求传到服务器上解压到/usr/local/java/然后在/etc/profile里设置环境变量export JAVA_HOME/usr/local/java/jdk1.8.0_202 export PATH$JAVA_HOME/bin:$PATH执行source /etc/profile后用java -version验证一下。这里有个比较容易踩的坑如果服务器上之前装过OpenJDKjava命令可能会指向旧版本你设置了JAVA_HOME也没用。检查/usr/bin/java这个软链接指向哪里必要时把它换掉。接着开放防火墙端口。SpringBoot应用默认端口是8080如果你的云服务器有安全组限制还需要在云控制台放行这个很容易漏。服务器本地的防火墙如果是firewalld执行firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload国内云厂商的服务器光开本机防火墙还没用控制台的安全组也要加一条规则很多第一次部署的人卡在这一步程序已经启动了本地curl没问题但外部就是访问不到。3.2 jar包启动方案手动启动与systemd托管环境准备好后把打包好的jar传到服务器上。这一步可以用scp也可以用rsync个人项目直接用scp就行scp target/mall-0.0.1-SNAPSHOT.jar root服务器IP:/opt/mall/最简单的启动方式就是手动执行java -jar mall-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod但是这种方式有个问题一旦你关闭终端进程就跟着退出了。所以新手一般会用到nohupnohup java -jar mall-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod logs/app.log 21 nohup让进程忽略挂断信号是放到后台执行日志输出到指定文件。但这种方式管理起来仍然不方便。想要关闭进程还得先ps -ef | grep java找到PID再kill -9。对于一个真正的生产环境这种管理方式太原始了。我更推荐用systemd来托管SpringBoot应用。在/etc/systemd/system/下创建一个service文件比如mall.service[Unit] DescriptionMall SpringBoot Application Afternetwork.target [Service] Typesimple Userappuser WorkingDirectory/opt/mall ExecStart/usr/local/java/jdk1.8.0_202/bin/java -Xms512m -Xmx1024m -jar /opt/mall/mall-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod Restarton-failure RestartSec10 StandardOutputappend:/var/log/mall/app.log StandardErrorappend:/var/log/mall/app.log [Install] WantedBymulti-user.target这里有几个配置值得解释一下。WorkingDirectory指定工作目录SpringBoot应用里如果用了相对路径读写文件会以这个目录为基础。Restarton-failure是进程崩溃自动重启的开关服务异常退出10秒后自动拉起这个对线上稳定性很重要。Userappuser是运行用户不要用root用户跑应用安全风险太大了。写好之后执行systemctl daemon-reload systemctl enable mall systemctl start mall从此以后启动、停止、重启、查状态都有标准命令了systemctl status mall systemctl restart mall journalctl -u mall -f这套方案管单机应用已经非常扎实了而且没有引入额外依赖运维成本很低。后续要发布新版本只需要替换jar文件然后systemctl restart mall就行。3.3 内存参数的设置与压测参考JVM参数里最常见的坑是内存设置不当。这里我分享几个实测总结不是纯理论是踩完坑之后的认知。-Xms和-Xmx控制堆内存初始值和最大值。我的建议是把两者设为相同值比如-Xms512m -Xmx512m这样JVM启动时一次性申请好内存运行时不用反复扩容性能更稳定GC行为也更容易预测。生产环境不要设置得太小太小了频繁GC也不要太大太大了留给系统和其他进程的内存就不够了可能直接被系统OOM Killer杀掉。一个粗略的经验如果服务器是4G内存给SpringBoot应用1G到2G是比较合适的。如果你的应用用了本地缓存比如Caffeine或者大量复杂查询需要根据压测结果往上调。另外注意把Metaspace和线程栈空间算进去用-Xss512k设置线程栈默认值在某些高并发场景下1M会显得浪费我一般调小到512k。4. Docker容器化部署实操4.1 Dockerfile多阶段构建的写法与原理如果你的团队已经用上了容器化那部署方式就和传统的不太一样了。Docker部署SpringBoot的核心是写一个正确的Dockerfile我直接给一个我在生产环境用过多轮、踩过不少坑之后沉淀下来的版本# 第一阶段构建 FROM maven:3.8-openjdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn -B -f pom.xml -DskipTests dependency:go-offline COPY src ./src RUN mvn -B -DskipTests clean package # 第二阶段运行 FROM openjdk:11-jre-slim WORKDIR /app COPY --frombuilder /app/target/mall-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENV SPRING_PROFILES_ACTIVEprod ENTRYPOINT [java, -Xms512m, -Xmx512m, -jar, app.jar]这段Dockerfile用到了多阶段构建。第一阶段用Maven镜像完成代码编译和打包第二阶段只保留JDK运行环境和最终生成的jar包。这样的好处非常明显第一构建阶段需要的Maven、源码、依赖等大量内容不会进入到最终的镜像里镜像体积能小很多。我见过有人把整个构建过程和应用放在一个镜像里最后镜像2G多而用多阶段构建之后只有200M左右。第二镜像里没有源码和编译工具安全性也更好。这里有个细节ENV SPRING_PROFILES_ACTIVEprod是把profile直接写到镜像里。但更好的做法是不要在Dockerfile里写死环境而是运行时通过docker run -e或docker-compose里的environment传入。因为一个镜像到了生产环境可能同一套代码要部署多个实例每个实例profile还不同写死在Dockerfile里就很死板。我给的例子写了一个默认值这是为了“开箱即用”但实际生产我一般不用这种写法而是从docker-compose指定。4.2 docker-compose编排SpringBoot依赖的多套服务实际项目中SpringBoot应用几乎不可能独立运行至少会依赖MySQL和Redis。这种情况下用docker-compose把整套服务编排起来比一个个docker run方便得多。下面是一个docker-compose.yml的例子version: 3.8 services: mysql: image: mysql:8.0 container_name: mall-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: mall_dev ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql restart: unless-stopped redis: image: redis:7-alpine container_name: mall-redis ports: - 6379:6379 volumes: - redis-data:/data restart: unless-stopped app: build: context: . dockerfile: Dockerfile container_name: mall-app depends_on: - mysql - redis environment: SPRING_PROFILES_ACTIVE: dev SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/mall_dev?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: root123 SPRING_REDIS_HOST: redis ports: - 8080:8080 restart: unless-stopped volumes: mysql-data: redis-data:注意这里SpringBoot应用里数据库地址写的是mysql而不是localhost这是因为容器间通信用的是服务名。SpringBoot应用在容器里访问MySQL容器的3306端口网络是通的因为它们在同一个docker-compose网络里。如果你在应用里写死localhost去连数据库那这个应用在容器里就永远连不上它本机的3306端口——因为localhost指向的是应用容器自己而MySQL跑在另一个容器里。环境中我直接配置了SPRING_DATASOURCE_URL来接编程式配置。很多人不理解为什么不在配置文件里配因为这里覆盖了配置文件的值。这又回到了前面讲的配置文件优先级操作系统环境变量优先级高于application-{profile}.yml。这个例子中的URL直接写在了compose里有点长实际项目里数据库敏感信息也可以抽到.env文件里管理。4.3 容器化部署时的环境变量与日志处理容器化部署里日志是需要单独设计的。容器本身是“不可变”的一旦进程退出容器里写的东西随之销毁所以日志必须挂载到宿主机或者采集到专门的日志系统。在docker-compose里可以这样配app: volumes: - app-logs:/app/logs或者直接挂载宿主机目录方便直接查看app: volumes: - /var/log/mall:/app/logs应用配置里日志文件路径要写成/app/logs/这样SpringBoot的logback配置输出到相对路径logs/实际就会写入容器里的/app/logs/而这个目录被挂载出来了。另外容器重启策略restart: unless-stopped很关键它让Docker守护进程在容器崩溃后自动重启。但注意unless-stopped和always的区别always是无论容器怎么退出的都会重启包括你手动stop也会被拉起来unless-stopped是如果用户手动stop了就不会重启。生产环境用unless-stopped更合理。5. 前后端分离部署与Nginx配合5.1 前端构建产物与SpringBoot的分工如果你做的是前后端分离项目比如SpringBoot Vue这种组合那部署时就涉及到前后端各自的上线问题。这个场景我在实际项目里处理过很多次这里把思路理一理。前端Vue项目构建后生成的是静态资源文件dist目录他们不依赖Node.js运行时只需要一个Web服务器托管这些静态文件同时把API请求反向代理到后端的SpringBoot服务上。SpringBoot这边则只需要启动自己作为API服务并配置好CORS或者让所有请求走Nginx统一入口。一个清晰的部署拓扑是这样的Nginx监听80端口处理所有外部请求。对于/api/开头的请求Nginx通过反向代理转发给本机或另一台服务器上的SpringBoot应用对于其他路径Nginx直接返回Vue打包后的静态文件。这样前后端共用同一个域名和端口能规避不少跨域问题体验也好很多。5.2 Nginx反向代理配置的关键点下面是一份能直接用的Nginx配置server { listen 80; server_name www.example.com; # 前端静态资源 location / { root /opt/dist; index index.html; try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; 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; } }解释几个关键点。第一try_files $uri $uri/ /index.html;这一行是Vue项目的路由核心因为Vue是单页应用路由是前端js控制的浏览器刷新/user/list这类路径时服务器上并没有这个物理文件所以要把请求回退到index.html让前端路由来接管。少了这一行刷新页面就会404。第二proxy_pass http://127.0.0.1:8080;这里没有带URI路径所以请求会原样转发。比如请求/api/user/list转发到后端就是/api/user/list。如果你的后端Controller的RequestMapping路径是/user/list没有/api前缀那就要这样写location /api/ { proxy_pass http://127.0.0.1:8080/; # 注意proxy_pass带上末尾的 /会把 /api/ 前缀在转发时去掉 }这个/的有无是Nginx配置里最容易搞混的细节我在这里就翻过车。带上/之后Nginx会把匹配到的/api/部分去掉再转发即收到/api/user/list转发给后端的是/user/list。不带则原样转发。第三proxy_set_header那几行要配上尤其是X-Forwarded-For这样后端的原生Servlet能获取到用户的真实IP否则所有请求的IP都是Nginx所在机器的IP。第四如果上传文件比较大还需要加一行client_max_body_size 20m;默认是1m超过就没法上传了。SpringBoot端还需要在配置里设置spring.servlet.multipart.max-file-size和spring.servlet.multipart.max-request-size。两端要配合着调。部署时前端dist目录可以直接传到服务器也可以用Nginx官方镜像通过Docker跑还可以在构建流水线里自动发布。整体思路就是这样一个反向代理加静态托管的组合。6. 常见问题与排查技巧实录6.1 配置文件不生效服务启动后用的还是旧配置这个问题我排查过太多次了总结下来原因主要集中在几个方向。第一profile根本没有激活。可能你设置的是spring.profiles.activedev但拼错了或者环境变量里的SPRING_PROFILES_ACTIVE值打错了。启动后看日志最直接SpringBoot启动时会打印一行The following 1 profile is active: dev如果没有这行说明profile没生效。第二配置文件被多个地方的值覆盖了。比如你在application.yml里明明配置了端口8888但启动的时候环境变量里有一个SERVER_PORT8080环境变量优先级高就会悄悄覆盖你的配置。排查方法是启动时加--debug参数SpringBoot会打印所有生效配置的来源包括每个属性来自哪个配置文件、哪个系统环境变量。第三打进去的jar包里的配置不是最新的。这个就是前面说的clean问题mvn clean package一定要加clean。我还遇到过一种情况就是有多套配置文件在resources目录下比如application-docker.yml和application-prod.yml然后启动时没看清把--spring.profiles.activeprod和--spring.profiles.activedocker搞混了两个环境配置数据源不一样应用虽然启动了但连不上库。所以配置文件命名和区分一定要清晰必要时可以在application.yml里加上${spring.profiles.active}相关的水印日志在应用启动时打印当前激活的profile一眼就能看出有没有激活错。6.2 端口被占用和服务启动失败端口被占用是部署时最经典的问题。SpringBoot默认8080端口如果你部署了多个应用或者服务器上已有其他服务占用就一直报Port already in use。排查命令是netstat -tlnp | grep 8080看最后一列PID和进程名确认是谁占用了端口。如果是你自己之前启动的旧进程用kill -9 PID结束掉。有些情况下8080被系统的一些管理进程占用可以换一个端口比如SpringBoot配置改server.port多环境文件里就更方便了各环境不同端口即可避免冲突。还有一种情况服务启动后过了几秒就自动退出了这通常不是因为端口而是某个依赖没就绪。比如数据库连接不上SpringBoot有health check机制连不上数据库的实例会不断重试最终启动失败。查看详细日志用journalctl -u mall -f或者tail -f /var/log/mall/app.log里面会明确报出是连接哪个地址超时。解决思路就是确认数据库、Redis这些中间件是否先启动好了并且地址从应用层面是否能访问到。如果是docker-compose容器间的可以先docker exec进app容器ping mysql看通不通再用mysql客户端直接连一下。6.3 内存溢出与系统OOM的处理SpringBoot应用部署到服务器后跑一阵子突然进程消失了dmesg日志里看到oom-killer这就是服务器物理内存不够系统主动杀掉了进程。这种情况我多次遇到。很多时候不是代码泄漏而是JVM内存参数设置不合理给多了导致服务器整体吃紧。排查JVM内存使用情况用jstat看GC频率再用jmap -dump:formatb,fileheap.hprof导出堆转储文件分析。做运维排查时jmap这个命令要慎用特别在堆内存很大的时候会阻塞应用一般线上先打开GC日志更稳妥。JVM参数里加上-XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/var/log/mall/gc.log就能把GC日志记录下来结合GC日志基本能判断是堆不够还是代码产生了过多垃圾。对于这种问题治本的方法是优化代码里的对象创建和缓存策略治标的方法是合理调整Xmx大小以及采取Restarton-failure来自动恢复。6.4 配置安全给敏感信息加个密最后分享一个和配置相关的进阶技巧配置加密。生产环境的数据库密码、API密钥如果直接明文写在配置文件里一旦代码仓库泄露或服务器被入侵敏感信息就全暴露了。方案上用Jasypt比较多。引入依赖后先用工具类生成加密后的密文然后配置明文密码为ENC(密文)的格式并在配置里指定加解密的密钥jasypt: encryptor: password: ${JASYPT_PASSWORD:}启动时通过环境变量传入加解密密钥或者通过启动命令传入java -jar app.jar --jasypt.encryptor.passwordyourSecretKey这样即使配置文件被拿到没有密钥也解不开密文。要注意的是Jasypt的密钥本身要妥善保管不要写进代码仓库用环境变量或私有配置管理服务来存储否则加密就形同虚设了。这套“部署 多环境配置”的方案跑通之后发布一个项目基本就是一套固定流程代码分支合并、构建打包、上传服务器、替换jar或镜像、重启服务。整个链路里最需要多花时间的反而是最开始的多环境配置设计。环境差异管理得越清楚后面部署的时候就越省心。最后分享一个我个人的经验自从我把所有部署操作都脚本化之后才真正从重复劳动里解放出来。比如写一个简单的发布脚本里面包括备份旧包、停止服务、替换新包、启动服务、健康检查这几步每次发布执行一个脚本就行。健康检查这一步我建议一定加检查/actuator/health返回UP才算发布成功。这看似简单但能挡住大量“启动没报错但实际没起来”的问题。后续如果再深入可以接Nacos做配置中心、接GitLab CI做自动发布那多环境配置就能做到完全不碰服务器一键发布到任意环境了。
返回列表