Docker——2.Docker Registry 私有仓库

Docker Registry 私有仓库

Registry的介绍

Docker Registry 是什么?

Docker Registry(镜像仓库)是一个集中存储、分发 Docker 镜像的服务 。你可以把它理解为「镜像的 Git 仓库 + 网盘」:开发者把构建好的镜像推上去(push),其他服务器再从上面拉下来(pull),这样就能保证开发、测试、生产环境用的镜像是完全一致的,避免出现「我这跑得好好的,到你那就报错」的经典玄学问题。

核心组成

  1. 镜像(Image):只读模板,包含运行应用所需的代码、运行时、库、依赖、配置。
  2. 仓库(Repository) :一类镜像的集合,通常用名字区分,比如 centosnginx
  3. 标签(Tag) :镜像的版本号,比如 centos:7.7.1908nginx:1.27-alpine
  4. Registry:仓库的集合,托管多个仓库。Docker Hub 是最知名的公共 Registry,Harbor 是企业级私有 Registry,本教程用官方开源的轻量级 Registry 2.8.3。

为什么不能只用 Docker Hub 公共仓库?

场景 公共仓库问题 私有仓库优势
内网环境 国内访问 Docker Hub 经常超时、限速 内网 5000 端口,带宽稳定可控
安全合规 业务镜像放在第三方平台不合规 数据完全自主,物理可控
定制镜像 公司内部基础镜像需要统一管理 统一构建、统一版本、统一扫描
节省带宽 多台服务器重复拉取同一镜像 一次推送,多次复用,且支持 P2P 分发
故障隔离 公共仓库宕机业务停摆 完全自主,不受外部服务影响

Registry 在企业中的典型定位

复制代码
开发者本地构建 → 推送到 Registry → 测试环境拉取 → 
自动化测试 → 推送到生产 Registry → 生产服务器拉取部署

Registry 在 CI/CD 流水线里处于承上启下 的核心位置:上游接构建系统(Jenkins / GitLab CI / GitHub Actions),下游接 Kubernetes、Docker Swarm、传统 Docker 主机。它是镜像流转的枢纽

5 个核心概念

1. 镜像分层与复用

Docker 镜像采用联合文件系统 (UnionFS),由多个只读层叠加组成。docker pull 时会按层下载,已存在的层直接复用,这也是为什么 docker push 速度很快 ------ 只传差异层。

2. 标签(Tag) vs 摘要(Digest)
  • Tag :人类可读的版本字符串,比如 v1latest1.27-alpine可变,官方可以重新打 tag 指向不同内容。
  • Digest :镜像内容的 SHA256 哈希,比如 sha256:8f2c78ca3141051eef77fb083066222abf20330a2345c970a5a61427aeb2dc7b不可变,生产环境追求绝对一致性时用 digest 拉取。
3. Registry 镜像存储结构
复制代码
/share/dockerimg/
└── docker/registry/v2/
    ├── repositories/   # 仓库元数据(每个镜像一个目录)
    │   └── centos/
    │       ├── _layers/    # 层引用
    │       ├── _manifests/ # 清单(记录 tag 指向哪个 digest)
    │       └── _uploads/   # 上传中的临时文件
    └── blobs/         # 实际镜像层数据(内容寻址存储)
        └── sha256/

理解这个结构,对后面的备份、清理、排障都很重要。

4. 为什么必须配置 insecure-registries

Docker 客户端默认强制 HTTPS ,因为公网环境明文传输会被中间人攻击篡改镜像。但内网私有仓库为了简化部署通常不开 TLS,这就需要在 daemon.json 里显式声明「这台 Registry 我信得过,允许 HTTP」。

5. --restart 策略怎么选?
策略 行为 适用场景
no 容器退出后不重启 一次性任务、调试
always 无论什么退出码都无限重启 核心服务(但要配合健康检查)
on-failure:N 仅非 0 退出码时重启,最多 N 次 防止崩溃循环吃资源
unless-stopped 容器被手动停止前一直重启 守护进程类服务

