引言:为什么需要私有镜像仓库?

想象一下,如果每次做饭都要跑到超市去买食材,那对于我这种懒人来说,那该有多麻烦!在软件开发的世界里,Docker镜像就像是我们的"食材",而公共镜像仓库(如Docker Hub)就是那个"超市"。但有时候我们要当大老板,需要自己的"私人超市"——这就是私有镜像仓库的由来。

私有镜像仓库不仅能提高镜像拉取速度(告别漫长的下载等待,等着等着就打呼噜了),还能保护你的专有镜像(毕竟,妈妈的秘方不能公开给所有人),同时还能确保镜像的可用性(不怕"超市"突然关门)。现在就来一步步搭建属于自己的Docker "私人超市" !

Docker镜像:现代软件开发的"食材"

在深入私有镜像仓库之前,让先了解一下Docker镜像到底是什么?如果把软件开发比作烹饪,那么Docker镜像就是预先准备好的食材包——里面包含了应用程序及其所有依赖。你不需要担心"这个酱料和那个香料是否兼容"(依赖冲突),也不用操心"这种蔬菜在我这个季节能不能买到"(环境差异)。

Docker Hub 作为全球最大的公共镜像仓库,就像一个24小时营业的国际超市,里面有各种各样的"食材包"。但是,频繁地从这个"超市"购物会面临几个问题:

  1. 网络延迟:从远程服务器下载大型镜像可能需要很长时间,特别是在网络条件不佳的情况下。

  2. 带宽成本:反复下载相同的镜像会消耗大量带宽,尤其是在团队环境中。

  3. 可用性风险:如果Docker Hub暂时不可用,你的部署流程可能会中断。

  4. 隐私问题:你可能不希望将包含专有代码的镜像上传到公共仓库。

这就是为什么我们需要建立自己的"私人超市"——私有镜像仓库。

第一步:准备环境

在开始之前,请确保你的 CentOS 系统已安装 Docker。如果还没有,可以通过以下命令安装:

# 更新软件包索引
sudo yum update -y

# 安装必要的软件包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加Docker仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装Docker CE
sudo yum install -y docker-ce docker-ce-cli containerd.io

# 启动Docker服务
sudo systemctl start docker

# 设置Docker开机自启
sudo systemctl enable docker

# 验证Docker是否安装成功
sudo docker run hello-world

如果看到"Hello from Docker!"的消息 ,Docker已成功安装!就像买到了建造超市的土地一样,我们已经准备好了基础设施。

安装Docker后,你可能会注意到系统添加了一个新的用户组 docker​。这个用户组允许非 root 用户运行 Docker 命令,而不需要每次都使用sudo​。如果你想避免总是输入sudo​,可以将当前用户添加到 docker ​组:

# 将当前用户添加到docker组
sudo usermod -aG docker $USER

# 应用新的组成员身份(需要重新登录或运行以下命令)
newgrp docker

# 测试无需sudo的Docker命令
docker run hello-world

第二步:启动私有镜像仓库

Docker官方提供了一个简单的Registry镜像,我们可以用它来快速搭建私有仓库。这个Registry是一个开源项目,专门用于存储和分发Docker镜像。

# 创建用于存储镜像的目录
sudo mkdir -p /opt/registry/data

# 启动Registry容器
sudo docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry \
  --restart=always \
  registry:2

这段命令做了什么?让我们逐一解析:

  • ​-d​:在后台运行容器(守护模式)

  • ​--name registry​:给容器起名为"registry",方便后续管理

  • ​-p 5000:5000​:将容器内的5000端口映射到主机的5000端口

  • ​-v /opt/registry/data:/var/lib/registry​:将主机上的/opt/registry/data​目录挂载到容器内的/var/lib/registry​目录,这样镜像数据就会持久化存储在主机上

  • ​--restart=always​:设置容器自动重启策略,确保服务器重启后Registry也会自动启动

  • ​registry:2​:使用的镜像名称和标签

这就像是用预制件快速搭建了一个超市的框架!现在,我们的私有镜像仓库已经在本地的5000端口上运行了。

你可以通过以下命令检查Registry容器是否正在运行:

# 查看运行中的容器
docker ps

# 应该能看到类似这样的输出
# CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                    NAMES
# 3a2e3e4f5g6h   registry:2   "/entrypoint.sh /etc…"   2 minutes ago   Up 2 minutes   0.0.0.0:5000->5000/tcp   registry

第三步:配置Docker客户端信任私有仓库

默认情况下,Docker客户端只信任HTTPS的仓库。但我们刚刚搭建的是HTTP仓库,需要告诉Docker:"嘿,老登,这是我的超市,Trust me!!"

# 创建或编辑Docker守护进程配置文件
sudo mkdir -p /etc/docker
sudo vi /etc/docker/daemon.json

