镜像
容器镜像代表封装了应用程序及其所有软件依赖关系的二进制数据。容器镜像是可以独立运行的可执行软件包,并且对它们的运行时环境做出非常明确的假设。
通常,您会创建应用程序的容器镜像并将其推送到注册表,然后再在 Pod 中引用它。
本页概述了容器镜像的概念。
说明
如果您正在寻找 Kubernetes 发布的容器镜像(例如 v1.35,最新的次要版本),请访问 Download Kubernetes。镜像名称
容器镜像通常被赋予一个名称,例如 pause、example/mycontainer 或 kube-apiserver。镜像还可以包含注册表主机名;例如:fictional.registry.example/imagename,并且可能还包含端口号;例如:fictional.registry.example:10443/imagename。
如果您没有指定注册表主机名,Kubernetes 会假定您指的是 Docker public registry。您可以通过在 container runtime 配置中设置默认镜像注册表来更改此行为。
在镜像名称部分之后,您可以添加一个标签或摘要(就像在使用 docker 或 podman 等命令时一样)。标签允许您识别相同镜像系列的不同版本。摘要是镜像特定版本的唯一标识符。摘要是镜像内容的哈希值,并且是不可变的。标签可以移动以指向不同的镜像,但摘要是固定的。
镜像标签由小写和大写字母、数字、下划线 (_)、句点 (.) 和连字符 (-) 组成。标签的长度可以长达 128 个字符,并且必须符合以下正则表达式模式:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}。您可以在 OCI Distribution Specification 中了解更多信息并找到验证正则表达式。
镜像摘要由哈希算法(例如 sha256)和哈希值组成。例如:sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07。您可以在 OCI Image Specification 中找到有关摘要格式的更多信息。
Kubernetes 可以使用的镜像名称示例包括
busybox— 仅镜像名称,没有标签或摘要。Kubernetes 将使用 Docker public registry 和 latest 标签。等效于docker.io/library/busybox:latest。busybox:1.32.0— 带有标签的镜像名称。Kubernetes 将使用 Docker public registry。等效于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 使用其缓存的镜像;否则,kubelet 拉取具有解析的摘要的镜像,并使用该镜像启动容器。
永不- kubelet 不尝试获取镜像。如果镜像以某种方式已存在于本地,kubelet 尝试启动容器;否则,启动失败。有关更多详细信息,请参阅 预拉取的镜像。
只要注册表可靠可访问,底层镜像提供商的缓存语义即使对于 imagePullPolicy: Always 也是高效的。您的容器运行时可以注意到镜像层已经存在于节点上,因此无需再次下载它们。
说明
您应避免在生产环境中部署容器时使用 :latest 标签,因为它更难跟踪正在运行的镜像版本,并且更难以正确回滚。
相反,请指定有意义的标签,例如 v1.42.0 和/或摘要。
为了确保 Pod 始终使用容器镜像的相同版本,您可以指定镜像的摘要;将 <image-name>:<tag> 替换为 <image-name>@<digest>(例如,image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2)。
在使用镜像标签时,如果镜像注册表更改了该标签所代表的代码,您最终可能会得到运行旧代码和新代码的 Pod 混合体。镜像摘要唯一标识镜像的特定版本,因此 Kubernetes 每次使用指定的镜像名称和摘要启动容器时,都会运行相同的代码。通过摘要指定镜像可以固定您运行的代码,从而防止注册表的更改导致这些版本的混合。
有第三方 admission controllers 会在创建 Pod(和 PodTemplate)时修改 Pod,以便运行的工作负载基于镜像摘要而不是标签来定义。如果您希望确保您的整个工作负载运行相同的代码,无论注册表的标签如何更改,这可能很有用。
默认镜像拉取策略
当您(或控制器)将新的 Pod 提交到 API 服务器时,您的集群在满足特定条件时会设置 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 创建容器时,容器可能处于 Waiting 状态,原因是 ImagePullBackOff。
状态 ImagePullBackOff 表示 Kubernetes 无法拉取容器镜像,因此容器无法启动(原因包括无效的镜像名称,或从没有 imagePullSecret 的私有仓库拉取)。BackOff 部分表示 Kubernetes 将继续尝试拉取镜像,并以递增的退避延迟进行重试。
Kubernetes 会增加每次尝试之间的延迟,直到达到编译时限制,即 300 秒(5 分钟)。
按运行时类拉取镜像
Kubernetes v1.29 [alpha](默认禁用)如果您启用了 RuntimeClassInImageCriApi 功能门,则 kubelet 将通过镜像名称和运行时处理程序的元组而不是仅通过镜像名称或摘要来引用容器镜像。您的 容器运行时 可以根据所选的运行时处理程序调整其行为。基于运行时类拉取镜像对于基于 VM 的容器(例如 Windows Hyper-V 容器)非常有用。
串行和并行镜像拉取
默认情况下,kubelet 串行拉取镜像。换句话说,kubelet 一次只向镜像服务发送一个镜像拉取请求。其他镜像拉取请求必须等到正在处理的请求完成。
节点独立做出镜像拉取决策。即使您使用串行镜像拉取,两个不同的节点也可以并行拉取相同的镜像。
如果您希望启用并行镜像拉取,可以将 serializeImagePulls 字段设置为 false,在 kubelet 配置 中。将 serializeImagePulls 设置为 false 后,镜像拉取请求将立即发送到镜像服务,并且会同时拉取多个镜像。
启用并行镜像拉取时,请确保您的容器运行时的镜像服务可以处理并行镜像拉取。
kubelet 永远不会代表一个 Pod 并行拉取多个镜像。例如,如果您有一个 Pod 包含一个 init 容器和一个应用程序容器,则这两个容器的镜像拉取将不会并行化。但是,如果您有两个 Pod 使用不同的镜像,并且启用了并行镜像拉取功能,则 kubelet 将代表这两个不同的 Pod 并行拉取镜像。
最大并行镜像拉取
Kubernetes v1.35 [stable]当 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,并且必须是类型为 kubernetes.io/dockercfg 或 kubernetes.io/dockerconfigjson。
配置节点以进行私有仓库身份验证
设置凭据的具体说明取决于您选择使用的容器运行时和仓库。您应该参考解决方案的文档以获取最准确的信息。
有关配置私有容器镜像仓库的示例,请参阅 从私有仓库拉取镜像 任务。该示例使用 Docker Hub 中的私有仓库。
用于身份验证镜像拉取的 kubelet 凭据提供程序
您可以配置 kubelet 以调用插件二进制文件,以动态获取容器镜像的仓库凭据。这是获取私有仓库凭据的最可靠和通用的方法,但也需要在 kubelet 级别进行配置才能启用。
此技术对于运行需要托管在私有仓库中的容器镜像的 静态 Pod 尤其有用。使用 ServiceAccount 或 Secret 来提供私有仓库凭据是不可能的,因为静态 Pod 的规范不能引用其他 API 资源。
有关详细信息,请参阅 配置 kubelet 镜像凭据提供程序。
config.json 的解释
原始 Docker 实现和 Kubernetes 解释 config.json 的方式不同。在 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.example/images": { "auth": "…" },
"*.my-registry.example/images": { "auth": "…" }
}
}
镜像拉取操作会将凭据传递给 CRI 容器运行时,以获取每个有效的模式。例如,以下容器镜像名称将成功匹配:
my-registry.example/imagesmy-registry.example/images/my-imagemy-registry.example/images/another-imagesub.my-registry.example/images/my-image
但是,这些容器镜像名称将不会匹配:
a.sub.my-registry.example/images/my-imagea.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。
确保镜像拉取凭据验证
Kubernetes v1.35 [beta](默认启用)如果您的集群启用了 KubeletEnsureSecretPulledImages 功能门控,Kubernetes 将验证需要凭据才能拉取的每个镜像的镜像凭据,即使该镜像已经存在于节点上。此验证可确保 Pod 请求中具有尚未成功使用提供的凭据拉取的镜像必须从注册表重新拉取镜像。此外,重复使用先前成功拉取镜像的相同凭据的镜像拉取无需从注册表重新拉取,而是在本地进行验证,无需访问注册表(前提是该镜像在本地可用)。这由 Kubelet 配置 中的 imagePullCredentialsVerificationPolicy 字段控制。
如果镜像已存在于节点上,此配置控制何时必须验证镜像拉取凭据
NeverVerify:模拟禁用此功能门控时的行为。如果镜像存在于本地,则不会验证镜像拉取凭据。NeverVerifyPreloadedImages:通过 kubelet 外部拉取的镜像不会被验证,但所有其他镜像都将验证其凭据。这是默认行为。NeverVerifyAllowListedImages:通过 kubelet 外部拉取的镜像以及 kubelet 配置中指定的preloadedImagesVerificationAllowlist中提及的镜像不会被验证。AlwaysVerify:所有镜像在使用前都将验证其凭据。
此验证适用于 预先拉取的镜像、使用节点范围 Secret 拉取的镜像以及使用 Pod 级别 Secret 拉取的镜像。
说明
在凭据轮换的情况下,先前用于拉取镜像的凭据将继续验证,无需访问注册表。新的或轮换的凭据需要从注册表重新拉取镜像。首次启用 KubeletEnsureSecretPulledImages
当首次启用 KubeletEnsureSecretPulledImages 时,无论是通过 kubelet 升级还是通过显式启用该功能,如果 kubelet 能够访问任何镜像,这些镜像都将被视为预先拉取的。这是因为在这种情况下,kubelet 没有关于镜像被拉取的记录。kubelet 仅能在首次拉取任何镜像时开始创建镜像拉取记录。
如果这令人担忧,建议在启用该功能之前清理节点上所有不应被视为预先拉取的镜像。
请注意,删除包含镜像拉取记录的目录将对 kubelet 重启产生相同的影响,特别是节点上由容器运行时当前缓存的镜像都将被视为预先拉取的。
使用 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
现在,您可以创建引用该 Secret 的 Pod,方法是在 Pod 定义中添加 imagePullSecrets 部分。 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 准入控制器 处于活动状态。否则,所有 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 镜像清单规范。
- 了解 容器镜像垃圾回收。
- 了解更多关于 从私有注册表拉取镜像 的信息。