vlambda博客
学习文章列表

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

K8S中文社区
国内 Kubernetes 技术爱好者聚集地,内容起于K8S而不止于K8S,涉及Docker、微服务、ServiceMesh、DevOps、虚拟化等云计算及云原生相关开源项目,分享技术、经验、资讯,坚持干货。
60篇原创内容
Official Account

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

校对:星空下的文仔、bot,来源:K8sMeetup社区

大家是否曾经考虑过更改 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 。其源代码可在此处获得: https://github.com/flant/examples/tree/master/2020/04-etcdhelper
新功能有什么作用?这是  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  用于转储数据:

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

2.构建  etcdhelper  工具:
  • 安装  golang

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

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

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

3.备份 etcd 数据:

不使用 K8s API,如何直接修改 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 地址:

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

5.2.准备  kubeadm  基本配置:

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

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

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

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

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

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

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

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

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

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

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

5.8.编辑 etcd 中的数据:

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

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

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

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

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

将  data.ClusterConfiguration.networking.serviceSubnet  参数切换到新的子网。
5.10.由于 kube-dns 地址已更改,因此需要在所有节点上更新 kubelet 配置:

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

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

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

减少停机时间
以下是有关如何最大程度地减少停机时间的一些想法:
  • 编辑控制平面清单后,我们可以使用新名称(例如 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:

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

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

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

指定新的  data.config.conf.clusterCIDR: 10.55.0.0/16
2.编辑 controller-manager 清单:

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

指定:--cluster-cidr=10.55.0.0/16。
3.验证所有集群节点的  .spec.podCIDR .spec.podCIDRs .InternalIP .status.addresses  当前值:

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

4.直接编辑 etcd 替换  podCIDR

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

5.检查  podCIDR  是否已更改:

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

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

原文链接:https://medium.com/flant-com/modifying-kubernetes-etcd-data-ed3d4bb42379

END