使用kubeadm创建Kubernetes集群

摘要

安装kubeadm

准备开始

  • 一台或多台运行着下列系统的机器:
    • Ubuntu 16.04+
    • Debian 9+
    • CentOS 7
    • Red Hat Enterprise Linux (RHEL) 7
    • Fedora 25+
    • HypriotOS v1.0.1+
    • Flatcar Container Linux (使用 2512.3.0 版本测试通过)
  • 每台机器 2 GB 或更多的 RAM (如果少于这个数字将会影响你应用的运行内存)
  • 2 CPU 核或更多
  • 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)
  • 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。请参见这里了解更多详细信息。
  • 开启机器上的某些端口。请参见这里 了解更多详细信息。
  • 禁用交换分区。为了保证 kubelet 正常工作,你 必须 禁用交换分区。

本次采用两台Ubuntu20主机,修改hosts

IP 主机名
172.19.184.2 k8s-main1
172.19.184.3 k8s-worker1

卸载自带vim

Ubuntu预安装的是tiny版本,我们需要安装vim的full版本。

卸载预装的版本

1
sudo apt-get -y remove vim-common

安装full版vim

1
sudo apt-get -y install vim

设置主机名

1
sudo hostnamectl set-hostname k8s-main1

修改hosts

关闭防火墙

关闭selinux

确保每个节点上 MAC 地址和 product_uuid 的唯一性

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

一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。 Kubernetes 使用这些值来唯一确定集群中的节点。 如果这些值在每个节点上不唯一,可能会导致安装 失败

检查网络适配器

如果你有一个以上的网络适配器,同时你的 Kubernetes 组件通过默认路由不可达,我们建议你预先添加 IP 路由规则,这样 Kubernetes 集群就可以通过对应的适配器完成连接。

允许 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

我这里遇到点小问题,问题不大修复一下就行。

修改/usr/lib/sysctl.d/50-default.conf

在出错的配置后面添加=1

image-20210119113815067

1
sudo sysctl --system

更多的相关细节可查看网络插件需求页面。

检查所需端口

控制平面节点

协议 方向 端口范围 作用 使用者
TCP 入站 6443 Kubernetes API 服务器 所有组件
TCP 入站 2379-2380 etcd 服务器客户端 API kube-apiserver, etcd
TCP 入站 10250 Kubelet API kubelet 自身、控制平面组件
TCP 入站 10251 kube-scheduler kube-scheduler 自身
TCP 入站 10252 kube-controller-manager kube-controller-manager 自身

工作节点

协议 方向 端口范围 作用 使用者
TCP 入站 10250 Kubelet API kubelet 自身、控制平面组件
TCP 入站 30000-32767 NodePort 服务† 所有组件

NodePort 服务 的默认端口范围。

使用 * 标记的任意端口号都可以被覆盖,所以你需要保证所定制的端口是开放的。

虽然控制平面节点已经包含了 etcd 的端口,你也可以使用自定义的外部 etcd 集群,或是指定自定义端口。

你使用的 Pod 网络插件 (见下) 也可能需要某些特定端口开启。由于各个 Pod 网络插件都有所不同, 请参阅他们各自文档中对端口的要求。

如果各个主机启用了防火墙,需要开放Kubernetes各个组件所需要的端口,可以查看Installing kubeadm中的Check required ports一节。

关闭swap

Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动。

1
sudo swapoff -a

修改/etc/fstab文件,注释掉swap的自动挂载,使用free -m确认swap已经关闭。

安装 runtime

为了在 Pod 中运行容器,Kubernetes 使用 容器运行时(Container Runtime)

默认情况下,Kubernetes 使用 容器运行时接口(Container Runtime Interface,CRI) 来与你所选择的容器运行时交互。

如果你不指定运行时,则 kubeadm 会自动尝试检测到系统上已经安装的运行时, 方法是扫描一组众所周知的 Unix 域套接字。 下面的表格列举了一些容器运行时及其对应的套接字路径:

