java -jar、systemd、Docker、Kubernetes,到底该怎么选?一张表讲清楚

上一篇文章发布后,有位读者留言:

“道理都懂,我还是 java -jar。”

看到这句话,我其实挺理解。

java -jar 简单、直接,一条命令就能把服务跑起来。很多业务规模不大,服务器也只有一两台,为了部署一个 Java 服务就引入整套容器平台,确实可能把简单问题复杂化。

所以问题不应该是:

java -jar 到底能不能用?

而应该是:

服务退出后谁来拉起?
服务器重启后谁来启动?
日志写满后谁来处理?
发布失败后怎么回滚?
实例多起来以后谁来调度?

java -jar 没有错。

真正危险的是,团队把“启动应用的命令”当成了“生产服务管理方案”。


一、先分清四种方式分别解决什么问题

这四种方式并不是简单的升级关系。

它们解决的问题不同:

java -jar:启动一个 Java 应用
systemd:管理一台 Linux 服务器上的服务
Docker:把应用和运行环境封装成容器
Kubernetes:在多台机器上管理大量容器和实例

如果只是本地运行一个 JAR,java -jar 已经够了。

如果是一台服务器上的正式服务,需要自动启动和异常拉起,systemd 往往更合适。

如果团队希望统一运行环境、镜像和交付方式,可以使用 Docker。

如果已经进入多实例、多节点、滚动发布和弹性伸缩阶段,才需要认真评估 Kubernetes。

不要因为工具更复杂,就默认它更适合自己的项目。


二、什么时候直接 java -jar 就够了?

最常见的启动方式:

java -jar order-service.jar \
  --spring.profiles.active=dev

它非常适合:

本地开发
测试环境
临时验证
一次性任务
演示项目
可以随时人工重启的小工具

java -jar 会执行 JAR 中指定的应用入口,但这个命令本身不负责:

开机自动启动
进程退出后自动重启
启动失败次数限制
统一查看服务状态
权限隔离
日志生命周期
版本切换和回滚

有人会再套一层:

nohup java -jar order-service.jar > app.log 2>&1 &

这样可以让进程离开当前终端继续运行,但它仍然没有变成一套完整的服务管理方案。

所以我的判断是:

能接受人工处理故障,就可以直接 java -jar;不能接受,就要给它增加托管能力。


三、单机生产环境,systemd 通常最省心

假设你的系统符合下面这些条件:

只有一两台 Linux 服务器
Java 服务数量不多
暂时没有容器平台
团队熟悉传统运维
希望服务开机自启、异常拉起

这种情况下,不一定需要上 Docker 或 Kubernetes。

systemd 就能覆盖最核心的服务管理需求。

一个简化示例:

[Unit]
Description=Order Service
After=network-online.target

[Service]
User=orderapp
WorkingDirectory=/opt/order-service/current
ExecStart=/usr/bin/java -Xms1g -Xmx1g -jar app.jar
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=45

[Install]
WantedBy=multi-user.target

启用并启动:

sudo systemctl daemon-reload
sudo systemctl enable --now order-service

之后可以统一管理:

systemctl status order-service
systemctl restart order-service
journalctl -u order-service -f

systemd 带来的价值不是让 Java 跑得更快,而是把服务交给操作系统管理:

服务器启动时自动拉起
异常退出后按策略重启
明确运行用户和目录
统一发送停止信号
统一查询状态和日志

对于单机、小团队和传统部署环境,这往往是成本最低、收益最高的一步。


四、Docker 解决的重点,是环境一致性

很多人把 Docker 理解成“比 systemd 更高级的启动工具”。

其实 Docker 更核心的价值是把这些东西统一封装:

应用
JRE
系统依赖
启动命令
端口
环境变量
资源限制

同一份镜像从测试环境运行到生产环境,可以减少:

测试环境能跑,生产环境缺少依赖
服务器 JDK 版本不一致
手工复制文件漏掉配置
不同机器目录结构不一致

单机运行示例:

docker run -d \
  --name order-service \
  --restart unless-stopped \
  -p 8080:8080 \
  order-service:20260728

Docker 的重启策略可以在容器退出或 Docker 守护进程重启后,根据配置重新启动容器。

但 Docker 本身不会自动替你解决所有生产问题:

多台服务器之间如何调度
多个实例怎样滚动发布
流量怎样摘除和恢复
配置和密钥如何管理
日志如何长期采集
节点故障后实例迁移到哪里

还有一个容易踩坑的地方:

