七叶笔记 » golang编程 » 不使用 K8s API,如何直接修改 etcd 数据?

不使用 K8s API,如何直接修改 etcd 数据?

本文探讨了不使用 Kubernetes api ,直接在 etcd 中处理数据的可能性,并在真实的 K8s 集群上成功测试了所有步骤。

大家是否曾经考虑过更改 Kubernetes 集群的 etcd 数据的“低级”方法?就是说在不使用任何通用的 Kubernetes 工具(例如 CLI 程序甚至 API)就更改 etcd 存储的数据。

一切从这开始

越来越多的客户(大多是开发人员)要求提供对 Kubernetes 集群的访问权限,以便与内部服务进行交互。他们希望能够直接连接到数据库或服务,将本地应用程序连接到集群中的其他应用程序等等。

例如,从本地计算机连接到 memcached .staging.svc. Cluster .local service。我们可以通过客户端连接的集群内部 VPN 来实现。我们公开与 Pod 和 service 相关的 子网 ,并将集群的 DNS 推送到客户端。结果,当客户端尝试连接到 memcached.staging.svc.cluster.local service 时,请求转到了集群的 DNS,它从集群的服务网络或 Pod 的地址返回该 service 的地址。

我们使用 kubeadm 配置 K8s 集群。在这种情况下,默认 service subnet(子网)是 192.168.0.0/16 ,而 Pod 子网是 10.244.0.0/16 。此方法大部分时候效果很好,但是也有几点值得注意:

  • 192.168.*.* 子网往往是在客户办公室中使用,甚至是开发商的家庭办公室。这就出现了问题:家用路由器会使用相同的地址空间,VPN 会将这些子网从集群推送到客户端。
  • 有几个集群(production、stage、多个 dev 集群)的情况下,它们将默认全部具有与 Pod 和服务的相同子网,这使得同时使用多个集群中的 services 非常困难。

我们在同一项目中使用了不同的子网来实现同一项目中的不同 service 和 Pod。在这种情况下,任何集群都有其自己的网络。在维护大量 K8s 集群时,我们不希望它们从头开始重新部署,因为会有许多正在运行的 service、有状态的应用程序等。这时候就出现了一个问题: 我们如何更改现有集群中的子网?

解决方案

最常见的方法是重新创建所有 ClusterIP 类型的 service。不过也有问题:

以下过程存在问题:配置完所有内容后,Pod 在 /etc/resolv.conf 中将旧 IP 用作 DNS nameserver。

由于仍然找不到解决方案,因此不得不使用 kubeadm reset 重置整个集群,再次将其初始化。

这并不适合所有场景,这里是案例更详细的情况:

  • 使用 Flannel。
  • 既有裸机集群,也有 Kubernetes 云集群。
  • 希望避免重新部署集群中的所有 service。
  • 希望尽可能轻松地进行过渡。
  • 该集群由 Kubernetes v1.16.6管理(但也适用于其他版本)。
  • 目标是在使用 kubeadm 在部署的集群中将 192.168.0.0/16 service subnet 替换为 172.24.0.0/16

实际上,很多人一直想研究 Kubernetes 是如何将其数据存储在 etcd 中以及该存储可以做什么。那我们为什么不通过替换旧的来更新 etcd 中的数据子网 IP 与新 IP ?

最好有现成的工具来修改 etcd 中的数据,虽然目前没有任何工具可以很好地满足我们的需求,但 OpenShift 的 etcdhelper 是一个很好的起点。这个工具可以使用证书连接 etcd,并能用 ls get dump 命令阅读 etcd 数据。

扩展 etcdhelper

因此,我们准备扩展 etcdhelper,以便将数据写入 etcd。我们创建了具有两个新功能的 etcdhelper 的更新版本: changeServiceCIDR changePodCIDR 。其源代码可在此处获得:

新功能有什么作用?这是 changeServiceCIDR 算法:

  • 创建一个反序列化器(deserializer)。
  • 编译正则表达式以替换 CIDR。
  • 遍历集群中的 ClusterIP service 列表,并对每个 service 执行操作。

这是我们的操作:

  • 解码 etcd 值并将其放置在 Go 对象中。
  • 使用正则表达式替换地址的前两个字节。
  • 从新子网的地址范围为 service 分配 IP 地址。
  • 创建一个序列化器,将 Go 对象转换为 protobuf,将新数据写入 etcd。

