在使用docker
容器运行时,配置访问http
私有仓库的方法较为简单,类似如下即可:
shell
# cat /etc/docker/daemon.json
{
"insecure-registries": ["192.168.0.4:5500"]
}
而当Kubernetes
使用containerd
时,其配置文件就相对复杂了。总的来说,有两种方式可以实现http
仓库的访问。
方法一:registry.configs
修改/etc/containerd/config.toml
中如下位置,默认配置为:
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
例如添加私有仓库http://192.168.0.4:5500
,则添加如下配置:
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.0.4:5500".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.0.4:5500"]
endpoint = ["http://192.168.0.4:5500"]
修改完成后重启containerd
:
shell
systemctl restart containerd
查看最新配置
shell
containerd config dump
测试镜像拉取:
shell
# crictl pull 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0
Image is up to date for sha256:69547dffc18fcddd4f41b7696a046ad7252ceeabce944106da94fa4bfb3c6b24
如需配置仓库认证信息,可参考:https://github.com/containerd/containerd/blob/main/docs/cri/registry.md
按照文档的说法,这种配置方式只支持到v1.6
,目前我们的生产环境最高版本也是1.6,因此尚未测试这种方式在v1.7
是否可行。
方法二:hosts.toml
这种方式较为推荐,因为它可以做到不同仓库的配置文件分开管理,首先我们将默认配置文件修改为(即修改config_path
):
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "etc/containerd/certs.d"
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
接着创建配置文件夹:
shell
cd /etc/containerd/
mkdir certs.d
然后创建仓库配置文件夹(以下也称主机命名空间namespace
)及配置文件hosts.toml
,文件夹名称为仓库域名或者[IP address][:port]
,本例中为:
mkdir certs.d/192.168.0.4:5500
touch certs.d/192.168.0.4:5500/hosts.toml
创建完成后文件树如下:
shell
# tree /etc/containerd/certs.d
/etc/containerd/certs.d
└── 192.168.0.4:5500
└── hosts.toml
hosts.toml
写入如下配置:
# cat /etc/containerd/certs.d/192.168.0.4\:5500/hosts.toml
server = "http://192.168.0.4:5500"
[host."http://192.168.0.4:5500"]
capabilities = ["pull", "resolve", "push"]
skip_verify = true
重启containerd
,并测试镜像拉取:
shell
# systemctl restart containerd
# crictl pull 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0
Image is up to date for sha256:69547dffc18fcddd4f41b7696a046ad7252ceeabce944106da94fa4bfb3c6b24
hosts.toml文件详解
hosts.toml
中指定的文件必须为绝对路径或相对于hosts.toml
的相对路径
server
字段
该主机命名空间的默认服务器地址,也即Registry
host
字段
即Mirror
,镜像地址(server
与host
的关系,就是Registry
和mirror
的关系,例如Docker Hub
是Registry
,而国内的镜像源如阿里镜像源则是Mirror
),如果配置了host
,当客户端试图拉取或推送镜像时,会根据配置文件中定义的顺序和能力去尝试这些镜像服务器。capabilities
字段
表示该主机被信任执行的一系列操作集合。skip_verify
表示是否跳过证书链验证,默认为false
,即不跳过验证。ca
字段
指定ca证书路径或路径集合。client
字段
指定客户端证书或证书集合。header
字段
指定请求header,由一系列键值对组成override_path
字段
表示主机的API根路径是在URL路径中定义的,而不是通过API规范定义的。这个参数主要用于与那些不遵循OCI(Open Container Initiative)标准、缺少/v2前缀的非合规Registry配合使用。在Docker或其他容器引擎与Registry交互时,默认情况下会按照OCI规范去访问Registry API,即请求通常以/v2/
开头,例如:https://registry.example.com/v2/<image>/<tag>
。然而,某些非标准的Registry可能并未遵循这一规范,其API根路径可能直接是注册表名之后的某个自定义路径。在这种情况下,通过将override_path
设置为true
并提供相应的路径,客户端就可以正确地定位到这些非标准Registry的API根路径,以实现与它们的有效通信。例如,如果API根路径是https://registry.example.com/custom/api
,则可以通过配置override_path
来适应这种特殊格式。
一个示例配置文件如下:
server = "https://registry-1.docker.io"
[host."https://mirror.registry"]
capabilities = ["pull"]
ca = "/etc/certs/mirror.pem"
skip_verify = false
[host."https://mirror.registry".header]
x-custom-2 = ["value1", "value2"]
[host."https://mirror-bak.registry/us"]
capabilities = ["pull"]
skip_verify = true
[host."http://mirror.registry"]
capabilities = ["pull"]
[host."https://test-1.registry"]
capabilities = ["pull", "resolve", "push"]
ca = ["/etc/certs/test-1-ca.pem", "/etc/certs/special.pem"]
client = [["/etc/certs/client.cert", "/etc/certs/client.key"],["/etc/certs/client.pem", ""]]
[host."https://test-2.registry"]
client = "/etc/certs/client.pem"
[host."https://test-3.registry"]
client = ["/etc/certs/client-1.pem", "/etc/certs/client-2.pem"]
[host."https://non-compliant-mirror.registry/v2/upstream"]
capabilities = ["pull"]
override_path = true
注意一:两种方法不能混用
笔者在撰文时使用的测试环境恰好碰到该问题,因此也顺手记录。当两者混用时,即有如下配置:
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "etc/containerd/certs.d"
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.0.4:5500".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.0.4:5500"]
endpoint = ["http://192.168.0.4:5500"]
此时systemctl restart containerd
,然后测试拉取镜像,会有如下报错:
# crictl pull 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0
FATA[0000] validate service connection: validate CRI v1 image API for endpoint "unix:///run/containerd/containerd.sock": rpc error: code = Unimplemented desc = unknown service runtime.v1.ImageService
对此问题进行排查,检查节点状态发现已NotReady
:
shell
# kubectl get node
NAME STATUS ROLES AGE VERSION
kcs-zrjtest-m-c8ggl NotReady control-plane 29h v1.25.7-eki.3.0.0
kcs-zrjtest-m-mqtb5 Ready control-plane 29h v1.25.7-eki.3.0.0
kcs-zrjtest-m-tbqmq Ready control-plane 29h v1.25.7-eki.3.0.0
kcs-zrjtest-s-h8gvl Ready <none> 29h v1.25.7-eki.3.0.0
kcs-zrjtest-s-k7n9f Ready <none> 29h v1.25.7-eki.3.0.0
kcs-zrjtest-s-q6tbt Ready <none> 29h v1.25.7-eki.3.0.0
kcs-zrjtest-s-sczsh Ready <none> 29h v1.25.7-eki.3.0.0
kcs-zrjtest-s-v9svm Ready <none> 29h v1.25.7-eki.3.0.0
检查节点kubelet
日志,有如下报错:
shell
# journalctl -u kubelet -f
Apr 10 17:27:31 kcs-zrjtest-m-c8ggl kubelet[15698]: E0410 17:27:31.681528 15698 remote_runtime.go:391] "ListPodSandbox with filter from runtime service failed" err="rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService" filter="nil"
Apr 10 17:27:31 kcs-zrjtest-m-c8ggl kubelet[15698]: E0410 17:27:31.681561 15698 kuberuntime_sandbox.go:297] "Failed to list pod sandboxes" err="rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
Apr 10 17:27:31 kcs-zrjtest-m-c8ggl kubelet[15698]: E0410 17:27:31.681571 15698 generic.go:205] "GenericPLEG: Unable to retrieve pods" err="rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
问题指向容器运行时,检查containerd
日志:
shell
Apr 10 17:23:32 kcs-zrjtest-m-c8ggl containerd[116927]: time="2024-04-10T17:23:32.560557543+08:00" level=warning msg="failed to load plugin io.containerd.grpc.v1.cri" error="invalid plugin config: `mirrors` cannot be set when `config_path` is provided"
即当指定了config_path
时,是不能再配置mirrors
的([plugins."io.containerd.grpc.v1.cri".registry.mirrors]
)。
解决办法也即方法一和方法二只保留其一。
注意二:这两种方法是用来配置cri
的,因此不适用于ctr
或nerdctl
命令
有些同学在配置完成后,使用ctr
或nerdctl
命令进行拉取测试,会发现测试无效:
shell
# ctr images pull 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0
INFO[0000] trying next host error="failed to do request: Head \"https://192.168.0.4:5500/v2/registry.k8s.io/ingress-nginx/kube-webhook-certgen/manifests/v1.3.0\": http: server gave HTTP response to HTTPS client" host="192.168.0.4:5500"
ctr: failed to resolve reference "192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0": failed to do request: Head "https://192.168.0.4:5500/v2/registry.k8s.io/ingress-nginx/kube-webhook-certgen/manifests/v1.3.0": http: server gave HTTP response to HTTPS client
这是因为以上配置是为cri
准备的( [plugins."io.containerd.grpc.v1.cri".registry]
),因此只适用于cri
客户端如crictl
、kubectl
,上文中也是使用crictl
进行的测试。
如果使用ctr
测试,可以使用--hosts-dir
指定配置文件:
ctr images pull --hosts-dir "/etc/containerd/certs.d" 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0
或者直接使用--plain-http
参数指定使用http
:
ctr images pull --plain-http 192.168.0.4:5500/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0