k3s高可用部署實踐及其原理

為了提升k3s的使用體驗,我們將推出由k3s開發(fā)人員撰寫的“k3s黑魔法”系列文章來詳細介紹k3s功能、原理等方面的內容。本篇文章是該系列的第二篇,文章詳細介紹k3s的HA部署實踐并探討其原理。

同時,歡迎大家添加k3s助手(微信號:k3s2019),加入官方微信群和大家一起交流。

前 言

上一篇我們探討了k3s單進程如何實現(xiàn)了對k8s各個服務的管理,我們通過單點部署的方式給大家展現(xiàn)了k3s運行時的效果。而在面向生產環(huán)境的實踐中,高可用HA是我們無法避免的問題,k3s本身也歷經(jīng)多個版本的迭代,HA方案也進行了不斷優(yōu)化,形成了目前的比較穩(wěn)定的HA方案。k3s的HA方案中,主要關注以下幾點:

  1. k3s 中的datastore如何選型?

  2. k3s worker節(jié)點如何能夠均衡訪問master服務?

  3. 除了datastore本身外,是否還需要依賴其他第三方組件?

帶著這些疑問,我們先進行HA部署實踐,然后進行原理上的探討。

HA部署實踐

目前,k3s支持SQLite/etcd/MySQL/PostgreSQL/DQLite等datastore,不同的datastore面向不同的使用場景。大部分用戶使用默認參數(shù)安裝時,都會使用到SQlite,而對于etcd,大家都已經(jīng)再熟悉不過。所以我們這里選擇大家熟知的MySQL來做HA的實踐,PostgreSQL與MySQL類似,我們就不再重復。

對于MySQL本身的HA,由于我們并不是在DBA范疇討論,所以我們直接使用公有云的RDS創(chuàng)建MySQL服務,并通過k3s來使用它。使用AWS創(chuàng)建MySQL服務非常簡單,很容易我們就能得到一個服務實例,如下:

在這里插入圖片描述

創(chuàng)建兩個節(jié)點,以k3s v1.0版本為例,分別執(zhí)行以下命令:

$ curl -sfL https://get.k3s.io | sh -s - server --datastore-endpoint='mysql://admin:Rancher2019k3s@tcp(k3s-mysql.csrskwupj33i.ca-central-1.rds.amazonaws.com:3306)/k3sdb'
# 注意database name不要加特殊字符

在任意一個節(jié)點中,查看node情況:

$ kubectl get no
NAME              STATUS   ROLES    AGE     VERSION
ip-172-31-4-119   Ready    master   7s      v1.16.3-k3s.2
ip-172-31-7-200   Ready    master   2m32s   v1.16.3-k3s.2

在我們添加一個worker節(jié)點之前,我們需要給多個k3s server提供一個統(tǒng)一的訪問入口,這可以使用以下方式實現(xiàn):

  1. L4層負載均衡器

  2. Round-robin DNS

  3. VIP或者彈性IP

本文采用比較簡單的DNS方案,我們針對k3s server的IP,添加了對應的A記錄,并得到了一個域名,使用這個域名我們就可以添加worker:

$ curl -sfL https://get.k3s.io | K3S_URL=https://k3s-t2.niusmallnan.club:6443 K3S_TOKEN=${K3S_TOKEN} sh -
 
$ kubectl get no
NAME              STATUS   ROLES    AGE   VERSION
ip-172-31-7-200   Ready    master   15m   v1.16.3-k3s.2
ip-172-31-4-119   Ready    master   12m   v1.16.3-k3s.2
ip-172-31-7-217   Ready    <none>   25s   v1.16.3-k3s.2

這樣兩臺k3s server同樣具備worker屬性,且我們又新增了一個純粹的woker。而這臺worker節(jié)點是如何連接k3s server的呢?之前的文章,我們探討過worker節(jié)點依然會通過thread方式運行kubelet/kubeproxy,而這些組件也需要連接api-server獲取資源狀態(tài),比如我們可以看kubelet的kubeconfig文件:

$ cat /var/lib/rancher/k3s/agent/kubelet.kubeconfig
apiVersion: v1
clusters:
- cluster:
    server: https://127.0.0.1:42545
    certificate-authority: /var/lib/rancher/k3s/agent/server-ca.crt
  name: local
contexts:
- context:
    cluster: local
    namespace: default
    user: user
  name: Default
current-context: Default
kind: Config
preferences: {}
users:
- name: user
  user:
    client-certificate: /var/lib/rancher/k3s/agent/client-kubelet.crt
    client-key: /var/lib/rancher/k3s/agent/client-kubelet.key

