name: remote-docker-nas description: 当在远程NAS或服务器上通过SSH运行Docker容器时使用,无需本地Docker守护进程,当远程主机无法从Docker Hub拉取镜像时,或当设置一个轻量级的笔记本电脑到NAS的Docker工作流程时使用crane。
远程Docker在NAS上
概述
通过SSH从您的笔记本电脑控制,在远程NAS/服务器上运行Docker——无需本地Docker守护进程。使用crane(约11MB)拉取镜像为tar包,并使用DOCKER_HOST=ssh://将命令发送到远程守护进程。
架构
笔记本电脑(控制平面) NAS/服务器(执行)
────────────────────── ────────────────────────
docker-compose.yml(本地) Docker守护进程
Makefile(本地) ──SSH──▶ 容器在此运行
源代码(本地) 数据存储在此处
crane pull → .tar(本地) docker load → 镜像在此处
前提条件
笔记本电脑:
crane— 轻量级镜像拉取器,无需守护进程brew install cranedocker仅CLI(无需Docker Desktop / OrbStack)- SSH访问NAS(配置
~/.ssh/config)
NAS/服务器:
- Docker守护进程运行中
- 您的用户加入
docker组sudo usermod -aG docker <nas-user> # 注销并重新登录以使组生效 # 验证:groups | grep docker
设置
设置DOCKER_HOST以通过SSH路由所有docker命令:
export DOCKER_HOST=ssh://<nas-host>
# 验证
docker info
添加到shell配置文件中以持久化:
# ~/.zshrc 或 ~/.bashrc
export DOCKER_HOST=ssh://<nas-host>
镜像工作流程
当NAS无法访问Docker Hub(防火墙/网络)时,在笔记本电脑上用crane拉取镜像并加载到NAS:
# 1. 拉取镜像为tar包(在笔记本电脑上运行,无需守护进程)
crane pull nginx:alpine /tmp/nginx-alpine.tar
# 2. 加载到NAS(DOCKER_HOST通过SSH路由此命令)
docker load -i /tmp/nginx-alpine.tar
# 3. 运行
docker run -d --name web -p 8080:80 nginx:alpine
关键注意事项
| 方面 | 行为 |
|---|---|
| Compose文件 | 从笔记本电脑读取(本地路径) |
| 卷路径 | 从NAS文件系统挂载(必须是NAS的绝对路径) |
| 源文件 | 必须通过scp复制到NAS,然后运行compose up |
| 端口绑定 | 端口绑定在NAS IP上,通过http://<nas-ip>:<port>访问 |
最常见的错误: 使用相对卷路径如./html:/usr/share/nginx/html。这在NAS上解析,而不是您的笔记本电脑。使用NAS的绝对路径。
Docker Compose模板
services:
web:
image: nginx:alpine
ports:
- "8888:80"
volumes:
- /home/<nas-user>/project/html:/usr/share/nginx/html:ro
restart: unless-stopped
部署工作流程:
# 1. 复制源文件到NAS
scp -r ./html <nas-host>:/home/<nas-user>/project/
# 2. 拉取并加载镜像
crane pull nginx:alpine /tmp/nginx-alpine.tar
docker load -i /tmp/nginx-alpine.tar
# 3. 启动(compose文件本地,执行远程)
docker compose up -d
Makefile模板
DOCKER_HOST := ssh://<nas-host>
NAS_PROJECT := /home/<nas-user>/project
export DOCKER_HOST
IMAGES := nginx:alpine
.PHONY: pull load sync up down ps logs clean
pull:
@for img in $(IMAGES); do \
echo "Pulling $$img..."; \
crane pull $$img /tmp/$$(echo $$img | tr '/:' '-').tar; \
done
load:
@for img in $(IMAGES); do \
tarfile=/tmp/$$(echo $$img | tr '/:' '-').tar; \
echo "Loading $$tarfile..."; \
docker load -i $$tarfile; \
done
sync:
scp -r ./html <nas-host>:$(NAS_PROJECT)/
up: pull load sync
docker compose up -d
down:
docker compose down
ps:
docker compose ps
logs:
docker compose logs -f
clean: down
docker compose rm -f
docker image prune -f
参考堆栈
references/目录中的即用型compose文件。用您的值替换<nas-host>和<nas-user>。
| 堆栈 | 文件 | 端口 | 描述 |
|---|---|---|---|
| Nginx Proxy Manager | nginx-reverse-proxy.yml |
80, 443, 81 | 带有SSL终止和Web UI的反向代理 |
| FreshRSS | freshrss.yml |
8280 | 带有PostgreSQL后端的RSS feed聚合器 |
| BookLore | booklore.yml |
6060 | 用于EPUB/PDF/CBZ的数字图书馆,带自动元数据 |
| Telegram Bot | telegram-bot.yml |
— | 运行24/7的Python机器人,自定义Dockerfile |
快速部署任何堆栈:
# 示例:部署FreshRSS
crane pull freshrss/freshrss:latest /tmp/freshrss.tar
crane pull postgres:16-alpine /tmp/postgres-16-alpine.tar
docker load -i /tmp/freshrss.tar
docker load -i /tmp/postgres-16-alpine.tar
docker compose -f references/freshrss.yml up -d
多服务架构(推荐用于生产):
Nginx Proxy Manager (:80/:443)
├── FreshRSS → localhost:8280
├── BookLore → localhost:6060
└── 其他应用 → localhost:XXXX
故障排除
| 问题 | 原因 | 修复 |
|---|---|---|
docker套接字上permission denied |
用户未加入docker组 |
sudo usermod -aG docker <user> + 重新登录 |
| 组已添加但仍拒绝 | 会话未刷新 | 注销并重新登录,或使用newgrp docker |
拉取时connection reset by peer |
NAS无法访问Docker Hub | 使用crane pull + docker load工作流程 |
卷上403 Forbidden |
NAS上文件不存在 | 先用scp复制文件到NAS |
port already in use |
该端口有其他服务 | 更改端口映射或停止冲突服务 |
通过SSH时sudo需要密码 |
未分配TTY | 使用ssh -t进行交互式操作,或将用户加入docker组 |