天天看點

Kubernetes as Database: 使用kubesql查詢kubernetes資源

寫在前面

kubectl雖然查詢單個的kubernetes資源或者清單都已經比較友善,但是進行更為多個資源的聯合查詢(比如pod和node),以及查詢結果的二次處理方面卻是kubectl無法勝任的。是以一直以來,我都有想法将kubernetes作為資料庫進行查詢。在去年,我開發了第二個版本的kubesql。相關資訊在https://xuxinkun.github.io/2019/03/11/kubesql/,代碼留存在https://github.com/xuxinkun/kubesql/tree/python。這個版本較之我最早的spark離線方式已經有所改觀,但是無法應對中型、甚至較小規模的叢集,性能上存在較大問題。部署上也較為繁雜,且不夠穩定,有一些bug(會異常退出)。而且對于label等字段都無法處理,可用性較差。我總起來不滿意,但是一直沒時間去重構。直到最近,聽了關于presto的一個分享,我感覺重構的機會來了。

這一次kubesql完全抛棄了原有的架構,基于presto進行開發。這裡摘抄一段presto的簡介:presto是一個開源的分布式SQL查詢引擎,适用于互動式分析查詢,資料量支援GB到PB位元組。Presto的設計和編寫完全是為了解決像Facebook這樣規模的商業資料倉庫的互動式分析和處理速度的問題。presto具有豐富的插件接口,可以極為便捷的對接外部存儲系統。

考慮使用presto的主要原因是避免了SQL查詢引擎的邏輯耦合到kubesql中,同時其穩定和高性能保證了查詢的效率。這樣kubesql的主要邏輯專注于擷取k8s的resource變化,以及将resource轉化為關系型資料的邏輯上。

kubesql使用

重構後的kubesql開源項目位址在https://github.com/xuxinkun/kubesql。

先介紹下如何部署和使用。部署方式目前主要使用docker部署,很快會支援k8s的部署方式。

部署前需要擷取kubeconfig。假設kubeconfig位于/root/.kube/config路徑下,則隻要一條指令即可運作。

docker run -it -d --name kubesql -v /root/.kube/config:/home/presto/config xuxinkun/kubesql:latest
           
如果橋接網絡不能通k8s api,則可以使用實體機網絡,加入--net=host參數即可。注意presto端口使用8080,可能會有端口沖突。

而後就可以進行使用了。使用指令為

docker exec -it kubesql presto --server localhost:8080 --catalog kubesql --schema kubesql
           

這時自動進入互動式查詢模式,即可進行使用了。目前已經支援了pods和nodes兩種資源的查詢,對應為三張表,nodes,pods和containers(container是從pod中拆出來的,具體原因見下文原理一節)。

三張表支援的列參見https://github.com/xuxinkun/kubesql/blob/master/docs/table.md。

presto支援一些内置的函數,可以用這些函數來豐富查詢。https://prestodb.io/docs/current/functions.html。

這裡我舉一些使用kubesql查詢的例子。

比如想要查詢每個pod的cpu資源情況(requests和limits)。

presto:kubesql> select pods.namespace,pods.name,sum("requests.cpu") as "requests.cpu" ,sum("limits.cpu") as "limits.cpu" from pods,containers where pods.uid = containers.uid group by pods.namespace,pods.name
     namespace     |                 name                 | requests.cpu | limits.cpu 
-------------------+--------------------------------------+--------------+------------
 rongqi-test-01    | rongqi-test-01-202005151652391759    |          0.8 |        8.0 
 ljq-nopassword-18 | ljq-nopassword-18-202005211645264618 |          0.1 |        1.0 
           

又比如我想要查詢每個node上剩餘可以配置設定的cpu情況(用node上allocatable.cpu減去node上所有pod的requests.cpu的總和)

presto:kubesql> select nodes.name, nodes."allocatable.cpu" - podnodecpu."requests.cpu" from nodes, (select pods.nodename,sum("requests.cpu") as "requests.cpu" from pods,containers where pods.uid = containers.uid group by pods.nodename) as podnodecpu where nodes.name = podnodecpu.nodename;
    name     |       _col1        
-------------+--------------------
 10.11.12.29 | 50.918000000000006 
 10.11.12.30 |             58.788 
 10.11.12.32 | 57.303000000000004 
 10.11.12.34 |  33.33799999999999 
 10.11.12.33 | 43.022999999999996 
           

再比如需要查詢所有所有2020-05-12後建立的pod。

presto:kube> select name, namespace,creationTimestamp from pods where creationTimestamp > date('2020-05-12') order by creationTimestamp desc;
                         name                         |        namespace        |    creationTimestamp    
------------------------------------------------------+-------------------------+-------------------------
 kube-api-webhook-controller-manager-7fd78ddd75-sf5j6 | kube-api-webhook-system | 2020-05-13 07:56:27.000 
           

還可以根據标簽來查詢,查詢所有标簽的appid是springboot,且尚未排程成功的pod。以及計數。

标簽appid在pods表裡則會有一列,列名為"labels.appid",使用該列作為條件來删選pod。

