kubeadm部署K8S集群(踩坑实录)

上一次用kubeadm部署k8s还是在2023年,那个时候应该还是k8s v1.22版本,虽然当时容器运行时已经变成了containerd,但是我还是习惯用docker,所以当时还是用了docker,第一次部署大概花了2天左右,踩了不少坑。 这次突然心血来潮,想重新部署一下最新版k8s v1.35, 本来想装逼一把,用二进制部署k8s,结果搞到证书的部署就懵逼了,装逼失败,卒。遂改成kubeadm部署,原本以为会很快,毕竟之前部署过好几次,算是轻车熟路的,但是没想到这次前前后后搞了5天左右才搞定,因此记录一下自己的踩坑记录,应该是全网最细节的踩坑实录了。 只要照着我贴出来的命令,基本100%可以在45分钟内完成部署,且不需要翻墙。

节点规划

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

部署详细步骤

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

第一阶段:所有节点通用准备 (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
  1. 安装容器运行时 推荐使用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组件镜像的管理,就是crictl和ctr两个命令。 简单来说,crictl 是专门为 Kubernetes 设计的,而 ctrcontainerd 自身的通用管理工具。

  1. 面向用户与场景

    • **crictl**:主要供 Kubernetes 集群管理员和开发者 使用,用于在节点上调试容器运行时、查看 Pod 状态、检查容器日志等,是 K8s 环境下的标准调试工具。

    • ctr:更偏向 容器运行时维护者和高级用户,用于对 containerd 进行底层的操作和排错,例如管理快照、直接操作镜像内容等。

  2. 与 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

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命令,会有wanring,比较麻烦,作为金牛座,虽然不求完美,但是不喜欢报错

复制代码
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
  1. 配置kubectl 使当前用户可以管理集群。

    复制代码
    mkdir -p $HOME/.kube
    cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    chown $(id -u):$(id -g) $HOME/.kube/config
  2. 安装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部署时候的网络地址段。

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

Flannel涉及的网络插件镜像组件地址至少我可以下载,如果你还是下不了,就参考前面k8s的方式,用crictl和ctr的组合。组件就如下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就不咋地会了,摸索了很久,在containerdv1.x和v2.x之间来回踩坑。 最后梳理一下步骤,先看一下containerd版本信息,最终确认我是v2.2 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)修改配置文件。containerdv1.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下创建。从百度/google/deepseek的查询结果总结,在certs.d下需要创建对应的镜像网站,比如docker.io的镜像就需要创建一个docker.io的目录,k8s.gcr就需要创建k8s.gcr, 另外可以创建一个_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  
server = "https://registry-1.docker.io"

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

EOF 

docker.io

复制代码
mkdir /etc/containerd/certs.d/dock.io
touch /etc/containerd/certs.d/dock.io/hosts.toml
cat <<EOF | tee /etc/containerd/certs.d/dock.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

填完坑以后,再看一下Nginx的实例状态,终于变成Runing了,如果你的没有变化,请记得在两台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

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

复制代码
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建设私有镜像仓库Habor

相关推荐
牛奶咖啡132 小时前
Prometheus+Grafana构建云原生分布式监控系统(十三)_Prometheus数据模型及其PromQL
云原生·prometheus·prometheus数据类型·promql使用场景·promql表达式解析·promql数据类型·监控系统的方法论与指标
等什么君!2 小时前
Docker 数据卷:MySQL 数据同步实战
运维·docker·容器
礼拜天没时间.3 小时前
《Docker实战入门与部署指南:从核心概念到网络与数据管理》:环境准备与Docker安装
运维·网络·docker·容器·centos
张小凡vip3 小时前
Kubernetes---存储方案:Rook自动结合Ceph
ceph·容器·kubernetes
Cyber4K3 小时前
【Kubernetes专项】K8s 控制器 StatefulSet 从入门到企业实战应用
云原生·容器·kubernetes
风流倜傥唐伯虎3 小时前
Windows 版 Docker 的 Linux 环境(docker-desktop)与 builder-jammy-base:latest 镜像核心区别
linux·docker·容器
Ha_To3 小时前
2026.1.30 搭建docker仓库
运维·docker·容器
lpfasd1233 小时前
Docker Desktop 在国内使用的囧境:镜像拉取失败、加速器失效与破局之道
运维·docker·容器
江湖有缘3 小时前
Docker部署SurveyKing调查问卷系统和考试系统
运维·docker·容器