扒一扒k8s及ocp运行时及pod生命周期那些事~

一、k8s中容器运行时的选择问题

Kubelet 的职责在于通过 RPC 管理容器的生命周期,实现容器生命周期的钩子,以及存活和健康监测,执行 Pod 的重启策略等。

Pod是k8s调度的最小单位,pod由一组应用容器组成,其中包含了共有的环境和资源约束。在 CRI 里,这个环境被称为 PodSandbox。

在启动 Pod 之前,Kubelet 调用 RuntimeService.RunPodSandbox 来创建环境。

这一过程包括为 Pod 设置网络(分配 IP);

PodSandbox 激活之后,就可以独立的创建、启动、停止和删除不同的容器了;

Sandbox(沙箱)即容器本身,是容器而非pod的API,之所以叫PodSandbox是因为创建的沙箱(容器)用于部署pod;

Kubelet 会在停止和删除 PodSandbox 之前首先停止和删除其中的容器。

1、 主流容器运行时:dockershim、Containerd与CRI-O对比

Docker

对Docker来说,负责响应这个请求的就是一个叫作 dockershim 的组件,它把 CRI 请求里的内容拿出来,然后组装成 Docker API 请求发给 Docker Daemon。对于别的容器项目来说,扮演shim角色的组件统称为CRI-shim,可见,CRI-shim就是实现CRI中定义的每一个接口,然后把具体的 CRI 请求“翻译”成对后端容器项目的请求或者操作。

docker-shim

注意一点的是,我们在部署docker时并没有部署这个docker-shim组件,docker-shim代码是包含在kubelet中的,但是其它的容器项目,就需要在hosts中部署自己的CRI-shim了。

Docker项目是C/S架构,Docker client端发送请求给docker daemon采用root用户运行在host上,通过socket进行通信,docker daemon接收创建容器的请求再发送给containerd,容器进程是docker daemon的子进程,而不是dockerCLI的子进程。

可以通过查看进程树

# pstree -p
systemd(1)-+-agetty(3596)
           |-crond(25481)
           |-dbus-daemon(3498)
           |-dhclient(3744)
           |-dockerd(8050)-+-containerd(8062)-+-containerd-shim(7104)-+-pause(7123)
           |               |                  |                       |-{containerd-shim}(7105)
           |               |                  |                       |-{containerd-shim}(7106)
           |               |                  |                       |-{containerd-shim}(7107)
           |               |                  |                       |-{containerd-shim}(7108)
           |               |                  |                       |-{containerd-shim}(7109)
           |               |                  |                       |-{containerd-shim}(7110)
           |               |                  |                       |-{containerd-shim}(7111)
           |               |                  |                       |-{containerd-shim}(7112)
           |               |                  |                       |-{containerd-shim}(7152)
           |               |                  |                       `-{containerd-shim}(8816)
           |               |                  |-containerd-shim(7327)-+-containerd(17056)-+-{containerd}(17061)
           |               |                  |                       |                   |-{containerd}(17062)
           |               |                  |                       |                   |-{containerd}(17063)
           |               |                  |                       |                   |-{containerd}(17064)
           |               |                  |                       |                   |-{containerd}(17067)
           |               |                  |                       |                   `-{containerd}(17068)

CRI-containerd

CNCF的containerd就是充当了CRI-shim的角色,kubelet调用CRI,containerd响应CRI请求,进而调用runc创建容器,runc才是真正通过设置namesapce,cgroup,chroot的创建容易的幕后英雄。

CRI-O的范围

CRI-O的目的是提供一种在OCI一致的运行时和kubelet之间的集成方式,它实现了kubelet容器运行时的接口,CRI-O的范围与CRI的范围相关。

从更高的角度来看,CRI-O能够在一下功能发展的更加严格规范:
支持更多image的格式包括目前使用的docker image的格式;
支持更多的方式来下载image包括对image的可信和验证;
容器镜像管理(管理image的层,文件系统);
容器进程的生命周期管理;
CRI所需求的monitoring和logging;
CRI需求的资源隔离

CRI-O 目前支持的功能

  1. Pod和container的lifecycle
  2. Image lifecycle
  3. CNI网络集成
  4. Logging
  5. Exec(sync/streaming)
  6. Attach/Detach
  7. Port forwarding
  8. OOM 检测和汇报
  9. daemon 重启
  10. 支持多种存储插件(overlay,devicemapper,aufs,btrfs)
  11. Selinux
  12. Seccomp
  13. 清除容器
  14. 支持runc
  15. Gpg check on image pull
  16. Mixed runtimes (runc and Clear Containers)

 2、为什么 CRI 是围绕容器进行的?

Kubernetes 有一个 Pod 资源的接口。CRI开发团队曾经可能采用的一个 CRI 的设计就是抽象复用 Pod 对象,容器运行时就可以自行实现自己的控制逻辑和状态转换,这样一来,就能极大地简化 API,让 CRI 能够更广泛的适用于多种容器运行时。但是CRI开发团队经过深入讨论之后,放弃了这一想法。

