问题描述
正如标题,在虚拟化环境中,我们将使用 kubeadm 部署高可用集群。我们选择通过 kubeadm 部署集群是因为:通过 kubeadm 部署的集群能够满足最佳实践的要求;并且我们能够通过 kubeadm 命令进行集群管理,比如 Bootstrap Token 管理、集群升级等等;并且我们还未达到使用 Kubespray 的规模。因此,我们遵循着官方的建议,完成对生产环境的部署(虽然以后我们肯定会踩别人踩过的坑,但是现实情况就是这个样子)。我们也知道还有很多第三方的解决方案,但是我们依旧未达到那样的规模,贸然的引入复杂且巨大的技术栈,对于我们来说并不是明智的选择。迭代更新与推翻重建是必然的,我们无法一部到位。所以,对于我们来说,就目前的情形以及未来的规模,选择 kubeadm 部署集群是最好的选择。
创建这篇笔记是为了记录我们的部署过程,并为大家提供部署经验,也便于我们在日后能够进行快速部署,而不是指导大家如何部署高可用的 Kubernetes 集群。虽然参考笔记我们的能够保证集群部署成功,但是由于部分细节未提及,很可能会部署失败,因此我们仍旧建议参考官方文档完成集群的部署。
该笔记将记录:在 Linux 中,如何搭建 Kubernetes 1.20 集群,以及常见问题处理。
补充说明
集群架构
集群架构:我们采用官方的 Stacked Control Plane 架构,以为该架构所需要的主机数量少,而其缺点也是我们能接受的,且从目前来看对未来的影响不大。
节点数量:共计 6 个节点,Control Plane x 3,Worker Node x 3
高可用性:我们采用 kube-vip 来实现高可用,所有节点访问 VIP 来通讯。而且 kube-vip x 3 都运行在 Control Plane 所在的主机中,以减少集群主机数量。
部署信息
操作系统:
Ubuntu 18.04.5 LTS(鉴于 CentOS 现在的情况,我们只能另选其他发行版。而服务软件支持的 Linux 发行版是有限的,比如 Docker 支持 CentOS、Debian、Fedora、Ubuntu 发行版。此外,结合使用习惯,我们能在 Debian 和 Ubuntu 之间选择。最后决定选择 Ubuntu 是因为该发行版的工具链更丰富,而 Debian 的应用及内核会稍微落后些。也许 Ubuntu 的稳定性差些,但是都在容忍范围内,不然我们就要容忍工具链陈旧难以排查问题的情况)
服务版本:
1)Docker 19.03.12
2)kubeadm 1.20.5
3)Calico 3.18.1
网络信息:
1)Control Plane:172.31.253.60(负载均衡的虚拟地址),172.31.253.61 k8scp-01,172.31.253.62 k8scp-02,172.31.253.63 k8scp-03
2)Worker Node:172.31.253.71 k8swn-01,172.31.253.72 k8swn-02,172.31.253.73 k8swn-03
参考文档
我们按照官方文档的要求,完成对 Kubernetes 1.20 集群的部署。以下是我们阅读官方文档的顺序:
1)Container runtimes | Kubernetes
2)Installing Kubernetes with deployment tools | Kubernetes
…. Bootstrapping clusters with kubeadm
…….. Installing kubeadm
…….. Creating a cluster with kubeadm | Kubernetes
…….. Creating Highly Available clusters with kubeadm | Kubernetes
鉴于篇幅有限,我们仅记录与此次部署相关的内容。我们忽略细节描述与解释说明,更多内容,参考官方文档。
解决方案
第一阶段、运行环境检查
执行主机:所有节点
Control Plane、Worker Node
环境要求必须满足,需要检查以下信息:
1)兼容 Linux 主机;
通用:我们使用官方提供的工具部署 kubeadm 来部署 Kubernets 集群,所以需要与其兼容的 Linux 发行版。我们使用 Ubuntu 18.04 发行版。
干净:我们建议的新的主机中部署节点,而不建议在已有其他环境的主机中部署集群。各种环境及配置交织在一起,集群组件可能无法正常处理这些场景,需要更精细的配置。
2)最少 2 CPU + 2G RAM 资源,或以上;
部署 Kubernetes 集群的最小资源要求,否则将无法运行其他应用程序。
节点主机尽量相互远离,在我们的实验环境中,所有的节点都位于相同物理主机,当主机负载便高(磁盘响应慢),etcd 的写入变慢,etcd 内部开始选举,而 etcd 选举期间无法连接,导致 apiserver 无法访问,进而导致 controller-manager 和 scheduler 的频繁重启。
3)节点网络互联(公网或私网皆可);
注意事项:这里的“互联”是指通过网卡的网络地址能够直接互相访问。而不是“阿里云主机能够访问腾讯云主机”的那种互联,这两个主机内网地址是不能互联的,所以是不可行的。但是,Hostinger 的主机与 Vultr 的主机能够满足要求,因为这两家服务商的主机网卡直接绑定公网地址。
此外,如果主机存在多张网卡,则需要正确配置路由,以保证节点能够互相访问。
配置允许 iptables 处理 bridge 流量:
# 添加内核模块 cat > /etc/modules-load.d/k8s.conf <<EOF br_netfilter EOF systemctl restart systemd-modules-load.service # 修改内核参数 cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system
配置 ipvs 模块,当 kube-proxy 运行在 IPVS mode 下需要使用这些模块:
# 添加内核模块 cat >> /etc/modules-load.d/k8s.conf <<EOF # run kube-proxy in ipvs mode ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh ## use nf_conntrack instead of nf_conntrack_ipv4 for Linux kernel 4.19 and later ## nf_conntrack_ipv4 nf_conntrack EOF systemctl restart systemd-modules-load.service
配置 Calico 需要使用内核模块:
# **追加** Calico 要求的内核模块 # 那些注释(#)的内核模块,虽然官方文档要求,但是可能因为文档未更新。在 Ubuntu 18.04 TLS 中,我们未找到这些模块 # https://github.com/kubernetes-sigs/kubespray/issues/6289 cat >> /etc/modules-load.d/k8s.conf <<EOF # for Calico ip_set ip_tables ip6_tables ipt_REJECT ipt_rpfilter ipt_set nf_conntrack_netlink # nf_conntrack_proto_sctp sctp xt_addrtype xt_comment xt_conntrack # xt_icmp # xt_icmp6 xt_ipvs xt_mark xt_multiport # xt_rpfilter xt_sctp xt_set xt_u32 ipip EOF # 加载内核模块配置 systemctl restart systemd-modules-load.service
禁用 IPv6 协议栈(参考 a 5-second delay 问题):
# 针对 Ubuntu 18.04 发行版,或者其他使用 Netplan 的 Ubunut 发行版 cat > /etc/netplan/99-zzz-no-ipv6.yaml <<EOF network: ethernets: enp1s0: link-local: [] EOF netplan apply # 针对 CentOS Linux release 7.x 发行版,但是不适用 Ubuntu 18.04 发行版 cat > /etc/sysctl.d/99-disable-ipv6.conf <<EOF net.ipv6.conf.all.disable_ipv6=1 net.ipv6.conf.default.disable_ipv6=1 net.ipv6.conf.default.disable_ipv6=1 EOF sysctl --system
4)主机信息唯一:主机名、MAC 地址、product_uuid 信息;
查看 cat /sys/class/dmi/id/product_uuid 文件,保证 product_uuid 唯一,否则安装过程会失败。
5)必要端口未被占用;
需要检查的必要端口,参考 Check required ports 页面。我们采用新的主机,所以必要端口不太可能被占用。
同时需要将端口添加到防火墙,以允许访问。我们选择禁用防火墙:
systemctl stop ufw.service systemctl disable ufw.service
6)禁用交换分区(SWAP);
如果未禁用交换分区,则当内存页的换入与换出,会影响调度性能。官方要求必须管理交换分区:
sed -i -E 's%(^[^#].+\sswap\s.+)%# \1%g' /etc/fstab swapoff -a
7)检查容器运行环境;
我们已经在最开始部署容器运行环境,并进行相关设置。kubeadm 会根据 Unix Domain Socket 路径来甄别主机中部署的容器环境(Docker /var/run/dockershim.sock;containerd /run/containerd/containerd.sock;CRI-O /var/run/crio/crio.sock)。
我们(1)采用 Docker 作为容器运行环境,并(2)采用 systemd 作为 cgroup manager 以简化问题(而未使用 cgroupfs 作为 cgroup manager)。
安装 Docker 环境:Installing Docker on Ubuntu
修改 Docker 配置:
cat > /etc/docker/daemon.json <<EOF { "live-restore": true, "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2" } EOF systemctl start docker systemctl enable docker systemctl reload docker systemctl restart docker
注意事项:在主机中,不能同时运行两种容器环境,否则会出现错误。
第二阶段、安装 kubeadm/kubectl/kubelet 工具
执行主机:所有节点
Control Plane、Worker Node
接下来,开始安装 kubeadm/kubectl/kubelet 应用;
版本选择
如果不知道应该选择什么版本,建议参考云平台支持的版本:
1)Supported Versions of Kubernetes
2)Amazon EKS Kubernetes versions – Amazon EKS
虽然在官方文档中允许跨一个次版本号,但是为了避免不必要的问题,对于这些工具,我们统一使用 1.18.9 版本(我们从 AWS 支持的版本中选择的)。
# 04/08/2021 我们在部署 1.20.5 版本时(这是目前为止最新的版本),遇到各种失败,尤其是在加入第三个节点时 etcd 无法自动创建。当我们切换到 1.18.9 版本,相同的部署步骤,集群能够正常运行(对于大型服务,不用最新版似乎是黄金定律)
安装服务
# 首先,安装依赖工具 apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl # 然后,导入仓库密钥 # 尽管下载 apt-key.gpg 存在困难,但是应该尽量从官方站点下载(请勿随意使用第三方密钥) # apt-key adv --keyserver keyserver.ubuntu.com --recv-keys FEEA9169307EA071 8B57C5C2836F4BEB curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - cat > /etc/apt/sources.list.d/kubernetes.list <<EOF deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main EOF apt-get update # 最后,安装工具 # apt-get install -y kubelet=1.18.17-00 kubeadm=1.18.17-00 kubectl=1.18.17-00 --allow-downgrades apt-get install -y kubelet=1.18.9-00 kubeadm=1.18.9-00 kubectl=1.18.9-00 apt-mark hold kubelet kubeadm kubectl # 禁止更新
我们这里保存 ./apt-key.gpg 文件
第三阶段、配置 kube-vip 环境
执行主机:首个 Control Plane 节点(k8scp-01)
官方文档已给出高可用方案,参考 kubeadm/ha-considerations.md at master · kubernetes/kubeadm 文档。
我们采用其中的 kube-vip 实现高可用,替代以往的 keepalivd + haproxy 方案。
注意事项,如果采用阿里云主机部署,需要购买 HaVIP 服务,才能实现 VIP 的访问。否则,即使 VIP 绑定到某台主机中,其他主机也无法访问该地址。这个 HaVIP 是为了实现 VIP 的访问(可能是因为网络环境的关系),并不是为了替代 kube-vip 或 keepalived 服务。现在(04/21/2021),HaVIP 需要申请(🔗),通过之后在 专有网络/高可用虚拟IP 中使用服务。但是,只能绑定两台机器,所以无法配置真正的高可用,仅能部署单 Master 集群。
配置 control-plane-endpoint 地址
cat >> /etc/hosts <<EOF 172.31.253.60 control-plane-endpoint EOF
我们将域名解析直接写入 /etc/hosts 文件,但是我们建议将该域名通过 DNS 服务解析。
创建 kube-vip 配置
mkdir -pv /etc/kube-vip/ cat > /etc/kube-vip/config.yaml <<EOF localPeer: id: k8scp-01 address: 172.31.253.61 port: 10000 remotePeers: - id: k8scp-02 address: 172.31.253.62 port: 10000 - id: k8scp-03 address: 172.31.253.63 port: 10000 vip: 172.31.253.60 gratuitousARP: true singleNode: false startAsLeader: true interface: "enp1s0" loadBalancers: - name: API Server Load Balancer type: tcp port: 56443 bindToVip: true backends: - port: 6443 address: 172.31.253.61 - port: 6443 address: 172.31.253.62 - port: 6443 address: 172.31.253.63 EOF
创建 kube-vip 的 Static Pod 配置
mkdir -pv /etc/kubernetes/manifests/ docker run -it --rm plndr/kube-vip:0.3.3 sample manifest \ > /etc/kubernetes/manifests/kube-vip.yaml
第四阶段、通过 kubeadm 部署集群
执行主机:首个 Control Plane 节点(k8scp-01)
集群初始化
# kubeadm init \ --control-plane-endpoint "control-plane-endpoint:56443" \ --apiserver-bind-port 6443 \ --upload-certs \ --image-repository "registry.aliyuncs.com/google_containers" \ --v=10 [init] Using Kubernetes version: v1.20.5 [preflight] Running pre-flight checks ... [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/ You can now join any number of the control-plane node running the following command on each as root: kubeadm join control-plane-endpoint.d3rm.org:8443 --token tswdac.5r48i5er2k8vhpfx \ --discovery-token-ca-cert-hash sha256:1fc8a4ce5b335807a18cbe7750aed90b0d7efbed06f9123271a7266eb31c15e9 \ --control-plane --certificate-key 42f30016c5ce2c76de2fbdf14dd7d85c125b5b02567110bb4d22a48c24341d96 Please note that the certificate-key gives access to cluster sensitive data, keep it secret! As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use "kubeadm init phase upload-certs --upload-certs" to reload certs afterward. Then you can join any number of worker nodes by running the following on each as root: kubeadm join control-plane-endpoint.d3rm.org:8443 --token tswdac.5r48i5er2k8vhpfx \ --discovery-token-ca-cert-hash sha256:1fc8a4ce5b335807a18cbe7750aed90b0d7efbed06f9123271a7266eb31c15e9
完成后续任务
按照 kubeadm init 要求,完成后续设置:
mkdir -p $HOME/.kube cp /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config
配置 kube-proxy ipvs 模式
# kubectl edit -n kube-system configmaps kube-proxy ... mode: ipvs ... # kubectl delete pods -n kube-system -l k8s-app=kube-proxy pod "kube-proxy-xxxxx" deleted
能够在初始化配置文件中设置,然后再完成初始化设置。但是,暂时无法通过命令行指定 kube-proxy 运行模式。
第五阶段、安装网络插件
如果未安装网络插件,查看 journalctl -f -u kubelet 将提示如下错误消息:
10541 kubelet.go:2184] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
我们使用 Calico 网络插件:
1)检查依赖,参考 System requirements 文档。
网络检查:在内网环境中,我们已经禁止 Control Plane 的防火墙,以允许端口放行。
权限检查:在默认配置中,kubelet 具有 –allow-privileged=true 选项,因此能以 CAP_SYS_ADMIN 允许 Calico 容器,满足要求。
集群要求:
在默认配置中,kubelet 以 –network-plugin=cni 启动,满足要求。
集群必须使用 Calico 作为唯一插件,且不支持从其他网络插件迁移到 Calico 插件。我们满足要求。
我们在内核启用 ipvs 模块,因此 kube-proxy 运行在 IPVS 模式下(但需要部署时修改 ConfigMap 配置)。
主机网段、Service 网段是自动选择的,不存在冲突。
内核模块依赖:我们已在开始处添加 Calico 依赖的内核模块。
2)下载并根据需要修改
# 按照文档的描述,我们的场景应该使用 Kubernetes API datastore 存储,且小于 50 个节点数 curl https://docs.projectcalico.org/manifests/calico.yaml -O
3)应用网络配置:
kubectl apply -f calico.yaml
第六阶段、添加其他节点到集群中
添加 Control Plane 节点
鉴于前几个阶段已经执行,现仅需在节点上执行第三阶段(部署 kube-vip 服务),以及将节点加入集群,但执行顺序上有所不同:
1)修改 /etc/hosts 文件
2)创建 kube-vip 配置
但是,需要对配置文件进行修改,比如 localPeer remotePeers startAsLeader interface 字段。这里不再详细说明,针对该配置文件的格式,已经非常明确应该修改哪些内容。
3)将节点加入集群(我们还不清楚为什么使用 kubeadm init 输出的命令会失败,需要重新生成才能加入集群):
kubeadm join control-plane-endpoint.d3rm.org:8443 --token tswdac.5r48i5er2k8vhpfx \ --discovery-token-ca-cert-hash sha256:1fc8a4ce5b335807a18cbe7750aed90b0d7efbed06f9123271a7266eb31c15e9 \ --control-plane --certificate-key 42f30016c5ce2c76de2fbdf14dd7d85c125b5b02567110bb4d22a48c24341d96
4)创建 kube-vip static pod 配置
该步骤必须放在节点加入集群后执行,这是因为 kube-vip 的怪异行为,必须这样处理。
添加 Worker 节点
1)修改 /etc/hosts 文件
主要是为了处理「修改主机名的」问题。如果主机名已经解析到 127.0.0.1 便无需修改。
2):
// 创建加入命令(如果有必要) # kubeadm token create --print-join-command W1130 03:58:43.247465 2325287 configset.go:202] WARNING: kubeadm cannot validate... kubeadm join 192.168.10.130:6443 --token ugb23y.35g8iz02esnlu39m --discovery-tok... // 加入集群 kubeadm join control-plane-endpoint.d3rm.org:6443 --token 4pedgf.a2uc2vvtrknce1wb \ --discovery-token-ca-cert-hash sha256:0fce70ebfdc980066ea08cdfdc54a835215077834b54f10a36196c22a9dacb09
常见问题处理
open /etc/kubernetes/pki/ca.crt: no such file or directory
问题描述:
# kubeadm join control-plane-endpoint.d3rm.org:8443 --token zb93u8.0xbcfu00xlo8ulbr \ > --discovery-token-ca-cert-hash sha256:396025ff1affbf913c70af034e20ab7c0385631625439db833a099eee88fa0a3 \ > --control-plane [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' error execution phase preflight: One or more conditions for hosting a new control plane instance is not satisfied. failure loading certificate for CA: couldn't load the certificate file /etc/kubernetes/pki/ca.crt: open /etc/kubernetes/pki/ca.crt: no such file or directory Please ensure that: * The cluster has a stable controlPlaneEndpoint address. * The certificates that must be shared among control plane instances are provided. To see the stack trace of this error execute with --v=5 or higher
原因分析:我们遇到该错误是因为在 kubeadm init 时,未指定 –upload-certs 选项,因此后续操作需要手动传递证书到各个节点。
解决方案:重新生成 join 命令
# kubeadm init phase upload-certs --upload-certs [upload-certs] Storing the certificates in Secret "kubeadm-certs" in the "kube-system" Namespace [upload-certs] Using certificate key: 67b240dd68a9f4dba7017164bb095b08cf21b6445e20199d529269b7bd685973 # kubeadm token create --print-join-command --certificate-key 67b240dd68a9f4dba7017164bb095b08cf21b6445e20199d529269b7bd685973 kubeadm join control-plane-endpoint.d3rm.org:6443 --token i4lcbu.5jpmipmdp6wjde3j --discovery-token-ca-cert-hash sha256:95147bd44e3109a96414ac7f9d480ef5d42a43819bd91d22c84289f71b460bde --control-plane --certificate-key 67b240dd68a9f4dba7017164bb095b08cf21b6445e20199d529269b7bd685973
calico/node is not ready: BIRD is not ready: BGP not established with 10.117.6.1
# vim calico.yaml ... - name: IP_AUTODETECTION_METHOD value: "interface=<ethx>" ...
也可能是因为主机环境过于复杂,而不是“干净的新主机”,导致集群组建无法正常处理,需要进一步配置组件。
我们犯过的错误
我们犯过以下错误:
1)主机环境不干净:导致组件无法针对环境做出正确判断,导致集群初始化失败。比如:
在 kubeadm reset 后,当前节点的 etcd 未从 etcd 集群中剔除,导致当前节点无法再次加入集群。我们应该完全重置集群(测试环境),或者修改主机名(使其成为“新主机”)。
2)组件版本不一致:导致节点在加入集群时失败;
参考文献
calico/node is not ready: BIRD is not ready: BGP not established (Calico 3.6 / k8s 1.14.1) · Issue #2561
Container runtimes | Kubernetes
Creating a cluster with kubeadm | Kubernetes
Disabling IPv6 in Ubuntu Server 18.04 – Ask Ubuntu
Install Calico networking and network policy for on-premises deployments
IPVS-Based In-Cluster Load Balancing Deep Dive | Kubernetes
jenkins – Kubernetes – Calico-Nodes 0/1 Ready – Stack Overflow
kubeadm – Enable IPVS Mode in Kube Proxy on a ready Kubernetes Local Cluster – Stack Overflow
kubernetes – Nodes get no certificates when trying to join a cluster with `kubeadm`
kubernetes/README.md at master · kubernetes/kubernetes
kube-vip/kubernetes-control-plane.md at master · plunder-app/kube-vip
linux – Is there a way to refresh the current configuration used by modprobe with a newly updated modules.conf file?
gnupg – How do I bypass/ignore the gpg signature checks of apt? – Ask Ubuntu