[大厂实践] Netflix容器平台内核panic可观察性实践

在某些情况下,K8S节点和Pod会因为出错自动消失,很难追溯原因,其中一种情况就是发生了内核panic。本文介绍了Netflix容器平台针对内核panic所做的可观测性增强,使得发生内核panic的时候,能够导出信息,帮助排查问题。原文: Kubernetes And Kernel Panics

最近,我们为了减轻容器平台Titus客户(工程师,而不是最终用户)的痛苦,开始调查"孤儿(Orphaned)"pod。有些pod从不会结束,只能被垃圾收集,没有真正令人满意的最终状态。我们的服务任务(比如ReplicatSet)所有者不会太在意,但Batch用户会非常在意。如果没有真正的返回码,怎么才能知道重试是否安全?

即使只占系统中总pod的一小部分,这些孤儿pod对用户来说也是真正的痛苦。这些pod到底去哪儿了?为什么不见了?

本文展示了如何将最坏情况(内核panic)与Kubernetes(k8s)联系起来,并最终与我们的运维人员联系起来,这样我们就可以跟踪k8s节点是如何以及为什么消失的。

孤儿Pod从何而来?

因为底层k8s节点对象消失了,所以孤儿pod也消失了。一旦发生这种情况,GC[1]进程将删除该pod。在Titus上,我们运行自定义控制器来存储Pod和Node对象的历史,这样我们就可以保存一些解释并将其显示给用户。对应的失败模式在我们的UI中是这样的:

当k8s节点和它的pod消失时,用户会看到什么
当k8s节点和它的pod消失时,用户会看到什么

这是一种解释,但我和用户都不太满意。为什么代理丢失了?

丢失的节点从何而来?

节点可能因为任何原因消失,尤其是在"云"中。当这种情况发生时,通常是云供应商提供的k8s云控制器检测到实际的服务器(在我们的例子中是EC2实例)已经消失,并反过来删除k8s节点对象。这仍然没有真正回答为什么。

如何确保每个消失的实例都有原因,提供解释,并和pod关联在一起?这一切都始于一个注释:

{
     "apiVersion": "v1",
     "kind": "Pod",
     "metadata": {
          "annotations": {
               "pod.titus.netflix.com/pod-termination-reason": "Something really bad happened!",
...

创建存放这些数据的地方就是一个很好的开始。现在我们所要做的就是让GC控制器意识到这个注释,然后将其分发给任何可能导致pod或节点意外消失的进程中。添加注释(而不是修补状态)可以保留pod的其余部分。(我们还为终止原因添加了注释,并为标记添加了简短的reason-code)

pod-termination-reason注释对于填充人类可读的消息非常有用,例如:

  • "此pod被更高优先级的作业($id)抢占了"
  • "由于底层硬件失败,必须终止此pod ($failuretype) "
  • "这个pod必须被终止,因为$user在节点上运行sudo halt "
  • "这个pod意外死亡,因为底层节点内核panic了!"

但是等等,我们如何为内核panic的节点注释pod呢?

捕获内核Panic

当Linux内核出现问题时,能做的就不多了。但是,如果可以发出某种"在我的最后一口气中,诅咒Kubernetes!"UDP数据包呢?

受这篇Google Spanner论文的启发,Spanner节点发出"最后一口气"UDP数据包来释放租约和锁,也可以配置服务器在内核panic时使用一个常用的Linux模块netconsole来做同样的事情。

配置Netconsole

事实上,Linux内核甚至可以发送带有字符串"kernel panic"的UDP数据包,而它正在panic,这有点令人惊讶。能做到这一点是因为netconsole需要配置实现填写好的整个IP头。没错,必须告诉Linux源MAC、IP和UDP端口是什么,以及目标MAC、IP和UDP端口是什么,实际上是在为内核构造UDP数据包。但是,有了这些准备工作,当时机成熟时,内核可以很容易的构造[2]数据包,并在系统崩溃时将其从(预配置的)网络接口中取出。幸运的是,netconsole-setup命令使设置变得非常简单,所有配置选项可以动态[3]设置,这样当端点发生变化时,就可以指向新的IP。

一旦设置完成,内核消息将在modprobe之后开始流动。想象一下,整个操作就像执行dmesg | netcat -u $destination 6666,只不过是在内核空间中。

Netconsole"最后的怒吼"数据包

通过netconsole设置,内核panic的最后怒吼看起来就像一组UDP数据包,就像人们可能期望的那样,其中UDP数据包的数据只是内核消息的文本。在内核panic的情况下,看起来像这样(每行一个UDP数据包):

Kernel panic - not syncing: buffer overrun at 0x4ba4c73e73acce54
[ 8374.456345] CPU: 1 PID: 139616 Comm: insmod Kdump: loaded Tainted: G OE
[ 8374.458506] Hardware name: Amazon EC2 r5.2xlarge/, BIOS 1.0 10/16/2017
[ 8374.555629] Call Trace:
[ 8374.556147] <TASK>
[ 8374.556601] dump_stack_lvl+0x45/0x5b
[ 8374.557361] panic+0x103/0x2db
[ 8374.558166] ? __cond_resched+0x15/0x20
[ 8374.559019] ? do_init_module+0x22/0x20a
[ 8374.655123] ? 0xffffffffc0f56000
[ 8374.655810] init_module+0x11/0x1000 [kpanic]
[ 8374.656939] do_one_initcall+0x41/0x1e0
[ 8374.657724] ? __cond_resched+0x15/0x20
[ 8374.658505] ? kmem_cache_alloc_trace+0x3d/0x3c0
[ 8374.754906] do_init_module+0x4b/0x20a
[ 8374.755703] load_module+0x2a7a/0x3030
[ 8374.756557] ? __do_sys_finit_module+0xaa/0x110
[ 8374.757480] __do_sys_finit_module+0xaa/0x110
[ 8374.758537] do_syscall_64+0x3a/0xc0
[ 8374.759331] entry_SYSCALL_64_after_hwframe+0x62/0xcc
[ 8374.855671] RIP: 0033:0x7f2869e8ee69
...
连接到Kubernetes

最后要连接的是Kubernetes (k8s),需要k8s控制器完成以下工作:

  1. 监听端口6666上的netconsole UDP数据包,观察来自节点的类似内核panic的情况。
  2. 在内核出现故障时,查找与传入netconsole数据包的IP地址相关联的k8s节点对象。
  3. 对于该k8s节点,找到绑定到它的所有pod,注释,然后删除这些pod。
  4. 对于k8s节点,注释节点,然后删除。

第1步和第2步可能是这样的:

for {
    n, addr, err := serverConn.ReadFromUDP(buf)
    if err != nil {
        klog.Errorf("Error ReadFromUDP: %s", err)
    } else {
        line := santizeNetConsoleBuffer(buf[0:n])
        if isKernelPanic(line) {
            panicCounter = 20
            go handleKernelPanicOnNode(ctx, addr, nodeInformer, podInformer, kubeClient, line)
        }
    }
    if panicCounter > 0 {
        klog.Infof("KernelPanic context from %s: %s", addr.IP, line)
        panicCounter++
    }
}

然后第3和第4步:

func handleKernelPanicOnNode(ctx context.Context, addr *net.UDPAddr, nodeInformer cache.SharedIndexInformer, podInformer cache.SharedIndexInformer, kubeClient kubernetes.Interface, line string) {
    node := getNodeFromAddr(addr.IP.String(), nodeInformer)
    if node == nil {
        klog.Errorf("Got a kernel panic from %s, but couldn't find a k8s node object for it?", addr.IP.String())
    } else {
        pods := getPodsFromNode(node, podInformer)
        klog.Infof("Got a kernel panic from node %s, annotating and deleting all %d pods and that node.", node.Name, len(pods))
        annotateAndDeletePodsWithReason(ctx, kubeClient, pods, line)
        err := deleteNode(ctx, kubeClient, node.Name)
        if err != nil {
            klog.Errorf("Error deleting node %s: %s", node.Name, err)
        } else {
            klog.Infof("Deleted panicked node %s", node.Name)
        }
    }
}

有了这些代码,一旦检测到内核故障,pod和节点就会立即消失。不需要等待任何GC进程。注释帮助记录发生在节点和pod上的事情:

真实的pod在真实的k8s节点上丢失了,这个节点发生了真实的内核panic!
真实的pod在真实的k8s节点上丢失了,这个节点发生了真实的内核panic!
结论

将作业标记为由于内核panic而失败可能不会让客户满意。但当他们知道我们现在有必要的可观察性工具来开始修复这些内核panic时,就会感到满意!


你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

参考资料
[1]

Pod garbage collection: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-garbage-collection

[2]

Linux netconsole.c: https://github.com/torvalds/linux/blob/94f6f0550c625fab1f373bb86a6669b45e9748b3/drivers/net/netconsole.c#L932

[3]

Initialize netconsole at boot time: https://wiki.ubuntu.com/Kernel/Netconsole#Step_3:_Initialize_netconsole_at_boot_time

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/459951.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[当人工智能遇上安全] 11.威胁情报实体识别 (2)基于BiGRU-CRF的中文实体识别万字详解

您或许知道&#xff0c;作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用&#xff0c;您就有福利了&#xff0c;作者将重新打造一个《当人工智能遇上安全》系列博客&#xff0c;详细介绍人工智能与安全相关的论文、实践&#xff0c;并分享各种案…

jsp康养小镇管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP康养小镇管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&a…

一文读懂转融通

最近多家公司都在讲解关于转融通要求。今天我们就来详细聊聊&#xff01; 转融通是一种证券借贷机制&#xff0c;它允许机构投资者在融资融券交易中借入或借出证券。 具体来说&#xff0c;机构投资者可以向券商借入证券&#xff0c;或者将持有的证券借给券商&#xff0c;以扩大…

【Spring】Bean 的实例化方式

Spring 为 Bean 提供了多种实例化方式&#xff0c;通常包括4种方式 也就是说在 Spring 中为 Bean 对象的创建准备了多种方案&#xff0c;目的是&#xff1a;更加灵活 第一种&#xff1a;通过构造方法实例化 第二种&#xff1a;通过简单工厂模式实例化 第三种&#xff1a;通过…

程序设计进阶——C语言(翁凯版)第七周

7.1 文件 7.1.1 格式化输入输出 格式化的输入输出 printf%[flags][width][.prec][hIL]typescanf%[flag]type printf和scanf都是有返回值的&#xff0c;printf返回读入的项目数&#xff0c;scanf返回输出的字符数&#xff0c;在要求严格的程序中&#xff0c;应该判断每次调用…

安全名词解析-数据安全、敏感个人信息、数据分级分类

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 数据安全02 敏感个人信息03 数据分级分类 01 数据安全 数据&#xff0c;是指任何以电子或者其他方式对信息的记录。数据处理&#xff0c;包括数据的收集、存储、使用、加工、传输、提供、公开等。 …

3.3-媒资管理之MinIo分布式文件系统上传视频

文章目录 媒资管理5 上传视频5.1 需求分析5.2 断点续传技术5.2.1 什么是断点续传5.2.2 分块与合并测试5.2.3 视频上传流程5.2.4 minio合并文件测试 5.3 接口定义5.4 上传分块开发5.4.1 DAO开发5.4.2 Service开发5.4.2.1 检查文件和分块5.4.2.2 上传分块5.4.2.3 上传分块测试 5.…

Flink Checkpoint过程

Checkpoint 使用了 Chandy-Lamport 算法 流程 1. 正常流式处理&#xff08;尚未Checkpoint&#xff09; 如下图&#xff0c;Topic 有两个分区&#xff0c;并行度也为 2&#xff0c;根据奇偶数 我们假设任务从 Kafka 的某个 Topic 中读取数据&#xff0c;该Topic 有 2 个 Pa…

猫头虎分享已解决Bug || docker: Error response from daemon: network not found

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

cleanmymacX和腾讯柠檬哪个好用

很多小伙伴在使用Mac时&#xff0c;会遇到硬盘空间不足的情况。遇到这种情况&#xff0c;我们能做的就是清理掉一些不需要的软件或者一些占用磁盘空间较大的文件来腾出空间。我们可以借助一些专门的清理工具&#xff0c;本文中我们来推荐几款好用的Mac知名的清理软件。并且将Cl…

【已解决】:pip is configured with locations that require TLS/SSL

在使用pip进行软件包安装的时候出现问题&#xff1a; WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. 解决&#xff1a; mkdir -p ~/.pip vim ~/.pip/pip.conf然后输入内容&#xff1a; [global] ind…

如何构建一个高效的微服务治理闭环管理体系

随着企业业务的快速发展和数字化转型的推进&#xff0c;微服务架构因其高度的灵活性、可扩展性和可维护性而逐渐成为主流。然而&#xff0c;微服务架构的复杂性也带来了诸多治理挑战。为了有效应对这些挑战&#xff0c;构建一个微服务治理闭环至关重要。 1、微服务治理概述 微…