使用 kubeadm 安装 k8s 集群

使用 kubeadm 安装 k8s 集群

本次实践所使用的集群的组件:

  • cilium —— 一个 CNI 组件。网络连通性的功能很普通,默认使用 vxlan 实现,也可以自行配置 bgp,甚至可以利用 CNI 的链把通网的功能交给 calico 等其他 CNI 来实现。其亮点在与基于 ebpf 实现的网络策略和链路跟踪功能,同时也可以使用 ebpf 实现 k8s 的 service vip 功能,从而替换 kube-proxy。cilium 使用的 epbf 依赖 新版 linux 内核

由于 linux 内核 nf_conntrack 功能的问题,导致 iptables 和 ipvs 对 service 的 vip 进行 nat 时,将并发的、且源 ip、源端口、目标 ip、目标端口相同的两个 udp 协议呀请求识别为一个“连接”,于是在目标返回第一个响应时,认为这个“连接”已经结束,从而抛弃掉第二个响应。导致请求方等待响应超时重试的问题。

由于这个问题在新版本的内核中也没有完全修复,因此决定抛弃 kube-proxy,改为完全实现了 kube-proxy 所有功能的 CNI 组件——cilium。

RKE 虽然支持自定义 CNI 组件,但并不支持跳过 kube-proxy 的安装。而且我们在项目中的其他需求,也因为 RKE 对集群的傻瓜式管理造成了自定义集群的困难。因此,我们决定抛弃 RKE,使用 GKE。

  • 由于 cilium 的 ebpf 功能依赖新版本的 linux 内核。官方推荐内核 5.3+,因此我们直接使用 elrepo 上最新版的 ml 内核。

操作系统、节点准备、Docker 环境、nexus3 仓库配置与 《rancher 高可用 + 离线部署》 中类似,细节略有不同,本实践不再详细说明。

节点规划

192.168.1.146   etcd, controlplane
192.168.1.147   etcd, controlplane
192.168.1.148   etcd, controlplane
192.168.1.149   worker
192.168.1.150   worker
192.168.1.151   worker

controlplane 节点也被成为 master。上面运行着以下重要部件:

  • etcd 分布式数据库。k8s 集群所有状态和数据均保存在这里。与 zookeeper 的原理类似,3 个节点即可容许 1 个节点失效。etcd 只与 apiserver 进行交互。每个 apiserver 直接访问与它同在一台机器上的 etcd 即可。
  • kube-apiserver 统一 api 接口。k8s 的所有组件均与 apiserver 交互。
  • kube-scheduler 调度器。决定如何分配资源的组件。
  • kube-controller 控制器。决定如何实现资源的组件。

外部负载均衡器

在集群中,etcd 本身组成高可用集群后,每个 etcd 实例只被所在节点上的 apiserver 访问。因此不需要负载均衡器。scheduler 调度器和 controller 不会被主动访问,因此也不需要负载均衡器。只有 apiserver 作为用户和各个组件与集群交互的入口,需要一个负载均衡器来保证 apiserver 的高可用。

在集群内的 default 命名空间下,有一个名为 kubernetes 的 service 服务即为集群内 apiserver 的负载均衡器。而针对 apiserver 在集群外的访问,需要一个外部的负载均衡器。apiserver 虽然是基于 http 的 json api,但是鉴权使用的是 tls 双向证书。因此,你需要一个 4 层的负载均衡器,为 apiserver 进行高可用配置。无论你使用 haproxy、lvs 还是 F5、nginx stream,你都只需要将一个负载均衡器的统一入口,即 域名/ip + 端口 转发至所有 apiserver 的端口即可。

没有外部负载均衡器时的变通

本实践中,我们将会使 apiserver 监听 6444 端口,同时指定 apiserver 的统一入口为 127.0.0.1:6443。在集群所有节点上手动启动一个 nginx 容器,将 127.0.0.1:6443 转发至所有 apiserver 的 6444 端口。

将如下所示的 nginx 配置保存为:/opt/kube-api-proxy.conf