运行时 域套接字
Docker /var/run/docker.sock
containerd /run/containerd/containerd.sock
CRI-O /var/run/crio/crio.sock

如果同时检测到 Docker 和 containerd,则优先选择 Docker。 这是必然的,因为 Docker 18.09 附带了 containerd 并且两者都是可以检测到的, 即使你仅安装了 Docker。 如果检测到其他两个或多个运行时,kubeadm 输出错误信息并退出。

kubelet 通过内置的 dockershim CRI 实现与 Docker 集成。

参阅容器运行时 以了解更多信息。

安装Docker(略)

修改docker cgroup driver为systemd

根据文档CRI installation中的内容,对于使用systemd作为init system的Linux的发行版,使用systemd作为docker的cgroup driver可以确保服务器节点在资源紧张的情况更加稳定,因此这里修改各个节点上docker的cgroup driver为systemd。

创建或修改/etc/docker/daemon.json

1
2
3
{
"exec-opts": ["native.cgroupdriver=systemd"]
}

重启docker

1
2
3
systemctl restart docker
docker info | grep Cgroup
Cgroup Driver: systemd

安装 kubeadm、kubelet 和 kubectl

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

  • kubeadm:用来初始化集群的指令。
  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
  • kubectl:用来与集群通信的命令行工具。

kubeadm 不能 帮你安装或者管理 kubeletkubectl,所以你需要 确保它们与通过 kubeadm 安装的控制平面的版本相匹配。 如果不这样做,则存在发生版本偏差的风险,可能会导致一些预料之外的错误和问题。 然而,控制平面与 kubelet 间的相差一个次要版本不一致是支持的,但 kubelet 的版本不可以超过 API 服务器的版本。 例如,1.7.0 版本的 kubelet 可以完全兼容 1.8.0 版本的 API 服务器,反之则不可以。

有关安装 kubectl 的信息,请参阅安装和设置 kubectl文档。

警告:

这些指南不包括系统升级时使用的所有 Kubernetes 程序包。这是因为 kubeadm 和 Kubernetes 有特殊的升级注意事项

关于版本偏差的更多信息,请参阅以下文档:

1
2
3
4
5
6
7
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -

cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
sudo apt-get update

由于我这里不安装最新版本

1
2
3
4
5
# 查询所有版本
sudo apt-cache madison kubelet |awk '{print $3}'
# 因为我这里安装kubernetes=V1.19.3,所以这里安装1.19.3版本
sudo apt-get install -y kubelet=1.19.3-00 kubeadm=1.19.3-00 kubectl=1.19.3-00
sudo apt-mark hold kubelet kubeadm kubectl

使用 kubeadm 创建集群

操作指南

在你的主机上安装 kubeadm

拉取镜像

如果下方初始化指定了repository,此步可不操作

查看需要的镜像列表

1
sudo kubeadm config images list --kubernetes-version=1.19.3

从阿里云拉去镜像

1
2
3
4
kubeadm config images list
kubeadm config images list | sed -e 's/^/docker pull /g' -e 's#k8s.gcr.io#registry.cn-hangzhou.aliyuncs.com/google_containers#g' | sh -x
docker images | grep registry.cn-hangzhou.aliyuncs.com/google_containers | awk '{print "docker tag",$1":"$2,$1":"$2}' | sed -e 's/registry.cn-hangzhou.aliyuncs.com\/google_containers/k8s.gcr.io/2' | sh -x
docker images | grep registry.cn-hangzhou.aliyuncs.com/google_containers | awk '{print "docker rmi """$1""":"""$2}' | sh -x

使用脚本拉去指定版本镜像

1
2
3
wget https://raw.githubusercontent.com/bwcxyk/Figurebed/master/shell/k8s/pull_k8s_images_from_aliyun.sh
chmod +x pull_k8s_images_from_aliyun.sh
sh pull_k8s_images_from_aliyun.sh

Ubuntu注意可能执行报错

