云原生容器化-4 Docker仓库

1.Docker仓库

1.1 Docker Hub

docker仓库用于存放docker镜像,可以分为公用和私有两种。Docker Hub是全球公用的仓库,因服务器在国外,国内基本不可以;一般需要配置阿里、腾讯等加速器。公司内部而言,可以搭建私有的Docker仓库(如Harbor)。

说明 :使用Docker Hub下载镜像时,不需要登录;但是推送、删除、修改时需要登录。

登录与注销涉及如下命令:

shell 复制代码
#登录时携带用户名、密码、仓库地址信息
docker login --username test --password test@123 192.168.0.22:8000
docker login --username seong --password 3er4#ER$ 192.168.0.22:8000
#注销时需要仓库信息
docker logout 192.168.0.22:8000

注册成功后,在/root/.docker/config.json文件中记录仓库登录成功信息:

json 复制代码
{
        "auths": {
                "192.168.0.22:8000": {
                        "auth": "c2Vvbmc6M2VyNCNFUiQ="
                }
        }
}

注销命令执行时,将删除文件中对应信息(可以重复注销)。

1.2 常见的本地仓库:Registory和Harbor

Docker提供了registry镜像,可以较为方便地搭建仓库。Harbor提供了对用户更为友好的UI界面,推荐使用Harbor。需要注意: Harbor通过docker-compose管理,因此安装Harbor是需要先安装/docker-compose,详细的安装流程请参考服务器环境搭建-2 Docker与Harbor

1.3 配置docker仓库

docker的配置文件路径为/etc/docker/daemon.json,可以通过registry-mirrors属性添加docker仓库。改动配置文件后,需要重启docker(执行service docker restart)生效。如可以配置腾讯的docker仓库镜像:

json 复制代码
{
    "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}

默认情况下,docker使用https协议与仓库通信。将上述搭建的Harbor仓库配置为docker的仓库地址时会报错: http: server gave HTTP response to HTTPS client.

可以通过在insecure-registries属性中添加白名单地址来规避。如Harbor仓库的地址为:"http://192.168.0.22:8000", 可以配置为:

json 复制代码
{
    "registry-mirrors": ["https://mirror.ccs.tencentyun.com"],       
    "insecure-registries": ["192.168.0.22:8000"]    
} 

除了在daemon.json配置文件中修改外,还可以再docker的service文件(/usr/lib/systemd/system/docker.service)中进行配置:

复制代码
[Service]
Type=notify
ExecStart=/usr/bin/dockerd --insecure-registry=192.168.0.22:8000  -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always

ExecStart表示进程启动命令,通过--insecure-registry参数设置允许通过http访问的仓库地址。

说明:修改docker.service文件后,需要执行systemctl daemon-reload使得配置文件生效;然后执行service docker restart使用新的service文件重启docker.

2. Docker Registry v2 API介绍和案例分析

Docker Registry v2 API是Docker引擎与Docker Registry进行交互时的接口规范。Docker引擎进行镜像的下拉、删除、推送和修改时需要调用Docker Registry提供的接口。

Docker对容器和镜像进行了分层设计,API也以分层为基础。Docker镜像数据包括:数据(blob)和元数据(manifest)两个部分,前者是实际的二进制数据,后者是镜像的描述数据,V2 API为这两部分分别提供了接口。

本文介绍推送和下拉镜像相关的API,其他API请参考: Distribution Registry / Reference Overview / HTTP API V2

2.1 API接口版本协商

2.1.1 API接口版本协商

url : GET /v2/
参数 :无
响应: 返回200 OK表示请求成功; 返回401未经授权.

2.1.2 案例:

http 复制代码
GET /v2/ HTTP/1.1
Host: 192.168.0.22:8000
User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\))
Accept-Encoding: gzip
Connection: close

HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
X-Content-Type-Options: nosniff
Date: Mon, 05 Feb 2024 01:48:16 GMT
Connection: close

{}

说明:Docker引擎与仓库交互前,会使用该接口进行协议确认。收到 200 OK表示仓库支持V2版本API,即Docker引擎可继续使用V2 API与仓库交互。

2.2 推送镜像

2.2.1 推送镜像的 manifests

url : PUT /v2/<name>/manifests/<reference>
参数 :

