作者: huyang

  • Day 56

    昨天右眼疼

    今天左眼疼

    好难受,吃个鸡腿缓解一下。

    ……何时才能结束

    ……

  • Day 59

    前几天刷推特白嫖了台华为云服务器,装宝塔、装 Docker,把抖音续火花和 dsh 都搬了上去,抖音风控太严了,只有朋友的能续火花,自己的还被ban了,控制台加密码、SSH 改密钥登录,折腾了一整天,啥正事没干。

    不过现在直接随时信息使用deepseek,我很喜欢,很方便。

    今天早上刷到新闻:PHP 爆了个 9.2 分的大洞,不用登录就能打穿。赶紧查自己服务器——华为的容器里没 PHP,宿主机那个也没对外服务,虚惊一场。

    刚松口气,想起来博客是 WordPress 的。一查响应头:PHP 8.2.29,修复线 8.2.35,正中靶心。它在阿里云那台上,跑的是 Docker 官方镜像。

    晚上自习回来开修:先备份,再拉新镜像(自带修复版 PHP 8.3.36),重建容器,外网复核过线。旧镜像和备份包删掉,

    啊……..我在干什么,除了学习啥都感兴趣,这破网站有漏洞都没人看

  • Day 67

    博客挂了两天

    打开自己的博客,等来的不是熟悉的首页,而是一行冷冰冰的英文:

    Error establishing a database connection

    我什么都没改过。上一篇发文的时候还好好的。这是故事的起点——从这行报错开始,我花了将近一天时间,穿过四层真相,最后在一个”孤儿进程”手里找回了我的网站。

    一、现场勘查

    我的博客跑在一台 1.7G 内存的阿里云小机器上,宝塔面板 + Docker Compose 部署的 WordPress + MySQL 8.0,数据目录挂载在宿主机,容器整整十四个月没有动过。

    docker compose logs 拉出来,现场是这样的:

    wordpress-1 | 1.162.146.33 - - [14/Oct/2026:00:20:19 +0000] "GET / HTTP/1.1" 500 2808 "..."
    wordpress-1 | 138.246.253.24 - - [14/Oct/2026:00:22:04 +0000] "GET /robots.txt HTTP/1.1" 500 2808 "..."
    wordpress-1 | 45.156.128.15 - - [14/Oct/2026:00:24:26 +0000] "GET / HTTP/1.1" 500 2808 "..."
    

    所有请求——首页、登录页、robots.txt——一律 500,而且响应体大小清一色 2808 字节。完全一致的体积说明大家在看同一张错误页:数据库连不上。

    再看 MySQL 侧,更诡异的一幕:

    [InnoDB] InnoDB initialization has started.
    [InnoDB] InnoDB initialization has ended.
    [Server] Starting XA crash recovery...
    [Server] XA crash recovery finished.
    [Server] /usr/sbin/mysqld: ready for connections.
    

    这套”开机广播”每隔几秒就重演一遍。XA crash recovery 只在数据库上次没有正常关闭时出现——它不是在重启,是在”复活”,而且复活不了几秒。

    二、第一层真相:它不是挂了,是在死循环

    $ docker ps -a | grep db
    7ec82916ca6d   mysql:8.0   ...   Restarting (137) 41 seconds ago   dk_wordpress-db-1
    

    Restarting (137)。137 = 128 + 9,退出码 9 是 SIGKILL——进程被人一枪打死,不是自己崩的。这两者的区别很关键:MySQL 自己崩溃会留遗书(错误日志里会有 InnoDB 错误、断言失败等等),而我的日志里每次死亡都是上一秒还 ready for connections、下一秒直接消失,干干净净。

    一声不吭的死,等于外部击杀。谁会 SIGKILL 一个数据库?头号嫌疑人:内核 OOM killer。

    三、排除法:三个”不是我”

    怀疑归怀疑,破案要证据。我先排除了几个方向:

    配置被动过? 没有。容器十四个月没重建,.env、数据卷、wp-config.php 无人动过。这和”我一直用得好好的”吻合。

    容器有内存上限? docker inspect 显示 Memory=0,没有套笼子。

    systemd-oomd 或定时任务在杀进程? 系统是 CentOS 7,根本没有 oomd;crontab 里只有一个每天跑一次的宝塔日志任务,对不上”几秒死一次”的节奏。

    最迷惑的是 dmesg | grep oom 居然是空的。内核 OOM 杀人必然会留 Out of memory: Killed process ... 的记录,一条都没有。(后来才明白:dmesg 的环形缓冲区可能早就滚没了、或被清理脚本清过——**要看权威记录,得去 /var/log/messages**。)

    四、铁证:案发在三天前的深夜

    $ grep -iE 'out of memory|killed process' /var/log/messages | tail
    Oct 12 23:11:23 xxx kernel: AliYunDunMonito invoked oom-killer: ...
    Oct 12 23:11:23 xxx kernel: Out of memory: Kill process 10188 (ib_pg_flush_co) score 151 ...
    Oct 12 23:11:23 xxx kernel: Killed process 10188 (ib_pg_flush_co), UID 999, anon-rss:388380kB
    Oct 12 23:11:23 xxx kernel: Killed process 10205 (ib_srv_mon), UID 999, ...
    

    10 月 12 日 23:11:23——我最后一次正常发文正是那之前。ib_pg_flush_co、ib_srv_mon 都是 mysqld 的 InnoDB 内部线程,UID 999 是容器里的 mysql 用户。凶手、时间、死者全齐了。

    作案动机也清楚:这台 1.7G 的机器上,宝塔面板、阿里云的安骑士监控(AliYunDunMonito,引爆 OOM 的正是它申请内存的动作)、Apache、Docker 各占一份,MySQL 8 默认配置光 performance_schema 加 buffer pool 起步就要 500M+。内存不够分的,内核只好杀最肥的那个。

    打个比方:物理内存是桌面,swap 是桌下的柜子。我的桌面早就摆满了,柜子里也塞了半柜;MySQL 每次开工要一次性在桌上摊开 500M 的材料,桌上柜下都周转不开,于是它每次开工就被清场。

    这不是”改了什么”导致的事故,是十四个月的缓慢消耗——数据慢慢涨、插件慢慢多、公网机器人的流量只增不减,水位悄悄爬过了警戒线。

    五、第一次修复:swap + 瘦身

    确认病因后动手,三步:

    # 1. 冷备份(数据库停机状态下整目录拷贝,423M,全站家底)
    docker compose stop db
    cp -a ${DATA_PATH}/db /root/db_backup_20261014
    
    # 2. swap 从 1G 扩到 3G
    dd if=/dev/zero of=/swapfile2 bs=1M count=2048
    chmod 600 /swapfile2 && mkswap /swapfile2 && swapon /swapfile2
    
    # 3. 给 MySQL 瘦身,compose 里加:
    command:
      - --innodb-buffer-pool-size=64M
      - --performance-schema=OFF
      - --max-connections=30
    

    三个参数分别管:数据缓存(我的库一共不到 500M,64M 装下常用部分绰绰有余)、内部自我监控(个人博客用不上,白吃三四百 M)、并发连接上限(30 个同时连接对个人博客是天文数字)。

    docker compose up -d db,结果——死得慢了。从”每几秒死一次”变成”几分钟死一次”。有进步,但没根治。

    六、第二层真相:锁

    再次翻日志,这次刷屏的换了内容:

    [ERROR] [MY-012574] [InnoDB] Unable to lock ./ibdata1 error: 11
    [ERROR] [MY-012574] [InnoDB] Unable to lock ./ibdata1 error: 11
    [ERROR] [MY-012574] [InnoDB] Unable to lock ./ibdata1 error: 11
    

    ibdata1 是 InnoDB 的核心数据文件,MySQL 启动时第一件事就是给它上锁。error 11 = 文件已被别人锁着——有另一个 mysqld 进程攥着我的数据库不放。容器里这个每秒重试一次,永远抢不到。

    谁是”另一个”?在宿主机上把所有 mysqld 进程和当前容器的进程对照着看:

    $ ps aux | grep mysqld
    root   1244 ... /www/server/mysql/bin/mysqld_safe ...    # 宝塔自己装的 MySQL,无关
    mysql  1956 ... /www/server/mysql/bin/mysqld ...         # 同上
    polkitd 5318 ... mysqld                                   # ← 裸 mysqld,369M,不属于任何容器
    polkitd 8728 ... mysqld --innodb-buffer-pool-size=64M ... # 当前容器里的正主
    
    $ docker top dk_wordpress-db-1
    PID   CMD
    8728  mysqld --innodb-buffer-pool-size=64M ...
    

    PID 5318,一个不带任何参数的裸 mysqld——旧容器默认配置的样子。它是这两天反复强杀过程中遗留的孤儿进程:容器没了,进程还在,数据文件的锁也就一直没释放。

    kill -9 5318
    

    三十秒后,正在每秒重试抢锁的正主立刻接管,InnoDB initialization 顺利走完。SHOW DATABASES 列出完好的 wordpress_db,curl 状态码:

    200