本地 Kubernetes 集群安装
0. 网络环境的准备
Kubernetes 用到的很多镜像都在 gcr.io 上,在国内访问会有困难。
这里提供两个手段:
在家庭路由器上整个科学代理,实现全局科学上网。
使用
liangyuanpeng
大佬在评论区提供的 gcr 国内镜像地址,这需要进行如下替换:
- k8s.gcr.io—> lank8s.cn
- gcr.io—> gcr.lank8s.cn
1. 节点的环境准备
首先准备三台 Linux 虚拟机,系统按需选择,然后调整这三台机器的设置:
节点配置:
- master:不低于 2c/3g,硬盘 20G
- 主节点性能也受集群 Pods 个数的影响,上述配置应该可以支撑到每个 Worker 节点跑 100 个 Pod.
- worker:看需求,建议不低于 2c/4g,硬盘不小于 20G,资源充分的话建议 40G 以上。
- master:不低于 2c/3g,硬盘 20G
处于同一网络内并可互通(通常是同一局域网)
各主机的 hostname 和 mac/ip 地址以及
1
/sys/class/dmi/id/product_uuid
,都必须唯一
- 这里新手最容易遇到的问题,是 hostname 冲突
必须关闭 swap 交换内存,kubelet 才能正常工作
方便起见,我直接使用 ryan4yin/pulumi-libvirt 自动创建了五个 opensuse leap 15.3 虚拟机,并设置好了 ip/hostname.
1.1 iptables 设置
目前 kubernetes 的容器网络,默认使用的是 bridge 模式,这种模式下,需要使 iptables
能够接管 bridge 上的流量。
配置如下:
| 1 2 3 4 5 6 7 8 9 10
| sudo modprobe br_netfilter 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
|
| ———————— | ———————————————————— |
| | |
1.2 开放节点端口
局域网环境的话,建议直接关闭防火墙。这样所有端口都可用,方便快捷。
通常我们的云上集群,也是关闭防火墙的,只是会通过云服务提供的「安全组」来限制客户端 ip
Control-plane 节点,也就是 master,需要开放如下端口:
Protocol | Direction | Port Range | Purpose | Used By |
---|---|---|---|---|
TCP | Inbound | 6443* | Kubernetes API server | All |
TCP | Inbound | 2379-2380 | etcd server client API | kube-apiserver, etcd |
TCP | Inbound | 10250 | kubelet API | Self, Control plane |
TCP | Inbound | 10251 | kube-scheduler | Self |
TCP | Inbound | 10252 | kube-controller-manager | Self |
Worker 节点需要开发如下端口:
Protocol | Direction | Port Range | Purpose | Used By |
---|---|---|---|---|
TCP | Inbound | 10250 | kubelet API | Self, Control plane |
TCP | Inbound | 30000-32767 | NodePort Services† | All |
另外通常我们本地测试的时候,可能更想直接在 80
443
8080
等端口上使用 NodePort
, 就需要修改 kube-apiserver 的 --service-node-port-range
参数来自定义 NodePort 的端口范围,相应的 Worker 节点也得开放这些端口。
2. 安装 containerd
首先是环境配置:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter nf_conntrack EOF sudo modprobe overlay sudo modprobe br_netfilter sudo modprobe nf_conntrack # Setup required sysctl params, these persist across reboots. cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF # Apply sysctl params without reboot sudo sysctl --system
|
| ————————————————— | ———————————————————— |
| | |
安装 containerd+nerdctl:
1 2 3 4 5 6 7 8 |
wget https://github.com/containerd/nerdctl/releases/download/v0.11.1/nerdctl-full-0.11.1-linux-amd64.tar.gz tar -axvf nerdctl-full-0.11.1-linux-amd64.tar.gz # 这里简单起见,rootless 相关的东西也一起装进去了,测试嘛就无所谓了... mv bin/* /usr/local/bin/ mv lib/systemd/system/containerd.service /usr/lib/systemd/system/ systemctl enable containerd systemctl start containerd |
---|---|
nerdctl
是一个 containerd 的命令行工具,但是它的容器、镜像与 Kubernetes 的容器、镜像是完全隔离的,不能互通!
目前只能通过 crictl
来查看、拉取 Kubernetes 的容器、镜像,下一节会介绍 crictl 的安装。
3. 安装 kubelet/kubeadm/kubectl
试用 crictl:
1 2 3 4 5 6 |
export CONTAINER_RUNTIME_ENDPOINT='unix:///var/run/containerd/containerd.sock' # 列出所有 pods,现在应该啥也没 crictl pods # 列出所有镜像 crictl images |
---|---|
4. 为 master 的 kube-apiserver 创建负载均衡实现高可用
根据 kubeadm 官方文档 Kubeadm Docs - High Availability Considerations 介绍,要实现 kube-apiserver 的高可用,目前最知名的负载均衡方式是 keepalived+haproxy,另外也可以考虑使用 kube-vip 等更简单的工具。
简单起见,我们直接用 kube-vip 吧,参考了 kube-vip 的官方文档:Kube-vip as a Static Pod with Kubelet.
P.S. 我也见过有的安装工具会直接抛弃 keepalived,直接在每个节点上跑一个 nginx 做负载均衡,配置里写死了所有 master 的地址…
首先使用如下命令生成 kube-vip 的配置文件,以 ARP 为例(生产环境建议换成 BGP):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| cat <<EOF | sudo tee add-kube-vip.sh # 你的虚拟机网卡,opensuse/centos 等都是 eth0,但是 ubuntu 可能是 ens3 export INTERFACE=eth0 # 用于实现高可用的 vip,需要和前面的网络接口在同一网段内,否则就无法路由了。 export VIP=192.168.122.200 # 生成 static-pod 的配置文件 mkdir -p /etc/kubernetes/manifests nerdctl run --rm --network=host --entrypoint=/kube-vip ghcr.io/kube-vip/kube-vip:v0.3.8 \ manifest pod \ --interface $INTERFACE \ --vip $VIP \ --controlplane \ --services \ --arp \ --leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml EOF bash add-kube-vip.sh
|
| —————————————————— | ———————————————————— |
| | |
三个 master 节点都需要跑下上面的命令(worker 不需要),创建好 kube-vip 的 static-pod 配置文件。 在完成 kubeadm 初始化后,kubelet 会自动把它们拉起为 static pod.
5. 使用 kubeadm 创建集群
其实需要运行的就是这条命令:
kubeadm 应该会报错,提示你有些依赖不存在,下面先安装好依赖项。
1 |
sudo zypper in -y socat ebtables conntrack-tools |
---|---|
再重新运行前面的 kubeadm 命令,应该就能正常执行了,它做的操作有:
- 拉取控制面的容器镜像
- 生成 ca 根证书
- 使用根证书为 etcd/apiserver 等一票工具生成 tls 证书
- 为控制面的各个组件生成 kubeconfig 配置
- 生成 static pod 配置,kubelet 会根据这些配置自动拉起 kube-proxy 以及其他所有的 k8s master 组件
运行完会给出三部分命令:
将
kubeconfig
放到$HOME/.kube/config
下,kubectl
需要使用该配置文件连接 kube-apiservercontrol-plane 节点加入集群的命令:
- 这里由于我们提前添加了 kube-vip 的 static-pod 配置,这里的 preflight-check 会报错,需要添加此参数忽略该报错 -
--ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests
1 2 3 4
kubeadm join 192.168.122.200:6443 --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane --certificate-key <key> \ --ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests
- 这里由于我们提前添加了 kube-vip 的 static-pod 配置,这里的 preflight-check 会报错,需要添加此参数忽略该报错 -
worker 节点加入集群的命令:
1 2
kubeadm join 192.168.122.200:6443 --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>
跑完第一部分 kubeconfig
的处理命令后,就可以使用 kubectl 查看集群状况了:
1 2 3 4 5 6 7 8 9 10 11 12 |
k8s-master-0:~/kubeadm # kubectl get no NAME STATUS ROLES AGE VERSION k8s-master-0 NotReady control-plane,master 79s v1.22.1 k8s-master-0:~/kubeadm # kubectl get po --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-78fcd69978-6tlnw 0/1 Pending 0 83s kube-system coredns-78fcd69978-hxtvs 0/1 Pending 0 83s kube-system etcd-k8s-master-0 1/1 Running 6 90s kube-system kube-apiserver-k8s-master-0 1/1 Running 4 90s kube-system kube-controller-manager-k8s-master-0 1/1 Running 4 90s kube-system kube-proxy-6w2bx 1/1 Running 0 83s kube-system kube-scheduler-k8s-master-0 1/1 Running 7 97s |
---|---|
现在在其他节点运行前面打印出的加入集群的命令,就可以搭建好一个高可用的集群了。
所有节点都加入集群后,通过 kubectl 查看,应该是三个控制面 master,两个 worker:
1 2 3 4 5 6 7 |
k8s-master-0:~/kubeadm # kubectl get node NAME STATUS ROLES AGE VERSION k8s-master-0 NotReady control-plane,master 26m v1.22.1 k8s-master-1 NotReady control-plane,master 7m2s v1.22.1 k8s-master-2 NotReady control-plane,master 2m10s v1.22.1 k8s-worker-0 NotReady <none> 97s v1.22.1 k8s-worker-1 NotReady <none> 86s v1.22.1 |
---|---|
现在它们都还处于 NotReady 状态,需要等到我们把网络插件安装好,才会 Ready.
现在再看下集群的证书签发状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
❯ kubectl get csr --sort-by='{.spec.username}' NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION csr-95hll 6m58s kubernetes.io/kube-apiserver-client-kubelet system:bootstrap:q8ivnz <none> Approved,Issued csr-tklnr 7m5s kubernetes.io/kube-apiserver-client-kubelet system:bootstrap:q8ivnz <none> Approved,Issued csr-w92jv 9m15s kubernetes.io/kube-apiserver-client-kubelet system:bootstrap:q8ivnz <none> Approved,Issued csr-rv7sj 8m11s kubernetes.io/kube-apiserver-client-kubelet system:bootstrap:q8ivnz <none> Approved,Issued csr-nxkgx 10m kubernetes.io/kube-apiserver-client-kubelet system:node:k8s-master-0 <none> Approved,Issued csr-cd22c 10m kubernetes.io/kubelet-serving system:node:k8s-master-0 <none> Pending csr-wjrnr 9m53s kubernetes.io/kubelet-serving system:node:k8s-master-0 <none> Pending csr-sjq42 9m8s kubernetes.io/kubelet-serving system:node:k8s-master-1 <none> Pending csr-xtv8f 8m56s kubernetes.io/kubelet-serving system:node:k8s-master-1 <none> Pending csr-f2dsf 8m3s kubernetes.io/kubelet-serving system:node:k8s-master-2 <none> Pending csr-xl8dg 6m58s kubernetes.io/kubelet-serving system:node:k8s-worker-0 <none> Pending csr-p9g24 6m52s kubernetes.io/kubelet-serving system:node:k8s-worker-1 <none> Pending |
---|---|
能看到有好几个 kubernetes.io/kubelet-serving
的证书还处于 pending 状态, 这是因为我们在 kubeadm 配置文件中,设置了 serverTLSBootstrap: true
,让 Kubelet 从集群中申请 CA 签名证书,而不是自签名导致的。
设置这个参数的主要目的,是为了让 metrics-server 等组件能使用 https 协议与 kubelet 通信,避免为 metrics-server 添加参数 --kubelet-insecure-tls
.
目前 kubeadm 不支持自动批准 kubelet 申请的证书,需要我们手动批准一下:
1 2 |
# 批准 Kubelet 申请的所有证书 kubectl certificate approve csr-cd22c csr-wjrnr csr-sjq42 csr-xtv8f csr-f2dsf csr-xl8dg csr-p9g24 |
---|---|
在未批准这些证书之前,所有需要调用 kubelet api 的功能都将无法使用,比如:
- 查看 pod 日志
- 获取节点 metrics
- 等等
5.1 常见问题
5.1.1 使用国内镜像源
如果你没有科学环境,kubeadm 默认的镜像仓库在国内是拉不了的。 如果对可靠性要求高,最好是自建私有镜像仓库,把镜像推送到私有仓库。
可以通过如下命令列出所有需要用到的镜像地址:
1 2 3 4 5 6 7 8 |
❯ kubeadm config images list --kubernetes-version v1.22.1 k8s.gcr.io/kube-apiserver:v1.22.1 k8s.gcr.io/kube-controller-manager:v1.22.1 k8s.gcr.io/kube-scheduler:v1.22.1 k8s.gcr.io/kube-proxy:v1.22.1 k8s.gcr.io/pause:3.5 k8s.gcr.io/etcd:3.5.0-0 k8s.gcr.io/coredns/coredns:v1.8.4 |
---|---|
使用 skopeo
等工具或脚本将上述镜像拷贝到你的私有仓库,或者图方便(测试环境)也可以考虑网上找找别人同步好的镜像地址。将镜像地址添加到 kubeadm-config.yaml
中再部署。
5.1.2 重置集群配置
创建集群的过程中出现任何问题,都可以通过在所有节点上运行 kubeadm reset
来还原配置,然后重新走 kubeadm 的集群创建流程。
但是要注意几点:
kubeadm reset
会清除包含 kube-vip 配置在内的所有 static-pod 配置文件,所以 master 节点需要重新跑下前面给的 kube-vip 命令,生成下 kube-vip 配置。kubeadm reset
不会重置网络接口的配置,master 节点需要手动清理下 kube-vip 添加的 vip:ip addr del 192.168.122.200/32 dev eth0
.- 如果你在安装了网络插件之后希望重装集群,顺序如下:
- 通过
kubectl delete -f xxx.yaml
/helm uninstall
删除所有除网络之外的其他应用配置 - 删除网络插件
- 先重启一遍所有节点,或者手动重置所有节点的网络配置
- 建议重启,因为我不知道该怎么手动重置… 试了
systemctl restart network
并不会清理所有虚拟网络接口。
- 建议重启,因为我不知道该怎么手动重置… 试了
- 通过
如此操作后,再重新执行集群安装,应该就没啥毛病了。
6. 验证集群的高可用性
虽然网络插件还没装导致集群所有节点都还没 ready,但是我们已经可以通过 kubectl 命令来简单验证集群的高可用性了。
首先,我们将前面放置在 k8s-master-0 的认证文件 $HOME/.kube/config
以及 kunbectl 安装在另一台机器上,比如我直接放我的宿主机。
然后在宿主机上跑 kubectl get node
命令验证集群的高可用性:
- 三个主节点都正常运行时,kubectl 命令也正常
- pause 或者 stop 其中一个 master,kubectl 命令仍然能正常运行
- 再 pause 第二个 master,kubectl 命令应该就会卡住,并且超时,无法使用了
- resume 恢复停掉的两个 master 之一,会发现 kubectl 命令又能正常运行了
到这里 kubeadm 的工作就完成了,接下来再安装网络插件,集群就可用了。
7. 安装网络插件
社区有很多种网络插件可选,比较知名且性能也不错的,应该是 Calico 和 Cilium,其中 Cilium 主打基于 eBPF 的高性能与高可观测性。
下面分别介绍这两个插件的安装方法。(注意只能安装其中一个网络插件,不能重复安装。)
需要提前在本机安装好 helm,我这里使用宿主机,因此只需要在宿主机安装:
| 1 2 3 4 5
| # 一行命令安装,也可以自己手动下载安装包,都行 curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash # 或者 opensuse 直接用包管理器安装 sudo zypper in helm
|
| ———— | ———————————————————— |
| | |
7.1 安装 Cilium
官方文档:https://docs.cilium.io/en/v1.10/gettingstarted/k8s-install-kubeadm/
cilium 通过 eBPF 提供了高性能与高可观测的 k8s 集群网络, 另外 cilium 还提供了比 kube-proxy 更高效的实现,可以完全替代 kube-proxy.
这里我们还是先使用 kube-proxy 模式,先熟悉下 cilium 的使用:
| 1 2 3 4
| helm repo add cilium https://helm.cilium.io/ helm search repo cilium/cilium -l | head helm install cilium cilium/cilium --version 1.10.4 --namespace kube-system
|
| ———- | ———————————————————— |
| | |
可以通过 kubectl get pod -A
查看 cilium 的安装进度,当所有 pod 都 ready 后,集群就 ready 了~
cilium 也提供了专用的客户端:
1 2 3 4 |
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz{,.sha256sum} sha256sum --check cilium-linux-amd64.tar.gz.sha256sum sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin rm cilium-linux-amd64.tar.gz{,.sha256sum} |
---|---|
然后使用 cilium 客户端检查网络插件的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ cilium status --wait /¯¯\ /¯¯\__/¯¯\ Cilium: OK \__/¯¯\__/ Operator: OK /¯¯\__/¯¯\ Hubble: disabled \__/¯¯\__/ ClusterMesh: disabled \__/ DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5 Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2 Containers: cilium Running: 5 cilium-operator Running: 2 Cluster Pods: 2/2 managed by Cilium Image versions cilium quay.io/cilium/cilium:v1.10.4@sha256:7d354052ccf2a7445101d78cebd14444c7c40129ce7889f2f04b89374dbf8a1d: 5 cilium-operator quay.io/cilium/operator-generic:v1.10.4@sha256:c49a14e34634ff1a494c84b718641f27267fb3a0291ce3d74352b44f8a8d2f93: 2 |
---|---|
cilium 还提供了命令,自动创建 pod 进行集群网络的连接性测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
❯ cilium connectivity test ℹ️ Monitor aggregation detected, will skip some flow validation steps ✨ [kubernetes] Creating namespace for connectivity check... ✨ [kubernetes] Deploying echo-same-node service... ✨ [kubernetes] Deploying same-node deployment... ✨ [kubernetes] Deploying client deployment... ✨ [kubernetes] Deploying client2 deployment... ✨ [kubernetes] Deploying echo-other-node service... ✨ [kubernetes] Deploying other-node deployment... ... ℹ️ Expose Relay locally with: cilium hubble enable cilium status --wait cilium hubble port-forward& 🏃 Running tests... ... --------------------------------------------------------------------------------------------------------------------- ✅ All 11 tests (134 actions) successful, 0 tests skipped, 0 scenarios skipped. |
---|---|
通过 kubectl get po -A
能观察到,这个测试命令会自动创建一个 cilium-test
名字空间,并在启动创建若干 pod 进行详细的测试。
整个测试流程大概会持续 5 分多钟,测试完成后,相关 Pod 不会自动删除,使用如下命令手动删除:
1 |
kubectl delete namespace cilium-test |
---|---|
7.2 安装 Calico
官方文档:https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises
也就两三行命令。安装确实特别简单,懒得介绍了,看官方文档吧。
但是实际上 calico 的细节还蛮多的,建议通读下它的官方文档,了解下 calico 的架构。
8. 查看集群状态
官方的 dashboard 个人感觉不太好用,建议直接在本地装个 k9s 用,特别爽。
1 |
sudo zypper in k9s |
---|---|
然后就可以愉快地玩耍了。
9. 安装 metrics-server
这一步可能遇到的问题:Enabling signed kubelet serving certificates
如果需要使用 HPA 以及简单的集群监控,那么 metrics-server 是必须安装的,现在我们安装一下它。
首先,跑 kubectl 的监控命令应该会报错:
1 2 |
❯ kubectl top node error: Metrics API not available |
---|---|
k9s 里面应该也看不到任何监控指标。
现在通过 helm 安装它:
| 1 2 3 4
| helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/ helm search repo metrics-server/metrics-server -l | head helm upgrade --install metrics-server metrics-server/metrics-server --version 3.5.0 --namespace kube-system
|
| ———- | ———————————————————— |
| | |
metrics-server 默认只会部署一个实例,如果希望高可用,请参考官方配置:metrics-server - high-availability manifests
等 metrics-server 启动好后,就可以使用 kubectl top
命令啦:
10. 为 etcd 添加定期备份能力
请移步 [etcd 的备份与恢复](https://github.com/ryan4yin/knowledge/blob/master/datastore/etcd/etcd 的备份与恢复.md)
11. 安装 Volume Provisioner
在我们学习使用 Prometheus/MinIO/Tekton 等有状态应用时,它们默认情况下会通过 PVC 声明需要的数据卷。
为了支持这个能力,我们需要在集群中部署一个 Volume Provisioner.
对于云上环境,直接接入云服务商提供的 Volume Provisioner 就 OK 了,方便省事而且足够可靠。
而对于 bare-metal 环境,比较有名的应该是 rook-ceph,但是这个玩意部署复杂,维护难度又高,不适合用来测试学习,也不适合生产环境。
对于开发、测试环境,或者个人集群,建议使用:
- local 数据卷,适合数据可丢失,且不要求分布式的场景,如开发测试环境
- NFS 数据卷,适合数据可丢失,对性能要求不高,并且要求分布式的场景。比如开发测试环境、或者线上没啥压力的应用
- https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
- https://github.com/kubernetes-csi/csi-driver-nfs
- NFS 数据的可靠性依赖于外部 NFS 服务器,企业通常使用群晖等 NAS 来做 NFS 服务器
- 如果外部 NFS 服务器出问题,应用就会崩。
- 直接使用云上的对象存储,适合希望数据不丢失、对性能要求不高的场景。
- 直接使用 https://github.com/rclone/rclone mount 模式来保存数据,或者直接同步文件夹数据到云端(可能会有一定数据丢失)。