当前位置: 首页 > article >正文

kubeadm构建k8s源码阅读环境

目标

前面看了minikube的源码了解到其本质是调用了kubeadm来启动k8s集群,并没有达到最初看代码的目的。 所以继续看看kubeadm的代码,看看能否用来方便地构建源码调试环境。

k8s源码编译

kubeadm源码在k8s源码库中,所以要先克隆k8s源码。之前用minikube创建的k8s集群是v1.32.0
在这里插入图片描述

所以克隆v1.32.0版本的代码

git clone  --branch v1.32.0 --single-branch https://github.com/kubernetes/kubernetes.git

考虑到后续可能要改改源码并保存下来,所以我forkmaster分支去编译。

Makefile中可以看到如何编译
在这里插入图片描述

在编译前先修改.go-version文件中go的版本, 默认里面指定的是1.23.4k8s源码中要求go版本是1.23.0以上就可以了,我的是1.23.3不想重新下载go压缩包了,所以改了。

修改shell脚本让其输出编译的命令,看不到命令我不是很放心
在这里插入图片描述

通过环境变量指定版本号,修改完版本后执行编译命令编译kubeadm

export KUBE_GIT_VERSION=v1.32.0 
export KUBE_GIT_COMMIT=$(git rev-parse --short HEAD) 
export KUBE_GIT_TREE_STATE=clean
make all DBG=1 WHAT=cmd/kubelet

可以看到编译的命令已经带上了禁用优化的参数了
在这里插入图片描述

kubeadm在一开始检查的过程中会调用kubelet获取版本号,所以我把全部二进制文件都编译了

export KUBE_GIT_VERSION=v1.32.0 
export KUBE_GIT_COMMIT=$(git rev-parse --short HEAD) 
export KUBE_GIT_TREE_STATE=clean
make all DBG=1

调试命令如下

dlv --headless --listen=:8005 --api-version=2 --accept-multiclient --log exec /root/kubernetes/_output/bin/kubeadm -- init --cri-socket unix:///run/containerd/containerd.sock

vscode配置如下

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "kubeadm",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "remotePath": "/root/kubernetes",
            "port": 8005,
            "host": "4c",
            "showLog": true,
            "trace": "verbose",
            "substitutePath": [
                {
                    "from": "${workspaceFolder}",
                    "to": "/root/kubernetes"
                },
                {
                    "from": "/Users/wy/wy/workspace_go/pkg/mod", // 本地路径
                    "to": "/root/go_path/pkg/mod" // 远程路径
                },
            ]
        }
    ]
}

调试源码前准备工作

PATH环境变量

将前面编译源码生成二进制文件的目录添加到PATH环境变量中,因为kubeadm需要调用kubelet

containerd启用cri插件

kubeadm中,contianerd是默认的容器运行时,containerd需要启动cri插件,随docker启动的contianerd默认是禁用了cri插件的。

containerd配置cri插件官方文档地址:
https://github.com/containerd/containerd/blob/main/docs/cri/config.md

docker version命令查看docker版本是27.5.0, containerd对应的版本是1.7.25
containerd配置文件默认位置是/etc/containerd/config.toml

在配置文档中有完整的配置文件样例且有大量的注释,有需要的时候再来看,但这不是我们目前要关注的
在这里插入图片描述

使用命令生成默认配置

# 备份旧配置
cp /etc/containerd/config.toml /etc/containerd/config.toml.bak
# 生成默认配置
containerd config default > /etc/containerd/config.toml

修改的值如下,v1.32.0版本k8s要求是3.10版本,没有的话会触发下载镜像的操作

SystemdCgroup = true
sandbox_image = "registry.k8s.io/pause:3.10"

需要重启containerd。由于我docker服务是apt安装的,估计是自动装的containerd,是由systemd托管的。所以重启命令如下

sudo systemctl restart containerd

下载k8s相关镜像

由于网络问题,你得先下载k8s的镜像

# 查看需要下载的镜像
kubeadm config images list

镜像清单如下

