私有相册的可用性保障:一次 Immich 服务中断的分层排查与恢复

举报
yd_43947564 发表于 2026/10/29 23:03:08 2026/10/29
【摘要】 一次深夜的手机端「连不上服务器」,最终定位为存储链路故障引发的应用崩溃。本文按服务层、存储层、虚拟化层、硬件层逐层排查,给出可复用的恢复顺序与四条加固措施。

自建 Immich 已经稳定跑了几个月,直到某天晚上手机 App 突然提示「无法连接服务器」。这类故障最麻烦的地方在于:表面看是应用挂了,实际根因往往在几层之外。

本文记录完整的排查与恢复过程,重点不是某一条命令,而是分层的思路和恢复的顺序。

一、故障现象

三个现象同时出现:

  • 手机端和网页端都连不上,请求直接超时
  • 服务器上 curl 接口无响应,端口没有监听
  • 但数据库、缓存、机器学习容器看起来都还在运行

这说明问题不在「整个主机挂了」,而是某个环节断了。

二、分层排查

私有相册的依赖链通常是这样:

手机 App
   ↓ HTTP
应用容器(Immich Server)
   ↓ 读写
媒体存储(SMB/CIFS 挂载)
   ↓ 网络
存储服务器(NAS 虚拟机)
   ↓
物理磁盘

任何一层出问题,表现都是「应用连不上」。所以要从下往上验证,而不是从上往下猜。

第 1 层:应用容器

docker ps -a --format '{{.Names}}\t{{.Status}}'

结果很明确:immich_server 状态是 Exited (1),而数据库和缓存容器正常。

再看退出前的日志,出现两类错误:

EHOSTDOWN: host is down, open '/data/library/.../IMG_xxxx.jpg'
TypeError: Cannot read properties of undefined (reading 'handleMessage')

第一行是关键线索:应用在读媒体文件时,底层报「主机不可达」。这不是应用配置问题,是存储链路断了。第二行是应用在错误处理路径上又崩了一次,属于次要现象。

第 2 层:存储挂载

mount | grep cifs
ls /mnt/photos

结果:

//nas/photo on /mnt/photos type cifs (...)
ls: cannot access '/mnt/photos': Host is down

挂载条目还在,但访问报 Host is down。这是典型的 CIFS 悬挂挂载:连接断了,但挂载点没有被自动清理。

注意:mount 显示挂着,不代表它可用。判断存储是否真的可用,必须实际读一次。

第 3 层:存储服务器

ping -c 2 <nas-ip>

100% 丢包,ARP 表里的状态是 FAILED。说明 NAS 本身已经不在线。

到虚拟化平台查看:

qm list
qm status 101

结果:

status: io-error

找到根因了。 虚拟机因为磁盘 I/O 错误被 QEMU 自动暂停(-no-shutdown 模式下进程还在,但 CPU 停止执行)。虚拟机一停,SMB 服务就没了,CIFS 挂载随之失效,应用读文件全部失败然后崩溃。

第 4 层:物理磁盘

内核日志里能看到完整证据链:

usb 2-3.2: reset SuperSpeed USB device number 4 using xhci_hcd   ← USB 链路反复重置
I/O error, dev sdb, sector 16785280 op 0x1:(WRITE)               ← 写 I/O 错误
sd 2:0:0:0: [sdb] Attached SCSI disk                             ← 重新枚举

外接 USB 硬盘在几小时内重置了二十多次,最终出现 I/O 错误。

三、恢复顺序(关键)

知道原因后,恢复必须按依赖链从下往上,顺序错了会反复失败:

步骤 动作 为什么这个顺序
1 备份虚拟机的配置文件 改配置前留退路
2 停止故障虚拟机 解除 io-error 暂停态
3 移除故障磁盘的直通配置 让虚拟机不再依赖那块盘
4 启动虚拟机 此时只挂正常磁盘
5 验证 NAS 网络与共享 ping + SMB 端口 + 实际读文件
6 验证挂载点可读写 读一个文件 + touch 测试写入
7 才启动应用容器 存储没就绪就启动,只会再崩一次

第 7 步特别重要:存储不可用时不要急着拉起应用。它会在启动阶段读不到文件,然后进入崩溃循环,日志被刷满,反而掩盖真实问题。

恢复后验证清单:

应用接口返回 pong
容器 healthy,重启次数为 0
资产总数与故障前一致
缩略图可访问(随机抽 3 张)
原图可下载(随机抽 3 张)

只看到「容器 running」不算恢复完成,要能真正取到数据。

四、四条加固措施

1. 画清楚依赖链,监控分层

原来的监控只检查「应用接口是否存活」。这次故障中,接口挂了只是表象,真正的信号在更下层。

改进后的巡检同时检查三层:

应用层:HTTP 接口返回 pong
容器层:四个容器 Up 且 healthy
主机层:根分区使用率 < 85%

异常时只输出问题项,正常时完全静默——监控的价值在于出事时能被看见,而不是每天报一次「一切正常」。

2. 给容器加资源上限和日志轮转

应用崩溃时日志会短时间暴涨。不加限制的话,容器日志能把宿主磁盘写满,把小故障放大成磁盘告警。

services:
  immich-server:
    mem_limit: 1100m
    logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: "3"

数据库容器不要设内存上限——它需要页缓存来维持性能,限制了反而拖慢查询。

3. 下调后台任务并发

媒体库挂的是网络存储,并发太高会让每个任务都在等 I/O:

缩略图生成:8 → 4
元数据提取:7 → 3
后台任务:  5 → 4

在 3 核 + 网络存储的环境下,这个配置比默认值稳得多。

4. 清理失效挂载,别让它看起来正常

CIFS 卸载后,挂载点的目录项会损坏,表现为:

drwxr-xr-x  ...  download
ls: cannot access '/mnt/download': No such file or directory

此时 umount 会报“not mounted”,因为路径本身解析不了。唯一的干净做法是重启引用该挂载点的容器,让挂载表重新建立。

五、小结

这次故障最值得记住的三点:

  1. 「应用连不上」不等于「应用有问题」。依存链越深,越要从底层往上验证。
  2. mount 显示挂着不代表可用,docker ps 显示 running 不代表正常,判断依据必须是「能不能真的读到数据」。
  3. 恢复顺序比恢复速度重要。存储没就绪就拉应用,只会把一次故障变成一次崩溃循环。

私有化部署的便利是自己掌控数据,代价是每一条依赖链都要自己负责。把依赖关系画出来、把监控建在正确的层级上,比事后救火有效得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。