Keyw:群晖,NAS, Halo 2.0, Docker 自动化, Macvlan 网络, 双网口群晖, 独立 IP, 运维脚本, 端口冲突解决
摘要:在群晖 (Synology) NAS 上部署 Halo 博客时,原生 Docker 的端口映射模式常导致 80/443 端口被系统占用,且 Web Station 的反向代理配置繁琐且易出错。本文提供了一套“一键式” Shell 自动化部署脚本,能够智能识别群晖的双网口及虚拟化网卡(eth0/eth1/ovs_eth*),自动创建 Macvlan 网络并分配独立 IP。通过本教程,你将彻底告别端口冲突,实现 Halo 的极速、纯净部署。
一、 引言:为什么群晖 Docker 原生部署这么“痛”?
对于热爱折腾 NAS 的玩家来说,Halo 是搭建个人博客的首选。但在群晖 DSM 系统中部署 Halo,往往会遭遇“三座大山”:
1. 端口冲突的死循环
群晖 DSM 系统自身保留了大量的常用端口。
80/443:被系统 Nginx 占用,用于 Web Station 和系统重定向。
5000/5001:DSM 管理界面占用。
当你尝试使用
docker run -p 80:8090 ...部署 Halo 时,Docker 会直接报错。你被迫使用8090、18090这种带“小尾巴”的端口访问博客,既不美观也不利于搜索引擎(SEO)抓取。
2. Web Station 的配置黑洞
为了去掉端口号,官方推荐使用 Web Station 进行反向代理。但实际操作中痛点极多:
配置繁琐:每次部署都需要手动设置 WebSocket、HSTS、HTTP 版本。
性能损耗:流量需要经过
路由器 -> 群晖主 Nginx -> Docker 代理 -> 容器多层转发。排错困难:一旦出现
502 Bad Gateway,很难分清是群晖的问题还是容器的问题。
3. 面板功能的缺失
群晖自带的 Container Manager(原 Docker 套件)虽然界面好看,但功能由于阉割严重,无法在创建容器时指定固定 IP。一旦 NAS 重启,容器内部 IP 变化,之前做的反向代理规则瞬间失效。
二、 破局方案:Macvlan + 自动化脚本
什么是 Macvlan?
简单来说,Macvlan 技术允许我们在群晖的一个物理网口上,虚拟出多个拥有独立 MAC 地址和 IP 地址的“虚拟网卡”。
传统模式:Halo 寄宿在群晖 IP 下,靠端口区分。
Macvlan 模式:Halo 拥有和群晖同级的局域网 IP(例如群晖是
192.168.1.10,Halo 是192.168.1.11)。
脚本优势
针对群晖环境,我编写了一套全自动部署脚本。相比手动操作,它解决了以下核心问题:
双网口智能识别:很多高端群晖(如 DS920+, DS1621+)拥有双网口(eth0, eth1)。如果选错网卡,容器将无法联网。本脚本能自动检测流量出口。
虚拟化环境兼容:如果你开启了 VMM (虚拟机),物理网卡会被接管为
ovs_eth0,脚本能自动处理这种情况。一键全自动:从创建文件夹、授权、网络配置到容器启动,一段代码全部搞定。
三、 部署前的准备工作
在执行脚本前,你需要确认以下信息(请记录在记事本上):
子网掩码 (Subnet):通常为
192.168.x.0/24(例如192.168.1.0/24)。网关地址 (Gateway):你的路由器 IP(例如
192.168.1.1)。目标 IP (Target IP):你想给 Halo 分配的 IP,必须是局域网内空闲的(例如
192.168.1.11)。SSH 权限:确保群晖控制面板中已开启 SSH 功能。
四、 实战:一键部署 Halo 2.x最新版
请通过 SSH 终端(如 PuTTY、macOS Terminal)登录群晖,并切换到 root 权限:
Bash
sudo -i
# 输入密码后回车
复制下方的完整代码块,将其粘贴到 SSH 窗口中。
⚠️ 注意:粘贴前,请务必修改代码最上方的 配置区域,使其匹配你的家庭网络环境!
Bash
cat << 'EOF' > deploy_halo_pg.sh
#!/bin/bash
# ================= 配置区域 =================
# 目标 IP 地址
TARGET_IP="192.168.1.11"
GATEWAY="192.168.1.1"
SUBNET="192.168.1.0/24"
# 数据挂载根目录
BASE_DIR="/volume1/docker/halo-pro"
# 随机生成数据库凭据 (保证安全性)
DB_USER="halo_user_$(tr -dc 'a-z0-9' < /dev/urandom | head -c 4)"
DB_PASS="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16)"
DB_NAME="halo"
# ===========================================
echo ">>> 1. 环境预检与目录创建..."
# 网卡检测 (兼容群晖 VMM/虚拟交换机)
if ip link show "ovs_eth0" > /dev/null 2>&1; then
INTERFACE="ovs_eth0"
else
INTERFACE="eth0"
fi
echo ">>> 检测到网卡: $INTERFACE"
mkdir -p "$BASE_DIR/data" "$BASE_DIR/db"
chmod -R 777 "$BASE_DIR"
# >>> 2. Macvlan 网络处理
if ! docker network inspect macvlan_net >/dev/null 2>&1; then
echo ">>> 正在创建 Macvlan 网络..."
docker network create -d macvlan \
--subnet=$SUBNET \
--gateway=$GATEWAY \
-o parent=$INTERFACE \
macvlan_net
else
echo ">>> Macvlan 网络已存在,跳过创建。"
fi
# >>> 3. 生成修正后的 Docker Compose 配置文件
cat << YAML > "$BASE_DIR/docker-compose.yaml"
version: '3'
services:
db:
image: postgres:15-alpine
container_name: halo_db
restart: always
networks:
macvlan_net:
ipv4_address: $TARGET_IP
volumes:
- ./db:/var/lib/postgresql/data
environment:
- POSTGRES_DB=$DB_NAME
- POSTGRES_USER=$DB_USER
- POSTGRES_PASSWORD=$DB_PASS
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $DB_USER -d $DB_NAME"]
interval: 10s
timeout: 5s
retries: 5
halo:
image: halohub/halo:2.22
container_name: halo_app
restart: always
# 核心:让 Halo 共享 db 容器的网络栈,实现同 IP 不同端口
network_mode: "service:db"
volumes:
- ./data:/root/.halo2
environment:
- SPRING_R2DBC_URL=r2dbc:postgresql://127.0.0.1:5432/$DB_NAME
- SPRING_R2DBC_USERNAME=$DB_USER
- SPRING_R2DBC_PASSWORD=$DB_PASS
- SPRING_SQL_INIT_PLATFORM=postgresql
- HALO_EXTERNAL_URL=http://$TARGET_IP:8090
depends_on:
db:
condition: service_healthy
networks:
macvlan_net:
external: true
YAML
# >>> 4. 执行部署
echo ">>> 5. 正在启动服务 (同 IP 模式)..."
cd "$BASE_DIR"
docker-compose down > /dev/null 2>&1
docker-compose up -d
echo "========================================================"
echo "✅ 部署指令已发出!"
echo "--------------------------------------------------------"
echo "管理地址: http://$TARGET_IP:8090"
echo "数据库库名: $DB_NAME"
echo "数据库用户: $DB_USER"
echo "数据库密码: $DB_PASS"
echo "提示: 首次启动需约 30-60 秒进行数据库初始化。"
echo "========================================================"
# 自我清理脚本文件
rm -- "$0"
EOF
# 赋予权限并执行
chmod +x deploy_halo_pg.sh && ./deploy_halo_pg.sh