文档选 on-failure:3 是为了在程序异常时自动恢复 ,又不会因为端口冲突、磁盘满等外部原因导致无限重启死循环。


生产环境的更高阶选择

本文档的 Registry 属于轻量级自建 ,适合中小团队和内网环境。如果场景是多团队、大规模、合规要求高,建议评估以下方案:

方案 特点 适用规模
Harbor CNCF 毕业项目,集成 UI、RBAC、漏洞扫描、镜像复制、Helm Chart 仓库 中大型企业首选
Dragonfly 字节跳动开源的 P2P 镜像分发,十万级并发拉取 巨型集群(K8s 节点 1000+)
Nexus / Artifactory 通用制品库管理,支持 Docker、Maven、npm、PyPI 等 已有同类基础设施
云厂商 ACR / ECR / GCR 全托管,免运维,按量付费 上云企业、追求零运维

对学习者和小团队来说,从官方 Registry 入手是最扎实的第一步 ------ 会真正理解镜像仓库的底层原理,再去用 Harbor 会事半功倍。

详细步骤

步骤 1:拉取固定稳定版 Registry 镜像

复制代码
docker pull registry:2.8.3

说明

  1. 为什么不写 docker pull registry:不带版本等价于拉 registry:latest,官方更新后标签自动切换;扩容、重装环境时版本不一致,跨大版本存储结构可能损坏。

  2. registry:2.8.3 是 2.x 长期稳定维护分支,适配 Docker 20.10,无已知存储 bug,兼容性最好。

  3. 由于你已经配置了轩辕镜像加速器(xuanyuan.run / xuanyuan.dev),实际拉取会命中国内缓存,速度比直连 Docker Hub 快几倍到几十倍。

  4. 验证拉取完成:

    docker images | grep registry

预期输出:

复制代码
registry       2.8.3           26b2eb03618e   2 years ago     25.4MB

步骤 2:创建持久化存储目录 + 权限修复

复制代码
# 创建多级存储目录,不存在则自动生成
mkdir -pv /share/dockerimg

# 生产标准权限配置(推荐长期使用)
chown 1000:1000 /share/dockerimg

说明

  1. Registry 容器内部运行进程 UID = 1000,容器内用户不是 root。

  2. 宿主机新建目录默认属主 root,容器进程无写入权限,执行 docker push 会报 permission denied

  3. chown 1000:1000:把目录归属修改为容器内部运行用户,完美解决权限写入报错。

  4. 临时测试可以用 chmod 777 /share/dockerimg,但生产环境禁止,权限太大存在安全风险。

  5. 验证目录权限:

    ls -ld /share/dockerimg

预期输出:

复制代码
drwxr-xr-x 2 1000 1000 6 7月 21 10:00 /share/dockerimg

步骤 3:防火墙永久放行 5000 端口

复制代码
# 永久放行 TCP 5000 端口,重启防火墙/服务器不失效
firewall-cmd --add-port=5000/tcp --permanent

# 重载防火墙规则,配置立即生效
firewall-cmd --reload

# 校验端口是否成功开放
firewall-cmd --list-ports

说明

  1. CentOS 7 默认 firewalld 防火墙拦截外部入站端口,本机虽然能 curl 访问,但其他 192.168.2.x 网段服务器会连接超时。
  2. --permanent:写入永久规则,不加此参数仅临时生效,服务器重启后端口拦截恢复。
  3. firewall-cmd --reload:必须执行,否则新增端口规则不生效。
  4. 校验成功标准:输出包含 5000/tcp

步骤 4:后台启动 Registry 容器(优化重启策略)

javascript 复制代码
docker run -d \
-p 5000:5000 \
--restart=always \
--restart=on-failure:3 \
-v /share/dockerimg:/var/lib/registry \
registry:2.8.3

