kubernetes学习二:kubeadm安装kubernetes集群(单master)

1 参考文档

官方文档

kubeadm安装k8s v1.18.0

cgroup造成的kubelet启动失败

2 环境准备

2.1 物理环境

主机 IP 配置 OS
master/node1 192.168.67.139 2core 2GB Centos7.9
node2 192.168.67.140 2core 2GB Centos7.9
node3 192.168.67.141 2core 2GB Centos7.9
node4 192.168.67.142 2core 2GB Centos7.9

2.2 软件环境

软件 版本
docker v20.10.12
kubeadmin v1.23.3
kubelet v1.23.3
kubectl v1.23.3

2.3 镜像

镜像 版本
conformance v1.23.3
kube-apiserver v1.23.3
kube-controller-manager v1.23.3
kube-proxy v1.23.3
kube-scheduler v1.23.3
flannel v1.0.1

3 步骤

3.1 准备工作

3.1.1 确保各节点MAC和product_uuid的唯一性

  • 你可以使用命令 ip linkifconfig -a 来获取网络接口的 MAC 地址
  • 可以使用 sudo cat /sys/class/dmi/id/product_uuid 命令对 product_uuid 校验

一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。

3.1.2 运行iptables检查桥接流量

确保 br_netfilter 模块被加载。这一操作可以通过运行 lsmod | grep br_netfilter 来完成。若要显式加载该模块,可执行 sudo modprobe br_netfilter

为了让你的 Linux 节点上的 iptables 能够正确地查看桥接流量,你需要确保在你的 sysctl 配置中将 net.bridge.bridge-nf-call-iptables 设置为 1。例如:

1
2
3
4
5
6
7
8
9
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system

3.1.3 关闭防火墙

1
2
systemctl stop firewalld 
systemctl disable firewalld

如果各个主机启用了防火墙,需要开放Kubernetes各个组件所需要的端口。如下图所示,详细信息请看官网

在这里插入图片描述

3.1.4 永久关闭swap

vim /etc/fstab

image-20220207202709095

image-20220207202741311

如果不修改,kubelet启动报错:

image-20220207202527741

3.1.5 关闭selinux

1
2
3
4
5
# 将 SELinux 设置为 permissive 或 disabled模式(相当于将其禁用)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

sudo sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

3.2 安装docker

卸载旧版本

1
2
3
4
5
6
7
8
9
10
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

源安装

执行以下命令安装依赖包:

1
$ sudo yum install -y yum-utils

鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。

执行下面的命令添加 yum 软件源:

1
2
3
4
5
6
7
8
9
10
$ sudo yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

$ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

# 官方源
# $ sudo yum-config-manager \
# --add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo

安装docker-ce

1
$ sudo yum install docker-ce docker-ce-cli containerd.io

开机自启

1
2
3
启动 Docker
$ sudo systemctl enable docker
$ sudo systemctl start docker

3.3 安装 kubeadm、kubelet 和 kubectl

你需要在每台机器上安装以下的软件包:

  • kubeadm:用来初始化集群的指令。
  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
  • kubectl:用来与集群通信的命令行工具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

# 安装服务
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

# 设置kubelet开机自启
sudo systemctl enable kubelet

3.4 配置cgroup驱动程序

需保证容器服务和kubelet的cgroup驱动一致,否则kubelet启动报错:

image-20220208095347892

image-20220208095316580

由于 kubeadm 把 kubelet 视为一个系统服务来管理,所以对基于 kubeadm 的安装, 我们推荐使用 systemd 驱动,不推荐 cgroupfs 驱动。

3.4.1 Cgroup 驱动程序说明

image-20220208110411304

警告:

你需要确保容器(docker)和 kubelet 所使用的是相同的 cgroup 驱动,否则 kubelet 进程会失败。

相关细节可参见kubelet配置 cgroup 驱动容器配置cgroup驱动

3.4.2 查看kubelet和docker的驱动程序

  • kubelet
1
2
3
4
[root@node1 ~]#  cat /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS="--cgroup-driver=cgroup --hostname-override=node --network-plugin=cni --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/k8s-images-kx/pause:3.6"

# --cgroup-driver=cgroup 该节点驱动程序为cgroup
  • docker
