每个运行的容器都是可重复的; 包含依赖环境在内的标准,意味着无论你在哪里运行它都会得到相同的行为。
容器将应用程序从底层的主机设施中解耦。 这使得在不同的云或 OS 环境中部署更加容易。
容器镜像是一个随时可以运行的软件包, 包含运行应用程序所需的一切:代码和它需要的所有运行时、应用程序和系统库,以及一些基本设置的默认值。
根据设计,容器是不可变的:你不能更改已经运行的容器的代码。 如果有一个容器化的应用程序需要修改,则需要构建包含更改的新镜像,然后再基于新构建的镜像重新运行容器。
容器镜像(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
可以设置的值,以及这些值的效果:
IfNotPresent
只有当镜像在本地不存在时才会拉取。Always
每当 kubelet 启动一个容器时,kubelet 会查询容器的镜像仓库, 将名称解析为一个镜像摘要。 如果 kubelet 有一个容器镜像,并且对应的摘要已在本地缓存,kubelet 就会使用其缓存的镜像; 否则,kubelet 就会使用解析后的摘要拉取镜像,并使用该镜像来启动容器。Never
Kubelet 不会尝试获取镜像。如果镜像已经以某种方式存在本地, kubelet 会尝试启动容器;否则,会启动失败。 更多细节见提前拉取镜像。只要能够可靠地访问镜像仓库,底层镜像提供者的缓存语义甚至可以使 imagePullPolicy: Always
高效。 你的容器运行时可以注意到节点上已经存在的镜像层,这样就不需要再次下载。
注意:在生产环境中部署容器时,你应该避免使用
:latest
标签,因为这使得正在运行的镜像的版本难以追踪,并且难以正确地回滚。 相反,应指定一个有意义的标签,如v1.42.0
。
为了确保 Pod 总是使用相同版本的容器镜像,你可以指定镜像的摘要; 将 :
替换为 @
,例如 image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
。
当使用镜像标签时,如果镜像仓库修改了代码所对应的镜像标签,可能会出现新旧代码混杂在 Pod 中运行的情况。 镜像摘要唯一标识了镜像的特定版本,因此 Kubernetes 每次启动具有指定镜像名称和摘要的容器时,都会运行相同的代码。 通过摘要指定镜像可固定你运行的代码,这样镜像仓库的变化就不会导致版本的混杂。
当你(或控制器)向 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 分钟)。
从私有仓库读取镜像时可能需要密钥。 凭证可以用以下方式提供:
容器运行环境是负责运行容器的软件。
Kubernetes 支持许多容器运行环境,例如 containerd、 CRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。
Kubernetes 的容器环境给容器提供了几个重要的资源:
一个容器的 hostname 是该容器运行所在的 Pod 的名称。通过 hostname
命令或者调用 libc 中的 gethostname
函数可以获取该名称。
Pod 名称和命名空间可以通过 下行 API 转换为环境变量。
Pod 定义中的用户所定义的环境变量也可在容器中使用,就像在 container 镜像中静态指定的任何环境变量一样。
创建容器时正在运行的所有服务都可用作该容器的环境变量。 这里的服务仅限于新容器的 Pod 所在的名字空间中的服务,以及 Kubernetes 控制面的服务。
对于名为 foo 的服务,当映射到名为 bar 的容器时,定义了以下变量:
FOO_SERVICE_HOST=<其上服务正运行的主机> FOO_SERVICE_PORT=<其上服务正运行的端口>
服务具有专用的 IP 地址。如果启用了 DNS 插件 , 可以在容器中通过 DNS 来访问服务。
RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。
可以在不同的 Pod 设置不同的 RuntimeClass,以提供性能与安全性之间的平衡。 例如,如果你的部分工作负载需要高级别的信息安全保证,你可以决定在调度这些 Pod 时尽量使它们在使用硬件虚拟化的容器运行时中运行。 这样,你将从这些不同运行时所提供的额外隔离中获益,代价是一些额外的开销。
还可以使用 RuntimeClass 运行具有相同容器运行时但具有不同设置的 Pod。
在节点上配置 CRI 的实现(取决于所选用的运行时)
RuntimeClass 的配置依赖于 运行时接口(CRI)的实现。所有这些配置都具有相应的 handler
名,并被 RuntimeClass 引用。 handler 必须是有效的 DNS 标签名。
创建相应的 RuntimeClass 资源
每个配置都需要有一个用于标识配置的 handler
。 针对每个 handler 需要创建一个 RuntimeClass 对象。RuntimeClass 资源当前只有两个重要的字段:RuntimeClass 名 (metadata.name
) 和 handler (handler
)。对象定义如下所示:
# RuntimeClass 定义于 node.k8s.io API 组
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
# 用来引用 RuntimeClass 的名字
# RuntimeClass 是一个集群层面的资源
name: myclass
# 对应的 CRI 配置的名称
handler: myconfiguration
RuntimeClass 对象的名称必须是有效的 DNS 子域名。
更多关于CRI请看官网
一旦完成集群中 RuntimeClasses 的配置, 你可以在 Pod spec 中指定 runtimeClassName
来使用它。例如:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
runtimeClassName: myclass
# ...
这一设置会告诉 kubelet 使用所指的 RuntimeClass 来运行该 pod。 如果所指的 RuntimeClass 不存在或者 CRI 无法运行相应的 handler, 那么 pod 将会进入 Failed
终止阶段。 你可以查看相应的事件, 获取执行过程中的错误信息。
如果未指定 runtimeClassName
,则将使用默认的 RuntimeHandler,相当于禁用 RuntimeClass 功能特性。