Harbor 高可用 (HA) 与 HTTPS 安全加固实践
Harbor 作为企业级的 Docker 镜像仓库,在生产环境中稳定可靠地运行至关重要。本文将深入探讨如何为 Harbor 配置高可用性 (HA) 方案,并启用 HTTPS 来保障数据传输安全,确保 Harbor 服务的连续性和安全性。
一、Harbor 高可用 (HA) 解决方案概述
在生产环境中,单点故障是不可接受的。为了保证 Harbor 服务的高可用,通常需要考虑以下几个层面:
- 接入层高可用:确保用户或 CI/CD 系统始终能通过一个稳定的入口访问 Harbor 服务,即使后端某个 Harbor 实例宕机。
- Harbor 服务自身高可用:运行多个 Harbor 实例,分担负载或实现故障转移。
- 后端存储高可用:Harbor 存储镜像数据(Registry)和元数据(Database)。这两部分都需要高可用方案。
- Registry 存储:通常使用共享存储,如 NFS、Ceph、S3 兼容对象存储等,确保所有 Harbor 实例访问的是同一份数据。
- Database 存储:使用高可用的数据库集群,如 PostgreSQL 主从复制或集群方案。
本文将重点关注 接入层高可用 的一种实现方式:使用 Keepalived 实现 Virtual IP (VIP) 漂移。
方案:基于 Keepalived 的 Active/Passive HA
- 架构:部署两个(或多个)配置完全相同的 Harbor 实例。使用 Keepalived 在这两个实例之间管理一个 VIP。
- 工作原理:
- Keepalived 通过 VRRP (Virtual Router Redundancy Protocol) 协议选举一个 Master 节点。
- Master 节点持有 VIP,负责处理所有通过 VIP 访问 Harbor 的请求。
- Backup 节点处于待命状态,持续监听 Master 节点的心跳。
- Keepalived 配置健康检查脚本,用于探测本地 Harbor 服务的可用性。
- 当 Master 节点宕机或其上的 Harbor 服务不可用(健康检查失败)时,Keepalived 会降低其优先级。
- Backup 节点检测到 Master 故障后,如果自身健康且优先级最高,则接管 VIP,成为新的 Master。
- 整个切换过程对客户端透明,客户端始终访问 VIP。
- 前提条件:
- 至少两个已部署并配置相同的 Harbor 节点(假设为
10.0.0.101
和10.0.0.102
)。 - Harbor 的 Registry 数据必须使用 共享存储(如 NFS、S3 等)。
- Harbor 的数据库必须使用 外部高可用数据库集群 或 主从复制,并配置所有 Harbor 实例连接到该集群/主库(或其 VIP)。 Keepalived 只解决入口 IP 的漂移,不解决数据同步问题。
- 网络环境支持 VRRP 协议(通常需要二层可达,注意防火墙规则)。
- 至少两个已部署并配置相同的 Harbor 节点(假设为
二、使用 Keepalived 实现 Harbor VIP 漂移
以下步骤演示如何在两个 Harbor 节点(docker101: 10.0.0.101
, docker102: 10.0.0.102
)上配置 Keepalived,实现 VIP 10.0.0.250
的高可用。
1. 安装 Keepalived
在两个 Harbor 节点上都执行安装命令:
# 在 docker101 和 docker102 上执行
sudo apt update
sudo apt -y install keepalived
2. 创建健康检查脚本
Keepalived 需要一个脚本来检查本地 Harbor 服务的健康状况。这里我们创建一个简单的脚本,检查 Harbor Nginx 前端(通常监听 80 或 443 端口)是否可用。
# 在 docker101 和 docker102 上创建此脚本
sudo mkdir -p /etc/keepalived/
sudo tee /etc/keepalived/check_harbor.sh > /dev/null <<'EOF'
#!/bin/bash
# 检查 Harbor Nginx 端口是否监听
# $1: 要检查的端口号 (例如 80 或 443)
PORT_TO_CHECK=${1:-80} # 默认为 80 端口
CHECK_URL="http://127.0.0.1:${PORT_TO_CHECK}/" # 检查本地 Nginx
# 尝试访问 Harbor Nginx 代理的某个路径,根据实际情况调整
# 对于较新版本 Harbor,可以直接检查 /api/v2.0/ping 或 /v2/
# 这里我们简单检查本地端口是否能建立连接
if curl --output /dev/null --silent --head --fail --max-time 2 "${CHECK_URL}"; then
# 或者使用 nc/ss 检查端口监听:
# if ss - Hltn sport = :${PORT_TO_CHECK} | grep LISTEN; then
exit 0 # 端口正常,返回 0
else
exit 1 # 端口异常,返回 1
fi
EOF
# 赋予执行权限
sudo chmod +x /etc/keepalived/check_harbor.sh
- 注意:这个脚本非常基础。生产环境建议使用更健壮的检查,例如:
- 检查 Harbor Core API 是否正常 (
/api/v2.0/ping
)。 - 如果使用 HTTPS,请修改
CHECK_URL
为https://127.0.0.1:${PORT_TO_CHECK}/
并可能需要添加curl -k
(不推荐,最好配置信任) 或检查特定 API 端点。 - 脚本的检查目标端口 (
$1
) 需要与keepalived.conf
中vrrp_script
的参数一致。如果 Harbor 使用 HTTPS,则应检查 443 端口。
- 检查 Harbor Core API 是否正常 (
3. 配置 Keepalived (Master 节点 - docker101)
编辑 docker101
上的 Keepalived 配置文件 /etc/keepalived/keepalived.conf
。
# 在 docker101 上执行
sudo tee /etc/keepalived/keepalived.conf > /dev/null <<'EOF'
! Configuration File for keepalived
global_defs {
# 标识本节点的路由器 ID,建议使用节点 IP 或唯一名称
router_id harbor_node_101
# 启用 VRRP 脚本日志记录 (可选, 方便调试)
# vrrp_log_run_scripts
}
# 定义健康检查脚本
vrrp_script chk_harbor_service {
script "/etc/keepalived/check_harbor.sh 80" # 检查 HTTP 80 端口,如果用 HTTPS 则改为 443
interval 2 # 每 2 秒执行一次脚本
timeout 1 # 脚本执行超时时间
fall 2 # 连续 2 次失败则认为服务故障
rise 2 # 连续 2 次成功则认为服务恢复
weight -20 # 如果脚本失败(exit 非0),则将本节点优先级降低 20
}
# 定义 VRRP 实例
vrrp_instance VI_HARBOR {
state MASTER # 初始状态为 Master
interface ens33 # VIP 绑定的网络接口,根据实际情况修改
virtual_router_id 51 # VRID,同一 VRRP 组内的节点必须相同!
priority 101 # 优先级,Master 节点通常设置较高值
advert_int 1 # VRRP 通告间隔(秒)
# mcast_src_ip 10.0.0.101 # (可选) 指定发送 VRRP 报文的源 IP,通常自动选择接口 IP
# 如果网络环境复杂或有多 IP,建议指定
# nopreempt # (可选) Master 恢复后不抢占 VIP,让当前 Master 继续服务。
# 如果希望 Master 恢复后立即夺回 VIP,请注释或移除此行。
authentication {
auth_type PASS # 认证方式
auth_pass yinzhengjie_k8s_ha # 认证密码,同一 VRRP 组内必须相同!
}
# 追踪健康检查脚本
track_script {
chk_harbor_service # 关联上面定义的脚本
}
# 定义虚拟 IP 地址 (VIP)
virtual_ipaddress {
10.0.0.250/32 dev ens33 # VIP 地址和子网掩码,/32 表示单主机路由
# dev ens33 (可选) 明确指定 VIP 绑定的接口
}
# (可选) 针对单播模式 (如果组播受限)
# unicast_src_ip 10.0.0.101 # 本机 IP
# unicast_peer {
# 10.0.0.102 # 对端节点 IP
# }
}
EOF
- 关键配置解释:
router_id
: 节点的唯一标识。vrrp_script
: 定义如何执行健康检查。weight -20
表示检查失败时,优先级降低 20。state MASTER
: 指定此节点优先成为 Master。interface
: VIP 将绑定到此网络接口。virtual_router_id
: 极其重要,同一高可用组内的所有 Keepalived 节点必须配置 相同 的 VRID(例如 51)。priority
: 决定 Master 选举,值越高优先级越高。Master 应高于 Backup。authentication
: 确保只有密码匹配的节点才能加入 VRRP 组。track_script
: 将健康检查脚本与 VRRP 实例关联。当脚本失败时,根据weight
调整优先级。virtual_ipaddress
: 要管理的 VIP。/32
通常用于 VIP。nopreempt
: (可选) 防止已恢复的原 Master 节点抢占 VIP。在 Active/Passive 模式下,如果希望故障恢复后 VIP 留在当前工作的节点上(减少不必要的切换),可以启用nopreempt
。如果希望原 Master 恢复后总是拿回 VIP,则不设置或注释掉此项。
4. 配置 Keepalived (Backup 节点 - docker102)
编辑 docker102
上的 Keepalived 配置文件 /etc/keepalived/keepalived.conf
。大部分配置与 Master 相同,主要修改 state
, priority
, 和 router_id
。
# 在 docker102 上执行
sudo tee /etc/keepalived/keepalived.conf > /dev/null <<'EOF'
! Configuration File for keepalived
global_defs {
router_id harbor_node_102 # 修改为节点 102 的唯一标识
# vrrp_log_run_scripts
}
vrrp_script chk_harbor_service {
script "/etc/keepalived/check_harbor.sh 80" # 与 Master 节点保持一致
interval 2
timeout 1
fall 2
rise 2
weight -20
}
vrrp_instance VI_HARBOR {
state BACKUP # 初始状态为 Backup
interface ens33 # 与 Master 节点保持一致
virtual_router_id 51 # !! 必须与 Master 节点相同 !!
priority 100 # !! 优先级必须低于 Master 节点 (例如 100) !!
advert_int 1
# mcast_src_ip 10.0.0.102 # (可选) 指定源 IP
# nopreempt # (可选) 通常 Backup 节点也建议设置 nopreempt
authentication {
auth_type PASS
auth_pass yinzhengjie_k8s_ha # !! 必须与 Master 节点相同 !!
}
track_script {
chk_harbor_service # 与 Master 节点保持一致
}
virtual_ipaddress {
10.0.0.250/32 dev ens33 # 与 Master 节点保持一致
}
# (可选) 针对单播模式
# unicast_src_ip 10.0.0.102 # 本机 IP
# unicast_peer {
# 10.0.0.101 # 对端节点 IP
# }
}
EOF
- 主要区别:
router_id
: 更改为harbor_node_102
。state
: 设置为BACKUP
。priority
: 设置为100
,低于 Master 节点的101
。virtual_router_id
和auth_pass
: 必须 与 Master 节点完全一致。
5. 启动并设置 Keepalived 开机自启
在两个 Harbor 节点上都执行:
# 在 docker101 和 docker102 上执行
sudo systemctl restart keepalived
sudo systemctl enable keepalived
6. 验证初始状态
-
查看 VIP:在
docker101
(Master) 上执行ip addr show ens33
,应该能看到10.0.0.250
这个 VIP。# 在 docker101 上执行 ip addr show ens33 # ... 输出应包含类似行 ... # inet 10.0.0.101/24 brd 10.0.0.255 scope global ens33 # valid_lft forever preferred_lft forever # inet 10.0.0.250/32 scope global ens33 <--- VIP 存在 # valid_lft forever preferred_lft forever # ...
-
在
docker102
(Backup) 上执行ip addr show ens33
,不应该 看到10.0.0.250
这个 VIP。 -
查看 Keepalived 日志:检查 Keepalived 服务状态和日志,确认 VRRP 状态。
# 在 docker101 和 docker102 上查看 sudo systemctl status keepalived sudo journalctl -u keepalived -f # 实时查看日志 # 或者查看日志文件 (如果配置了) # sudo tail -f /var/log/syslog | grep Keepalived
在 Master (
docker101
) 日志中应该看到类似Entering MASTER STATE
的信息。在 Backup (docker102
) 日志中应该看到类似Entering BACKUP STATE
的信息。 -
访问 Harbor:尝试通过 VIP
http://10.0.0.250
(或https://
如果已配置 HTTPS) 访问 Harbor Web UI,应该可以正常访问。
7. 测试故障切换
-
模拟 Master 故障:在
docker101
上停止 Keepalived 服务。# 在 docker101 上执行 sudo systemctl stop keepalived
-
观察 VIP 漂移:
- 在
docker101
上再次执行ip addr show ens33
,确认 VIP10.0.0.250
已经消失。 - 在
docker102
上执行ip addr show ens33
,现在应该能看到 VIP10.0.0.250
。
# 在 docker102 上执行 ip addr show ens33 # ... 输出现在应包含 VIP ... # inet 10.0.0.102/24 brd 10.0.0.255 scope global ens33 # valid_lft forever preferred_lft forever # inet 10.0.0.250/32 scope global ens33 <--- VIP 已漂移至此 # valid_lft forever preferred_lft forever # ...
- 查看
docker102
的 Keepalived 日志,应该看到类似Entering MASTER STATE
的信息。
- 在
-
再次访问 Harbor:继续通过 VIP
http://10.0.0.250
访问 Harbor Web UI。由于 VIP 已经切换到docker102
,并且docker102
上的 Harbor 服务正常运行(且连接到共享存储和数据库),访问应该仍然成功。
8. 测试故障恢复 (Failback)
-
在
docker101
上重新启动 Keepalived 服务。# 在 docker101 上执行 sudo systemctl start keepalived
-
观察 VIP 行为:
- 如果未设置
nopreempt
:docker101
的优先级 (101) 高于docker102
(100),docker101
会重新抢占 VIP。VIP 将从docker102
消失,并重新出现在docker101
上。 - 如果设置了
nopreempt
:即使docker101
恢复且优先级更高,它也不会主动抢占。VIP 将 继续保留 在docker102
上,直到docker102
发生故障。
- 如果未设置
根据你的配置(包含了 nopreempt
),VIP 应该会留在 docker102
上。
三、Harbor 部署基于官方认证的 HTTPS
为 Harbor 启用 HTTPS 是生产环境的基本要求,可以加密客户端与 Harbor 之间的数据传输,防止中间人攻击。
前提:
- 你已经从受信任的证书颁发机构 (CA) 获取了 SSL/TLS 证书(例如,Let's Encrypt 或商业 CA)。
- 证书通常包含至少两个文件:证书文件 (
.crt
或.pem
,可能包含证书链) 和私钥文件 (.key
)。 - 证书的通用名称 (Common Name, CN) 或主题备用名称 (Subject Alternative Name, SAN) 必须与你打算用于访问 Harbor 的域名完全匹配(例如
www.yinzhengjie.com
)。
步骤:
1. 准备证书文件
-
在部署 Harbor 的节点上创建一个用于存放证书的目录(如果使用 HA,请在 所有 Harbor 节点上执行此操作)。
# 假设在 docker102 上操作 (HA 环境下,docker101 也需要) # 使用你实际的 Harbor 安装路径 HARBOR_BASE_DIR="/path/to/your/harbor" # 例如 /opt/harbor mkdir -p ${HARBOR_BASE_DIR}/certs cd ${HARBOR_BASE_DIR}/certs
-
将你的证书文件(例如
yourdomain.crt
)和私钥文件(例如yourdomain.key
)上传或复制到这个certs
目录。- 示例中使用的是
yinzhengjie.com_bundle.crt
和yinzhengjie.com.key
。请替换为你自己的文件名。 - 确保证书文件权限安全,私钥文件尤其重要,通常设置为
chmod 600 yourdomain.key
。
# 示例:假设证书已下载并解压到当前目录的 yinzhengjie.com_nginx 子目录 # 实际操作中,直接将 .crt 和 .key 文件放入 ${HARBOR_BASE_DIR}/certs 即可 # 例如: # cp /path/from/download/www.yinzhengjie.com_bundle.crt ${HARBOR_BASE_DIR}/certs/ # cp /path/from/download/www.yinzhengjie.com.key ${HARBOR_BASE_DIR}/certs/ # sudo chmod 600 ${HARBOR_BASE_DIR}/certs/www.yinzhengjie.com.key
- 示例中使用的是
2. 修改 Harbor 配置文件 (harbor.yml
)
-
编辑 Harbor 的主配置文件
harbor.yml
。# 假设在 docker102 上操作 (HA 环境下,docker101 的 harbor.yml 也需要同步修改) cd ${HARBOR_BASE_DIR} vim harbor.yml
-
找到并修改以下配置项:
-
hostname
: 必须 设置为你的证书匹配的域名。# hostname 是外部访问 Harbor 的地址,必须与证书 CN/SAN 匹配 hostname: www.yinzhengjie.com
-
http
: (可选) 如果只想允许 HTTPS 访问,可以注释掉http
部分或只保留https
部分。# http: # # port for http, default is 80 # port: 80
-
https
: 配置 HTTPS 相关参数。# https configuration https: # https port for harbor, default is 443 port: 443 # The path of cert and key files for nginx # !! 使用你实际存放证书的绝对路径 !! certificate: /path/to/your/harbor/certs/www.yinzhengjie.com_bundle.crt private_key: /path/to/your/harbor/certs/www.yinzhengjie.com.key
确保
certificate
和private_key
的路径指向你在步骤 1 中放置的实际文件。
-
3. 应用配置并重新安装/更新 Harbor
修改 harbor.yml
后,需要重新运行 Harbor 的安装脚本来应用更改。这通常会重新生成 Nginx 等组件的配置并重启容器。
-
停止当前运行的 Harbor (如果正在运行)
# 在 Harbor 安装目录下执行 (docker101 和 docker102 都需要,如果 HA) sudo docker-compose down -v # -v 会移除关联的数据卷,如果需要保留旧数据,请谨慎操作或备份
-
运行
prepare
脚本 (重要):此脚本会根据harbor.yml
生成所需的配置文件。# 在 Harbor 安装目录下执行 (docker101 和 docker102 都需要,如果 HA) sudo ./prepare
-
运行
install.sh
脚本:完成安装或更新过程。# 在 Harbor 安装目录下执行 (docker101 和 docker102 都需要,如果 HA) # 根据需要添加其他模块,如 --with-notary, --with-trivy, --with-chartmuseum sudo ./install.sh --with-chartmuseum
--with-chartmuseum
是用于启用 Helm Chart 仓库功能,根据你的需求添加。
4. 验证 HTTPS 访问
-
DNS 配置 (生产环境):确保你的域名 (
www.yinzhengjie.com
) 的 DNS 解析指向 Harbor 的 VIP (10.0.0.250
)。 -
本地 Hosts 文件 (测试环境):如果在测试环境或 DNS 未配置,可以临时修改操作系统的
hosts
文件,将域名指向 VIP。- Windows:
C:\Windows\System32\drivers\etc\hosts
- Linux/macOS:
/etc/hosts
- 添加一行:
10.0.0.250 www.yinzhengjie.com
- Windows:
-
访问 Web UI:打开浏览器,访问
https://www.yinzhengjie.com
(或者你配置的hostname
和https.port
,默认是 443)。- 浏览器应该显示安全的锁图标,表示连接已通过 HTTPS 加密并且证书受信任。
- 登录 Harbor 并进行基本操作,确认功能正常。
-
Docker/kubectl 客户端配置:如果客户端(如 Docker daemon 或 Kubernetes 节点)需要信任此 Harbor 仓库,并且使用的是官方 CA 签发的证书,通常不需要额外配置。如果使用自签名证书或私有 CA 证书,则需要在客户端配置信任该证书。
四、总结与后续
通过结合 Keepalived 实现的 VIP 漂移和标准的 HTTPS 配置,我们为 Harbor 构建了一个更健壮、更安全的运行环境。
关键要点回顾:
- Harbor HA 基础:Keepalived 解决了入口 IP 的高可用,但 必须 配合共享存储 (Registry) 和高可用数据库 (Database) 才能实现完整的 Harbor 服务高可用。
- Keepalived 配置:注意
virtual_router_id
,priority
,authentication
,track_script
等关键参数的正确设置,确保 Master 和 Backup 节点配置协调一致。健康检查脚本 (check_harbor.sh
) 对及时发现服务故障至关重要。 - HTTPS 配置:
harbor.yml
中的hostname
必须与证书匹配,certificate
和private_key
路径必须正确。修改配置后需运行./prepare
和./install.sh
生效。 - 生产环境:使用 DNS 解析 VIP,而非修改
hosts
文件。确保证书由受信任的 CA 签发。
后续考虑:
- 监控:对 Keepalived 服务本身、VIP 状态以及 Harbor 各组件进行监控。
- 数据库 HA:详细规划并实施 PostgreSQL 或其他数据库的高可用方案。
- 存储冗余:确保共享存储(NFS、S3 等)本身具有高可用性和数据冗余。
- 备份策略:定期备份 Harbor 数据库和 Registry 存储。
- 负载均衡:对于更高负载的场景,可以考虑使用负载均衡器(如 Nginx, HAProxy, F5 等)配合多个 Active 的 Harbor 实例,但这需要更复杂的配置来处理会话和状态。
希望这篇详细的指南能帮助你成功部署高可用且安全的 Harbor 服务!