一、架构选型与场景定位
在容器化落地过程中,私有镜像仓库是核心基础设施。根据业务规模与安全合规要求,通常面临两种选择:轻量级的官方 Docker Registry 与企业级的 Harbor。
Docker Registry 适合作为学习实验、临时中转站或边缘节点的缓存层。它的优势在于极简部署,但功能单一,缺乏用户界面和细粒度的权限控制。特别是在生产环境中,其镜像清理机制存在明显短板——删除镜像后元数据仍驻留内存,往往需要重启容器才能释放空间,这种“中断式”维护在生产环境是不可接受的。此外,默认配置下它仅支持 HTTP 明文传输,存在数据泄露风险。
Harbor 则是云原生时代的企业级标准。它提供了完善的 Web UI、基于角色的访问控制(RBAC)、镜像漏洞扫描以及审计日志。对于核心业务集群,必须采用 Harbor 来保障供应链安全。它支持自动垃圾回收(GC),无需重启服务即可清理磁盘空间,并原生支持 HTTPS 和高可用架构。
结论:测试环境可快速搭建 Registry 验证流程,但生产环境必须部署 Harbor,且需严格配置 HTTPS 与外部存储,杜绝单点故障与明文传输。
二、轻量级 Registry 的标准化部署(测试/边缘场景)
虽然 Registry 不适合核心生产区,但在内网隔离区或 CI 测试环节仍有应用价值。部署时需重点解决数据持久化与清理机制问题。
1. 部署配置优化
直接使用 docker run 容易导致配置分散且难以维护,推荐采用 docker-compose 进行标准化管理。关键在于开启删除功能并挂载数据卷,防止容器移除后数据丢失。
# docker-compose-registry.yml
version: '3'
services:
registry:
image: registry:2.8.3
container_name: local-registry
restart: always
ports:
- "5000:5000"
environment:
# 必须显式开启删除功能,否则 API 拒绝删除请求
- REGISTRY_STORAGE_DELETE_ENABLED=true
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
volumes:
- ./data:/var/lib/registry # 数据持久化,解耦容器生命周期
- ./config:/etc/docker/registry
# 生产建议:实际使用中应前置 Nginx 反向代理,由 Nginx 处理 HTTPS 证书与基本认证
2. 镜像清理的正确姿势
在实际操作中,直接调用 API 删除镜像后,磁盘空间往往不会立即释放。这是因为 Registry 进程将元数据缓存在内存中。传统的“重启容器”方案会导致服务中断,不符合运维连续性要求。
标准的处理流程是执行垃圾回收(Garbage Collection)并触发配置重载。若版本不支持热重载,则采用滚动更新策略,确保服务不中断:
# 第一步:执行垃圾回收,清理未被引用的镜像层
docker exec -it local-registry registry garbage-collect --delete-untagged /etc/docker/registry/config.yml
# 第二步:若空间未释放,使用 docker-compose 进行滚动重启(而非直接 kill/restart)
# 这将重新拉起容器并加载最新的文件系统状态,实现零停机维护
docker-compose up -d --force-recreate --no-deps registry
三、Harbor 企业级部署与自动化实践
Harbor 的部署复杂度远高于 Registry,涉及数据库、Redis、核心服务及存储等多个组件。在生产环境落地时,版本稳定性、数据安全与网络配置是三大核心考量。
1. 关键配置策略
- 版本锁定:避免盲目追求最新版。例如 v2.15.0 曾存在已知缺陷,生产环境建议锁定在 v2.14.x LTS 版本或经官方验证的最新稳定版。
- 存储规划:默认配置将数据存放在
/var/lib/harbor,这极易导致宿主机根分区爆满。必须将data_volume挂载至独立的数据盘、NFS 或对象存储(如 S3/Ceph),确保镜像数据与系统盘分离。 - 强制 HTTPS:生产环境严禁使用
insecure-registries绕过证书验证。必须申请合法 SSL 证书(或企业内部 CA 签发的受信任证书),并在harbor.yml中启用 HTTPS 端口。
2. 自动化部署脚本
手动修改配置文件容易出错且难以复用。以下脚本实现了预检、备份、配置自动生成及安装的一体化流程,特别强化了密码管理与端口检查。
#!/bin/bash
set -e
HARBOR_VERSION="v2.14.0"
DATA_DIR="/data/harbor"
CERT_DIR="/etc/harbor/ssl"
echo ">>> 开始预检..."
# 检查关键端口占用,避免与现有 Nginx 或其他服务冲突
for port in 80 443 4443; do
if ss -ntl | grep -q ":$port "; then
echo "错误:端口 $port 已被占用,请先停止相关服务。"
exit 1
fi
done
# 备份旧配置(升级场景)
if [ -d "/usr/local/harbor" ]; then
cp -r /usr/local/harbor/harbor.yml /usr/local/harbor/harbor.yml.bak.$(date +%F)
fi
echo ">>> 生成配置文件..."
cd /usr/local/harbor
cp harbor.yml.tmpl harbor.yml
# 自动化替换关键参数
sed -i "s/hostname: reg.mydomain.com/hostname: harbor.corp.local/" harbor.yml
sed -i "s|# https:|https:|" harbor.yml
sed -i "s|# port: 443| port: 443|" harbor.yml
sed -i "s|# certificate:| certificate: $CERT_DIR/server.crt|" harbor.yml
sed -i "s|# private_key:| private_key: $CERT_DIR/server.key|" harbor.yml
# 安全提示:生产环境严禁在脚本中硬编码密码。
# 此处建议使用环境变量传入,或在安装时交互式输入
ADMIN_PWD="${HARBOR_ADMIN_PWD:-StrongP@ssw0rd}"
sed -i "s/harbor_admin_password: Harbor12345/harbor_admin_password: $ADMIN_PWD/" harbor.yml
echo ">>> 执行安装..."
./prepare
# 建议开启 Trivy 漏洞扫描与 Chartmuseum Helm 仓库支持
./install.sh --with-trivy --with-chartmuseum
echo "Harbor 部署完成,访问地址:https://harbor.corp.local"
四、初始化配置:从可视化操作到自动化交付
部署完成 Harbor 服务只是第一步。许多初学者习惯登录 Web 界面手动下载证书、点击创建项目,这种“手工坊”模式在测试环境尚可,但在生产环境中效率低下且难以审计。成熟的运维体系应将证书分发与项目创建纳入自动化流程。
1. 根证书的分发与信任(替代手动下载)
若使用自签名证书,所有客户端(K8s 节点、CI 构建机)必须信任该证书。手动在网页下载 ca.crt 再逐台复制不仅繁琐,还容易遗漏。
自动化分发逻辑:
直接从运行中的 Harbor Nginx 容器提取证书,并分发至客户端的标准信任目录 /etc/docker/certs.d/<域名>/,随后重启 Docker 守护进程。
# 在任意需要连接 Harbor 的客户端节点执行
HARBOR_DOMAIN="harbor.corp.local"
CERT_DIR="/etc/docker/certs.d/${HARBOR_DOMAIN}"
# 1. 创建证书目录
mkdir -p ${CERT_DIR}
# 2. 从 Harbor 服务器提取证书 (假设已配置 SSH 免密或直接在本机)
# 这里演示从本地运行的 harbor-nginx 容器直接拷贝,避免网页下载步骤
docker cp harbor-nginx:/etc/nginx/cert/server.crt ${CERT_DIR}/ca.crt
# 3. 重启 Docker 使证书生效
systemctl restart docker
# 4. 验证信任
docker login https://${HARBOR_DOMAIN}
注:若未执行此步,客户端将报 x509: certificate signed by unknown authority 错误。
2. 项目空间的自动化创建(替代网页点击)
Harbor 的核心概念是“项目(Project)”,用于隔离镜像资源。手动在网页点击“新建项目”无法适应微服务快速迭代的节奏,也无法通过代码管理权限变更。
API 自动化创建方案:
利用 Harbor API 替代鼠标点击,可实现批量创建、权限预设及与 CI/CD 流水线的联动。
#!/bin/bash
# create-harbor-project.sh
HARBOR_URL="https://harbor.corp.local"
USER="admin"
PASSWORD="${HARBOR_ADMIN_PWD}" # 从环境变量获取,严禁硬编码
PROJECT_NAME="oldboyedu-lb"
echo "正在通过 API 创建项目:${PROJECT_NAME} ..."
# 调用 Harbor v2.0+ API 创建私有项目
curl -k -X POST "${HARBOR_URL}/api/v2.0/projects" \
-u "${USER}:${PASSWORD}" \
-H "Content-Type: application/json" \
-d "{
\"project_name\": \"${PROJECT_NAME}\",
\"public\": false,
\"metadata\": {
\"auto_scan\": true,
\"enable_content_trust\": false
},
\"storage_limit\": -1,
\"registry_id\": null
}"
if [ $? -eq 0 ]; then
echo "项目 ${PROJECT_NAME} 创建成功,立即可用。"
# 可选:在此处继续调用 API 创建 Robot Account 供 CI 使用
else
echo "项目创建失败,请检查是否已存在或认证信息。"
fi
通过这种方式,我们将原本需要在可视化窗口进行的“下载文件”、“点击创建”等操作,转化为可版本控制的脚本,确保了环境的一致性。
五、CI/CD 集成与安全推送
在流水线中集成镜像推送时,安全性与脚本的健壮性至关重要。许多初级脚本习惯将密码明文写在命令中,或通过 docker login -p 直接传递密码,这会在 history 记录或进程列表中留下安全隐患。
1. 安全的推送脚本
最佳实践是利用 --password-stdin 参数,通过管道传递密码,避免密码出现在命令行参数中。同时,脚本应具备严格的错误退出机制(set -e),防止构建失败后仍执行推送。
#!/bin/bash
set -e
REGISTRY_URL="harbor.corp.local"
PROJECT="oldboyedu-lb"
IMAGE_NAME="homework"
# 优先使用 CI 环境变量作为版本号, fallback 到 latest
VERSION="${BUILD_VERSION:-latest}"
echo "正在登录镜像仓库..."
# 安全登录:密码通过 stdin 传入,不留痕迹
echo "${HARBOR_PASSWORD}" | docker login "$REGISTRY_URL" -u "${HARBOR_USERNAME}" --password-stdin
echo "正在构建镜像..."
docker build \
--build-arg data="web-${BUILD_NUMBER}" \
-f web.dockerfile \
-t "$REGISTRY_URL/$PROJECT/$IMAGE_NAME:$VERSION" \
.
echo "正在推送镜像..."
docker push "$REGISTRY_URL/$PROJECT/$IMAGE_NAME:$VERSION"
# 清理登录态
docker logout "$REGISTRY_URL"
echo "发布成功:$REGISTRY_URL/$PROJECT/$IMAGE_NAME:$VERSION"
2. Docker Compose 构建联动
在本地或测试环境,可利用 Docker Compose 进行构建验证。注意,生产环境的 push 动作应剥离到 CI 流水线的独立步骤中,Compose 文件主要负责定义构建上下文与镜像标签。
# docker-compose.ci.yml
services:
app:
build:
context: .
dockerfile: web.dockerfile
args:
data: ${DEPLOY_ENV}
image: harbor.corp.local/oldboyedu-lb/homework:${CI_COMMIT_SHA}
# 此处仅定义镜像元数据,实际推送由外部脚本控制
六、常见故障排查与避坑指南
在日常运维中,以下几类问题最为高频,需建立标准化的排查思路。
1. HTTP/HTTPS 协议冲突
当客户端配置了 HTTPS 而服务端仅开启 HTTP 时,会报错 http: server gave HTTP response to HTTPS client。
- 解决:若是内网测试环境,需在客户端
/etc/docker/daemon.json中添加"insecure-registries": ["ip:port"]并重启 Docker 守护进程。但在生产环境,正确的做法是服务端配置合法的 SSL 证书,强制启用 HTTPS。
2. 镜像删除后空间未释放
这是 Registry 最常见的“假性”故障。表现为 API 返回删除成功,但 df -h 显示磁盘占用未变。
- 原因:元数据缓存未刷新。
- 解决:执行
registry garbage-collect命令清理孤儿层,随后对容器进行滚动重启以刷新内存状态。切勿直接暴力重启导致服务不可用。
3. 权限拒绝 (Unauthorized)
推送时报 unauthorized to access repository。
- 排查:首先确认
docker login是否成功;其次检查 Harbor 项目中该用户是否被赋予了Push权限(角色是否为 Developer 或 Maintainer);最后确认项目名称是否与 URL 路径完全匹配(Harbor 对项目名大小写敏感)。
4. 启动失败与端口冲突
Harbor 启动卡在 Starting Harbor ...。
- 排查:90% 的情况是 80/443/4443 端口被宿主机其他服务(如 Nginx)占用。使用
ss -ntl检查端口,调整 Nginx 监听端口或修改 Harbor 配置映射。务必查看docker-compose logs -f定位具体报错组件。
生产环境红线:
- 严禁在脚本或配置文件中硬编码明文密码。
- 严禁在生产集群使用
insecure-registries绕过证书验证。 - 严禁将 Harbor 数据目录放在根分区,必须挂载独立存储以防磁盘写满导致节点宕机。
- 严禁忽略定期 GC 任务,否则软删除的镜像层会迅速耗尽存储空间。
七、高可用架构与监控体系
对于核心业务,单节点 Harbor 无法满足 SLA 要求,需构建高可用架构并纳入统一监控。
1. 高可用 (HA) 设计要点
经典的 HA 方案采用 Keepalived + Nginx 做负载均衡后端挂载多实例 Harbor。但需注意以下深层依赖:
- 状态外置:Harbor 内部的 PostgreSQL 和 Redis 默认是有状态的。生产环境强烈建议外置数据库与缓存集群(如使用 Patroni 管理的 PG 集群、Redis Sentinel),避免容器重启导致元数据丢失。
- 存储共享:所有 Harbor 实例必须挂载同一份后端存储(NFS/S3),确保任意节点拉取的镜像数据一致。
- 会话保持:若未外置 Redis 存储 Session,Nginx 需配置 IP Hash 策略实现会话粘滞,防止用户登录后跳转节点导致鉴权失败。
2. 监控指标体系
利用 Harbor 暴露的 Prometheus 接口,重点关注以下指标并配置告警:
- 存储容量:
sum(harbor_project_quota_usage_bytes),当使用率超过 85% 时触发告警,提前扩容。 - 服务健康:
up{job="harbor-core"}和up{job="harbor-registry"},任何组件宕机立即通知。 - 推送失败率:
rate(harbor_http_request_total{status=~"5.."}),监控连续的错误请求,及时发现服务异常。
八、总结与运维口诀
为了便于团队记忆与执行,将上述经验浓缩为以下操作准则:
运维口诀:
测试可用 Registry,生产必上 Harbor 站。
HTTPS 证书要配全,存储挂载独立盘。
密码莫写脚本里,Insecure 靠边站。
项目创建调 API,莫在网页点点看。
证书分发自动化,客户端信保平安。
登录善用 Stdin 传,构建标签版本换。
磁盘空间设告警,GC 定期跑一遍。
落地检查清单:
- 确认 Harbor 版本为官方推荐的稳定版。
- SSL 证书已配置且被客户端信任(通过脚本自动分发)。
- 数据卷已挂载至大容量独立存储。
- 项目空间通过 API 自动创建,无手工遗留。
- CI/CD 密码已通过 Secret 管理,无明文泄露。
- 制定了镜像保留策略(如仅保留最近 10 个版本)。
- 监控大盘已上线,磁盘与宕机告警已测试生效。