首先,Kubelet 有很多的 Pod 级功能和机制(例如循环崩溃的处理),交给容器运行时实现的话,会造成很重的负担;第二,更重要的是,Pod 标准还在高速前进。很多的新功能(例如容器初始化)是由 Kubelet 直接管理容器的,而无需容器运行时进行变更。

CRI 选择了围绕容器进行实现,这样容器运行时能够共享这些通用特性,获得更好的开发进度

二、Pod 的生命周期

Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者被终止。

Pod 自身不具有自愈能力。如果 Pod 被调度到某节点而该节点之后失效, Pod 会被删除;类似地,Pod 无法在因节点资源耗尽或者节点维护而被驱逐期间继续存活。

1、Pod的唯一标识UID

任何给定的 Pod (由 UID 定义)从不会被“重新调度(rescheduled)”到不同的节点; 相反,这一 Pod 可以被一个新的、几乎完全相同的 Pod 替换掉。 如果需要,新 Pod 的名字可以不变,但是其 UID 会不同。

openshift环境下可以通过下面命令查询出pod的uid

oc get po gange3-system-67bd9b5884-822fc  -oyaml|grep uid

uid: 4bf83ef1-0992-4782-a934-174e5b81c00a

2、Pod 就绪态的状态

命令 kubectl patch 不支持修改对象的状态。 如果需要设置 Pod 的 status.conditions,应用或者 Operators 需要使用 PATCH 操作。你可以使用 Kubernetes 客户端库之一来编写代码, 针对 Pod 就绪态设置定制的 Pod 状况。

对于使用定制状况的 Pod 而言,只有当下面的陈述都适用时,该 Pod 才会被评估为就绪:

  • Pod 中所有容器都已就绪;
  • readinessGates 中的所有状况都为 True 值。

当 Pod 的容器都已就绪,但至少一个定制状况没有取值或者取值为 False, kubelet 将 Pod 的状况设置为 ContainersReady。

3、Pod内容器探针探测类型

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

livenessProbe

指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success

readinessProbe

指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure 如果容器不提供就绪态探针,则默认状态为 Success

startupProbe

指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器, 而容器依其重启策略进行重启 如果容器没有提供启动探测,则默认状态为 Success

4、常见pod的状态转换

Pod的容器数

Pod当前状态

发生的事件

Pod结果状态

 

 

 

 

 

RestartPolicy=Always

RestartPolicy=OnFailure

RestartPolicy=Never

包含一个容器

Running

容器成功退出

Running

Succeeded

Succeeded

包含一个容器

Running

容器失败退出

Running

Running

Failure

包含两个容器

Running

1个容器失败退出

Running

Running

Running

包含两个容器

Running

容器被OOM杀掉

Running

Running

Failure

5、容器运行时内存超出限制

  • 容器以失败状态终止。
  • 记录 OOM 事件。
  • 如果restartPolicy为:
    • Always:重启容器;Pod phase 仍为 Running。
    • OnFailure:重启容器;Pod phase 仍为 Running。
    • Never: 记录失败事件;Pod phase 仍为 Failed。

三、实战篇-OpenShift4节点运行容器过程

OpenShift 4的集群节点使用了基于CRI-O的容器运行环境。每个节点的kubelet通过gRPC调用CRI-O,而CRI-O运行符合OCI规范的容器。

1、Master给节点上的kubelet服务发送来一个启动一个新pod的请求

systemctl status kubelet|grep kubelet.conf

           └─2303 kubelet --config=/etc/kubernetes/kubelet.conf --bootstrap-kubeconfig=/etc/kubernetes/kubeconfig --kubeconfig=/var/lib/kubelet/kubeconfig --container-runtime=remote --container-runtime-endpoint=/var/run/crio/crio.sock --runtime-cgroups=/system.slice/crio.service --node-labels=node-role.kubernetes.io/worker,node.openshift.io/os_id=rhcos --node-ip=10.19.59.97 --minimum-container-ttl-duration=6m0s --volume-plugin-dir=/etc/kubernetes/kubelet-plugins/volume/exec --cloud-provider= --hostname-override= --pod-infra-container-image=quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:a978dee2cb368c8d5ec2682d0bc8b23f1b1282b15c3abf03b087006f97aad6f8 --system-reserved=cpu=500m,memory=5.5Gi --v=2

2、kubelet将请求通过CRI转发给CRI-O守护进程来启动新的POD

查看crictl的配置,缺省crictl连接的是该节点本地的“unix:///var/run/crio/crio.sock”

cat /etc/crictl.yaml

runtime-endpoint: "unix:///var/run/crio/crio.sock"

timeout: 0

debug: false

查看crio运行环境的配置文件中的“相关”参数

cat /etc/crio/crio.conf | grep -v "#"  | sed '/^$/d'

[crio]

[crio.runtime]

selinux = true

[crio.network]

plugin_dirs = [

"/usr/libexec/cni",

]

[crio.metrics]

enable_metrics = true

metrics_port = 9537

查看crio系统服务的配置文件

more /usr/lib/systemd/system/crio.service

[Unit]

Description=Container Runtime Interface for OCI (CRI-O)