说明

  1. -d:detach 后台运行,不占用当前终端。
  2. -p 5000:5000:宿主机 5000 端口映射容器内部 5000 端口,外部服务器通过 192.168.2.123:5000 访问仓库。
  3. --restart=on-failure:3:优化原文档的 --restart=always
  • always:容器无论什么原因停止都会无限重启,磁盘满、端口占用时会死循环。
  • on-failure:3:仅程序异常崩溃时重启,最多重试 3 次,避免资源死循环占用。
  1. -v /share/dockerimg:/var/lib/registry:数据卷挂载
  • 宿主机 /share/dockerimg:永久存储镜像。
  • 容器内 /var/lib/registry:Registry 默认镜像存储路径。
  • 容器删除、重建,宿主机目录镜像文件保留,实现持久化。
  1. registry:2.8.3:指定固定稳定镜像版本。

验证容器启动成功:

复制代码
docker ps | grep registry

正常输出会显示容器 ID、镜像、5000 端口映射:

复制代码
380ff2e59d41   registry:2.8.3      "/entrypoint.sh /etc..."   49 seconds ago   Up 48 seconds   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   naughty_williams

步骤 5:API 访问验证仓库服务是否正常运行

复制代码
curl http://192.168.2.123:5000/v2/_catalog

结果解读

  1. 全新空仓库,返回 JSON:{"repositories":[]} → 服务完全正常。
  2. 报错连接拒绝:防火墙未放行端口 / 容器未启动。
  3. 证书报错:后续 daemon.json 未配置信任 HTTP 仓库,不影响本机 curl 测试。

预期输出:

json 复制代码
{"repositories":[]}

步骤 6:配置 Docker 守护进程信任 HTTP 私有仓库

Docker 默认拒绝无 SSL 加密的 HTTP 仓库,必须在 daemon.json 加入仓库信任列表,同时合并轩辕镜像加速器配置,避免多文件覆盖失效。

6.1 编辑 Docker 配置文件
复制代码
/etc/docker/daemon.json

设置以下标准 JSON 配置(适配本机 IP + 轩辕双镜像源,无多余逗号,语法合规):

json 复制代码
cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": [
    "https://672tq9h17eavbw.xuanyuan.run",
    "https://672tq9h17eavbw.xuanyuan.dev"
  ],
  "insecure-registries": [
    "672tq9h17eavbw.xuanyuan.run",
    "672tq9h17eavbw.xuanyuan.dev",
    "192.168.2.123:5000"
  ]
}
EOF

配置字段详解

  1. registry-mirrors:轩辕镜像加速器地址,加速拉取 Docker Hub 公共镜像(centos / nginx / redis 等)。
  2. insecure-registries:信任非 HTTPS 仓库地址,告诉 Docker 允许明文 HTTP 方式推拉镜像。这里同时加入了两个轩辕镜像域名(因为它们走 HTTP 协议)和内网 Registry 地址 192.168.2.123:5000
  3. JSON 强制规范:末尾键值对不能加逗号,否则 Docker 启动失败。
  4. 不要复制原文档里带 @ 符号和空格的写法(如 " @https://xxx/ "),@ 和多余空格会导致 JSON 解析报错,Docker 启动失败。
6.2 配置生效完整流程(两步缺一不可)
复制代码
# 重载 systemd 系统守护进程,读取新 daemon.json 配置
systemctl daemon-reload

# 重启 Docker 服务,加载新配置
systemctl restart docker
  1. daemon-reload:systemd 缓存 Docker 配置,修改文件后必须重载,否则 Docker 读取旧配置。
  2. restart docker:让 Docker 引擎重新加载信任仓库、加速器配置。
  3. 验证 Docker 运行状态:

显示 active (running) 代表无报错启动成功。

步骤 7:本地镜像上传至本机 Registry 仓库

7 拉取测试的镜像
复制代码
docker pull centos:7.7.1908
7.1 镜像打标签(强制格式,不打标无法推送)
复制代码
docker tag centos:7.7.1908 192.168.2.123:5000/centos:v1
  • 192.168.2.123:5000:Registry 仓库地址。

  • centos:镜像名称。

  • v1:业务自定义版本号,可改为 v1.020260721 等。

    192.168.2.123:5000/centos v1 08d05d1d5859 6 years ago 204MB
    centos 7.7.1908 08d05d1d5859 6 years ago 204MB

