开发者指南-本地 Kubernetes 集群搭建与远程调试:开启探索K8S源码新篇章

978 阅读9分钟

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

前言

Kubernetes(简称 K8s)已经成为现代容器编排的标准,在云计算和 DevOps 领域扮演着至关重要的角色。作为一个功能强大且复杂的系统,Kubernetes 提供了强大的工具来管理容器化应用的部署、扩展和运维。然而,对于许多开发者和运维人员来说,如何在本地环境中搭建并调试 Kubernetes 集群仍然是一个挑战。(这里说的不是调试部署在 k8s 内部的业务服务,而是调试 k8s apiserver、kube-controller、kube-scheduler 等等)

 

在这篇文章中,我们将详细介绍如何在 macOS 上搭建并调试 Kubernetes 集群,为希望深入了解其内部工作原理的开发者打开第一步。

我们将从环境准备开始,逐步介绍所需的工具和安装过程。接下来,我们会配置 Kubernetes 环境,并部署一个示例应用,帮助你熟悉 Kubernetes 的基本操作和概念。在此基础上,我们将深入讲解如何调试 Kubernetes。

通过本篇文章,你将能够:

  • 在 macOS 上搭建 Kubernetes 集群 (Linux,Windos 同样适用)
  • 使用 DLV 进行调试和问题排查
  • 探索并理解 Kubernetes 的源码

 

无论你是为了学习 Kubernetes 的基础知识,还是为了提升自己的调试和开发能力,这篇文章都将成为你探索 Kubernetes 世界的重要指南。让我们一起踏上在 macOS 上调试 Kubernetes,并探索其源码的精彩旅程吧!

 

环境说明

本文均是在操作系统:macOS Sonoma 14.3.1 环境中完成的。(其实不同系统以及版本也无所谓),通过安装虚拟机,在虚拟机内部搭建 k8s 本地集群,利用 dlv 在 goland 中进行远程调试。

两个关键前提:

  1. 网络是 OK 的(简单来说要保证能 ping 通 google)
  2. 一切操作用 root 用户

ps:作者在搭建过程中踩到很多坑,强烈建议按照步骤一步一步操作,否则可能会遇到作者踩过的坑(具体问题感兴趣的可以私信)

 

Multipass 虚拟机搭建

虚拟机我们选择使用 Multipass 进行搭建,期间尝试过 vmware 以及 直接使用 docker desk k8s 均有问题(vmware 在 macOS Sonoma 上安装 vmware tool 失败,没有办法复制粘贴,很难搞,docker desk k8s 要调试过于复杂,需要替换的东西比较多)

 

Multipass 是一个轻量级的虚拟化工具,旨在帮助用户快速创建和管理 Ubuntu 虚拟机。它由 Canonical 维护,特别适合需要在本地环境中运行多个独立 Ubuntu 实例的开发者和运维人员。Multipass 简化了虚拟机的创建、配置和管理过程,使得在不同环境下运行和测试应用变得更加容易。

 

下载&安装 Multipass

macos 中可以直接进入官网下载地址进行安装, multipass.run/install

image.png

创建虚拟机实例

创建并启动一个 2c,4g 的虚拟机,名字:vm01

multipass launch --cpus 2 --disk 20G --memory 4G --name vm01

进入虚拟机

当创建完成后,我们可以使用如下命令进入到虚拟机,后续的操作基本都是在虚拟机里面完成。

multipass shell vm01

更多 multipass 相关指令可以执行 multipass info 进行查看,例如如何重启,删除虚拟机等等

   

安装 contaienrd

在上一步进入到虚拟机中之后,我们准备在虚拟机内安装容器运行时 containerd, 因为是本地搭建建议所有操作都是用 root 用户,避免受到权限问题困扰。

1. 卸载旧版本

 sudo apt-get remove docker docker-engine docker.io containerd runc

如果需要删除镜像及容器数据则执行以下命令

 sudo rm -rf /var/lib/docker
 sudo rm -rf /var/lib/containerd

2. 准备包环境

2.1、更新 apt,允许使用 https。

 sudo apt-get update
 sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

2.2、添加 docker 官方 GPG key。

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

2.3、设置软件仓库源

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

3. 安装 containerd

# 安装 containerd
sudo apt-get update
sudo apt-get install -y containerd.io


# 如果是安装 docker 则执行:
sudo apt-get install docker-ce docker-ce-cli containerd.io


# 查看运行状态
systemctl enable containerd
systemctl status containerd

安装指定版本

# 查看版本
apt-cache madison containerd


# sudo apt-get install containerd=<VERSION>

