写点什么

​Kubernetes 的演变:从 etcd 到分布式 SQL 的过渡

  • 2023-08-18
    福建
  • 本文字数:5724 字

    阅读完需:约 19 分钟

​Kubernetes的演变:从etcd到分布式SQL的过渡

DevRel 领域专家 Denis Magda 表示,他偶然发现了一篇解释如何用 PostgreSQL 无缝替换 etcd 的文章。该文章指出,Kine 项目作为外部 etcd 端点,可以将 Kubernetes etcd 请求转换为底层关系数据库的 SQL 查询。



受到这种方法的启发,Magda 决定进一步探索 Kine 的潜力,从 etcd 切换到 YugabyteDB。YugabyteDB 是一个基于 PostgreSQL 构建的分布式 SQL 数据库。

一、etcd 有什么问题?


etcd 是 Kubernetes 用来存放所有集群数据的键值库。


在 Kubernetes 集群遇到可扩展性或高可用性(HA)问题之前,它通常不会引起人们的注意。以可扩展和高可用性(HA)的方式管理 etcd 对于大型 Kubernetes 部署来说尤其具有挑战性。


此外,Kubernetes 社区对 etcd 项目的未来开发也有越来越多的担忧。它的社区规模正在缩小,只有少数维护人员有兴趣和能力支持和推进这个项目。


这些问题催生了 Kine,这是一个 etcd API 到 SQL 的转换层。Kine 正式支持 SQLite、PostgreSQL 和 MySQL,这些系统的使用量正在不断增长,并且拥有强大的社区。

二、为什么选择分布式 SQL 数据库?

虽然 PostgreSQL、SQLite 和 MySQL 是 Kubernetes 的理想选择,但它们是为单一服务器部署而设计和优化的。这意味着它们可能会带来一些挑战,特别是对于具有更严格的可扩展性和可用性要求的大型 Kubernetes 部署。


如果开发人员的 Kubernetes 集群要求 RPO(恢复点目标)为零,RTO(恢复时间目标)以秒为单位测量,那么 MySQL 或 PostgreSQL 部署的架构和维护将是一个挑战。如果人们有兴趣深入研究这个话题,可以探索 PostgreSQL 的高可用性选项。


分布式 SQL 数据库作为一个相互连接的节点集群,可以跨多个机架、可用区或区域部署。通过设计,它们具有高可用性和可扩展性,因此可以为 Kubernetes 改进相同的特性。

三、在 YugabyteDB 上启动 Kine


而决定使用 YugabyteDB 作为 Kubernetes 的分布式 SQL 数据库是受到 PostgreSQL 的影响。YugabyteDB 建立在 PostgreSQL 源代码的基础上,在提供自己的分布式存储实现的同时,重用了 PostgreSQL 的上半部分(查询引擎)。


YugabyteDB 和 PostgreSQL 之间的紧密联系允许开发人员为 YugabyteDB 重新设计 PostgreSQL 的 Kine 实现。然而需要继续关注,这不会是一个简单的提升和转移的故事。


现在,将这些想法转化为行动,并在 YugabyteDB 上启动 Kine。为此,使用了一个配备了 8 个 CPU 和 32GB 内存的 Ubuntu22.04 虚拟机。


首先,在虚拟机上启动一个三个节点的 YugabyteDB 集群。在进行分布式之前,可以在单个服务器上对分布式 SQL 数据库进行试验。有多种方法可以在本地启动 YugabyteDB,但作者更喜欢的方法是通过 Docker:


Shell  mkdir ~/yb_docker_data
docker network create custom-network
docker run -d --name yugabytedb_node1 --net custom-network \ -p 15433:15433 -p 7001:7000 -p 9000:9000 -p 5433:5433 \ -v ~/yb_docker_data/node1:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --tserver_flags="ysql_sequence_cache_minval=1" \ --base_dir=/home/yugabyte/yb_data --daemon=false docker run -d --name yugabytedb_node2 --net custom-network \ -p 15434:15433 -p 7002:7000 -p 9002:9000 -p 5434:5433 \ -v ~/yb_docker_data/node2:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --join=yugabytedb_node1 --tserver_flags="ysql_sequence_cache_minval=1" \ --base_dir=/home/yugabyte/yb_data --daemon=false docker run -d --name yugabytedb_node3 --net custom-network \ -p 15435:15433 -p 7003:7000 -p 9003:9000 -p 5435:5433 \ -v ~/yb_docker_data/node3:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --join=yugabytedb_node1 --tserver_flags="ysql_sequence_cache_minval=1" \ --base_dir=/home/yugabyte/yb_data --daemon=false
复制代码