# 添加以下内容(假设你的服务器IP是192.168.1.100)
{
  "insecure-registries": ["192.168.1.100:5000"]
}

# 保存并关闭文件(按ESC,然后输入:wq回车)

# 重启Docker服务
sudo systemctl restart docker

记得将 192.168.1.100​ 替换为你自己的服务器IP地址。这就像是告诉你的好兄弟:"这个地址的超市是我开的,随便拿!"

为什么 Docker 默认只信任HTTPS仓库?这是出于安全考虑。HTTPS 提供了加密传输,防止中间人攻击,确保下载的镜像确实来自正确的源头,而不是被篡改过的版本。在生产环境中,强烈建议为你的私有仓库配置HTTPS。

如果你想在生产环境中使用HTTPS,需要获取SSL证书(可以使用Let's Encrypt等免费证书服务)并相应地配置Registry:

# 假设你已经有了证书文件
sudo mkdir -p /opt/registry/certs
# 将证书文件放在/opt/registry/certs目录下

# 重新启动Registry容器,启用HTTPS
sudo docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry \
  -v /opt/registry/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  --restart=always \
  registry:2

第四步:测试私有镜像仓库

现在,让我们来测试一下我们的"私人超市"是否正常营业:

# 拉取一个测试镜像
sudo docker pull centos:7

# 给镜像打标签,准备推送到私有仓库
sudo docker tag centos:7 192.168.1.100:5000/my-centos

# 推送镜像到私有仓库
sudo docker push 192.168.1.100:5000/my-centos

# 删除本地镜像(为了测试拉取)
sudo docker rmi 192.168.1.100:5000/my-centos

# 从私有仓库拉取镜像
sudo docker pull 192.168.1.100:5000/my-centos

如果一切顺利,你应该能够成功推送和拉取镜像。这就像是在你的"私人超市"里存放和取用食材!

让我们理解一下这个过程:

  1. 首先,我们从Docker Hub拉取了官方的CentOS镜像。

  2. 然后,我们给这个镜像打上了一个新标签,指向我们的私有仓库。

  3. 接着,我们将带有新标签的镜像推送到私有仓库。

  4. 为了测试,我们删除了本地的镜像。

  5. 最后,我们从私有仓库拉取镜像,验证它是否正常工作。

这个过程展示了私有仓库的基本功能:存储和分发Docker镜像。

第五步:增强安全性(可选但推荐)

我们的"私人超市"现在还没有安装防盗系统!任何知道你仓库地址的人都可以推送和拉取镜像。让我们添加一些基本的安全措施:

# 停止并删除当前的Registry容器
sudo docker stop registry
sudo docker rm registry

# 创建存储认证信息的目录
sudo mkdir -p /opt/registry/auth

# 创建用户名和密码
sudo docker run --rm --entrypoint htpasswd registry:2 -Bbn myuser mypassword > /opt/registry/auth/htpasswd

# 重新启动Registry容器,启用认证
sudo docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry \
  -v /opt/registry/auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  --restart=always \
  registry:2

现在,你的"私人超市"有了门禁系统!在推送或拉取镜像时,需要提供用户名和密码:

# 登录到私有仓库
sudo docker login 192.168.1.100:5000 -u myuser -p mypassword

# 之后就可以正常推送和拉取镜像了

这种基于htpasswd的认证是最简单的方式,适合小型团队或个人使用。对于更大规模的部署,你可能需要考虑更复杂的认证方案,如OAuth或LDAP集成。

除了认证,你还可以实施以下安全措施:

  1. 网络隔离:将Registry部署在私有网络中,只允许特定的IP地址访问。

  2. 镜像扫描:使用工具如Clair或Trivy扫描镜像中的安全漏洞。

  3. 访问控制:实施细粒度的访问控制,限制谁可以推送或拉取特定的镜像。

第六步:管理你的私有镜像仓库

随着时间推移,你的"私人超市"可能会堆满各种"食材"。以下是一些管理技巧:

  1. 查看仓库中的镜像:

    curl -X GET http://192.168.1.100:5000/v2/_catalog
    

    如果启用了认证 ,需要提供凭据:

    curl -X GET -u myuser:mypassword http://192.168.1.100:5000/v2/_catalog
    
  2. 查看特定镜像的标签:

    curl -X GET http://192.168.1.100:5000/v2/my-centos/tags/list
    
  3. 定期清理不再使用的镜像(防止"超市"货架过满 ):
    默认情况下,Registry不允许删除镜像。要启用删除功能,需要在启动Registry时设置环境变量:

    # 停止并删除当前的Registry容器
    sudo docker stop registry
    sudo docker rm registry
    
    # 重新启动Registry,启用删除功能
    sudo docker run -d \
      --name registry \
      -p 5000:5000 \
      -v /opt/registry/data:/var/lib/registry \
      -v /opt/registry/auth:/auth \
      -e "REGISTRY_AUTH=htpasswd" \
      -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
      -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
      -e "REGISTRY_STORAGE_DELETE_ENABLED=true" \
      --restart=always \
      registry:2
    

    然后,你可以使用Registry API删除镜像:

    # 删除特定标签的镜像(需要先获取摘要)
    # 1. 获取镜像摘要
    curl -v -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://192.168.1.100:5000/v2/my-centos/manifests/latest
    
    # 2. 使用摘要删除镜像(将<digest>替换为实际的摘要值 )
    curl -X DELETE http://192.168.1.100:5000/v2/my-centos/manifests/<digest>
    
    # 3. 运行垃圾回收以释放磁盘空间
    sudo docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
    
  4. 备份你的Registry数据:
    定期备份/opt/registry/data​目录是个好习惯:

    # 创建备份
    sudo tar -czf registry-backup-$(date +%Y%m%d ).tar.gz /opt/registry/data
    
    # 将备份移动到安全位置
    sudo mv registry-backup-*.tar.gz /path/to/backup/location/
    
  5. 监控Registry的健康状态:
    你可以使用简单的健康检查脚本:

    # 创建健康检查脚本
    cat > registry-health-check.sh << 'EOF'
    #!/bin/bash
    
    REGISTRY_URL="http://192.168.1.100:5000"
    
    # 检查Registry是否响应
    response=$(curl -s -o /dev/null -w "%{http_code}" $REGISTRY_URL/v2/ )
    
    if [ $response -eq 200 ] || [ $response -eq 401 ]; then
      echo "Registry is healthy (HTTP $response)"
      exit 0
    else
      echo "Registry is not healthy (HTTP $response)"
      exit 1
    fi
    EOF
    
    # 添加执行权限
    chmod +x registry-health-check.sh
    
    # 设置定时任务,每小时检查一次
    (crontab -l 2>/dev/null; echo "0 * * * * /path/to/registry-health-check.sh >> /var/log/registry-health.log 2>&1") | crontab -
    

第七步:与CI/CD系统集成

私有镜像仓库的真正价值在于将其集成到你的持续集成/持续部署(CI/CD)流程中。以下是一个简单的示例,展示如何在Jenkins流水线中使用私有仓库:

pipeline {
    agent any
    
    environment {
        REGISTRY = "192.168.1.100:5000"
        IMAGE_NAME = "my-app"
        IMAGE_TAG = "${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Build') {
            steps {
                sh 'docker build -t $REGISTRY/$IMAGE_NAME:$IMAGE_TAG .'
            }
        }
        
        stage('Push') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'registry-credentials', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
                    sh 'docker login $REGISTRY -u $USERNAME -p $PASSWORD'
                    sh 'docker push $REGISTRY/$IMAGE_NAME:$IMAGE_TAG'
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh 'docker pull $REGISTRY/$IMAGE_NAME:$IMAGE_TAG'
                sh 'docker stop my-app || true'
                sh 'docker rm my-app || true'
                sh 'docker run -d --name my-app -p 8080:8080 $REGISTRY/$IMAGE_NAME:$IMAGE_TAG'
            }
        }
    }
}

