使用kubeadm搭建K8s多节点集群
目录
使用kubeadm搭建K8s多节点集群
1. 准备资源2. 安装容器运行时
2.1 介绍2.2 Linux支持的CRI的端点2.3 安装Containerd 3. 安装三大件4. 配置cgroup driver5. 创建集群
5.1 在master上初始化集群5.2 准备用户的 k8s 配置文件5.3 其他节点加入集群5.4 安装第三方网络插件5.5 在普通节点执行kubectl5.6 删除集群 6. 验证集群7. 疑难解决
7.1 解决calico镜像下载较慢的问题7.2 解决calico密钥过期问题7.3 解决k8s证书过期问题7.4 安装其他网络插件-flannel 参考
为了提高命令行使用效率,建议先[安装ohmyzsh]。
yum install -y git zsh wget
wget https://gitee.com/mirrors/oh-my-zsh/raw/master/tools/install.sh
vi install.sh
# 修改下面两行
# REPO=${REPO:-ohmyzsh/ohmyzsh}
# REMOTE=${REMOTE:-https://github.com/${REPO}.git}
# 为
# REPO=${REPO:-mirrors/oh-my-zsh}
# REMOTE=${REMOTE:-https://gitee.com/${REPO}.git}
# 保存 并 执行
chmod +x install.sh && ./install.sh
# 修改主题
ls ~/.oh-my-zsh/themes
vi ~/.zshrc
# 找到 ZSH_THEME 行,修改为自己想用的主题名称即可
# 安装插件
git clone https://gitee.com/jsharkc/zsh-autosuggestions.git $ZSH_CUSTOM/plugins/zsh-autosuggestions
git clone https://gitee.com/jsharkc/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting
# 配置插件
sed -i 's/plugins=(git)/plugins=(git zsh-autosuggestions zsh-syntax-highlighting)/' ~/.zshrc
# 设置别名
echo 'alias kk="kubectl"' >> ~/.zshrc
echo 'alias m="minikube"' >> ~/.zshrc # 如果安装了minikube
# 生效
source ~/.zshrc
123456789101112131415161718192021222324252627282930
此外,本教程演示安装的K8s版本为 v1.27.0,你也可以选择其他版本,但最好是官方仍在维护的版本。
在生产环境中,你不必自行安装维护K8s集群基础组件,可以使用云厂商提供的K8s服务,如腾讯云 TKE和阿里云 ACK,国外有AWS
EKS、Google
GKE、Azure
AKS等。
1. 准备资源
10.0.2.2 k8s-master
10.0.2.3 k8s-node1
12
两台机,最低配置如下:
cpu: 2c+mem: 2g+disk: 20g+network: 同属一个子网
在实战中,master节点配置通常是中高配置(如4c8g,8c16g),虽然k8s不会调度pod到master上运行,但由于Master是整个 Kubernetes
集群的核心部分,负责协调、管理和调度所有工作负载。并且Master节点上运行着各种关键组件(如
etcd、kube-apiserver、kube-controller-manager 和 kube-scheduler),这些组件都需要处理大量的网络流量和计算任务。
2. 安装容器运行时
2.1 介绍
容器运行时是指用于直接对镜像和容器执行基础操作(比如拉取/删除镜像和对容器的创建(使用镜像)/查询/修改/获取/删除等操作)的软件。
最开始的K8s版本只支持Docker作为容器运行时,但为了更好与底层容器技术解耦(同时也是为了兼容其他容器技术),K8s
在v1.5.0就引入了容器运行时接口(CRI)。CRI是K8s与第三方容器运行时通信接口的标准化抽象,它定义了容器运行时必须实现的一组标准接口,
包括前面所说的针对镜像和容器的各项基础操作。
K8s通过CRI支持多个容器运行时,包括Docker、containerd、CRI-O、qemu等,
更多请查看CNCF旗下的容器运行时列表。
后来之所以K8s要宣称放弃Docker(在K8s
v1.20)而选择container作为默认容器运行时,是因为Docker并不只是一个容器软件,而是一个完整的技术堆栈,它包含了许多除了容器软件基础功能以外的东西,这些东西不是K8s所需要的,而且增加K8s调度容器的性能开销。
由于Docker本身并不支持CRI(在提出CRI的时候),所以K8s代码库(自v1.5.0起)中实现了一个叫做docker-shim的CRI兼容层来作为中间垫片连接了Docker与K8s。
K8s官方表示,一直以来,维护docker-shim都是一件非常费力不讨好的事情,它使K8s的维护变得复杂,所以当CRI稳定之后,K8s立即在代码库中添加了docker-shim即将废弃的提示(v1.20),
如果使用Docker作为运行时,在kubelet启动时打印一个警告日志。最终在K8s v1.24中删除了docker-shim相关的代码。
官方文档:废弃Dockershim
如果在K8s v1.20及以后版本依然使用Docker作为容器运行时,需要安装配置一个叫做cri-dockerd的组件(作用类似docker-shim),它是一个轻量级的守护进程,用于将Docker请求转换为CRI请求。
官方文档:从dockershim迁移到cri-dockerd
关于containerd,其实它就是Docker内部的容器运行时,只是Docker将containerd封装了一层,并且添加了其他有助于用户使用Docker的多项功能。
K8s废弃了docker-shim以后,Docker公司也声明了会和Mirantis公司继续维护docker-shim(作为一个Docker内的一个组件)。
然而,K8s的这一系列操作不仅仅是针对容器运行时,还包括容器网络层、容器存储层都制定了相应的接口规范,分别叫做CNI和CSI。此外,
还有一个叫[OCI][OCI](Open Container Initiative)的组织,它标准化了容器工具和技术之间的许多接口,包括容器映像打包(OCI
image-spec)和运行容器(OCI runtime-spec)的标准规范,这就让来自不同厂商开发的容器产品(镜像/运行时)能够更好的协同工作。
CRI也是建立在这些底层规范的基础上,为管理容器提供了一个端到端的标准。
2.2 Linux支持的CRI的端点
Runtime | Path to Unix domain socket |
---|---|
containerd | unix:///var/run/containerd/containerd.sock |
CRI-O | unix:///var/run/crio/crio.sock |
Docker Engine (using cri-dockerd) | unix:///var/run/cri-dockerd.sock |
这里只列出了常见的容器运行时及对应的socket端点,对于其他容器运行时,你会在它们的安装文档中看到对应的端点路径。
containerd对CRI的支持最开始也是单独的一个项目,叫做[cri][cri](但对外叫
cri-containerd
),后来被集成到containerd中。
2.3 安装Containerd
kubernetes 1.24.x及以后版本默认CRI为containerd。安装containerd时自带的命令行工具是ctr
,我们可以使用ctr
来直接管理containerd中的镜像或容器资源(包括由K8s间接管理的)。
由K8s间接管理的镜像和容器资源都存放在containerd中名为
k8s.io
的命名空间下,例如你可以(在安装完集群后)通过ctr -n k8s.io c ls
查看K8s在当前节点调度的容器列表。
而K8s提供的基于CRI的命令行工具则是crictl
,会在下一节中安装K8s基础组件时自动安装。例如你可以通过 crictl ps
查看K8s在当前节点调度的容器列表,使用crictl -h
查看使用帮助。
在所有机器上运行以下命令:
# - 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# - 设置源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install containerd -y
containerd --version
# 创建或修改配置,参考下面的文字说明
# vi /etc/containerd/config.toml
systemctl enable containerd # 开机启动
systemctl daemon-reload
systemctl restart containerd
systemctl status containerd
1234567891011121314151617
对于/etc/containerd/config.toml
文件,我们需要修改其中关于镜像源部分的配置,以实现部分镜像仓库源的镜像下载加速。修改的位置关键字为:registry.mirrors
, sandbox_image
。
你也可以使用仓库中的 containerd.config.toml 进行覆盖(先备份现有的)。
3. 安装三大件
即 kubeadm、kubelet 和 kubectl。
在centos上安装(所有节点):
# 设置阿里云为源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
# centos 安装各组件
# -- 你也可以仅在一个节点安装kubectl,用于管理集群
sudo yum install -y wget lsof net-tools jq
kubelet-1.27.0 kubeadm-1.27.0 kubectl-1.27.0 --disableexcludes=kubernetes
# 开机启动,且立即启动
sudo systemctl enable --now kubelet
# 检查版本
kubeadm version
kubelet --version
kubectl version
# 配置容器运行时,以便后续通过crictl管理 集群内的容器和镜像
crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock
123456789101112131415161718192021222324252627
如果是ubuntu系统(供参考):
apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
# 设置阿里源
cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet=1.27.0-00 kubeadm=1.27.0-00 kubectl=1.27.0-00
# 查看软件仓库包含哪些版本 apt-cache madison kubelet
# 删除 apt-get remove -y kubelet kubeadm kubectl
12345678910111213
注意更新节点时间(部署的Pod资源会使用节点的时间):
yum install -y ntpdate
ntpdate -u pool.ntp.org
12
4. 配置cgroup driver
在 Kubernetes 集群中,为了确保系统资源的一致性和协同工作,kubelet 和容器运行时的配置需要匹配。其中一个关键的配置项是 cgroup
驱动。kubelet 是 Kubernetes 集群中的节点代理,负责与容器运行时通信,而 cgroup 驱动则决定了 kubelet 如何在底层 Linux
系统上组织和控制容器的资源。
这里分为两个步骤:
配置容器运行时 cgroup 驱动配置 kubelet 的 cgroup 驱动
对于第一步,本文编写时安装的containerd(K8s使用的容器运行时)默认使用systemd
作为croup驱动,所以无需配置。
而第二步,从K8s v1.22起,kubeadm也默认使用systemd
作为 kubelet 的cgroupDriver。
本节只做必要的步骤说明,由于演示安装的是v1.27.0版本,并不需要执行配置操作。如果想要了解更多细节,
可以参考官方文档。
5. 创建集群
下面的命令需要在所有机器上执行。
设置hosts
# 建议主机ip与教程一致
cat <<EOF >> /etc/hosts
10.0.2.2 k8s-master
10.0.2.3 k8s-node1
EOF
12345
设置每台机器的hostname
# 在master节点执行
hostnamectl set-hostname k8s-master
# 在node1节点执行
hostnamectl set-hostname k8s-node1
12345
logout后再登录可见。
# 关闭swap:
swapoff -a # 临时关闭
sed -ri 's/.*swap.*/#&/' /etc/fstab #永久关闭
# 关闭selinux
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
# 关闭防火墙
iptables -F
iptables -X
systemctl stop firewalld.service
systemctl disable firewalld
12345678910111213
设置sysctl
cat > /etc/sysctl.conf << EOF
vm.swappiness=0
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
EOF
sysctl -p # 生效
cat <<EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
EOF
sysctl --system # 生效
# 加载内核模块
modprobe br_netfilter # 网络桥接模块
modprobe overlay # 联合文件系统模块
lsmod | grep -e br_netfilter -e overlay
123456789101112131415161718192021222324252627
5.1 在master上初始化集群
# 提前拉取需要的image
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers
# 查看拉取的镜像
$ crictl images
IMAGE TAG IMAGE ID SIZE
registry.aliyuncs.com/google_containers/coredns v1.9.3 5185b96f0becf 14.8MB
registry.aliyuncs.com/google_containers/etcd 3.5.6-0 fce326961ae2d 103MB
registry.aliyuncs.com/google_containers/kube-apiserver v1.27.0 48f6f02f2e904 35.1MB
registry.aliyuncs.com/google_containers/kube-controller-manager v1.27.0 2fdc9124e4ab3 31.9MB
registry.aliyuncs.com/google_containers/kube-proxy v1.27.0 b2d7e01cd611a 20.5MB
registry.aliyuncs.com/google_containers/kube-scheduler v1.27.0 62a4b43588914 16.2MB
registry.aliyuncs.com/google_containers/pause 3.8 4873874c08efc 311kB
registry.cn-hangzhou.aliyuncs.com/google_containers/pause 3.6 6270bb605e12e 302kB
# 初始化集群
# --apiserver-advertise-address 指定 Kubernetes API Server 的宣告地址,可以不设置让其自动检测
# 其他节点和客户端将使用此地址连接到 API Server
# --image-repository 指定了 Docker 镜像的仓库地址,用于下载 Kubernetes 组件所需的容器镜像。在这里,使用了阿里云容器镜像地址,可以加速镜像的下载。
# 注意:即使提取拉取了镜像,这里也要指定相同的仓库,否则还是会拉取官方镜像
# --service-cidr 指定 Kubernetes 集群中 Service 的 IP 地址范围,Service IP 地址将分配给 Kubernetes Service,以允许它们在集群内通信
# --pod-network-cidr 指定 Kubernetes 集群中 Pod 网络的 IP 地址范围。Pod IP 地址将分配给容器化的应用程序 Pod,以便它们可以相互通信。
$ kubeadm init
--image-repository registry.aliyuncs.com/google_containers
--kubernetes-version v1.27.0
--service-cidr=20.1.0.0/16
--pod-network-cidr=20.2.0.0/16
[init] Using Kubernetes version: v1.27.0
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
... 省略
12345678910111213141516171819202122232425262728293031323334
[k8s-cluster-init.log]是一个k8s集群初始化日志实例。
[root@k8s-master ~]# kubeadm init --apiserver-advertise-address=10.0.2.2 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.27.0 --service-cidr=20.1.0.0/16 --pod-network-cidr=20.2.0.0/16
[init] Using Kubernetes version: v1.27.0
[preflight] Running pre-flight checks
[WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function correctly
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [20.1.0.1 10.0.2.2]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-master localhost] and IPs [10.0.2.2 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-master localhost] and IPs [10.0.2.2 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 9.006817 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-master as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: vqb857.z9mcxh8s8kft5pb1
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
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 10.0.2.2:6443 --token 4iwa6j.ejrsfqm26jpcshz2
--discovery-token-ca-cert-hash sha256:f8fa90012cd3bcb34f3198b5b6184dc45104534f998ee601ed97c39f2efa8b05
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
注意暂存日志输出的最后部分:
...
kubeadm join 10.0.2.2:6443 --token 4iwa6j.ejrsfqm26jpcshz2
--discovery-token-ca-cert-hash sha256:f8fa90012cd3bcb34f3198b5b6184dc45104534f998ee601ed97c39f2efa8b05
123
这是一条普通节点加入集群的命令。其中包含一个临时token和证书hash,如果忘记或想要查询,通过以下方式查看:
# 1. 查看token
$ kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
4iwa6j.ejrsfqm26jpcshz2 23h 2023-11-13T08:32:40Z authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token
# 2. 查看cert-hash
$ openssl x509 -in /etc/kubernetes/pki/ca.crt -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256
(stdin)= f8fa90012cd3bcb34f3198b5b6184dc45104534f998ee601ed97c39f2efa8b05
12345678
token默认有效期24h。在过期后还想要加入集群的话,我们需要手动创建一个新token:
# cert-hash一般不会改变
$ kubeadm token create --print-join-command
kubeadm join 10.0.2.2:6443 --token eczspu.kjrxrem8xv5x7oqm --discovery-token-ca-cert-hash sha256:f8fa90012cd3bcb34f3198b5b6184dc45104534f998ee601ed97c39f2efa8b05
$ kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
4iwa6j.ejrsfqm26jpcshz2 23h 2023-11-14T08:25:28Z authentication,signing <none> system:bootstrappers:kubeadm:default-node-token
eczspu.kjrxrem8xv5x7oqm 23h 2023-11-14T08:32:40Z authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token
1234567
5.2 准备用户的 k8s 配置文件
以便用户可以使用 kubectl 工具与 Kubernetes 集群进行通信,下面的操作只需要在master节点执行一次。
若是root用户,执行:
echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> /etc/profile
source /etc/profile
12
不是root用户,执行:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
123
查看节点状态:
[root@k8s-master calico]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane 7m14s v1.27.0
[root@k8s-master calico]# kubectl cluster-info
Kubernetes control plane is running at https://10.0.2.2:6443
CoreDNS is running at https://10.0.2.2:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
1234567891011
这里由于还未安装pod网络插件,所以是NotReady,后面步骤解决。
5.3 其他节点加入集群
# 在node1上执行
# 注意使用初始化集群时输出的命令,确认token和cert-hash正确
$ kubeadm join 10.0.2.2:6443 --token ihde1u.chb9igowre1btgpt --discovery-token-ca-cert-hash sha256:fcbe96b444325ab7c854feeae7014097b6840329a608415b08c3af8e8e513573
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
12345678910111213141516
然后在master上查看节点状态:
[root@k8s-master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane 3m48s v1.27.0
k8s-node1 NotReady <none> 6s v1.27.0
1234
下节解决节点状态是NotReady
的问题。
5.4 安装第三方网络插件
Kubernetes 需要网络插件(Container Network Interface: CNI)来提供集群内部和集群外部的网络通信。以下是一些常用的 k8s 网络插件:
Flannel:Flannel 是最常用的 k8s 网络插件之一,它使用了虚拟网络技术来实现容器之间的通信,支持多种网络后端,如 VXLAN、UDP 和
Host-GW。Calico:Calico 是一种基于 BGP 的网络插件,它使用路由表来路由容器之间的流量,支持多种网络拓扑结构,并提供了安全性和网络策略功能。Canal:Canal 是一个组合了 Flannel 和 Calico 的网络插件,它使用 Flannel 来提供容器之间的通信,同时使用 Calico
来提供网络策略和安全性功能。Weave Net:Weave Net 是一种轻量级的网络插件,它使用虚拟网络技术来为容器提供 IP 地址,并支持多种网络后端,如 VXLAN、UDP 和
TCP/IP,同时还提供了网络策略和安全性功能。Cilium:Cilium 是一种基于 eBPF (Extended Berkeley Packet Filter) 技术的网络插件,它使用 Linux
内核的动态插件来提供网络功能,如路由、负载均衡、安全性和网络策略等。Contiv:Contiv 是一种基于 SDN 技术的网络插件,它提供了多种网络功能,如虚拟网络、网络隔离、负载均衡和安全策略等。Antrea:Antrea 是一种基于 OVS (Open vSwitch) 技术的网络插件,它提供了容器之间的通信、网络策略和安全性等功能,还支持多种网络拓扑结构。
更多插件列表查看 官方文档 。
这里选择calico,安装步骤如下:
mkdir -p ~/k8s/calico && cd ~/k8s/calico
# 注意calico版本需要匹配k8s版本,否则无法应用
wget --no-check-certificate https://raw.gitmirror.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
#!!!
# 修改calico.yaml,在 CALICO_IPV4POOL_CIDR 的位置,修改value为pod网段:20.2.0.0/16 (与前面的--pod-network-cidr参数一致)
# 应用配置文件
# - 这将自动在Kubernetes集群中创建所有必需的资源,包括DaemonSet、Deployment和Service等
kubectl apply -f calico.yaml
# 观察calico 的几个 pod是否 running,这可能需要几分钟
$ kubectl get pods -n kube-system -w |grep calico
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-74cfc9ffcc-85ng7 0/1 Pending 0 17s
calico-node-bsqtv 0/1 Init:ErrImagePull 0 17s
calico-node-xjwt8 0/1 Init:ErrImagePull 0 17s
...
# 观察到calico镜像拉取失败,查看pod日志
kubectl describe pod -n kube-system calico-node-bsqtv
# 从输出中可观察到是拉取 docker.io/calico/cni:v3.26.1 镜像失败,改为手动拉取(在所有节点都执行)
ctr -n k8s.io image pull docker.io/calico/cni:v3.26.1 &&
ctr -n k8s.io image pull docker.io/calico/node:v3.26.1 &&
ctr -n k8s.io image pull docker.io/calico/kube-controllers:v3.26.1
# 检查
$ ctr image ls
# 删除calico pod,让其重启
kk delete pod -l k8s-app=calico-node -n kube-system
kk delete pod -l k8s-app=calico-kube-controllers -n kube-system
# 观察pod状态
kk get pods -A --watch
# ok后,重启一下网络(笔者出现集群正常后,无法连通外网,重启后可以)
service network restart
123456789101112131415161718192021222324252627282930313233343536373839
当需要重置网络时,在master节点删除calico全部资源:kubectl delete -f calico.yaml
,然后在所有节点执行:
rm -rf /etc/cni/net.d && service kubelet restart
1
安装calicoctl
(可选),方便观察calico的各种信息和状态:
# 第1种安装方式(推荐)
curl -o /usr/local/bin/calicoctl -O -L "https://hub.gitmirror.com/https://github.com/projectcalico/calico/releases/download/v3.26.1/calicoctl-linux-amd64"
chmod +x /usr/local/bin/calicoctl
# calicoctl 常用命令
calicoctl node status
calicoctl get nodes
# 第2种安装方式
curl -o /usr/local/bin/kubectl-calico -O -L "https://hub.gitmirror.com/https://github.com/projectcalico/calico/releases/download/v3.26.1/calicoctl-linux-amd64"
chmod +x /usr/local/bin/kubectl-calico
kubectl calico -h
# 检查Calico的状态
[root@k8s-master calico]# kubectl calico node status
Calico process is running.
IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+--------------+-------------------+-------+----------+-------------+
| 10.0.2.3 | node-to-node mesh | up | 13:26:06 | Established |
+--------------+-------------------+-------+----------+-------------+
IPv6 BGP status
No IPv6 peers found.
# 列出Kubernetes集群中所有节点的状态,包括它们的名称、IP地址和状态等
[root@k8s-master calico]# kubectl calico get nodes
NAME
k8s-master
k8s-node1
12345678910111213141516171819202122232425262728293031
现在再次查看集群状态,一切OK。
[root@k8s-master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 64m v1.27.0
k8s-node1 Ready <none> 61m v1.27.0
1234
5.5 在普通节点执行kubectl
默认情况下,我们只能在master上运行kubectl命令,如果在普通节点执行会得到以下错误提示:
[root@k8s-node1 ~]# kubectl get nodes
The connection to the server localhost:8080 was refused - did you specify the right host or port?
12
kubectl命令默认连接本地的8080端口,需要修改配置文件,指向master的6443端口。当然,除了连接地址外还需要证书完成认证。
这里可以直接将master节点的配置文件拷贝到普通节点:
# 拷贝配置文件到node1,输入节点密码
[root@k8s-master ~]# scp /etc/kubernetes/admin.conf root@k8s-node1:/etc/kubernetes/
# 在节点配置环境变量后即可使用
[root@k8s-node1 ~]# echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> /etc/profile
[root@k8s-node1 ~]# source /etc/profile
[root@k8s-node1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 17h v1.27.0
k8s-node1 Ready <none> 16h v1.27.0
12345678910
但是,在实际环境中,我们通常不需要做这个操作。因为普通节点相对master节点只是一种临时资源,可能会以后某个时间点退出集群。
而且/etc/kubernetes/admin.conf
是一个包含证书密钥的敏感文件,不应该存在于普通节点上。
5.6 删除集群
后面如果想要彻底删除集群,在所有节点执行:
kubeadm reset -f # 重置集群 -f 强制执行
rm -rf /var/lib/kubelet # 删除核心组件目录
rm -rf /etc/kubernetes # 删除集群配置
rm -rf /etc/cni/net.d/ # 删除容器网络配置
rm -rf /var/log/pods && rm -rf /var/log/containers # 删除pod和容器日志
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X # 删除 iptables 规则
service kubelet restart
# 镜像一般保留,查看当前节点已下载的镜像命令如下
crictl images
# 快速删除节点上的全部镜像
# rm -rf /var/lib/containerd/*
# 然后可能需要重启节点才能再次加入集群
reboot
1234567891011121314
6. 验证集群
这一节通过在集群中快速部署nginx服务来验证集群是否正常工作。
在master上执行下面的命令:
# 创建pod
kubectl create deployment nginx --image=nginx
# 添加nginx service,设置映射端口
# 如果是临时测试:kubectl port-forward deployment nginx 3000:3000
kubectl expose deployment nginx --port=80 --type=NodePort
# 查看pod,svc状态
$ kubectl get pod,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-76d6c9b8c-g5lrr 1/1 Running 0 7m12s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 20.1.0.1 <none> 443/TCP 94m
service/nginx NodePort 20.1.255.52 <none> 80:30447/TCP 7m
123456789101112131415
上面通过NodePort类型的Service来暴露了Pod,它将容器80端口映射到所有节点的一个随机端口(这里是30447)。
然后我们可以通过访问节点端口来测试在所有集群机器上的pod连通性:
# 在master上执行
$ curl http://10.0.2.2:30447
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
$ curl http://10.0.2.3:30447
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
123456789101112131415
删除部署
kubectl delete deployment nginx
kubectl delete svc nginx
12
至此,使用kubeadm搭建集群结束。但是还有一些进阶话题需要讨论,比如k8s镜像清理、日志存储等,参考下一篇文档。
7. 疑难解决
7.1 解决calico镜像下载较慢的问题
镜像下载慢会导致节点一直停留在NotReady
状态,可以通过手动拉取的方式解决:
# awk去重
$ cat calico.yaml|grep image: |awk '!seen[$0]++'
image: docker.io/calico/cni:v3.26.1
image: docker.io/calico/node:v3.26.1
image: docker.io/calico/kube-controllers:v3.26.1
# 一次性手动拉取上面的三个镜像(需要在所有节点执行)
$ grep -oP 'image:s*K[^[:space:]]+' calico.yaml |awk '!seen[$0]++' | xargs -n 1 ctr image pull
# 等价于
ctr image pull docker.io/calico/cni:v3.26.1
ctr image pull docker.io/calico/node:v3.26.1
ctr image pull docker.io/calico/kube-controllers:v3.26.1
12345678910111213
因为之前安装containerd时在其配置文件中添加了国内源,所以这里直接使用ctr手动拉取位于docker.io
的镜像的速度会很快。
在后续测试过程中,你也可以使用这个方式来解决国外镜像下载慢的问题。对于生产环境,通常是使用本地镜像仓库,一般不会有这个问题。
k8s默认使用
crictl pull <image-name>
命令拉取镜像,但crictl读取不到containerd设置的国内源,所以才会慢。
7.2 解决calico密钥过期问题
每个新创建的Pod都需要calico分配IP,如果calico无法分配IP,就会导致Pod启动异常:
$ kk describe po hellok8s-go-http-999f66c56-4k72x
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreatePodSandBox 3d6h kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "603ebf87036af6c05e6bf26c82403b404cc9763b5d20ab89cd08286969899348": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
123456
这里的connection is unauthorized: Unauthorized
其实是calico的日志,根本原因是calico用来查询集群信息的ServiceAccount
Token过期了。
calico使用的token存储在/etc/cni/net.d/calico-kubeconfig
,通过cat可以查看。这个token的有效期只有24h,
但不知为何calico没有自动续期导致Pod无法正常创建和删除(对应分配和释放IP操作)。
一个快速解决的办法是删除calico-node
Pod,这样它在重建calico-node
Pod后会生成新的token:
$ kk delete po -l k8s-app=calico-node -A
pod "calico-node-v94sd" deleted
pod "calico-node-xzxbd" deleted
123
再次观察Pod状态就会正常了。
7.3 解决k8s证书过期问题
默认情况下kubernetes集群各个组件的证书有效期是一年,这可以通过以下命令查看:
$ kubeadm certs check-expiration
[check-expiration] Reading configuration from the cluster...
[check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Nov 15, 2024 08:33 UTC 317d ca no
apiserver Nov 15, 2024 08:33 UTC 317d ca no
apiserver-etcd-client Nov 15, 2024 08:33 UTC 317d etcd-ca no
apiserver-kubelet-client Nov 15, 2024 08:33 UTC 317d ca no
controller-manager.conf Nov 15, 2024 08:33 UTC 317d ca no
etcd-healthcheck-client Nov 15, 2024 08:33 UTC 317d etcd-ca no
etcd-peer Nov 15, 2024 08:33 UTC 317d etcd-ca no
etcd-server Nov 15, 2024 08:33 UTC 317d etcd-ca no
front-proxy-client Nov 15, 2024 08:33 UTC 317d front-proxy-ca no
scheduler.conf Nov 15, 2024 08:33 UTC 317d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Nov 13, 2033 08:33 UTC 9y no
etcd-ca Nov 13, 2033 08:33 UTC 9y no
front-proxy-ca Nov 13, 2033 08:33 UTC 9y no
1234567891011121314151617181920
当证书过期后,执行kubectl命令会得到证书过期的提示,导致无法管理集群。通过以下命令进行证书更新:
# 首先备份旧证书
$ cp -r /etc/kubernetes/ /tmp/backup/
$ ls /tmp/backup
admin.conf controller-manager.conf kubelet.conf manifests pki scheduler.conf
# 对单个组件证书续期(一年)
$ kubeadm certs renew apiserver
[renew] Reading configuration from the cluster...
[renew] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
certificate for serving the Kubernetes API renewed
$ kubeadm certs check-expiration |grep apiserver
apiserver Jan 01, 2025 16:17 UTC 364d ca no
apiserver-etcd-client Nov 15, 2024 08:33 UTC 317d etcd-ca no
apiserver-kubelet-client Nov 15, 2024 08:33 UTC 317d ca no
# 或者直接对全部组件证书续期
$ kubeadm certs renew all
[renew] Reading configuration from the cluster...
[renew] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed
certificate for serving the Kubernetes API renewed
certificate the apiserver uses to access etcd renewed
certificate for the API server to connect to kubelet renewed
certificate embedded in the kubeconfig file for the controller manager to use renewed
certificate for liveness probes to healthcheck etcd renewed
certificate for etcd nodes to communicate with each other renewed
certificate for serving etcd renewed
certificate for the front proxy client renewed
certificate embedded in the kubeconfig file for the scheduler manager to use renewed
Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.
# 重启kubelet
systemctl restart kubelet
# 如果不是root用户
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
123456789101112131415161718192021222324252627282930313233343536373839
7.4 安装其他网络插件-flannel
flannel也是一个可以用于 Kubernetes 的 overlay 网络提供者。可以用来替换calico,
但它的缺陷是不支持Kubernetes的NetworkPolicy,请慎重考虑。
在一个运行稳定的Kubernetes集群中更换网络插件是一个非常风险的操作,最好是安装集群时确认好安装哪一种网络插件。
下面是安装步骤:
wget --no-check-certificate https://hub.gitmirror.com/https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 修改Pod网段:搜索文件内:net-conf.json
# net-conf.json: |
# {
# "Network": "10.244.0.0/16", => 20.2.0.0/16
# "Backend": {
# "Type": "vxlan"
# }
# }
kubectl apply -f kube-flannel.yml
1234567891011
可以手动拉取镜像以提高效率:
$ cat kube-flannel.yml|grep image:
image: docker.io/flannel/flannel:v0.23.0
image: docker.io/flannel/flannel-cni-plugin:v1.2.0
image: docker.io/flannel/flannel:v0.23.0
images=$(grep 'image:' kube-flannel.yml | awk '{print $2}')
for image in $images; do
ctr image pull $image
done
123456789
安装成功后,能看到flannel部署的两个Pod以及集群内置的coredns Pod都处于Running状态:
$ kk get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-flannel kube-flannel-ds-672rx 1/1 Running 0 43s
kube-flannel kube-flannel-ds-v4hzd 1/1 Running 0 43s
kube-system coredns-c676cc86f-8vpk4 1/1 Running 0 4m36s
kube-system coredns-c676cc86f-fzzjp 1/1 Running 0 4m36s
...
1234567
在测试环境中,你可以通过修改本机时间来检查flannel是否像calico那样存在token过期的问题:
# 改为1天后的时间,甚至是10天后,半年后都可
# - 但注意k8s组件证书默认有效期一年,如果改为一年后,kubectl命令会无法正常执行
$ date -s '2023-11-18 19:00:00'
$ kk apply -f ../practice/deployment.yaml
deployment.apps/hellok8s-go-http created
# pod能够running,就说明不存在token过期问题
$ kk get po
NAMESPACE NAME READY STATUS RESTARTS AGE
default hellok8s-go-http-999f66c56-7fdst 1/1 Running 0 1s
default hellok8s-go-http-999f66c56-j5dhx 1/1 Running 0 1s
123456789101112
参考
使用 kubeadm 创建集群掘金-冰_点-Kubernetes 之7大CNI 网络插件用法和对比