7.2 推送镜像到私有仓库
复制代码
docker push 192.168.2.123:5000/centos:v1

查看宿主机持久化存储

复制代码
ls -lhrt /share/dockerimg/docker/registry/v2/repositories/

成功推送后,目录内会生成 centos 文件夹,代表镜像已持久化保存到宿主机,删除容器镜像不丢失。

步骤 8:局域网其他服务器拉取仓库镜像

编辑 daemon.json,添加相同信任配置:

json 复制代码
{
  "registry-mirrors": [
    "https://672tq9h17eavbw.xuanyuan.run",
    "https://672tq9h17eavbw.xuanyuan.dev"
  ],
  "insecure-registries": [
    "672tq9h17eavbw.xuanyuan.run",
    "672tq9h17eavbw.xuanyuan.dev",
    "192.168.2.123:5000"
  ]
}

编辑 daemon.json,添加相同信任配置:

重载并重启 Docker:

复制代码
systemctl daemon-reload
systemctl restart docker
8.2 目标机器拉取镜像
复制代码
docker pull 192.168.2.123:5000/centos:v1

常见报错说明

报错信息 原因 解决方法
x509 certificate / server gave HTTP response to HTTPS client 未配置 insecure-registries daemon.json 加入 192.168.2.123:5000,重启 Docker
connection refused 仓库主机防火墙未放行 5000 端口 在 Registry 服务器执行步骤 3 的 firewall-cmd 命令
permission denied 仓库宿主机存储目录权限未修改 1000:1000 在 Registry 服务器执行步骤 2 的 chown 1000:1000

步骤 9:配置 Basic Auth 账号密码认证(最后一步)

当前部署完成后,内网任意一台服务器无需账号密码,可随意 push 覆盖、delete 删除业务镜像,无操作审计,故障无法追溯操作人。生产环境必须开启认证。

9.1 安装 htpasswd 工具生成账号密码
复制代码
yum install -y httpd-tools

# 创建认证目录
mkdir -pv /share/auth

# 创建用户 admin,密码 lengmoxi(-B 强制 bcrypt 加密)
htpasswd -Bbn admin lengmoxi > /share/auth/htpasswd

说明

  1. -B:使用 bcrypt 加密,比默认 MD5 安全得多。
  2. -b:允许在命令行直接传入密码,避免交互式输入(适合脚本场景)。
  3. > /share/auth/htpasswd:把账号密码写入文件,文件会被挂载到容器内供 Registry 读取。
9.2 带认证重启 Registry 容器

先停掉旧容器:

复制代码
# 查看旧容器 ID
docker ps | grep registry

# 停止旧容器(container-id 替换为实际值)
docker stop <container-id>

# 删除旧容器(数据卷 /share/dockerimg 保留,镜像不丢)
# 可以先不删除,如果出错就删除这个容器
docker rm <container-id>

启动新容器,挂载认证文件:

javascript 复制代码
docker run -d \
-p 5000:5000 \
--restart=on-failure:3 \
-v /share/dockerimg:/var/lib/registry \
-v /share/auth:/auth \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Login" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2.8.3

环境变量说明

  1. REGISTRY_AUTH=htpasswd:开启 htpasswd 认证模式。
  2. REGISTRY_AUTH_HTPASSWD_REALM="Registry Login":认证域提示,登录框会显示这个字符串。
  3. REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd:容器内认证文件路径,对应宿主机 /share/auth/htpasswd
9.3 客户端推拉前必须登录

每台需要使用 Registry 的服务器(192.168.2.123 自身 + 其他 192.168.2.x 机器)都要执行:

复制代码
docker login 192.168.2.123:5000
# Username: admin
# Password: lengmoxi

