kubeadm构建k8s源码阅读环境

news/2025/2/8 18:08:52 标签: kubernetes, 容器, 云原生

目标

前面看了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.niftyadmin.cn/n/5845183.html

相关文章

Node.js中http模块(二)

一、http模块 http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供的 http.createServer0) 方法,就能方便的把一台普通的电脑,变成一台 Web 服务器,从而对外提供 Web 资源服务。 二、域名和域名服务器 尽管 I…

git SourceTree 使用

Source Tree 使用原理 文件的状态 创建仓库和提交 验证 再克隆的时候发发现一个问题,就是有一个 这个验证,起始很简单 就是 gitee 的账号和密码,但是要搞清楚的是账号不是名称,我之前一直再使用名称登录老是出问题 这个很简单的…

位置定位与IP属地:异同解析与实际应用

在数字化和网络化的今天,位置定位和IP属地已成为我们日常生活中不可或缺的两个概念。那么,位置定位和IP属地是不是一样的?‌虽然都涉及到地理位置的识别,实则在定义、应用场景及精确度上存在着显著差异。本文旨在深入探讨位置定位…

确保数据一致性:RabbitMQ 消息传递中的丢失与重复问题详解

前言 RabbitMQ 是一个常用的消息队列工具,虽然它能帮助高并发环境下实现高效协同,但我们也曾遇到过因网络波动、确认机制失效、系统故障和代码异常等原因导致消息丢失或重复消费的问题,本文将探讨原因及解决方案,希望能为大家提供…

shell脚本控制——处理信号

Linux利用信号与系统中的进程进行通信。你可以通过对脚本进行编程,使其在收到特定信号时执行某些命令,从而控制shell脚本的操作。 1.重温Linux信号 Linux系统和应用程序可以产生超过30个信号。下表列出了在shell脚本编程时会遇到的最常见的Linux系统信…

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-files.py

files.py ultralytics\utils\files.py 目录 files.py 1.所需的库和模块 2.class WorkingDirectory(contextlib.ContextDecorator): 3.def spaces_in_path(path): 4.def increment_path(path, exist_okFalse, sep"", mkdirFalse): 5.def file_age(path__fi…

问题大集04-浏览器阻止从 本地 发起的跨域请求,因为服务器的响应头 Access-Control-Allow-Origin 设置为通配符 *

1、问题 localhost/:1 Access to XMLHttpRequest at xxx(请求) from origin http://localhost:xxx(本地) has been blocked by CORS policy: The value of the Access-Control-Allow-Origin header in the response must not be t…

WPF 进度条(ProgressBar)示例一

本文讲述&#xff1a;WPF 进度条(ProgressBar)简单的样式修改和使用。 进度显示界面&#xff1a;使用UserControl把ProgressBar和进度值以及要显示的内容全部组装在UserControl界面中&#xff0c;方便其他界面直接进行使用。 <UserControl x:Class"DefProcessBarDemo…