容器镜像代表封装了应用程序及其所有软件依赖项的二进制数据。容器镜像是一种可执行的软件捆绑包,可以独立运行,并对运行环境有非常明确的假设。
通常,您会创建应用程序的容器镜像并将其推送到仓库,然后在 Pod 中引用它。
本页面概述了容器镜像的概念。
容器镜像通常有一个名称,例如 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 分发规范中了解更多信息并查看验证正则表达式。如果您不指定标签,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 — 同时带有标签和摘要的镜像名称。仅摘要将被用于拉取。当您首次创建 Deployment、StatefulSet、Pod 或其他包含 PodTemplate 的对象时,如果未明确指定拉取策略,则默认情况下该 Pod 中所有容器的拉取策略将被设置为 IfNotPresent。此策略导致 kubelet 在镜像已存在时跳过拉取。
容器的 imagePullPolicy 和镜像的标签都会影响 kubelet 何时尝试拉取(下载)指定镜像。
以下是您可以为 imagePullPolicy 设置的值及其影响的列表:
IfNotPresentAlways永不只要仓库可可靠访问,底层镜像提供程序的缓存语义甚至使 imagePullPolicy: Always 也变得高效。您的容器运行时可以检测到镜像层已存在于节点上,因此无需再次下载。
在生产环境中部署容器时,您应该避免使用 :latest 标签,因为这更难追踪正在运行的镜像版本,也更难进行正确的版本回滚。
相反,请指定一个有意义的标签(例如 v1.42.0)和/或摘要。
为了确保 Pod 始终使用相同版本的容器镜像,您可以指定镜像的摘要;将 <image-name>:<tag> 替换为 <image-name>@<digest>(例如,image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2)。
使用镜像标签时,如果镜像仓库更改了该标签所代表的代码,您最终可能会得到混合运行旧代码和新代码的 Pod。镜像摘要唯一标识镜像的特定版本,因此 Kubernetes 每次启动具有该镜像名称和摘要的容器时都会运行相同的代码。通过摘要指定镜像可以锁定您运行的代码,从而防止仓库端的更改导致版本混合。
有一些第三方准入控制器可以在 Pod(及 PodTemplate)创建时对其进行变更,使得运行的工作负载基于镜像摘要而非标签定义。如果您想确保整个工作负载在仓库标签发生更改时始终运行相同的代码,这非常有用。
当您(或控制器)向 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。当 kubelet 使用容器运行时开始为 Pod 创建容器时,容器可能会因为 ImagePullBackOff 而处于 Waiting 状态。
状态 ImagePullBackOff 意味着由于 Kubernetes 无法拉取容器镜像(原因可能是无效的镜像名称,或者在没有 imagePullSecret 的情况下从私有仓库拉取),容器无法启动。BackOff 部分表明 Kubernetes 将继续尝试拉取镜像,且退避延迟会逐渐增加。
Kubernetes 会增加每次尝试之间的延迟,直到达到编译后的限制,即 300 秒(5 分钟)。
Kubernetes v1.29 [alpha](默认禁用)如果您启用 RuntimeClassInImageCriApi 特性门控,kubelet 将通过镜像名称和运行时处理程序的元组(而非仅仅是镜像名称或摘要)来引用容器镜像。您的容器运行时可能会根据所选的运行时处理程序调整其行为。基于运行时类拉取镜像对于基于 VM 的容器(例如 Windows Hyper-V 容器)非常有用。
默认情况下,kubelet 串行拉取镜像。换句话说,kubelet 一次只向镜像服务发送一个镜像拉取请求。其他镜像拉取请求必须等待当前处理的请求完成。
节点独立做出镜像拉取决策。即使您使用串行镜像拉取,两个不同的节点也可以并行拉取同一个镜像。
如果您想启用并行镜像拉取,可以在 kubelet 配置中将 serializeImagePulls 字段设置为 false。当 serializeImagePulls 设置为 false 时,镜像拉取请求将立即发送到镜像服务,并同时拉取多个镜像。
在启用并行镜像拉取时,请确保您的容器运行时的镜像服务能够处理并行拉取。
kubelet 永远不会代表同一个 Pod 并行拉取多个镜像。例如,如果您的 Pod 包含初始化容器和应用程序容器,这两个容器的镜像拉取操作不会并行化。但是,如果您有两个使用不同镜像的 Pod,并且启用了并行镜像拉取功能,kubelet 将会代表这两个不同的 Pod 并行拉取镜像。
Kubernetes v1.35 [stable]当 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 插件。
供应商特定或本地扩展
如果您使用的是自定义节点配置,您(或您的云提供商)可以实现自己的机制,将节点认证到容器仓库。
以下是对这些选项的详细解释。
imagePullSecretsKubernetes 支持在 Pod 上指定容器镜像仓库密钥。所有 imagePullSecrets 必须是与 Pod 处于同一 命名空间中的 Secret。这些 Secret 必须是 kubernetes.io/dockercfg 或 kubernetes.io/dockerconfigjson 类型。
设置凭据的具体说明取决于您选择使用的容器运行时和仓库。您应该查阅解决方案的文档以获取最准确的信息。
有关配置私有容器镜像仓库的示例,请参阅 从私有仓库拉取镜像 任务。该示例使用了 Docker Hub 中的私有仓库。
您可以配置 kubelet 调用插件二进制文件以动态获取容器镜像的仓库凭据。这是获取私有仓库凭据最强大、最通用的方法,但也需要进行 kubelet 级别的配置才能启用。
此技术对于运行需要私有仓库中托管的容器镜像的 静态 Pod 特别有用。在静态 Pod 的规范中无法使用 ServiceAccount 或 Secret 来提供私有仓库凭据,因为它不能在规范中引用其他 API 资源。
详情请参阅 配置 kubelet 镜像凭据提供程序。
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/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-imagekubelet 会为每个找到的凭据按顺序执行镜像拉取。这意味着 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 重启时会有同样的效果,特别是当前容器运行时在节点中缓存的镜像都将被视为预拉取。
您需要知道用于认证仓库的用户名、仓库密码和客户端电子邮件地址,以及其主机名。运行以下命令,用适当的值替换占位符:
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 仅适用于单个私有仓库。
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。
您可以将其与节点级的 .docker/config.json 结合使用。凭据将会合并。
配置私有仓库有多种解决方案。以下是一些常见用例和建议的解决方案。
imagePullSecrets。imagePullSecrets 中。如果您需要访问多个仓库,可以为每个仓库创建一个 Secret。
在较旧的 Kubernetes 版本中,kubelet 与云提供商凭据有直接集成。这提供了动态获取镜像仓库凭据的能力。
kubelet 凭据提供程序集成有三种内置实现:ACR(Azure 容器仓库)、ECR(Elastic 容器仓库)和 GCR(Google 容器仓库)。
从 Kubernetes 1.26 版本开始,该遗留机制已被移除,因此您需要:
imagePullSecrets 和至少一个 Secret 指定镜像拉取凭据。