changePodCIDR 功能与 changeServiceCIDR 基本上相同,唯一的区别是我们编辑节点的规范时,用新的子网替换了 .spec.PodCIDR 的值,而不是 service。

用法

更换 serviceCIDR

这事很容易实现,但会导致停机,并且集群中的所有 Pod 都将被重新创建。这里我们先展示主要步骤,然后分享如何最大程度地减少停机时间。

准备步骤:

  • 安装必要的软件并构建补丁 etcdhelper 工具;
  • 备份 etcd 和 /etc/kubernetes

以下是更改 serviceCIDR 的操作摘要:

  • 更改 apiserver 和控制器管理器清单。
  • 重新颁发证书。
  • 修改 etcd 中 services 的 ClusterIP 规范。
  • 重新启动集群中的所有 Pod。

以下是这些步骤的详细说明:

1.安装 etcd-client 用于转储数据:

2.构建 etcdhelper 工具:

  • 安装 golang

  • 复制 etcdhelper.go ,下载依赖项,构建工具:

3.备份 etcd 数据:

4.在 Kubernetes 控制平面清单中切换 service subnet。在 /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-controller-manager.yaml 中替换 –service-cluster-ip-range 值与新子网( 172.24.0.0/16 替代 192.168.0.0/16 )。

5.由于我们在对 kubeadm 颁发 apiserver 证书(以及其他证书)的 service subnet 进行更改,因此我们需要重新颁发:

5.1.检查当前证书颁发的域和 IP 地址:

5.2.准备 kubeadm 基本配置:

5.3.删除 crt key 旧文件(删除它们才能颁发新证书):

5.4.重新颁发 API 服务器的证书:

5.5.检查是否为新子网颁发了证书:

5.6.重新颁发 API 服务器证书后,重启其容器:

5.7.续订嵌入 admin.conf 的证书 :

5.8.编辑 etcd 中的数据:

此时 DNS 停止解析集群中的域名。这是因为现有的 Pod 在 /etc/resolv.conf 中仍具有旧的 CoreDNS(kube-dns) 地址,而 kube-proxy 已经使用新子网,并更改了 iptables 规则。

5.9.在 kube-system 命名空间中编辑 ConfigMap:

在该 CM 中:

替换 ClusterDNS 为 kube-dns service 的新 IP 地址: kubectl -n kube-system get svc kube-dns

在该 CM 中:

data.ClusterConfiguration.networking.serviceSubnet 参数切换到新的子网。

5.10.由于 kube-dns 地址已更改,因此需要在所有节点上更新 kubelet 配置:

5.11.现在该重启集群中的所有 Pod 了:

减少停机时间

以下是有关如何最大程度地减少停机时间的一些想法:

  • 编辑控制平面清单后,我们可以使用新名称(例如 kube-dns-tmp)和新地址(172.24.0.10)创建新的 kube-dns service。
  • 我们在 etcdhelper 中插入 if 条件,这能防止修改 kube-dns service。
  • 将所有 kubelet 中的旧 ClusterDNS 地址替换为新的(旧 service 将继续与新 service 同时运行)。
  • 等所有应用程序的 Pod 都在约定的时间重新部署。
  • 删除 kube-dns-tmp service 并编辑 serviceSubnetCIDRkube-dns service。

该策略可以将停机时间缩短到大约一分钟:删除 kube-dns-tmp service 和切换 kube-dns service 的子网所需时间。

修改 podNetwork

在此过程中,我们使用 etcdhelper 来修改 podNetwork。这是操作顺序:

  • kube-system 命名空间中编辑配置。
  • 编辑 kube-controller-manager 的清单。
  • 直接在 etcd 中编辑 podCIDR。
  • 重新启动集群中的所有节点。

以下是上述操作的详细说明:

1.在 kube-system 命名空间中编辑 ConfigMap:

替换 data.ClusterConfiguration.networking.podSubnet 为新的子网( 10.55.0.0/16 )。

指定新的 data.config.conf.clusterCIDR: 10.55.0.0/16

2.编辑 controller-manager 清单:

指定:–cluster-cidr=10.55.0.0/16。

3.验证所有集群节点的 .spec.podCIDR .spec.podCIDRs .InternalIP .status.addresses 当前值:

4.直接编辑 etcd 替换 podCIDR

5.检查 podCIDR 是否已更改:

6.一次重新启动集群的所有节点。

7.如果至少有一个带有旧 podCIDR 的节点,kube-controller-manager 将不会启动,集群中的 Pod 也不会被调度。

原文链接:

相关文章