找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1059

积分

0

好友

139

主题
发表于 16 小时前 | 查看: 0| 回复: 0

上一次用kubeadm部署K8S还是在2023年,那时应该是K8S v1.22版本。虽然当时容器运行时已经变成了containerd,但我还是习惯用docker,所以第一次部署大概花了2天左右,踩了不少坑。

这次心血来潮,想重新部署一下最新版K8S v1.35。本来想装逼一把,用二进制部署,结果搞到证书部分就懵了,装逼失败,卒。遂改成kubeadm部署,原本以为会很快,毕竟之前部署过好几次,算是轻车熟路。但没想到这次前前后后搞了5天左右才搞定,因此记录一下自己的踩坑记录,应该算全网最细节的踩坑实录了。只要照着我贴出来的命令,基本100%可以在45分钟内完成部署,且不需要翻墙。

节点规划

  • 操作系统:Ubuntu 24.03
  • 三台虚拟机
    • 1台 master: 192.168.36.101
    • 2台 node:
      • node01: 192.168.36.102
      • node02: 192.168.36.103
  • K8S版本:1.35(stable)

部署详细步骤

整个部署过程分为三个阶段:第一阶段主要对三个节点进行基础配置,安装containerd(K8S从1.20版本开始正式使用containerd来替换docker作为容器运行时,集成在K8S内)。第二阶段在master01服务器上配置控制平面,第三阶段将其余节点加入集群。

Kubernetes集群初始化三阶段流程图

第一阶段:所有节点通用准备 (Master & Worker)

这些操作需要在集群中的所有节点上执行。

1. 关闭交换分区并配置内核

Kubernetes要求禁用交换分区,并启用内核模块。

# 临时禁用交换分区
swapoff -a
# 永久禁用,注释掉/etc/fstab中swap相关的行
sed -i.bak '/\sswap\s/d' /etc/fstab

# 加载内核模块
modprobe overlay
modprobe br_netfilter

# 设置系统参数
cat <<EOF |  tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
EOF

sysctl --system

2. 安装容器运行时

推荐使用 containerd

# Ubuntu/Debian 系统
apt-get update
apt-get install -y containerd

# 生成默认配置文件并启用systemd cgroup驱动
mkdir -p /etc/containerd
containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

systemctl restart containerd
systemctl enable containerd

这里有一个不得不讲的点,关系到后面对K8S组件镜像的管理,就是 crictlctr 两个命令。

简单来说,crictl 是专门为 Kubernetes 设计的,而 ctrcontainerd 自身的通用管理工具。

  • 面向用户与场景
    • crictl:主要供 Kubernetes 集群管理员和开发者 使用,用于在节点上调试容器运行时、查看 Pod 状态、检查容器日志等,是 K8s 环境下的标准调试工具。
    • ctr:更偏向 容器运行时维护者和高级用户,用于对 containerd 进行底层的操作和排错,例如管理快照、直接操作镜像内容等。
  • 与 Kubernetes 的集成
    • crictl 与 Kubernetes 的集成更紧密,它的许多命令输出格式(如 crictl pods)与 kubectl 的视图类似,便于对照。
    • ctr 不感知 Kubernetes。例如,Kubernetes 通过 crictl 拉取的镜像默认存放在 k8s.io 命名空间,而 ctr 如果不在命令中明确指定 -n k8s.io,则默认拉取到 default 空间,这会导致 crictl 和 kubelet 看不到这些镜像。

3. 安装 kubeadm, kubelet, kubectl

必须使用Kubernetes官方的新软件包仓库 pkgs.k8s.io。这里以安装 v1.35 稳定版为例,请根据需要替换为最新的稳定版本号(如v1.36)。

# Ubuntu/Debian 系统
# 添加Kubernetes官方仓库
mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list

# 安装并锁定版本
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

第二阶段:初始化控制平面 (仅在Master节点执行)

1. 初始化集群

选择一个与后续网络插件匹配的Pod网段(CIDR)。

