一个Pods建立的背后 - 从Kubernetes源码角度剖析

前言 Pods 是 Kubernetes 集群中的基础单元,它们可以组织和运行多个容器。从用户的角度,Pods 可以很方便地被创建和管理,但是它们的实际工作原理却鲜有人关注或者一知半解。在这篇文章中,我们将从 Kubernetes 源码的角度,详细剖析一个 Pods 建立的背后。同时会分析各个组件的工作流程,以及它们如何协同工作,以实现创建和管理 Pods 的目标。同时我们还将在代码解读中,讲解每个组件的职责和实现原理,以更好地理解 Pods 工作的核心机制。 Pod基础知识 Pods 由一个或多个 Container 组成,或者可以说是容器的抽象,所有容器共享一个网络命名空间、存储卷等,可以很方便地共享数据和通信。 首先我们需要通过声明式配置文件来创建 Pod,描述容器的配置和所需的资源,并通过 kubectl 命令提交给 Kubernetes 集群中,而后集群的调度器也就是 Scheduler 组件会根据预先配置的亲和性与对硬件资源的考量,将 Pod 放到一个合适的节点,一旦调度成功,Kubernetes 会启动其中的容器并为其分配网络命名空间、IP 地址和存储卷。 在 Pod 启动成功后,Kubernetes 会不断监视其状态,并提供了更新、扩展和故障转移等编排功能,而在这篇文章中,我将会假设您已经有了对于 Kubernetes 已经具备了一定程度的水平和经验。同时,为了保证学习的流畅性,文章会摒弃例如 PriorityClass 之类的对于主流程关联性没有那么重要的特性 创建Pod的过程 我们在上面基础复习的时候,已经简述了一个 Pod 从组件上是如何被传递并且被创建了,但是如果想要阅读源码,不仅需要查看代码实现,还需要理解 Kubernetes 的架构设计和组件间的通信机制。 过程简述 我们首先将 YAML 声明使用 kubectl 进行提交,kubectl 会通过 Kubeconfig 文件与 kube-apiserver 进行通信。kubeconfig 中的 token 会与 kube-apiserver 进行校验,这个 token 通常被配置在集群中某一个 Secrets 中,与某个具备一定权限的 ServiceAccount 做了绑定,在通过认证后 kube-apiserver 将对提交的 YAML 声明进行语法和逻辑检查,以确保其符合 Kubernetes 的要求。这些检查包括验证声明中的资源是否存在、依赖关系是否正确等,如果没有问题则将其写到 Etcd 中,至此这个 Pod 的声明就被视为已经接受并处理了,注意这里有个小的要点,kube-apiserver 从 etcd 中读取数据是因为 etcd 作为 Kubernetes 的后端存储,存放了所有集群数据,包括 Pods 的定义。这样的设计使得 kube-apiserver 成为了唯一直接与 etcd 交互的组件,以确保数据的一致性和集群状态的准确性,因此整个 kube-apiserver 并不单单只做最前端的接受请求处理,甚至有的时候请求量过大还会成为集群的性能瓶颈 在Kubernetes中,kube-scheduler 是控制平面的一个关键组件,负责监控未分配节点的新创建 Pod,并为它们选择合适的节点以便运行,kube-scheduler 通过建立一个 “Watch” 机制来持续监听 kube-apiserver 上关于 Pods 的信息。当 kube-apiserver 检测到新的 Pod 创建事件(这些事件信息是从etcd中获取的),它会通知 kube-scheduler。kube-scheduler 接收到这些变更后,就会开始处理未分配节点的Pods ApiServer的角色 Controller Manager的角色 Scheduler的角色 Kubelet的角色 Pods的运行过程 Kubelet的监视工作 容器的启动与停止 Pods状态的更新与管理 Pods的持久化 Volumes原理

May 11, 2024 · 1 min · Sxueck

通过RBAC权限最小化创建云效AppStack流水线用户

云效官方文档对于 Pipeline 的权限要求 权限要求 权限类型 非分批发布 分批发布 Namespace 读权限 读权限 ControllerRevision (apps/v1/ControllerRevision) 全部权限 全部权限 待部署的 Kubernetes 对象 全部权限 全部权限 Rollout (standard.oam.dev/v1alpha1) - 全部权限 待部署的工作负载对象 全部权限 全部权限 CRD (apiextensions.k8s.io/v1/CustomResourceDefinition) - 全部权限(用于安装 Rollout CRD) ServiceAccount (core/v1/ServiceAccount) - 全部权限(用于维持 Rollout 控制器的运行) ClusterRole (rbac.authorization.k8s.io/v1/ClusterRole) - 全部权限(用于维持 Rollout 控制器的运行) ClusterRoleBinding (rbac.authorization.k8s.io/v1/ClusterRoleBinding) - 全部权限(用于维持 Rollout 控制器的运行) Role (rbac.authorization.k8s.io/v1/Role) - 全部权限(用于维持 Rollout 控制器的运行) RoleBinding (rbac.authorization.k8s.io/v1/RoleBinding) - 全部权限(用于维持 Rollout 控制器的运行) Deployment (apps/v1/Deployment) - 全部权限(用于维持 Rollout 控制器的运行) Pod (core/v1/Pod) - 全部权限(用于 Rollout 控制器的安装后 E2E 测试) 注:"-" 表示在该场景下不需要相应的权限。...