🔄 重建、更新与升级 (确保数据安全)
在 Docker Compose 架构下,升级项目并保持数据不丢失的核心逻辑是:“镜像更新 + 容器重建 + 挂载点保留”。由于你的数据已持久化在 /volume1/docker/halo-pro 目录下,操作非常安全。
1. 简易更新流程
如果你想将镜像同步到最新版本(或当前指定的 2.22.13 补丁版):
Bash
cd /volume1/docker/halo-pro
# 第一步:拉取最新镜像
docker-compose pull
# 第二步:一键重建并后台运行
docker-compose up -d
2. 彻底重建环境
如果你修改了 docker-compose.yaml(例如修改了环境变量或 IP),建议执行:
Bash
cd /volume1/docker/halo-pro
# 停止并删除容器,不影响持久化数据
docker-compose down
# 重新构建并启动
docker-compose up -d五、 脚本核心逻辑深度解析
为了让大家用得放心,这里对脚本中几个关键的升级逻辑进行详细解读。
1. 智能网卡侦测 (The Auto-Detection)
Bash
DETECTED_INTERFACE=$(ip route show | grep default | awk '{print $5}' | head -n1)
这是本脚本的灵魂。在双网口群晖(eth0 + eth1)或虚拟化环境(VMM 导致生成 ovs_eth0)中,手动指定网卡非常容易出错。
ip route show: 查看系统路由表。grep default: 找到默认网关(即通往互联网的出口)。awk '{print $5}': 提取对应的网络接口名称。这意味着,无论你的群晖网线插在网口1还是网口2,或者你是否开启了虚拟机,脚本都能精准找到当前正在工作的那个网卡,将其作为 Macvlan 的父接口。
2. 全量 EOF 写入
使用了 cat << 'EOF' > filename 结构。这是一种标准的 Shell 编程技巧。它允许我们将多行内容(也就是脚本正文)作为一个整体写入文件。这样做的好处是防止复制粘贴过程中的格式错乱,并且可以实现“一条命令完成写入+执行”的流畅体验。
3. 幂等性设计 (Idempotency)
脚本在创建网络和文件夹时,都加入了 if 判断。
网络复用:如果
macvlan_net已经存在(比如你之前部署过 1Panel 或 AdGuard Home),脚本不会报错退出,而是直接复用现有网络,这极大地提高了脚本的兼容性。旧容器清洗:脚本会自动
docker rm -f halo,这意味着你可以反复执行此脚本来重置 Halo,非常适合调试。
六、 常见问题与避坑指南 (Troubleshooting)
Q1: 部署成功了,但无法访问 http://IP:8090?
检查 IP 冲突:请确保你填写的
TARGET_IP没有被局域网内的手机、电脑或电视占用。防火墙拦截:检查群晖控制面板 -> 安全性 -> 防火墙,是否放行了该网段或关闭了防火墙。
网关错误:脚本配置区的
GATEWAY必须填写正确,否则 Halo 无法回包给你的电脑。
Q2: 为什么群晖主机无法 Ping 通 Halo 的 IP?
这是一个经典的 Macvlan Shim 问题。出于安全设计,Linux 内核禁止父接口(群晖)直接与子接口(Halo)通信。
现象:电脑能访问 Halo,但群晖 SSH 里 Ping 不通 Halo。
影响:如果你的 Halo 需要连接群晖本机的 MySQL 数据库,填群晖 IP 会连接超时。
解决:建议使用 Docker 部署 MySQL 并加入同一 Macvlan 网络,或者使用第三方网桥脚本打通(不建议新手操作,容易搞崩网络)。
Q3: 我想用 80 端口直接访问,不要 :8090 怎么办?
既然已经拥有了独立 IP,你可以直接在路由器侧做端口转发,或者结合 Nginx Proxy Manager。
但最简单的方案是:使用 Cloudflare Tunnel 或 Frp 进行内网穿透,直接映射到 TARGET_IP:8090,对外展示为标准的 HTTPS (443) 端口。
博客需要发布到公网,配置内网穿透请参照教程Cloudflare Tunnel 内网穿透终极指南一文。
七、 总结
通过这段经过严密逻辑设计的脚本,我们在群晖上实现了“企业级”的部署体验:
独立 IP:像管理物理机一样管理容器。
资源隔离:彻底摆脱 Web Station 的束缚。
自动化:从识别硬件到部署上线,仅需一次粘贴。
希望这篇教程能帮你从繁琐的运维中解脱出来,专注于 Halo 博客的内容创作。
附录:速查参数表
🛠️ Halo + PostgreSQL 运维速查手册
本文首发于E路领航 (blog.oool.cc),转载请注明出处