# 创建时可指定Pod网段
kubeadm init --pod-network-cidr=10.10.0.0/16 --kubernetes-version stable

重要:命令执行成功后,会输出用于加入集群的 kubeadm join ... 命令,请务必完整保存

天坑一号:

在大多数的K8S部署步骤里,初始化往往就是上面一条命令,但对于很多新手来说,却是第一道不可逾越的鸿沟。因为默认的镜像仓库在国内根本访问不了,需要通过其他方式进行预下载。所以一般需要用如下步骤,放弃思考,只要贴!贴!贴!

1) 检查k8s需要部署的镜像版本

root@master01:~# kubeadm config images list
registry.k8s.io/kube-apiserver:v1.35.0
registry.k8s.io/kube-controller-manager:v1.35.0
registry.k8s.io/kube-scheduler:v1.35.0
registry.k8s.io/kube-proxy:v1.35.0
registry.k8s.io/coredns/coredns:v1.13.1
registry.k8s.io/pause:3.10.1
registry.k8s.io/etcd:3.6.6-0

2)利用crictl从国内源下载k8s镜像

crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.35.0
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.35.0
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.35.0
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.35.0
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.13.1
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10.1
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.6.6-0

从阿里云镜像仓库拉取K8S组件镜像的终端截图

3)利用ctr重新打tag,使镜像被k8s识别

ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.35.0 registry.k8s.io/kube-apiserver:v1.35.0
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.35.0 registry.k8s.io/kube-controller-manager:v1.35.0
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.35.0 registry.k8s.io/kube-scheduler:v1.35.0
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.35.0 registry.k8s.io/kube-proxy:v1.35.0
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.13.1 registry.k8s.io/coredns/coredns:v1.13.1
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10.1 registry.k8s.io/pause:3.10.1
ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.6.6-0 registry.k8s.io/etcd:3.6.6-0

4) 检查确认

crictl image list | grep k8s
root@master01:~# crictl image list | grep k8s
WARN[0000] Config "/etc/crictl.yaml" does not exist, trying next: "/usr/bin/crictl.yaml"
WARN[0000] Image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
register.k8s.io/etcd                                                          3.6.6-0             0a108f7189562       23.6MB
registry.k8s.io/etcd                                                          3.6.6-0             0a108f7189562       23.6MB
register.k8s.io/pause                                                         3.10.1              cd073f4c5f6a8       320kB
registry.k8s.io/pause                                                         3.10.1              cd073f4c5f6a8       320kB
registry.k8s.io/coredns/coredns                                               v1.13.1             aa5e3ebc0dfed       23.6MB
registry.k8s.io/kube-controller-manager                                       v1.35.0             5c6acd67e9cd1       27.7MB
registry.k8s.io/kube-proxy                                                    v1.35.0             32652ff1bbe6b       25.8MB
registry.k8s.io/kube-scheduler

GO~GO~GO~ 重新回到第一步,一把过!

kubeadm init --pod-network-cidr=10.10.0.0/16 --kubernetes-version stable

5) 消除crictl的warning(可选)

root@node02:~# crictl image list
WARN[0000] Config "/etc/crictl.yaml" does not exist, trying next: "/usr/bin/crictl.yaml"
WARN[0000] Image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.

如果你也不喜欢报错,可以参考如下内容,创建一个 /etc/crictl.yaml 文件。

root@master01:~# cat /etc/crictl.yaml
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: false
pull-image-on-create: true
disable-pull-on-run: false

部署成功后的输出内容如下:

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.36.101:6443 --token zjg02r.81m6chikxqoj2uxf \
        --discovery-token-ca-cert-hash sha256:bf104617ac122706d8198fa17771fcdf1246634f6be6a651bdac7db1a401ffb5

2. 配置kubectl

使当前用户可以管理集群。

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

3. 安装Pod网络插件

这是集群能用的关键一步。必须安装一个CNI插件,Pod才能跨节点通信。以安装Calico为例:

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.1/manifests/tigera-operator.yaml
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.1/manifests/custom-resources.yaml

