上一篇 下一篇 分享链接 返回 返回顶部

韩国游戏服务器上线后进程意外退出,如何通过守护配置与日志定位原因?

发布人:Minchunlin 发布时间:2026-10-02 15:48 阅读量:5

一台用于承载韩国游戏服务器的 Linux 主机在上线后出现了“玩家间歇性掉线、管理接口返回 502、进程每隔几分钟重新出现”的现象。现场不能先把问题归因于反向代理或证书,第一步应确认:究竟是游戏进程主动退出、被系统终止,还是进程仍在运行但监听端口或代理链路异常。

建立韩国游戏服务器上线后出现进程与入口异常的真实排障现场。

最有效的判断路径是先看 systemd 的退出结果,再对照应用日志和内核日志,最后检查 Nginx 的上游连接与证书。若日志显示 status=9/KILL 并伴随内核内存不足记录,应优先处理资源边界;若显示 status=0/SUCCESS,则进程可能是正常退出,Restart=on-failure 不会自动拉起它;若游戏端口没有监听,Nginx 的 502 或连接拒绝通常只是结果,不是根因。

现场先确认:退出的是进程,还是入口链路

下面以 Ubuntu 22.04/24.04、systemd 管理游戏进程、Nginx 负责管理接口或 TCP/UDP 转发为例。路径、端口和启动参数需要替换成实际值,示例中的 korean-game.service、27015 和域名均为假设值。

假设故障时间为 21:10,现场先执行:

sudo systemctl status korean-game.service --no-pager -l
sudo systemctl show korean-game.service \
  -p ActiveState \
  -p SubState \
  -p Result \
  -p ExecMainCode \
  -p ExecMainStatus \
  -p MainPID \
  -p NRestarts \
  -p MemoryCurrent \
  -p MemoryPeak

重点关注以下结果:

观察结果通常意味着什么下一步
ActiveState=failed,ExecMainStatus=1程序报错退出、配置或依赖异常查看应用日志和启动参数
status=0/SUCCESS程序主动正常退出,或收到正常停止信号检查应用自停逻辑、定时任务和部署脚本
status=9/KILL可能被 OOM、管理员命令或系统强制终止查看内核日志、资源限制和操作审计
status=203/EXECExecStart 路径、权限或可执行文件有问题检查文件存在性和执行权限
status=217/USERUser= 或 Group= 配置无法使用检查系统用户和目录权限
服务为 active,但端口未监听启动参数错误、程序卡住,或监听了其他地址/端口查看进程命令行和监听列表
服务为 active,端口正常,但外部访问失败Nginx 配置、协议类型、证书或入口防护链路有问题检查 Nginx 和本地直连结果

systemctl status 只能显示最近状态,不能替代完整日志。不要在证据收集前连续执行多次 restart,否则原始退出原因可能被新的启动信息覆盖。

关键线索:按时间把三类日志对齐

1. 查看服务日志

先按故障时间截取,不要一开始就只看最新几十行:

sudo journalctl -u korean-game.service \
  --since "2026-10-02 21:00:00" \
  --until "2026-10-02 21:20:00" \
  -o short-iso \
  --no-pager

如果服务只输出了启动信息,没有业务错误,再确认是否把标准输出和标准错误接入了 journald。可以检查:

sudo systemctl cat korean-game.service
sudo systemctl show korean-game.service \
  -p StandardOutput \
  -p StandardError \
  -p WorkingDirectory \
  -p ExecStart

典型的异常线索包括:

2026-10-02T21:10:14+0800 game-server[1842]: config file not found: /etc/korean-game/server.yml
2026-10-02T21:10:14+0800 systemd[1]: korean-game.service: Main process exited, code=exited, status=1/FAILURE

这类结果说明程序确实启动过,但因配置或依赖失败,并非 Nginx 将其“踢下线”。

2. 查看内核和资源日志

进程被系统内存回收时,应用日志可能来不及写出完整错误,内核日志往往更直接:

sudo journalctl -k \
  --since "2026-10-02 21:00:00" \
  --until "2026-10-02 21:20:00" \
  --no-pager