1
2
3
4
5
[root@node1 ~]# docker info |grep Cgroup
Cgroup Driver: cgroupfs
Cgroup Version: 1

# docker驱动程序为 cgroupfs

3.4.4 Docker配置cgroup驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF

[root@node3 ~]# systemctl daemon-reload
[root@node3 ~]# systemctl restart docker
[root@node3 ~]# docker info | grep cgroup
Cgroup Driver: systemd
Cgroup Version: 1

3.4.5 kubelet配置cgroup驱动(可选)

kubelet不配置默认即使用systemd

1
2
3
4
5
6
7
# 将kubelet和docker 的驱动程序改成一致。
vim /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS="--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.1"
# 将cgroup改成systemd

[root@node3 ~]# systemctl daemon-reload
[root@node3 ~]# systemctl restart kubelet

3.5 kubeadm创建集群

3.5.1 kubeadm config

kubeadm已经进入GA阶段,其控制面初始化和加入节点步骤都支持大量的可定制内容,因此kubeadm还提供了配置文件功能用于复杂定制。同时,kubeadm将配置文件以ConfigMap的形式保存到集群之中,便于后续的查询和升级工作。kubeadm config子命令提供了对这一组功能的支持:

◎ kubeadm config upload from-file:由配置文件上传到集群中生成ConfigMap。

◎ kubeadm config upload from-flags:由配置参数生成ConfigMap。

◎ kubeadm config view:查看当前集群中的配置值。

◎ kubeadm config print init-defaults:输出kubeadm init默认参数文件的内容。

◎ kubeadm config print join-defaults:输出kubeadm join默认参数文件的内容。

◎ kubeadm config migrate:在新旧版本之间进行配置转换。

◎ kubeadm config images list:列出所需的镜像列表。

◎ kubeadm config images pull:拉取镜像到本地。例如,执行kubeadm config print init-defaults,可以取得默认的初始化参数文件:

1
kubeadm config print init-defaults > init.default.yaml

3.5.2 导出kubeadm集群默认配置文件

1
kubeadm config print init-defaults > init.default.yaml

3.5.3 修改默认配置文件

修改如下内容:

  1. 主节点IP——advertiseAddress
  2. 国内阿里镜像地址imageRepository——registry.cn-hangzhou.aliyuncs.com/k8s-images-kx(通过阿里镜像仓库拉取的国外镜像)
  3. pod网段配置——不同网络插件网段不一样详细见官网(配置网络插件subnet地址段)

image-20220208135503045

将上面的内容保存为init-config.yaml备用。

纠正:

podSubent的值设置为flannel的默认网段,FLANNEL_NETWORK的默认值(可通过flannel.yaml文件查看);podSubent=10.244.0.0/16

原因:

后面配置NFS存储类的时候,pod无法正常访问kube-apiserver。

参考:

k8s 1.20.x版本NFS动态存储配置 - 巽逸 - 博客园 (cnblogs.com)

(71条消息) Kubeadm 部署 使用flannel无法连接service/kubernetes_weixin_40455124的博客-CSDN博客

image-20220215134508429

3.5.4 下载kubernetes相关镜像

使用config images list 子命令查询所需的镜像,例如

1
2
3
4
5
6
7
8
9
10
[root@node1 home]# kubeadm config images list --config=init.default.yaml
k8s.gcr.io/kube-apiserver:v1.23.0
k8s.gcr.io/kube-controller-manager:v1.23.0
k8s.gcr.io/kube-scheduler:v1.23.0
k8s.gcr.io/kube-proxy:v1.23.0
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6

# 默认初始化配置文件下载的是1.23.0版本,如上图显示,可以修改初始化配置文件,改为1.23.3版本

使用config images pull 子命令下载所需的镜像,例如(国内无法下载,只做演示)

image-20220207174123729

预拉取镜像

通过阿里云镜像仓库构建下载镜像,具体方法不做细说。

镜像使用方式有2种:

  1. 修改镜像名为国外镜像名,例如

k8s.gcr.io/kube-apiserver:v1.23.0
k8s.gcr.io/kube-controller-manager:v1.23.0
k8s.gcr.io/kube-scheduler:v1.23.0
k8s.gcr.io/kube-proxy:v1.23.0
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6

  1. 修改初始化配置文件,修改国外仓库为阿里云仓库