registry.k8s.io/kube-apiserver:v1.32.1
registry.k8s.io/kube-controller-manager:v1.32.1
registry.k8s.io/kube-scheduler:v1.32.1
registry.k8s.io/kube-proxy:v1.32.1
registry.k8s.io/coredns/coredns:v1.12.0
registry.k8s.io/pause:3.10
registry.k8s.io/etcd:3.5.17-0

然后用github action大法下载镜像,下载完成后,检查镜像

ctr --namespace k8s.io images list | awk '{print $1}'

镜像已经拉取成功了
在这里插入图片描述

配置kubelet

kubeadm init命令会使用systemctl命令重启kubelet,所以需要编写 /etc/systemd/system/kubelet.service

但是具体怎么写,需要通过官方提供的apt命令安装kubeadm后,使用kubeadm命令安装一次k8s,然后查看kubelet.service可以看到具体的脚本是怎么写的
在这里插入图片描述

可以看到需要编写两个文件,分别是kubelet.service以及 10-kubeadm.conf

编写/etc/systemd/system/kubelet.service

[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=https://kubernetes.io/docs/home/
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/root/kubernetes/_output/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10
KillMode=process
Delegate=yes

[Install]
WantedBy=multi-user.target

编写/etc/systemd/system/kubelet.service.d/10-kubeadm.conf,可以看到这里指定了config.yaml文件

# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/root/kubernetes/_output/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

然后启用该service

systemctl enable kubelet.service

调试源码

主要是想搞清kubeadm是怎么部署k8s集群的,看看能不能用来调试代码。
代码入口是/cmd/kubeadm/kubeadm.go

kubeadm源码调试

kebeadm中将部署集群的每个步骤抽象成phase组成一个数组,然后遍历这个数组,运行每个phase对应的函数,当phase数组遍历完了,kubeadm init命令就完成了
在这里插入图片描述

cmd命令初始化的时候,可以看到有哪些phase
在这里插入图片描述

其实在磊哥的《深入剖析Kubernetes》中有写了kubeadm的部署原理,只是有些东西还是得自己看看才知道
在这里插入图片描述

很明显,kubelet的启动对应是NewKubeletStartPhase,随后的NewWaitControlPlanePhase中等待apiserver启动完成。

... 省略
initRunner.AppendPhase(phases.NewKubeletStartPhase())
initRunner.AppendPhase(phases.NewWaitControlPlanePhase())
... 省略

Kubelet 运行时,它会持续监视参数staticPodPath指定的目录,如果有新的 Pod 配置文件加入,Kubelet 会自动创建 Pod。这种启动Pod的方式叫静态启动,该过程不需要 apiserver 参与调度。

kubeadm生成的kubelet的配置文件中有个staticPodPath选项,值如下

staticPodPath: /etc/kubernetes/manifests

该目录下的yaml都是kubeadm生成的,共四个yaml文件,分别是

  • etcd.yaml
  • kube-apiserver.yaml
  • kube-controller-manager.yaml
  • kube-scheduler.yaml

kubelet源码调试

正常情况下,kubelet是不会有问题的,如果在kubeadm init命令执行过程提示 kubelet失败,得看kubelet到底报啥错了。查看kubelet日志

journalctl -u kubelet

如果从日志上没看出是啥问题,可以调试下源码看看。当代码运行到启动kubelet时,启动所需要的配置文件都已经生成了,所以调试kubelet的时候,可以先退出kubeadm的调试。

调试kubelet需要先把kubeadm init的代码运行到下图中的位置,然后kill掉调试kubeadm initdlv进程,然后调试kubelet进程
在这里插入图片描述

调试前需要先停止kubelet,并禁用自动重启

systemctl disable kubelet.service
systemctl stop kubelet

用下面的命令调试kubelet源码

KUBELET_CONFIG_ARGS="--config=/var/lib/kubelet/config.yaml"
KUBELET_KUBECONFIG_ARGS="--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
KUBELET_KUBEADM_ARGS="--container-runtime-endpoint=unix:///run/containerd/containerd.sock --pod-infra-container-image=registry.k8s.io/pause:3.10"
dlv --headless --listen=:8005 --api-version=2 --accept-multiclient --log exec /root/kubernetes/_output/bin/kubelet -- $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

变量$KUBELET_EXTRA_ARGS是由文件/etc/default/kubelet中定义的,默认是空值,所以我没有赋值。

读取pod文件的代码位置如下,config.NewSourceFile中会启动goroutinue监听/etc/kubernetes/manifests的文件,如果文件有改动,会往一个 channel发送数据
在这里插入图片描述

最后在kubelet的主循环中处理channel中的数据,创建、更新或者删除pod
在这里插入图片描述

启动的代码东西太多了,等需要的时候再回来看,此处只是记录下代码的位置

kube-scheduler源码调试

回到主题上,k8s相关的组件都是通过静态pod的方式启动的,要了解一个pod,就得看它的yaml文件,以kube-scheduler组件为例

在这里插入图片描述

可以看到是使用宿主机的网络命名空间,那么就可以直接使用执行源码编译的二进制文件启动kube-schedualer代替静态pod的方式来实现断点调试了。

先用kubeadm init命令把k8s部署成功,默认情况下,出于安全原因,不会在控制平面节点上调度 Pod。想要在控制平面节点上调度需要执行下面的命令

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

然后删除对应的kube-scheduler.yaml,由于kubelet是持续监视着/etc/kubernetes/manifests的,如果文件有变动,则执行对应的操作,例如我删除了yaml文件,kubelet会删除掉对应的pod

删除yaml文件后已经看不到kube-scheduler了,coredns是要等到CNI插件安装成功后才会启动,这里先不管
在这里插入图片描述

参考yaml中的启动命令,使用下面的命令调试

dlv --headless --listen=:8005 --api-version=2 --accept-multiclient --log exec /root/kubernetes/_output/bin/kube-scheduler -- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf --authorization-kubeconfig=/etc/kubernetes/scheduler.conf --bind-address=127.0.0.1 --kubeconfig=/etc/kubernetes/scheduler.conf --leader-elect=false

参数说明

  • --authentication-kubeconfig 负责身份认证,确保 kube-scheduler 可以连接 API Server。
  • --authorization-kubeconfig 负责权限授权,确保 kube-scheduler 有权限调度 Pod。
  • --leader-elect=false因为没有多个节点,所以把leader选举关掉

可以看到已经能够成功断点调试了,SchedulerOne函数就是对Pod进行调度的入口函数。
在这里插入图片描述


http://www.kler.cn/a/537991.html

相关文章:

  • Json-RPC框架项目(一)
  • 基于Java的远程视频会议系统(源码+系统+论文)
  • DNS攻击方式有哪些,应该采取哪些应对措施?
  • 如何避免大语言模型中涉及丢番图方程的问题
  • 2025.2.9机器学习笔记:PINN文献阅读
  • C#+halcon机器视觉九点标定算法
  • JAVA—SpringBoot—Vue篮球联赛校园管理系统
  • 激活函数篇 02 —— 双曲正切函数tanh
  • Eclipse IDE 快捷键大全
  • 镭速大文件传输软件如何邀请他人上传文件
  • 树和二叉树_6
  • Java序列化与反序列化:原理、实践与陷阱
  • Swift语言的云计算
  • 混合专家模型(MoE)概述:智能计算的新范式
  • Redis --- 使用HyperLogLog实现UV(访客量)
  • B树详解及其C语言实现
  • java 读取sq3所有表数据到objectNode
  • 使用TensorFlow和Keras构建卷积神经网络:图像分类实战指南
  • Maven插件—代码规范格式化spotless-maven-plugin
  • 记录虚拟机安装银河麒麟V10系统中遇到的一些问题
  • 深度学习和机器学习的区别|自注意力机制和多头注意力机制的展示|售前面试题
  • 从长尾关键词到页面优化,打造完整的SEO策略
  • 泛微OA E-Cology /messager/users.data 信息泄漏漏洞
  • Java 一键将 Word 文档转为 PDF
  • Open3d Qt的环境配置
  • DeepSeek-R1 云环境搭建部署流程