代码对于标准bash而言没有错,因为Ubuntu/Debian为了加快开机速度,用dash代替了传统的bash,是dash在捣鬼,解决方法就是取消dash。

1
sudo dpkg-reconfigure dash

在选择项中选No

初始化控制平面节点

注:在master节点上进行如下操作

在master进行Kubernetes集群初始化。

1
2
3
sudo kubeadm init --kubernetes-version=v1.19.3 \
--apiserver-advertise-address=172.19.184.2 \
--pod-network-cidr=10.244.0.0/16

–kubernetes-version是安装v1.19.3
–api server地址是master本机IP地址
–image-repository是镜像拉取地址
–pod-network-cidr:指定pod网络的IP地址范围。如果设置,控制平面将为每个节点自动分配CIDRs。
–service-cidr:指定svc网络的IP地址范围,为service VIPs使用不同的IP地址。(默认10.96.0.0/12)
使用了非默认的CIDR(一定要和宿主机的局域网的CIDR不一样!)

kubeadm init 首先运行一系列预检查以确保机器 准备运行 Kubernetes。这些预检查会显示警告并在错误时退出。然后 kubeadm init 下载并安装集群控制平面组件。这可能会需要几分钟。 完成之后你应该看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

You should now deploy a Pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

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

image-20210119134613545

要使非 root 用户可以运行 kubectl,请运行以下命令, 它们也是 kubeadm init 输出的一部分:

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

或者,如果你是 root 用户,则可以运行:

1
export KUBECONFIG=/etc/kubernetes/admin.conf

记录 kubeadm init 输出的 kubeadm join 命令。 你需要此命令将节点加入集群

令牌用于控制平面节点和加入节点之间的相互身份验证。 这里包含的令牌是密钥。确保它的安全, 因为拥有此令牌的任何人都可以将经过身份验证的节点添加到你的集群中。 可以使用 kubeadm token 命令列出,创建和删除这些令牌。 请参阅 kubeadm 参考指南

查看一下集群状态,确认个组件都处于healthy状态:

1
2
kubectl get nodes
kubectl get cs

集群初始化如果遇到问题,可以使用下面的命令进行清理:

1
2
3
4
5
6
kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/

安装 Pod 网络附加组件

注意:

本节包含有关网络设置和部署顺序的重要信息。 在继续之前,请仔细阅读所有建议。

你必须部署一个基于 Pod 网络插件的 容器网络接口 (CNI),以便你的 Pod 可以相互通信。 在安装网络之前,集群 DNS (CoreDNS) 将不会启动。

  • 注意你的 Pod 网络不得与任何主机网络重叠: 如果有重叠,你很可能会遇到问题。 (如果你发现网络插件的首选 Pod 网络与某些主机网络之间存在冲突, 则应考虑使用一个合适的 CIDR 块来代替, 然后在执行 kubeadm init 时使用 --pod-network-cidr 参数并在你的网络插件的 YAML 中替换它)。

  • 默认情况下,kubeadm 将集群设置为使用和强制使用 RBAC(基于角色的访问控制)。 确保你的 Pod 网络插件支持 RBAC,以及用于部署它的 manifests 也是如此。

  • 如果要为集群使用 IPv6(双协议栈或仅单协议栈 IPv6 网络), 请确保你的Pod网络插件支持 IPv6。 IPv6 支持已在 CNI v0.6.0 版本中添加。

说明: 目前 Calico 是 kubeadm 项目中执行 e2e 测试的唯一 CNI 插件。 如果你发现与 CNI 插件相关的问题,应在其各自的问题跟踪器中记录而不是在 kubeadm 或 kubernetes 问题跟踪器中记录。

一些外部项目为 Kubernetes 提供使用 CNI 的 Pod 网络,其中一些还支持网络策略

请参阅实现 Kubernetes 网络模型 的附加组件列表。

你可以使用以下命令在控制平面节点或具有 kubeconfig 凭据的节点上安装 Pod 网络附加组件:

1
kubectl apply -f <add-on.yaml>

