新的容器镜像仓库选择

Posted by 梁远鹏 on 2023-01-18 | 阅读 |,阅读约 3 分钟

TOC

简介

Zot 是思科开源的遵循 OCI 规范的容器镜像仓库,目前捐赠给了 CNCF,是 Sandbox 级别项目.

本文主要讲述作为镜像代理仓库下的应用场景.

部署

Kubernetes

前提

  • helm
  • kubernetes cluster

这里使用 kind 来创建一个研究用的 K8S 单节点集群.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  apiServerAddress: "0.0.0.0"
  apiServerPort: 6443
containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
    endpoint = ["https://gcr.lank8s.cn"]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
    endpoint = ["https://registry.lank8s.cn"]  
nodes:
- role: control-plane
  extraMounts:
  - hostPath: /var/log/tmp
    containerPath: /var/log
  extraPortMappings:
  - containerPort: 5000
    hostPort: 31150
    listenAddress: "0.0.0.0"

开始部署

原本是想使用官方的 helm chart 来部署 zot,但是官方 helm 还处于比较早期的阶段,因此这里自己写了一个 yaml 文件来部署 zot.

TODO 配置固定的 nodePort 端口号,用于测试访问.

开启认证

默认情况下,所有用户都可以做任何操作,包括 读取/创建/更新/删除,在镜像代理仓库的场景下,这显然是不合理的,匿名用户只需要具备读权限即可.而管理员则具备其他权限,例如删除操作.

到目前为止 Zot 几种认证:

客户端

  • TLS 证书认证 (basic TLS 和 mTLS)
  • HTTP basic 认证
  • HTTP bearer 认证

服务器端

  • LDAP
  • httpasswd

当同时配置了 LDAP 和 httpasswd 时,优先使用 LDAP 认证,如果 LDAP 服务不可用时,回退到 httpasswd 认证机制.

实战

本文使用 HTTP basic 认证作为演示效果.

首先需要在 zot 的配置文件中开启 httpasswd 方式的认证:

...
"http": {
...
"auth": {
            "htpasswd": {
              "path": "/tmp/htpasswd"
            }
        }
}
...

先创建一个管理员用户, 账号和密码都是 admin

htpasswd -bc /tmp/htpasswd admin admin

首先拉取一下镜像,

docker pull localhost:10015/kube-proxy:v1.26.0

尝试用匿名用户来删除镜像:

curl -X DELETE http://localhost:10015/v2/kube-proxy/manifests/v1.26.0

可以看到会提示没权限,添加用户密码后再尝试一次:

echo -n admin:admin | base64
curl -X DELETE -H "Authorization: Basic: " http://localhost:10015/v2/kube-proxy/manifests/v1.26.0

可以看到,请求返回成功了,检查一下镜像数据是否还在:

这时候已经没有镜像数据了,从 Zot 的日志中也可以看到相关删除操作的提示:

扩展

“extensions”: { “metrics”: {}, “sync”: {}, “search”: {}, “scrub”: {}, “lint”: {} }

实际案例

lank8s

目前 lank8s 服务已经从 distribution 仓库切换到了 Zot 镜像仓库.对于 lank8s 来说,Zot 的一个比较大的吸引力是可以对多个容器镜像仓库代理,在 Distribution 的 repo 中早有人提了这个功能 issue,但是一直没有人来实现这部分内容.

{
    "extensions": {
        "scrub": {
            "enable": true,
            "interval": "3h"
        },
        "sync": {
            "enable": true,
            "registries": [
                {
                    "urls": [
                        "https://gcr.lank8s.cn"
                    ],
                    "onDemand": true,
                    "tlsVerify": true,
                    "maxRetries": 2,
                    "retryDelay": "5m",
                    "content": [
                    {
                        "prefix": "/google-containers/*"
                    }]
                },
                {
                    "urls": [
                        "https://registry.lank8s.cn"
                    ],
                    "onDemand": true,
                    "tlsVerify": true,
                    "maxRetries": 2,
                    "retryDelay": "5m"
                }
            ]
        }
    }
}

