宜信容器云是一套基于 kubernetes 的容器管理平台。业务线用户在容器云上部署应用程序时,常常会遇到容器无法启动或者应用程序运行不正常的情况。为了方便用户排查在应用上云过程中的问题,我们在 web 端集成了一系列的排错方式,如下图:
一、终端信息
终端信息查看的是容器实例运行时的标准输出日志。
效果等同于:kubectl logs PODNAME [-c CONTAINER]
基本原理如下图:
应用部署时,所属节点的 kubelet 通过 grpc 调用容器运行时接口(container runtime interface),来请求 docker 守护进程创建容器运行时。
此时,docker 守护进程会创建一个协程来接收容器运行时的标准输出日志,这个协程最终将 STDOUT(标准输出)的日志写到容器运行时所在节点的对应目录下:/var/lib/docker/containers/container_id/{container_id-json.log}
如下图:
在 web 端查看对应实例的终端信息时,kubelet 将接收的 Api-server 请求转化成 docker client 来请求 docker 守护进程。Docker 守护进程到相应的目录下读取对应容器的日志文件数据,再由 kubelet 返回日志数据到 Api-server,最终显示到 web 端,供用户查看。
容器日志的生命周期与容器的生命周期一致,容器销毁后,其相关的日志文件也会销毁。
二、events
events 是 kubelet 用来记录容器启动及运行过程中的事件。
效果等同于:kubectl get events
同样,当使用 kubectl describe pod 来查看 pod 时,也一样能看到与该 pod 相关的 events,从这些信息中可以很清楚看到事件的状态变化,从而获知 pod 启动失败的多种原因。比如:
1)没有可用的 node 供调度,如调度的节点资源不够;
2)健康状态检查失败;
3)拉取镜像失败,如下图:
events 的基本实现如下图:
events 中包含事件相关的类型、原因、来源、消息等,会在 kubelet 和 controller manager 等组件中生成,广播出去后,经过一系列的函数过滤、聚合等,再发送给 Api-server 存到 etcd 中。当 web 端查看 events 事件时,请求 Api-server 读取 etcd 中相应的事件,并返回显示,供用户查看异常参数、错误状态等。
三、web terminal
web terminal 可提供一个交互式的界面 shell,可执行各种命令。
效果等同于:kubectl exec -it <podName> -c <containerName> bash
web 端显示如图:
实现如下:
web terminal 主要是通过 websocket 技术实现的,前端交互界面使用的是开源项目 container-terminal(https://github.com/kubernetes-ui/container-terminal),其提供了一个容器的TTY(虚拟终端)。
当查看 web terminal 时,前端 web 发起了一个 websocket 请求,到 Api-server。再由所属节点的 kubelet 响应该 Api-server 的请求,并与容器运行时建立连接。
之所以 kubelet 能够与容器运行时建立连接,是因为 kubelet 定义了一个 CRI 规范中的 RuntimeServiceClient 接口,而容器运行时中的 RuntimeServiceServer(即 Streaming Server,提供了 streaming API)实现了该接口。
kubelet 和容器运行时建立连接后,kubelet 返回请求,Api-server 将请求升级为 SPDY(SPDY 允许在单个的 TCP 请求中复用独立的 STDIN/STDOUT/STDERR),并将 WS 的流映射到 SPDY 相应的标准流上,便与目标容器运行时 Streaming Server 建立了流,Api-server 便实现了 web 与容器运行时的数据交互。
此时,在 web 端输入命令,下发执行完后,可看到返回的结果,如此便实现了交互。
web terminal 提供了进入容器的便利,用户可以执行任何操作,为了安全,我们做了必要的安全措施:
1)记录了用户的操作命令。
待用户输入命令后,记录操作,作为安全审计。
2)生产环境使用普通用户进入容器。
即在 exec 进入容器时的命令/bin/bash -i
更改为/bin/bash –c chmod -R 777 $KUBERNETES_FILELOGS;useradd spider > /dev/null 2>&1;su spider
,其中环境变量 $KUBERNETES_FILELOGS 为在容器创建时需要赋权的文件目录。主要是防止用户误操作,删除存储挂载等。
四、debug 容器
debug 容器是通过工具容器来对业务容器排障。
在使用 web terminal 来调试应用程序的过程中,业务线用户经常需要各式各样的命令来调试程序。之前的解决方案要么是给业务线定制他们所需的基础镜像,尽量涵盖多的所需命令,要么就是在业务线用户构建镜像时在 Dockerfile 中添加命令。
但是,因为业务线众多,定制基础镜像工作量过大;而在构建业务镜像时添加过多命令,又操作繁琐,并可能会带来安全隐患。这些解决方案实际上都不符合容器技术的实践原则–尽可能构建最简容器镜像,而精简后的镜像又极度缺失所需的命令工具。
鉴于存在这样的矛盾,我们集成并改造了 kubectl-debug(https://github.com/aylei/kubectl-debug)这个插件。容器实质上是由cgroup和namespace限制的一组进程,只要能够加入到这个进程的各项namespace,就可实现交互。因此,debug容器的基本思路是:启动一个包含众多排障工具命令的容器,来加入到业务容器的namespace中,便能够在工具容器中实现对业务容器的排障。
效果类似于:
docker run -it --network=container:<container_ID> --pid=container:<container_ID> --ipc=container :<container_ID> -v /log/container _ID:/debugviewlogs <image>
web 端显示如下图:
debug 容器原理如下图:
将 Debug-agent 以 DaemonSet 的形式部署到 kubernetes 集群的所有节点中,并挂载了宿主机的/var/docker/docker.sock,实现与 docker daemon 的通信。如上图的步骤:
1)web 端提供 pod 的 cluster、namespace、podname 信息,向后端服务 Backend server 发起 websocket 请求;
2)后端服务 Backend server 接收到请求后,向 Api-server 验证该 pod 是否存在,并返回 pod 所在的宿主机 Node 和 pod 的容器信息,根据状态判断是否可以 debug;
注意:如果 pod 的状态 reason 是 CrashLoopBackOff,那么 Backend server 将会请求 Api-server 让 kubelet 复制一个 pod, 复制的 Pod 被改写了启动命令(sleep)、去掉了 label 及健康检查。后续 debug 操作是对复制后 pod 进行的。
3)Backend server 传递 debug 的 pod 信息,发起 debug 请求(升级的 SPDY 请求,映射了 WS 的标准流)。
4)Debug-agent 收到请求后,开始拉取 debug 工具镜像,进而创建一个 debug 容器,并将 debug 容器的各个 namespace 设置为目标业务容器 app 的 namespace。再将宿主 Node 的目录/log/ 挂载到 debug 容器的目录/debugviewlogs 中,便可实现将 debug 容器中生成的文件在 web 端下载。如下两图:
创建完 debug 容器后,Debug-agent 将 Backend server 的 SPDY 请求中继到 debug 容器。debug 容器将 SPDY 的标准流 attach 到业务容器中。如此,web 端便可与 debug 容器实现交互。在 debug 操作结束后,Debug-agent 便会将 debug 容器清理回收。同样的,debug 的操作也做了安全审计。
因此,我们只要构建一个包含众多排障工具的镜像,不仅实践了业务镜像尽可能最简的原则,还提供了调试应用程序所需的各种命令工具。
总结
终端信息、events、web terminal 及 debug 容器都提供了一个可视化的 web,让用户能够方便快速地实现对 pods 排错和对应用程序的排障。
本文转载自宜信技术学院网站。
原文链接:http://college.creditease.cn/detail/331
评论