容器镜像(Image)所承载的是封装了应用程序及其所有软件依赖的二进制数据。 容器镜像是可执行的软件包,可以单独运行;该软件包对所处的运行时环境具有 良定(Well Defined)的假定。
你通常会创建应用的容器镜像并将其推送到某仓库(Registry),然后在 Pod 中引用它。
本页概要介绍容器镜像的概念。
容器镜像通常会被赋予 pause
、example/mycontainer
或者 kube-apiserver
这类的名称。 镜像名称也可以包含所在仓库的主机名。例如:fictional.registry.example/imagename
。 还可以包含仓库的端口号,例如:fictional.registry.example:10443/imagename
。
如果你不指定仓库的主机名,Kubernetes 认为你在使用 Docker 公共仓库。
在镜像名称之后,你可以添加一个标签(Tag)(与使用 docker
或 podman
等命令时的方式相同)。 使用标签能让你辨识同一镜像序列中的不同版本。
镜像标签可以包含小写字母、大写字母、数字、下划线(_
)、句点(.
)和连字符(-
)。 关于在镜像标签中何处可以使用分隔字符(_
、-
和 .
)还有一些额外的规则。 如果你不指定标签,Kubernetes 认为你想使用标签 latest
。
当你最初创建一个 Deployment、 StatefulSet、Pod 或者其他包含 Pod 模板的对象时,如果没有显式设定的话,Pod 中所有容器的默认镜像 拉取策略是 IfNotPresent
。这一策略会使得 kubelet 在镜像已经存在的情况下直接略过拉取镜像的操作。
容器的 imagePullPolicy
和镜像的标签会影响 kubelet 尝试拉取(下载)指定的镜像。
以下列表包含了 imagePullPolicy
可以设置的值,以及这些值的效果:
只要能够可靠地访问镜像仓库,底层镜像提供者的缓存语义甚至可以使 imagePullPolicy: Always 高效。 你的容器运行时可以注意到节点上已经存在的镜像层,这样就不需要再次下载。
在生产环境中部署容器时,你应该避免使用 :latest
标签,因为这使得正在运行的镜像的版本难以追踪,并且难以正确地回滚。
相反,应指定一个有意义的标签,如 v1.42.0
。
为了确保 Pod 总是使用相同版本的容器镜像,你可以指定镜像的摘要; 将 <image-name>:<tag>
替换为 <image-name>@<digest>
,例如 image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
。
当使用镜像标签时,如果镜像仓库修改了代码所对应的镜像标签,可能会出现新旧代码混杂在 Pod 中运行的情况。 镜像摘要唯一标识了镜像的特定版本,因此 Kubernetes 每次启动具有指定镜像名称和摘要的容器时,都会运行相同的代码。 通过摘要指定镜像可固定你运行的代码,这样镜像仓库的变化就不会导致版本的混杂。
有一些第三方的准入控制器 在创建 Pod(和 Pod 模板)时产生变更,这样运行的工作负载就是根据镜像摘要,而不是标签来定义的。 无论镜像仓库上的标签发生什么变化,你都想确保你所有的工作负载都运行相同的代码,那么指定镜像摘要会很有用。
当你(或控制器)向 API 服务器提交一个新的 Pod 时,你的集群会在满足特定条件时设置 imagePullPolicy
字段:
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 分钟)。
除了提供二进制的镜像之外,容器仓库也可以提供 容器镜像索引。 镜像索引可以根据特定于体系结构版本的容器指向镜像的多个 镜像清单。 这背后的理念是让你可以为镜像命名(例如:pause
、example/mycontainer
、kube-apiserver
) 的同时,允许不同的系统基于它们所使用的机器体系结构取回正确的二进制镜像。
Kubernetes 自身通常在命名容器镜像时添加后缀 -$(ARCH)
。 为了向前兼容,请在生成较老的镜像时也提供后缀。 这里的理念是为某镜像(如 pause
)生成针对所有平台都适用的清单时, 生成 pause-amd64
这类镜像,以便较老的配置文件或者将镜像后缀影编码到其中的 YAML 文件也能兼容。
从私有仓库读取镜像时可能需要密钥。 凭证可以用以下方式提供:
设置凭据的具体说明取决于你选择使用的容器运行时和仓库。
Kubernetes 默认仅支持 Docker 配置中的 auths
和 HttpHeaders
部分, 不支持 Docker 凭据辅助程序(credHelpers
或 credsStore
)。
对于 config.json
的解释在原始 Docker 实现和 Kubernetes 的解释之间有所不同。 在 Docker 中,auths
键只能指定根 URL ,而 Kubernetes 允许 glob URLs 以及 前缀匹配的路径。这意味着,像这样的 config.json
是有效的:
{
"auths": {
"*my-registry.io/images": {
"auth": "…"
}
}
}
使用以下语法匹配根 URL (*my-registry.io
):
pattern:
{ term }
term:
'*' 匹配任何无分隔符字符序列
'?' 匹配任意单个非分隔符
'[' [ '^' ] 字符范围
字符集(必须非空)
c 匹配字符 c (c 不为 '*','?','\\','[')
'\\' c 匹配字符 c
字符范围:
c 匹配字符 c (c 不为 '\\','?','-',']')
'\\' c 匹配字符 c
lo '-' hi 匹配字符范围在 lo 到 hi 之间字符
现在镜像拉取操作会将每种有效模式的凭据都传递给 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
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
, 则会优先使用(对应 IfNotPresent
)或者一定使用(对应 Never
)本地镜像。
如果你希望使用提前拉取镜像的方法代替仓库认证,就必须保证集群中所有节点提前拉取的镜像是相同的。
这一方案可以用来提前载入指定的镜像以提高速度,或者作为向私有仓库执行身份认证的一种替代方案。
所有的 Pod 都可以使用节点上提前拉取的镜像。
运行使用私有仓库中镜像的容器时,建议使用这种方法。
Kubernetes 支持在 Pod 中设置容器镜像仓库的密钥。
你需要知道用于向仓库进行身份验证的用户名、密码和客户端电子邮件地址,以及它的主机名。 运行以下命令,注意替换适当的大写值:
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, 而不是执行上面的命令。
如果你在使用多个私有容器仓库,这种技术将特别有用。 原因是 kubectl create secret docker-registry
创建的是仅适用于某个私有仓库的 Secret。
Pod 只能引用位于自身所在名字空间中的 Secret,因此需要针对每个名字空间 重复执行上述过程。
现在,在创建 Pod 时,可以在 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 执行以上操作。 不过,设置该字段的过程也可以通过为 服务账号 资源设置 imagePullSecrets
来自动完成。
你也可以将此方法与节点级别的 .docker/config.json
配置结合使用。 来自不同来源的凭据会被合并。
配置私有仓库有多种方案,以下是一些常用场景和建议的解决方案。
如果你需要访问多个仓库,可以为每个仓库创建一个 Secret。 kubelet
将所有 imagePullSecrets
合并为一个虚拟的 .docker/config.json
文件。