等待几分钟,使用 kubectl get pods -n calico-system -w 观察,直到所有Pod状态变为 Running

天坑二号:

OK,不出意外,calico的安装就是第二个天坑了。虽然短短的几条命令,但是我却花了8个小时,最后无疾而终。主要还是calico对应的镜像组件URL地址在国内访问不稳定,有一次就只差一个镜像文件了,但还是失败了。无奈放弃了Calico,转战稍微简单一点的Flannel网络插件。

具体步骤如下,还是只要贴!贴!贴!

首先是Flannel的yaml文件,内容有点多(可以从官方获取),唯一有一处需要修改,就是 Network 要修改为你在用 kubeadm init 时候指定的Pod网络地址段。

  net-conf.json: |
    {
      "Network": "10.10.0.0/16",
      "EnableNFTables": false,
      "Backend": {
        "Type": "vxlan"
      }
    }

Flannel涉及的网络插件镜像组件地址至少我可以下载。如果你还是下不了,就参考前面K8S组件镜像的方式,用 crictlctr 的组合。组件就如下3个:

root@master01:~# cat flannel.yaml | grep image
        image: ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1
        image: ghcr.io/flannel-io/flannel:v0.27.4
        image: ghcr.io/flannel-io/flannel:v0.27.4

等待一段时间,估摸在5-10分钟(一杯咖啡的时间),顺利的话就OK了,我是一把过的。毕竟前面两个坑花了我差不多10多个小时,已经算是百般折磨了。状态变成 Running 后,就算OK了。

root@master01:~# kubectl get pod -n kube-flannel -o wide
NAME                    READY   STATUS    RESTARTS        AGE     IP               NODE       NOMINATED NODE   READINESS GATES
kube-flannel-ds-4c4tr   1/1     Running   4 (8m13s ago)   3d17h   192.168.36.101   master01   <none>           <none>
kube-flannel-ds-mh9ks   1/1     Running   4 (7m22s ago)   3d17h   192.168.36.102   node01     <none>           <none>
kube-flannel-ds-prjqz   1/1     Running   3 (7m19s ago)   3d17h   192.168.36.103   node02     <none>           <none>

踩坑完毕,继续 GO~GO~GO~

第三阶段:加入工作节点 (在所有Worker节点执行)

在每个工作节点上,运行第一阶段保存的 kubeadm join 命令。

kubeadm join <控制平面节点的IP:6443> --token <令牌> --discovery-token-ca-cert-hash sha256:<哈希值>

依次在我的 Node01 和 Node02 两个节点上执行,没啥难度。token就是之前部署成功后输出的命令。

kubeadm join 192.168.36.101:6443 --token zjg02r.81m6chikxqoj2uxf \
        --discovery-token-ca-cert-hash sha256:bf104617ac122706d8198fa17771fcdf1246634f6be6a651bdac7db1a401ffb5

验证与测试

回到 Master节点,执行以下命令验证:

# 查看所有节点状态,应都为 Ready
kubectl get nodes

# 部署一个测试应用
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort

# 查看服务,获取NodePort端口
kubectl get svc nginx

访问任意节点的IP地址和NodePort端口(如 http://<节点IP>:30080),应能看到Nginx欢迎页。

天坑三号:

这应该是我碰到的最无语的坑了。如果没有配置镜像加速,你会发现你连一个最简单的Nginx的Deployment都无法创建。这就是containerd镜像加速的坑。如果是用docker的话,镜像加速很简单,地球人都会。

# Docker镜像加速方法:
cat <<EOF | tee /etc/docker/daemon.json
{ "registry-mirrors": [
 "https://docker.xuanyuan.me" ]
}
EOF

但是说实话,containerd就不咋会了,摸索了很久,在containerd v1.x和v2.x之间来回踩坑。最后梳理一下步骤:

1)确认containerd版本