4. 修改配置

在 Linux 上,containerd 的默认 CRI 套接字是 /run/containerd/containerd.sock

4.1、生成默认配置

containerd config default > /etc/containerd/config.toml

4.2、修改 CgroupDriver 为 systemd

k8s 官方推荐使用 systemd 类型的 CgroupDriver。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

5. 重启 containerd

systemctl restart containerd

 

安装 Etcd

1. 更新 apt

apt-get update

2. 安装并运行 etcd

apt install etcd-server

3. 关闭 etcd

这里需要先关闭 etcd,因为在后面启动 k8s 本地集群时会自动拉起 etcd, 所以我们先关闭它,否则在启动 k8s 集群时会提示端口冲突

systemctl stop etcd

 

编译&启动 k8s 集群(v1.26.4)

1. 下载 k8s 源码

在虚拟机中的任意目录,例如/root 下执行

git clone https://github.com/kubernetes/kubernetes.git

2. 切换版本

这里需要注意,尽量不要使用最新版本,经过踩坑在最新版本的 k8s 中 (1.30 使用的 golang 是 1.22, 在这个版本中通过 goland dlv 远程 debug 不生效,查阅很多资料后没有什么太多信息,后面降低了 k8s 版本和 golang 版本 最终生效了)。

进入 k8s 目录

cd kubernetes/

在这里我们选择 k8s 的版本为 v1.26.4!!!

git checkout v1.26.4

3. 安装 golang(1.19.3)

因为我的电脑是 m3 芯片,所以需要选择 golang 的 arm 版本,如果大家是 interl 芯片,在这里可以选择 amd 版本。 选择 golang 1.19 版本是因为 k8s 1.26.4 版本也是用的 golang1.19。 这里尽量保持对应。

3.1.下载 golang

进入官网找到需要下载的 golang 版本 go.dev/dl/ , 进入/root 目录下执行

wget https://go.dev/dl/go1.19.3.linux-arm64.tar.gz

3.2. 安装 golang

1. rm -rf /usr/local/go && tar -C /usr/local -xzf go1.19.3.linux-arm64.tar.gz
2. export PATH=$PATH:/usr/local/go/bin
3. go version

image.png

4. 安装 cfssl

cfssl 生成证书使用的,在 k8s 编译&安装脚本中会使用到,我们在这里提前安装

apt install golang-cfssl

5. 修改 k8s local-up-cluster.sh 参数

  1. 进入到我们下载的 k8s 源码目录的 hack 目录下 cd /root/kubernetes/hack

  2. 编辑 local-up-cluster.sh 脚本, 主要修改有两处: 修改 CGROUP_DRIVER 为 systemd 和 修改 KUBELET_RESOLV_CONF = /run/systemd/resolve/resolv.conf!!!

  3. vim local-up-cluster.sh

  4. 修改 CGROUP_DRIVER 为 systemd image.png

  5. 修改 KUBELET_RESOLV_CONF = /run/systemd/resolve/resolv.conf image.png

  6. 保存修改,退出编辑模式

 

6. 安装 make

apt-get install make

7. 编译&启动 k8s 集群

进入到 k8s 代码根目录,例如:/root/kubernetes,执行 hack 目录下的 local-up-cluster.sh 脚本

./hack/local-up-cluster.sh

当你看到如下输入,证明整个 k8s 集群已经启动完成了。(如果不按照上面的步骤一步一步来,你有可能会遇到各种问题,例如 coredns pod 启动不起来)

image.png    

重新编译 APIServer & 开启调试

下面我将以调试 api-server 为例进行介绍,其他组件道理相同。

1. kill apiserver 进程

首先 kill 掉上一步 我们启动的 api server 进程,目的是禁用 golang 的编译优化,重新编译 api server 然后才能进行 dlv 远程调试。

上一步启动 k8s 集群的窗口 我们先保留不动,重新打开一个 shell 窗口,进入到虚拟机内,切换 root 账号

1. multipass shell vm01
2. sudo -iu root

查找 api-server 进程,并且 kill 它, 然后将进程的启动参数粘贴复制下来下面会用到!!!

1. ps -ef|grep apiserver
2. kill -9 1166146

image.png

2. 重新编译 apiserver

如果仔细查看过源码中的 make 文件,官方提供了 debug 的编译参数 DBG,如下: image.png  

我们只需要重新编译 apiserver 即可,所以执行如下命令

1. cd /root/kubernetes/
2. make DBG="1" WHAT="cmd/kube-apiserver"

image.png

3. 重新启动 apiserver