安装Tigera Calico

Install the Tigera Calico operator and custom resource definitions.

1
2
wget https://docs.projectcalico.org/manifests/tigera-operator.yaml
kubectl create -f tigera-operator.yaml

Install Calico by creating the necessary custom resource. For more information on configuration options available in this manifest, see the installation reference.

1
2
3
4
wget https://docs.projectcalico.org/manifests/custom-resources.yaml
# 替换ip地址
sed -i 's/192.168.0.0/10.244.0.0/g' custom-resources.yaml
kubectl create -f custom-resources.yaml

Note: Before creating this manifest, read its contents and make sure its settings are correct for your environment. For example, you may need to change the default IP pool CIDR to match your pod network CIDR.

Confirm that all of the pods are running with the following command.

1
watch kubectl get pods -n calico-system

确认所有Pod正在运行

每个集群只能安装一个 Pod 网络。

安装 Pod 网络后,您可以通过在 kubectl get pods --all-namespaces 输出中检查 CoreDNS Pod 是否 Running 来确认其是否正常运行。 一旦 CoreDNS Pod 启用并运行,你就可以继续加入节点。

如果您的网络无法正常工作或CoreDNS不在“运行中”状态,请查看 kubeadm故障排除指南

控制平面节点隔离

默认情况下,出于安全原因,你的集群不会在控制平面节点上调度 Pod。 如果你希望能够在控制平面节点上调度 Pod, 例如用于开发的单机 Kubernetes 集群,即移除污点taints,允许master节点部署pod。请运行:

1
kubectl taint nodes --all node-role.kubernetes.io/master-

输出看起来像:

1
node/k8s-main1 untainted

image-20210120145728982

这将从任何拥有 node-role.kubernetes.io/master taint 标记的节点中移除该标记, 包括控制平面节点,这意味着调度程序将能够在地方调度 Pods。

1
kubectl get no -o yaml | grep taint -A 5

无显示,说明污点去除成功。

定义污点和容忍度

污点定义在节点的node Spec中,而容忍度则定义在PodpodSpec中,它们都是键值型数据,但又都额外支持一个效果effect标记,语法格式为key=value:effect,其中keyvalue的用法及格式与资源注俯-信息相似, 而effect则用于定义对Pod对象的排斥等级,它主要包含以下三种类型

  • NoSchedule
    不能容忍此污点的新Pod对象不可调度至当前节点,属于强制型约束关系,节点上现存的Pod对象不受影响。
  • PreferNoSchedule
    NoSchedule的柔性约束版本,即不能容忍此污点的新Pod对象尽量不要调度至当前节点,不过无其他节点可供调度时也允许接受相应的Pod对象。节点上现存的Pod对象不受影响。
  • NoExecute
    不能容忍此污点的新Pod对象不可调度至当前节点,属于强制型约束关系,而且节点上现存的Pod对象因节点污点变动或Pod容忍度变动而不再满足匹配规则时,Pod对象将被驱逐。

Pod对象上定义容忍度时,它支持两种操作符:一种是等值比较Equal,表示容忍度与污点必须在keyvalueeffect三者之上完全匹配;另一种是存在性判断Exists,表示二者的keyeffect必须完全匹配,而容忍度中的value字段要使用空值。

一个节点可以配置使用多个污点,一个Pod对象也可以有多个容忍度,不过二者在进行匹配检查时应遵循如下逻辑。

  • 首先处理每个有着与之匹配的容忍度的污点
  • 不能匹配到的污点上,如果存在一个污点使用了NoSchedule效用标识,则拒绝调度Pod对象至此节点
  • 不能匹配到的污点上,若没有任何一个使用了NoSchedule效用标识,但至少有一个使用了PreferNoScheduler,则应尽量避免将Pod对象调度至此节点
  • 如果至少有一个不匹配的污点使用了NoExecute效用标识,则节点将立即驱逐Pod对象,或者不予调度至给定节点;另外,即便容忍度可以匹配到使用了 NoExecute效用标识的污点,若在定义容忍度时还同时使用tolerationSeconds属性定义了容忍时限,则超出时限后其也将被节点驱逐。