error_log stderr notice;
worker_processes auto;
events {
    multi_accept on;
    use epoll;
    worker_connections 1024;
}
stream {
    upstream kube_apiserver {
        server 192.168.1.146:6444;
        server 192.168.1.147:6444;
        server 192.168.1.148:6444;
    }
    server {
        listen 6443;
        proxy_pass kube_apiserver;
        proxy_timeout 30;
        proxy_connect_timeout 2s;
    }
}

运行一个始终启动的 nginx 容器:

docker run -d --restart=always --network=host \
    --name nginx-proxy \
    -v /opt/kube-api-proxy.conf:/etc/nginx/nginx.conf \
    nginx:1.17.10-alpine

这样在集群所有节点上启动这个 nginx 容器后,我们就可以愉快的开始折腾 kubeadm 了。

安装 controlplane

我们规划了三台节点作为 controlane。我们只用在一台节点上设置好集群的配置文件,安装好这台节点后,其他节点依据节点角色直接加入集群即可。

初始化第一个 controlplane

首先我们为集群生成默认配置:

kubeadm config print init-defaults > kubeadm.init.yaml

接着修改以下字段:

apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
bootstrapTokens:
- groups:
  token: foobarfoobarfoobar # 修改加入集群所使用的 token
localAPIEndpoint:
  advertiseAddress: 192.168.1.146 # 这个节点上的 apiserver 监听的 ip
  bindPort: 6444 # apiserver 监听的端口
nodeRegistration:
  name: 192.168.1.146 # 这个节点的名字
---
kind: ClusterConfiguration
clusterName: originsteam # 集群名字
etcd:
  local:
    dataDir: /opt/etcd # etcd 使用的数据目录。etcd 对磁盘 io 要求较高。
imageRepository: google_containers # docker 镜像的路径。本实践使用的 nexus3 代理了 https://registry.aliyuncs.com 仓库。
controlPlaneEndpoint: "127.0.0.1:6443" # apiserver 的外部负载均衡入口
networking:
  dnsDomain: cluster.local # 集群内部 dns 使用的域
  serviceSubnet: 10.43.0.0/16 # 集群 server 使用的 cicr
  podSubnet: 10.44.0.0/16 # 集群 pod 使用的 cicr

之后我们就可以启动第一个节点了:

kubeadm init \
    --config kubeadm.init.yaml \
    --upload-certs \
    --skip-phases=addon/kube-proxy

由于我们需要使用 cilium 替换 kube-proxy,因此我们使用 --skip-phases=addon/kube-proxy 跳过了 kube-proxy 的安装。

如果安装失败,你可以使用 kubeadm revert 命令重置这台机器上 kubeadm 的操作,然后重新安装。

安装成功后,命令会输出一大堆内容,告诉你其他节点加入集群的方法。我们只需要记住里面的两个参数,并替换到下面的命令中:

  • --discovery-token-ca-cert-hash sha256:fobarfoobarfoobarbar 集群 ca 指纹
  • --certificate-key barfoobarfoobarfoofoo 新增 controplane 节点需要的私钥指纹

token 是你在 kubeadm.init.yaml 里面设置的。

安装其他 controlplane 节点

在其他 controlplane 节点上运行,注意替换命令中的参数:

kubeadm join \
    127.0.0.1:6443 \
    --token foobarfoobarfoobar \
    --discovery-token-ca-cert-hash sha256:fobarfoobarfoobarbar \
    --control-plane \
    --certificate-key barfoobarfoobarfoofoo \
    --apiserver-advertise-address 192.168.1.148 \ # 替换为节点 ip
    --node-name 192.168.1.148 \ # 替换为节点 ip
    --apiserver-bind-port 6444

如果安装失败,你可以使用 kubeadm revert 命令重置这台机器上 kubeadm 的操作,然后重新安装。

安装 CNI 组件

在线安装:

helm repo add cilium https://helm.cilium.io/

helm install cilium cilium/cilium --version 1.7.3 \
    --namespace kube-system \
    --set global.kubeProxyReplacement=strict \
    --set global.registry=cilium \
    --set global.k8sServiceHost=127.0.0.1 \
    --set global.k8sServicePort=6443

离线安装

可在有 helm 的机器上渲染好 yaml:

helm fetch cilium/cilium --version 1.7.3