将 kill apiserver 进程时复制的启动命令和参数,重新执行下,将编译后的 apiserver 启动起来(执行命令时可能会遇到括号提示需要转义的操作,在括号前加一个反斜杠\就可以了)

例如:

/home/ubuntu/kubernetes/_output/bin/kube-apiserver --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins=/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$

 

4. 安装 dlv 1.9.1 版本

Delve 是 Go 语言的一个调试工具,专为开发者设计,用于调试 Go 程序。Delve 提供了丰富的功能,使得开发者可以方便地设置断点、检查变量、单步执行代码以及分析程序的运行状态。Delve 是 Go 开发者的强大工具,能够显著提高代码调试和问题排查的效率

  这里绝不建议,在虚拟机中使用 apt install delve,因为这会安装最新版本 ,当时被这个问题困扰很久,最新版本的 dlv attach 进程都没啥问题,但是一旦在 goland 开启 debug 断点,断点就失效自动变灰色。 所以后来手动安装 delve 的 1.9.1 版本和 golang 1.19 对应。

go install github.com/go-delve/delve/cmd/dlv@v1.9.1

dlv 会自动安装到 go path 下的 bin 目录, 查看 go path 可以通过 go env 查看 image.png  

看到下面的内容就代表安装成功了 image.png  

5. dlv attach apiserver

在 go path 的 bin 目录中,执行 dlv attach 到重新编译后的 api server 进程中。首先查找 apiserver 进程号,然后执行 dlv 命令(最后一个参数是 api server 进程号)

./dlv --listen=:2345 --headless=true --api-version=2  attach 1201848

image.png  

到这里虚拟机中的任务 我们都已经完成了,启动了 k8s,重新编译了 apiserver 并且用 dlv 开启了 attach 了 apiserver 进程,打开了远程调试的监听端口 2345

ps: 关于 delve 这里不做过多介绍,感兴趣的可以自行查阅资料或者用 dlv help 查看使用方法。

 

6. goland 配置远程调试

  1. 接下来我们在主机上下载与虚拟机中版本一致的 k8s 源码,然后用 goland 打开

  2. 然后编辑调试配置 image.png

  3. 添加 Go Remote 配置 image.png

  4. 配置虚拟机 ip 和远程端口号 image.png

  5. 接下来在源码中添加断点,点击 goland 的 debug 按钮,开启调试(下面的章节我会举例说明) image.png  

调试 API Server 如何处理 List 请求

1. 虚拟中使用 Kubectl (root 用户)

  1. 在虚拟机中我们的 k8s 源码下载到了/root/kubernetes 目录下,我们进入到编译目录下/root/kubernetes/_output,找到 kubectl 二进制文件。cd /root/kubernetes/_output image.png

  2. 执行./kubectl get pods,这个时候大概率会报错,提示证书验证不通过。我们需要执行如下命令,将 kubeconfig 文件 copy 到 root 用户的.kube 文件夹下 cp /var/run/kubernetes/admin.kubeconfig /root/.kube/config

  3. 重新执行./kubectl get pods 就会发现没有问题,因为此时咱们搭建的集群还没有部署过 pod,所以会得到如下输出 image.png  

2. 部署 Demo Pod

  1. 首先在虚拟机中的 K8S 集群部署 pod,这里我们参考官网的 demo 就可以: kubernetes.io/zh-cn/docs/…
  2. 需要注意 kubectl,我们按照上一步的方式运行 kubectl 就可以,例如:
/root/kubernetes/_output/bin/kubectl apply -f nginx.yaml

3. 关键函数添加断点

  1. 在 goland 中的 k8s 源码目录找到如下文件:staging/src/k8s.io/apiserver/pkg/endpoints/handlers/get.go
  2. 对 ListResource func 添加断点 image.png

4. 在虚拟中执行 kubectl get pods,查询 pod 列表

/root/kubernetes/_output/bin/kubectl get pods

5. 调试成功

此时可以看到 goland 中的断点已经生效,可以愉快的开启调试,探索源码了 image.png

 

总结

通过这篇文章,我们详细介绍了如何在开发机上搭建和调试 Kubernetes 集群,为开发者和运维人员提供了深入探索 Kubernetes 内部工作原理的基础。我们从环境准备开始,逐步介绍了所需工具和安装过程,配置了 Kubernetes 环境,并部署了一个示例应用,帮助你熟悉 Kubernetes 的调试的完整过程。

最后希望你在探索 Kubernetes 世界的过程中,能够不断深入理解其工作原理,并在实际应用中游刃有余,迎接更多 Kubernetes 带来的挑战和机遇。