Docker Registry+WEB UI构建专属镜像仓库

0 阅读8分钟

通常我们在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 registrydocker-registry192.168.56.250Ubuntu 18.04
docker clientdocker-client192.168.56.200CentOS 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_URLdocker pull 命令中显示的仓库地址同 REGISTRY_URL1.1.0+
DELETE_IMAGES是否允许删除镜像(true/falsefalse-
SHOW_CONTENT_DIGEST是否显示标签的 content digest(sha256)false1.4.9+
CATALOG_ELEMENTS_LIMIT目录页最多显示的镜像数量10001.4.9+
SINGLE_REGISTRY是否禁用仓库切换菜单(true/falsefalse2.0.0+
NGINX_PROXY_PASS_URL代理仓库地址(如 [***] CORS 问题-2.0.0+
NGINX_LISTEN_PORT监听端口(root 用户默认 80,非 root 用户默认 8080)80/80802.2.0+
DEFAULT_REGISTRIES初始仓库列表(逗号分隔,SINGLE_REGISTRY=false 时生效)-2.1.0+
READ_ONLY_REGISTRIES是否禁止添加/删除仓库(SINGLE_REGISTRY=false 时生效)false2.1.0+
THEME主题(light/dark/autoauto2.4.0+
TAGLIST_ORDER标签排序规则(如 alpha-asc;num-descalpha-asc;num-desc2.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

image.png

2. 镜像下载

登录docker-client服务

image.png

[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页面是否上传

image.png

大功告成。