presto:kubesql> select namespace,name,phase from pods where phase = 'Pending' and "labels.appid" = 'springboot';
     namespace      |     name     |  phase  
--------------------+--------------+---------
 springboot-test-rd | v6ynsy3f73jn | Pending 
 springboot-test-rd | mu4zktenmttp | Pending 
 springboot-test-rd | n0yvpxxyvk4u | Pending 
 springboot-test-rd | dd2mh6ovkjll | Pending 
 springboot-test-rd | hd7b0ffuqrjo | Pending
 
 presto:kubesql> select count(*) from pods where phase = 'Pending' and "labels.appid" = 'springboot';
  _col0 
 -------
      5 
           

kubesql原理

kubesql的架構如圖所示:

Kubernetes as Database: 使用kubesql查詢kubernetes資源

kubesql裡主要有三個子產品部分:

  • kubesql-watcher: 監聽k8s api pod和node的變化。并将pod和node的結構化資料轉化為關系型資料(以Map的方式進行儲存)。
  • kubecache: 用于緩存pod和node的資料。
  • kubesql-connector: 作為presto的connector,接受來自presto的調用,通過kubecache查詢列資訊和對應資料,并傳回給presto關于列和資料的資訊。

其中最主要的部分是kubesql-connector。presto插件開發指南可以參考https://prestodb.io/docs/current/develop.html。我沒有選擇從零開始,而是基于已有的localfile插件https://github.com/prestodb/presto/tree/0.234.2/presto-local-file進行的開發。如何進行presto的插件開發,後面我再寫文章來解讀。

由于所有資料都緩存在記憶體中,是以幾乎無磁盤需求。但是也需要根據叢集的規模來提供較大的記憶體。

以pod資料為例,pod中主要資料分成三部分,metadata,spec和status。

metadata中比較難以處理的部分是label和annotation。我将label這個map進行展平,每個key都作為一列。比如

labels:
    app: mysql
    owner: xxx
           

我使用labels作為字首,拼合labels裡面的key作為列名。進而得到兩條資料為:

labels.app: mysql
labels.owner: xxx
           

對于pod A存在app的label但是pod B并沒有該标簽,則對于pod B來說,該列labels.app的值則為null。

類似的annotations也是類似的處理方式。進而讓annotations也就可以成為可以用于篩選pod的條件了。

對于spec來說,最大的困難在于containers的處理。因為一個pod裡面可能有若幹個containers,是以我直接将containers作為一張新的表。同時在containers表裡增加一個uid的列,用來表明該行資料來自于哪個pod。containers裡面的字段也對應都加入到containers表中。containers中比較重要的關于資源的如request和limit,我直接使用requests.作為字首,拼合resource作為列名。比如

requests.cpu

requests.memory

等。這裡cpu的單獨處理為double類型,機關為核,比如100m這裡會轉化為0.1。記憶體等則為bigint,機關為B。

對于status中,比較難于處理的是conditions和containerStatus。conditions是一個清單,但是每個condition的type不相同。是以我将type作為字首,用來生成conditon的列名。比如:

conditions:
  - lastProbeTime: null
    lastTransitionTime: 2020-04-22T09:03:10Z
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: 2020-04-22T09:03:10Z
    status: "True"
    type: ContainersReady
           

那麼在pod表中,我對應可以得到這些列:

Column Type Extra Comment
containersready.lastprobetime timestamp
containersready.lasttransitiontime
containersready.message varchar
containersready.reason
containersready.status
ready.lastprobetime
ready.lasttransitiontime
ready.message
ready.reason
ready.status

這樣我就可以通過"ready.status" = "True" 來篩選condition裡type為ready且status為True的pod了。

containerStatus因為與containers一一對應,是以我将containerStatus合并到containers表裡,并且根據container name一一對應起來。

後記

本次重構後kubesql我直接釋出為1.0.0版本,并且已經在日常使用了。且借助于記憶體和presto的高性能,我測試過5萬pod的叢集,查詢時間為毫秒級。目前暫未發現明顯的bug。大家有發現bug或者新的feature也可以提issue給我。我後期也會再維護該項目。

因為目前隻有pods和nodes資源,相對于k8s龐大的資源來說,還隻是冰山一角。但是增加每個資源要加入相當數量的代碼。我也在考慮如何使用openapi的swagger描述來自動生成代碼。

部署上現在是用docker來部署,馬上也會增加kubernetes的部署方式,這樣會更加便捷。

同時我在考慮,在未來,讓presto的每個worker負責一個叢集的cache。這樣一個presto叢集可以查詢所有的k8s叢集的資訊。該功能還需要再做設計和考慮。

作者:xuxinkun

出處:xinkun的部落格

連結:https://www.cnblogs.com/xuxinkun/

本文版權歸作者所有,歡迎轉載。

未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

歡迎掃描右側二維碼關注微信公衆号

xinkun的部落格 進行訂閱。也可以通過微信公衆号留言同作者進行交流。
Kubernetes as Database: 使用kubesql查詢kubernetes資源

繼續閱讀