cri-containerd运行时使用私有镜像仓库

cri-containerd运行时使用私有镜像仓库

  • 介绍
  • 从docker切换到containerd
  • cri-containerd使用私有镜像仓库的注意事项
    • 不支持使用plain-http的镜像仓库
    • 不支持自签名证书
    • 制定username:password配置auth
  • 总结

介绍

k8s缺省使用docker运行时,但是1.12.x开始支持使用containerd运行时,这样调用路径从原来的kubelet->dockershim->dockerd->containerd->runc-shim->runc 优化为kublet->cri->containerd->runc->shim->runc, 而且cri还是作为containerd的一个插件跑在一个进程空间,看起来是提高了性能,但是如果习惯于在节点用docker命令的话就不要切换了,或者只切换部分节点。
我就是只切换了一个节点作为实验,方法其实很简单,只是修改下kubelet的启动命令就好了,但是如果你是用的私有镜像仓库的话会碰到一些问题,这里我主要目的是记录下cri-containerd使用私有镜像仓库时碰到的问题。

从docker切换到containerd

修改kubelet的启动命令添加如下参数--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock

systemctl daemon-reload
systemctl restart kubelet

注意kubelet是通过/run/containerd/containerd.sock和containerd通信的,安装有docker(新一点的版本)的节点都会有个daemon进程,如果是全新没有安装docker的节点,只要先创建配置文件,然后手动或者以服务方式启动containerd进程即可。

containterd config default > /etc/containerd/config.toml, 
/usr/bin/conainerd

正常情况下如果你只是使用官方镜像库,pod就可以调度到这个使用containerd的节点了,但是如开头说的,如果是私有仓库,还有几个问题要解决

cri-containerd使用私有镜像仓库的注意事项

不支持使用plain-http的镜像仓库

现象是调度到该节点的pod都启动失败,kubectl describe pod/xxx 显示的出错信息server gave HTTP response to HTTPS client, 我是用docker registry搭建的简易本地仓库,没有启用tls, 很明显cri-containerd是希望ssl加密传输的,不过containerd本身是支持走明文的,因为ctr --address=/run/containerd/containerd.sock images pull --plain-http --user admin:xxxxxx image-registry:5000/gcr.io/google_containers/pause-amd64:3.1通过添加plain-http参数可以成功,说明是cri不支持, 而且也没找到类似docker的insecure-registry配置.

只能通过重新配置registry解决:

先生成服务段证书docker-registry-server.pem, 再修改registry的启动命令, (注意保持原来的端口5000, 这样其他仍然采用docker运行时的节点不受影响)

docker run -d -p 5000:443 --restart=always --name image-registry \
  -v /cloud/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 \
  -v /cloud/registry/:/var/lib/registry/ \
  -v /cloud/registry/certs:/certs/ \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/docker-registry-server.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/docker-registry-server-key.pem \
  image-registry:5000/registry:latest

不支持自签名证书

修改了containerd的配置文件指向新的registry端点https://image-registry:5000后重启containerd, pod启动仍然失败,报错:

failed to pull image "image-registry:5000/gcr.io/google_containers/pause-amd64:3.1": 
failed to resolve image "image-registry:5000/gcr.io/google_containers/pause-amd64:3.1": 
failed to do request: Head https://172.16.137.128:5000/v2/gcr.io/google_containers/pause-amd64/manifests/3.1: 
x509: certificate signed by unknown authority

因为用的是自己生成的自签名证书,所以报错了,同样containerd其实是支持这个特性的, ctr有个参数–skip-verify可以绕开这个问题ctr --address=/run/containerd/containerd.sock images pull --skip-verify --user admin:xxxxx image-registry:5000/gcr.io/google_containers/pause-amd64:3.1,但是cri没有地方设置ca证书,也没有配置可以设置skip-verify, 这也太弱了吧,觉得不可能阿,google上看到文档里说可以配置:

[plugins.cri.registry.tls_configs]
  [plugins.cri.registry.tls_configs."my.custom.registry"]
    ca_file   = "ca.pem"
    cert_file = "cert.pem"
    key_file  = "key.pem"

按照这个修改配置后重启仍然不行,检查下来原来是最新的版本才支持这个配置, 重新编译:

git clone github.com/containerd/cri
make install.deps

或者

make _output/containerd 

然后把containerd, containerd-shim-runc-v1, containerd-shim-runc-v2上传到最新节点上,重起containerd, 注意上传前要先停掉进程。

制定username:password配置auth

经过上一步后理应可以成功了,可是仍然报错:

rpc error: code = Unknown desc = failed to get sandbox image "image-registry:5000/gcr.io/google_containers/pause-amd64:3.1": 
failed to pull image "image-registry:5000/gcr.io/google_containers/pause-amd64:3.1": 
failed to resolve image "image-registry:5000/gcr.io/google_containers/pause-amd64:3.1": no available registry endpoint: 
unexpected status code https://image-registry:5000/v2/gcr.io/google_containers/pause-amd64/manifests/3.1: 401 Unauthorized

看起来像是没有配置鉴权信息,可是我确实已经配置了username, password, 也配了auth token的, ctr命令行用-u也是可以的, 怎么还报401?

看代码,最后发现代码里有一个问题(bug?)

 pkg/server/images_pull.go
237 func (c *criService) credentials(auth *runtime.AuthConfig) func(string) (string, string, error) {
238     return func(host string) (string, string, error) {
239         if auth == nil {
240             // Get default auth from config.
241             for h, ac := range c.config.Registry.Auths {
242                 u, err := url.Parse(h)
243                 if err != nil {
244                     return "", "", errors.Wrapf(err, "parse auth host %q", h)
245                 }
246                 if u.Host == host {
247                     auth = toRuntimeAuthConfig(ac)
248                     break
249                 }
250             }
251         }
252         return ParseAuth(auth)
253     }
254 }

第246行直接拿u.Host和host做比较会失败,因为h不带Scheme, rl.Parse(h)解析出来出来的u.Host为空, 修改/etc/containerd/config.toml配置文件绕开这个问题

    [plugins."io.containerd.grpc.v1.cri".registry]
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://registry-1.docker.io"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."image-registry:5000"]
          endpoint = ["https://image-registry:5000"]
      [plugins."io.containerd.grpc.v1.cri".registry.auths]
        [plugins."io.containerd.grpc.v1.cri".registry.auths."https://image-registry:5000"]
          auth = "XXXXXXXXXXX"
          username = "admin"
          password = "XXXXX"
      [plugins."io.containerd.grpc.v1.cri".registry.tls_configs]
        [plugins."io.containerd.grpc.v1.cri".registry.tls_configs."image-registry:5000"]
          ca_file   = "/etc/containerd/ca.crt"
          cert_file = "/etc/containerd/docker-registry-server.pem"
          key_file = "/etc/containerd/docker-registry-server-key.pem"

可以看到针对auths的设置明显和mirrors以及tls_confis不一样,要额外制定https Scheme, 应该是一个bug,好在这个版本还没最终release.

总结

总的来说从docker切换到containerd是比较方便的,只是如果使用私有镜像时会有问题:

  • 只支持使用tls的registry
  • 最新commit才支持使用自签名证书,最新release版本containerd-1.2.5还不支持
  • auth认证配置有个bug,必须明确指定https

你可能感兴趣的:(k8s,containerd,cri,registry)