helm template cilium ./cilium-1.7.3.tgz \
    --output-dir ./ \
    --namespace kube-system \
    --set global.kubeProxyReplacement=strict \
    --set global.registry=cilium \
    --set global.k8sServiceHost=127.0.0.1 \
    --set global.k8sServicePort=6443

然后将渲染好的 cilium 目录打包至集群安装:

kubectl apply -R -f cilium

加入 worker 节点

kubeadm join \
    127.0.0.1:6443 \
    --token foobarfoobarfoobar \
    --discovery-token-ca-cert-hash sha256:fobarfoobarfoobarbar \
    --node-name 192.168.1.149

如果安装失败,你可以使用 kubeadm revert 命令重置这台机器上 kubeadm 的操作,然后重新安装。

metrics-server

可在有 helm 的机器上渲染好 yaml:

helm fetch stable/metrics-server

helm template metrics-server ./metrics-server-2.11.1.tgz \
    --output-dir . \
    --namespace kube-system \
    --set image.repository=google_containers/metrics-server-amd64 \
    --set args='{"--kubelet-insecure-tls"}'

然后将渲染好的 cilium 目录打包至集群安装:

kubectl apply -R -f metrics-server

ingress-nginx

可在有 helm 的机器上渲染好 yaml:

helm fetch ingress-nginx/ingress-nginx
helm template ingress-nginx ./ingress-nginx-2.1.0.tgz \
    --output-dir . \
    --namespace ingress-nginx \
    --set controller.image.repository=kubernetes-ingress-controller/nginx-ingress-controller \
    --set controller.dnsPolicy=ClusterFirstWithHostNet \
    --set controller.hostNetwork=true \
    --set controller.kind=DaemonSet \
    --set controller.service.enabled=false \
    --set controller.reportNodeInternalIp=true \
    --set controller.admissionWebhooks.enabled=false

然后将渲染好的 cilium 目录打包至集群安装:

kubectl create namespace ingress-nginx
kubectl apply -n ingress-nginx -R -f ingress-nginx

hubble

Network, Service & Security Observability for Kubernetes

hubble 是 cilium 的 web ui,用于观测 cilium 对集群的流量监测。

hubble 目前没有 helm repo,所以直接下载 git 仓库后,在 install/kubernetes 下渲染。

helm template hubble-ui hubble \
    --output-dir ../ \
    --namespace kube-system \
    --set metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,http}" \
    --set ui.enabled=true \
    --set image.repository=cilium/hubble \
    --set ui.image.repository=cilium/hubble-ui \
#   如果不想手动配置 ingress,可以添加以下配置
#    --set ingress.enabled=true \
#    --set ingress.hosts="{hubble.foobar.com}" \

渲染完成后即可将 install/hubble 目录打包至集群安装:

kubectl apply -R -f .

图形化管理

在初始化第一个 controplane 节点后,你就可以直接使用 Lens 这个本地图形化客户端、k8s IDE 来观察和管理集群了。当然,你也可以安装 rancher 来基于 web GUI 来管理,你可以参考 《rancher 高可用 + 离线部署》的部分内容来为 GKE 集群安装 rancher。

离线安装的镜像准备

外部负载均衡器

来自 dockerhubnginx:1.17.10-alpine

GKE

配置好 kubeadm.init.yaml 后,使用以下命令即可获取所需的镜像:

kubeadm config images list --config kubeadm.init.yaml

来自 registry.aliyuncs.com 仓库的

google_containers/kube-apiserver:v1.18.0
google_containers/kube-controller-manager:v1.18.0
google_containers/kube-scheduler:v1.18.0
google_containers/kube-proxy:v1.18.0
google_containers/pause:3.2
google_containers/etcd:3.4.3-0
google_containers/coredns:1.6.7

cilium

可以在渲染好的 yaml 中找到,来自 dockerhub

cilium/cilium:v1.7.3
cilium/operator:v1.7.3

metrics-server

可以在渲染好的 yaml 中找到,来自 registry.aliyuncs.com 仓库的

google_containers/metrics-server-amd64:v0.3.6

ingress-nginx

可以在渲染好的 yaml 中找到,来自 quay.io 仓库的

kubernetes-ingress-controller/nginx-ingress-controller:0.32.0

hubble

可以在渲染好的 yaml 中找到,来自 quay.io 仓库的

