Pod
最后更新于
最后更新于
Pod 是一组紧密关联的容器集合,它们共享 IPC 和 Network namespace,是 Kubernetes 调度的基本单位。Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
Pod 的特征
包含多个共享 IPC 和 Network namespace 的容器,可直接通过 localhost 通信
所有 Pod 内容器都可以访问共享的 Volume,可以访问共享数据
无容错性:直接创建的 Pod 一旦被调度后就跟 Node 绑定,即使 Node 挂掉也不会被重新调度(而是被自动删除),因此推荐使用 Deployment、Daemonset 等控制器来容错
优雅终止:Pod 删除的时候先给其内的进程发送 SIGTERM,等待一段时间(grace period)后才强制停止依然还在运行的进程
特权容器(通过 SecurityContext 配置)具有改变系统配置的权限(在网络插件中大量应用)
Kubernetes v1.8+ 支持容器间共享 PID namespace,需要 docker >= 1.13.1,并配置 kubelet
--docker-disable-shared-pid=false
。在 Kubernetes v1.10+
--docker-disable-shared-pid
已被弃用,如果要共享 PID namespace,需要设置 v1.PodSpec 中的 ShareProcessNamespace 为 true,如下所示
通过 yaml 或 json 描述 Pod 和其内容器的运行环境以及期望状态,比如一个最简单的 nginx pod 可以定义为
在生产环境中,推荐使用 Deployment、StatefulSet、Job 或者 CronJob 等控制器来创建 Pod,而不推荐直接创建 Pod。
目前,Kubernetes 仅支持使用 Docker 镜像来创建容器,但并非支持 Dockerfile 定义的所有行为。如下表所示
Kubernetes 以 PodStatus.Phase
抽象 Pod 的状态(但并不直接反映所有容器的状态)。可能的 Phase 包括
Pending: API Server已经创建该Pod,但一个或多个容器还没有被创建,包括通过网络下载镜像的过程。
Running: Pod中的所有容器都已经被创建且已经调度到 Node 上面,但至少有一个容器还在运行或者正在启动。
Succeeded: Pod 调度到 Node 上面后均成功运行结束,并且不会重启。
Failed: Pod中的所有容器都被终止了,但至少有一个容器退出失败(即退出码不为 0 或者被系统终止)。
Unknonwn: 状态未知,因为一些原因Pod无法被正常获取,通常是由于 apiserver 无法与 kubelet 通信导致。
可以用 kubectl 命令查询 Pod Phase:
PodSpec 中的 restartPolicy
可以用来设置是否对退出的 Pod 重启,可选项包括 Always
、OnFailure
、以及 Never
。比如
单容器的 Pod,容器成功退出时,不同 restartPolicy
时的动作为
Always: 重启 Container; Pod phase
保持 Running.
OnFailure: Pod phase
变成 Succeeded.
Never: Pod phase
变成 Succeeded.
单容器的 Pod,容器失败退出时,不同 restartPolicy
时的动作为
Always: 重启 Container; Pod phase
保持 Running.
OnFailure: 重启 Container; Pod phase
保持 Running.
Never: Pod phase
变成 Failed.
2个容器的 Pod,其中一个容器在运行而另一个失败退出时,不同 restartPolicy
时的动作为
Always: 重启 Container; Pod phase
保持 Running.
OnFailure: 重启 Container; Pod phase
保持 Running.
Never: 不重启 Container; Pod phase
保持 Running.
2个容器的 Pod,其中一个容器停止而另一个失败退出时,不同 restartPolicy
时的动作为
Always: 重启 Container; Pod phase
保持 Running.
OnFailure: 重启 Container; Pod phase
保持 Running.
Never: Pod phase
变成 Failed.
单容器的 Pod,容器内存不足(OOM),不同 restartPolicy
时的动作为
Always: 重启 Container; Pod phase
保持 Running.
OnFailure: 重启 Container; Pod phase
保持 Running.
Never: 记录失败事件; Pod phase
变成 Failed.
Pod 还在运行,但磁盘不可访问时
终止所有容器
Pod phase
变成 Failed
如果 Pod 是由某个控制器管理的,则重新创建一个 Pod 并调度到其他 Node 运行
Pod 还在运行,但由于网络分区故障导致 Node 无法访问
Node controller等待 Node 事件超时
Node controller 将 Pod phase
设置为 Failed.
如果 Pod 是由某个控制器管理的,则重新创建一个 Pod 并调度到其他 Node 运行
Volume 可以为容器提供持久化存储,比如
更多挂载存储卷的方法参考 Volume。
在使用私有镜像时,需要创建一个 docker registry secret,并在容器中引用。
创建 docker registry secret:
比如使用 Azure Container Registry(ACR):
在引用 docker registry secret 时,有两种可选的方法:
第一种是直接在 Pod 描述文件中引用该 secret:
第二种是把 secret 添加到 service account 中,再通过 service account 引用(一般是某个 namespace 的 default service account):
支持三种 RestartPolicy
Always:当容器失效时,由Kubelet自动重启该容器。RestartPolicy的默认值。
OnFailure:当容器终止运行且退出码不为0时由Kubelet重启。
Never:无论何种情况下,Kubelet都不会重启该容器。
注意,这里的重启是指在 Pod 所在 Node 上面本地重启,并不会调度到其他 Node 上去。
环境变量为容器提供了一些重要的资源,包括容器和 Pod 的基本信息以及集群中服务的信息等:
(1) hostname
HOSTNAME
环境变量保存了该 Pod 的 hostname。
(2)容器和 Pod 的基本信息
Pod 的名字、命名空间、IP 以及容器的计算资源限制等可以以 Downward API 的方式获取并存储到环境变量中。
(3) 集群中服务的信息
容器的环境变量中还可以引用容器运行前创建的所有服务的信息,比如默认的 kubernetes 服务对应以下环境变量:
由于环境变量存在创建顺序的局限性(环境变量中不包含后来创建的服务),推荐使用 DNS 来解析服务。
支持三种 ImagePullPolicy
Always:不管本地镜像是否存在都会去仓库进行一次镜像拉取。校验如果镜像有变化则会覆盖本地镜像,否则不会覆盖。
Never:只是用本地镜像,不会去仓库拉取镜像,如果本地镜像不存在则Pod运行失败。
IfNotPresent:只有本地镜像不存在时,才会去仓库拉取镜像。ImagePullPolicy的默认值。
注意:
默认为 IfNotPresent
,但 :latest
标签的镜像默认为 Always
。
拉取镜像时 docker 会进行校验,如果镜像中的 MD5 码没有变,则不会拉取镜像数据。
生产环境中应该尽量避免使用 :latest
标签,而开发环境中可以借助 :latest
标签自动拉取最新的镜像。
通过设置 dnsPolicy 参数,设置 Pod 中容器访问 DNS 的策略
ClusterFirst:优先基于 cluster domain (如 default.svc.cluster.local
) 后缀,通过 kube-dns 查询 (默认策略)
Default:优先从 Node 中配置的 DNS 查询
通过设置 spec.hostIPC
参数为 true,使用主机的 IPC 命名空间,默认为 false。
通过设置 spec.hostNetwork
参数为 true,使用主机的网络命名空间,默认为 false。
通过设置 spec.hostPID
参数为 true,使用主机的 PID 命名空间,默认为 false。
通过 spec.hostname
参数实现,如果未设置默认使用 metadata.name
参数的值作为 Pod 的 hostname。
通过 spec.subdomain
参数设置 Pod 的子域名,默认为空。
比如,指定 hostname 为 busybox-2 和 subdomain 为 default-subdomain,完整域名为 busybox-2.default-subdomain.default.svc.cluster.local
,也可以简写为 busybox-2.default-subdomain.default
:
注意:
默认情况下,DNS 为 Pod 生成的 A 记录格式为 pod-ip-address.my-namespace.pod.cluster.local
,如 1-2-3-4.default.pod.cluster.local
上面的示例还需要在 default namespace 中创建一个名为 default-subdomain
(即 subdomain)的 headless service,否则其他 Pod 无法通过完整域名访问到该 Pod(只能自己访问到自己)
注意,必须为 headless service 设置至少一个服务端口(spec.ports
,即便它看起来并不需要),否则 Pod 与 Pod 之间依然无法通过完整域名来访问。
从 v1.9 开始,可以在 kubelet 和 kube-apiserver 中设置 --feature-gates=CustomPodDNS=true
开启设置每个 Pod DNS 地址的功能。
注意该功能在 v1.10 中为 Beta 版,v1.9 中为 Alpha 版。
对于旧版本的集群,可以使用 ConfigMap 来自定义 Pod 的 /etc/resolv.conf
,如
Kubernetes 通过 cgroups 限制容器的 CPU 和内存等计算资源,包括 requests(请求,调度器保证调度到资源充足的 Node 上,如果无法满足会调度失败)和 limits(上限)等:
spec.containers[].resources.limits.cpu
:CPU 上限,可以短暂超过,容器也不会被停止
spec.containers[].resources.limits.memory
:内存上限,不可以超过;如果超过,容器可能会被终止或调度到其他资源充足的机器上
spec.containers[].resources.limits.ephemeral-storage
:临时存储(容器可写层、日志以及 EmptyDir 等)的上限,超过后 Pod 会被驱逐
spec.containers[].resources.requests.cpu
:CPU 请求,也是调度 CPU 资源的依据,可以超过
spec.containers[].resources.requests.memory
:内存请求,也是调度内存资源的依据,可以超过;但如果超过,容器可能会在 Node 内存不足时清理
spec.containers[].resources.requests.ephemeral-storage
:临时存储(容器可写层、日志以及 EmptyDir 等)的请求,调度容器存储的依据
比如 nginx 容器请求 30% 的 CPU 和 56MB 的内存,但限制最多只用 50% 的 CPU 和 128MB 的内存:
注意
CPU 的单位是 CPU 个数,可以用 millicpu (m)
表示少于 1 个 CPU 的情况,如 500m = 500millicpu = 0.5cpu
,而一个 CPU 相当于
AWS 上的一个 vCPU
GCP 上的一个 Core
Azure 上的一个 vCore
物理机上开启超线程时的一个超线程
内存的单位则包括 E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki
等。
从 v1.10 开始,可以设置 kubelet ----cpu-manager-policy=static
为 Guaranteed(即 requests.cpu 与 limits.cpu 相等)Pod 绑定 CPU(通过 cpuset cgroups)。
为了确保容器在部署后确实处在正常运行状态,Kubernetes 提供了两种探针(Probe)来探测容器的状态:
LivenessProbe:探测应用是否处于健康状态,如果不健康则删除并重新创建容器。
ReadinessProbe:探测应用是否启动完成并且处于正常服务状态,如果不正常则不会接收来自 Kubernetes Service 的流量,即将该Pod从Service的endpoint中移除。
Kubernetes 支持三种方式来执行探针:
exec:在容器中执行一个命令,如果 命令退出码 返回 0
则表示探测成功,否则表示失败
tcpSocket:对指定的容器 IP 及端口执行一个 TCP 检查,如果端口是开放的则表示探测成功,否则表示失败
httpGet:对指定的容器 IP、端口及路径执行一个 HTTP Get 请求,如果返回的 状态码 在 [200,400)
之间则表示探测成功,否则表示失败
Pod 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器。Init 容器在所有容器运行之前执行(run-to-completion),常用来初始化配置。
如果为一个 Pod 指定了多个 Init 容器,那些容器会按顺序一次运行一个。 每个 Init 容器必须运行成功,下一个才能够运行。 当所有的 Init 容器运行完成时,Kubernetes 初始化 Pod 并像平常一样运行应用容器。
下面是一个 Init 容器的示例:
因为 Init 容器具有与应用容器分离的单独镜像,使用 init 容器启动相关代码具有如下优势:
它们可以包含并运行实用工具,出于安全考虑,是不建议在应用容器镜像中包含这些实用工具的。
它们可以包含使用工具和定制化代码来安装,但是不能出现在应用镜像中。例如,创建镜像没必要 FROM 另一个镜像,只需要在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具。
应用镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
它们使用 Linux Namespace,所以对应用容器具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用容器不能够访问。
它们在应用容器启动之前运行完成,然而应用容器并行运行,所以 Init 容器提供了一种简单的方式来阻塞或延迟应用容器的启动,直到满足了一组先决条件。
Init 容器的资源计算,选择一下两者的较大值:
所有 Init 容器中的资源使用的最大值
Pod 中所有容器资源使用的总和
Init 容器的重启策略:
如果 Init 容器执行失败,Pod 设置的 restartPolicy 为 Never,则 pod 将处于 fail 状态。否则 Pod 将一直重新执行每一个 Init 容器直到所有的 Init 容器都成功。
如果 Pod 异常退出,重新拉取 Pod 后,Init 容器也会被重新执行。所以在 Init 容器中执行的任务,需要保证是幂等的。
容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数。支持两种钩子:
postStart: 容器创建后立即执行,注意由于是异步执行,它无法保证一定在 ENTRYPOINT 之前运行。如果失败,容器会被杀死,并根据 RestartPolicy 决定是否重启
preStop:容器终止前执行,常用于资源清理。如果失败,容器同样也会被杀死
而钩子的回调函数支持两种方式:
exec:在容器内执行命令,如果命令的退出状态码是 0
表示执行成功,否则表示失败
httpGet:向指定 URL 发起 GET 请求,如果返回的 HTTP 状态码在 [200, 400)
之间表示请求成功,否则表示失败
postStart 和 preStop 钩子示例:
默认情况下,容器都是以非特权容器的方式运行。比如,不能在容器中创建虚拟网卡、配置虚拟网络。
Kubernetes 提供了修改 Capabilities 的机制,可以按需要给容器增加或删除。比如下面的配置给容器增加了 CAP_NET_ADMIN
并删除了 CAP_KILL
。
可以通过给 Pod 增加 kubernetes.io/ingress-bandwidth
和 kubernetes.io/egress-bandwidth
这两个 annotation 来限制 Pod 的网络带宽
仅 kubenet 支持限制带宽
目前只有 kubenet 网络插件支持限制网络带宽,其他 CNI 网络插件暂不支持这个功能。
kubenet 的网络带宽限制其实是通过 tc 来实现的
可以通过 nodeSelector、nodeAffinity、podAffinity 以及 Taints 和 tolerations 等来将 Pod 调度到需要的 Node 上。
也可以通过设置 nodeName 参数,将 Pod 调度到指定 node 节点上。
比如,使用 nodeSelector,首先给 Node 加上标签:
接着,指定该 Pod 只想运行在带有 disktype=ssd
标签的 Node 上:
nodeAffinity、podAffinity 以及 Taints 和 tolerations 等的使用方法请参考 调度器章节。
默认情况下,容器的 /etc/hosts
是 kubelet 自动生成的,并且仅包含 localhost 和 podName 等。不建议在容器内直接修改 /etc/hosts
文件,因为在 Pod 启动或重启时会被覆盖。
默认的 /etc/hosts
文件格式如下,其中 nginx-4217019353-fb2c5
是 podName:
从 v1.7 开始,可以通过 pod.Spec.HostAliases
来增加 hosts 内容,如
v1.8 + 支持给容器分配 HugePages,资源格式为 hugepages-<size>
(如 hugepages-2Mi
)。使用前要配置
开启 --feature-gates="HugePages=true"
在所有 Node 上面预分配好 HugePage ,以便 Kubelet 统计所在 Node 的 HugePage 容量
使用示例
注意事项
HugePage 资源的请求和限制必须相同
HugePage 以 Pod 级别隔离,未来可能会支持容器级的隔离
基于 HugePage 的 EmptyDir 存储卷最多只能使用请求的 HugePage 内存
使用 shmget()
的 SHM_HUGETLB
选项时,应用必须运行在匹配 proc/sys/vm/hugetlb_shm_group
的用户组(supplemental group)中
从 v1.8 开始,可以为 Pod 设置一个优先级,保证高优先级的 Pod 优先调度。
优先级调度功能目前为 Beta 版,在 v1.11 版本中默认开启。对 v1.8-1.10 版本中使用前需要开启:
--feature-gates=PodPriority=true
--runtime-config=scheduling.k8s.io/v1alpha1=true --admission-control=Controller-Foo,Controller-Bar,...,Priority
为 Pod 设置优先级前,先创建一个 PriorityClass,并设置优先级(数值越大优先级越高):
Kubernetes 自动创建了
system-cluster-critical
和system-node-critical
等两个 PriorityClass,用于 Kubernetes 核心组件。
为 Pod 指定优先级
当调度队列有多个 Pod 需要调度时,优先调度高优先级的 Pod。而当高优先级的 Pod 无法调度时,Kubernetes 会尝试先删除低优先级的 Pod 再将其调度到对应 Node 上(Preemption)。
注意:受限于 Kubernetes 的调度策略,抢占并不总是成功。
PodDisruptionBudget (PDB) 用来保证一组 Pod 同时运行的数量,这些 Pod 需要使用 Deployment、ReplicationController、ReplicaSet 或者 StatefulSet 管理。
Sysctls 允许容器设置内核参数,分为安全 Sysctls 和非安全 Sysctls:
安全 Sysctls:即设置后不影响其他 Pod 的内核选项,只作用在容器 namespace 中,默认开启。包括以下几种
kernel.shm_rmid_forced
net.ipv4.ip_local_port_range
net.ipv4.tcp_syncookies
非安全 Sysctls:即设置好有可能影响其他 Pod 和 Node 上其他服务的内核选项,默认禁止。如果使用,需要管理员在配置 kubelet 时开启,如 kubelet --experimental-allowed-unsafe-sysctls 'kernel.msg*,net.ipv4.route.min_pmtu'
v1.6-v1.10 示例:
从 v1.11 开始,Sysctls 升级为 Beta 版本,不再区分安全和非安全 sysctl,统一通过 podSpec.securityContext.sysctls 设置,如
很多容器都是配置了 UTC 时区,与国内集群的 Node 所在时区有可能不一致,可以通过 HostPath 存储插件给容器配置与 Node 一样的时区:
Kubernetes 版本 | Core API 版本 | 默认开启 |
---|---|---|
Dockerfile 指令 | 描述 | 支持 | 说明 |
---|---|---|---|
v1.5+
core/v1
是
ENTRYPOINT
启动命令
是
containerSpec.command
CMD
命令的参数列表
是
containerSpec.args
ENV
环境变量
是
containerSpec.env
EXPOSE
对外开放的端口
否
使用 containerSpec.ports.containerPort 替代
VOLUME
数据卷
是
使用 volumes 和 volumeMounts
USER
进程运行用户以及用户组
是
securityContext.runAsUser/supplementalGroups
WORKDIR
工作目录
是
containerSpec.workingDir
STOPSIGNAL
停止容器时给进程发送的信号
是
SIGKILL
HEALTHCHECK
健康检查
否
使用 livenessProbe 和 readinessProbe 替代
SHELL
运行启动命令的 SHELL
否
使用镜像默认 SHELL 启动命令