这个流水线做了三件事:

  1. 构建Docker镜像并标记为私有仓库地址

  2. 登录到私有仓库并推送镜像

  3. 从私有仓库拉取镜像并部署

通过这种方式,你的CI/CD系统可以自动构建、存储和部署Docker镜像,而不需要依赖公共仓库。

结语:享受你的"私人超市"

恭喜!你现在拥有了自己的Docker"私人超市"。无需每次都跑到公共"超市"排队,你可以在自己的网络中高速获取所需的镜像"食材"。

私有镜像仓库带来的好处是显而易见的:

  • 更快的部署速度:从本地网络拉取镜像比从互联网下载快得多。

  • 更好的控制:你可以完全控制哪些镜像可以存储和使用。

  • 更高的安全性:敏感的镜像不会暴露在公共网络中。

  • 更可靠的可用性:即使互联网连接中断,你的部署流程仍然可以正常工作。

记住,就像真正的超市需要维护一样,定期备份你的/opt/registry/data​目录是个好习惯。毕竟,谁也不想自己辛苦收集的"食材"突然消失,对吧?

随着你的团队和项目规模的增长,你可能需要考虑更高级的解决方案,如Harbor(一个企业级的Registry项目,提供更多功能如镜像扫描、复制和图形界面)或商业产品如JFrog Artifactory。但对于大多数小型团队和个人项目,Docker官方的Registry已经足够了。

参考文献

  1. Docker官方文档:Registry部署指南

  2. Docker官方文档:配置安全访问

  3. Docker官方文档:存储配置

  4. CentOS官方文档:在CentOS上安装Docker

  5. CentOS Wiki:防火墙配置

  6. GitHub: Docker Registry项目

  7. Harbor项目官方文档

  8. Docker安全最佳实践