注:在启动 YugabyteDB 节点时设置 ysql_sequence_cache_minval=1,以确保数据库序列可以按顺序递增 1。如果没有这个选项,一个 Kine 连接到 YugabyteDB 将缓存序列的下一个 100 个 ID。这可能导致在 Kubernetes 集群引导期间出现“版本不匹配”错误,因为一个 Kine 连接可能插入 ID 范围从 1 到 100 的记录,而另一个 Kine 连接可能插入 ID 范围从 101 到 200 的记录。


接下来,使用 PostgreSQL 实现启动一个连接到 YugabyteDB 的 Kine 实例:

(1)克隆 Kine 库:

Shell 1 git clone https://github.com/k3s-io/kine.git && cd kine
复制代码


(2)启动一个连接到本地 YugabyteDB 集群的 Kine 实例:

Shell 1 go run . --endpoint postgres://yugabyte:yugabyte@127.0.0.1:5433/yugabyte
复制代码


(3)连接 YugabyteDB,确认 Kine 架构已准备就绪

SQL  psql -h 127.0.0.1 -p 5433 -U yugabyte
yugabyte=# \d List of relations Schema | Name | Type | Owner --------+-------------+----------+---------- public | kine | table | yugabyte public | kine_id_seq | sequence | yugabyte(2 rows)
复制代码

很好,第一次测试成功了。Kine 将 YugabyteDB 视为 PostgreSQL,并且启动时没有任何问题。现在进入下一个阶段:使用 YugabyteDB 在 Kine 之上启动 Kubernetes。

四、使用 YugabyteDB 在 Kine 上启动 Kubernetes


Kine 可以被各种 Kubernetes 引擎使用,包括标准的 Kubernetes 部署、Rancher Kubernetes 引擎(RKE)或 K3 (一种轻量级的 Kubernetes 引擎)。为简单起见,将使用后者。


K3s 集群可以通过一个简单的命令启动:

(1)停止上一节中启动的 Kine 实例。

(2)启动连接到相同本地 YugabyteDB 集群的 K3s(K3s 可执行文件随 Kine 提供):

Shell curl -sfL https://get.k3s.io | sh -s - server --write-kubeconfig-mode=644 \ --token=sample_secret_token \--datastore-endpoint="postgres://yugabyte:yugabyte@127.0.0.1:5433/yugabyte"
复制代码


(3)Kubernetes 启动时应该没有问题,可以通过运行以下命令来确认:

Shell  k3s kubectl get nodes NAME STATUS ROLES AGE VERSION ubuntu-vm Ready control-plane,master 7m13s v1.27.3+k3s1
复制代码

Kubernetes 在 YugabyteDB 上无缝运行。这要归功于 YugabyteDB 很好的特性和与 PostgreSQL 的运行时兼容性。这意味着可以重用为 PostgreSQL 创建的大多数库、驱动程序和框架。


这可能标志着这一旅程的结束,可以回顾一下 K3s 日志。在 Kubernetes 引导期间,日志可能会报告缓慢的查询,如下所示:

SQL  INFO[0015] Slow SQL(total time: 3s) : SELECT  * FROM (  SELECT  (  SELECT  MAX(rkv.id) AS id  FROM  kine AS rkv), (  SELECT  MAX(crkv.prev_revision) AS prev_revision  FROM  kine AS crkv  WHERE  crkv.name = 'compact_rev_key'), kv.id AS theid, kv.name, kv.created, kv.deleted, kv.create_revision, kv.prev_revision, kv.lease, kv.value, kv.old_value  FROM  kine AS kv  JOIN (  SELECT  MAX(mkv.id) AS id  FROM  kine AS mkv  WHERE  mkv.name LIKE $1  GROUP BY  mkv.name) AS maxkv ON maxkv.id = kv.id  WHERE  kv.deleted = 0  OR $2) AS lkv ORDER BY  lkv.theid ASC LIMIT 10001
复制代码

