部署与运维:Linux部署、Nginx反向代理、Supervisor进程管理、日志轮转
说实话,很多做Android后端的朋友,写代码是一把好手,但一到部署环节就头疼。我当年第一次把Ktor应用扔到服务器上,以为直接java -jar就完事了,结果第二天早上发现进程挂了,日志也没了,用户反馈说App登不上去……嗯,那场面挺尴尬的。
这一章,我就把我在生产环境里摸爬滚打总结出来的经验,掰开揉碎了讲给你听。从Linux部署到Nginx反向代理,再到Supervisor保活和日志轮转,一条龙搞定。
1. Linux部署:从JAR包到系统服务
Ktor应用最终打包成一个可执行的JAR包。我个人习惯用Gradle的shadowJar插件,把所有依赖打成一个胖JAR,省心。
部署的第一步,是把JAR包传到服务器上。我一般用scp或者rsync,配合CI/CD流水线自动推送。
# 示例:使用rsync同步JAR包到服务器
rsync -avz build/libs/my-ktor-app-1.0.0-all.jar user@your-server:/opt/myapp/app.jar
然后,直接运行试试看:
java -jar /opt/myapp/app.jar
能跑起来,但问题来了——关掉终端,进程就没了。你想想看,生产环境哪能这么搞?
我建议的做法是:先创建一个专用的系统用户,避免直接用root跑应用。安全第一。
sudo useradd -m -s /bin/bash ktoruser
sudo mkdir -p /opt/myapp
sudo chown -R ktoruser:ktoruser /opt/myapp
然后,把JAR包丢进去,用nohup测试一下后台运行:
sudo -u ktoruser nohup java -jar /opt/myapp/app.jar > /opt/myapp/app.log 2>&1 &
但这只是临时方案。真正生产环境,我们需要更专业的进程管理工具——Supervisor。
2. Nginx反向代理:为什么需要它?
Ktor应用默认监听8080端口。但直接暴露应用端口到公网,有几个问题:
- 端口管理混乱:多个应用需要不同端口
- SSL/TLS证书配置麻烦
- 无法做负载均衡
- 静态资源处理效率低
Nginx就是来解决这些问题的。它站在Ktor前面,接收所有外部请求,然后转发给Ktor。
我在项目中遇到过一个问题:客户端上传大文件时,Ktor直接处理会导致内存飙升。后来用Nginx做反向代理,配合client_max_body_size和缓冲机制,轻松搞定。
下面是一个典型的Nginx配置:
server {
listen 80;
server_name api.yourdomain.com;
# 将请求转发到Ktor应用
location / {
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;
# WebSocket支持(Ktor常用)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
# 静态资源直接由Nginx处理
location /static/ {
root /var/www/myapp;
expires 7d;
add_header Cache-Control "public, immutable";
}
}
配置好之后,重启Nginx:
sudo nginx -t # 先检查配置语法
sudo systemctl reload nginx
现在,访问http://api.yourdomain.com,请求就会被转发到Ktor的8080端口。你想想看,这样是不是清爽多了?
3. Supervisor进程管理:让应用永不掉线
用nohup跑应用,一旦进程崩溃,没人管。Supervisor就是专门干这个的——监控进程状态,挂了自动重启。
安装Supervisor:
sudo apt-get install supervisor # Ubuntu/Debian
sudo yum install supervisor # CentOS/RHEL
然后,为Ktor应用创建一个配置文件:
sudo vim /etc/supervisor/conf.d/myapp.conf
内容如下:
[program:myapp]
command=java -jar /opt/myapp/app.jar
directory=/opt/myapp
user=ktoruser
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/myapp/error.log
stdout_logfile=/var/log/myapp/out.log
environment=JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64",APP_ENV="production"
解释几个关键参数:
| 参数 | 说明 |
|---|---|
autostart=true |
Supervisor启动时自动启动该程序 |
autorestart=true |
程序退出后自动重启 |
startretries=3 |
启动失败时重试3次 |
user=ktoruser |
以指定用户身份运行 |
/var/log/myapp需要提前创建,并确保ktoruser有写入权限。我曾经忘了这一步,结果Supervisor启动后日志死活不输出,排查了半天才发现是权限问题。
配置完成后,让Supervisor加载并启动:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myapp
常用管理命令:
sudo supervisorctl status # 查看所有进程状态
sudo supervisorctl restart myapp # 重启应用
sudo supervisorctl stop myapp # 停止应用
sudo supervisorctl tail -f myapp # 实时查看日志
现在,就算Ktor应用因为OOM或者其他原因挂了,Supervisor会在几秒内把它拉起来。你想想看,是不是比nohup靠谱一万倍?
4. 日志轮转:别让日志撑爆磁盘
应用跑久了,日志文件会越来越大。我见过一个项目,日志文件撑到了50GB,直接把磁盘占满,导致整个服务不可用。
Linux自带的logrotate就是用来解决这个问题的。它按天、按大小切割日志,还能压缩、删除旧日志。
创建一个logrotate配置文件:
sudo vim /etc/logrotate.d/myapp
内容如下:
/var/log/myapp/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
dateext
dateformat -%Y%m%d
postrotate
supervisorctl signal HUP myapp
endscript
}
参数含义:
daily:每天轮转一次rotate 7:保留最近7天的日志compress:轮转后压缩为.gzcopytruncate:先复制日志文件内容,再清空原文件(应用不需要重启)postrotate:轮转后执行脚本,这里向Supervisor发送HUP信号,让应用重新打开日志文件
测试logrotate配置:
sudo logrotate -d /etc/logrotate.d/myapp # 调试模式,不会实际执行
sudo logrotate -f /etc/logrotate.d/myapp # 强制轮转
确认没问题后,logrotate会每天自动执行。你也可以配合cron定时任务,但一般系统自带的logrotate已经配置好了每日执行。
5. 整体架构一览
说了这么多,我们把整个部署架构串起来看看:
整个流程很清晰:用户请求 → Nginx(反向代理+SSL) → Supervisor(管理Ktor进程) → Ktor应用处理请求。同时,logrotate在后台默默管理日志文件,防止磁盘爆满。
6. 一些实战建议
最后,分享几个我在生产环境里踩过的坑:
- JVM参数调优: 别用默认参数。我一般会设置
-Xms512m -Xmx1024m,根据业务量调整。如果内存给太少,Ktor在高并发下容易OOM。 - 健康检查接口: 在Ktor里加一个
/health端点,返回200。Nginx可以用它做被动健康检查,Supervisor也可以用httpok插件监控。 - 优雅关闭: Ktor支持优雅关闭,在
application.conf里设置ktor.deployment.shutdown.timeout = 30,让正在处理的请求完成后再退出。 - 监控告警: 配合Prometheus+Grafana,或者简单的Shell脚本+钉钉/微信通知。进程挂了虽然Supervisor会重启,但你得知道它挂过。
supervisorctl restart myapp。结果新版本启动失败,Supervisor重试3次后放弃了,服务直接挂掉。后来我改成先停掉旧进程,确认新版本能启动,再切换流量。或者用蓝绿部署,稳妥得多。
好了,这一章的内容就到这里。部署运维这件事,说白了就是「让应用稳定运行,出了问题能快速恢复」。把Nginx、Supervisor、logrotate这三件套用好,你的Ktor应用就能在生产环境里安安稳稳地跑下去。
公众号:蓝海资料掘金营,微信deep3321