重点搜索:

sudo journalctl -k --since "1 hour ago" --no-pager \
  | grep -Ei "out of memory|oom|killed process|memory cgroup|segfault"

假设看到:

kernel: Memory cgroup out of memory: Killed process 1842 (server)
systemd[1]: korean-game.service: Main process exited, code=killed, status=9/KILL

这时不要简单地把 RestartSec 调得更短。守护程序只会重复启动和被杀死,反而可能造成频繁加载存档、日志膨胀和玩家重复掉线。

如果系统启用了崩溃转储,还可以查询:

sudo coredumpctl list korean-game.service
sudo coredumpctl info korean-game.service

崩溃转储可能包含内存中的账号、配置或会话信息,只允许具备权限的运维人员读取,并确认磁盘有足够空间。若没有任何转储记录,不代表没有崩溃,可能是系统未启用收集、磁盘空间不足或程序被强制终止。

3. 检查端口、Nginx 和证书

先确认游戏程序实际监听的端口:

sudo ss -lntup
sudo ss -lntup | grep -E ':27015|:8080|:443'

再查看 Nginx 日志:

sudo journalctl -u nginx \
  --since "2026-10-02 21:00:00" \
  --until "2026-10-02 21:20:00" \
  --no-pager

sudo tail -n 100 /var/log/nginx/error.log

几类结果的判断方式如下:

  • connect() failed (111: Connection refused):Nginx 能找到目标地址,但目标端口没有进程监听,或监听地址不匹配。
  • upstream timed out:目标进程可能卡顿、资源耗尽、协议类型配置不匹配,或者超时时间不适合游戏心跳。
  • 管理接口出现 502,但游戏进程和游戏端口正常:大概率是管理接口端口、HTTP 配置或管理程序的问题。
  • Nginx 报证书过期、证书链错误或私钥无法读取:通常影响 HTTPS 管理入口,不会直接终止独立运行的游戏进程。
  • 游戏进程自己的日志出现 certificate load failed:说明证书由游戏程序直接加载,此时应按应用错误处理,而不是只检查 Nginx。

如果需要检查证书有效期:

sudo openssl x509 \
  -in /etc/letsencrypt/live/admin.example.test/fullchain.pem \
  -noout \
  -subject \
  -issuer \
  -dates

admin.example.test 只是示例域名。证书和私钥路径必须替换为实际文件。检查 Nginx 配置时使用:

sudo nginx -t

只有显示语法检查成功后,才执行重新加载:

sudo systemctl reload nginx

reload 一般不会中断已有连接,但配置错误时不应强行执行 restart。证书更新也应只重新加载 Nginx,不要因为证书变化而无条件重启有状态的游戏进程。

处置步骤:先建立可观察的进程,再接入入口

第一步:确认前置条件并备份

执行配置变更前,至少确认以下信息:

  • 操作系统和 Nginx 的实际版本;
  • 游戏二进制文件、启动参数、工作目录和运行用户;
  • 游戏使用的 TCP 或 UDP 端口;
  • 管理接口是否为 HTTP/HTTPS;
  • 存档目录、配置目录和证书目录;
  • 当前是否允许短暂中断玩家连接。

先检查资源和端口:

uname -a
systemctl --version | head -n 1
nginx -v
free -h
df -h
nproc
sudo ss -lntup

备份 systemd、Nginx 和应用配置。以下命令会把可能包含密钥和内部地址的文件打包到 /root,应限制访问权限,并根据实际路径调整:

sudo tar -czf /root/korean-game-deploy-$(date +%F-%H%M).tgz \
  /etc/systemd/system/korean-game.service \
  /etc/nginx/nginx.conf \
  /etc/nginx/conf.d \
  /etc/nginx/stream.d \
  /etc/korean-game
sudo chmod 600 /root/korean-game-deploy-*.tgz

存档备份需要考虑一致性。直接复制正在写入的存档,可能得到不完整文件。若应用没有快照机制,应安排维护窗口,先停止游戏服务,再备份存档目录:

sudo systemctl stop korean-game.service
sudo tar -czf /root/korean-game-save-$(date +%F-%H%M).tgz \
  /var/lib/korean-game
sudo systemctl start korean-game.service

停止服务会中断现有会话,且可能触发应用保存流程,不能在没有维护安排时执行。

第二步:用 systemd 明确守护行为

如果当前通过 screen、后台符号或临时脚本启动,建议先改为独立的 systemd 服务。下面是一个参考单元:

[Unit]
Description=Korean Game Server
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Type=simple
User=game
Group=game
WorkingDirectory=/opt/korean-game
ExecStart=/opt/korean-game/bin/server --config /etc/korean-game/server.yml

Restart=on-failure
RestartSec=5s

LimitNOFILE=65535
StandardOutput=journal
StandardError=journal

KillSignal=SIGTERM
KillMode=control-group
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

几个配置项需要结合程序行为判断:

  • ExecStart 必须使用绝对路径,参数中不要依赖交互式 Shell 环境变量。
  • WorkingDirectory 要与程序读取相对路径配置、存档和插件的方式一致。
  • Restart=on-failure 只会在异常退出或被信号终止时重启。程序以状态码 0 退出时,systemd 会认为这是正常结束。
  • 如果应用能够正确处理 SIGTERM,KillMode=control-group 可以让同一服务创建的子进程一起结束,避免残留进程继续占用端口。
  • LimitNOFILE 只解决文件描述符上限,不会解决内存不足或程序自身连接池耗尽。

应用用户已经存在时,先核对权限:

id game
sudo -u game test -x /opt/korean-game/bin/server
sudo -u game test -r /etc/korean-game/server.yml
sudo -u game test -w /var/lib/korean-game

确认单元文件和权限无误后:

sudo systemctl daemon-reload
sudo systemctl enable korean-game.service
sudo systemctl start korean-game.service
sudo systemctl status korean-game.service --no-pager -l

如果服务启动后立即失败,不要反复重启,直接读取本次启动日志:

sudo journalctl -u korean-game.service -b -n 200 --no-pager

第三步:让日志能够支撑定位

使用 journald 时,服务的标准输出和标准错误应保持可查询。若担心系统重启后日志消失,可以检查持久化日志目录:

sudo test -d /var/log/journal && echo "persistent journal enabled" \
  || echo "persistent journal not found"

在确认磁盘空间足够后,可以建立持久化目录:

sudo install -d -m 2755 -o root -g systemd-journal /var/log/journal
sudo systemctl restart systemd-journald

这一步会影响系统日志服务,但通常不会停止游戏进程。不要在故障尚未分析前执行以下清理动作:

journalctl --vacuum-time=1d
journalctl --vacuum-size=100M

它们会删除旧日志,可能破坏故障证据。日志保留策略应在完成备份和分析后,再根据磁盘容量设置。

如果游戏程序必须写独立日志,应确认目录由运行用户可写,并配置应用自身的轮转策略。不要让日志文件无限增长;可以先观察:

sudo du -sh /var/log/* /var/lib/korean-game 2>/dev/null | sort -h
sudo lsof -p "$(systemctl show korean-game.service -p MainPID --value)" \
  | grep -E '\.log|/var/log'

第四步:正确区分游戏转发和 HTTPS 管理入口

游戏协议不能直接套用 HTTP 反向代理。Nginx 的 http {} 适合管理面板、状态接口和 API;TCP/UDP 游戏流量应使用 Nginx 的 stream {},并且协议类型必须与游戏实际使用方式一致。

解释游戏转发链路与HTTPS管理入口必须分离的协议和配置边界。

图示对应原文命令:http {}。

先确认 Nginx 是否具备 stream 模块:

nginx -V 2>&1 | grep -- '--with-stream'

如果使用 UDP 游戏端口,配置可以是以下形式。该配置应放在 Nginx 顶层的 stream {} 中,不能嵌套到 http {} 内:

stream {
    log_format game_stream
        '$remote_addr [$time_local] '
        '$protocol $status '
        'upstream=$upstream_addr '
        'sent=$bytes_sent received=$bytes_received '
        'session=$session_time';

    access_log /var/log/nginx/game-stream-access.log game_stream;

    upstream korean_game_udp {
        server 127.0.0.1:27015;
    }

    server {
        listen 27015 udp;
        proxy_pass korean_game_udp;

        # 应大于游戏正常心跳间隔,示例值仅供参考
        proxy_timeout 120s;
    }
}

如果游戏实际使用 TCP,应改为 TCP 监听形式,不要同时保留错误的 UDP 配置:

server {
    listen 27015;
    proxy_pass 127.0.0.1:27015;
    proxy_timeout 1h;
}

UDP 的 proxy_timeout 不是越大越好。过小可能误判空闲会话,过大则会保留大量失效会话。应根据游戏心跳间隔、并发连接数和资源余量调整。

管理接口的 HTTPS 配置应与游戏数据端口分开。例如:

server {
    listen 443 ssl;
    server_name admin.example.test;

    ssl_certificate     /etc/letsencrypt/live/admin.example.test/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/admin.example.test/privkey.pem;

    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;
    }
}

证书只用于 HTTPS 管理面板或 API。除非游戏协议本身支持并要求加密,否则不要把游戏 UDP 端口错误地放进 HTTPS 配置中。完成修改后按以下顺序验证:

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager -l
sudo ss -lntup | grep -E ':27015|:443'

第五步:设置资源边界,但不要把上限设成陷阱

进程意外退出经常与内存、文件描述符、CPU 或磁盘边界有关。可以先读取 systemd 看到的运行数据:

sudo systemctl show korean-game.service \
  -p MainPID \
  -p MemoryCurrent \
  -p MemoryPeak \
  -p CPUUsageNSec \
  -p TasksCurrent

再读取进程级限制:

pid="$(sudo systemctl show korean-game.service -p MainPID --value)"
sudo cat "/proc/$pid/limits"

如果主机有 16 GiB 内存,系统和其他必要服务大约需要 2 至 4 GiB,且游戏进程峰值长期接近 10 至 11 GiB,可以考虑设置一个留有余量的边界,例如:

[Service]
MemoryHigh=12G
MemoryMax=14G
CPUQuota=700%
TasksMax=4096
LimitNOFILE=65535

这些数值只是参考配置,不应直接套用到所有韩国游戏服务器:

  • MemoryHigh 是压力线,达到后系统会施加回收压力;
  • MemoryMax 是硬上限,超过后服务可能被 cgroup 终止;
  • CPUQuota=700% 大致表示允许使用 7 个 CPU 核心的计算时间,但具体效果取决于主机核数和程序并发模型;
  • TasksMax 限制进程及其子线程数量,设置过小可能导致线程创建失败;
  • LimitNOFILE 只适用于程序确实需要大量连接或文件句柄的情况。

修改资源边界前,必须确认当前峰值、存档加载峰值和玩家高峰期负载。若日志出现 Memory cgroup out of memory,优先提高可用内存、减少程序实际占用或重新规划服务分配,不要只把 Restart=always 打开来掩盖问题。

第六步:用本地结果和入口结果分层验证

验证应先从本机到进程,再从 Nginx 到进程,最后才测试外部玩家连接。

先确认进程状态稳定:

sudo systemctl is-active korean-game.service
sudo systemctl show korean-game.service \
  -p MainPID \
  -p NRestarts \
  -p ExecMainStatus \
  -p MemoryCurrent \
  -p MemoryPeak

TCP 服务可以使用:

nc -vz 127.0.0.1 27015

UDP 没有统一的通用握手,nc -u 显示发送成功也不能证明游戏协议正常。UDP 服务应使用游戏客户端、管理接口或应用自带健康检查验证,同时结合:

sudo ss -uapn | grep ':27015'
sudo tail -f /var/log/nginx/game-stream-access.log

管理接口则可以检查本地 HTTPS 响应:

curl -kI https://127.0.0.1/ \
  -H 'Host: admin.example.test'

-k 只适合本机临时排查证书名称不匹配,不应作为正式客户端验证方式。正式检查应使用正确域名并验证证书链。

一次较完整的成功标准包括:

  • korean-game.service 持续处于 active;
  • 在观察窗口内 NRestarts 不再增加;
  • 游戏端口由预期进程监听;
  • Nginx 配置测试成功,HTTPS 管理接口证书未过期;
  • Nginx 日志不再出现持续性的连接拒绝或上游超时;
  • 内核日志没有新的 OOM、段错误或 cgroup 强制终止;
  • 进程内存峰值没有贴近 MemoryMax;
  • 玩家连接、管理接口和日志时间能够相互对应。

常见失败分支与回滚方式

systemd 配置后服务无法启动

先不要删除原文件,查看具体错误:

sudo journalctl -u korean-game.service -b -n 200 --no-pager
sudo systemd-analyze verify /etc/systemd/system/korean-game.service

如果确认新单元文件有问题,在已经备份的前提下恢复:

sudo cp -a /etc/systemd/system/korean-game.service.bak \
  /etc/systemd/system/korean-game.service
sudo systemctl daemon-reload
sudo systemctl start korean-game.service

备份文件名可能不同,恢复前应先用 ls -l /etc/systemd/system/ 核对。若服务已经进入重启循环,先停止服务再恢复,停止动作会中断游戏连接:

sudo systemctl stop korean-game.service

Nginx 配置或证书变更失败

nginx -t 失败时,不要执行 reload。恢复配置后再次检查:

sudo cp -a /etc/nginx/nginx.conf.bak \
  /etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginx

如果只是证书文件替换失败,应恢复成一对匹配的证书和私钥,再执行 nginx -t。不要只替换证书而保留不匹配的私钥,也不要为了临时恢复而关闭 HTTPS 校验或暴露未加密的管理入口。

设置 MemoryMax 后仍然退出

如果日志明确显示触发了内存上限,应先确认是否为 systemd 设置导致:

sudo systemctl show korean-game.service \
  -p MemoryHigh \
  -p MemoryMax \
  -p MemoryCurrent \
  -p MemoryPeak

回滚方式是恢复原单元文件或提高边界,但提高前要确认主机还留有系统运行空间。若整台主机已经接近满内存,盲目提高 MemoryMax 可能把问题转化为全局 OOM。

Nginx 显示正常但玩家仍掉线

此时对照三条时间线:

展示玩家掉线排查时如何将systemd、Nginx stream和游戏进程日志按故障窗口对齐。

  1. korean-game.service 是否在掉线时间发生重启;
  2. Nginx stream 日志是否出现会话超时或上游断开;
  3. 游戏进程是否有连接数、线程数、文件描述符或协议错误。

如果本机直连游戏端口稳定,而经由 Nginx 失败,优先检查 listen 使用的是 TCP 还是 UDP、上游端口是否一致,以及 proxy_timeout 是否短于游戏心跳间隔。不要通过反复重启进程来处理一个纯入口配置问题。

复盘时容易漏掉的边界

生产环境中,守护配置、日志、反向代理和证书解决的是不同层次的问题:

  • systemd 负责进程生命周期,不能修复程序自身的配置错误;
  • Nginx 负责入口和转发,不能证明后端游戏逻辑正常;
  • 证书负责 HTTPS 身份与加密,通常不会导致独立游戏进程退出;
  • 日志负责还原时间线,但日志目录满盘后也可能让程序异常;
  • 资源边界用于防止单个服务挤占整台主机,设置过低同样会主动制造退出;
  • 备份不仅包括配置文件,还包括可恢复的存档、证书私钥和当前生效的服务单元。

最后应保留一次故障前后的状态记录,包括 systemctl show、journalctl、journalctl -k、ss -lntup、Nginx 配置测试结果和资源峰值。下一次再出现“进程意外退出”时,可以先按退出码定位,再按端口和代理日志确认入口,避免把 502、证书错误或玩家掉线现象误认为同一个故障。

目录结构
全文