cilium/hubble:v0.5.0
cilium/hubble-ui:latest

备份

集群需要备份的内容有:

  • 第一个 controplane 节点上的 /etc/kubernetes/pki 证书目录
  • etcd 快照
  • 其它持久化数据,比如持久卷。

一般生产环境中,第三点由专门的存储服务设施完成,和集群分离,且带有快照功能。因此,本实践仅讨论前两点的恢复。

证书和私钥

pki 目录中保存着整个集群使用到的所有证书和私钥,除用作根 ca 的证书是 10 年有效期外,其它证书均为 1 年有效期。因此,证书目录的备份不用太频繁,可以选择每个月备份一次即可。

由于 kubeadm 签署的证书,每个 controplane 节点均不完全一样,具体区别在证书使用者的 ip 上。我们只需要选择一个 controlplane 节点,备份上面的 /etc/kubernetes/pki 目录即可。当然,恢复时也需要在这个节点上进行——至少是同样ip的节点上。

etcd

etcd 可以选择每天备份,甚至每小时备份。无论你使用脚本、crontab、或者使用 k8s 自己的定时任务都可以。

etcdctl \
    --cert /etc/kubernetes/pki/etcd/peer.crt \
    --key /etc/kubernetes/pki/etcd/peer.key \
    --cacert /etc/kubernetes/pki/etcd/ca.crt \
    --endpoints https://192.198.1.146:2379 \
    snapshot save etcd-snapshot-save.db

pki 目录和 etcd-snapshot-save.db 文件打包压缩备份即可。

恢复

在所有 controlplane 节点上运以下命令以重置节点环境:

kubeadm reset -f

最好 reboot 一次,这样节点的 iptables、ipvs、网络接口等状态会被清除。

然后选择一台节点进行

恢复备份的 pki 证书到 /etc/kubernetes/pki

cp -r pki /etc/kubernetes/

可以运行以下命令看看证书里面的 ip 和节点 ip 是否对应:

openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | grep kubernetes.default.svc

为 etcd 节点生成数据文件

etcdctl snapshot restore etcd-snapshot-save.db \
    --name=192.168.1.146 \
    --initial-cluster=192.168.1.146=https://192.168.1.146:2380 \
    --initial-advertise-peer-urls=https://192.168.1.146:2380

将生成的数据文件放置在 etcd 数据目录中:

cp -r 192.168.1.146.etcd/member/ /opt/etcd/

准备好安装集群时的 kubeadm.init.yaml,使用 kubeadm 初始化集群:

kubeadm init \
    --config kubeadm.init.yaml \
    --upload-certs \
    --skip-phases=addon/kube-proxy\
    --ignore-preflight-errors=DirAvailable--opt-etcd

这里加入了 --ignore-preflight-errors=DirAvailable--opt-etcd 参数使 kubeadm 忽略了 etcd 数据目录非空的错误,因此 kubeadm 初始化的“新”集群就会依然使用老集群的数据。同时,由于已经提前放置好了 pki 目录,证书不会重新生成,而直接使用了老的证书。

和安装新集群一样,我们只需要记住命令运行结束后输出的两个参数:

  • --discovery-token-ca-cert-hash sha256:fobarfoobarfoobarbar 集群 ca 指纹,和旧集群一样
  • --certificate-key barfoobarfoobarfoofoo 新增 controplane 节点需要的私钥指纹。新的,要记

token 是你在 kubeadm.init.yaml 里面设置的。和旧集群一样。

接着我们将剩余的 controplane 节点加入集群。你需要先删除刚刚恢复的集群中的其它 controplane 节点,然后再运行命令加入集群,注意替换命令中的参数:

kubeadm join \
    127.0.0.1:6443 \
    --token foobarfoobarfoobar \
    --discovery-token-ca-cert-hash sha256:fobarfoobarfoobarbar \
    --control-plane \
    --certificate-key barfoobarfoobarfoofoo \
    --apiserver-advertise-address 192.168.1.148 \ # 替换为节点 ip
    --node-name 192.168.1.148 \ # 替换为节点 ip
    --apiserver-bind-port 6444

将其它 controlplane 节点安装完毕后,集群恢复完成。

你可能感兴趣的:(使用 kubeadm 安装 k8s 集群)