
1. 迁移背景与选型理由CentOS停服之后为什么我选了openEuler 24.03 LTS如果你手上还跑着一堆CentOS 7的机器2024年6月30日之后应该已经感受到了压力——官方停止维护CVE漏洞没人补yum源纷纷失效装个新软件包都费劲。之前CentOS 8提前终止维护更让很多人措手不及。我手上有十几台跑业务的服务期间负责的项目大多是基于Java的Spring Boot服务配合MySQL、Redis和Nginx这套组合在CentOS上跑了五六年稳定得让人懒得动它。但停服这件事不是“懒得动”就能绕过去的等出了问题再迁移成本更高。当时摆在面前的选择其实有几个切到Ubuntu/Debian系继续用CentOS Stream或者上Rocky/Alma这类RHEL兼容发行版再就是openEuler。我逐一评估过。Ubuntu的apt体系和现有运维脚本不兼容团队里好几个同事对apt不熟。CentOS Stream是滚动更新生产环境求稳的心态下我接受不了。Rocky/Alma确实平滑但本质上是RHEL的下游重建对我这种想减少对美国上游依赖的团队来说心理上总觉得不踏实。最后定下来用openEuler 24.03 LTS核心是这几点LTS版本周期长社区明确承诺维护多年这点对生产环境太重要了我不可能每两年折腾一次大迁移。兼容性做得好openEuler在RPM生态上兼容性很强我的install脚本、systemd服务文件、yum/dnf使用习惯几乎不需要改。国内社区活跃出问题找资料快文档和Issue响应速度都不错。实际装完测下来JDK、MySQL、Redis、Nginx这几样主流软件的安装方式和CentOS 7时期的习惯非常接近迁移成本比预想低很多。当然openEuler不是没有缺点比如默认软件源里的版本偏保守某些第三方repo可能需要手动适配但整体上在24.03 LTS这个版本上日常部署体验已经很成熟。这篇文章就是把我从零开始在一台干净服务器上部署这套全栈环境的过程完整记录下来。不求讲得多高深但保证每一步都是我自己敲过、验证过的能让你照着走一遍就少踩几个坑。2. 系统安装与初始化最小化安装后的第一轮配置2.1 安装镜像选择与分区规划openEuler 24.03 LTS的ISO从官网下载分DVD和Everything等版本服务器场景直接选DVD版就够了不需要Everything里那些额外包。安装时语言建议选English避免编码问题后面需要中文再装语言包也来得及。分区这里多说一句很多人习惯“一键自动分区”服务器上我不推荐这么做。我的做法是/boot1GB启动分区给内核和grub用/50GB系统根目录/data剩余全部空间作为数据盘挂载点MySQL的数据目录、应用的日志目录都放在这里swap如果内存小于16GB建议留和内存等量的swap内存够大可以设4GB或干脆关掉这样规划的好处是系统盘和数据盘分离将来如果系统挂了、要重装数据盘的内容不受影响直接重新挂载即可。我踩过一次根目录写满导致服务全部异常的坑从那以后所有生产机器都是这个分区方式。2.2 首次登录后的基础优化安装完成后先做几个基础配置别看步骤简单省掉任何一个后面都会浪费更多时间。第一件事配置静态IP。24.03默认用NetworkManager修改IP最稳妥的方式是用nmcli直接编辑配置文件在某些版本上可能不生效。# 查看当前网卡名称和连接名 nmcli device status nmcli connection show # 假设连接名为 ens33修改为静态IP 192.168.1.100 nmcli connection modify ens33 ipv4.method manual \ ipv4.addresses 192.168.1.100/24 \ ipv4.gateway 192.168.1.1 \ ipv4.dns 223.5.5.5,114.114.114.114 nmcli connection up ens33 ip addr show ens33这块值得专门提醒一个坑如果你有多个网卡nmcli connection modify的时候一定要确认连接名对应的是哪块网卡改错了会把远程连接搞断。生产环境改网络配置前最好先准备好带外管理或者现场操作手段。第二件事更换软件源。默认源在国内访问速度还行但如果你的服务器在海外或者某些网络环境建议换成镜像站。# 备份原始源 cp -a /etc/yum.repos.d/openEuler.repo /etc/yum.repos.d/openEuler.repo.bak # 编辑仓库文件将 url 中的 repo.openeuler.org 替换为镜像站地址 sed -i s|repo.openeuler.org|mirrors.tuna.tsinghua.edu.cn/openeuler|g /etc/yum.repos.d/openEuler.repo dnf clean all dnf makecache第三件事系统更新和基础工具。dnf update -y dnf install -y vim net-tools lrzsz wget curl tar unzip telnet lsofnet-tools装上是为了保留ifconfig/netstat的肌肉记忆虽然新系统推荐ip命令但排查问题时netstat -lntp的熟悉程度还是高一些。第四件事关防火墙和SELinux。这里不是无脑劝你关内网环境可以先关掉降低排障成本等全部服务调通后再按需开启并配置放行规则。公网环境我建议不关而是放行精确端口但排查问题时临时关闭排查完再打开这套思路在下面各个组件的联调阶段很管用。systemctl stop firewalld systemctl disable firewalld sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/configSELinux这里多说两句。如果你不想彻底禁用至少先设置为permissive模式跑一段时间。我之前在SELinux enforcing模式下部署Nginx反向代理莫名奇妙502排查到最后发现是httpd_can_network_connect这个布尔值没开。这类问题在新系统上特别容易浪费几个小时所以要么关掉要么熟悉SELinux的排错方法。3. Java环境部署OpenJDK的安装、配置与验证3.1 选JDK版本的关键考量openEuler 24.03 LTS默认源里的OpenJDK版本有8、11、17、21。选哪个取决于你现有项目的字节码编译版本。以最常见的Spring Boot项目来说如果你的项目是用Java 8写的且依赖了一些只在JDK 8下表现正常的旧库就继续用JDK 8不要为了追新而升级——生产环境追求的是稳定复制。如果你是新项目可以直接选JDK 17它是最普及的LTS版本Spring Boot 3.x对它的支持已经非常成熟。我这次演示选的是OpenJDK 17原因是我要部署的Spring Boot 3应用最低要求就是17。另外注意openEuler的OpenJDK包名带java-[version]-openjdk完整安装版是java-17-openjdk而不是java-17-openjdk-headless。很多人习惯装headless版但跑Spring Boot时有时需要用到字体、图形类库headless不包含这些会遇到莫名其妙的异常。3.2 安装与配置# 通过 dnf 安装OpenJDK 17完整版 dnf install -y java-17-openjdk java-17-openjdk-devel # 查看安装位置 which java ls -l /etc/alternatives/javaopenEuler默认会配置好JAVA_HOME了吗答案是部分配置。/etc/profile.d/java.sh这个脚本会设置JAVA_HOME但很多情况下需要重新登录或者手动source它才生效。为了保险起见我习惯在/etc/profile里显式追加一份同时把自己的应用统一用同一个JDK版本避免多版本切换带来的混乱。cat /etc/profile EOF export JAVA_HOME/usr/lib/jvm/java-17-openjdk export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar EOF source /etc/profile java -version这里有个小细节/usr/lib/jvm/java-17-openjdk最后的实际路径可能是xxxx架构后缀像java-17-openjdk-17.0.x.x.x.arch这样的版本号目录。验证的时候用ls /usr/lib/jvm/看清楚再写。3.3 验证Java环境是否可用不要只执行java -version就完事我见过不少人在这一步跳过去后面项目起不来才回头怀疑JDK装坏了。建议写一个最小的Jar包或者直接用已有的Spring Boot Fat Jar验证一下java -jar myapp.jar --server.port8080 --spring.profiles.activetest curl http://localhost:8080/actuator/health看到{status:UP}说明Java环境和端口监听都正常再继续往下装其他组件。Java这块坑最少但很多人恰恰是栽在最简单的地方——压根没装-devel包导致后面编译某些依赖时找不到javac。4. MySQL 8.0部署从repo配置到远程访问的完整链路4.1 软件源策略用官方repo还是openEuler源openEuler 24.03 LTS源里自带MySQL严格说自带的是MariaDB或者是MySQL 8.0社区版不同小版本源里情况不一样。为了可控性我推荐用MySQL官方提供的yum repo但要注意openEuler 24.03基于内核5.10、glibc版本较高MySQL官方repo的el8或el9包通常都能装上具体要看你下载的release包版本。我采用的方式是直接使用MySQL官方Yum仓库# 安装MySQL官方repo rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm # 或者下载到本地再装避免网络中断 wget https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm dnf install -y mysql80-community-release-el9-1.noarch.rpm # 安装MySQL社区版 dnf install -y mysql-community-server如果官方repo在你网络环境下特别慢也可以把mysql-community-server相关的几个rpm包下载到本地再用dnf install ./xxx.rpm离线安装。小版本不一致问题不大MySQL 8.0的内核版本是同一个大版本就行。4.2 数据目录规划与初始化我习惯把MySQL的数据目录放到独立数据盘上也就是前面分区时说的/data。这个习惯来源于一次根目录被慢查询日志和binlog写满导致MySQL直接宕机的事故从那以后MySQL相关的数据文件一律走独立挂载点。mkdir -p /data/mysql # 先启动一次让系统自动初始化默认目录再停掉做目录迁移 systemctl start mysqld systemctl stop mysqld # 默认数据目录在 /var/lib/mysql mv /var/lib/mysql /data/mysql # 如果系统默认目录里有mysql用户需要调整属主 chown -R mysql:mysql /data/mysql # 修改配置文件 cat /etc/my.cnf EOF [mysqld] datadir/data/mysql socket/var/lib/mysql/mysql.sock port3306 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci lower_case_table_names1 max_connections1000 innodb_buffer_pool_size2G # 时区与本地对齐 default-time-zone08:00 skip-name-resolve EOF systemctl start mysqldlower_case_table_names1这个参数建议在初始化之前就定好因为MySQL 8.0中这个参数在初始化后修改会直接导致表名大小写不一致问题甚至服务起不来。如果你之前的项目里有大小写混合的表名和1冲突那就维持0但应用层代码要保证表名大小写统一否则在Linux上会报找不到表。skip-name-resolve是性能优化项MySQL不会对客户端IP做反向DNS解析。代价是grant授权时不能用主机名只能写IP。对应用服务器固定IP的场景这个取舍完全值得。4.3 初始化密码与安全策略第一次启动MySQL后临时密码会写入错误日志。这个步骤很经典但每次都会有人问“密码在哪里”。grep temporary password /var/log/mysqld.log # 输出示例A temporary password is generated for rootlocalhost: xxxxxx # 登录并修改密码 mysql -uroot -p ALTER USER rootlocalhost IDENTIFIED BY YourStrongPass2025; FLUSH PRIVILEGES;MySQL 8.0默认密码策略是validate_password强度要求较高必须有大小写字母、数字和特殊字符长度至少8位。如果是在内网测试环境可以用下面的方式降低强度但生产环境千万别这么做SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;4.4 创建业务账号与远程访问配置root账号只允许localhost登录应用服务器如果和MySQL分离要单独创建远程访问账号。这里有一个安全细节不要用root去连也不要用%通配符给所有IP放行除非你确认网络安全组已经隔离了访问来源。-- 创建专用账号假设应用服务器IP为192.168.1.150 CREATE USER app_user192.168.1.150 IDENTIFIED BY AppPass2025; GRANT ALL PRIVILEGES ON mydb.* TO app_user192.168.1.150; FLUSH PRIVILEGES; -- 验证 mysql -uapp_user -p -h192.168.1.100 mydb远程访问确认没有问题后记得检查一下3306端口是否监听netstat -lntp | grep 3306如果MySQL装在本机本机应用连接时用socket方式最快JDBC URL里写成jdbc:mysql://localhost:3306/mydb会自动走socket如果写成127.0.0.1则是TCP连接会多一层网络开销。同一台机上部署可以优先用localhost。4.5 连接数打满、时区报错的排障两个高频问题单独说一下。连不上/连接数打满。默认max_connections是151生产环境显然不够。我遇到过促销活动期间数据库直接报Too many connections应用雪崩。除了把max_connections调大到500-1000还要在应用层连接池上做限制。比如HikariCP的maximumPoolSize不要超过MySQLmax_connections的70%留出30%给运维连接和其他工具。否则应用一扩容数据库先挂。时区报错。Spring Boot连接MySQL 8.0时经常报The server time zone value CST is unrecognized这是因为驱动版本和服务器时区设置不匹配。在my.cnf中明确配上default-time-zone08:00后这个问题从根上消失不用在JDBC URL里追加serverTimezoneAsia/Shanghai也能正常连接。5. Redis部署与运行调优缓存服务的配置要点5.1 安装方式选择Redis的安装有几种方式dnf直接装、源码编译、用Docker跑。考虑到前面提到的生产环境团队习惯以及Redis本身编译很快、参数定制性好我选择了dnf装但如果你的openEuler源里Redis版本较老也可以从源码编译安装最新稳定版。# 查看源里的版本 dnf info redis # 安装 dnf install -y redis redis-server --versionopenEuler 24.03 LTS源里的Redis版本如果是7.x那直接用dnf装完全够用。Redis 7.x相比6.x有不少性能优化特别是多线程IO和更高效的内存管理生产环境建议优先使用。5.2 核心配置项修改Redis默认配置/etc/redis/redis.conf或/etc/redis.conf取决于你的安装方式需要改的核心项有几处。守护进程与绑定地址# 如果你用systemd管理Redis可以不以daemonize方式运行让systemd直接管理前台进程 # 192.168.1.100 是本机IP只允许内网访问 bind 127.0.0.1 192.168.1.100 protected-mode yes port 6379这里有个习惯问题。如果daemonize yessystemd管理起来反而别扭推荐保持daemonize no因为systemd本身可以管理前台进程、跟踪PID崩溃了还能根据配置自动拉起。持久化策略Redis的数据持久化有RDB快照和AOF日志两种方式。缓存场景下可以只开RDB但如果Redis里存了部分重要的业务数据比如分布式锁、排行榜建议同时开启AOF并且用appendfsync everysec来平衡性能与安全性。appendonly yes appendfsync everysec save 900 1 save 300 10 save 60 10000内存淘汰策略这是生产环境必配项。不配置的话Redis内存写满后会直接拒绝写入导致业务异常。常见选择是allkeys-lru对所有key使用LRU算法淘汰如果缓存数据中部分key绝对不能丢比如验证码就用volatile-lru只淘汰设置了过期时间的key。maxmemory 4gb maxmemory-policy allkeys-lrumaxmemory的值建议不超过物理内存的70%要给操作系统和Java应用留足空间。密码认证requirepass YourRedisPassword如果只是内网使用、又做了网络安全隔离不设密码也说得过去。但一旦Redis所在网络有公网暴露风险不设密码等于裸奔。我见过一台没设密码、默认端口6379的Redis上线第二天就被挖矿程序植入了定时任务。这不是危言耸听安全组的配置一定要核查。5.3 systemd管理Redis服务用dnf安装的Redis会自动注册systemd服务。手工修改配置文件后需要重启才能生效。systemctl enable --now redis systemctl restart redis systemctl status redis redis-cli -a YourRedisPassword ping # 返回 PONG 说明服务正常用redis-cli带密码的时候会有一个warning提示密码暴露在命令行历史中所以生产环境推荐用环境变量的方式验证REDISCLI_AUTHYourRedisPassword redis-cli ping5.4 Redis CLI的常用排查命令部署完后建议把下面这几条命令跑一遍确认运行状态redis-cli -a YourRedisPassword info memory | grep -E used_memory_human|maxmemory_human|mem_fragmentation_ratio redis-cli -a YourRedisPassword info clients | grep connected_clients redis-cli -a YourRedisPassword info stats | grep -E total_commands_processed|expired_keys重点关注mem_fragmentation_ratio内存碎片率。如果这个值长期大于1.5说明碎片太多可能需要重启Redis或者调整jemalloc相关参数。如果小于1则可能是开启了swap或者发生了内存溢出这种状态比碎片高更危险。6. Nginx部署与反向代理配置6.1 安装与基础目录Nginx在openEuler源里是稳定版直接用dnf安装。dnf install -y nginx nginx -v systemctl enable --now nginx装完立刻确认一下服务是否正常systemctl status nginx curl http://localhost看到Welcome to nginx或者openEuler的默认欢迎页就说明起步没毛病。很多人到这一步就跳过配置检查结果后面改了一堆配置重启后才发现语法错误浪费时间。6.2 配置核心思路反向代理Nginx在这里承担的是反向代理的角色外部流量先到Nginx再由Nginx把请求转发给后端Java应用。好处很多可以统一做SSL终结、负载均衡、静态资源分离、限流、日志记录。配置文件的习惯是不要动主配置/etc/nginx/nginx.conf而是在/etc/nginx/conf.d/下新建一个专用配置。主配置里通常有include /etc/nginx/conf.d/*.conf;天然支持这种方式。cat /etc/nginx/conf.d/myapp.conf EOF upstream myapp_backend { server 127.0.0.1:8080 weight5 max_fails3 fail_timeout30s; # 如果有多个应用实例继续加server行 # server 192.168.1.151:8080 weight5; keepalive 32; } server { listen 80; server_name api.example.com; access_log /var/log/nginx/myapp-access.log main; error_log /var/log/nginx/myapp-error.log warn; # 静态资源直接由Nginx处理不转给后端 location /static/ { alias /data/myapp/static/; expires 7d; add_header Cache-Control public, max-age604800; } # 后端API请求转发 location /api/ { proxy_pass http://myapp_backend; 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 5s; proxy_read_timeout 60s; } location / { proxy_pass http://myapp_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } EOF # 测试配置语法 nginx -t # 优雅重载服务不中断 nginx -s reload6.3 静态资源与动静分离Java应用里的Spring Boot默认使用内嵌Tomcat处理静态资源性能远不如Nginx直接返回。对于图片、CSS、JS这类资源让Nginx直接读磁盘能减轻Tomcat的压力也提升用户访问速度。部署时把Spring Boot的静态文件路径配置到/data/myapp/static/Nginx直接alias到这个目录。注意alias和root的区别root /data/myapp/static/会拼上URI路径alias则直接映射到指定目录配置静态资源推荐用alias避免多了一层目录。6.4 配置HTTPS如果域名已经备案、有正规证书强烈推荐上HTTPS。openEuler 24.03上Nginx开启SSL很简单用Lets Encrypt的证书或公司内部证书都行。server { listen 443 ssl http2; server_name api.example.com; ssl_certificate /etc/nginx/certs/api.example.com.crt; ssl_certificate_key /etc/nginx/certs/api.example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 非HTTPS请求自动跳转 if ($scheme ! https) { return 301 https://$host$request_uri; } }证书获取这块不展开Lets Encrypt的certbot工具在openEuler上可以正常使用。企业内网环境可以搭建私有CA把CA证书下发到各服务器和终端也能实现HTTPS加密。6.5 systemd、日志切割与文件句柄Nginx的systemd服务文件里默认LimitNOFILE可能不够高。高并发场景下Nginx会报too many open files哪怕worker_rlimit_nofile设置了也白搭因为systemd层面的限制优先级更高。mkdir -p /etc/systemd/system/nginx.service.d cat /etc/systemd/system/nginx.service.d/override.conf EOF [Service] LimitNOFILE65535 EOF systemctl daemon-reload systemctl restart nginx日志切割用Nginx自带的手段是做信号量重开日志文件配合logrotate。openEuler上安装Nginx后/etc/logrotate.d/nginx这个配置通常会自带确认一下里面的log路径对不对就行。对/var/log/nginx/*.log做按天切割、保留7天基本够用。7. 组件联调与高频踩坑排查7.1 一个完整的部署顺序建议这四个组件不是装完就完事联调时的顺序很关键。我的建议是先装Java本地把Java应用跑起来确认端口通再装MySQL建库建账号让Java应用连上MySQL验证CRUD装Redis让Java应用写入Redis缓存验证读缓存和过期策略最后装Nginx把80端口代理到Java应用的端口验证从浏览器到Nginx到Java的完整链路这个顺序能保证每一步都只排查一个变量。如果全装完再一把梭联调出了问题你根本不知道是该查DB还是查Redis还是查Nginx。7.2 Java应用崩溃最常见的几个原因堆内存不足。Spring Boot默认堆内存是物理内存的1/4如果机器内存小、机器上还跑着MySQL和RedisJava很容易OOM。启动时显式指定java -Xms512m -Xmx1024m -jar myapp.jarJDBC驱动版本不兼容。openEuler上如果MySQL版本是8.0.x应用的mysql-connector-java驱动建议用8.0.x配套的版本不要用5.x驱动不然会报Communications link failure或者Public Key Retrieval is not allowed。后者可以在JDBC URL后面加allowPublicKeyRetrievaltrue来规避但治本的方法是用配套驱动版本。Redis连接池耗尽。如果应用里用Lettuce客户端默认池大小有限高并发下会出现Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException。调大配置是一方面更重要的是检查代码里有没有忘记释放连接的地方。Spring Boot的RedisTemplate如果使用不当比如在循环里创建连接而不归还就是用多大的池都不够。7.3 MySQL时区、字符集与大小写敏感这三个问题我在这套环境上每次重装都会遇到一次列一下自查清单时区报错参考前面配置default-time-zone08:00同时检查JDBC URL中是否还有serverTimezoneAsia/Shanghai两个都配上并不冲突。中文乱码MySQL侧确认character-set-serverutf8mb4同时建库时显式DEFAULT CHARACTER SET utf8mb4不要指望默认值。Java侧在Spring Boot的连接URL上加上characterEncodingUTF-8。表名大小写lower_case_table_names1后Hibernate/JPA生成的表名是全小写如果代码中使用了Table(name UserInfo)这样的混合命名Hibernate会自动转成userinfoLinux文件系统敏感SQL里写UserInfo会找不到表。统一用lower_case_table_names1并在代码里全部用小写表名和字段名能省去99%的烦恼。7.4 Nginx 502/504的排查链路Nginx一切正常但页面报502/504的时候不要先去怀疑Nginx配置问题。我自己的排查路径是固定的看Nginx错误日志tail -f /var/log/nginx/myapp-error.log能看到“connect() failed (111: Connection refused)”还是“upstream timed out”。确认Java应用还活着curl http://127.0.0.1:8080/actuator/health不通就去看Java日志通常是应用挂了或GC卡死。如果是Connection refusedJava应用挂了或者端口没监听。如果是Connection reset by peerJava应用可能正在重启或线程池已满检查Tomcat的server.tomcat.max-threads配置。如果是upstream timed out大多数请求在Java侧处理超过Nginx的proxy_read_timeout默认60s适当调大到120s同时排查后端是否有慢SQL或外部接口调用阻塞。注意Nginx的proxy_connect_timeout默认是60s内网环境下一般1-5s就够了。如果设置太长后端不可用时用户会一直转圈等待。调短后用户体验会好很多也更方便及时发现问题。7.5 SELinux和网络不通的特殊情况如果你没有关掉SELinux就要注意Nginx转发后端的坑。OpenEuler的SELinux默认策略中httpd相关的布尔值默认是不允许网络请求的。# 检查是否开启 getsebool httpd_can_network_connect # 开启 setsebool -P httpd_can_network_connect 1-P参数让它永久生效。如果不开这个Nginx到Tomcat的转发会被SELinux拦下来表现就是501/502而Nginx和Java日志里都没有明显异常纯粹是环境问题。这条经验值好几个小时。7.6 服务开机自启的完整方案部署完成后四个服务的开机自启都要配置好不能依赖人工手工拉起。systemctl enable mysqld systemctl enable redis systemctl enable nginxJava应用这里有些人喜欢用nohuprc.local我说实话非常不推荐。rc.local的启动顺序不可控而且没有守护进程功能Java进程崩了不会自动拉起。更规范的做法是写一个systemd服务文件cat /etc/systemd/system/myapp.service EOF [Unit] DescriptionMy Spring Boot Application Afternetwork.target mysqld.service redis.service Wantsmysqld.service redis.service [Service] Typesimple Userappuser Groupappuser WorkingDirectory/data/myapp ExecStart/usr/bin/java -Xms512m -Xmx1024m -jar /data/myapp/myapp.jar ExecStop/bin/kill -s TERM $MAINPID SuccessExitStatus143 Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable --now myapp注意Aftermysqld.service redis.service只是保证MySQL和Redis的服务单元被触发并不保证它们真正就绪。所以Java应用里要有重连机制启动时数据库暂时连不上也要能重试不能一失败就退出。Spring Boot的HikariCP默认初始化失败会快速失败可以在配置里加spring.datasource.hikari.initialization-fail-timeout60000或者干脆设成-1让它无限重试。8. 写在最后几个可能影响你决策的细节整套环境从系统安装到四个组件全部调通我大概花了半天时间。对比之前在CentOS 7上从零部署的流程openEuler 24.03 LTS的整体感觉是“熟悉又不完全一样”。熟悉的是RPM生态、systemd管理、路径结构不完全一样的是默认软件源里的版本号、个别工具的依赖行为和SELinux策略。有几个细节我觉得有实际参考价值升级内核和驱动前先备份。openEuler的内核是自维护的升级有风险。如果你用到了一些特殊的硬件驱动或内核模块升级前先在测试机验证不要在生产直接dnf update一把梭。openEuler源中某些软件的包名和CentOS不一样。比如Nginx、Redis这种都是直接同名但有些工具会带后缀装之前先dnf search一下别凭老经验硬装。如果你是从CentOS的yum习惯转过来的openEuler的dnf命令参数几乎无缝yum命令本身在24.03上也能用它是dnf的别名兼容。脚本里的yum install xxx不用特意改。遇到问题优先看官方文档。openEuler的文档站内容很全包括系统管理、软件安装、迁移指南很多问题不用去百度查二手答案直接翻官方文档反而更快。如果看到这里你想给现有的CentOS机器做迁移我给的建议是先挑一台非核心业务机器按这套流程完整跑一遍确认你的应用兼容性没问题再批量切换。别看这个过程简单在系统版本切换这种事情上测试环境再充分都不为过。