April 15, 2024 · 5 min · Sxueck

Ruby实现一个简单的DNS查询工具

前言 最近需要写一个自动轮询生成指定域名的最快 IP 的工具,本身是一个小工具的属性,直接是用 Ruby 调试加上编写花费了三个小时完成了部分功能,过程比我想象地要难一点,这里直接放出来代码给大家参考 同时我也参考了这段 Gist :https://gist.github.com/jvns/1e5838a53520e45969687e2f90199770 效果 代码和部分讲解 # frozen_string_literal: true require 'socket' require 'stringio' DNS_TYPES = { 1 => "A", 2 => "NS", 5 => "CNAME", } class DNSEncapsulationOfWriteOperations # fixed format, just hardcode private def make_question_header(query_id) [query_id, 0x0100, 0x0001, 0x0000, 0x0000, 0x0000].pack('nnnnnn') end public def make_dns_query(domain, query_id) question = domain.split('.').map { |label| [label.length, label].pack('Ca*') }.join + "\x00" question << [0x0001, 0x0001].pack('nn') # query type and class make_question_header(query_id) + question end end class DNSEncapsulationOfReadOperations def read_domain_name(buf) domain = [] loop do label_length = buf....

April 2, 2024 · 3 min · Sxueck

源码随笔 - Kured插件

前言 Kured(Kubernetes Reboot Daemon)是一个针对 Kubernetes 的守护程序,其功能是在底层操作系统的软件包管理系统指示需要重新启动时,进行安全的自动节点重启 该守护程序通过监视重启标志文件(例如 /var/run/reboot-required)或监测特定的哨兵命令是否成功运行来确定是否需要进行重启操作。它会持续不断地观察这些指示,并在需要时采取相应的行动 为了确保在进行节点重启时的平稳运行,Kured 利用了 Kubernetes 的 APIServer 中的锁机制,以保证一次只有一个节点进行重启。这种锁机制确保了在集群中同一时间只有一个节点被重启,并避免了重启操作之间的冲突 此外,Kured 还提供了一些可选功能,例如在存在活动的 Prometheus 警报或特定的 Pod 时,可以推迟执行重启操作。这样可以避免在系统处于故障状态或关键服务正在运行时进行重启,从而确保整个集群的稳定性和可用性 总体来说,这个项目整体难度不算太高,通过对这个项目的二开可以加深对于集群的理解,具体的代码可以参考 https://github.com/kubereboot/kured ,截止目前,最新版本为 1.15.1 Kured 代码流程简述 主流程 它的 flags 使用了 cobra 库,同时在 Run 形参里面声明了 Root 函数为主要的执行函数,注意不要被 Execute 的调用所疑惑了 window, err := timewindow.New(rebootDays, rebootStart, rebootEnd, timezone) 这里使用了从 flags 中声明的重启时间,其中的 rebootDays 根据上下文来看,是个 []string 类型,推测估计是直接填写日期字符串 buildHostCommand(1, restartCommand) 这个方法实际上是将外部的宿主机的控制权限映射到了内部的容器控制点(当然必须事先声明 hostPID:true 和 privileged:true这两个选项允许容器进程获取额外的权限),如果我们进入函数内部进行查看,可以看到它是使用了 Linux 的 Nsenter 命令,这个命令会进入宿主机的命名空间(特别是在这里指定了 -m 选项,与之和宿主机的 PID 为 1 的进程做挂载)。这样,随后追加的 command 就会在宿主机上执行,而不是在容器内部...

April 1, 2024 · 4 min · Sxueck

从Brutal算法的实现引申出的随想

