本文将介绍国内网络环境下,如何在 debian12 系统,使用 Kubeadm 搭建基于 containerd 容器运行、包含 Master 和 Worker 两节点的、目前(2024.08.12)最新最新版本(v1.30.3)的 kubernetes 集群。
主机规划
角色 | 主机名 | IP | 版本 | 配置 |
---|---|---|---|---|
Master Node | k8s-master | 192.168.0.210 | debian12 | 2C/4G/30G |
Worker Node 1 | k8s-worker01 | 192.168.0.211 | debian12 | 2C/4G/30G |
软件版本
软件名称 | 版本 | 备注 |
---|---|---|
PVE | v7.4-18 | |
KVM | debian12 | PVE 虚拟主机使用 debian12 cloud qcow2 镜像 |
containerd | v1.6.20 | 官方公布从 1.24 版本开始移除 dockershim |
kubernetes | v1.30.3 | docker 可 v1.23.6,其它推荐官方仍在维护的版本 |
calico | v3.28.1 | 注意版本需与 k8s 版本匹配,查看支持的 k8s 版本 |
dashboard | v2.7.0 | 官方从 v7.0.0 开始,仅支持 Helm 方式安装 |
主机设置
所有节点机器都执行。
设置主机名
设置主机名
# master 和 所有 woker 节点都执行
hostnamectl set-hostname "k8s-master" # Run on master node
hostnamectl set-hostname "k8s-worker01" # Run on 1st worker node
修改 /etc/hosts
映射关系
192.168.0.210 k8s-master
192.168.0.211 k8s-worker01
关闭防火墙
# Master node
sudo ufw allow 6443/tcp
sudo ufw allow 2379/tcp
sudo ufw allow 2380/tcp
sudo ufw allow 10250/tcp
sudo ufw allow 10251/tcp
sudo ufw allow 10252/tcp
sudo ufw allow 10255/tcp
sudo ufw reload
# Worker nodes
sudo ufw allow 10250/tcp
sudo ufw allow 30000:32767/tcp
sudo ufw reload
# 或者直接关闭防火墙
sudo ufw disable
关闭 swap 内存
# 临时关闭
swapoff -a
# 以“#”符号注释已久关闭
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 查看状态
free -m
设置内核参数
# 开机加载 overlay、br_netfilter 模块
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# 确认模块加载
lsmod | grep -E 'overlay|br_netfilter'
# 将桥接的 IPv4 流量传递到 iptables 的链
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
# 使修改生效
sysctl --system
同步时区
apt-get install ntp
ntpdate time.windows.com
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
sudo apt-get install ntp
sudo apt-get install ntpdate
sudo ntpdate ntp.ubuntu.com
配置免密登录
# https://www.cnblogs.com/binliubiao/p/14823221.html
# https://lework.github.io/2019/06/30/k8s-ha-bin-install/
# 在 k8s-master 操作
apt install sshpass -y
ssh-keygen -t rsa -P '' -f /root/.ssh/id_rsa
for NODE in k8s-master k8s-worker01 k8s-worker02; do
echo "--- $NODE ---"
sshpass -p 123456 ssh-copy-id -o "StrictHostKeyChecking no" -i /root/.ssh/id_rsa.pub ${NODE}
ssh ${NODE} "hostnamectl set-hostname ${NODE}"
done
# 其中 123456 是服务器的密码
安装 containerd run time
所有节点机器都执行。
k8s 官方公布从 1.24 版本开始移除 dockershim
containerd 实现了 kubernetes 的 Container Runtime Interface (CRI) 接口,提供容器运行时核心功能,如镜像管理、容器管理等,相比 dockerd 更加简单、健壮和可移植。
apt update
apt install containerd -y
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
# 配置 containerd 用 systemdcgroup 启动
# 配置与 kubeadm init 指定一样的镜像源
sed -i "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml
sed -i "s/sandbox_image = \"registry\.k8s\.io\/pause:3\.6\"/sandbox_image = \"registry\.aliyuncs\.com\/google_containers\/pause:3\.9\"/g" /etc/containerd/config.toml
# 重启服务
systemctl enable containerd
systemctl restart containerd
systemctl status containerd
# 查看版本
containerd --version
# containerd github.com/containerd/containerd 1.6.20~ds1 1.6.20~ds1-1+b1
安装 kubernetes 工具
所有节点机器都执行。
安装 kubeadm、kubelet、kubectl 工具
kubectl
: 用以控制集群的客户端工具kubeadm
: 用以构建一个 k8s 集群的官方工具kubelet
: 工作在集群的每个节点,负责容器的一些行为如启动
# 添加源和 GPG 证书
apt install gpg -y
# 非 v1.30.3,其它版本更改对应的版本号
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# 安装
apt update
apt install kubelet kubeadm kubectl -y
# apt-mark 用于将软件包标记/取消标记为自动安装。 hold 选项用于将软件包标记为保留,以防止软件包被自动安装、升级或删除。
apt-mark hold kubelet kubeadm kubectl
# 非 v1.30.3,其它版本更改对应的版本号
# apt-get remove --purge kubelet kubeadm kubectl
# apt search kubelet && apt search kubeadm && apt search kubectl
# apt install kubelet=1.30.3-1.1 kubeadm=1.30.3-1.1 kubectl=1.30.3-1.1
# apt install kubelet=1.30.3* kubeadm=1.30.3* kubectl=1.30.3*
# 检查版本
kubeadm version
# kubeadm version: &version.Info{Major:"1", Minor:"30", GitVersion:"v1.30.3", GitCommit:"6fc0a69044f1ac4c13841ec4391224a2df241460", GitTreeState:"clean", BuildDate:"2024-07-16T23:53:15Z", GoVersion:"go1.22.5", Compiler:"gc", Platform:"linux/amd64"}
kubelet --version
# Kubernetes v1.30.3
kubectl version
# Client Version: v1.30.3
# Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
# Server Version: v1.30.3
初始化节点
拉取镜像在所有节点机器都执行,然后依机器的情况,分别执行【初始化 master 节点】或者【初始化 worker 节点】二选一。
# 提前拉取镜像
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers
# 查看镜像
crictl images
初始化 master 节点
使用 kubeadm 初始化 master 节点或首节点。
- 不要在非 master 节点上运行
kubeadm init
,如果在非 master 节点上运行了kubeadm init
,它会尝试初始化该节点为一个新的控制平面节点,导致该节点不能正常加入现有集群。 - 如果要将一个非 master 节点加入到现有的 Kubernetes 集群中并,成为一个 worker 节点,在 master 节点上生成
kubeadm join
命令, 并在非 master 节点(worker 节点)上运行即可。 --image-repository
指定 docker 镜像的仓库地址,用于下载 kubernetes 组件所需的容器镜像。因为 k8s 的很多镜像都在外网无法访问,所以这里使用了阿里云容器镜像地址。注意,即使提前拉取了镜像,这里也要指定相同的仓库,否则还是会拉取官方镜像如果访问不了导致拉取失败。--ignore-preflight-errors=NumCpu
如果你的服务器不足 2 核 cpu 可以添加,来忽略错误。- 如果你在 init 后因发生任何异常导致初始化终止了,可以使用
kubeadm reset -f
强制重置之后再重新进行初始化,注意该命令会将此 master 完全重置。 - 命令 kubeam init 完整参数见官方文档 https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-init/
# 初始化 kubeadm
kubeadm init --image-repository registry.aliyuncs.com/google_containers
# 报错查看日志
systemctl status kubelet
journalctl -xeu kubelet
# # 初始化 kubeadm,方法二,使用 config 模式
# https://www.cnblogs.com/wswind/p/14972730.html
# cat /var/lib/kubelet/config.yaml
# kubeadm config print init-defaults > kubeadm.yaml
# 修改节点名
# sed -i "s/name: node/name: k8s-master/g" kubeadm.yaml
# 修改镜像仓库
# sed -i "s/imageRepository: registry\.k8s\.io/imageRepository: registry\.cn-hangzhou\.aliyuncs\.com\/google_containers/g" kubeadm.yaml
# sed -i "s/advertiseAddress: 1\.2\.3\.4/advertiseAddress: 192\.168\.0\.212/g" kubeadm.yaml
# 把 kubelet 的 cgroup 驱动配置为 systemd
# tee -a kubeadm.yaml <<EOF
# EOF
# cat <<EOF >> kubeadm.yaml
# ---
# # Added by How,for kubelet systemd cgroup
# apiVersion: kubelet.config.k8s.io/v1beta1
# kind: KubeletConfiguration
# cgroupDriver: systemd
# EOF
# kubeadm init --config=kubeadm.yaml
# 成功后按照提示执行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf
# 查看已加入的节点,k8s-master 节点执行
kubectl get nodes
# 查看集群状态,k8s-master 节点执行
kubectl get cs
如果错过初始化 Kubernetes 集群时输出的用于将其它节点加入集群的 kubeadm join 命令或者需要再次查看它。
可以在 master 节点通过以下方式来获取
添加 master 节点的命令获取方式
# 获取新的 certificate-key
kubeadm init phase upload-certs --upload-certs
# 生成添加 master 节点的命令
kubeadm token create --print-join-command --certificate-key <your-new-certificate-key>
添加 worker 节点的命令获取方式
# 添加 worker 节点的命令获取方式
kubeadm token create --print-join-command
初始化 worker 节点
依照上文的 【添加 worker 节点的命令获取方式】 章节,在 master 节点上获取到添加非 master(worker 节点)节点的 kubeadm join
命令执行。
kubeadm join 192.168.0.210:6443 --token 80rfzl.wfa1lardiybcwl03 --discovery-token-ca-cert-hash sha256:d33d29d7afc98aa08a7134e3ba0aaa1a25498a25cd250f9871d7c9c659a78164
# 配置 kubectl 命令工具
# https://github.com/chaseSpace/k8s-tutorial-cn/blob/main/install_by_kubeadm/install.md#55-%E5%9C%A8%E6%99%AE%E9%80%9A%E8%8A%82%E7%82%B9%E6%89%A7%E8%A1%8Ckubectl
# 普通节点执行 kubectl get nodes 提示错误:memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp [::1]:8080: connect: connection refused
mkdir ~/.kube && cp /etc/kubernetes/kubelet.conf ~/.kube/config
# 执行完成后,可以尝试使用如下两条命令测试 kubectl 是否可用
# 查看已加入的节点,k8s-worker01 节点执行
kubectl get nodes
# 从主节点运行以下命令来检查节点状态,k8s-master 节点执行
kubectl get cs
现在有了 master 和 node 节点,但是所有节点状态都是 NotReady
,这是因为没有 CNI 网络插件。
安装 CNI 网络插件
安装 CNI 网络插件在所有节点机器执行。
Kubernetes 需要网络插件 (Container Network Interface: CNI) 来提供集群内部和集群外部的网络通信。
常用的 k8s 网络插件有 Flannel、Calico、Canal、Weave-net 等。
Calico 性能更强,Flannel 更简单方便。
这里选择 Calico,注意版本需要匹配 k8s 版本,否则无法应用,查看支持的 k8s 版本。
# flannel 的安装参考,但是 CNI插件部分可以结合看 “CNI插件,kubelet组件在启动时,在命令行选项 --network-plugin=cni 来选择CNI插件。它会自动搜索 --cni-bin-dir (default /opt/cni/bin)指定目录下的网络插件,并使用 --cni-conf-dir (default /etc/cni/net.d) 目录下配置文件设置每个Pod的网络。CNI配置文件引用的插件必须--cni-bin-dir目录中。新版k8s 不需要单独安装CNI, calico自带有cni插件 —— https://www.cnblogs.com/binliubiao/p/14823221.html”
# 在 Kubernetes 1.24 之前,CNI 插件也可以由 kubelet 使用命令行参数 cni-bin-dir 和 network-plugin 管理。Kubernetes 1.24 移除了这些命令行参数, CNI 的管理不再是 kubelet 的工作。—— https://kubernetes.io/zh-cn/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/
# https://www.jianshu.com/p/fde6c8a90706
# k8s集群中的pod通信依赖于网络插件我使用的是flannel,应用前可先浏览配置。注意master初始化集群时的--pod-network-cidr参数需要与 kube-flannel.yml 中的 net-conf.json:Network 保持一致,
# https://www.cnblogs.com/wswind/p/14972730.html
# https://github.com/chaseSpace/k8s-tutorial-cn/blob/main/install_by_kubeadm/install.md#54-%E5%AE%89%E8%A3%85%E7%AC%AC%E4%B8%89%E6%96%B9%E7%BD%91%E7%BB%9C%E6%8F%92%E4%BB%B6
# https://blog.csdn.net/u010978399/article/details/136850644
# https://www.linuxtechi.com/install-kubernetes-cluster-on-debian/
wget --no-check-certificate https://raw.gitmirror.com/projectcalico/calico/v3.28.1/manifests/calico.yaml
kubectl delete -f calico.yaml
kubectl apply -f calico.yaml
# 查看 pod 状态
kubectl get pods -n kube-system --watch
# 报错看日志( Init:ImagePullBackOff )
kubectl describe pod calico-node-hv4ss -n kube-system
# 查看 pod 部署的所在 node
kubectl get pods -n kube-system -o wide
# 手动拉取镜像(在所有节点都执行)
export http_proxy=http://192.168.0.104:10809 && export https_proxy=http://192.168.0.104:10809 && export no_proxy="localhost, 127.0.0.1, ::1"
ctr -n k8s.io image pull docker.io/calico/cni:v3.28.1
ctr -n k8s.io image pull docker.io/calico/node:v3.28.1
ctr -n k8s.io image pull docker.io/calico/kube-controllers:v3.28.1
# 删除 Calico pod,让其重启
kubectl get pods -n kube-system | grep calico-node-hv4ss | awk '{print$1}'| xargs kubectl delete -n kube-system pods
kubectl describe po calico-node-hv4ss -n kube-system
# 防火墙中允许 Calico 端口,在所有节点上运行 ufw 命令
sudo ufw allow 179/tcp
sudo ufw allow 4789/udp
sudo ufw allow 51820/udp
sudo ufw allow 51821/udp
sudo ufw reload
# 验证 Calico pod 的状态,运行
kubectl get pods -n kube-system
# 查看 nodes 状态
kubectl get nodes
再次检查节点状态,确认主节点和工作节点处于就绪(Ready
)状态。现在,集群已准备好处理工作负载。
验证集群
通过在集群中部署 nginx 服务来验证集群是否正常工作。
# https://github.com/chaseSpace/k8s-tutorial-cn/blob/main/install_by_kubeadm/install.md#6-%E9%AA%8C%E8%AF%81%E9%9B%86%E7%BE%A4
# https://www.linuxtechi.com/install-kubernetes-cluster-on-debian/
# 创建
kubectl create deployment nginx-test-deployment --image=nginx:1.27-alpine --replicas 2
# 通过 NodePort 类型的 Service 来暴露 Pod
kubectl expose deployment nginx-test-deployment --name=nginx-test-service --type NodePort --port 80 --target-port 80
# 查看 pod 及服务信息
kubectl get pod,svc
kubectl describe svc nginx-test-service
# root@k8s-master:~/k8s/blog-in-kubernetes# kubectl get pod,svc
# NAME READY STATUS RESTARTS AGE
# pod/nginx-test-deployment-6876b4697f-68wnt 1/1 Running 0 13s
# pod/nginx-test-deployment-6876b4697f-dp66k 1/1 Running 0 13s
#
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# service/nginx-test-service NodePort 10.102.158.74 <none> 80:31493/TCP 6s
# 它将容器 80 端口映射到【所有节点】的一个随机端口(这里是 31493 )。 我们通过访问节点端口来测试在所有集群机器上的 pod 的连通性。
# 在 k8s-master(192.168.0.210)上执行
curl http://192.168.0.210:31493
curl http://192.168.0.211:31493
# k8s-master shell 打不开,局域网其它机器(192.168.0.104)浏览器可以打开
curl http://k8s-worker01:31493
# 删除部署
kubectl delete deployment nginx-test-deployment
kubectl delete svc nginx-test-service
安装 Dashboard
Kubernetes Dashboard 是 Kubernetes 开发的基于 Web 的面板,用户可以用它部署容器化的应用、监控应用的状态、执行故障排查任务以及管理 Kubernetes 各种资源。
从 7.0.0 版开始仅支持基于 Helm 的安装。这里用的 v2.7.0 版本,使用 yaml 文件的安装。
# https://github.com/kubernetes/dashboard/blob/v2.7.0/docs/user/access-control/creating-sample-user.md
# https://www.jianshu.com/p/fde6c8a90706
# https://zhuanlan.zhihu.com/p/91731765
# 下载 recommended.yaml 文件
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# 改为 NodePort 访问
# ...
# spec:
# ports:
# - port: 443
# targetPort: 8443
# name: https # 新加 name
# nodePort: 32001 # 新加 nodePort
# type: NodePort # 新加 nodePort
# selector:
# ...
# 安装
kubectl apply -f dashboard-recommended.yaml
kubectl delete -f dashboard-recommended.yaml
# 查看 pod 名称
kubectl get pods --namespace=kubernetes-dashboard -o wide
# 报错看日志
kubectl describe pod dashboard-metrics-scraper-795895d745-8fvbg --namespace=kubernetes-dashboard
# 手动拉取镜像(在所有节点都执行)
export http_proxy=http://192.168.0.104:10809 && export https_proxy=http://192.168.0.104:10809 && export no_proxy="localhost, 127.0.0.1, ::1"
ctr -n k8s.io image pull docker.io/kubernetesui/metrics-scraper:v1.0.8
ctr -n k8s.io image pull docker.io/kubernetesui/dashboard:v2.7.0
# 创建用户
# 创建文件 dashboard-adminuser.yaml
# apiVersion: v1
# kind: ServiceAccount
# metadata:
# name: admin-user
# namespace: kubernetes-dashboard
# ---
# apiVersion: rbac.authorization.k8s.io/v1
# kind: ClusterRoleBinding
# metadata:
# name: admin-user
# roleRef:
# apiGroup: rbac.authorization.k8s.io
# kind: ClusterRole
# name: cluster-admin
# subjects:
# - kind: ServiceAccount
# name: admin-user
# namespace: kubernetes-dashboard
# ---
# # 长期 token
# apiVersion: v1
# kind: Secret
# metadata:
# name: admin-user
# namespace: kubernetes-dashboard
# annotations:
# kubernetes.io/service-account.name: "admin-user"
# type: kubernetes.io/service-account-token
# 执行
kubectl apply -f dashboard-adminuser.yaml
# 临时 token
kubectl -n kubernetes-dashboard create token admin-user
# 获取长期 token
kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath={".data.token"} | base64 -d
# eyJhbGciOiJSUzI1NiIsImtpZCI6IkxEbzRCOEZxQ1ZrQnBHd1VkdjRsZzRPN1cxc1lNci1FQUozOEpMaTdyOFkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI1ZGQ3YjFhNi1iMWNiLTQ2NmItYjIzMC1mNGIyMWFkOTlhNDYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.ScCyX4_4DCSzYyX-lQK0LvR6MKJ5u0I3hsSs69Wy4ntSWu0hZBc0JUP1sA3Ku-5B-hdozUQVvxRz2kREcgL8aRmc57TrnzoatdJVfxYxXWbbsLcfUxpeTZHsfM9FpclIYb6ORRQSg3gdhVKcpiam5Bn7lpdF_zqWiBT1pvUw0NaBulHEGZHzyNSTg56EXmHIWLnrXveUlti_-Wv-esIRa0B9195_eKx-TYjLtYwyK2W1sMnuopbP0iYyPUm1FQQEnEWQydVxiGv3beXt7MfMJGxIRX-2aeqLsOumvxnl8Q5YL_9ulmnZAwC-Zp89SmnAEVik3VCMXPJfhecNyn719g
访问填写上步骤生成的 token
https://192.168.0.210:32001/#/workloads?namespace=default
至此,整个 k8s 集群已经搭建完毕。