登录成功输出:

复制代码
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded

安全提醒

  1. 登录凭证默认明文保存在 /root/.docker/config.json,内网环境可接受;公网或敏感环境建议配置 credential helper 加密存储。
  2. 退出登录:docker logout 192.168.2.123:5000
  3. 忘记密码怎么办:在 Registry 服务器重新执行 htpasswd -Bbn admin lengmoxi > /share/auth/htpasswd,然后 docker restart <container-id> 即可。
9.4 验证认证生效

未登录直接 docker pull

复制代码
docker pull 192.168.2.123:5000/centos:v1

应该返回:

复制代码
Error response from daemon: no basic auth credentials

登录后再次拉取即可成功。


生产补充方案速查表

缺陷 风险 解决方案
无账号密码鉴权 匿名 push 覆盖、delete 删除 Basic Auth(步骤 9)
无自动镜像清理 磁盘满导致写入失败 编写定时清理脚本,删除 _manifests/tags 中无用 tag
无日志审计 故障无法追溯操作人 开启容器日志持久化 + 对接 ELK
无漏洞扫描 不安全镜像流入生产 替换 Harbor(自带 Clair 扫描)
磁盘故障 镜像全部丢失 见下方备份脚本

每日定时备份脚本
shellscript 复制代码
#!/bin/bash
# Registry 镜像存储目录备份脚本
BACKUP_PATH="/data/registry_backup"
SOURCE_PATH="/share/dockerimg"
DATE=$(date +%Y%m%d)

# 创建备份目录
mkdir -p ${BACKUP_PATH}

# 压缩打包镜像数据
tar zcvf ${BACKUP_PATH}/registry_back_${DATE}.tar.gz ${SOURCE_PATH}

# 自动删除 7 天前旧备份,释放磁盘
find ${BACKUP_PATH} -name "registry_back_*.tar.gz" -mtime +7 -delete

加入 crontab 每天凌晨 2 点执行:

复制代码
0 2 * * * /bin/bash /data/registry_backup.sh

恢复流程 :仓库故障时,停止 registry 容器,解压备份包覆盖 /share/dockerimg,重启容器即可恢复全部镜像。

本机专属排错速查表(适配 192.168.2.123 + Docker 20.10)
  1. push 镜像报权限不足:执行 chown 1000:1000 /share/dockerimg
  2. 其他机器访问仓库超时:防火墙放行 5000 端口。
  3. docker pull 报证书不信任:检查 daemon.json IP 是否为 192.168.2.123:5000,重载并重启 Docker。
  4. 容器反复重启:磁盘空间不足、端口 5000 被其他程序占用、目录权限错误。
  5. curl 访问正常但 docker push 失败:daemon.json 配置未生效,重新执行 daemon-reload && restart docker
  6. Login Succeeded 后仍 no basic auth credentials:重新 docker login,确认 htpasswd 文件中密码正确,重启 registry 容器。
相关推荐
便利店10242 小时前
TCP 为什么有时故意跑不快?拥塞控制与滑动窗口一次讲清
服务器·网络协议·tcp·拥塞控制
杨某不才3 小时前
如何能让Linux服务器对shell 终端 + sftp 文件传输长期保活
linux·运维·服务器
vance043 小时前
免费Cloudflare隧道隐藏公网IP
linux·tcp/ip·github
三言老师4 小时前
文本工具组合统计服务器日志数据
linux·运维·服务器
m0_743697594 小时前
DNS服务器
运维·服务器
mounter6254 小时前
认识 Tetragon:基于 eBPF 的安全监控与强制执行工具
linux·ebpf·cve·kernel
aixingkong9216 小时前
AI超节点Scale Up域总线各层优化设计
linux·服务器·网络
kidwjb6 小时前
Linux内核-文件系统-文件系统目录和文件操作
linux·内核·文件系统
yj_xqj6 小时前
Docker 基础应用与介绍
运维·docker·容器
_艾伦 耶格尔.6 小时前
进程控制
linux