imageRepository: registry.cn-hangzhou.aliyuncs.com/k8s-images-kx
kind: ClusterConfiguration
kubernetesVersion: 1.23.3
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12

1
[root@node1 home]# kubeadm config images pull --config=init.default.yaml

3.5.5 初始化集群

1
[root@node1 home]# kubeadm init --config=init.default.yaml

初始化失败,可以如下命令重置

1
[root@node1 home]# kubeadm reset

显示如下安装成功

image-20220208140143943

普通用户使用集群需如下配置

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

3.5.6 查询集群状态

1
kubectl get node

image-20220208140453955

notready原因是没有安装pod网络。

3.5.7 安装pod网络(二选一)

各pod组件的链接均可以通过如下地址找到,及相关使用教程。

安装扩展(Addons) | Kubernetes

集群网络系统 | Kubernetes

网络说明

Kubernetes的网络通信问题:
  1. 容器间通信: 即同一个Pod内多个容器间通信,通常使用loopback来实现。
  2. Pod间通信: K8s要求,Pod和Pod之间通信必须使用Pod-IP 直接访问另一个Pod-IP
  3. Pod与Service通信: 即PodIP去访问ClusterIP,当然,clusterIP实际上是IPVS 或 iptables规则的虚拟IP,是没有TCP/IP协议栈支持的。但不影响Pod访问它.
  4. Service与集群外部Client的通信,即K8s中Pod提供的服务必须能被互联网上的用户所访问到。

需要注意的是,k8s集群初始化时的service网段,pod网段,网络插件的网段,以及真实服务器的网段,都不能相同,如果相同就会出各种各样奇怪的问题,而且这些问题在集群做好之后是不方便改的,改会导致更多的问题,所以,就在搭建前将其规划好。

CNI(容器网络接口):
  这是K8s中提供的一种通用网络标准规范,因为k8s本身不提供网络解决方案。
  目前比较知名的网络解决方案有:
    flannel
    calico
    canel
    kube-router
    …….
等等,目前比较常用的时flannel和calico,flannel的功能比较简单,不具备复杂网络的配置能力,calico是比较出色的网络管理插件,单具备复杂网络配置能力的同时,往往意味着本身的配置比较复杂,所以相对而言,比较小而简单的集群使用flannel,考虑到日后扩容,未来网络可能需要加入更多设备,配置更多策略,则使用calico更好

所有的网络解决方案,它们的共通性:
  1. 虚拟网桥
  2. 多路复用:MacVLAN
  3. 硬件交换:SR-IOV(单根-I/O虚拟网络):它是一种物理网卡的硬件虚拟化技术,它通过输出VF(虚拟功能)来将网卡虚拟为多个虚拟子接口,每个VF绑定给一个VM后,该VM就可以直接操纵该物理网卡。

kubelet来调CNI插件时,会到 /etc/cni/net.d/目录下去找插件的配置文件,并读取它,来加载该插件,并让该网络插件来为Pod提供网络服务。

flannel网络插件要怎么部署?
 1. flannel部署到那个节点上?
  因为kubelet是用来管理Pod的,而Pod运行需要网络,因此凡是部署kubelet的节点,都需要部署flannel来提供网络,因为kubelet正是通过调用flannel来实现为Pod配置网络的(如:添加网络,配置网络,激活网络等)。

3.5.7.1 flannel

flannel GIT仓库

在所有节点都pull下载flannel镜像,节点加入集群时自动创建flannel容器。

1
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

3.5.7.2 calico

calico官网

在所有节点都pull下载calico镜像

1
kubectl apply -f https://projectcalico.docs.tigera.io/manifests/canal.yaml

3.5.7.3 查看集群状态

coredns正常运行,节点状态ready.

image-20220208145623345

3.5.8 添加Node节点

官方文档

image-20220208150015590

1
2
3
4
# token和hash 下面会介绍如何获取
# control-plane-host和control-plane-port分别是控制面对应的ip和端口

kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