如果已经给容器配置了 Docker 重启策略,不要再让另一个宿主机进程管理器反复启动同一个容器。两套重启机制叠在一起,反而可能产生冲突和难以理解的状态。

所以 Docker 更适合:

希望统一交付环境
项目已经建立镜像构建流程
需要更清晰的资源和依赖边界
服务数量逐渐增加
但暂时不需要完整集群调度

五、Kubernetes 解决的是多实例和期望状态

当系统进入下面这种规模时,单独管理进程或容器会越来越吃力:

服务部署在多台节点
一个服务运行多个实例
需要滚动发布和快速回滚
节点故障后需要重新调度
需要服务发现和负载均衡
需要按负载扩缩容
团队已经有平台运维能力

Kubernetes 的核心思路不是“帮你运行一条命令”,而是维护期望状态。

例如你声明订单服务应该有 3 个副本:

spec:
  replicas: 3

如果其中一个 Pod 失败,控制器会尝试创建替代实例,让实际状态重新接近期望状态;节点不可用时,工作负载也可以在其他可用节点重新调度。

它适合解决:

实例编排
副本维护
服务发现
滚动更新
故障替换
资源调度
弹性扩缩

但 Kubernetes 也不是自动消除故障的魔法。

如果应用因为错误配置不断退出,它可能只会不断重启;如果探针设计错误,它甚至会把一次依赖抖动放大成连续重启。

Kubernetes 可以替换失败实例,但不会自动修复你的 SQL、线程池、内存泄漏和错误业务逻辑。

同时还要承担:

集群维护
网络和存储
监控与日志
配置与密钥
权限控制
版本升级
故障排查复杂度

如果只有两个 Java 服务和一台服务器,单纯为了“显得先进”而引入 Kubernetes,维护成本可能远大于收益。


六、一张表看清四种方式

对比项java -jarsystemdDockerKubernetes
主要职责启动应用单机服务管理环境封装与容器运行多节点工作负载编排
异常自动重启不负责支持配置重启策略后支持支持容器重启和副本替换
开机自动启动不负责支持依赖 Docker 与重启策略由集群维护期望状态
环境一致性较弱较弱
多实例管理人工单机内可管理单机内容器管理
滚动发布人工实现人工或脚本实现需要额外工具原生支持
学习维护成本最低中等
适合场景开发、测试、临时任务单机生产、小团队标准化交付、容器化多节点、多实例、平台化

没有一列是全绿的。

工具越往右,能力越强,团队要承担的维护成本也越高。


七、我的选择建议

场景一:本地开发或测试验证

直接使用:

java -jar app.jar

简单、透明、排查方便,没有必要增加额外复杂度。

场景二:一台服务器上的正式服务

优先考虑:

systemd + 固定部署目录 + 日志轮转 + 监控告警

它已经能解决大部分单机服务托管问题。

场景三:希望统一环境和交付方式

可以使用:

Docker 镜像 + 镜像仓库 + 明确的重启策略 + 日志采集

先把构建、配置、数据目录和监控处理好,再讨论更复杂的编排平台。

场景四:多节点、多实例和频繁发布

再评估:

Kubernetes + 标准化发布流程 + 完整可观测体系

前提是团队真的有人能够维护它,而不是只会把 YAML 部署进去。


八、选择前先回答三个问题

别先问“大家都在用什么”,先回答:

1. 服务是单机还是多节点?
2. 进程、日志、发布和回滚现在由谁负责?
3. 团队是否有能力长期维护引入的平台?

如果只有一台服务器,systemd 可能就是最务实的答案。

如果已经有成熟的容器平台,就没必要退回手工管理 JAR。

如果只是临时测试,直接 java -jar 反而最合适。

技术选型不是从简单走向复杂,而是找到当前阶段成本最低、故障有人兜底的方案。


写在最后

我并不反对 java -jar

我反对的是:

生产服务退出后没人知道
服务器重启后没人启动
日志写满磁盘后才发现
发布失败时找不到旧版本
出了问题只能靠人肉登录处理

一句话总结:

java -jar 是启动方式;systemd、Docker 和 Kubernetes 才是在不同规模下负责“谁来兜底”的方案。

你们生产环境现在使用 java -jar、Shell 脚本、systemd、Docker,还是 Kubernetes?

欢迎在评论区说说实际情况。也可以关注我。

如果你遇到服务无法启动、频繁重启、发布后 502 或容器探针异常,可以把脱敏后的错误日志、部署方式和环境说明发来,我会尽量帮你判断下一步应该查哪里。请务必隐藏密码、Token、IP、域名和客户数据。

更多推荐