root@master01:~# kubectl get node -o wide
NAME       STATUS                     ROLES           AGE     VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
master01   Ready                      control-plane   6d17h   v1.35.0   192.168.36.101   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://2.2.0
node01     Ready                      <none>          6d17h   v1.35.0   192.168.36.102   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://2.2.0
node02     Ready,SchedulingDisabled   <none>          6d17h   v1.35.0   192.168.36.103   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://2.2.0

2)修改配置文件

containerd v1.x版本是直接在 /etc/containerd/config.toml 文件内修改的,v2.0则做了优化,采用了外挂Plugin的方式。

cat /etc/containerd/config.toml
...
    [plugins.'io.containerd.cri.v1.images'.registry]
      config_path = '/etc/containerd/certs.d'
...

从配置文件可以看到,具体配置镜像加速需要在 /etc/containerd/certs.d 下创建目录。从查询结果总结,在 certs.d 下需要创建对应的镜像仓库域名目录,比如 docker.io 的镜像就需要创建一个 docker.io 的目录。另外可以创建一个 _default 目录,用于默认镜像加速的地址。

可参考结构如下,单个目录下需要创建一个 hosts.toml 文件,注意是hosts,不是host,不要问我为什么要注意:

root@master01:/etc/containerd/certs.d# tree
.
├── _default
│   └── hosts.toml
└── docker.io
    └── hosts.toml

具体命令如下:

  • _default 目录配置
mkdir /etc/containerd/certs.d/_default
touch /etc/containerd/certs.d/_default/hosts.toml
cat <<EOF | tee /etc/containerd/certs.d/_default/hosts.toml
server = "https://registry-1.docker.io"

[host."https://docker.m.daocloud.io"]
  capabilities = ["pull", "resolve"]

EOF
  • docker.io 目录配置
mkdir /etc/containerd/certs.d/docker.io
touch /etc/containerd/certs.d/docker.io/hosts.toml
cat <<EOF | tee /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://registry-1.docker.io"

[host."https://docker.m.daocloud.io"]
  capabilities = ["pull", "resolve"]

EOF

在配置文件过程中,我发现最终折腾我的地方是通过日志回溯发现的:我之所以无法启用镜像加速,原因竟是配置文件里有看不见的特殊字符,应该是我从网页上copy paste的时候失误了!这个问题大概困扰了我3个小时!

如果有问题需要排查,可以用如下命令查看containerd的日志,这也是一个基础的运维排错技巧。

journalctl -u containerd -f

containerd日志报错截图,显示key解析错误

填完坑以后,再看一下Nginx的实例状态,终于变成 Running 了。请记得在两台Node节点上同样配置镜像加速,同样的,不要问我为什么知道这个问题。

root@master01:~# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS      AGE   IP          NODE     NOMINATED NODE   READINESS GATES
nginx-56c45fd5ff-gnzhg   1/1     Running   2 (37m ago)   27h   10.10.1.6   node01   <none>           <none>

查看svc信息:

root@master01:~# kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP        6d17h   <none>
nginx        NodePort    10.110.67.9   <none>        80:31984/TCP   22h     app=nginx

验证Nginx访问,此处应有掌声~~~~

root@master01:~# curl -L 10.110.67.9
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

后续关键操作提醒

  • 令牌管理:初始化时生成的加入令牌默认24小时后过期。过期后,可在Master上用 kubeadm token create --print-join-command 生成新命令。
  • 集群升级:不可直接使用 apt upgrade。必须使用 kubeadm upgrade 进行规划式升级,并依次升级控制平面和工作节点。
  • 生产加固:考虑配置高可用控制平面、启用RBAC、设置网络策略等。

后面计划搭建一个NFS Server的虚机,通过NFS提供PVC功能,然后基于PVC建设私有镜像仓库Harbor。如果你对本文涉及的配置细节或踩坑过程有更深入的交流需求,欢迎到 云栈社区 的技术板块一起探讨。




上一篇:下一代核能技术详解:SMR与四代堆如何为AI数据中心供电?
下一篇:DeepEP源码分析:跨节点notify_dispatch的RDMA与NVLink同步机制剖析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-2 22:20 , Processed in 0.363691 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表