输出如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node3 ~]# kubeadm join --token abcdef.01234ad56789abcdef 192.168.76.139:6443 --discovery-token-ca-cert-hash sha256:500813d6c8feca1d82468821993be623d3d7ccdfa33d202f3c7fe3c6b39da086c7d
[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.

查询token

1
kubeadm token list

输出类似于以下内容:

1
5didvk.d09sbcov8ph2amjw

查询 –discovery-token-ca-cert-hash

1
2
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'

输出类似于以下内容:

1
8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

image-20220208153910342

3.5.8 测试

首先验证kube-apiserver, kube-controller-manager, kube-scheduler, pod network 是否正常:

1
2
3
4
5
6
7
8
9
10
11
12
# 部署一个 Nginx Deployment,包含两个Pod
# https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
kubectl create deployment nginx --image=nginx:alpine
kubectl scale deployment nginx --replicas=2

# 验证Nginx Pod是否正确运行,并且会分配192.168.开头的集群IP
kubectl get pods -l app=nginx -o wide

# 输出如下:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-54458cd494-p8jzs 1/1 Running 0 31s 192.168.1.2 node1 <none> <none>
nginx-54458cd494-v2m4b 1/1 Running 0 24s 192.168.1.3 node1 <none> <none>

再验证一下kube-proxy是否正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 以 NodePort 方式对外提供服务 https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/
kubectl expose deployment nginx --port=80 --type=NodePort

# 查看集群外可访问的Port
kubectl get services nginx

# 输出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.110.49.49 <none> 80:31899/TCP 4s

# 可以通过任意 NodeIP:Port 在集群外部访问这个服务,本示例中部署的集群IP分别是192.168.76.139-142
curl http://192.168.76.139:31899
curl http://192.168.76.140:31899

最后验证一下dns, pod network是否正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 运行Busybox并进入交互模式
kubectl run -it curl --image=radial/busyboxplus:curl

# 输入`nslookup nginx`查看是否可以正确解析出集群内的IP,已验证DNS是否正常
[ root@curl-66959f6557-6sfqh:/ ]$ nslookup nginx

# 输出
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: nginx
Address 1: 10.110.49.49 nginx.default.svc.cluster.local

# 通过服务名进行访问,验证kube-proxy是否正常
[ root@curl-66959f6557-6sfqh:/ ]$ curl http://nginx/

# 输出如下:
# <!DOCTYPE html> ---省略

# 分别访问一下2个Pod的内网IP,验证跨Node的网络通信是否正常
[ root@curl-66959f6557-6sfqh:/ ]$ curl http://192.168.1.2/
[ root@curl-66959f6557-6sfqh:/ ]$ curl http://192.168.1.3/

4 补充

4.1 允许master节点部署pod

这是因为k8s集群默认不让在master节点创建pod,也就是说Master Node不参与工作负载。

当前的master节点被打上了node-role.kubernetes.io/master:NoSchedule的污点:

1
[root@k8s-master k8s]# kubectl describe nodes node1 |grep -E '(Roles|Taints)'

img

允许master部署pod

1
2
3
[root@k8s-master nginx]# kubectl taint nodes node1 node-role.kubernetes.io/master-
# 输出
node/k8s-master untainted

img

禁止master部署pod

1
[root@k8s-master]# kubectl taint nodes node1 node-role.kubernetes.io/master=true:NoSchedule

4.2 卸载集群

想要撤销kubeadm执行的操作,首先要排除节点,并确保该节点为空, 然后再将其关闭。

在Master节点上运行:

1
2
kubectl drain <node name> --delete-local-data --force --ignore-daemonsets
kubectl delete node <node name>

然后在需要移除的节点上,重置kubeadm的安装状态:

1
sudo kubeadm reset
1
2
# 卸载集群后,建议手动删除CNI网络的地址段配置文件,避免重装集群造成影像。
rm -rf /run/flannel

如果你想重新配置集群,使用新的参数重新运行kubeadm init或者kubeadm join即可。

5 问题总结

5.1 创建deployment后,配置NodePort,无法通过任意节点访问

问题描述:

创建nginx的depolyment,并配置nodeport端口配置,只能通过pod所在的节点IP和暴漏端口访问,无法通过其他节点“IP:端口”访问;集群内pod之间不能通过pod IP互相访问;kube-dns解析异常等问题。

查看节点kube-proxy服务日志显示如下错误

image-20220209114644724

问题原因:节点没有开启路由转发功能。

解决办法:添加net.ipv4.ip_forward = 1到/etc/sysctl.conf或/etc/sysctl.d/k8s.conf,执行sysctl -p命令生效。

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!