我們可以看到kubelet訪問的是本地的服務端口,所以worker節(jié)點中一定存在一個proxy來反向代理api-server,查看下面的配置,我們就能找到答案:

cat /var/lib/rancher/k3s/agent/etc/k3s-agent-load-balancer.json
{
  "ServerURL": "https://k3s-t2.niusmallnan.club:6443",
  "ServerAddresses": [
    "172.31.4.119:6443",
    "172.31.7.200:6443"
  ]
}

進入數(shù)據(jù)庫中,我們可以看到k3s存儲的表結構,相當于把etcd那種kv模式改為關系型數(shù)據(jù)庫模式,比如:


$ select * from kine limit 3\G;
…
…
…
*************************** 3. row ***************************
             id: 3
           name: /registry/apiregistration.k8s.io/apiservices/v1.apiextensions.k8s.io
        created: 1
        deleted: 0
create_revision: 0
  prev_revision: 0
          lease: 0
          value: {"kind":"APIService","apiVersion":"apiregistration.k8s.io/v1beta1","metadata":{"name":"v1.apiextensions.k8s.io","uid":"c9109c3a-3475-42cb-a1e3-73ddb6161c55","creationTimestamp":"2019-11-19T09:18:06Z","labels":{"kube-aggregator.kubernetes.io/automanaged":"onstart"}},"spec":{"service":null,"group":"apiextensions.k8s.io","version":"v1","groupPriorityMinimum":16700,"versionPriority":15},"status":{"conditions":[{"type":"Available","status":"True","lastTransitionTime":"2019-11-19T09:18:06Z","reason":"Local","message":"Local APIServices are always available"}]}}
 
      old_value: NULL
3 rows in set (0.00 sec)

DQLite(https://github.com/canonical/dqlite)的方案更佳輕量級,可以理解為高可用版本的SQLite。k3s已經(jīng)內置了DQLite,所以啟用它是很方便的,由于DQLite的數(shù)據(jù)備份基于raft協(xié)議,所以基數(shù)個節(jié)點是比較穩(wěn)妥的方式,部署過程參考官方文檔即可:

https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/

那么,我們可以總結一下,各個datastore類型的使用場景,參考如下:

在這里插入圖片描述

總的來說,k3s HA的基本模式,如下圖所示:

在這里插入圖片描述

HA實現(xiàn)原理

我們都知道k8s的默認datastore是etcd,而k3s是如何將針對etcd的相關操作,轉化到其他類型的datastore上的呢?這里Rancher開發(fā)了一個新的組件叫kine(https://github.com/rancher/kine),由它來完成這項工作。k8s(k3s)的api-server默認使用etcd v3的存儲接口,etcd v3中被k8s使用的接口大致如下(https://github.com/etcd-io/etcd/blob/master/etcdserver/etcdserverpb/rpc.proto):

在這里插入圖片描述

在kine中,這些接口都依次被實現(xiàn)(Lease相關還不支持),可以參考這里:

https://github.com/rancher/kine/tree/master/pkg/server 。

kine將這些接口暴露給k3s的api-server。當我們給k3s設置相應的datastore-endpoint時,k3s的api-server還是配置連接etcd v3,但是etcd-servers的地址則配置為kine的服務地址,相當于所有的api讀寫操作都通過kine來處理,kine來把讀寫操作轉給MySQL/PostgreSQL/SQLite等。如果我們設置真正的etcd backend,kine會略過并直接暴露真實的etcd-servers給k3s api-server。

worker節(jié)點中的api-server代理通過一個tcpproxy(http://github.com/google/tcpproxy)來實現(xiàn),參考:

https://github.com/rancher/k3s/blob/master/pkg/agent/loadbalancer/loadbalancer.go 。

tcpproxy需要獲取真實的api-server的服務地址,這里主要靠worker與master建立的tunnel,也就是websocket數(shù)據(jù)通道獲取,這個tunnel是k3s獨有的。當api-server地址有更新,worker依靠websocket獲取新的地址,同時更新worker中的tcpproxy的配置。

后 記

高可用是軟件投產的基本標準,k3s目前已經(jīng)GA,并提供了HA的方案。無論是在邊緣場景中,還是開發(fā)測試中,都可以根據(jù)自身的需求選擇合適的datastore以及對應的HA方式。DQLite的方案目前還是實驗性質的,相信隨著整個邊緣計算生態(tài)的發(fā)展,這部分也會很快穩(wěn)定可用。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容