部署与运维: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";
    }
}
小技巧: 如果你用了Ktor的WebSocket功能,别忘了加上Upgrade头。我曾经漏掉这个,结果WebSocket连接一直断,排查了半天。

配置好之后,重启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:轮转后压缩为.gz
  • copytruncate:先复制日志文件内容,再清空原文件(应用不需要重启)
  • postrotate:轮转后执行脚本,这里向Supervisor发送HUP信号,让应用重新打开日志文件
为什么用copytruncate? 因为Ktor应用打开日志文件后,如果直接删除或移动文件,应用仍然往旧文件描述符里写。copytruncate先复制再截断,应用无感知。

测试logrotate配置:

sudo logrotate -d /etc/logrotate.d/myapp  # 调试模式,不会实际执行
sudo logrotate -f /etc/logrotate.d/myapp  # 强制轮转

确认没问题后,logrotate会每天自动执行。你也可以配合cron定时任务,但一般系统自带的logrotate已经配置好了每日执行。

5. 整体架构一览

说了这么多,我们把整个部署架构串起来看看:

Ktor应用部署架构 🌐 外部用户 Nginx 反向代理 端口 80/443 → 转发到 127.0.0.1:8080 Supervisor 进程管理 监控 Ktor 应用,崩溃自动重启 Ktor 应用 (JAR) 监听 8080 端口 logrotate 日志轮转 每日切割、压缩、保留7天 ← 接收请求 ← 保活 ← 管理日志

整个流程很清晰:用户请求 → 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会重启,但你得知道它挂过。
曾经踩过的坑: 有一次我部署新版本,直接替换了JAR包然后supervisorctl restart myapp。结果新版本启动失败,Supervisor重试3次后放弃了,服务直接挂掉。后来我改成先停掉旧进程,确认新版本能启动,再切换流量。或者用蓝绿部署,稳妥得多。

好了,这一章的内容就到这里。部署运维这件事,说白了就是「让应用稳定运行,出了问题能快速恢复」。把Nginx、Supervisor、logrotate这三件套用好,你的Ktor应用就能在生产环境里安安稳稳地跑下去。


公众号:蓝海资料掘金营,微信deep3321