上面的配置示例是当拉取镜像时(例如 kube-proxy:v1.26.0)如果 Zot 本地没有镜像信息则会同时从上游registry.lank8s.cn, 拉取,为什么没有从另一个上游gcr.lank8s.cn拉取镜像呢?是因为在字段content中配置了prefix,只有前缀为 google-containers 的容器镜像才会从上游 gcr.lank8s.cn 拉取镜像.

kind local registry

目前我在使用 kind 创建 k8s 集群做一些实验时,也是使用了 zot 作为实验时拉取到的容器镜像的持久化管理,避免每次用 kind 创建一个新的 k8s 测试集群环境时总是要拉取一次镜像.

可以看看为containerd配置仓库镜像这篇文章中关于这部分的内容.

官方文档: https://kind.sigs.k8s.io/docs/user/local-registry/

初体验

用户不友好

下面列出几个不太友好的地方.

  • 当从上游同步镜像时是阻塞整个镜像下载过程的,需要等待 Zot 将镜像从上游整个下载到本地之后,才会让客户端继续镜像的下载.而 Distribution 是分步同步的,因此可以很快的给到客户端响应,用户体验会相对较好.(目前每次都会从上游下载镜像,相当于重复下载了,相关 issue 在这里 )

  • 作为镜像代理模式工作时不支持S3存储.

  • 同步多个上游镜像源时无法分开存储,例如上游 1 的数据存储在路径 A,上游 2 的数据存储在路径 B. 有一个subpath的功能,不过这个针对的是 namespace 级别的.

无法直接使用 docker push

当然这不代表有问题,因为 Zot 项目的设计原则就是 OCI 镜像仓库,不实现任何供应商相关的协议,当然也包括 Docker.

使用 skopeo 来将镜像 push 到 zot 当中.

从下面的命令来看,可以看到 docker push时会失败.

oem@lan:~$ docker push localhost:10014/kube-proxy:v1.26.1
The push refers to repository [localhost:10014/kube-proxy]
524ce886e20a: Pushed 
8342c4e13d7f: Layer already exists 
manifest invalid: manifest invalid

接下来使用 skopeo 来推送镜像到本地的 zot 镜像仓库,这里直接使用 skopeo copy 命令,会先下载镜像,然后 push 到 zot,推送过程很顺利.

oem@lan:~$ docker run --rm quay.io/skopeo/stable copy docker://registry.lank8s.cn/kube-proxy:v1.26.0 docker://192.168.31.13:10014/kube-proxy:v1.26.0 --insecure-policy  --dest-tls-verify=false
Getting image source signatures
Copying blob sha256:7b2002b165df1a6be480139f99889c43327387546e29a86ea16824b777da12e4
Copying blob sha256:33905e809db95cdd940d3db7fc8f1486243bf738f5cd712b8f62b105be60dad6
Copying config sha256:556768f31eb1d6673ce1d1fc0ace1e814fc40eee9923275ba3a82635159afc69
Writing manifest to image destination
Writing manifest to image destination
Copying config sha256:a8401234c2d642ee82eb5ef43e29ecf86cbd194fb986721865222965904a4a41
Writing manifest to image destination
Storing signatures

总结

总体来说 Zot 在轻量级要求的场景下还是很不错的,例如目前 lank8scn 服务使用 Zot 来同步上游 gcr.ioregistry.k8s.io 部分镜像.

微信公众号

扫描下面的二维码关注我们的微信公众号,第一时间查看最新内容。同时也可以关注我的Github,看看我都在了解什么技术,在页面底部可以找到我的Github。

wechat-qrcode

温馨提示

本文还在持续创作中,如果你对本文主题感兴趣可以加我微信好友进行催更,博客下方可以找到我的微信联系方式 :)