用 Sablier + Nginx 实现容器按需启停,闲置自动回收内存
方案概述
Sablier 是一个开源的容器按需启动工具,配合 nginx 的 auth_request 认证代理机制,可以实现"请求到达时自动拉起容器,空闲后自动关闭"的完整闭环。核心原理如下:
- 为每个需要按需启动的目标容器添加
sablier.enable=true与sablier.group=<组名>标签,sablier 通过挂载的/var/run/docker.sock实时感知并管理这些容器。 - nginx 在访问目标服务前,先向 sablier 的
/api/strategies/blocking接口发起一个内部认证子请求(auth_request)。 - sablier 收到请求后,检查对应组名的容器是否已运行:
- 若未运行,则立即通过 Docker API 启动该组内所有容器,并在容器就绪后返回
200 OK,此时 nginx 将原始请求放行,转发至目标服务。 - 若已在运行,则直接返回
200 OK放行。
- 若未运行,则立即通过 Docker API 启动该组内所有容器,并在容器就绪后返回
- 请求结束后,容器会一直保持运行,直到超过
session_duration(会话时长)无新请求,sablier 自动将其停止,实现资源回收。
sablier 容器部署
docker-compose 配置
services:
sablier:
image: sablierapp/sablier:latest
container_name: sablier
restart: always
network_mode: bridge
environment:
- TZ=Asia/Shanghai
command:
- start
- --provider.name=docker
ports:
- "10000:10000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
参数说明:
--provider.name=docker:指定容器提供方为 Docker,sablier 通过宿主机 Docker API 管理容器。ports:暴露10000端口供 nginx 调用 sablier API。volumes:将宿主机/var/run/docker.sock挂载进容器,这是 sablier 与 Docker 通信的关键。
说明:sablier 使用默认配置即可,无需额外挂载 sablier.yaml 配置文件。
目标容器添加标签
为需要被 sablier 管理的容器(以 emby 为例)添加以下 labels:
labels:
- sablier.enable=true
- sablier.group=emby
emby 容器完整 docker-compose.yaml 示例
services:
emby:
image: emby/embyserver:latest
container_name: emby
hostname: emby
# user: 1000:10
network_mode: bridge
privileged: true
volumes:
- ./config:/config
- /volume3:/volume3
devices:
- /dev/dri:/dev/dri #需要硬解的配置
ports:
- 8096:8096
# - 8920:8920 #optional
- 7359:7359/udp #optional
- 1900:1900/udp #optional
restart: 'unless-stopped'
environment:
- TZ=Asia/Shanghai
- UID=0
- GID=0
labels:
- sablier.enable=true
- sablier.group=emby
端口 8096 对应 nginx 中 proxy_pass 的目标端口。
nginx 完整配置
前提条件
nginx 必须编译 ngx_http_auth_request_module 模块(通常默认已包含,可通过 nginx -V 2>&1 | grep http_auth_request_module 验证)。
完整 server 块示例
server {
listen 80;
server_name emby.example.com;
# ===== 在 server 下添加 ====
include /volume1/Workspace/nginx/sablier_auth.conf;
location / {
# ===== 在 location 下添加 ====
set $sablier_names emby; # 此处应为容器的 container_name,而非 sablier.group 标签值
set $sablier_session 30m;
auth_request /_sablier_auth;
proxy_pass http://127.0.0.1:8096; # 转发到 emby 实际服务端口
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;
}
}
注意:$sablier_names 需要设置的是目标容器的 container_name(容器名),而不是 sablier.group 标签值。请确保目标容器在 docker-compose 中显式配置了 container_name(如 emby 示例中的 container_name: emby),sablier 才能正确匹配并管理该容器。
sablier_auth.conf 内容
# ===== 在 server 下 添加 ====
# include /volume1/Workspace/nginx/sablier_auth.conf;
# ===== 在 location 下添加 ====
# set $sablier_names emby;
# set $sablier_session 30m;
# auth_request /_sablier_auth;
#
location = /_sablier_auth {
internal;
proxy_pass http://127.0.0.1:10000/api/strategies/blocking?names=$sablier_names&session_duration=$sablier_session;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
}
auth_request 相关配置项详解
auth_request /_sablier_auth;:指定认证子请求的 URI,该 location 必须为internal,仅用于内部转发。auth_request_set:可选,用于将子请求的响应变量赋值给 nginx 变量,便于后续逻辑判断。proxy_pass:将子请求转发至 sablier 的 blocking 策略接口,names对应目标容器组名,session_duration为会话时长。proxy_pass_request_body off:认证子请求无需携带原始请求 body,减少传输开销。proxy_set_header Content-Length "":因禁用了 body,必须将 Content-Length 置空,避免请求头冲突。proxy_connect_timeout:连接 sablier 的超时时间,建议 10s。proxy_read_timeout:等待 sablier 返回的读取超时,应大于容器的最大启动时间,建议 300s(5 分钟)。
认证响应机制:
- 当 sablier 返回
200时,nginx 放行原始请求; - 当返回
401或403时,nginx 直接向客户端返回对应错误码; - 若容器启动超时或 sablier 不可达,会根据上述超时设置返回
502 Bad Gateway。
多场景应用扩展
场景一:多服务分组
不同业务可配置为不同分组,共享同一套 sablier 与 nginx 认证机制。例如同时管理 emby 与 qbittorrent:
# 容器 emby
labels:
- sablier.enable=true
- sablier.group=emby
# 容器 qbittorrent
labels:
- sablier.enable=true
- sablier.group=download
nginx 中对应两个 location(或两个 server),分别设置 $sablier_names 即可。
场景二:不同会话时长
按服务重要性设置不同会话时长,例如对 nas 管理面板设置 1h,对临时 demo 设置 5m:
# nas 管理面板
location /nas/ {
set $sablier_names nas;
set $sablier_session 1h;
auth_request /_sablier_auth;
proxy_pass http://127.0.0.1:5000;
}
# 临时 demo
location /demo/ {
set $sablier_names demo;
set $sablier_session 5m;
auth_request /_sablier_auth;
proxy_pass http://127.0.0.1:8080;
}
场景三:dynamic 等待页模式
若不想让用户在容器启动期间白屏等待,可改用 dynamic 策略,通过等待页自动刷新感知启动进度。将 proxy_pass 指向 dynamic 接口:
location / {
proxy_pass http://127.0.0.1:10000/api/strategies/dynamic?names=$sablier_names&session_duration=$sablier_session&theme=hacker-terminal;
}
sablier 会返回一个等待页面,容器启动后自动跳转到目标服务,体验更佳。
常见问题与排障
1. docker.sock 权限问题
若 sablier 容器日志出现 permission denied while trying to connect to the Docker daemon socket,说明当前用户(或容器内映射的用户)无权访问 /var/run/docker.sock。
解决方法:
# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
newgrp docker
# 验证
docker ps
注意:不要随意 chmod 666,存在安全风险。若已修改,可复原:
sudo chown root:docker /var/run/docker.sock
sudo chmod 660 /var/run/docker.sock
2. 请求超时
- 现象:访问目标服务长时间无响应,最终报 504。
proxy_read_timeout或proxy_connect_timeout设置过短,容器启动超过超时时间。可将proxy_read_timeout调大(如 300s)。 - 排查:手动执行 sablier API 看响应耗时:
curl -v "http://127.0.0.1:10000/api/strategies/blocking?names=emby&session_duration=30m"
3. 容器不自动停止
- 现象:容器空闲后一直运行。先检查
session_duration是否设置过短,且请求是否持续产生。 - 排查:查看 sablier 日志,确认容器是否注册成功;若容器标签或组名配置错误,sablier 无法追踪,自然不会停止。
4. 认证失败排查
- 现象:访问返回 401/403。
- 排查:确认 sablier 服务正常,且
nginx能连通10000端口;检查 sablier 容器日志,查看请求是否被拒绝;确认names参数是否与容器标签组名一致。