目录
k8s 镜像
镜像名称
容器镜像通常会被赋予 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 可以设置的值,以及这些值的效果:
- IfNotPresent
只有当镜像在本地不存在时才会拉取。 - Always
每当 kubelet 启动一个容器时,kubelet 会查询容器的镜像仓库, 将名称解析为一个镜像摘要。 如果 kubelet 有一个容器镜像,并且对应的摘要已在本地缓存,kubelet 就会使用其缓存的镜像; 否则,kubelet 就会使用解析后的摘要拉取镜像,并使用该镜像来启动容器。 - Never
Kubelet 不会尝试获取镜像。如果镜像已经以某种方式存在本地, kubelet 会尝试启动容器;否则,会启动失败。 更多细节见提前拉取镜像。
只要能够可靠地访问镜像仓库,底层镜像提供者的缓存语义甚至可以使 imagePullPolicy: Always 高效。 你的容器运行时可以注意到节点上已经存在的镜像层,这样就不需要再次下载。
说明:
在生产环境中部署容器时,你应该避免使用 :latest 标签,因为这使得正在运行的镜像的版本难以追踪,并且难以正确地回滚。
相反,应指定一个有意义的标签,如 v1.42.0。
默认镜像拉取策略
当你(或控制器)向 API 服务器提交一个新的 Pod 时,你的集群会在满足特定条件时设置 imagePullPolicy 字段:
- 如果你省略了 imagePullPolicy 字段,并且容器镜像的标签是 :latest, imagePullPolicy 会自动设置为 Always。
- 如果你省略了 imagePullPolicy 字段,并且没有指定容器镜像的标签, imagePullPolicy 会自动设置为 Always。
- 如果你省略了 imagePullPolicy 字段,并且为容器镜像指定了非 :latest 的标签, imagePullPolicy 就会自动设置为 IfNotPresent。
说明:
容器的 imagePullPolicy 的值总是在对象初次 创建 时设置的,如果后来镜像的标签发生变化,则不会更新。
例如,如果你用一个 非 :latest 的镜像标签创建一个 Deployment, 并在随后更新该 Deployment 的镜像标签为 :latest,则 imagePullPolicy 字段 不会 变成 Always。 你必须手动更改已经创建的资源的拉取策略。
ImagePullBackOff
当 kubelet 使用容器运行时创建 Pod 时,容器可能因为 ImagePullBackOff 导致状态为 Waiting。
ImagePullBackOff 状态意味着容器无法启动, 因为 Kubernetes 无法拉取容器镜像(原因包括无效的镜像名称,或从私有仓库拉取而没有 imagePullSecret)。 BackOff 部分表示 Kubernetes 将继续尝试拉取镜像,并增加回退延迟。
Kubernetes 会增加每次尝试之间的延迟,直到达到编译限制,即 300 秒(5 分钟)。
使用私有仓库
从私有仓库读取镜像时可能需要密钥。 凭据可以用以下方式提供:
- 配置节点向私有仓库进行身份验证
- 所有 Pod 均可读取任何已配置的私有仓库
- 需要集群管理员配置节点
- kubelet 凭据提供程序,动态获取私有仓库的凭据
- kubelet 可以被配置为使用凭据提供程序 exec 插件来访问对应的私有镜像库
- 预拉镜像
- 所有 Pod 都可以使用节点上缓存的所有镜像
- 需要所有节点的 root 访问权限才能进行设置
- 在 Pod 中设置 ImagePullSecrets
- 只有提供自己密钥的 Pod 才能访问私有仓库
- 特定于厂商的扩展或者本地扩展
- 如果你在使用定制的节点配置,你(或者云平台提供商)可以实现让节点向容器仓库认证的机制
配置节点向私有仓库进行身份验证
设置凭据的具体说明取决于你选择使用的容器运行时和仓库。 你应该参考解决方案的文档来获取最准确的信息。
有关配置私有容器镜像仓库的示例, 请参阅任务从私有镜像库中拉取镜像。 该示例使用 Docker Hub 中的私有镜像仓库。
说明:
此方法尤其适合 kubelet 需要动态获取仓库凭据时。 最常用于由云提供商提供的仓库,其中身份认证令牌的生命期是短暂的。
你可以配置 kubelet,以调用插件可执行文件的方式来动态获取容器镜像的仓库凭据。 这是为私有仓库获取凭据最稳健和最通用的方法,但也需要 kubelet 级别的配置才能启用。
有关更多细节请参见配置 kubelet 镜像凭据提供程序
config.json 说明
对于 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 都可以使用节点上提前拉取的镜像
在 Pod 上指定 ImagePullSecrets
说明:
运行使用私有仓库中镜像的容器时,建议使用这种方法。
Kubernetes 支持在 Pod 中设置容器镜像仓库的密钥。 imagePullSecrets 必须全部与 Pod 位于同一个名字空间中。 引用的 Secret 必须是 kubernetes.io/dockercfg 或 kubernetes.io/dockerconfigjson 类型
使用 Docker Config 创建 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,因此需要针对每个名字空间重复执行上述过程。
使用案例
配置私有仓库有多种方案,以下是一些常用场景和建议的解决方案。
1.集群运行非专有镜像(例如,开源镜像)。镜像不需要隐藏。
- 使用来自公共仓库的公共镜像
- 无需配置
- 某些云厂商会自动为公开镜像提供高速缓存,以便提升可用性并缩短拉取镜像所需时间
2.集群运行一些专有镜像,这些镜像需要对公司外部隐藏,对所有集群用户可见
- 使用托管的私有仓库
- 在需要访问私有仓库的节点上可能需要手动配置
- 或者,在防火墙内运行一个组织内部的私有仓库,并开放读取权限
- 不需要配置 Kubernetes
- 使用控制镜像访问的托管容器镜像仓库服务
- 与手动配置节点相比,这种方案能更好地处理集群自动扩缩容
- 或者,在不方便更改节点配置的集群中,使用 imagePullSecrets
3.集群使用专有镜像,且有些镜像需要更严格的访问控制
- 确保 AlwaysPullImages 准入控制器被启用。否则,所有 Pod 都可以使用所有镜像。
- 确保将敏感数据存储在 Secret 资源中,而不是将其打包在镜像里
4.集群是多租户的并且每个租户需要自己的私有仓库
- 确保 AlwaysPullImages 准入控制器。否则,所有租户的所有的 Pod 都可以使用所有镜像。
- 为私有仓库启用鉴权
- 为每个租户生成访问仓库的凭据,放置在 Secret 中,并将 Secret 发布到各租户的名字空间下。
租户将 Secret 添加到每个名字空间中的 imagePullSecrets - 如果你需要访问多个仓库,可以为每个仓库创建一个 Secret。