镜像
容器镜像表示封装了应用程序及其所有软件依赖项的二进制数据。容器镜像是可执行的软件包,可以独立运行,并且对其运行时环境做出了非常明确的假设。
您通常会创建应用程序的容器镜像并将其推送到注册表,然后在 Pod 中引用它。
本页概述了容器镜像的概念。
注意
如果您正在寻找 Kubernetes 版本(例如 v1.30,最新的次要版本)的容器镜像,请访问 下载 Kubernetes。镜像名称
容器镜像通常会被赋予一个名称,例如 pause
、example/mycontainer
或 kube-apiserver
。镜像还可以包含注册表主机名;例如:fictional.registry.example/imagename
,并且可能还包含端口号;例如:fictional.registry.example:10443/imagename
。
如果您没有指定注册表主机名,Kubernetes 会假定您指的是 Docker 公共注册表。您可以通过在 容器运行时 配置中设置默认镜像注册表来更改此行为。
在镜像名称部分之后,您可以添加一个*标签*或*摘要*(与使用 docker
或 podman
等命令时的方式相同)。标签允许您识别同一系列镜像的不同版本。摘要是镜像特定版本的唯一标识符。摘要是镜像内容的哈希值,并且是不可变的。标签可以移动以指向不同的镜像,但摘要是固定的。
镜像标签由小写和大写字母、数字、下划线 (_
)、句点 (.
) 和破折号 (-
) 组成。它最多可以包含 128 个字符。并且必须遵循以下正则表达式模式:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}
您可以在 OCI 分发规范 中阅读更多信息并找到验证正则表达式。如果您没有指定标签,Kubernetes 会假定您指的是标签 latest
。
镜像摘要由哈希算法(例如 sha256
)和哈希值组成。例如:sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07
您可以在 OCI 镜像规范 中找到有关摘要格式的更多信息。
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
- 带有标签和摘要的镜像名称。仅使用摘要进行拉取。
更新镜像
当您第一次创建包含 Pod 模板的 Deployment、StatefulSet、Pod 或其他对象时,如果未明确指定,则默认情况下,该 Pod 中所有容器的拉取策略都将设置为 IfNotPresent
。此策略会导致 kubelet 在镜像已存在的情况下跳过拉取镜像。
镜像拉取策略
容器的 imagePullPolicy
和镜像的标签会影响 kubelet 尝试拉取(下载)指定镜像的时间。
以下是您可以为 imagePullPolicy
设置的值列表,以及这些值产生的影响
IfNotPresent
- 仅当镜像在本地不存在时才拉取镜像。
Always
- 每次 kubelet 启动容器时,kubelet 都会查询容器镜像注册表以将名称解析为镜像 摘要。如果 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
值总是在对象第一次*创建*时设置,并且如果镜像的标签或摘要稍后发生更改,则不会更新。
例如,如果您创建了一个 Deployment,其镜像的标签*不是* :latest
,并且稍后将该 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 并行拉取多个镜像。例如,如果您有一个包含 init 容器和应用程序容器的 Pod,则不会并行拉取这两个容器的镜像。但是,如果您有两个使用不同镜像的 Pod,则在启用并行镜像拉取时,kubelet 会代表这两个不同的 Pod 并行拉取镜像。
最大并行镜像拉取数
Kubernetes v1.27 [alpha]
当 serializeImagePulls
设置为 false 时,kubelet 默认情况下对同时拉取的最大镜像数量没有限制。如果您想限制并行镜像拉取的数量,可以在 kubelet 配置中设置 maxParallelImagePulls
字段。将 maxParallelImagePulls
设置为 *n* 后,一次只能拉取 *n* 个镜像,任何超过 *n* 的镜像拉取都必须等到至少一个正在进行的镜像拉取完成后才能进行。
启用并行镜像拉取时,限制并行镜像拉取的数量可以防止镜像拉取消耗过多的网络带宽或磁盘 I/O。
您可以将 maxParallelImagePulls
设置为大于或等于 1 的正数。如果将 maxParallelImagePulls
设置为大于或等于 2,则必须将 serializeImagePulls
设置为 false。如果 maxParallelImagePulls
设置无效,kubelet 将无法启动。
使用镜像索引的多架构镜像
除了提供二进制镜像之外,容器镜像仓库还可以提供 容器镜像索引。镜像索引可以指向容器的特定于架构的版本的多个 镜像清单。这个想法是,您可以为镜像设置一个名称(例如:pause
、example/mycontainer
、kube-apiserver
),并允许不同的系统获取适合其正在使用的机器架构的二进制镜像。
Kubernetes 本身通常使用后缀 -$(ARCH)
来命名容器镜像。为了向后兼容,请使用后缀生成旧镜像。这个想法是生成例如 pause
镜像,它包含所有架构的清单,以及例如 pause-amd64
镜像,它向后兼容可能使用后缀对镜像进行硬编码的旧配置或 YAML 文件。
使用私有镜像仓库
私有镜像仓库可能需要密钥才能从中读取镜像。
可以通过以下几种方式提供凭据
- 配置节点以向私有镜像仓库进行身份验证
- 所有 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 允许使用全局 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.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 都将具有对任何预先拉取的镜像的读取访问权限。
在 Pod 上指定 imagePullSecrets
注意
这是运行基于私有镜像仓库中的镜像的容器的推荐方法。Kubernetes 支持在 Pod 上指定容器镜像仓库密钥。imagePullSecrets
必须与 Pod 位于同一命名空间中。引用的 Secret 必须是 kubernetes.io/dockercfg
或 kubernetes.io/dockerconfigjson
类型。
使用 Docker 配置创建 Secret
您需要知道用于向镜像仓库进行身份验证的用户名、镜像仓库密码和客户端电子邮件地址,以及其主机名。运行以下命令,替换相应的 uppercase 值
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 容器注册表)、ECR(弹性容器注册表)和 GCR(Google 容器注册表)。
有关旧机制的更多信息,请阅读您正在使用的 Kubernetes 版本的文档。Kubernetes v1.26 到 v1.30 不包括旧机制,因此您需要
- 在每个节点上配置 kubelet 镜像凭据提供程序
- 使用
imagePullSecrets
和至少一个 Secret 指定镜像拉取凭据
下一步
- 阅读 OCI 镜像清单规范。
- 了解 容器镜像垃圾回收。
- 详细了解 从私有镜像仓库拉取镜像。