1\] `name`: 镜像名称 \[2\] `reference`: 标签/摘要 **响应**: 返回 201 Created表示已成功推送。 #### 2.2.2 推送镜像的 blobs **url** : PUT /v2//blobs/uploads/?digest= **参数** : \[1\] `name`: 镜像仓库名称 \[2\] uuid: 唯一ID标识 \[3\] digest: blob 的摘要 **响应**: 返回 201Created表示已成功推送。 #### 2.2.3 案例: 执行如下命令将从Docker Hub仓库下载的hello-world镜像,上传到本地仓库192.168.0.22:8000上: ```Python docker pull hello-world docker tag hello-world 192.168.0.22:8000/hello-world docker push 192.168.0.22:8000/hello-world ``` 在本地通过docker history命令可以看出hello-world包含两个镜像层, 其中一层镜像ID为d2c94e258dcb: ```shell [root@VM-4-6-centos ~]# docker history hello-world IMAGE CREATED CREATED BY SIZE COMMENT d2c94e258dcb 9 months ago CMD ["/hello"] 0B buildkit.dockerfile.v0 9 months ago COPY hello / # buildkit 13.3kB buildkit.dockerfile.v0 ``` 同时在环境上抓包,如下所示: ![在这里插入图片描述](https://file.jishuzhan.net/article/1757542345626095617/6fbabb01e4809e6f545aa9f63f299869.webp) 整个流程可以分为两个部分:上传各个镜像层的blobs数据和上传整个镜像的manifests数据; **\[1\] 上传各个镜像层的blobs数据** 由于hello-world包含两个镜像层c1ec31e和d2c94e,因此分别进行镜像层数据的推送。在每次推送过程会经过校验和传输两个阶段: (1)通过HEAD请求携带镜像的ID信息查询镜像层在仓库是否存在,收到200表示已存在,不需要上传,收到404表示不存在,(2)上传镜像层数据。 上传完成后,还会再次通过HEAD请求校验是否上传完成。 **\[2\] 上传整个镜像的manifests数据** 上述上传hello-world:latest镜像元数据的抓包信息如下: ```http PUT /v2/hello-world/manifests/latest HTTP/1.1 Host: 192.168.0.22:8000 User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\)) Content-Length: 524 Content-Type: application/vnd.docker.distribution.manifest.v2+json Accept-Encoding: gzip Connection: close { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { "mediaType": "application/vnd.docker.container.image.v1+json", "size": 581, "digest": "sha256:d2c94e258dcb3c5ac2798d32e1249e42ef01cba4841c2234249495f87264ac5a" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 2459, "digest": "sha256:c1ec31eb59444d78df06a974d155e597c894ab4cda84f08294145e845394988e" } ] }HTTP/1.1 201 Created Docker-Content-Digest: sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 Docker-Distribution-Api-Version: registry/2.0 Location: http://192.168.0.22:8000/v2/hello-world/manifests/sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 X-Content-Type-Options: nosniff Date: Mon, 05 Feb 2024 01:48:16 GMT Content-Length: 0 Connection: close ``` 进一步测试将hello-world打包成beta版本,然后推送到本地192.168.0.22:8000仓库: docker tag hello-world 192.168.0.22:8000/hello-world:beta2 docker push 192.168.0.22:8000/hello-world:beta2 同时在环境上抓包: ![在这里插入图片描述](https://file.jishuzhan.net/article/1757542345626095617/516cad89845a9b75949ce2b1ac420666.webp) 通过HEAD请求携带镜像的ID信息查询镜像层在仓库是否存在,收到200表示已存在,不需要上传镜像层数据,只需上传镜像的manifests即可。 由此也可以看出,Docker仓库将镜像层与镜像的元数据是分开存储的。镜像层可以被不同的镜像重复使用。 ### 2.3 拉取镜像 #### 2.3.1 获取镜像的manifests(元数据) **url** : `GET /v2//manifests/` **参数** : \[1\] `name`: 镜像名称 \[2\] `reference`: 标签/摘要 **响应**: 包含manifest信息的JSON 对象。 #### 2.3.2 获取镜像的blobs(二进制数据) **url** : `GET /v2//blobs/` **参数** : \[1\] `name`: 镜像名称 \[2\] `digest`: blob 的摘要 **响应**: 返回镜像的 二进制数据 #### 2.3.3 案例 将本地的hello-world:latest镜像删除,然后执行 docker pull 192.168.0.22:8000/hello-world从仓库拉重新拉取hello-world:latest镜像时,具体执行过程如下: \[1\] 通过HEAD请求获取hello-world:latest版本镜像的摘要sha256,如果返回404表示镜像不存在 ```http HEAD /v2/hello-world/manifests/latest HTTP/1.1 Host: 192.168.0.22:8000 User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\)) Accept: application/vnd.oci.image.manifest.v1+json Accept: application/vnd.docker.distribution.manifest.v2+json Accept: application/vnd.docker.distribution.manifest.list.v2+json Accept: application/vnd.oci.image.index.v1+json Accept: application/vnd.docker.distribution.manifest.v1+prettyjws Accept: application/json Connection: close HTTP/1.1 200 OK Content-Length: 524 Content-Type: application/vnd.docker.distribution.manifest.v2+json Docker-Content-Digest: sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 Docker-Distribution-Api-Version: registry/2.0 Etag: "sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7" X-Content-Type-Options: nosniff Date: Mon, 05 Feb 2024 03:43:16 GMT Connection: close ``` \[2\] 得到sha256信息为d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7, 然后将得到的摘要作为参数调用GET方法获取镜像的元数据: ```http GET /v2/hello-world/manifests/sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 HTTP/1.1 Host: 192.168.0.22:8000 User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\)) Accept: application/json Accept: application/vnd.oci.image.manifest.v1+json Accept: application/vnd.docker.distribution.manifest.v2+json Accept: application/vnd.docker.distribution.manifest.list.v2+json Accept: application/vnd.oci.image.index.v1+json Accept: application/vnd.docker.distribution.manifest.v1+prettyjws Accept-Encoding: gzip Connection: close HTTP/1.1 200 OK Content-Length: 524 Content-Type: application/vnd.docker.distribution.manifest.v2+json Docker-Content-Digest: sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 Docker-Distribution-Api-Version: registry/2.0 Etag: "sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7" X-Content-Type-Options: nosniff Date: Mon, 05 Feb 2024 03:43:16 GMT Connection: close { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { "mediaType": "application/vnd.docker.container.image.v1+json", "size": 581, "digest": "sha256:d2c94e258dcb3c5ac2798d32e1249e42ef01cba4841c2234249495f87264ac5a" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 2459, "digest": "sha256:c1ec31eb59444d78df06a974d155e597c894ab4cda84f08294145e845394988e" } ] } ``` \[3\] 从镜像的 manifests信息得知: hello-world:latest镜像由上下(d2c94e258dcb...)和(c1ec31eb5944...)两层组成。 Docker引擎判断本地是否有对应的镜像层,如果没有,会依次向仓库发送/v2//blobs/请求获取镜像层数据。此时本地环境不存在这两个镜像层,因此分别发送了两次请求: 请求c1ec31eb5944层镜像: ```http GET /v2/hello-world/blobs/sha256:c1ec31eb59444d78df06a974d155e597c894ab4cda84f08294145e845394988e HTTP/1.1 Host: 192.168.0.22:8000 User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\)) Accept-Encoding: gzip Connection: close .....数据.... ``` 请求d2c94e258dcb层镜像: ```http GET /v2/hello-world/blobs/sha256:d2c94e258dcb3c5ac2798d32e1249e42ef01cba4841c2234249495f87264ac5a HTTP/1.1 Host: 192.168.0.22:8000 User-Agent: docker/24.0.4 go/go1.20.5 git-commit/4ffc614 kernel/3.10.0-1160.92.1.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/24.0.4 \(linux\)) Accept-Encoding: gzip Connection: close .....数据.... ``` 上述流程的抓包信息如下: ![在这里插入图片描述](https://file.jishuzhan.net/article/1757542345626095617/aa2abe01290a5bcc5d533bc3f464d693.webp) 此时,再次执行docker pull 192.168.0.22:8000/hello-world指令,抓包信息如下: ![在这里插入图片描述](https://file.jishuzhan.net/article/1757542345626095617/f3627b9c953a0d1ac0fee7ec880d19a6.webp) 此时Docker引擎判断本地已有对应的镜像,无需再从仓库下载。 ## 3.总结 Docker仓库的搭建、配置、使用较为简单,上手操作就能掌握,重点在于理解分层的概念。从上述镜像的推送和拉取过程可以看出,Docker容器的镜像以层为颗粒度,从而减少了不必要的数据存储和传输。后续在介绍Docker容器中也会提到分层的概念,说明分层对降低计算机资源占用率的重要意义。

相关推荐
Asthenia041242 分钟前
Spring AOP 和 Aware:在Bean实例化后-调用BeanPostProcessor开始工作!在初始化方法执行之前!
后端
Asthenia04122 小时前
什么是消除直接左递归 - 编译原理解析
后端
Asthenia04122 小时前
什么是自上而下分析 - 编译原理剖析
后端
Asthenia04122 小时前
什么是语法分析 - 编译原理基础
后端
Asthenia04122 小时前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom2 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
Asthenia04123 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9654 小时前
ovs patch port 对比 veth pair
后端
Asthenia04124 小时前
Java受检异常与非受检异常分析
后端