K8s 专题——kubeadm 快速搭建集群
K8s 专题——kubeadm 快速搭建集群
关注小厨师,烹饪美味的技术餐
前言
工欲善其事,必先利其器。要想充分本地体验 K8s 的所有功能,首先我们必须在本地拥有一个 k8s 集群。要搭建一个集群我们需要安装非常多组件,不过幸好官方给我们提供了一个强大的傻瓜式集群安装工具 kubeadm
。本文将带大家快速安装如下网络结构的 K8s 集群,并带读者快速入门 Pod
和 Service
。
准备工作
我们需要在本地准备三个虚拟机,配置推荐如下,
-
Master 节点: -
Node 节点:
如果你不太清楚应该如何准备虚拟机集群,可以参考下面这篇文章,
1. 集群搭建
1.1 关闭防火墙
通常 K8s 部署在内网环境,外置防火墙,不需要主机防火墙,这里为了方便起见直接关闭了。
systemctl stop firewalld
systemctl disable firewalld
1.2 关闭 selinux
永久关闭方式:
vi /etc/selinux/config
并修改下面值为 disabled,
SELINUX=disabled
你也可以选择临时关闭:
setenforce 0
1.3 关闭 swap 分区
临时关闭:
swapoff -a
永久关闭:
sed -i '/ swap / s/^/#/' /etc/fstab
关闭 swap 分区是避免 cgroups
设置的内存上限失效,同时提高性能。
扩展导读——Swap 简介
-
什么是 swap
?
swap
是磁盘上的一块区域,用于在系统物理内存吃紧时,内存中不常访问数据的临时保存。
-
为什么关闭 swap
?
-
k8s 集群通过 cgroup 来限制各个容器的物理资源,如果开启了 swap
,那么容器占用内存达到上限时容器会通过swap out
来换出部分内存,这不符合 k8s (Pod
)资源设置上限的初衷 -
对于服务器应用来说,我们希望程序在内存达到阈值时通过 Linux 内核 OOM killer 杀死进程,然后通过监控程序重启应用(比如 k8s 副本控制器)。如果配置了 swap
则程序在内存吃紧时通过磁盘 I/O 换出部分内存,导致服务程序不仅得不到重启,还会因为频繁访问磁盘导致程序性能下降,最终导致业务长期中断。
-
swap
的意义
swap
对桌面系统比较有帮助,比如运行游戏或者办公软件等占用实际内存较大的程序,swap
能够实现物理内存不足时仍保持程序的运作。
1.4 规划主机名
修改本机主机名,
master 节点服务器上,
hostnamectl set-hostname k8s-master
node 节点服务器上执行,
# node1 服务器上执行
hostnamectl set-hostname k8s-node1
# node2 服务器上执行
hostnamectl set-hostname k8s-node2
在所有节点服务器上配置主机名映射,
cat >> /etc/hosts << EOF
192.168.100.101 k8s-master
192.168.100.102 k8s-node1
192.168.100.103 k8s-node2
EOF
1.5 将桥接的 IPv4 流量传递到 iptables 的链
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# 生效
sysctl --system
1.6 时间同步
时间同步的意义在于保证线上业务正确运行,比如订单业务、秒杀业务等对时区和时间有严格要求的场景。
yum install ntpdate -y
ntpdate time.windows.com
1.7 给所有节点安装 Docker/Kubeadm/Kubelet
1.7.1 安装 Docker
yum install -y yum-utils
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io
systemctl enable docker
systemctl start docker
配置镜像加速,这里我们采用阿里云镜像加速服务,
读者可以通过注册并登录阿里云账号后,访问 https://cr.console.aliyun.com/cn-shenzhen/instances/mirrors,获得 Docker 加速链接。
cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["你的加速链接"]
}
EOF
重启 Docker,
systemctl restart docker
1.7.2 添加阿里云YUM软件源
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
1.7.3 安装 kubeadm,kubelet 和 kubectl
yum install -y kubelet kubeadm kubectl
systemctl enable kubelet && systemctl start kubelet
1.8 部署 Kubernetes Master
本节的工作只需要在规划的 Master 节点上进行,Node 节点无需执行。
因为国外的 k8s 镜像站我们无法访问,所以这里还是配置阿里云镜像,
kubeadm init \
--apiserver-advertise-address=192.168.100.101 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.23.5 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16
当出现输出出现,
Your Kubernetes control-plane has initialized successfully!
说明我们的 master 已经成功启动了
这里作者踩了一个坑,如果你和作者一样出现了以下的问题,可以看看文末的坑 1
使用 kubectl 工具
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
查看当前节点状态,
kubectl get nodes
如果读者在执行 kubectl 命令时出现认证错误,可以看看文末的坑 2
1.9 加入节点
我们到两个 node 节点中执行下面的命令,该命令可以从成功初始化的输出中找到,
kubeadm join 192.168.100.101:6443 --token rlsevs.hlr6r0mgr6hu2351 --discovery-token-ca-cert-hash sha256:9a1dbf40b038e7bcbdd503f5bf189dd6b2e6a674c8dfedee5800141628986762
如果读者没有及时记录加入节点的命令也没关系,可以执行下面这个命令来获取,
kubeadm token create --print-join-command
当输出如下所示时,即为成功加入网络,
我们回到 master 节点中查看当前的节点情况,
kubectl get nodes
可以看到当前集群的所有节点均处于 NotReady 状态,这是因为集群中还缺少 CNI 网络插件。
扩展导读
CNI 全称为 Container Network Interface,即容器网络的 API 接口。这个接口的具体实现包括 Calico、Flannel、Terway、Weave Net 和 Contiv。在集群创建一个 Pod
要经过以下步骤,
-
通过 APIServer
写入Pod
配置 -
APIServer
通过Scheduler
(或其他方式)调度到某个具体的节点上 -
节点上的 Kubelet
监听到Pod
的绑定时,在本地创建容器,当执行到创建网络时,会通过 CNI 配置Pod
网络 -
配置完成时, Pod
也就创建成功了
关于 CNI 的其他细节,我们以后再聊。
1.10 部署 CNI 网络插件
安装之前我们先查看目前集群中的所有 pod,
kubectl get pods --all-namespaces
输出如下所示,
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6d8c4cb4d-dt9ds 0/1 Pending 0 4m12s
kube-system coredns-6d8c4cb4d-qwqzq 0/1 Pending 0 4m12s
kube-system etcd-master 1/1 Running 1 4m27s
kube-system kube-``APIServer``-master 1/1 Running 2 4m29s
kube-system kube-controller-manager-master 1/1 Running 2 4m27s
kube-system kube-proxy-dwkjs 1/1 Running 0 92s
kube-system kube-proxy-j8bhh 1/1 Running 0 86s
kube-system kube-proxy-kfjdm 1/1 Running 0 4m12s
kube-system kube-scheduler-master 1/1 Running 2 4m27s
可以看到,部分 pod 处于挂起的状态,这是因为我们还缺少网络插件。
我们首先获取 Flannel 插件配置文件,
$ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
然后通过以下命令进行安装,
kubectl apply -f kube-flannel.yml
在安装期间可以通过这个命令查看当前的进度,
watch kubectl get pods --all-namespaces
当看到所有组件均已经处于 Running 的时候,k8s 就配置完成啦!!!
2. 测试 kubernates 集群
我们创建一个 Nginx
的工作负载 pod
,
kubectl create deployment nginx --image=nginx
执行完上面的命令后,Nginx
并不会立刻运行在网络中,你可以通过下面的命令来查看负载的情况,
kubectl get pod
一开始你会发现 Pod
处于容器创建状态,等待一小会儿,重新执行后,你就能看到 Pod
处于运行状态,
现在我们对外暴露该 Pod
,这将会创建一个 Service
,
kubectl expose deployment nginx --port=80 --type=NodePort
现在我们查看当前 Pod
和 Service
的运行情况,
kubectl get pod,svc -o wide
我们可以看到 Pod
被调度到了 node2
上,同时将容器内的 80 端口映射到节点的 31535
端口,现在我们应该能通过任意节点 IP 加上该端口号访问到 Nginx
。
读者会发现尽管 Pod
只被部署到了一个节点上,但是却能通过集群中的任何节点 IP + 端口号访问到,这就是各节点上的 kube-proxy
进程根据 Service
的定义实现的反向代理,而具体的原理我们以后慢慢说。
至此有关 Kubeadm 搭建集群的教程就介绍到这啦~
那些让人难受的坑
1. kubeadm init 因为 cgroup 驱动不一致导致的初始化错误
如果读者在执行 kubeadm init 后出现以下的问题,
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused.
Unfortunately, an error has occurred:
timed out waiting for the condition
This error is likely caused by:
- The kubelet is not running
- The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)
If you are on a systemd-powered system, you can try to troubleshoot the error with the following commands:
- 'systemctl status kubelet'
- 'journalctl -xeu kubelet'
Additionally, a control plane component may have crashed or exited when started by the container runtime.
To troubleshoot, list all containers using your preferred container runtimes CLI.
Here is one example how you may list all Kubernetes containers running in docker:
- 'docker ps -a | grep kube | grep -v pause'
Once you have found the failing container, you can inspect its logs with:
- 'docker logs CONTAINERID'
那很有可能是 Docker 和 kubelet 服务中的 cgroup
驱动不一致的原因,主要有两种解决方案,
修改 Docker 驱动类型来适配 kubelet:
修改 Docker 配置文件,/etc/docker/daemon.json
{
# 添加下面的内容
"exec-opts": [
"native.cgroupdriver=systemd"
],
...
}
然后重启 Docker 即可,
systemctl restart docker
修改 kubelet 配置来适配 Docker,由于我们是通过 kubeadm 安装的,这里就不做介绍了。
扩展导读——Cgroup 简介
Linux cgroups 的全称是 Linux Control Groups,属于 Linux 内核的功能,主要的功能包括以下几点,
-
资源限制:限制不同进程的资源上限 -
优先级控制:不同的组可以有不同的优先级 -
控制:挂起一组进程或者重启一组进程
Cgroup 是许多技术的基础,比如 Docker 通过它来实现容器的资源管理。
2. 执行 kubectl 命令出现证书错误
如果执行 kubectl
时出现证书错误,多半是读者曾经重新安装过 k8s,严格来说 k8s 不应该出现这个问题,所以这是一个 bug,
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of “crypto/rsa: verification error” while trying to verify candidate authority certificate “kubernetes”)
说明你之前安装过 kubectl,需要先执行下面所示的命令来删除之前的目录,
rm -rf $HOME/.kube
然后重新安装 kubectl 工具
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
至此 K8s 集群的安装就介绍完毕啦,如果你觉得这篇文章对你有帮助,可以点赞和关注噢~~