在威联通 QNAP NAS 上让 Container Station(Docker)支持代理拉取镜像

背景

设备是我一直在用的一台 NAS:

  • 型号:TS-532X
  • CPU:Annapurna Labs AL324
  • 内存:8GB
  • 系统:QTS 5.2.8

平时主要跑一些 Docker 服务,都是通过 Container Station 管理的。

Container Station 本质上就是 QNAP 把 Docker + LXD 包了一层 UI,方便用户直接从镜像仓库拉应用。

问题也出在这里: 一切“看起来很简单”的东西,一旦出问题,就不太好查。

问题

最开始只是一个很普通的报错:

Error response from daemon: Get "https://registry-1.docker.io/...": timeout

或者更直接一点:

context deadline exceeded

表现其实很统一:

  • docker pull 卡住
  • Container Station 里部署应用失败
  • 镜像下载极慢甚至直接超时

但 NAS 本身网络是正常的:

  • 能 ping 外网
  • 能访问网页
  • 其他服务没问题

这时候就很容易误判成 DNS 或网络问题。

分析

Container Station ≠ 标准 Docker

很多教程会告诉你:

修改 /etc/docker/daemon.json,加 registry mirror 或 proxy

但在 QNAP 上,这一套基本是无效的

原因很简单:

  • Container Station 是 QNAP 自己改过的一套 Docker 运行环境
  • 配置路径和启动方式都不完全遵循标准 Linux

比如:

/etc/config/docker.json

这个文件确实存在,但它的用途更偏向:

镜像加速(registry mirror)

而不是代理。

真正的问题在“守护进程层”

这里要区分两个完全不同的概念:

守护进程(Docker Daemon)

  • 执行 docker pull
  • 负责和 docker.io / ghcr.io 通信

容器内部

  • 运行你的应用(比如 Immich / Nginx / etc)
  • 有自己独立的网络环境

这次的问题,只发生在第一层

也就是说:

Docker 引擎本身访问不了外网,而不是容器的问题。

为什么 UI 里没法解决?

Container Station 的 UI 并没有提供:

  • HTTP_PROXY 设置入口
  • Docker daemon 级代理配置

这点其实挺反直觉的,但也能理解:

官方更倾向你用“镜像仓库加速”,而不是代理

本质原因

问题的核心可以归纳成一句话:

Docker daemon 启动时,没有任何代理环境变量。

而在 Linux 里:

http_proxy / https_proxy

这种东西,本质是进程级环境变量

也就是说:

必须在 Docker 启动之前注入

解决方案

最后选择的是一个比较“底层”的方案:

直接修改 Container Station 的启动脚本

修改启动脚本

vi /etc/init.d/container-station.sh

找到 start) 分支(或者 main() 里的 start),在最前面插入:

export http_proxy="http://192.168.2.20:7890"
export https_proxy="http://192.168.2.20:7890"
export no_proxy="localhost,127.0.0.1,database,redis"

这里有几个细节:

  • 不能用 127.0.0.1
  • 必须写代理设备的局域网 IP
  • 如果你用的是圈 X / 旁路由,要开启 LAN 访问
  • 编辑语法:按 i 进入编辑,按 Esc 退出编辑,输入 :wq 保存退出,输入 :q! 不保存强制退出

重启服务

/etc/init.d/container-station.sh restart

验证是否生效

docker info | grep -i proxy

如果能看到:

HTTP Proxy: http://192.168.2.20:7890

基本就通了。

一个容易踩的坑

很多人做到这一步会以为:

“已经全局代理了”

其实不是。

Docker 代理分两层

守护进程级(刚刚改的)

作用:

  • docker pull
  • 下载镜像

解决的是:

拉不下来镜像的问题

容器级

作用:

  • 容器内部访问外网
  • 比如:
  • 下载 AI 模型
  • 请求 API
  • 地图服务

这个不会自动继承上面的 proxy。

如果需要,还要在 docker-compose.yml 里写:

environment:
  - HTTP_PROXY=http://192.168.2.20:7890
  - HTTPS_PROXY=http://192.168.2.20:7890

稳定性问题

这个方案不是没有代价。

重启 NAS:不会丢

因为:

/etc/init.d/container-station.sh

通常是一个软链接,实际指向硬盘上的 .qpkg 目录。

所以:

修改是持久的

更新 Container Station:会丢

只要你:

  • 在 App Center 更新 Container Station

系统会:

  • 覆盖整个 .qpkg/container-station 目录

也就是说:

你改的脚本会被还原

我的处理方式

没有搞复杂的自动化,反而是最简单的:

  • 把这几行 export 存下来
  • 每次更新后重新打一次补丁

原因也很现实:

QNAP 更新频率不高,这种操作成本其实很低