ARTICLE DETAIL

资讯详情

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

Java与Python项目服务器部署实战:从环境配置到前后端分离

Java与Python项目服务器部署实战:从环境配置到前后端分离 干开发这些年最常见的场景就是代码写得挺欢一到“部署”这两个字就头疼。Java项目打包出个jar或者war扔到服务器上跑不起来Python项目本地运行没问题换台机器一堆依赖报错。尤其是从“能运行”到“稳定运行”这中间那一大段路——环境配置、进程守护、日志排查、端口占用——踩的坑一个比一个经典。这篇就来聊聊Java和Python开发项目的部署这件事。不是那种照着文档念一遍的官方教程而是把我实际部署过程中验证过的方案、踩过的坑、调优过的配置都拿出来从服务器环境准备到Java和Python各自的主流部署方式再到前后端分离项目的整体落地一步一步拆开讲。无论你是在本地Windows/Linux上做开发调试还是要把项目真正扔到云服务器上给用户用这篇都能给你一套可以照着抄的完整参考。1. 部署的整体思路与环境准备部署这件事本质上就是“把代码变成服务”的过程。代码在你电脑上能跑不代表它在服务器上也能跑——依赖、端口、内存、权限、日志任何一环出问题服务就起不来。所以先把整体思路理清楚比急着敲命令重要得多。1.1 先搞清楚项目到底需要什么东西拿到一个项目别急着装环境。我的习惯是先用几分钟把项目的“家底”摸清楚列一张清单运行时环境Java项目需要JDKPython项目需要Python解释器版本对不对直接影响能不能跑。依赖管理Java项目用Maven还是GradlePython项目用pip还是conda依赖锁文件在哪。服务形态是单体应用Spring Boot fat jar、Flask app还是需要Web服务器容器Tomcat部署war包、NginxuWSGI跑Django。外部依赖数据库用什么MySQL、PostgreSQL、Redis、有没有消息队列、对象存储这决定了部署前要不要先准备这些服务。端口规划项目监听什么端口谁来转发外部请求防火墙规则怎么放行。举个例子。一个典型的Spring Boot项目pom.xml里dependencyManagement指定了Spring Boot 2.7.xJDK至少得17一个Django项目requirements.txt里写着Django 4.2Python得3.10往上数据库如果是PostgreSQL还得装psycopg2-binary。这些信息都会直接影响部署方案的选择。1.2 服务器准备与目录规划建议服务器这块新手最容易犯的错就是“环境装得乱七八糟”。今天装个JDK到默认路径明天装个Python到/user/local时间一长自己都找不到东西在哪。我的建议是建立一个统一的目录规范/opt/ ├── projects/ # 项目代码统一放这 ├── runtime/ # 运行时JDK、Python虚拟环境等 ├── logs/ # 日志统一目录 └── backup/ # 备份目录即使你只有一台小内存云服务器也建议按这个思路分目录别把项目直接铺在根目录或home下。原因很实际部署的排查效率大部分靠“东西在哪”撑起来。日志统一了排查看/opt/logs代码统一了改配置不用到处找。Linux服务器上建议使用普通用户比如叫deploy运行服务不要用root直接跑应用。原因有两个一是安全问题应用进程如果被攻击root权限的破坏力更大二是误操作风险用root改错文件的代价实在太高。需要权限的端口比如80、443通过Nginx转发或setcap解决后面会细说。2. Java项目的部署实战Java项目中目前最常见的形态就三种传统的war包扔Tomcat、Spring Boot的fat jar直接java -jar、以及容器化docker run。下面把每种的流程、关键配置和坑都过一遍。2.1 JDK的安装与版本选择技巧JDK是Java项目的命根子。现在JDK版本很多但实际部署时我的选择逻辑很简单Spring Boot 2.x 项目JDK 8或11。虽然JDK 8已经过了免费商用期但大量老项目还在用除非有安全合规要求否则稳定压倒一切。Spring Boot 3.x 项目JDK 17起步这是Spring官方指定的基础版本。纯Java工具类项目JRE都行但建议装JDK因为后续可能要用jstack、jmap等排查工具。安装方式推荐用包管理器或二进制解压不要用IDE自带的JDK。CentOS/RHEL系用yumUbuntu/Debian系用apt能少很多环境变量配置的麻烦。如果是自己手动解压JDK一定要记得配JAVA_HOME# 以OpenJDK 17为例解压到/opt/runtime/jdk-17后 export JAVA_HOME/opt/runtime/jdk-17 export PATH$JAVA_HOME/bin:$PATH配置写进/etc/profile.d/java.sh再source /etc/profile这样ssh新开的会话也能生效。一个非常常见的坑是明明系统里装了多个JDKjava -version显示的却不是你期望的那一个。排查思路很简单which java看路径再echo $JAVA_HOME看环境变量不对就在.bashrc或profile.d里修正。2.2 构建工具——Maven的项目打包细节Java项目构建现在还是Maven占绝对大头Gradle在Android和部分新项目里用得多。构建这一环最核心的要务是“可复现构建”。以Maven为例项目中一定要有pom.xml并且锁定parent版本和依赖版本。打包命令很简单mvn clean package -DskipTests但有几点值得注意-DskipTests是跳过测试代码的编译吗不是。它跳过的是测试执行但测试代码还是会被编译。如果想连编译都跳过用-Dmaven.test.skiptrue。实际部署时我一般用-DskipTests毕竟测试代码编译一下也能提前暴露问题。Maven默认会在本地仓库缓存依赖~/.m2/repository第一次打包会下很多依赖时间久是正常的。如果想在服务器上打包务必要把国内镜像配好比如阿里云镜像否则下载速度能让人怀疑人生。打war包还是jar包取决于pom.xml中packaging标签jar或war以及Spring Boot的spring-boot-maven-plugin是否是内置容器。如果用Tomcat部署warSpring Boot项目还要额外继承spring-boot-starter-tomcat并将主类配置为SpringBootServletInitializer子类否则war扔进Tomcat会404。打包完artifact在target目录下。常见命名格式项目名-版本号.jar或.war。这一步产出才是要部署的产物。2.3 Tomcat部署war包的完整流程Tomcat部署war多见于传统JavaWeb项目比如电商系统tpshop这类、老管理系统。流程不复杂但坑不少。首先去Apache Tomcat官网下载对应版本的二进制包解压到/opt/runtime/tomcat9。然后需要修改的核心配置是conf/server.xmlHTTP端口默认8080如果和现有服务冲突改成你规划的端口。appBase默认webapps目录。war包扔进去Tomcat启动时会自动解压。最大线程数配置这也是性能调优的关键Connector port8080 protocolHTTP/1.1 maxThreads200 minSpareThreads20 connectionTimeout20000 redirectPort8443 /maxThreads数量设置的逻辑是不要简单照搬网上推荐的200/400/800而要按你服务器CPU核数和业务特性评估。一个CPU密集型的服务线程数远大于CPU核数意义不大反而增加上下文切换开销。一般起步按CPU核数的2-4倍设置再压测调整。部署war时有个非常细节的操作如果war包重名Tomcat会保留旧解压目录。你想强行更新建议先把旧war和解压目录都删掉再把新war放进去。我见过太多“明明替换了war访问还是旧页面”的问题最后发现是旧目录残留。Tomcat启动脚本是bin/startup.sh停止是shutdown.sh。注意startup.sh启动后是后台进程要看日志去logs/catalina.out。这里有个强劲推荐启动后一定马上tail一下日志确认没有异常再关闭会话。不要启动完直接走人等三分钟后一堆连接异常再回来查效率太低了。2.4 Spring Boot的jar包部署与常用参数Spring Boot打出来的fat jar是现在Java项目部署的主流形态因为它自带Tomcat不需要额外装应用服务器部署就是从一条命令开始的java -jar app.jar但是直接裸奔运行有它的局限至少要加这么几个固定参数nohup java -Xms512m -Xmx512m -XX:UseG1GC \ -jar /opt/projects/app.jar \ --server.port8080 \ --spring.profiles.activeprod \ /opt/logs/app.out 21 几个参数说明-Xms512m -Xmx512m初始堆和最大堆设成一样避免运行时动态扩容导致GC频繁。具体值按服务器内存和项目要求来多数业务系统512m-2g比较常见。--spring.profiles.activeprod指定生产环境配置。这句话能救命的场景是本地配置和线上配置不同步把database地址、redis地址全写在application-prod.yml里部署时用profile隔离改环境不用改代码。nohup ... 后台运行。注意一定要把标准输出和错误输出重定向到日志文件否则用nohup跑久了日志会堆积到nohup.out里不说还可能因为你关闭终端导致输出中断。 /opt/logs/app.out 21把out和err都送到同一个文件。Java项目一个特有坑System.out输出的日志如果不重定向报关会话后就找不到了。如果项目里用了外置的配置文件更推荐的做法是配合--spring.config.location显式指定java -jar app.jar --spring.config.location/opt/projects/app/config/application.yml这样改配置不用重新打包部署灵活性高很多。2.5 用Docker部署Spring Boot项目容器化部署这几年已经成为主流特别是Spring Boot这种“自带容器”的Java项目做成Docker镜像非常自然。一个最简Dockerfile# 构建阶段 FROM maven:3.8-openjdk-17 AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline COPY src . RUN mvn clean package -DskipTests # 运行阶段 FROM openjdk:17-jre-slim WORKDIR /app COPY --frombuilder /build/target/app.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -Xms512m, -Xmx512m, -jar, app.jar]有几个细节值得说道说道多阶段构建的目的是让最终镜像只包含运行环境JRE不携带Maven和源码镜像体积小几个级别。能看到只有openjdk:17-jre-slim加一个jar运行阶段少了很多没用的东西。RUN mvn dependency:go-offline是把依赖先下载好这样后续改代码打包能有缓存构建速度快很多。第一次构建没有缓存得耐心等。镜像跑起来时端口映射要注意docker run -d -p 8080:8080 --name app myapp:latest。左边是宿主机端口右边是容器端口。如果你在容器里改server.port8081右端口也要跟着改。Docker方式最大的价值是环境一致性。同一个镜像在开发机、测试机、生产机上跑出来的结果完全一样再也不用面对“我本地好的呀”这种灵魂拷问。3. Python项目的部署实战Python项目的部署和Java非常不同Java构建出来的jar/war本身是编译产物而Python是解释型语言部署的本质是“把源码和运行环境一起搬运过去”。这也意味着Python部署的核心痛点集中在Python版本管理、依赖隔离、进程管理这三件事上。3.1 Python解释器安装与虚拟环境配置服务器上预装的Python版本通常比较老CentOS 7自带Python 2.7Ubuntu 20.04是Python 3.8而现在好多项目要求Python 3.9、3.10甚至3.11。这时候就需要手动安装Python。推荐用源码编译安装可控性最强# 下载Python 3.10.12 wget https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz tar zxvf Python-3.10.12.tgz cd Python-3.10.12 ./configure --prefix/opt/runtime/python3.10 --enable-optimizations make -j$(nproc) make install--prefix指定安装目录 不污染系统自带的Python。--enable-optimizations会启用PGO优化编译时间长一些但运行时性能更好。装完确认一下/opt/runtime/python3.10/bin/python3.10 -V接下来是虚拟环境。Python项目依赖隔离这件事我强调多少次都不算多。千万别“图省事”把所有项目的依赖都装进系统Python否则过俩月装一个新项目时依赖版本冲突能让你崩溃。我们团队的统一做法是每个项目一个venv。创建虚拟环境用自带的venv模块cd /opt/projects/myproject /opt/runtime/python3.10/bin/python3.10 -m venv venv source venv/bin/activate pip install -r requirements.txt激活后which pip要显示虚拟环境内的pip路径。如果发现不在检查是不是虚拟环境没激活或者shell缓存问题用hash -r刷新。装了依赖后务必用一个简单命令验证python -c import flask, requests; print(deps ok)这一步能在部署阶段提前暴露依赖缺失远比服务启动时报ModuleNotFoundError快。3.2 依赖管理的几种主流方案对比Python依赖管理目前有pip requirements.txt、Pipenv、Poetry这三种流派。requirements.txt是老牌可靠方案适合大多数中小项目。写法上推荐用pip freeze生成锁定版本会带上所有间接依赖版本号完全锁定不要只写顶级依赖名如requests不带版本——那等到两年后部署pip可能装了一个不兼容的新版本。Pipenv用Pipfile和Pipfile.lock管理引入了“锁定文件”的概念对虚拟环境下管理直观但历史包袱比较多社区活跃度有所下降。Poetry是现在个人比较推荐的方案pyproject.toml集中管理项目元数据和依赖一个文件干完很多事锁文件精确锁定依赖树。就是学习曲线比pip稍微陡一点。我的建议很直接中小项目用pip freeze requirements.txt venv地够用团队成员都会新项目想走得远一点直接上Poetry以后迁移、升级都顺手。但不管哪种方案锁定版本是硬道理。3.3 用Gunicorn跑Flask/Django应用Python项目跑起来之后直接暴露给公网是不建议的哪怕是开发环境。常规做法是用GunicornGreen Unicorn作为WSGI服务器跑Python应用再用Nginx做反向代理。安装Gunicornpip install gunicorn以Flask为例假设启动文件是app.py应用实例叫app启动命令gunicorn -w 4 -b 0.0.0.0:8000 app:app --daemon-w 4启动4个worker进程这个数值一般按CPU核心数*21来估。典型2核服务器设5个左右4核设9个但也不是越大越好要按你的服务负载实测调整。-b 0.0.0.0:8000绑定所有网卡的8000端口。很多人问为什么不是127.0.0.1——如果服务只给本机Nginx访问用127.0.0.1更安全但如果想直接通过公网IP访问测试就得0.0.0.0。--daemon后台运行。但我个人更推荐不考虑daemon而是配合systemd或supervisor来管理进程这样能自动重启、随系统启动、崩溃被拉起。Django的话Gunicorn命令写法类似gunicorn myproject.wsgi:application -w 4 -b 127.0.0.1:8000注意要切到项目目录下运行Django的配置文件路径才能解析到。Gunicorn有个很有用的参数--timeout。默认30秒如果某个接口耗时超过30秒被kill掉接口会返回502。大数据导出、报表生成这类接口就需要调大gunicorn -w 4 -b 127.0.0.1:8000 app:app --timeout 1203.4 用Nginx为Python项目做反向代理Nginx在前面承接外部HTTP请求把动态请求转发给Gunicorn这是Python生产环境部署最经典的一层架构。Nginx做两层事情暴露80/443端口和托管静态文件。一个最简配置server { listen 80; server_name example.com; # 静态文件由nginx直接返回不经过gunicorn location /static/ { alias /opt/projects/myproject/static/; expires 30d; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }location /static/这块对性能提升非常明显。Python应用如果用框架直接返回静态文件每个请求都要过一遍WSGI、Python代码Nginx直接读磁盘文件返回快几个数量级。Django项目的static文件需要先执行python manage.py collectstatic收拢到一个目录。Nginx改完配置后先nginx -t检查语法确认没问题再nginx -s reload平滑重载。不要直接nginx -s stop再启动能避免一瞬间的服务中断。3.5 Python项目的systemd服务单元配置手工nohup Gunicorn的daemon模式有一个痛点服务器重启后服务不会自己恢复进程崩了也没有自动拉起。解决这个问题的最佳方式是用systemd写服务单元。下面是Flask项目一个完整的service文件[Unit] DescriptionMy Flask App Afternetwork.target [Service] Userdeploy Groupdeploy WorkingDirectory/opt/projects/myproject EnvironmentPATH/opt/projects/myproject/venv/bin EnvironmentPYTHONUNBUFFERED1 ExecStart/opt/projects/myproject/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 app:app Restartalways RestartSec3 [Install] WantedBymulti-user.target几个字段的讲究EnvironmentPATH...让Gunicorn用虚拟环境内的Python避免依赖解析到系统Python的site-packages。PYTHONUNBUFFERED1让Python输出不缓冲日志实时写入否则journald里可能捞不到最新日志。Restartalways进程不管什么原因退出崩了、被killsystemd都会在3秒后自动重拉。WorkingDirectoryGunicorn的工作目录必须和项目目录一致否则相对路径加载配置会失败。保存到/etc/systemd/system/myapp.service后执行systemctl daemon-reload systemctl enable myapp systemctl start myapp systemctl status myappenable是设置开机自启status能看到当前状态和最近日志。这套组合拳下来Python服务的“保活”就交给systemd了比什么nohup都要可靠一个档次。4. 前后端分离项目的部署要点现在很多Java和Python项目都走向了前后端分离前端Vue/React打包成纯静态文件后端出RESTful API。这类型的部署有一个共同的“万能钥匙”用一个Nginx同时托管前端静态文件、反向代理后端API、解决跨域问题。4.1 前端静态资源的构建与部署以最常见的Vue项目为例构建命令npm install npm run build执行完成后会生成dist/目录里面是index.html、js/css资源等纯静态文件。把dist目录整体上传到服务器的/opt/projects/frontend/dist就完成一半了。Nginx托管部分server { listen 80; server_name example.com; root /opt/projects/frontend/dist; index index.html; # 前端路由是history模式时刷新页面会404需要这个配置 location / { try_files $uri $uri/ /index.html; } }关键就是try_files $uri $uri/ /index.html;这一行。Vue Router在history模式下用户访问/user/123刷新时Nginx找不到这个真实文件就会直接返回404try_files把它重写到index.html让前端路由接管。对SPA有核心作用少了它大概率出事。4.2 API反向代理与跨域问题的一并解决前端和后端如果部署在同域下即前端访问/api/userNginx转发给后端http://127.0.0.1:8080/api/user那是节省事情的做法因为同域根本没跨域问题。Nginx配置location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这里有一个特别容易踩的坑proxy_pass的URL末尾是否带/行为完全不同。proxy_pass http://127.0.0.1:8080;不带/会保留原路径proxy_pass http://127.0.0.1:8080/;带/会把location前缀替换掉。比如请求/api/user第一种后端收到/api/user第二种后端收到/user。所以一定要按你后端接口的实际路径设计好要不要带这个斜杠。如果前后端必须分开域比如前端部署在CDN后端单独一个域名跨域问题就得用后端解决。以Spring Boot为例最常用的两种方式加CrossOrigin注解在Controller类或方法上。写一个全局CORS配置类。Python Flask的方式from flask_cors import CORS CORS(app)Django则用django-cors-headers中间件。但我的经验是尽量通过Nginx同域部署来解决跨域逼不得已再配置CORS。因为CORS配置不当非常容易引发安全隐患而且排查起来debug难度更高。4.3 前后端分离项目的部署检查清单以下是我每次部署前后端分离项目都会过的检查项前端dist上传后先直接访问静态资源地址确认能打开index.html、css、js能加载。直接curl后端API接口确认接口能返回数据。在浏览器Network面板看请求和后端响应重点观察有没有404、502、503。前端页面如果白屏先看Console的报错多数是因为静态资源路径不对publicPath配置问题或API地址不对。后端日志看请求是否到达判断问题是出在Nginx层还是后端服务。这份清单能帮你把“前端问题、后端问题、Nginx问题”快速切分让排查思路清晰很多。5. 日志管理与问题排查的实战思路部署完只是开始真正考验人的是第二天早上运维告警说服务不可用。这部分是我写这篇博文最想让读者拿走的部分——一套完整的日志管理和问题排查思路。5.1 日志的规划与轮转配置先说要做到什么程度日志必须能按天或者按大小切分不能一个文件写到爆炸。Java Spring Boot项目推荐使用logback自带的滚动策略在logback-spring.xml里配置rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy。但更省心的方案让应用日志输出到固定目录再配合Linux的logrotate做系统级轮转。logrotate的配置示例/etc/logrotate.d/java-app/opt/logs/app.out { daily rotate 7 copytruncate compress missingok notifempty }daily每天轮转一次。rotate 7保留7个历史文件。copytruncate先复制当前日志再截断这样不需要重启应用。compress历史日志压缩成.gz节省磁盘。如果你用systemd管理Python服务可以不开自己的日志文件直接用journalctl -u myapp看日志。但要控制journald的体积可以在/etc/systemd/journald.conf设置SystemMaxUse500M防止日志把磁盘写满。Python项目用Gunicorn时访问日志和错误日志也要分开--access-logfile /opt/logs/gunicorn-access.log\ --error-logfile /opt/logs/gunicorn-error.log两个日志分开能显著提升排错效率。5.2 高频问题速查与排查流程我整理了一个部署阶段最常见的问题速查表可以说覆盖了80%的情况现象可能原因排查方法网络请求返回404路径配错、tomcat/work目录残留、路由模式问题检查Nginx配置、curl看返回、检查前后端路径502 Bad Gateway后端服务没起来、端口不对、防火墙拦截systemctl status看服务状态、ss -tlnp看端口监听、直接curl后端地址503 Service UnavailableNginx上游配置错误、后端过载未启动看Nginx error.log和上游健康检查配置Java进程启动即退出端口被占用、配置文件语法错误、内存不足ss -tlnp看端口、tail -100 /opt/logs/app.out看具体报错Python启动报ModuleNotFoundError依赖没装全、虚拟环境路径不对重新pip install -r requirements.txt、python -c import xxx验证页面访问很慢静态文件没走Nginx、数据库慢查询、连接池太小先压测看瓶颈在Nginx还是后端再针对性调优定时任务不执行cron环境变量、Python虚拟环境路径没配全检查crontab里的PATH、用绝对路径执行命令排查时我习惯的流程是“从外到内”先看网络层curl -I http://127.0.0.1和curl -I http://公网IP判断是服务器外网问题还是服务本身问题。再看服务层systemctl status 服务名或ps aux | grep java/python确认进程还活着。再看日志层Java项目看catalina.out或app.outPython项目看gunicorn-error.log或journalctl。最后才深入业务代码级问题。这个顺序能让你在五分钟内定位绝大多数部署问题。反着来一上来就看代码效率极低。5.3 端口排查与防火墙放行的经验一个新项目上线最容易碰到的是“服务起来了外面访问不了”。原因多半是两个服务没监听0.0.0.0或者防火墙没放行端口。查看端口监听状态ss -tlnp输出的Local Address列如果显示127.0.0.1:8080说明只有本机能访问外部网络访问不了——需要改成0.0.0.0:8080。如果显示:::8080则是IPv6的通配监听。防火墙这块不同发行版命令不一样UFWUbuntusudo ufw allow 8080/tcpfirewalldCentOS 7firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload云服务器安全组这个最容易忽略——即使服务器内部防火墙全开了云控制台的安全组/防火墙没放行公网依然进不来。还记得我刚入行时部署一个Django项目在服务器上curl死活通公网访问就是不行整整查了一小时最后发现是云控制台安全组忘了加规则。6. 自动化部署的进阶方向前面讲的都是手工部署流程固定了之后完全可以脚本化、自动化。下面说几个我从手工走向自动化的组合装备不复杂但实用价值极高。6.1 编写一键部署脚本以Java Spring Boot项目为例一个典型的deploy.sh#!/bin/bash # 一键部署脚本 - Spring Boot项目 APP_NAMEapp.jar APP_PATH/opt/projects/app BACKUP_PATH/opt/backup LOG_PATH/opt/logs echo 1. 备份旧版本 if [ -f $APP_PATH/$APP_NAME ]; then TIMESTAMP$(date %Y%m%d%H%M%S) cp $APP_PATH/$APP_NAME $BACKUP_PATH/${APP_NAME%.jar}-$TIMESTAMP.jar echo 已备份至 $BACKUP_PATH fi echo 2. 停掉旧进程 PID$(pgrep -f java -jar $APP_PATH/$APP_NAME) if [ -n $PID ]; then kill $PID sleep 5 fi echo 3. 拷贝新包 cp /tmp/dist/$APP_NAME $APP_PATH/ echo 4. 启动服务 nohup java -Xms512m -Xmx512m -jar $APP_PATH/$APP_NAME \ --spring.profiles.activeprod \ $LOG_PATH/app.out 21 echo 5. 检查启动状态 sleep 10 tail -30 $LOG_PATH/app.out这个脚本你在每次发布时都会用建议把备份、停服、替换、启动、检查做成一次性的、运行完有明确提示的步骤比手动敲命令能防住很多手滑事故。Python项目也可以如法炮制差异只是启动命令换成Gunicorn/systemctl。6.2 引入Jenkins流水线与部署的衔接项目发布频率高了人会越来越懒于是自动化CI/CD就安排上了。这里只说一个关键点流水线的产物最好就是最终要运行的那份包/镜像。不要在每个环境上都重复构建而是让Jenkins在流水线中完成构建产出的jar、war或Docker镜像由流水线直接分发到服务器。像这样的话测试环境测过的包和生产环境跑起来的包是完全同一个二进制。一个简化的Jenkins流水线Jenkinsfilepipeline { agent any stages { stage(拉取代码) { steps { git branch: main, url: gitgithub.com:xxx/app.git } } stage(构建) { steps { sh mvn clean package -DskipTests } } stage(发布) { steps { sh scp target/app.jar deployserver:/opt/projects/app/ sh ssh deployserver cd /opt/projects/app ./deploy.sh } } } }对于Docker部署的项目更优雅的做法是构建镜像推到镜像仓库服务器上用Portainer或Watchtower自动拉取更新。这块涉及的东西比较多但核心思想是一致的把重复劳动交给我们写的自动化脚本把人的精力留在真正需要判断的事情上。6.3 部署后的健康检查与回滚预案部署上线后我强烈建议至少做三件事一是立即检查健康检查接口。Spring Boot可以引入spring-boot-starter-actuator暴露/actuator/healthFlask可以写一个最简单的/health视图返回ok。部署脚本里最后检查这个接口返回200才算部署成功。二是对数据库做一次备份。特别是涉及表结构变更的版本没有备份就直接升级一旦出问题要回滚就是自找麻烦。三是提前准备好回滚方式。脚本化部署的反面就是回滚保留上一个版本的备份脚本加一个--rollback参数一键恢复到上一个版本的jar或代码。没有回滚预案的发布叫冒险有回滚预案的发布才叫技术操作。7. 关于部署工作的一些个人体会老话说得对“能跑起来不是本事倒不了才见功夫”。早年我部署项目也踩坑最狠的一次半夜发版把war包覆盖错了位置Tomcat起不来数据还差点出事——从那以后我养成一个毛病任何发布先备份、再操作、后验证三步缺一不可。部署这门手艺最核心的不是把命令背熟而是背后的一整套流程意识哪些参数是真正与你服务器性能相关的哪些路径是统一规划的日志在哪查服务怎么保活万一挂了怎么回滚。把这些想明白了Java还是Python传统服务器还是容器差异都只是命令层面的小事。最后再分享一个小技巧部署脚本里尽量用绝对路径少用相对路径。因为ssh会话的初始目录你没法保证一旦脚本用了相对路径就可能因为目录不对而找不到文件那种报错排查起来十分让人崩溃。
返回列表