在一台机器上运行 YugabyteDB 时,这可能不是一个重要的问题,但是一旦切换到分布式设置,这样的查询就会成为热点并产生瓶颈。


因此克隆了 Kine 源代码,并开始探索 PostgreSQL 实现,寻找潜在的优化机会。

五、YugabyteDB 的 Kine 优化


在这里,Magda 与 Franck Pachot 合作,Pachot 是一位精通 SQL 层优化的数据库专家,对应用程序逻辑没有或只有很少的更改。


在检查了 Kine 生成的数据库模式并将 EXPLAIN ANALYZE 用于某些查询之后,Franck 提出了对任何分布式 SQL 数据库都有利的基本优化。


幸运的是,优化不需要对 Kine 应用程序逻辑进行任何更改。所要做的就是引入一些 SQL 级别的增强。因此,创建了一个直接支持 YugabyteDB 的 Kine fork。


与此同时,与 PostgreSQL 相比,YugabyteDB 的实现有三个优化:

(1)kine 表的主索引已从 primary index(id)更改为 primary INCEX(id asc)。在默认情况下,YugabyteDB 使用哈希分片在集群中均匀分布记录。然而,Kubernetes 在 id 列上运行了许多范围查询,这使得切换到范围分片是合理的。

(2)通过在索引定义中包括 id 列,kine_name_prev_revision_uindex 索引已被更新为覆盖索引:

CREATE UNIQUE INDEX IF NOT EXISTS kine_name_prev_revision_uindex ON kine (name asc, prev_revision asc) INCLUDE(id);
复制代码

YugabyteDB 的索引分布类似于表记录。因此,索引条目可能引用存储在不同 YugabyteDB 节点上的 id。为了避免节点之间额外的网络往返,可以将 id 包含在二级索引中。


(3)Kine 在完成 Kubernetes 请求的同时执行许多连接。如果查询规划器/优化器决定使用嵌套循环连接,那么在默认情况下,YugabyteDB 查询层将每次读取和连接一条记录。为了加快这个过程,可以启用批处理嵌套循环连接。YugabyteDB 的 Kine 实现通过在启动时执行以下语句来实现:

ALTER DATABASE " + dbName + " set yb_bnl_batch_size=1024;
复制代码

尝试一下这个优化的 YugabyteDB 实现。


首先,停止之前的 K3s 服务,并从 YugabyteDB 集群中删除 Kine 模式:

(1)停止并删除 K3s 服务:

Shell  sudo /usr/local/bin/k3s-uninstall.sh sudo rm -r /etc/rancher
复制代码


(2)删除模式:

SQL  psql -h 127.0.0.1 -p 5433 -U yugabyte
drop table kine cascade;
复制代码

接下来,启动一个为 YugabyteDB 提供优化版本的 Kine 实例:

(1)克隆 fork:

Shell  git clone https://github.com/dmagda/kine-yugabytedb.git && cd kine-yugabytedb
复制代码


(2)启动 Kine:

Shell  go run . --endpoint "yugabytedb://yugabyte:yugabyte@127.0.0.1:5433/yugabyte"
复制代码

Kine 的启动没有任何问题。现在唯一的区别是,不是在连接字符串中指定“postgres”,而是指示“yugabytedb”以启用优化的 YugabyteDB 实现。关于 Kine 和 YugabyteDB 之间的实际通信,Kine 继续使用 Go 的标准 PostgreSQL 驱动程序。

六、在 Kine 的优化版本上构建 Kubernetes


最后,在这个优化版本的 Kine 上启动 k3。

要做到这一点,首先需要从资源中构建 k3:

(1)停止上一节中启动的 Kine 实例。

(2)克隆 K3s 存储库:

Shell  git clone --depth 1 https://github.com/k3s-io/k3s.git && cd k3s
复制代码


(3)打开 go.mod 文件,并在 replace(..)部分的末尾添加以下行:

Go  github.com/k3s-io/kine => github.com/dmagda/kine-yugabytedb v0.2.0
复制代码

这条指令告诉 Go 使用带有 YugabyteDB 实现的最新版本的 Kinefork。


(4)启用对私有仓库和模块的支持:

Shell  go env -w GOPRIVATE=github.com/dmagda/kine-yugabytedb
复制代码


(5)确保更改生效:

Shell  go mod tidy
复制代码


(6)准备构建 K3s 的完整版本:

Shell  mkdir -p build/data && make download && make generate
复制代码


(7)构建完整版本:

Shell  SKIP_VALIDATE=true make
复制代码

完成构建大约需要五分钟。

注意:一旦停止使用这个自定义 K3s 构建,可以按照说明卸载它。

七、在优化的 Kubernetes 版本上运行示例工作负载


在构建完成后,可以使用 Kine 的优化版本启动 K3s。

(1)导航到包含构建构件的目录:

Shell  cd dist/artifacts/
复制代码


(2)通过连接到本地 YugabyteDB 集群启动 K3s:

Shell  sudo ./k3s server \  --token=sample_secret_token \ --datastore-endpoint="yugabytedb://yugabyte:yugabyte@127.0.0.1:5433/yugabyte"
复制代码


(3)确认 Kubernetes 启动成功:

Shell  sudo ./k3s kubectl get nodes
NAME STATUS ROLES AGE VERSION ubuntu-vm Ready control-plane,master 4m33s v1.27.4+k3s-36645e73
复制代码

现在,部署一个示例应用程序,以确保 Kubernetes 集群不仅仅能够自我引导:

(1)采用 Kubernetes 克隆一个库的例子:

Shell  git clone https://github.com/digitalocean/kubernetes-sample-apps.git
复制代码


(2)部署 Emojivoto 应用:

Shell  sudo ./k3s kubectl apply -k ./kubernetes-sample-apps/emojivoto-example/kustomize
复制代码


(3)确保所有部署和服务成功启动:

Shell  sudo ./k3s kubectl get all -n emojivoto  NAME READY STATUS RESTARTS AGE pod/vote-bot-565bd6bcd8-rnb6x    1/1 Running 0 25s pod/web-75b9df87d6-wrznp  1/1 Running 0 24s pod/voting-f5ddc8ff6-69z6v   1/1 Running 0 25s pod/emoji-66658f4b4c-wl4pt  1/1 Running 0 25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/emoji-svc ClusterIP 10.43.106.87 <none> 8080/TCP,8801/TCP 27s service/voting-svc ClusterIP 10.43.14.118 <none> 8080/TCP,8801/TCP 27s service/web-svc ClusterIP 10.43.110.237 <none> 80/TCP 27s
NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/vote-bot 1/1 1 1 26s deployment.apps/web 1/1 1 1 25s deployment.apps/voting 1/1 1 1 26s deployment.apps/emoji 1/1 1 1 26s
NAME DESIRED CURRENT READY AGE replicaset.apps/vote-bot-565bd6bcd8 1 1 1 26s replicaset.apps/web-75b9df87d6 1 1 1 25s replicaset.apps/voting-f5ddc8ff6 1 1 1 26s replicaset.apps/emoji-66658f4b4c 1 1 1 26s
复制代码


(4)使用 CLUSTER_IP:80 调用服务/web svc 以触发应用程序逻辑:

Shell  curl 10.43.110.237:80
复制代码


应用程序将使用以下 HTML 进行响应:

HTML  <!DOCTYPE html> <html>  <head>  <meta charset="UTF-8">  <title>Emoji Vote</title>  <link rel="icon" href="/img/favicon.ico">   <script async src="https://www.googletagmanager.com/gtag/js?id=UA-60040560-4"></script>  <script>  window.dataLayer = window.dataLayer || [];  function gtag(){dataLayer.push(arguments);}  gtag('js', new Date());  gtag('config', 'UA-60040560-4');  </script> </head> <body>  <div id="main" class="main"></div>  </body>  <script type="text/javascript" src="/js" async></script>  </html>
复制代码

八、结语

完成工作!Kubernetes 现在可以使用 YugabyteDB 作为其所有数据的分布式和高可用性 SQL 数据库。

现在可以进入下一阶段:在跨多个可用性区域和区域的真正云计算环境中部署 Kubernetes 和 YugabyteDB,并测试解决方案如何处理各种中断。

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
​Kubernetes的演变:从etcd到分布式SQL的过渡_Kubernetes_互联网工科生_InfoQ写作社区