docs/deployment.md
Rama main · réplica de lectura del repositorio original.
# Docker 部署、升级、备份与回滚
ImageSilo 的生产边界始终是一个容器、一个 Go 进程、一个本地 `/data`。不需要 Redis、外部数据库、任务队列或第二个应用服务。图片处理并发默认且推荐保持为 `1`。
## 首次部署
每次成功发布都会同时更新完整版本标签和可变的 `latest`。生产环境仍建议固定使用完整版本标签或 digest;`latest` 更适合希望自动跟随最新发布的测试环境。示例中的标签仅作占位:
```bash
export IMAGESILO_IMAGE=ghcr.io/willxup/imagesilo:v1.0.0-rc.1
docker pull "$IMAGESILO_IMAGE"
docker volume create imagesilo-data
docker run --detach \
--name imagesilo \
--restart unless-stopped \
--publish 127.0.0.1:8080:8080 \
--env IMAGESILO_PROCESSING_CONCURRENCY=1 \
--env IMAGESILO_DELIVERY_CONCURRENCY=0 \
--env IMAGESILO_TRUST_PROXY_HEADERS=true \
--volume imagesilo-data:/data \
"$IMAGESILO_IMAGE"
docker logs -f imagesilo
```
仅在尚无管理员时,启动日志会输出一次 `bootstrap_token`。打开 `/admin/setup` 填入该 token 后设置管理员账号和初始策略;token 只保存于当前进程内存,初始化成功后立即清除,未初始化重启则重新生成。无人值守部署可改用 `imagesilo admin create --password-stdin`,创建管理员后 Web 初始化会自动关闭。
确认 `/healthz`、`/readyz`、管理员登录和一张微型测试图片后,再把反向代理指向 `127.0.0.1:8080`。TLS、请求体上限和公网限速应由现有反向代理承担;ImageSilo 自身仍保持单进程。
推荐拓扑是 Nginx Proxy Manager → `127.0.0.1:8080`。ImageSilo 默认信任 Nginx 的客户端地址头,并按单个有效 `X-Real-IP`、`X-Forwarded-For` 最右侧有效地址、TCP 对端地址的顺序用于登录 IP 限速。因此后端端口不得绕过 NPM 直接暴露公网;确需直连部署时显式设置 `IMAGESILO_TRUST_PROXY_HEADERS=false`。NPM 可以长期缓存带哈希的前端静态资源,但不要给可见性和内容都可能变化的图片 URL 设置长 TTL;公开图片由 `ETag`/`304` 复验,私密图片使用 `private, no-store`。
`IMAGESILO_DELIVERY_CONCURRENCY` 默认 `0`,表示不限制同时读取数量,由 Nginx、操作系统和文件系统负责连接与页缓存调度。配置为 `1`—`4096` 时,应用会对标准 URL、历史别名、迁移目录和缩略图统一限流;满载时立即返回 `503` 与 `Retry-After`。图片正文不进入 Go 全图缓存,读取依靠内存元数据索引、文件流、ETag 和操作系统页缓存,保持轻量。
## bind mount
宿主机目录必须位于本地文件系统,不支持 NFS 或 SMB。容器固定以数字 UID/GID `10001:10001` 运行:
```bash
sudo install -d -o 10001 -g 10001 -m 0750 \
/srv/imagesilo/data/db \
/srv/imagesilo/data/images \
/srv/imagesilo/data/migrations \
/srv/imagesilo/data/cache/thumbnails \
/srv/imagesilo/data/tmp
```
将 `--volume imagesilo-data:/data` 替换为 `--volume /srv/imagesilo/data:/data`。权限错误会在启动阶段直接失败,不会退回 root 运行。
需要保留旧图床目录结构时,可以再把旧图片根目录只读挂载到 `/data/migrations`,例如 `--volume /srv/legacy-images:/data/migrations:ro`。挂载目录中的相对路径会直接成为公开 URL,后台迁移管理仍可浏览和复制直链。
只有需要从后台永久删除“专用迁移副本”时,才同时使用可写挂载、确保 UID/GID `10001:10001` 拥有删除权限,并增加 `--env IMAGESILO_MIGRATION_MUTATIONS=true`。该开关默认 `false`;仅改为可写挂载不会自动开放删除。不要对旧图床唯一原件启用此能力,具体边界见[导入指南](./imports.md)。
## 一致备份
ImageSilo 不在应用进程中实现备份。备份必须在停止写入后覆盖完整 `/data`:
1. 停止反向代理写流量。
2. `docker stop --time 20 imagesilo`,确认退出码为 `0`。
3. 复制 named volume 对应目录,或对 bind mount 所在本地文件系统创建快照。
4. 同时记录当前不可变镜像标签和 digest。
5. 启动原容器,复核 readiness、登录、标准 URL 和历史别名。
不要只复制 SQLite 主文件;WAL、图片文件和数据库必须来自同一个停止写入时点。
## 升级
1. 先完成完整 `/data` 备份,并保留旧镜像标签和 digest。
2. 拉取新版本镜像,在独立临时端口和独立数据副本上完成 smoke test。
3. 停止正式容器,使用新镜像和原 `/data` 启动。
4. 验证 `/readyz`、管理员登录、公开/私密图片、上传和历史别名。
5. 观察结构化日志、RSS、goroutine、文件描述符和磁盘占用。
## 回滚
数据库迁移是向前执行的,因此不能假设旧二进制一定能读取升级后的数据库。可靠回滚是:
1. 停止新容器。
2. 恢复升级前的完整 `/data` 备份或快照。
3. 使用记录过 digest 的旧镜像启动。
4. 复核 readiness、图片字节哈希和历史别名。
不要让新旧两个容器同时写同一个 `/data`。