……………

[Service]

Type=notify

EnvironmentFile=-/etc/sysconfig/crio

Environment=GOTRACEBACK=crash

ExecStart=/usr/bin/crio \

          $CRIO_CONFIG_OPTIONS \

          $CRIO_RUNTIME_OPTIONS \

          $CRIO_STORAGE_OPTIONS \

          $CRIO_NETWORK_OPTIONS \

          $CRIO_METRICS_OPTIONS

ExecReload=/bin/kill -s HUP $MAINPID

TasksMax=infinity

LimitNOFILE=1048576

LimitNPROC=1048576

LimitCORE=infinity

OOMScoreAdjust=-999

TimeoutStartSec=0

Restart=on-abnormal



用户可以设置的 oom_score_adj,范围是 -1000到 1000

对于oom_score_adj参数是-999的表示kubernetes永远不会因为OOM而被kill掉(最后被删掉,配置-1000则linux永远不会回收)。

3、查看crio服务的运行状态,ciro是一个守护进程

“MCO environment configuration”可以看出crio的配置是由OpenShiftMCO控制的。

systemctl status crio

4、CRI-O使用从容器镜像仓库中拉取Image

5、下载的Image会被解压到容器的根文件系统中

6、OCI Runtime 之 runc相关查询命令

在为容器创建了根文件系统后,CRI-O会生成一个OCI运行时规范json文件,描述如何使用OCI生成工具运行容器。CRI-O 使用该规范启动 OCI 兼容运行时以运行容器进程。默认的 OCI Runtime 是 runc

a、使用runc list可以列出当前环境中所有的container,ID表示containerID PID表示容器的进程ID,其中stopped的容器进程ID为0。

runc list|head -5

 b、通过containerID我们可以查看container的监控进程:

ps -ef | grep 0095730803c5c0368a69e18a8167a85d091b705d8c8d5733c05e1a6a3075ceed

root        5728       1  0 Feb18 ?        00:00:00 /usr/bin/conmon -b /run/containers/storage/overlay-containers/0095730803c5c0368a69e18a8167a85d091b705d8c8d5733c05e1a6a3075ceed/userdata

7、conmon(container monitor)进程

每个容器由一个单独的conmon(container monitor)进程监控。

它处理容器的日志,并记录容器进程的退出代码。

conmon会负责为每一个container起一个进程来监控容器额定运行,从它的启动参数我们可以看出,它会监控容器的log,socket信息,退出信息,还可以通过pidfile查看监控进程所监控的容器的服务进程ID:

a、执行以下命令查找test-jcjg-gange-2-dev-oracle项目中system相关的Pod相关信息

crictl pods --namespace test-jcjg-gange-2-dev-oracle

MCD_POD_NAME=gange3-system-6f85d4b6c8-h2mgc

MCD_POD_ID=fd68b752333a6

MCD_FULL_POD_ID=$(crictl inspectp $MCD_POD_ID | jq .status.id | cut -d "\"" -f 2)

b、根据MCD_FULL_POD_ID查找该Pod包含的Container。可以看到该Pod中有1个容器,名称是

crictl ps -p $MCD_FULL_POD_ID

CONTAINER           IMAGE                                                                                                                                           CREATED             STATE               NAME                ATTEMPT             POD ID

16c23fd258aec       registry-dev.hisense.com/test-jcjg-gange-2-dev-oracle/gange3-system@sha256:a9302fc3dd79ddd5146c32d00826426f5ead70706e9346d2f2489d81d5144257   7 minutes ago       Running             gange3-system      0                   fd68b752333a6

c、查询系统中同时包括conmon和machine-config-daemon的进程。可以看到有1个“/usr/bin/conmon”进程,它们使用的参数对应了上一步的1个容器和Pod本身。

ps -ef | grep $MCD_POD_NAME | grep conmon

root     3893409       1  0 06:41 ?        00:00:02 /usr/bin/conmon -b /run/containers/storage/overlay-containers/16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50/userdata -c 16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50 --exit-dir /var/run/crio/exits -l /var/log/pods/test-jcjg-gange-2-dev-oracle_gange3-system-6f85d4b6c8-h2mgc_4bf83ef1-0992-4782-a934-174e5b81c00a/gange3-system/0.log --log-level info -n k8s_gange3-system_gange3-system-6f85d4b6c8-h2mgc_test-jcjg-gange-2-dev-oracle_4bf83ef1-0992-4782-a934-174e5b81c00a_0 -P /run/containers/storage/overlay-containers/16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50/userdata/conmon-pidfile -p /run/containers/storage/overlay-containers/16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50/userdata/pidfile --persist-dir /var/lib/containers/storage/overlay-containers/16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50/userdata -r /usr/bin/runc --runtime-arg --root=/run/runc --socket-dir-path /var/run/crio -u 16c23fd258aec82ac1feb662849a10d2ffdf17e027021d562b1e04f32343ad50 -s

格式:namespace + podname + uid

8、pod的网络是通过使用CNI来设置的,所以任何CNI插件都可以和CRI-O一起使用

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

力哥讲技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值