镜像
容器镜像代表封装了应用及其所有软件依赖关系的二进制数据。容器镜像是一些可独立运行的可执行软件包,它们对其运行时环境做出了非常明确的假定。
通常,你会创建应用对应的容器镜像并将其推送到仓库,然后在 Pod 中引用它。
本页面提供了容器镜像概念的概述。
说明
如果你在寻找 Kubernetes 发布的容器镜像(例如 v1.33,最新的小版本),请访问 下载 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 或其他包含 Pod 模板的对象时,如果未明确指定拉取策略,则该 Pod 中所有容器的拉取策略默认设置为 IfNotPresent
。此策略会导致 kubelet 在镜像已存在的情况下跳过拉取。
镜像拉取策略
容器的 imagePullPolicy
和镜像的标签影响 kubelet 何时尝试拉取(下载)指定的镜像。
以下是你可以为 imagePullPolicy
设置的值及其效果列表:
IfNotPresent
- 仅在镜像不在本地时拉取。
Always
- 每当 kubelet 启动容器时,它都会查询容器镜像仓库,将名称解析为镜像摘要(digest)。如果 kubelet 本地缓存了具有该确切摘要的容器镜像,则 kubelet 使用其缓存的镜像;否则,kubelet 会拉取具有已解析摘要的镜像,并使用该镜像启动容器。
Never
- kubelet 不会尝试获取镜像。如果镜像以某种方式已存在于本地,kubelet 会尝试启动容器;否则,启动会失败。有关更多详细信息,请参阅预拉取镜像。
只要仓库可靠可访问,底层镜像提供者的缓存语义使得即使 imagePullPolicy: Always
也很高效。你的容器运行时可以注意到镜像层已存在于节点上,因此无需再次下载。
说明
在生产环境中部署容器时,应避免使用 :latest
标签,因为它更难跟踪正在运行的镜像版本,也更难正确回滚。
相反,应指定有意义的标签(例如 v1.42.0
)和/或摘要。
为了确保 Pod 始终使用相同版本的容器镜像,你可以指定镜像的摘要;将 <image-name>:<tag>
替换为 <image-name>@<digest>
(例如,image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
)。
使用镜像标签时,如果镜像仓库更改了标签所代表的代码,你可能会遇到 Pod 运行旧代码和新代码混合的情况。镜像摘要唯一地标识镜像的特定版本,因此 Kubernetes 每次使用指定的镜像名称和摘要启动容器时,都会运行相同的代码。通过摘要指定镜像可以固定你运行的代码,这样仓库的更改就不会导致版本混用。
有些第三方准入控制器在 Pod(和 Pod 模板)创建时会修改它们,以便运行的工作负载基于镜像摘要而不是标签来定义。如果你想确保所有工作负载都运行相同的代码,无论仓库标签如何更改,这可能很有用。
默认镜像拉取策略
当你(或控制器)向 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 HyperV 容器)很有帮助。
串行和并行镜像拉取
默认情况下,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
设置无效而无法启动。
带镜像索引的多架构镜像
除了提供二进制镜像外,容器仓库还可以提供容器镜像索引。镜像索引可以指向容器特定架构版本的多个镜像清单(manifests)。其想法是,你可以为镜像指定一个名称(例如:pause
、example/mycontainer
、kube-apiserver
),并允许不同的系统根据其使用的机器架构获取正确的二进制镜像。
Kubernetes 本身通常使用后缀 -$(ARCH)
为容器镜像命名。为了向后兼容,请生成带有后缀的旧镜像。其想法是生成包含所有架构清单的 pause
镜像,以及为向后兼容旧配置或可能硬编码带有后缀的镜像的 YAML 文件而生成的 pause-amd64
等镜像。
使用私有仓库
私有仓库可能需要密钥才能读取其中的镜像。
可以通过几种方式提供凭据:
- 配置节点向私有仓库进行身份验证
- 所有 Pod 都可以读取任何已配置的私有仓库
- 需要集群管理员配置节点
- Kubelet 凭据提供程序,用于动态获取私有仓库的凭据
- 可以配置 kubelet 为相应的私有仓库使用凭据提供程序 exec 插件。
- 预拉取镜像
- 所有 Pod 都可以使用节点上缓存的任何镜像
- 需要对所有节点具有 root 权限才能设置
- 在 Pod 上指定 ImagePullSecrets
- 只有提供了自身密钥的 Pod 才能访问私有仓库
- 供应商特定或本地扩展
- 如果你使用的是自定义节点配置,你(或你的云供应商)可以实现自己的机制来向容器仓库验证节点的身份。
这些选项将在下面详细解释。
配置节点向私有仓库进行身份验证
设置凭据的具体说明取决于你选择使用的容器运行时和仓库。你应该查阅你的解决方案文档以获取最准确的信息。
有关配置私有容器镜像仓库的示例,请参阅从私有仓库拉取镜像任务。该示例使用 Docker Hub 中的私有仓库。
用于认证镜像拉取的 Kubelet 凭据提供程序
说明
当 kubelet 需要动态获取仓库凭据时,这种方法特别适用。最常用于由云供应商提供的仓库,其身份验证令牌通常是短时有效的。你可以配置 kubelet 调用插件二进制文件来动态获取容器镜像的仓库凭据。这是获取私有仓库凭据最健壮、最通用的方法,但同时也需要进行 kubelet 级别的配置才能启用。
有关更多详细信息,请参阅配置 kubelet 镜像凭据提供程序。
config.json 的解释
config.json
的解释在原始 Docker 实现和 Kubernetes 解释之间有所不同。在 Docker 中,auths
键只能指定根 URL,而 Kubernetes 允许 glob URL 和前缀匹配路径。唯一的限制是 glob 模式 (*
) 必须包含每个子域的句点 (.
)。匹配的子域数量必须等于 glob 模式 (*.
) 的数量,例如:
*.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.io/images": { "auth": "…" },
"*.my-registry.io/images": { "auth": "…" }
}
}
现在,镜像拉取操作将把凭据传递给 CRI 容器运行时,以匹配每个有效的模式。例如,以下容器镜像名称将成功匹配:
my-registry.io/images
my-registry.io/images/my-image
my-registry.io/images/another-image
sub.my-registry.io/images/my-image
但不会匹配:
a.sub.my-registry.io/images/my-image
a.b.sub.my-registry.io/images/my-image
kubelet 会按顺序对找到的每个凭据执行镜像拉取。这意味着 config.json
中也可能存在指向不同路径的多个条目:
{
"auths": {
"my-registry.io/images": {
"auth": "…"
},
"my-registry.io/images/subpath": {
"auth": "…"
}
}
}
如果现在一个容器指定要拉取镜像 my-registry.io/images/subpath/my-image
,那么如果其中一个认证源失败,kubelet 将尝试从两个认证源下载。
预拉取镜像
说明
如果你可以控制节点配置,这种方法是合适的。如果你的云供应商管理节点并自动替换它们,则此方法将不可靠。默认情况下,kubelet 会尝试从指定的仓库拉取每个镜像。但是,如果容器的 imagePullPolicy
属性设置为 IfNotPresent
或 Never
,则会使用本地镜像(分别是优先使用或仅使用)。
如果你想依靠预拉取镜像作为仓库认证的替代方案,你必须确保集群中的所有节点都拥有相同的预拉取镜像。
这可用于预加载某些镜像以提高速度,或作为认证到私有仓库的替代方案。
在 Pod 上指定 ImagePullSecrets
说明
这是运行基于私有仓库镜像的容器的推荐方法。Kubernetes 支持在 Pod 上指定容器镜像仓库密钥。所有 imagePullSecrets
必须与 Pod 位于同一命名空间中。引用的 Secret 必须是 kubernetes.io/dockercfg
或 kubernetes.io/dockerconfigjson
类型。
确保镜像拉取凭据已验证
Kubernetes v1.33 [alpha]
(默认禁用)如果启用了 KubeletEnsureSecretPulledImages
特性门控,Kubernetes 将对每个需要凭据才能拉取的镜像验证其凭据,即使该镜像已存在于节点上。此验证可确保 Pod 请求中未使用提供的凭据成功拉取的镜像必须从仓库重新拉取。此外,如果之前使用相同凭据成功拉取过镜像,则无需从仓库重新拉取,而是在本地进行验证,无需访问仓库(前提是镜像在本地可用)。这由 Kubelet 配置中的 imagePullCredentialsVerificationPolicy
字段控制。
此配置控制当镜像已存在于节点上时,何时必须验证镜像拉取凭据:
NeverVerify
:模拟禁用此特性门控时的行为。如果镜像存在于本地,则不验证镜像拉取凭据。NeverVerifyPreloadedImages
:kubelet 外部拉取的镜像不验证,但所有其他镜像会验证其凭据。这是默认行为。NeverVerifyAllowListedImages
:在 kubelet 配置中指定的preloadedImagesVerificationAllowlist
中提及的、且在 kubelet 外部拉取的镜像不验证。AlwaysVerify
:所有镜像在使用前都会验证其凭据。
此验证适用于 预先拉取的镜像、使用节点范围 Secret 拉取的镜像,以及使用 Pod 级别 Secret 拉取的镜像。
说明
在凭证轮换的情况下,之前用于拉取镜像的凭证将继续有效,无需访问注册表进行验证。新的或轮换后的凭证将需要从注册表中重新拉取镜像。使用 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 只能引用其自身命名空间中的 image pull 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,可以自动完成此字段的设置。
查看 将 ImagePullSecrets 添加到 ServiceAccount 以获取详细说明。
你可以将此与每个节点的 .docker/config.json
结合使用。凭证将被合并。
用例
配置私有注册表有多种解决方案。以下是一些常见用例和建议的解决方案。
- 集群仅运行非专有(例如开源)镜像。无需隐藏镜像。
- 从公共注册表使用公共镜像
- 无需配置。
- 一些云提供商会自动缓存或镜像公共镜像,这提高了可用性并减少了拉取镜像的时间。
- 从公共注册表使用公共镜像
- 集群运行一些专有镜像,这些镜像对公司外部人员隐藏,但对所有集群用户可见。
- 使用托管的私有注册表
- 在需要访问私有注册表的节点上可能需要手动配置
- 或者,在你防火墙后运行一个具有开放读取权限的内部私有注册表。
- 无需 Kubernetes 配置。
- 使用控制镜像访问的托管式容器镜像注册表服务
- 与手动节点配置相比,它更适用于节点自动扩缩容。
- 或者,在更改节点配置不方便的集群上,使用
imagePullSecrets
。
- 使用托管的私有注册表
- 带有专有镜像的集群,其中少数镜像需要更严格的访问控制。
- 确保 AlwaysPullImages Admission Controller 处于活动状态。否则,所有 Pod 都可能拥有访问所有镜像的权限。
- 将敏感数据放入 "Secret" 资源中,而不是将其打包在镜像中。
- 多租户集群,其中每个租户需要自己的私有注册表。
- 确保 AlwaysPullImages Admission Controller 处于活动状态。否则,所有租户的所有 Pod 都可能拥有访问所有镜像的权限。
- 运行需要授权的私有注册表。
- 为每个租户生成注册表凭证,放入 Secret 中,并将 Secret 填充到每个租户的命名空间。
- 租户将该 Secret 添加到每个命名空间的 imagePullSecrets 中。
如果你需要访问多个注册表,你可以为每个注册表创建一个 Secret。
遗留的内置 kubelet 凭证提供程序
在旧版本的 Kubernetes 中,kubelet 与云提供商凭证直接集成。这使其能够动态地获取镜像注册表的凭证。
kubelet 凭证提供程序集成的内置实现有三种:ACR (Azure 容器注册表)、ECR (Elastic Container Registry) 和 GCR (Google Container Registry)。
有关遗留机制的更多信息,请阅读你正在使用的 Kubernetes 版本的文档。Kubernetes v1.26 到 v1.33 不包含此遗留机制,因此你需要:
- 在每个节点上配置 kubelet 镜像凭证提供程序
- 使用
imagePullSecrets
和至少一个 Secret 指定镜像拉取凭证
下一步
- 阅读 OCI 镜像清单规范。
- 了解 容器镜像垃圾收集。
- 进一步了解 从私有注册表拉取镜像。