部署大型 Kubernetes 生产集群
本文将探讨如何部署一个较大规模的 Kubernetes 生产集群。
准备工作
硬件和软件推荐
对于不同规模的 Kubernetes 生产集群有着不同的硬件规则要求,以下将针对不同规模的集群给出相应的硬件要求
ETCD
etcd 通常在开发或测试目的下使用有限资源时运行良好;在笔记本电脑或廉价云计算机上开发 etcd 是很常见的。 然而,在生产中运行 etcd 集群时,一些硬件指南对于正确管理非常有用。 这些建议不是硬性规定;它们作为强大的生产部署的良好起点。
CPUs
很少的 etcd 部署需要大量的 CPU 容量。典型集群需要两到四个核心才能平稳运行。 负载较重的 etcd 部署,为数千个客户端或每秒数万个请求提供服务,往往会受到 CPU 限制, 因为 etcd 可以从内存中提供请求。这样高负载的部署通常需要 8 至 16 个专用核心。
内存
etcd 的内存占用相对较小,但其性能仍取决于具有足够内存。 etcd 服务器将积极缓存 key-value 数据,并花费大部分其余内存跟踪观察者,通常 8GB 就足够了; 对于拥有数千个观察者和数百万个 keys 的重型部署,请相应地分配 16GB 到 64GB 内存。
硬盘
快速磁盘是 etcd 部署性能和稳定性最关键的因素。对于大型集群建议使用 100MB/s 或者更高的硬盘速度;尽可能地使用 SSD 作为 etcd 的存储后端。
网络
多成员 etcd 部署受益于快速可靠的网络。为了使 etcd 既一致又分区容错,不可靠的网络和分区故障会导致可用性差。 低延迟可以确保 etcd 成员快速通信、高带宽可以减少恢复失败的 etcd 成员所需的时间。 1GbE 对于常见的 etcd 部署已足够。对于大型 etcd 群集,10GbE 网络将减少平均恢复时间。
硬件配置推荐
以下是针对 Kubernetes 规模做出的硬件配置推荐
集群规模 | 节点数 | vCPUs | 内存(GB) | 硬盘 | Concurrent IOPS | 硬盘带宽 (MB/s) |
---|---|---|---|---|---|---|
小 | 50 | 2 | 8 | 50G SSD | 1500 | 25 |
中 | 250 | 4 | 16 | 150G SSD | 4500 | 75 |
大 | 1000 | 8 | 32 | 250G SSD | 7500 | 125 |
超大 | 3000 | 16 | 64 | 500G SSD | 15,000 | 250 |
初始化操作
关闭防火墙或开启所有流量
以下方式任选其一
关闭防火墙
systemctl stop firewalld
允许所有流量
systemctl start firewalld
firewall-cmd --set-default-zone=trusted
firewall-cmd --complete-reload
systemctl disable iptables && systemctl enable firewalld
开机加载 ipvs 内核模块
cat << EOF > /etc/modules-load.d/ipvs.conf
# Load IPVS at boot
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
EOF
修改/etc/sysctl.conf 文件
echo 'br_netfilter' >/etc/modules-load.d/br_netfilter.conf
modprobe br_netfilter
cat>>/etc/sysctl.conf <<EOF
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
EOF
sysctl -p
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
禁用 swap
sed -i "/swap/d" /etc/fstab
swapoff -a #kube scheduler要求关闭swap
Kubernetes
- 在集群中,确保所有计算机之间存在全网络连接(公网或私网)
- 在所有机器上具有 sudo 权限
- 可以使用其他工具;本教程以 sudo 举例
master 节点配置要求
节点数 | 配置 |
---|---|
1-5 | 1vCPU 4G 内存 |
6-10 | 2vCPU 8G 内存 |
11-100 | 4vCPU 16G 内存 |
101-250 | 8vCPU 32G 内存 |
251-500 | 16vCPU 64G 内存 |
大于 500 | 32vCPU 128G 内存 |
部署
⚠️ 注意 请给所有的节点关闭防火墙以及设置 hosts
配置清单
服务类型 | IP/主机名 | 配置 |
---|---|---|
ETCD | 10.20.0.1/etcd1 | 4C16G |
ETCD | 10.20.0.2/etcd2 | 4C16G |
ETCD | 10.20.0.3/etcd3 | 4C16G |
ETCD | 10.20.0.4/etcd4 | 4C16G |
ETCD | 10.20.0.5/etcd5 | 4C16G |
ETCD | 10.20.0.6/etcd6 | 4C16G |
Master | 10.20.0.10/master1 | 32C128G |
Master | 10.20.0.11/master2 | 32C128G |
Master | 10.20.0.12/master3 | 32C128G |
Worker | 10.20.0.20/worker1 | 32C128G |
Worker | 10.20.0.21/worker2 | 32C128G |
… | … | … |
Worker | 10.20.0.221/worker200 | 32C128G |
步骤 1:安装 kubelet 和 kubeadm
该步骤需要在所有机器上执行,建议使用
Ansible
等脚本批量执行
添加 kubernetes.repo
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
无法访问的话,可以切换成使用镜像源 https://developer.aliyun.com/mirror/kubernetes/
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
设置 SELinux
# Set SELinux in permissive mode (effectively disabling it)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
安装 kubernetes 组件
这是会安装最新版本
# 这是会安装最新版本
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
或者安装指定版本
sudo yum install -y kubelet-1.25.8 kubeadm-1.25.8 kubectl-1.25.8 --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
系统参数设置
modprobe overlay # 打开overlay
modprobe br_netfilter # 打开netfilter
cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOF #内核处理
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
步骤 2:安装 CRI-O
该步骤需要在所有机器上执行,建议使用
Ansible
等脚本批量执行
OS=CentOS_8
VERSION=1.25 # 安装 1.25 版本的 cri-o
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/devel:kubic:libcontainers:stable.repo
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo
yum install -y cri-o
安装 podman
cri-o 默认不提供前端 cli,需要额外安装 podman
dnf install -y podman
获取 kubernetes 镜像列表
kubeadm config images list
registry.k8s.io/kube-apiserver:v1.25.8
registry.k8s.io/kube-controller-manager:v1.25.8
registry.k8s.io/kube-scheduler:v1.25.8
registry.k8s.io/kube-proxy:v1.25.8
registry.k8s.io/pause:3.8
registry.k8s.io/etcd:3.5.6-0
registry.k8s.io/coredns/coredns:v1.9.3
修改 cri-o 配置
vim /etc/crio/crio.conf
讲 pause_image 修改成镜像列表一致
[crio.image]
pause_image="registry.k8s.io/pause:3.8"
# 或者使用镜像地址
# pause_image="registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.8"
# 这个可以设置非 https 的镜像仓库
# insecure_registries = ["your.insecure.registry:5000", "another.insecure.registry:5000"]
启动 CRI-O 并设置其开机自启动
sudo systemctl start crio
sudo systemctl enable crio
步骤 3:安装 ETCD
该步骤将基于 kubeadm 部署 etcd 高可用集群
服务类型 | IP/主机名 | 配置 |
---|---|---|
ETCD | 10.20.0.1/etcd1 | 4C16G |
ETCD | 10.20.0.2/etcd2 | 4C16G |
ETCD | 10.20.0.3/etcd3 | 4C16G |
ETCD | 10.20.0.4/etcd4 | 4C16G |
ETCD | 10.20.0.5/etcd5 | 4C16G |
ETCD | 10.20.0.6/etcd6 | 4C16G |
将 kubelet 配置为 etcd 的服务管理器
# /usr/lib/systemd/system/kubelet.service.d 如果不存在
# 可以执行 systectl status kubelet 获取正确路径
cat <<EOF > /usr/lib/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
[Service]
ExecStart=
# 将下面的 "systemd" 替换为你的容器运行时所使用的 cgroup 驱动。
# kubelet 的默认值为 "cgroupfs"。
# cri-o 需要使用 --cgroup-driver="systemd"
# 如果需要的话,将 "--container-runtime-endpoint " 的值替换为一个不同的容器运行时。
ExecStart=/usr/bin/kubelet --address=127.0.0.1 --pod-manifest-path=/etc/kubernetes/manifests --cgroup-driver=systemd --container-runtime=remote --container-runtime-endpoint=unix:///run/crio/crio.sock
Restart=always
EOF
systemctl daemon-reload
systemctl restart kubelet
为 kubeadm 创建配置文件
# 使用你的主机 IP 替换 HOST0、HOST1 和 HOST2 的 IP 地址
export HOST0=10.20.0.1
export HOST1=10.20.0.2
export HOST2=10.20.0.3
# 使用你的主机名更新 NAME0、NAME1 和 NAME2
export NAME0="etcd1"
export NAME1="etcd2"
export NAME2="etcd3"
# 创建临时目录来存储将被分发到其它主机上的文件
mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/
HOSTS=(${HOST0} ${HOST1} ${HOST2})
NAMES=(${NAME0} ${NAME1} ${NAME2})
for i in "${!HOSTS[@]}"; do
HOST=${HOSTS[$i]}
NAME=${NAMES[$i]}
cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml
---
apiVersion: "kubeadm.k8s.io/v1beta3"
kind: InitConfiguration
nodeRegistration:
name: ${NAME}
localAPIEndpoint:
advertiseAddress: ${HOST}
---
apiVersion: "kubeadm.k8s.io/v1beta3"
kind: ClusterConfiguration
kubernetesVersion: v1.25.8 # 替换版本
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
etcd:
local:
serverCertSANs:
- "${HOST}"
peerCertSANs:
- "${HOST}"
extraArgs:
initial-cluster: ${NAMES[0]}=https://${HOSTS[0]}:2380,${NAMES[1]}=https://${HOSTS[1]}:2380,${NAMES[2]}=https://${HOSTS[2]}:2380
initial-cluster-state: new
name: ${NAME}
listen-peer-urls: https://${HOST}:2380
listen-client-urls: https://${HOST}:2379
advertise-client-urls: https://${HOST}:2379
initial-advertise-peer-urls: https://${HOST}:2380
EOF
done
生成证书颁发机构(CA 证书)
如果你已经拥有 CA,那么唯一的操作是复制 CA 的 crt 和 key 文件到 /etc/kubernetes/pki/etcd/ca.crt
和 /etc/kubernetes/pki/etcd/ca.key
。 复制完这些文件后继续下一步,“为每个成员创建证书”。
如果你还没有 CA,则在 $HOST0(你为 kubeadm 生成配置文件的位置)上运行此命令。
kubeadm init phase certs etcd-ca --kubernetes-version v1.25.8
这一操作创建如下两个文件:
- /etc/kubernetes/pki/etcd/ca.crt
- /etc/kubernetes/pki/etcd/ca.key
为每个成员创建证书
kubeadm init phase certs etcd-server --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST2}/
# 清理不可重复使用的证书
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
kubeadm init phase certs etcd-server --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
cp -R /etc/kubernetes/pki /tmp/${HOST1}/
find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
kubeadm init phase certs etcd-server --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-peer --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
# 不需要移动 certs 因为它们是给 HOST0 使用的
# 清理不应从此主机复制的证书
find /tmp/${HOST2} -name ca.key -type f -delete
find /tmp/${HOST1} -name ca.key -type f -delete
将 ${HOST0} 的文件复制到主目录
mv /tmp/${HOST0}/* ~/
复制证书和 kubeadm 配置
证书已生成,现在必须将它们移动到对应的主机。
USER=root
HOST=${HOST1}
scp -r /tmp/${HOST}/* ${USER}@${HOST}:
ssh ${USER}@${HOST}
root@HOST $ mv pki /etc/kubernetes/
确保已经所有预期的文件都存在
创建静态 Pod 清单
既然证书和配置已经就绪,是时候去创建清单了。 在每台主机上运行 kubeadm 命令来生成 etcd 使用的静态清单。
root@HOST0 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
root@HOST1 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
root@HOST2 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
注意:etcd-ca 证书只需要执行一次。 上面已经创建好了 etcd-cluster-1 的集群,接下来只需要重复以上操作,在 etcd4,etcd5,etcd6 创建一个 etcd-cluster-2 etcd 集群
步骤 4:初始化 Kubernetes 控制平面
从 etcd 所在的任意节点复制以下内容在所有 master 节点上
/etc/kubernetes/pki/etcd/ca.crt
/etc/kubernetes/pki/apiserver-etcd-client.crt
/etc/kubernetes/pki/apiserver-etcd-client.key
在 master(10.20.0.10)
节点上添加以下文件,
kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
# apiserver LOAD_BALANCER_ADDRESS
# 高可用的环境下,请务必设置该参数
controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT"
kubernetesVersion: v1.25.8 # 替换版本
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers # 改成镜像地址
networking:
dnsDomain: cluster.local
podSubnet: 10.244.0.0/16 # 按需修改
serviceSubnet: 10.96.0.0/12 # 按需修改
etcd:
external:
endpoints:
- https://10.20.0.1:2379
- https://10.20.0.2:2379
- https://10.20.0.3:2379
caFile: /etc/kubernetes/pki/etcd/ca.crt
certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
apiServer:
extraArgs:
etcd-servers-overrides: "/events#https://10.20.0.4:2379,https://10.20.0.5:2379,https://10.20.0.6:2379"
# 该参数给签发的证书附加 IP 或者 域名
#certSANs:
# -
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# kubelet specific options here
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# kube-proxy specific options here
执行以下命令进行初始化
sudo kubeadm init --config kubeadm.yaml --upload-certs
将会看到类似于以下的输出
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.20.0.10:6443 --token zjtxal.8au2tvz9sh1hgsmp \
--discovery-token-ca-cert-hash sha256:13d1241a5b5454d8052ef84176df45d4e82ad5e3a26b6dab9a4c5d888badb818
如在这个过程发生了意外情况,导致无法部署,请检查一下由什么问题引起的。 解决问题后,快捷的方式是将这台 master 重置,然后重新复制 etcd 的证书,再重新初始化
kubeadm reset -f
完成这部操作后,请从 步骤 4:初始化 Kubernetes 控制平面 重新开始
完成这一步之后需要安装 Calico CNI
步骤 5:安装 CNI(Calico)
首先,在您的集群上安装 operator
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
下载必要的自定义资源以配置 Calico
curl https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml -O
如果您希望自定义 Calico 安装,请在本地自定义下载的 custom-resources.yaml 清单。
# This section includes base Calico installation configuration.
# For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
# Note: The ipPools section cannot be modified post-install.
ipPools:
- blockSize: 26
cidr: 192.168.0.0/16 # 不要和现有 IP 段发生重合
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
---
# This section configures the Calico API server.
# For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.APIServer
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
创建清单以安装 Calico
kubectl create -f custom-resources.yaml
步骤 6:添加 Kubernetes master 节点
执行先前由第一个节点上的 kubeadm init 输出提供给你的 join 命令。 它看起来应该像这样:
kubeadm join 10.20.0.10:6443 --token zjtxal.8au2tvz9sh1hgsmp \
--discovery-token-ca-cert-hash sha256:13d1241a5b5454d8052ef84176df45d4e82ad5e3a26b6dab9a4c5d888badb818 \
--control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
步骤 7:添加 Kubernetes worker 节点
kubeadm join 10.20.0.10:6443 --token zjtxal.8au2tvz9sh1hgsmp \
--discovery-token-ca-cert-hash sha256:13d1241a5b5454d8052ef84176df45d4e82ad5e3a26b6dab9a4c5d888badb818
(可选) 将 ETCD 节点加入集群
将 ETCD 加入集群的好处是可以在集群内一起管理,需要加入集群的 ETCD 可以执行以下操作
rm -rf /usr/lib/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
systemctl daemon-reload
systemctl stop kubelet
kubeadm join 10.20.0.10:6443 --token zjtxal.8au2tvz9sh1hgsmp \
--discovery-token-ca-cert-hash sha256:13d1241a5b5454d8052ef84176df45d4e82ad5e3a26b6dab9a4c5d888badb818
加入集群后,需要将 etcd 所在节点设置禁止调度,避免其他应用抢占资源
kubectl cordon <NODENAME>
集群管理和优化
ETCD 优化
提高磁盘 IO 性能 ETCD
对磁盘写入延迟非常敏感,对于负载较重的集群建议磁盘使用 SSD 固态硬盘。可以使用 diskbench 或 fio 测量磁盘实际顺序 IOPS。
提高 ETCD 的磁盘 IO 优先级
由于 ETCD 必须将数据持久保存到磁盘日志文件中,因此来自其他进程的磁盘活动可能会导致增加写入时间,结果导致 ETCD 请求超时和临时 leader 丢失。当给定高磁盘优先级时,ETCD 服务可以稳定地与这些进程一起运行:
sudo ionice -c2 -n0 -p $(pgrep etcd)
添加节点
在运行一段时间后,可能存在添加新的节点的需求,通过 kubeadm 将可以很容易实现
执行以下命令将会创建一个新的 token 和一条命令
kubeadm token create --print-join-command
将类似于这样
kubeadm join 10.20.0.10:6443 --token zjtxal.8au2tvz9sh1hgsmp \
--discovery-token-ca-cert-hash sha256:13d1241a5b5454d8052ef84176df45d4e82ad5e3a26b6dab9a4c5d888badb818
参考资料
- https://github.com/cri-o/cri-o/blob/main/install.md#other-yum-based-operating-systems
- https://etcd.io/docs/v3.5/op-guide/hardware/
- https://kubernetes.io/zh-cn/docs/setup/production-environment/
- https://kubernetes.io/zh-cn/docs/setup/best-practices/cluster-large/
- https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/setup-ha-etcd-with-kubeadm/
- https://imroc.cc/kubernetes/index.html