镜像
容器镜像表示封装了应用程序及其所有软件依赖项的二进制数据。容器镜像是可独立运行的可执行软件包,它们对其运行时环境做出了明确的假设。
通常,你会为应用程序创建一个容器镜像并将其推送到一个注册中心,然后在 Pod 中引用它。
本页面概述了容器镜像的概念。
注意
如果你正在寻找 Kubernetes 版本(例如 v1.34,最新的次要版本)的容器镜像,请访问 下载 Kubernetes。镜像名称
容器镜像通常会有一个名称,例如 pause
、example/mycontainer
或 kube-apiserver
。镜像还可以包含一个注册中心主机名;例如:fictional.registry.example/imagename
,也可能包含一个端口号;例如:fictional.registry.example:10443/imagename
。
如果你未指定注册中心主机名,Kubernetes 假定你指的是 Docker 公共注册中心。你可以在容器运行时配置中设置默认镜像注册中心来更改此行为。
在镜像名称部分之后,你可以添加一个 标签(tag) 或 摘要(digest)(与你使用 docker
或 podman
等命令时的方式相同)。标签允许你识别同一系列镜像的不同版本。摘要是镜像特定版本的唯一标识符。摘要是镜像内容的哈希值,并且是不可变的。标签可以指向不同的镜像,但摘要是固定的。
镜像标签由小写和大写字母、数字、下划线 (_
)、句点 (.
) 和连字符 (-
) 组成。标签最长可达 128 个字符,并且必须符合以下正则表达式模式:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}
。你可以在 OCI Distribution Specification 中阅读更多内容并找到验证正则表达式。如果你没有指定标签,Kubernetes 假定你指的是 latest
标签。
镜像摘要由哈希算法(例如 sha256
)和哈希值组成。例如:sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07
。你可以在 OCI Image Specification 中找到更多关于摘要格式的信息。
Kubernetes 可以使用的一些镜像名称示例:
busybox
— 仅有镜像名称,没有标签或摘要。Kubernetes 将使用 Docker 公共注册中心和latest
标签。等同于docker.io/library/busybox:latest
。busybox:1.32.0
— 带标签的镜像名称。Kubernetes 将使用 Docker 公共注册中心。等同于docker.io/library/busybox:1.32.0
。registry.k8s.io/pause:latest
— 带自定义注册中心和latest
标签的镜像名称。registry.k8s.io/pause:3.5
— 带自定义注册中心和非latest
标签的镜像名称。registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07
— 带摘要的镜像名称。registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07
— 带标签和摘要的镜像名称。仅使用摘要进行拉取。
更新镜像
当你首次创建 Deployment、StatefulSet、Pod 或其他包含 PodTemplate 的对象时,如果未明确指定拉取策略,则默认情况下,该 Pod 中所有容器的拉取策略将设置为 IfNotPresent
。此策略会导致 kubelet 跳过拉取已存在的镜像。
镜像拉取策略
容器的 imagePullPolicy
和镜像的标签都会影响 kubelet 尝试拉取(下载)指定镜像的 时间。
以下是你可以为 imagePullPolicy
设置的值及其影响的列表
IfNotPresent
- 仅当镜像在本地不存在时才拉取。
Always
- 每当 kubelet 启动容器时,它都会查询容器镜像注册中心,将名称解析为镜像摘要。如果 kubelet 在本地缓存了具有该确切摘要的容器镜像,则 kubelet 将使用其缓存的镜像;否则,kubelet 将拉取具有已解析摘要的镜像,并使用该镜像启动容器。
Never
- kubelet 不会尝试获取镜像。如果镜像以某种方式已在本地存在,kubelet 会尝试启动容器;否则,启动失败。有关更多详细信息,请参阅预拉取镜像。
只要注册中心可靠可访问,底层镜像提供程序的缓存语义即使是 imagePullPolicy: Always
也能高效运行。你的容器运行时可以注意到镜像层已存在于节点上,因此无需再次下载。
注意
部署生产环境中的容器时,应避免使用 :latest
标签,因为它更难追踪正在运行的镜像版本,并且更难以正确回滚。
相反,请指定有意义的标签,例如 v1.42.0
和/或摘要。
为确保 Pod 始终使用相同版本的容器镜像,你可以指定镜像的摘要;将
替换为
(例如,image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
)。
当使用镜像标签时,如果镜像注册中心更改了该标签所代表的代码,你可能会遇到同时运行旧代码和新代码的 Pod 混合情况。镜像摘要唯一标识镜像的特定版本,因此 Kubernetes 每次使用指定的镜像名称和摘要启动容器时都会运行相同的代码。通过摘要指定镜像会固定你运行的代码,这样注册中心的变化就不会导致版本混合。
有一些第三方准入控制器会在 Pod(和 PodTemplates)创建时对其进行修改,使得运行中的工作负载是基于镜像摘要而不是标签来定义的。如果你想确保整个工作负载运行相同的代码,无论注册中心发生什么标签更改,这可能很有用。
默认镜像拉取策略
当你(或控制器)向 API 服务器提交新的 Pod 时,如果满足特定条件,集群将设置 imagePullPolicy
字段
- 如果你省略
imagePullPolicy
字段并指定容器镜像的摘要,则imagePullPolicy
将自动设置为IfNotPresent
。 - 如果你省略
imagePullPolicy
字段,并且容器镜像的标签是:latest
,则imagePullPolicy
将自动设置为Always
。 - 如果你省略
imagePullPolicy
字段,并且你未指定容器镜像的标签,则imagePullPolicy
将自动设置为Always
。 - 如果你省略
imagePullPolicy
字段,并且你指定了一个不是:latest
的容器镜像标签,则imagePullPolicy
将自动设置为IfNotPresent
。
注意
容器的 imagePullPolicy
值始终在对象首次 创建 时设置,并且如果镜像的标签或摘要稍后更改,则不会更新。
例如,如果你使用一个标签 不是 :latest
的镜像创建 Deployment,然后将该 Deployment 的镜像更新为 :latest
标签,则 imagePullPolicy
字段将 不会 更改为 Always
。你必须在对象初始创建后手动更改任何对象的拉取策略。
必需的镜像拉取
如果你想始终强制拉取,可以执行以下操作之一
- 将容器的
imagePullPolicy
设置为Always
。 - 省略
imagePullPolicy
并使用:latest
作为要使用的镜像标签;当你提交 Pod 时,Kubernetes 会将策略设置为Always
。 - 省略
imagePullPolicy
和要使用的镜像标签;当你提交 Pod 时,Kubernetes 会将策略设置为Always
。 - 启用 AlwaysPullImages 准入控制器。
ImagePullBackOff
当 kubelet 使用容器运行时开始为 Pod 创建容器时,容器可能由于 ImagePullBackOff
而处于 Waiting 状态。
状态 ImagePullBackOff
意味着容器无法启动,因为 Kubernetes 无法拉取容器镜像(原因可能是镜像名称无效,或者从私有注册中心拉取但没有 imagePullSecret
)。BackOff
部分表示 Kubernetes 将继续尝试拉取镜像,并以逐渐增加的退避延迟。
Kubernetes 会增加每次尝试之间的延迟,直到达到编译的限制,即 300 秒(5 分钟)。
按运行时类拉取镜像
Kubernetes v1.29 [alpha]
(默认禁用)如果启用 RuntimeClassInImageCriApi
特性门控,kubelet 将通过镜像名称和运行时处理器组成的元组(而不是仅仅镜像名称或摘要)引用容器镜像。你的容器运行时可以根据选定的运行时处理器调整其行为。基于运行时类拉取镜像对于基于虚拟机的容器(例如 Windows Hyper-V 容器)很有用。
串行和并行镜像拉取
默认情况下,kubelet 串行拉取镜像。换句话说,kubelet 一次只向镜像服务发送一个镜像拉取请求。其他镜像拉取请求必须等到正在处理的请求完成。
节点独立做出镜像拉取决策。即使你使用串行镜像拉取,两个不同的节点也可以并行拉取相同的镜像。
如果你想启用并行镜像拉取,可以在 kubelet 配置中将 serializeImagePulls
字段设置为 false。将 serializeImagePulls
设置为 false 后,镜像拉取请求将立即发送到镜像服务,并且多个镜像将同时拉取。
启用并行镜像拉取时,请确保容器运行时的镜像服务能够处理并行镜像拉取。
kubelet 永远不会为单个 Pod 并行拉取多个镜像。例如,如果你的 Pod 包含一个 Init 容器和一个应用容器,这两个容器的镜像拉取将不会并行化。但是,如果你有两个使用不同镜像的 Pod,并且启用了并行镜像拉取功能,kubelet 将会为这两个不同的 Pod 并行拉取镜像。
最大并行镜像拉取
Kubernetes v1.32 [beta]
当 serializeImagePulls
设置为 false 时,kubelet 默认对同时拉取的最大镜像数量不设限制。如果你想限制并行镜像拉取的数量,可以在 kubelet 配置中设置 maxParallelImagePulls
字段。将 maxParallelImagePulls
设置为 n,则最多只能同时拉取 n 个镜像,任何超出 n 的镜像拉取都必须等待至少一个正在进行的镜像拉取完成。
当启用并行镜像拉取时,限制并行镜像拉取的数量可以防止镜像拉取消耗过多的网络带宽或磁盘 I/O。
你可以将 maxParallelImagePulls
设置为一个大于或等于 1 的正数。如果你将 maxParallelImagePulls
设置为大于或等于 2,则必须将 serializeImagePulls
设置为 false。kubelet 将因 maxParallelImagePulls
设置无效而无法启动。
带镜像索引的多架构镜像
除了提供二进制镜像外,容器注册中心还可以提供容器镜像索引。镜像索引可以指向多个镜像清单,用于容器的特定架构版本。其思想是你可以为镜像提供一个名称(例如:pause
、example/mycontainer
、kube-apiserver
),并允许不同的系统为它们正在使用的机器架构获取正确的二进制镜像。
Kubernetes 项目通常会为其发布的容器镜像创建名称中包含后缀 -$(ARCH)
的镜像。为了向后兼容,会生成带有后缀的旧镜像。例如,名为 pause
的镜像将是一个包含所有受支持架构的清单的多架构镜像,而 pause-amd64
将是用于旧配置或硬编码镜像名称包含后缀的 YAML 文件的向后兼容版本。
使用私有注册中心
私有注册中心可能需要身份验证才能从中发现和/或拉取镜像。凭据可以通过以下几种方式提供
只有提供自己密钥的 Pod 才能访问私有注册中心。
- 所有 Pod 都可以读取任何配置的私有注册中心。
- 需要集群管理员进行节点配置。
使用 kubelet 凭据提供程序 插件动态获取私有注册中心的凭据
kubelet 可以配置为使用凭据提供程序 exec 插件来获取相应私有注册中心的凭据。
- 所有 Pod 都可以使用节点上缓存的任何镜像。
- 需要对所有节点进行 root 访问才能设置。
供应商特定或本地扩展
如果你正在使用自定义节点配置,你(或你的云提供商)可以实现自己的机制来对节点进行容器注册中心身份验证。
这些选项将在下面详细解释。
在 Pod 上指定 imagePullSecrets
注意
这是运行基于私有注册中心镜像的容器的推荐方法。Kubernetes 支持在 Pod 上指定容器镜像注册中心密钥。所有 imagePullSecrets
都必须是与 Pod 位于同一个命名空间中的 Secret。这些 Secret 必须是 kubernetes.io/dockercfg
或 kubernetes.io/dockerconfigjson
类型。
配置节点以对私有注册中心进行身份验证
设置凭据的具体说明取决于你选择使用的容器运行时和注册中心。你应该参考你的解决方案文档以获取最准确的信息。
有关配置私有容器镜像注册中心的示例,请参阅从私有注册中心拉取镜像任务。该示例使用 Docker Hub 中的私有注册中心。
用于经过身份验证的镜像拉取的 Kubelet 凭证提供者
你可以配置 kubelet 以调用插件二进制文件,从而动态获取容器镜像的注册中心凭证。这是获取私有注册中心凭证最健壮和最通用的方法,但同时也需要 kubelet 级别的配置才能启用。
此技术对于运行需要私有注册中心中托管的容器镜像的静态 Pod 尤其有用。在静态 Pod 的规范中无法使用ServiceAccount 或Secret 提供私有注册中心凭证,因为它的规范中不能引用其他 API 资源。
有关更多详细信息,请参阅配置 kubelet 镜像凭证提供者。
config.json 的解释
config.json
的解释在原始 Docker 实现和 Kubernetes 解释之间有所不同。在 Docker 中,auths
键只能指定根 URL,而 Kubernetes 允许全局 URL 以及前缀匹配的路径。唯一的限制是全局模式(*
)必须为每个子域包含点(.
)。匹配的子域数量必须等于全局模式(*.
)的数量,例如
*.kubernetes.io
将 不 匹配kubernetes.io
,但会匹配abc.kubernetes.io
。*.*.kubernetes.io
将 不 匹配abc.kubernetes.io
,但会匹配abc.def.kubernetes.io
。prefix.*.io
将匹配prefix.kubernetes.io
。*-good.kubernetes.io
将匹配prefix-good.kubernetes.io
。
这意味着这样的 config.json
是有效的
{
"auths": {
"my-registry.example/images": { "auth": "…" },
"*.my-registry.example/images": { "auth": "…" }
}
}
镜像拉取操作会将凭据传递给 CRI 容器运行时,以匹配每个有效模式。例如,以下容器镜像名称将成功匹配
my-registry.example/images
my-registry.example/images/my-image
my-registry.example/images/another-image
sub.my-registry.example/images/my-image
但是,这些容器镜像名称将 不 匹配
a.sub.my-registry.example/images/my-image
a.b.sub.my-registry.example/images/my-image
kubelet 会为每个找到的凭据顺序执行镜像拉取。这意味着 config.json
中针对不同路径的多个条目也是可能的
{
"auths": {
"my-registry.example/images": {
"auth": "…"
},
"my-registry.example/images/subpath": {
"auth": "…"
}
}
}
如果现在容器指定要拉取的镜像为 my-registry.example/images/subpath/my-image
,那么如果其中一个认证源失败,kubelet 将尝试使用这两个认证源下载它。
预拉取镜像
注意
如果你可以控制节点配置,则此方法是合适的。如果你的云提供商管理节点并自动替换它们,则此方法将不可靠。默认情况下,kubelet 尝试从指定的注册中心拉取每个镜像。但是,如果容器的 imagePullPolicy
属性设置为 IfNotPresent
或 Never
,则使用本地镜像(分别为优先或独占)。
如果你想依赖预拉取镜像作为注册中心身份验证的替代方案,你必须确保集群中的所有节点都具有相同的预拉取镜像。
这可以用于预加载某些镜像以提高速度,或者作为对私有注册中心进行身份验证的替代方案。
与使用kubelet 凭据提供程序类似,预拉取镜像也适用于启动依赖于私有注册中心中托管镜像的静态 Pod。
确保镜像拉取凭证验证
如果集群的 KubeletEnsureSecretPulledImages
特性门控已启用,Kubernetes 将验证每个需要凭证才能拉取的镜像的镜像凭证,即使该镜像已存在于节点上。此验证可确保 Pod 请求中未能使用所提供的凭证成功拉取的镜像必须从注册中心重新拉取。此外,如果镜像在本地可用,重新使用相同凭证并已成功拉取镜像的请求将无需从注册中心重新拉取,而是通过本地验证(不访问注册中心)。这由 Kubelet 配置中的 imagePullCredentialsVerificationPolicy
字段控制。
此配置控制当镜像已存在于节点上时,何时必须验证镜像拉取凭证
NeverVerify
:模拟禁用此功能门控的行为。如果镜像在本地存在,则不验证镜像拉取凭证。NeverVerifyPreloadedImages
:不验证在 kubelet 外部拉取的镜像,但所有其他镜像都将验证其凭证。这是默认行为。NeverVerifyAllowListedImages
:不验证在 kubelet 外部拉取并在 kubelet 配置中指定的preloadedImagesVerificationAllowlist
中提到的镜像。AlwaysVerify
:所有镜像在使用前都将验证其凭证。
此验证适用于预拉取镜像、使用节点范围 Secrets 拉取的镜像以及使用 Pod 级别 Secrets 拉取的镜像。
注意
在凭证轮换的情况下,之前用于拉取镜像的凭证将继续验证,而无需访问注册中心。新的或轮换的凭证将需要从注册中心重新拉取镜像。创建带 Docker 配置的 Secret
你需要知道用于向注册中心进行身份验证的用户名、注册中心密码和客户端电子邮件地址,以及其主机名。运行以下命令,将占位符替换为相应的值
kubectl create secret docker-registry <name> \
--docker-server=<docker-registry-server> \
--docker-username=<docker-user> \
--docker-password=<docker-password> \
--docker-email=<docker-email>
如果你已经有 Docker 凭证文件,那么你可以将其作为 Kubernetes Secret 导入,而不是使用上述命令。基于现有 Docker 凭证创建 Secret 解释了如何进行设置。
如果你使用多个私有容器注册中心,这尤其有用,因为 kubectl create secret docker-registry
创建的 Secret 仅适用于单个私有注册中心。
注意
Pod 只能引用其自身命名空间中的镜像拉取 Secret,因此每个命名空间都需要执行一次此过程。在 Pod 上引用 imagePullSecrets
现在,你可以通过在 Pod 定义中添加 imagePullSecrets
部分来创建引用该 Secret 的 Pod。imagePullSecrets
数组中的每个项只能引用同一个命名空间中的一个 Secret。
例如
cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
EOF
cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF
对于每个使用私有注册中心的 Pod,都需要执行此操作。
但是,你可以通过在 ServiceAccount 资源中指定 imagePullSecrets
部分来自动执行此过程。有关详细说明,请参阅向 ServiceAccount 添加 ImagePullSecrets。
你可以将其与每个节点的 .docker/config.json
结合使用。凭据将合并。
用例
有许多用于配置私有注册中心的解决方案。以下是一些常见的用例和建议的解决方案。
- 集群仅运行非专有(例如开源)镜像。无需隐藏镜像。
- 使用公共注册中心的公共镜像
- 无需配置。
- 一些云提供商会自动缓存或镜像公共镜像,这提高了可用性并减少了拉取镜像的时间。
- 使用公共注册中心的公共镜像
- 集群运行一些专有镜像,这些镜像应向公司外部人员隐藏,但对所有集群用户可见。
- 使用托管的私有注册中心
- 可能需要在需要访问私有注册中心的节点上进行手动配置。
- 或者,在防火墙后运行一个具有开放读取权限的内部私有注册中心。
- 无需 Kubernetes 配置。
- 使用控制镜像访问的托管容器镜像注册中心服务
- 与手动节点配置相比,它在节点自动扩缩方面表现更好。
- 或者,在不方便更改节点配置的集群上,使用
imagePullSecrets
。
- 使用托管的私有注册中心
- 带有专有镜像的集群,其中少数需要更严格的访问控制。
- 确保AlwaysPullImages 准入控制器处于活动状态。否则,所有 Pod 都可能访问所有镜像。
- 将敏感数据移动到 Secret 资源中,而不是将其打包到镜像中。
- 一个多租户集群,每个租户都需要自己的私有注册中心。
- 确保AlwaysPullImages 准入控制器处于活动状态。否则,所有租户的所有 Pod 都可能访问所有镜像。
- 运行一个需要授权的私有注册中心。
- 为每个租户生成注册中心凭据,存储到 Secret 中,并将 Secret 传播到每个租户命名空间。
- 然后租户将该 Secret 添加到每个命名空间的
imagePullSecrets
中。
如果你需要访问多个注册中心,可以为每个注册中心创建一个 Secret。
旧版内置 kubelet 凭证提供者
在较旧版本的 Kubernetes 中,kubelet 与云提供商凭据直接集成。这提供了动态获取镜像注册中心凭据的能力。
kubelet 凭证提供程序集成有三个内置实现:ACR (Azure Container Registry)、ECR (Elastic Container Registry) 和 GCR (Google Container Registry)。
从 Kubernetes 1.26 版本开始,旧机制已删除,因此你需要
- 在每个节点上配置 kubelet 镜像凭证提供者;或者
- 使用
imagePullSecrets
和至少一个 Secret 指定镜像拉取凭证。
下一步
- 阅读 OCI 镜像清单规范。
- 了解容器镜像垃圾收集。
- 了解更多关于从私有注册中心拉取镜像的信息。