前篇 关于 TCP 拥塞的控制算法,入门知识可以从 《TCP拥塞控制算法BBR的原理和改进实践》开始过一遍 Brutal 算法的核心代码剖析 static void brutal_update_rate(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct brutal *brutal = inet_csk_ca(sk); u64 sec = tcp_sock_get_sec(tp); u64 min_sec = sec - PKT_INFO_SLOTS; u32 acked = 0, losses = 0; u32 ack_rate; // Scaled by 100 (100=1.00) as kernel doesn't support float u64 rate = brutal->rate; u32 cwnd; u32 mss = tp->mss_cache; u32 rtt_ms = (tp->srtt_us >> 3) / USEC_PER_MSEC; if (!...

December 6, 2023 · 3 min · Sxueck

浅谈Kubernetes Operator模式的开发

前言 这篇博客是属于对《Kubernetes Operator 进阶开发》的初步章节一个笔记总结,作为 2023 第一篇博文,也作为重新深入梳理 K8s 的基础,往后会逐步将之前的坑给填上 Operator的基本概念 作为最入门的篇章,我们将从写一个 Demo 开始,在这里将默认已经存在了一个可用的集群环境,当然如果在开始学习之前,你能有一些 Client-Go 基础那就再好不过了 控制器模式 控制器模式在日常生活中的应用非常广泛,也是根据原书的例子进行举例,当然这里缩减了非常多,也加入了些个人的想法 夏天我们需要调整空调制冷,现在过程如下: 空调启动后设置一个温度,例如 25 度 空调检测室温,确定室温是否高于目标值 如果高于目标阈值,例如室温 27 度,则开始制冷操作 如果低于目标阈值,保持静默到下一个探测周期 这其实就是控制器模式典型的工作流程,外部输入一个 “期望值”,期间一个 “控制器” 不断按照 “周期” 观测 “环境状态” 的 “实际值” 和 “期望值” 之间的差异,然后不断调整两者,以便维持平衡,这个过程则被称为 “调谐” Kubernetes中的控制器 Kubernetes 中通过 “声明式API” 定义了一系列的资源对象,然后通过许多的 Controllers 来 “调谐” 这些资源对象的实际状态以向期待状态靠拢,从而实现整个集群 “尽可能” 靠拢配置中声明的期望状态 一个Deployment的创建 我们都知道集群中有个组件,叫做 Kube-Controller-Manager,这个组件就是一系列控制器的集合,现在可以重新梳理一下这个面试中经常被问到的问题了 在编辑好一个 Deployment YAML 配置文件并使用 Kubectl 提交后,这个资源声明就被传递给了 Kube-ApiServer,接着 Kube-Controller-Manager 中的 Deployment 控制器监听到了消息,注意 K8s 中的组件传递原则 — 是上层组件先将自身置于被修改后的状态,即先假设资源已经被创建,而下层组件监听到了上游变化后,将自身或者环境状态 “调谐” 为上游一致,在这里 Deployment Controller 根据 Spec 字段的定义创建对应的 ReplicaSet 资源,而 RS Controller 监听到了 RS 的变化,并也根据 Spec 字段中声明创建了 Pod,这里其实还有一个 Kubelet 的环节在中间,只是这里被我们抽象掉了...

September 26, 2023 · 3 min · Sxueck

杂谈 - 关于我买了条跨境专线这件事情

前前言 这是一篇杂谈,意味着不会像其他技术博文一样要求思路很严谨,也或者里面会有些错误或者不可靠的地方,欢迎探讨。 前言 再又一次看到谷歌搜索的人机认证后,我叹了口气。 自从高二有了自己第一台电脑后,怀着对 Linux 极大的兴趣,通过折腾 VPS 逐渐初次接触到了 “翻墙” 这个概念,才恍然意识,原来英文互联网能获取那么多一针见血的教程/知识,原来 Youtube (那时候对哔哩哔哩还没感兴趣)的视频那么好看,加上 Google 针对百度压倒性的搜索质量等等,让我从 “简中局域网居民”(不含贬义) 逐渐变成了一位 “国际互联网公民”。 那时候 Shadowsocks 横空出世,机场的概念还没兴起,自建梯子是当时的主流,支持 AliPay 的 Vultr 凭借着最低 35 元/月的最低套餐,变成了不知道多少 “年轻人的第一台 VPS”,我犹记得刚刚开始的时候,Vultr 东京 NTT 线路节点到深圳也才 45ms,学着教程部署一套 SS 后畅游外网似乎是一套那么理所当然的操作。 然而 Vultr 被人挤满后,当时夸张到需要连开五台机器,才能找出可以正常没有被墙的 IP,只要识别到了该 IDC 东京网段,网站会屏蔽你的服务,这就是因为低门槛导致不可控的因素极具增多,不是每个人都是一名合格的邻居,爬虫/垃圾邮件/DDOS/反动内容充斥着这里时候,我们意识到是时候找条新路子了。 一年亦或者是两年后,一方面是对持续维护翻墙服务器感到了厌烦,SS/SSR 在当时属于不断升级对抗阶段,如果更新不及时被 GFW 发现协议特征(最为经典就是 TCP 重放探测了),你会发现服务器 IP 被墙并不是件很意外的事情。另一方面,机场的概念被提出,只需要一台最低价格套餐就能享受到数以十计的节点,速度拉满,这简直不要太舒服。 机场时期 机场模式将会是目前以及未来的主流。 机场模式伴随度过大学时光,Stackoverflow / Github / Google 学术等等也是借着机场的便利才得以顺通,对于大部分人来说,机场完完全全可以当成最优解,只需要刷刷 Ins 或者谷歌搜索这种用途,又何必用其他的手段呢。 然而事情的便利都是有代价的,机场低廉的价格意味着需要进行超售才能维持成本,一条号称全 BGP 线路机场套餐 300G 才 10 元,一个出口 IP 被几百人使用,注意一个合格的机场是不会直连国外落地节点的,客户线路太不可控,万一效果不理想炸起来口碑就没了。一般都是在国内放置一个 BGP 入口,可以是阿里云/腾讯云等等,三网客户汇聚后转发到对于 BGP 节点优秀的出口线路。例如 PCCW / CN2 这些。...

June 28, 2023 · 3 min · Sxueck