botfromzero
本章目录
    Telegram 机器人实战 第 5 部分 · 让它一直跑着

    第 10 章

    部署上线

    第 1 章说过:机器人就是你电脑上的一个进程,关掉就没了。这一章把它挪到一台不关机的机器上——开机自启、崩了自动重来、出事能查。

    约 23 分钟最后一公里aiogram 3.x · 最后验证 2026-08
    01

    第 1 章那句话,现在来兑现

    开篇就说过:机器人是跑在你电脑上的一个进程,你关掉电脑它就死了。前九章一直是这样——终端开着它就活着,Ctrl+C 它就没了。

    这一章把它挪到一台不关机的机器上,并且做到三件事:

    开机自启服务器重启后它自己回来
    崩了自动重来不用你半夜爬起来
    出事能查日志留着,而且能按时间翻
    02

    买什么样的服务器

    一个 Telegram bot 消耗的资源少得可怜。最便宜的那档就够

    配置1 核 / 512MB 内存 / 10GB 硬盘。签到 bot 实际占用大概 60MB
    机房位置别选国内 —— 国内机器连不上 Telegram,还得给服务器配代理,自找麻烦。日本、新加坡、香港都行
    系统Ubuntu LTS。这一章的命令按它写
    ✓ 选在境外还有个好处

    服务器直连 Telegram,.env 里的 PROXY_URL 那行可以删掉了。第 2 章配代理的麻烦,到这里自动消失。

    03

    先把 SSH 弄安全

    服务器一开机就会被扫。别问为什么,公网上全是自动扫描的机器人,密码弱一点几个小时就被爆开。

    第一件事是换成密钥登录。在你自己电脑上生成一对:

    你的电脑
    ssh-keygen -t ed25519 -f ~/.ssh/mybot -C "mybot"ssh-copy-id -i ~/.ssh/mybot.pub root@你的服务器IP

    验证能用密钥登进去之后,关掉密码登录:

    服务器上
    cat > /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'PasswordAuthentication noKbdInteractiveAuthentication noPermitRootLogin prohibit-passwordEOFsshd -t && systemctl reload ssh
    ⬤ 顺序千万别搞反

    先关密码、后配密钥的话,你会把自己锁在门外,只能去服务商后台开网页控制台救。
    正确顺序:装公钥 → 开个新终端验证密钥能登 → 那个窗口别关 → 再改配置。万一改错了,还有一个活着的连接能改回来。

    再顺手把防火墙开上,只放需要的端口:

    服务器上
    apt install -y ufwufw allow 22/tcpufw --force enableufw status

    bot 用轮询模式,不需要开任何入站端口——它只往外连。所以除了 22,什么都不用开。

    04

    把代码搬上去

    最省事的办法是 rsync,不用装 git、不用建仓库:

    你的电脑 · deploy.sh
    #!/usr/bin/env bashset -euo pipefailHOST="root@你的服务器IP"REMOTE="/opt/mybot"rsync -avz --delete \  --exclude '.venv/' \  --exclude '__pycache__/' \  --exclude 'data.db' \        # 别把本地测试数据推上去覆盖线上!  --exclude '.env' \           # 线上的 .env 单独维护  ./ "$HOST:$REMOTE/"ssh "$HOST" "systemctl restart mybot && systemctl is-active mybot"
    ◆ 那两个 --exclude 很要紧

    --delete 会让远端跟本地完全一致。不排除 data.db 的话,一次部署就能把线上所有用户数据换成你本地的测试数据。
    .env 同理——线上的 token 和配置跟本地不一样,覆盖了 bot 就起不来。

    服务器上准备环境(只做一次):

    服务器上
    apt install -y python3-venvmkdir -p /opt/mybot && cd /opt/mybotpython3 -m venv .venv.venv/bin/pip install aiogram python-dotenv# 线上的 .env,注意没有 PROXY_URLcat > .env <<'EOF'BOT_TOKEN=你的tokenEOFchmod 600 .env
    05

    交给 systemd 管着

    这是整章的核心。写一个服务定义文件,剩下的事系统帮你干:

    /etc/systemd/system/mybot.service
    [Unit]Description=My Telegram BotAfter=network-online.targetWants=network-online.target[Service]Type=simpleWorkingDirectory=/opt/mybotExecStart=/opt/mybot/.venv/bin/python bot.py# 崩了就重启,但别疯狂重启Restart=alwaysRestartSec=5StartLimitIntervalSec=300StartLimitBurst=5# 日志直接进 journalStandardOutput=journalStandardError=journalSyslogIdentifier=mybot# 收紧权限:只能写自己的目录User=mybotNoNewPrivileges=truePrivateTmp=trueProtectSystem=strictProtectHome=trueReadWritePaths=/opt/mybot[Install]WantedBy=multi-user.target
    这几行的作用
    Restart=always进程没了就拉起来,不管是崩的还是被 kill 的
    StartLimitBurst=55 分钟内重启超过 5 次就停手。没有这个,配置写错时它会每 5 秒重启一次,日志刷满硬盘
    After=network-online开机时等网络就绪再启动,否则第一次连 Telegram 会失败
    ProtectSystem=strict整个文件系统只读,只有 ReadWritePaths 列出的能写。万一代码有漏洞,损失被圈在这一个目录里
    User=mybot别用 root 跑。先 useradd -r -s /usr/sbin/nologin mybot 建这个用户,再把目录给它
    启动
    useradd -r -s /usr/sbin/nologin mybotchown -R mybot:mybot /opt/mybotsystemctl daemon-reloadsystemctl enable --now mybotsystemctl status mybot

    看到 active (running) 就成了。现在关掉终端、重启服务器,它都会自己回来

    06

    出事了怎么查

    日志进了 journal,用 journalctl 翻:

    常用几条
    journalctl -u mybot -f                  # 实时盯着(相当于 tail -f)journalctl -u mybot -n 100 --no-pager   # 最近 100 行journalctl -u mybot --since "1 hour ago"  # 最近一小时journalctl -u mybot -p err              # 只看错误journalctl -u mybot --since today | grep -i traceback

    第 7 章配的那个带时间戳的日志格式,在这里就有用了 —— 用户说「刚才没反应」,你能直接按时间翻到那一段。

    ✓ 限制日志大小,免得撑爆硬盘

    journal 默认能吃掉不少空间。10GB 的小机器上,在 /etc/systemd/journald.conf 里加一句 SystemMaxUse=500M,然后 systemctl restart systemd-journald

    07

    怎么知道它还活着

    Restart=always 管得了崩溃,管不了「进程还在但已经不干活了」 —— 比如卡在某个死循环里、或者第 8 章说的定时任务悄悄死了。

    最省事的自检办法:让 bot 每小时给自己发个心跳,你隔一阵瞄一眼。

    tasks.py 追加
    ADMIN_ID = 你的用户ID       # 找 @userinfobot 问它async def heartbeat(bot: Bot) -> None:    """每小时报一次平安,顺便带上关键数字。"""    while True:        await asyncio.sleep(3600)        try:            n = storage.count_today_checkins()            await bot.send_message(ADMIN_ID, f"还活着 · 今日签到 {n}")        except Exception:            log.exception("心跳失败")

    它的价值不在「告诉你活着」,在于停了你就会注意到。比装一套监控系统实在得多。

    再加一个:把第 5 章的备份挂上 cron。

    crontab -e
    # 每天凌晨 3 点备份,只留最近 7 份0 3 * * * cd /opt/mybot && .venv/bin/python backup.py && ls -t backup-*.db | tail -n +8 | xargs -r rm
    ◆ 备份在同一台机器上,只能算半个备份

    硬盘坏了或者服务器被删,备份跟着一起没。真在意数据的话,加一条 rsync 定期拉回你自己电脑,或者传到对象存储。
    至少每个月手动恢复一次试试 —— 没验证过的备份不算备份。

    08

    更新代码的正确姿势

    你的电脑
    ./deploy.sh

    就一句。但有几件事要留神:

    重启会打断正在进行的对话用 MemoryStorage 的话(第 5 章),正在兑换的人会卡住。挑没人用的时候推
    先看日志再走开journalctl -u mybot -f 盯十几秒,确认它真的起来了。systemctl restart 返回成功不代表程序没崩——它可能起来又立刻挂了
    改了表结构要格外小心第 5 章那个坑:本地删库重跑没事,线上老库直接炸。推之前把备份下载一份到本地
    09

    上线检查表

    推给真实用户之前,把这几条过一遍:

    检查怎么验
    必查token 没写进代码grep -rn "[0-9]\{8,\}:AA" . 应该什么都搜不到
    必查.env.gitignore而且权限是 600
    必查重启后能自己回来reboot,等两分钟,发一条 /start
    必查崩了能自己起来kill -9 掉进程,5 秒后再发消息,应该正常
    必查有备份,而且能恢复跑一次备份,再用备份文件开一次 sqlite3
    建议全局错误处理在(第 7 章)故意触发一个异常,看进程有没有死
    建议日志有时间戳journalctl -u mybot -n 5
    建议心跳能收到把间隔临时改成 60 秒试一次
    建议磁盘还有空间df -h,日志和备份都会长
    「崩了能自己起来」这条一定要真 kill -9 试一次。很多人配完 systemd 从来没验证过,等真崩的那天才发现配置写错了。
    10

    到这儿你会了什么

    回头看第 1 章那张五步图 —— 现在五步都走完了:

    1 建 bot 拿 token 5 分钟 2 装环境 Python、依赖、代理 最容易卡在这 3 跑通第一句 它回你话了 第一次成功 4 做出真功能 你想要的那个 几小时 5 让它一直跑 挪到服务器 就是这一章 全部走完
    建 bot、装环境、跑通第一句、做出真功能、让它一直跑着 —— 整条路走完了

    更重要的是中间那几章:你知道了用户点两次会怎样、群里人多了消息为什么会丢、支付回调为什么会重复。这些东西换个框架、换个平台依然成立 —— 它们不是 aiogram 的知识,是写联网服务的知识。

    这一章的要点

    1. 先验证密钥能登录,再关密码登录,而且留一个活着的终端窗口
    2. 轮询模式不需要开任何入站端口,防火墙只留 22
    3. systemd 三件套:Restart=always + StartLimitBurst 防疯狂重启 + ProtectSystem=strict
    4. 部署脚本必须 exclude 掉 data.db.env,否则一次部署冲掉所有线上数据
    5. systemctl restart 成功不代表程序没崩,要看日志
    6. 心跳消息比监控系统实用:它停了你就会注意到
    7. 没恢复验证过的备份不算备份
    ✓ 最后一个练习

    故意把 .env 里的 token 改错一位,然后 systemctl restart mybot
    观察:systemctl status 显示什么?journalctl 里是什么错?5 分钟内它重启了几次,然后停在哪个状态?
    把这个过程走一遍,以后线上真出事时你就知道该看哪儿了。

    部署顺利吗?

    服务器配置最容易卡在细节上。哪一步出问题了,把报错贴进来。留不留联系方式都行。