master节点设置污点taint

1
kubectl taint nodes k8s-main1 node-role.kubernetes.io/master=:NoSchedule

注意⚠️ : 为k8s-main1设置的这个taint中, node-role.kubernetes.io/masterkey, value为空, effectNoSchedule

如果输入命令时, 你丢掉了=符号, 写成了node-role.kubernetes.io/master:NoSchedule, 会报error: at least one taint update is required错误

测试集群DNS是否可用

1
2
3
4
5
6
7
8
kubectl run curl --image=radial/busyboxplus:curl -it
# 进入后执行nslookup kubernetes.default确认解析正常
[ root@curl-5cc7b478b6-r997p:/ ]$ nslookup kubernetes.default
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: kubernetes.default
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local

image-20210119164504723

kube-proxy开启ipvs

修改ConfigMap的kube-system/kube-proxy中的config.confmode: “ipvs”

1
kubectl edit cm kube-proxy -n kube-system

image-20210119164633970

之后重启各个节点上的kube-proxy pod

1
kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
1
2
kubectl get pod -n kube-system | grep kube-proxy
kubectl logs kube-proxy-8hkh5 -n kube-system

image-20210119164711463

日志中打印出了Using ipvs Proxier,说明ipvs模式已经开启。

向Kubernetes集群中添加Node节点

下面将node这个主机添加到Kubernetes集群中,在node上执行:

1
2
sudo kubeadm join 172.19.184.2:6443 --token 5iab54.6o9zfohgsc1vt9vo \
--discovery-token-ca-cert-hash sha256:1e56d8d955f28577a8e49a9f9fb5c8ce82fad2c07742b8565428c7ec501df365

image-20210119135024730

在master节点上执行命令查看集群中的节点

重点查看STATUS内容为Ready时,则说明集群状态正常。

创建Pod以验证集群是否正常。

1
2
3
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pod,svc

清理

如果你在集群中使用了一次性服务器进行测试,则可以关闭这些服务器,而无需进一步清理。你可以使用 kubectl config delete-cluster 删除对集群的本地引用。

但是,如果要更干净地取消配置群集, 则应首先清空节点并确保该节点为空, 然后取消配置该节点。

删除节点

使用适当的凭证与控制平面节点通信,运行:

1
kubectl drain k8s-worker1 --delete-local-data --force --ignore-daemonsets

在删除节点之前,请重置 kubeadm 安装的状态(在移除节点执行

1
sudo kubeadm reset

重置过程不会重置或清除 iptables 规则或 IPVS 表。如果你希望重置 iptables,则必须手动进行:

1
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

如果要重置 IPVS 表,则必须运行以下命令:

1
ipvsadm -C

现在删除节点:

1
kubectl delete node k8s-worker1

如果你想重新开始,只需运行 kubeadm initkubeadm join 并加上适当的参数。

清理控制平面

你可以在控制平面主机上使用 kubeadm reset 来触发尽力而为的清理。

有关此子命令及其选项的更多信息,请参见kubeadm reset参考文档。

局限性

集群弹性

此处创建的集群具有单个控制平面节点,运行单个 etcd 数据库。 这意味着如果控制平面节点发生故障,你的集群可能会丢失数据并且可能需要从头开始重新创建。

解决方法:

平台兼容性

kubeadm deb/rpm 软件包和二进制文件是为 amd64,arm (32-bit),arm64,ppc64le 和 s390x 构建的遵循多平台提案

从 v1.12 开始还支持用于控制平面和附加组件的多平台容器镜像。

只有一些网络提供商为所有平台提供解决方案。请查阅上方的 网络提供商清单或每个提供商的文档以确定提供商是否 支持你选择的平台。

参考链接:

安装 kubeadm
使用 kubeadm 创建集群
k8s 污点和容忍度