一文学会Containerd配置和镜像加速
❤️ 摘要: 本文介绍了 Containerd 的基本概念及其在 Kubernetes 中的作用,并详细说明了如何通过配置代理、使用国内镜像源或手动下载等方式加速镜像拉取。此外,还提供了针对不同场景的具体配置方法,包括通过环境变量、
config.toml
文件和hosts.toml
文件实现镜像加速的具体步骤。这些方法有助于提高容器镜像的拉取效率。通过这些详细的步骤和示例,读者可以更好地理解和掌握Containerd的使用方法。
目录
- 1 概念
- 1.1 什么是Containerd
- 1.1.1 Containerd如何为Kubernetes服务?
- 1.1.2 创建单容器 pod 的工作原理
- 1.2 Containerd的镜像加速方式
- 1.2.1 镜像代理或镜像同步
- 1.2.2 使用国内的镜像源
- 1.2.3 手动下载并上传到私有镜像仓库
- 1.2.4 三种方式对比
- 1.1 什么是Containerd
- 2 Containerd配置镜像加速
- 2.1 未设置镜像加速
- 2.2 Containerd代理实现镜像加速
- 2.2.1 配置 Containerd 的环境变量
- 2.2.2 测试拉取镜像
- 2.3 Containerd的config.toml实现镜像加速
- 2.4 Containerd的hosts.toml实现镜像加速
- 2.4.1 主机配置方式对比config.toml配置方式的区别
- 2.4.2 通过主机配置方式配置
- 2.4.3 为所有注册表设置默认镜像
- 2.4.4 hosts.toml 内容详细说明
- 2.4.4.1 公共仓库案例
- 2.4.4.2 私有仓库案例
- 3 参考资料
1 概念
1.1 什么是Containerd
前文《一文掌握Containerd配置Harbor私有仓库》已经简单介绍过Containerd以及其配置说明。
从 Kubernetes 1.20 版本开始,Containerd 被设置为默认的容器运行时接口(CRI)。Kubernetes 官方在此版本中逐步淘汰了 Docker 作为默认 CRI,转而推荐使用 Containerd 来处理容器管理任务。所以如果您正在使用Kubernetes,那么了解并掌握Containerd是必要的。
1.1.1 Containerd如何为Kubernetes服务?
Containerd 通常与Kubelet运行在同一节点上,而Containerd内置了一个cri
插件,通过cri
插件处理来自 Kubelet 的所有 CRI 服务请求,并使用 containerd 来管理完整的容器生命周期和容器镜像。
下图是Kubelet与Containerd的关系图:
1.1.2 创建单容器 pod 的工作原理
❔ 说明: 以创建单容器pod的例子解释kublet、Containerd的工作原理
- Kubelet 通过 CRI 运行时服务 API 调用
cri
插件来创建 pod; cri
使用 CNI 创建并配置 pod 的网络命名空间;cri
使用containerd内部创建并启动一个特殊的暂停容器(Pause)(沙箱容器),并将该容器放入pod的cgroup和命名空间中(为简洁起见,省略了步骤);- 随后Kubelet 通过 CRI 镜像服务 API 调用
cri
插件来拉取应用程序容器镜像; - 如果节点上不存在镜像,
cri
会进一步使用 containerd 来拉取镜像; - 然后,Kubelet 通过 CRI 运行时服务 API 调用
cri
,使用拉取的容器镜像在 pod 内创建并启动应用程序容器; - 最终
cri
插件使用containerd内部创建应用程序容器,将其放入pod的cgroup和命名空间中,然后启动pod的应用程序容器。经过这些步骤,一个 pod 及其对应的应用程序容器就被创建并运行了。
1.2 Containerd的镜像加速方式
❔ 说明:国内用户在拉取镜像时,往往会遇到过镜像拉取超时或者速度极慢的问题,那是因为
docker
,Containerd
默认都是指向国外的镜像源(没有科学上网无法直接访问), 如docker.io, gcr.io,ghcr.io,k8s.gcr.io,quay.io。
目前实现镜像加速的方式有三种。
- 镜像代理或镜像同步
- 使用国内的镜像源
- 手动下载并上传到私有镜像仓库
1.2.1 镜像代理或镜像同步
- 镜像代理:通过代理服务器加速镜像拉取,代理会将请求转发到目标镜像仓库,并在本地缓存镜像。常见的方案有 Docker 官方提供的镜像加速器或配置私有代理。
- 镜像同步:使用代理服务器下载外部公共镜像再同步到本地镜像仓库,减少跨国下载的延迟,并确保镜像的稳定性和安全性。
1.2.2 使用国内的镜像源
使用国内的镜像源,如阿里云、网易云、华为云等提供的公共镜像加速服务,这些服务在国内多个地区都部署镜像官方仓库,大大减少网络传输延迟。
1.2.3 手动下载并上传到私有镜像仓库
手动从公共镜像仓库打包下载镜像,然后上传到本地或私有镜像仓库,确保本地有需要的镜像。
1.2.4 三种方式对比
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
镜像代理或镜像同步 | ○ 速度提升:通过代理或同步,镜像从本地网络获取,避免跨国网络带来的延迟问题。 ○ 高可用性:同步镜像可以确保即使外部仓库不可用,本地镜像仍可使用,增强容错性。 ○ 自动化:镜像同步可通过自动任务(如 Harbor 的同步功能)定期更新,无需人工干预。 | ○ 复杂度:设置代理或镜像同步可能需要额外的网络配置或额外的基础设施(如网络代理,私有镜像仓库)。 ○ 开销成本:部署代理服务需要额外的带宽费用,本地存储的镜像占用较多的磁盘空间。 | ○ 大规模集群或企业内部环境,希望加速镜像拉取并提高可用性。 ○ 需要对镜像进行统一管理、保证安全性。 |
使用国内的镜像源 | ○ 简单快捷:直接通过配置国内镜像源即可加速,无需复杂的网络设置。 ○ 成本低:大多数国内镜像源是免费的。 ○ 镜像同步及时:国内镜像源通常会快速同步官方仓库的镜像,更新速度较快。 | ○ 依赖第三方服务:依赖国内的镜像源,无法保证服务的稳定性和长期可用性。 ○ 受限于公共镜像源的内容:如果某些镜像源未同步特定镜像,可能需要额外的处理步骤。 | ○ 小规模或个人用户,需要快速加速镜像拉取且不愿意搭建自有基础设施。 ○ 对镜像源的稳定性和可用性要求较低的场景。 |
手动下载并上传到私有镜像仓库 | ○ 完全自主:不依赖外部服务,镜像管理自主可控。 ○ 安全性:镜像经过本地或私有仓库管理,确保镜像来源安全可控,适合企业的安全策略要求。 | ○ 手动维护:需要手动下载和上传镜像,耗时耗力,尤其在更新频繁时难以管理。 ○ 复杂性高:需要私有镜像仓库和相关基础设施的支持,增加运维复杂度。 ○ 存储成本:所有镜像都需要存储在私有仓库中,带来较大的存储和管理负担。 | ○ 需要严格管理镜像安全和来源的企业环境。 ○ 需要确保离线环境中使用的镜像始终可用的场景。 ○ 容器需求较小的场景,如学习或测试。 |
2 Containerd配置镜像加速
❔ 说明:本环境是Containerd版本: v1.6.4(systemd部署); Kubernetes版本:V1.29.7(二进制部署)
2.1 未设置镜像加速
[root@k8s-master1 containerd.service.d]# time crictl pull gcr.io/google-containers/busybox:latest
E0911 19:34:59.050185 19189 remote_image.go:238] "PullImage from image service failed" err="rpc error: code = Unknown desc = failed to pull and unpack image \"gcr.io/google-containers/busybox:latest\": failed to resolve reference \"gcr.io/google-containers/busybox:latest\": failed to do request: Head \"https://gcr.io/v2/google-containers/busybox/manifests/latest\": dial tcp 108.177.97.82:443: i/o timeout" image="gcr.io/google-containers/busybox:latest"
FATA[0030] pulling image: rpc error: code = Unknown desc = failed to pull and unpack image "gcr.io/google-containers/busybox:latest": failed to resolve reference "gcr.io/google-containers/busybox:latest": failed to do request: Head "https://gcr.io/v2/google-containers/busybox/manifests/latest": dial tcp 108.177.97.82:443: i/o timeout
- 这里拉取gcr.io的镜像肯定会超时。
2.2 Containerd代理实现镜像加速
关于Linux系统代理,可以通过两种方法实现。
方法一: 如果你希望系统中所有应用都使用代理,可以直接在系统的环境变量中配置实现全局代理。(自行百度,这里不展开说明。)
方法二: 针对Containerd服务配置代理,进而实现镜像加速。
2.2.1 配置 Containerd 的环境变量
❔说明: 你需要提前部署代理服务器,并在Containerd服务器配置代理服务。(代理部署方法自行百度,这里不展开说明。)
查看Containerd的服务文件位置
[root@k8s-master1 ~]# systemctl cat containerd
# /usr/local/lib/systemd/system/containerd.service
编辑 /etc/systemd/system/containerd.service.d/http-proxy.conf
文件(如果文件不存在,则创建它):
# 添加containerd.service链接
cd /etc/systemd/system/
ln -s /usr/local/lib/systemd/system/containerd.service containerd.service
# 创建containerd.service的代理配置
mkdir -p /etc/systemd/system/containerd.service.d
vim /etc/systemd/system/containerd.service.d/http-proxy.conf
添加以下内容(根据你的代理信息进行修改):
[Service]
Environment="HTTP_PROXY=http://your-proxy-server:port"
Environment="HTTPS_PROXY=http://your-proxy-server:port"
Environment="NO_PROXY=localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,,.svc,.cluster.local"
❔ 参数说明:
- HTTP_PROXY:设置 HTTP 请求的代理。
- HTTPS_PROXY:设置 HTTPS 请求的代理。
- NO_PROXY:设置不走代理的网段
⚠️ 在生产环境谨慎配置系统代理,否则可能会导致网络不通影响正常业务,建议
NO_PROXY
配置包括但不限以下网段:
- 本地地址和网段:
localhost
和127.0.0.1
或127.0.0.0/8
- Kubernetes 的默认域名后缀:
.svc
和.cluster.local
- Kubernetes Node 的网段甚至所有应该不用 proxy 访问的 node 网段:
<nodeCIDR>
- APIServer 的内部 URL:
<APIServerInternalURL>
- Service Network:
<serviceNetworkCIDRs>
- (如有)etcd 的 Discovery Domain:
<etcdDiscoveryDomain>
- Cluster Network:
<clusterNetworkCIDRs>
- 其他特定平台相关网段(如 DevOps, Git/制品仓库):
<platformSpecific>
- 常用内网网段:
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
将代理配置为 Containerd
的服务环境变量后,重新加载并重启 Containerd
服务:
systemctl daemon-reload
systemctl restart containerd
2.2.2 测试拉取镜像
尝试拉取镜像
[root@k8s-master1 system]# time crictl pull gcr.io/google-containers/busybox:latest
Image is up to date for sha256:36a4dca0fe6fb2a5133dc11a6c8907a97aea122613fa3e98be033959a0821a1f
real 0m2.694s
user 0m0.011s
sys 0m0.007s
- 发现拉取成功, 而且速度还不错。
2.3 Containerd的config.toml实现镜像加速
如果没有/etc/containerd/config.toml
,执行以下命令生成默认配置
sudo containerd config default | sudo tee /etc/containerd/config.toml
编辑Containerd的配置文件,添加以下镜像配置
# 找到mirrors配置处
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://docker.io","https://6qxc6b6n.mirror.aliyuncs.com","https://docker.m.daocloud.io","https://dockerproxy.com/"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
endpoint = ["https://gcr.m.daocloud.io","https://gcr.nju.edu.cn","https://gcr.dockerproxy.com"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
endpoint = ["https://k8s-gcr.m.daocloud.io","https://gcr.nju.edu.cn/google-containers/","https://k8s.dockerproxy.com/"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
endpoint = ["https://quay.m.daocloud.io","https://quay.nju.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
endpoint = ["https://k8s.m.daocloud.io","https://k8s.nju.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.elastic.co"]
endpoint = ["https://elastic.m.daocloud.io"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"]
endpoint = ["https://ghcr.m.daocloud.io","https://ghcr.nju.edu.cn"]
重启Containerd服务
systemctl restart containerd
尝试在节点上通过Containerd直接拉取镜像,验证是否成功:
[root@k8s-master1 containerd.service.d]# time crictl pull gcr.io/google-containers/busybox:latest
Image is up to date for sha256:36a4dca0fe6fb2a5133dc11a6c8907a97aea122613fa3e98be033959a0821a1f
real 0m5.123s
user 0m0.013s
sys 0m0.058s
- 成功拉取镜像,速度也可以。
2.4 Containerd的hosts.toml实现镜像加速
❔ 说明:Containerd v1.5 版本以后新增了新的镜像仓库配置方式—
主机配置(hosts.toml)
,同时支持ctr
(containerd 工具)、containerd 镜像服务客户端和 CRI 客户端(例如kubectl
和crictl
)等客户端。
2.4.1 主机配置方式对比config.toml配置方式的区别
config.toml
是Containerd
的核心配置文件,定义了Containerd
的全局行为和设置。hosts.toml
是从config.toml
全局配置中分离出来,专用于配置Containerd
的镜像仓库相关信息。这个文件可以定义多个镜像仓库以及如何访问这些仓库,可以实现更细粒度管理。hosts.toml
最大于config.toml
的优势是配置不需要重启即可生效,避免了修改配置出错导致Containerd无法起来和减小修改全局配置的风险。
2.4.2 通过主机配置方式配置
为Containerd配置注册表将通过在配置目录中为每个所需的注册表主机指定(可选) hosts.toml
文件来完成。注意:此目录下的更新不需要重新启动containerd守护进程。
配置全局配置文件/etc/containerd/config.toml
,指定主机配置主目录,配置如下:
version = 2
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
⚠️ 注意:同时将之前config.toml关于容器仓库的配置都要注释掉, 否则会冲突。
[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."harbor.zx".auth]
# username = "admin"
# password = "admin12345"
#
# [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.zx".tls]
# insecure_skip_verify = false
# ca_file = "/etc/containerd/certs.d/harbor.zx/ca.crt"
# cert_file = "/etc/containerd/certs.d/harbor.zx/client.cert"
# key_file = "/etc/containerd/certs.d/harbor.zx/client.key"
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
# endpoint = ["https://6qxc6b6n.mirror.aliyuncs.com","https://docker.m.daocloud.io","https://dockerproxy.com/"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
# endpoint = ["https://gcr.m.daocloud.io","https://gcr.nju.edu.cn","https://gcr.dockerproxy.com"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
# endpoint = ["https://k8s-gcr.m.daocloud.io","https://gcr.nju.edu.cn/google-containers/","https://k8s.dockerproxy.com/"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
# endpoint = ["https://quay.m.daocloud.io","https://quay.nju.edu.cn"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
# endpoint = ["https://k8s.m.daocloud.io","https://k8s.nju.edu.cn"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.elastic.co"]
# endpoint = ["https://elastic.m.daocloud.io"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"]
# endpoint = ["https://ghcr.m.daocloud.io","https://ghcr.nju.edu.cn"]
#[plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.zx"]
# endpoint = ["https://harbor.zx"]
重启Containerd服务
systemctl restart containerd
如docker.io
的示例,其主机目录结构以下:
# 创建命名空间
mkdir -p /etc/containerd/certs.d/docker.io
# 创建主机配置
touch /etc/containerd/certs.d/docker.io/hosts.toml
# 目录结构
$ tree /etc/containerd/certs.d
/etc/containerd/certs.d
└── docker.io # 命名空间名称与镜像仓库域名一致
└── hosts.toml # 该镜像仓库的配置文件
❔ 目录结构说明:
- 主机配置主目录为
/etc/containerd/certs.d
- 官方文档提出了三个概念: 注册表主机(registry host)、注册表镜像(registry mirror)、注册表主机命名空间(registry host namespace )
- 注册表主机, 例如,docker.io、quay.io和 ghcr.io, 即容器镜像的仓库,一般以http/https协议提供对对服务;
- 注册表镜像是
registry host
的缓存或加速代理,类似于CDN加速; - 注册表主机命名空间即是主机配置主目录下的子目录,如docker.io, 格式可以是
[registry_host_name|IP address][:port]
2.4.3 为所有注册表设置默认镜像
如果没有其他命名空间匹配,则可以选择将_default
注册表主机命名空间用作后备。
$ tree /etc/containerd/certs.d
/etc/containerd/certs.d
└── _default
└── hosts.toml
$ cat /etc/containerd/certs.d/_default/hosts.toml
[host."https://registry.example.com"]
capabilities = ["pull", "resolve"]
2.4.4 hosts.toml 内容详细说明
对于容器仓库config_path
中的每个容器仓库命名空间目录,您可以包含一个hosts.toml
配置文件。以下根级别 toml 字段适用于容器仓库命名空间:
⚠️ 注意:
hosts.toml
文件中指定的所有路径可以是绝对路径,也可以是相对于hosts.toml
文件的相对路径。
2.4.4.1 公共仓库案例
下面以docker.io
作为配置案例:
编写hosts.toml
server = "https://docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
[host."https://dockerproxy.com/"]
capabilities = ["pull", "resolve"]
❔ 参数说明:
字段 | 说明 | 可能值 |
---|---|---|
server | 指定此注册表主机命名空间的默认服务器 | 1) 如果仅配置server,没有配置host,则访问server地址; 2)如果配置一个或多个主机,则按顺序尝试访问所有host ,则server将用作后备; 3)如果没有server和host,则以命名空间来访问 |
host | 指定此注册表主机终端节点,可以有一个或多个主机。 | 可以是域名或者是IP地址,类似于CNAME; |
capabilities | 是一个可选设置,用于指定主机能够执行哪些操作。 | 1)push是指向镜像仓库推送镜像; 2)pull是向镜像仓库拉取镜像; 3)resolve是将镜像(image:tag)翻译成唯一标识的 digest(哈希值) |
测试拉取镜像
[root@k8s-master1 certs.d]# crictl pull docker.io/library/busybox
Image is up to date for sha256:87ff76f62d367950186bde563642e39208c0e2b4afc833b4b3b01b8fef60ae9e
2.4.4.2 私有仓库案例
以harbor私有仓库为例:
创建主机配置目录
mkdir -p /etc/containerd/certs.d/harbor.zx/
编辑hosts.toml文件
server = "https://harbor.zx"
[host."https://harbor.zx"]
capabilities = ["pull", "resolve", "push"]
skip_verify = true
#
ca = "/etc/containerd/certs.d/harbor.zx/ca.crt"
#双向认证配置客户端证书
#client = ["/etc/certs/client.cert", "/etc/certs/client.key"]
❔参数说明:
-
skip_verify
:是否跳过对注册表的证书链和主机名的验证。这只能用于测试或与其他验证连接的方法结合使用。 (默认为false
) - ca:
ca
(证书颁发机构认证)可以设置为一个路径或一组路径,每个路径都指向一个 ca 文件,用于通过注册表命名空间进行身份验证。 - client:
client
证书配置, 如果是双向认证需要配置客户端证书。
3 参考资料
[1] Containerd 如何配置 Proxy
[2]主流可用Docker镜像加速站列表汇总
[3]docker proxy官方文档
[4]Registry Configuration - Introduction
[5] Configure Image Registry