通常我们在docker拉取的镜像都是在docker hub或quay.io等公有仓库获取,那么在实际工作中,每个公司如果使用到docker,那么肯定是要搭建自己的私有仓库。那么接下来就通过docker提供的registry镜像来搭建我们自己的私有仓库。
docker-registry 是官方提供的轻量级私有 Docker 镜像仓库,并通过 docker-registry-ui 提供 Web 界面,适用于个人或小型团队。
一、准备工作
1. 环境要求
- 操作系统:Linux(CentOS 7+/Ubuntu 18.04+/Rocky Linux 9+)
- CPU:2 核以上
- 内存:2GB 以上
- 存储:50GB 以上(根据镜像大小调整)
- Docker 版本:24.0.0+
- Docker Compose 版本:2.0+
- 本次使用VirtualBox 进行实验
| 角色 | 主机名 | IP | 操作系统 |
|---|---|---|---|
| docker registry | docker-registry | 192.168.56.250 | Ubuntu 18.04 |
| docker client | docker-client | 192.168.56.200 | CentOS 7.6 |
2. 安装 Docker 和 Docker Compose
2.1 安装 Docker
curl -fsSL https://get.docker.com | bash
systemctl enable docker --now
2.2 验证 Docker 版本
docker version
确保 Client & Server 版本均为 24.0.0 及以上。
2.3 安装 Docker Compose
mkdir -p /usr/local/lib/docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker-compose version
确保 Docker-compose 版本均为 2.0 及以上
3.拉取镜像
3.1 拉取registry镜像
这里默认拉取最新版本,不指定版本就默认拉取latest版本
docker pull registry
Using default tag: latest
latest: Pulling from library/registry
ddad3d7c1e96: Pull complete
6eda6749503f: Pull complete
363ab70c2143: Pull complete
5b94580856e6: Pull complete
12008541203a: Pull complete
Digest: sha256:121baf25069a56749f249819e36b386d655ba67116d9c1c6c8594061852de4da
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
3.2 拉取docker-registry-ui镜像
这里选择joxit/docker-registry-ui
docker pull joxit/docker-registry-ui:latest
latest: Pulling from joxit/docker-registry-ui
1074353eec0d: Pull complete
25f453064fd3: Pull complete
567f84da6fbd: Pull complete
da7c973d8b92: Pull complete
33f95a0f3229: Pull complete
085c5e5aaa8e: Pull complete
0abf9e567266: Pull complete
4f4fb700ef54: Pull complete
86542d87c26f: Pull complete
f00587c9d3c4: Pull complete
4938dcb4f5b5: Pull complete
7c58ebf04d2e: Pull complete
eee8323a8c5c: Pull complete
Digest: sha256:9cc16677bec05cf5c20cfdb82edd79a0622880612c7a71273f4a30a0ae3cd17c
Status: Downloaded newer image for joxit/docker-registry-ui:latest
joxit/docker-registry-ui:latest
配置选项
以下环境变量用于自定义界面行为(当 SINGLE_REGISTRY=true 时,部分选项仅对单仓库模式生效):
| 变量名 | 说明 | 默认值 | 版本要求 |
|---|---|---|---|
REGISTRY_URL | 仓库地址(需配置 CORS),如 [***] | 从界面域名推导 | - |
REGISTRY_TITLE | 界面标题 | 从 REGISTRY_URL 推导 | 0.3.4+ |
PULL_URL | docker pull 命令中显示的仓库地址 | 同 REGISTRY_URL | 1.1.0+ |
DELETE_IMAGES | 是否允许删除镜像(true/false) | false | - |
SHOW_CONTENT_DIGEST | 是否显示标签的 content digest(sha256) | false | 1.4.9+ |
CATALOG_ELEMENTS_LIMIT | 目录页最多显示的镜像数量 | 1000 | 1.4.9+ |
SINGLE_REGISTRY | 是否禁用仓库切换菜单(true/false) | false | 2.0.0+ |
NGINX_PROXY_PASS_URL | 代理仓库地址(如 [***] CORS 问题 | - | 2.0.0+ |
NGINX_LISTEN_PORT | 监听端口(root 用户默认 80,非 root 用户默认 8080) | 80/8080 | 2.2.0+ |
DEFAULT_REGISTRIES | 初始仓库列表(逗号分隔,SINGLE_REGISTRY=false 时生效) | - | 2.1.0+ |
READ_ONLY_REGISTRIES | 是否禁止添加/删除仓库(SINGLE_REGISTRY=false 时生效) | false | 2.1.0+ |
THEME | 主题(light/dark/auto) | auto | 2.4.0+ |
TAGLIST_ORDER | 标签排序规则(如 alpha-asc;num-desc) | alpha-asc;num-desc | 2.5.0+ |
PULL_URL | 镜像上传下载地址 | - | 2.5.0+ |
主题自定义(2.4.0+)
通过 THEME_* 环境变量自定义主题颜色,支持以下参数(示例值为默认的亮色/暗色主题配置):
| 变量名 | 亮色主题值 | 暗色主题值 |
|---|---|---|
THEME_PRIMARY_TEXT | #25313b | #98a8bd |
THEME_BACKGROUND | #ffffff | #22272e |
THEME_HEADER_BACKGROUND | #25313b | #333a45 |
确认镜像已下载到本地
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 99b916d8206b 4 weeks ago 57.7MB
joxit/docker-registry-ui latest fc0c719c7b3f 5 weeks ago 22.1MB
二、开始部署
1. docker-compose文件的准备
services:
registry:
image: registry:latest
container_name: registry
restart: always
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[https://192.168.56.250]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]'
REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]'
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
REGISTRY_HTTP_TLS_CERTIFICATE: '/certs/server.crt'
REGISTRY_HTTP_TLS_KEY: '/certs/server.key'
ports:
- '443:5000'
volumes:
- $PWD/data:/var/lib/registry
- $PWD/certs:/certs
networks:
- mynet
registry-ui:
image: joxit/docker-registry-ui:latest
environment:
- REGISTRY_TITLE="Gssyf Private Registry"
- DELETE_IMAGES=true
- SINGLE_REGISTRY=true
- CATALOG_ELEMENTS_LIMIT=50 #限制目录页面元素数量
- TAGLIST_PAGE_SIZE=100 #设置每页显示标签数量
- SHOW_CONTENT_DIGEST=true
- NGINX_PROXY_PASS_URL=https://registry:5000
- PULL_URL=https://registry.gssyf.com
- SHOW_CATALOG_NB_TAGS=true #在大仓库中禁用标签数量显示
- CATALOG_MIN_BRANCHES=1
- CATALOG_MAX_BRANCHES=1
- REGISTRY_SECURED=false
container_name: registry-ui
restart: always
ports:
- '80:80'
networks:
- mynet
depends_on:
- registry
networks:
mynet:
driver: bridge
external: true
2. 创建容器网络(可选)
docker network create --driver=bridge --gateway=192.168.137.1 --subnet=192.168.137.0/16 mynet
3. https自签证书创建(可选)
创建文件
# 创建证书生成脚本文件
touch gen-tls.sh
# 为证书生成脚本文件授权
chmod +x gen-tls.sh
# 编写生成脚本
vim gen-tls.sh
内容如下:
#!/bin/bash
# 文件名: gen-tls.sh
# 描述:根据用户输入的信息自动生成自签证书
# 执行方法: ./gen-tls.sh
echo "🔐 开始生成 CA 根证书和服务器证书..."
# ==========================================
# 第一步:收集用户输入
# ==========================================
echo
read -p "请输入服务器主域名 (例如 a.com): " COMMON_NAME
read -p "请输入额外的 DNS 名称 (可选,支持空格/逗号/分号分隔,例如 www.a.com): " EXTRA_DNS
read -p "请输入服务器 IP 地址 (可选,支持空格/逗号/分号分隔,例如127.0.0.1): " IP_ADDRESSES
if [ -z "$COMMON_NAME" ]; then
echo "错误: 域名不能为空。"
exit 1
fi
# ==========================================
# 🔧 处理旧证书 (备份或删除)
# ==========================================
WORK_DIR="./certs"
if [ -d "$WORK_DIR" ]; then
echo "⚠️ 发现旧证书目录: $WORK_DIR"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="./certs_backup_${TIMESTAMP}"
echo "❓ 请选择如何处理旧证书:"
echo " 1) 备份旧证书 (推荐,旧证书将移动到 $BACKUP_DIR)"
echo " 2) 删除旧证书"
echo " 3) 退出"
read -p "请输入选项 (1-3): " CHOICE
case $CHOICE in
1)
echo "📦 正在备份旧证书..."
mv "$WORK_DIR" "$BACKUP_DIR"
;;
2)
echo "🗑️ 正在删除旧证书..."
rm -rf "$WORK_DIR"
;;
3)
echo "👋 退出。"
exit 0
;;
*)
exit 1
;;
esac
fi
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"
# ==========================================
# 第二步:生成 CA 证书
# ==========================================
echo "1/4 生成 CA 私钥..."
openssl genrsa -out ca.key 4096
echo "2/4 生成 CA 根证书..."
# 解决 .rnd 错误
openssl rand -writerand $HOME/.rnd 2>/dev/null || true
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
-subj "/CN=My Private CA/O=a/C=CN"
# ==========================================
# 第三步:生成服务器证书配置 (兼容写法)
# ==========================================
echo "3/4 生成服务器私钥..."
openssl genrsa -out server.key 4096
CONF_FILE="server_cert.conf"
cat > "$CONF_FILE" << EOF
[req]
default_bits = 4096
default_md = sha256
distinguished_name = req_distinguished_name
x509_extensions = v3_req
req_extensions = v3_req
[req_distinguished_name]
C = CN
ST = Gansu
L = Lanzhou
O = a
OU = admin
CN = $COMMON_NAME
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $COMMON_NAME
EOF
# 处理域名
counter=2
echo "$EXTRA_DNS" | tr ' ,;' '\n' | while read dns; do
if [ -n "$dns" ]; then
echo "DNS.$counter = $dns" >> "$CONF_FILE"
fi
counter=$((counter + 1))
done
# 处理 IP
counter=1
echo "$IP_ADDRESSES" | tr ' ,;' '\n' | while read ip; do
if [ -n "$ip" ]; then
echo "IP.$counter = $ip" >> "$CONF_FILE"
fi
counter=$((counter + 1))
done
# ==========================================
# 第四步:签发证书
# ==========================================
echo "4/4 生成服务器证书..."
# 注意: 由于 while 循环在子 Shell 中运行,上面的 counter 变量在循环外不可见。
# 我们不需要在 openssl 命令中使用 counter,只需要生成 CSR 和签发即可。
openssl req -new -key server.key -out server.csr -config "$CONF_FILE" -subj "/CN=$COMMON_NAME"
# 服务器证书CA签发
openssl x509 -req -in server.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial \
-out server.crt -days 365 -sha256 -extfile "$CONF_FILE" -extensions v3_req
echo "🎉 完成!证书位于 $(pwd)"
# ==========================================
# 第五步:整理输出
# ==========================================
echo
echo "🎉 证书生成成功!"
echo "📁 证书目录: $(pwd)"
echo "📄 文件列表: ca.key, ca.crt, server.key, server.crt, server.csr, server_cert.conf"
echo "📄 生成的文件:"
echo " - ca.crt: CA 根证书 (需安装到客户端信任库)"
echo " - server.crt: 服务器证书 (公钥)"
echo " - server.key: 服务器私钥 (保密!)"
echo " - server.csr: 证书签名请求 (可选,已签发后可删除)"
echo " - server_cert.conf: 证书配置文件 (可选,可用于查看 SAN 配置)"
echo
echo "💡 下一步操作建议:"
echo " 1. 将 'server.crt' 和 'server.key' 部署到您的服务器应用中。"
echo " 2. 将 'ca.crt' 安装到所有客户端的系统信任库中,以避免证书警告。"
echo " 例如在 Ubuntu/Debian 上:"
echo " sudo cp ca.crt /usr/local/share/ca-certificates/a.crt"
echo " sudo update-ca-certificates"
echo " # 对于基于 RHEL/CentOS/Fedora 的系统"
echo " sudo cp ca.crt /etc/pki/ca-trust/source/anchors/a.crt"
echo " update-ca-trust extract"
echo " 3. 验证"
echo " openssl verify registry.crt"
echo " # 输出应为:registry.crt: OK."
4.启动私有镜像仓库
# 启动
docker-compose -p registry -f docker-compose.yml up -d
# 停止
docker-compose -p registry -f docker-compose.yml down
扩展内容
一键启动/停止脚本 start.sh
#!/bin/bash
# 文件名:start.sh
# 启动脚本
CONTAINER_NAME=registry
PWD=`pwd`
NUM=`docker-compose ls | grep $CONTAINER_NAME | egrep -v grep | wc -l`
if [ $NUM -eq 0 ];then
docker-compose -p $CONTAINER_NAME -f $PWD/docker-compose.yml up -d
sleep 1
echo -e "\033[32m $CONTAINER_NAME 服务启动中。。。 \033[0m"
else
echo -e "\033[33m $CONTAINER_NAME 服务已启动! \033[0m"
exit 0
fi
stop.sh
#!/bin/bash
# 文件名:stop.sh
# 停止脚本
CONTAINER_NAME=registry
PWD=`pwd`
NUM=`docker-compose ls | grep $CONTAINER_NAME | egrep -v grep | wc -l`
if [ $NUM -ne 0 ];then
docker-compose -p $CONTAINER_NAME -f $PWD/docker-compose.yml down
echo -e "\033[33m $CONTAINER_NAME 服务停止成功! \033[0m"
exit 0
else
echo -e "\033[31m $CONTAINER_NAME 服务已停止! \033[0m"
exit 0
fi
三、验证与镜像上传下载
1. 访问web管理页面
通过浏览器访问https://192.168.56.250
2. 镜像下载
登录docker-client服务
[root@docker-clinet app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@docker-clinet app]#
[root@docker-clinet app]# docker pull registry.gssyf.com/nginx:1.28.0
1.28.0: Pulling from nginx
c25bee1cbcbb: Pull complete
049fab013ee9: Pull complete
7f0da823c3c6: Pull complete
e92ea34e4fd2: Pull complete
d9cafe4fb0f5: Pull complete
276a6b624459: Pull complete
9aec91fa40af: Pull complete
Digest: sha256:bea1007cd1e666e708bed8057d84af4af3fe39a0097f3992207d8f67c6c5f795
Status: Downloaded newer image for registry.gssyf.com/nginx:1.28.0
registry.gssyf.com/nginx:1.28.0
[root@docker-clinet app]#
镜像下载成功
3. 镜像上传
[root@docker-clinet app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.gssyf.com/nginx 1.28.0 34e04bb6b4bb 2 months ago 192MB
busybox latest 08ef35a1c3f0 17 months ago 4.43MB
[root@docker-clinet app]#
# 修改镜像标签
[root@docker-clinet app]# docker tag busybox:latest registry.gssyf.com/busybox:latest
[root@docker-clinet app]# docker push registry.gssyf.com/busybox:latest
The push refers to repository [registry.gssyf.com/busybox]
e14542cc0629: Pushed
latest: digest: sha256:be49435f6288f9c5cce0357c2006cc266cb5c450dbd6dc8e3a3baec10c46b065 size: 527
[root@docker-clinet app]#
检查registry web页面是否上传
大功告成。