KubeFed: Kubernetes Federation v2

Kubernetes Federation(聯邦) 一直是很有趣的議題,並被重視的功能,Federation 目的是希望實現單一叢集統一管理多個 Kubernetes 叢集的機制,這些叢集可能是跨地區(Region),也可能是在不同公有雲供應商(Cloud Provider)上,亦或者是公司內部自行建立的叢集。一但叢集進行聯邦後,就可以利用 Federation API 資源來統一管理多個叢集的 Kubernetes API 資源,如定義 Deployment 如何部署到不同叢集上,其叢集所需的副本數等。

而 Kubernetes Federation 的誕生正是希望利用聯邦機制來解決一些問題,並達到一些好處,如以下:

  • 簡化管理多個叢集的 Kubernetes 物件(如 Deployment, Service 等)。
  • 在多個叢集之間分散工作負載(容器),以提升應用程式(服務)的可靠性。
  • 跨叢集的資源排程,依據排程策略在多個叢集進行應用程式(服務)部署。
  • 在不同叢集中,能更快速更容易地遷移應用程式(服務)。
  • 跨叢集的服務發現,服務可以提供給當地存取,以降低延遲。
  • 實踐多雲(Multi-cloud)或混合雲(Hybird Cloud)的部署。

雖然 Kubernetes 社區很早之前就已實現 Federation(v1) 機制,但隨著 Kubernetes 成長,Federation v1 發展的越來越緩慢,並在之後版被棄用了,而 SIG Multi-Cluster 團隊也隨即提出新架構 Federation v2 來取代。

至於 v1 為何被棄用?而 v2 又帶來什麼變化呢?我將在接下來部分簡單說明。

為什麼 v1 被棄用?

Federation v1 在 Kubernetes v1.3 左右時,就已經著手設計(Design Proposal),並在後面幾個版本中釋出了相關的元件與命令列工具(kubefed),來用於幫助使用者快速建立聯邦叢集,並在 Kubernetes v1.6 時,進入了 Beta 階段。

但 Federation v1 在進入 Beta 後,就沒有更進一步的發展,一直到 Kubernetes v1.11 左右正式被棄用。至於為什麼被棄用呢?這是因為開發團隊認為叢集聯邦的實踐比想象中還要困難,有許多問題是 v1 架構沒被考慮進去的,比如說:

  • 控制平面元件會因為發生問題,而影響整體叢集效能。
  • 無法相容新的 Kubernetes API 資源。
  • 無法有效的在多個叢集管理權限,如不支援 RBAC。
  • 聯邦層級的設定與策略依賴 API 資源的 Annotations 物件內容,這使得彈性不佳。

而這些問題基本上是當初設計架構所延伸而來。我們可以透過了解整體架構來更容易知道問題點。

從上圖架構中得知 Federation v1 的設計沿用類似 Kubernetes 控制平面架構,其主要元件有以下:

  • federation-apiserver: 提供 Federation API 資源,只支援部分 Kubernetes API resources
  • federation-controller-manager: 協調不同叢集之間的狀態,如同步 Federated 資源與策略,並建立 Kubernetes 物件至對應叢集上。
  • etcd: 儲存 Federation 的狀態。

Federation v1 提供了kubefed工具來簡化安裝 Federation 控制平面元件的過程,並提供新增/刪除叢集的操作。

從程式碼大致可以了解 Federation v1 的 API Server 在基礎上是透過 k8s.io/apiserver 套件開發,這種是採用 API Aggregation 方式來擴充 Kubernetes API。而 Federated API 資源則是實作 Adapter 來管理 Kubernetes API 資源,但這些 Adapter 都是寫死 API 版本的,比如說 Deployment 只支援extensions/v1beta1版本 API,所以當建立一個apps/v1的 Deployment,並且在 Annotations 設定聯邦策略時,就會無法正常運作。

因此,若想要支援其他資源(如 CronJob)或版本,就必須在 Federation types 新增對應的 Adapter,然後透過 Code Generator 產生 API 的 client-go 套件給 Controller Manager 操作 API 使用,最後重新建構一版本來更新 API Server 與 Controller Manager 以提供對其他資源與版本的支援。另外 Federation v1 在設計之初未考慮 RBAC 與 CRD(TPR) 功能,因此無法提供跨叢集的權限控管,以及客製化資源的擴展。從上述狀況中,就能感受到 Federation v1 在擴展性有很多挑戰。

也正因為在架構上無法很好解決這些問題,Federation v1 才會發展的越來越緩慢,並在最後被棄用。當然也是因為有這樣的發展過程與經驗,才能有 Federation v2 的誕生。

Federation v2

Kubernetes Cluster Federation 又名 KubeFed 或 Federation v2,是 Kubernetes SIG Multi-Cluster 團隊新提出的叢集聯邦架構(Architecture DocBrainstorming Doc),新架構在 Federation v1 基礎之上,簡化擴展 Federated API 過程,並加強跨叢集服務發現與排程的功能。另外 KubeFed 在設計之初,有兩個最重要核心理念是 KubeFed 希望實現的,分別為Modularization(模組化)Customizable(可客制),這兩個理念大概是希望 KubeFed 能夠跟隨著 Kubernetes 生態發展,並持續保持相容性與擴展性。

KubeFed 在元件上最大改變是將 API Server 移除,並透過 CRD 機制來完成 Federated Resources 的擴充。而 KubeFed Controller 則管理這些 CRD,並實作同步 Resources、跨叢集排程等功能。從架構圖中看到,KubeFed 提出了一些新概念來加強功能的擴展。

Concepts

目前 KubeFed 透過 CRD 方式新增了四種 API 群組來實現聯邦機制的核心功能:

API Group 用途
core.kubefed.k8s.io 叢集組態、聯邦資源組態、KubeFed Controller 設定檔等。
types.kubefed.k8s.io 被聯邦的 Kubernetes API 資源。
scheduling.kubefed.k8s.io 副本排程策略。
multiclusterdns.kubefed.k8s.io 跨叢集服務發現設定。

而在這些核心功能中,我們必須先瞭解一些 KebeFed 提出的基礎概念後,才能更清楚知道 KubeFed 是如何運作的。

Cluster Configuration

用來定義哪些 Kubernetes 叢集要被聯邦。可透過kubefedctl join/unjoin來加入/刪除叢集,當成功加入時,會建立一個 KubeFedCluster 物件來儲存叢集相關資訊,如 API Endpoint、CA Bundle 等。這些資訊會被用在 KubeFed Controller 存取不同 Kubernetes 叢集上,以確保能夠建立 Kubernetes API 資源,示意圖如下所示。

在 Federation 中,會區分 Host 與 Member 兩種類型叢集。

  • Host: 用於提供 KubeFed API 與控制平面的叢集。
  • Member: 透過 KubeFed API 註冊的叢集,並提供相關身份憑證來讓 KubeFed Controller 能夠存取叢集。Host 叢集也可以作為 Member 被加入。

Type Configuration

定義了哪些 Kubernetes API 資源要被用於聯邦管理。比如說想將 ConfigMap 資源透過聯邦機制建立在不同叢集上時,就必須先在 Federation Host 叢集中,透過 CRD 建立新資源 FederatedConfigMap,接著再建立名稱為 configmaps 的 Type configuration(FederatedTypeConfig) 資源,然後描述 ConfigMap 要被 FederatedConfigMap 所管理,這樣 KubeFed Controllers 才能知道如何建立 Federated 資源。以下為簡單範例:

apiVersion: core.kubefed.k8s.io/v1beta1
kind: FederatedTypeConfig
metadata:
name: configmaps
namespace: kube-federation-system
spec:
federatedType:
group: types.kubefed.k8s.io
kind: FederatedConfigMap
pluralName: federatedconfigmaps
scope: Namespaced
version: v1beta1
propagation: Enabled
targetType:
kind: ConfigMap
pluralName: configmaps
scope: Namespaced
version: v1

若想新增 CRD 的 Federated API 的話,可透過 kubefedctl enable <res> 指令來建立,如下:

$ kubefedctl enable etcdclusters
$ kubectl api-resources | grep etcd
etcdclusters etcd etcd.database.coreos.com true EtcdCluster
federatedetcdclusters fetcd types.kubefed.k8s.io true FederatedEtcdCluster

$ kubectl -n kube-federation-system get federatedtypeconfigs | grep etcd
etcdclusters.etcd.database.coreos.com 3m16s

而一個 Federated 資源一般都會具備三個主要功能,這些資訊能夠在 spec 中由使用者自行定義,如下範例:

apiVersion: types.kubefed.k8s.io/v1beta1
kind: FederatedDeployment
metadata:
name: test-deployment
namespace: test-namespace
spec:
template: # 定義 Deployment 的所有內容,可理解成 Deployment 與 Pod 之間的關析。
metadata:
labels:
app: nginx
spec:
...
placement: # 定義哪些叢集要建立該 Federated 物件。
clusters:
- name: cluster2
- name: cluster1
overrides: # 定義叢集的 spec.template 中哪個欄位要被覆寫。
- clusterName: cluster2
clusterOverrides:
- path: spec.replicas
value: 5
  • Template: 定義 Federated 資源所引用的(即 Type configuration 的目標資源)物件資訊。如 FederatedDeployment 中的spec.template會定義 Deployment 所有內容。
  • Placement: 定義 Federated 資源要分散到哪些叢集上,若沒有該物件,則不會分散到任何叢集中。如 FederatedDeployment 中的spec.placement定義了兩個叢集時,這些叢集將被同步建立相同的 Deployment。另外也支援用 spec.placement.clusterSelector 的方式來選擇要放置的叢集。
  • Override: 定義修改指定叢集的 Federated 資源中的spec.template內容。如部署FederatedDeployment 到不同公有雲上的叢集時,就能透過spec.overrides來調整 Volume 或副本數。

目前 Override 不支援List(Array)的欄位。比如說無法修改spec.template.spec.containers[0].image

Scheduling

KubeFed 提供了一種自動化機制來將工作負載實例分散到不同的叢集中,這能夠基於總副本數叢集的放置策略來將 Deployment 或 ReplicaSet 資源進行排程。排程策略是透過建立 ReplicaSchedulingPreference(RSP) 物件,再由 KubeFed RSP Controller 監聽與擷取 RSP 內容來將工作負載實例建立到指定的叢集上。

以下為一個 RSP 範例。假設有三個叢集被聯邦,名稱分別為 ap-northeast、us-east 與 us-west。

apiVersion: scheduling.kubefed.k8s.io/v1alpha1
kind: ReplicaSchedulingPreference
metadata:
name: test-deployment
namespace: test-ns
spec:
targetKind: FederatedDeployment
totalReplicas: 15 # 所有叢集 Pod 副本數總和
clusters: # 定義叢集排程策略
"*":
weight: 2
maxReplicas: 12
ap-northeast:
minReplicas: 1
maxReplicas: 3
weight: 1

當該範例建立後,RSP Controller 會收到資源,並匹配對應 namespace/name 的 FederatedDeployment 與 FederatedReplicaSet 是否存在,若存在的話,會根據設定的策略計算出每個叢集預期的副本數,之後覆寫 Federated 資源中的spec.overrides內容以修改每個叢集的副本數,最後再由 KubeFed Sync Controller 來同步至每個叢集的 Deployment。以上面為例,結果會是 ap-northeast 叢集會擁有 3 個 Pod,us-east 跟 us-west 則分別會有 6 個 Pod。

  • spec.clusters未定義的話,則預設為{"*":{Weight: 1}}
  • 若有定義 spec.replicas 的 overrides 時,副本會以 RSP 為優先考量。
  • 分配的計算機制可以參考 kubefed/pkg/controller/util/planner/planner.go

Multi-Cluster DNS

KubeFed 提供了一組 API 資源,以及 Controllers 來實現跨叢集 Service/Ingress 的 DNS records 自動產生機制,並結合 ExternalDNS 來同步更新至 DNS 服務供應商。以下為簡單範例:

apiVersion: multiclusterdns.kubefed.k8s.io/v1alpha1
kind: Domain
metadata:
name: test
namespace: kube-federation-system
domain: k8s.example.com
---
apiVersion: multiclusterdns.kubefed.k8s.io/v1alpha1
kind: ServiceDNSRecord
metadata:
name: nginx
namespace: development
spec:
domainRef: test
recordTTL: 300

首先假設已建立一個名稱為 nginx 的 FederatedDeployment,然後放到 development namespace 中,並且也建立了對應的 FederatedService 提供 LoadBalancer。這時當建立上述 Domain 與 ServiceDNSRecord 後,KubeFed 的 Service DNS Controller 會依據 ServiceDNSRecord 物件內容,去收集不同叢集的 Service 資訊,並將這些資訊更新至 ServiceDNSRecord 狀態中,接著 DNS Endpoint Controller 會依據該 ServiceDNSRecord 的狀態內容,建立一個 DNSEndpoint 物件,並產生 DNS records 資源,最後再由 ExternalDNS 來同步更新 DNS records 至 DNS 供應商。下圖是 Service DNS 建立的架構。

若是 Ingress 的話,會由 IngressDNSRecord 物件取代,並由 Ingress DNS Controller 收集資訊。

Setup Federation v2 on AWS

本節將簡單說明如何在 AWS 建立跨不同地區的 Kubernetes 聯邦叢集,其架構如下圖所示。

  • KubeFed 官方文件也提供了 On-premises(minikube 與 kind)、GKE 與 IBM ICP 的教學,來讓使用者快速學習如何建置。

事前準備

開始前,需要先安裝下列工具到操作機器上來提供使用:

  • kubectl:用來操作部署完成的 Kubernetes 叢集。版本為 v1.14.1

  • kops:用來部署與管理公有雲上的 Kubernetes 叢集。版本為 v1.14.0

    • Mac OS X:

      $ brew update && brew install kops
    • Linux distro:

      $ curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
      $ chmod +x kops-linux-amd64 && sudo mv kops-linux-amd64 /usr/local/bin/kops
  • helm: 用來部署 Federation v2 元件的工具。

  • kubefedctl:用來新增與加入叢集成為聯邦的工具。可以到連結中下載二進制檔,版本為 v0.1.0-rc1

  • AWS CLI:用來操作 AWS 服務的工具。

$ sudo pip install awscli
$ aws --version
aws-cli/1.15.4

上述工具完成後,還要準備一下資訊:

  • 申請 AWS 帳號,並在 IAM 服務新增一個 User 設定存取所有服務(AdministratorAccess)。另外這邊要記住 AccessKey 與 SecretKey。

一般來說只需開啟 S3、Route53、EC2、EBS、ELB 與 VPC 權限,但由於偷懶就全開。以下為各 AWS 服務在本次安裝中的用意:

  • IAM: 提供身份認證與存取管理。
  • EC2: Kubernetes 叢集部署的虛擬機環境。
  • ELB: Kubernetes 元件與 Service 負載平衡。
  • Route53: 提供 Public domain 存取 Kubernetes 叢集環境與應用程式。
  • S3: 儲存 Kops 狀態。
  • VPC: 提供 Kubernetes 的 Host 與 CNI 網路環境。
  • 擁有自己的 Domain Name,這邊可以在 AWS Route53 註冊,或者是到 GoDaddy 購買。

設定腳本參數

教學將使用撰寫好的腳本 aws-k8s-federation 進行部署。首先在操作的節點透過 Git 取得腳本:

$ git clone https://github.com/kairen/aws-k8s-federation
$ cd aws-k8s-federation
$ cp .env.sample .env

編輯.env檔案並修改一下參數:

# Kubernetes version
export KUBERNETES_VERSION="1.14.1"

# Your domain name and envs
export DOMAIN_NAME="k8s.example.com" # 你的 Domain Name(這邊為 <hoste_dzone_name>.<domain_name>)

建立 Route53 Hosted Zone

首先透過 aws 工具進行設定使用指定 AccessKey 與 SecretKey:

$ aws configure
AWS Access Key ID [****************QGEA]:
AWS Secret Access Key [****************zJ+w]:
Default region name [None]:
Default output format [None]:

設定的 Keys 可以在~/.aws/credentials找到。

接著需要在 Route53 建立一個 Hosted Zone,並在 Domain Name 供應商上設定NameServers

$ ./0-create-hosted-domain.sh
# output
...
{
"HostedZone": {
"ResourceRecordSetCount": 2,
"CallerReference": "2018-04-25-16:16",
"Config": {
"PrivateZone": false
},
"Id": "/hostedzone/Z2JR49ADZ0P3WC",
"Name": "k8s.example.com."
},
"DelegationSet": {
"NameServers": [
"ns-1547.awsdns-01.co.uk",
"ns-1052.awsdns-03.org",
"ns-886.awsdns-46.net",
"ns-164.awsdns-20.com"
]
},
"Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z2JR49ADZ0P3WC",
"ChangeInfo": {
"Status": "PENDING",
"SubmittedAt": "2018-04-25T08:16:57.462Z",
"Id": "/change/C3802PE0C1JVW2"
}
}

之後將上述NameServers新增至自己的 Domain name 的 record 中,如 Godaddy:

部署叢集

當上述流程都完成後,即可依照腳本順序來完成 Kubernetes 叢集與 Federation 叢集的建立,過程中也會包含操作 Federation v2 的功能。最終會以 Federated 物件建立一個 NGINX 應用程式,並部署到不同叢集中,然後透過 MultiClusterDNS + ExternalDNS 來自動建立 DNS records 到 AWS 上。

Summary

雖然 KubeFed 還處於 Alpha 階段,但整體架構基於 Federation v1 進行了很多改善,而從 GitHub 近幾個月的貢獻來看,可以發現 Red Hat 在此專案投入許多的貢獻,這對於 KubeFed 來說是很好的發展。而現在 KubeFed 官方也有釋出一些文件,用來幫助使用者快速在 Minikube、kind(Kubernetes IN Docker)、GCP 上部署,這對於初次接觸的人是一項福利。

另外有趣的是從 OpenShift v4.0 Roadmap 中,也能看到 KubeFed 被放入其中,在 Operator Hubfederation-v2-operator 也能看到 Red Hat 在這方面的佈局。我想之後 Kubernetes 叢集聯邦又會再次成為熱烈討論的議題,或許 Red Hat 也是希望透過 KubeFed 實現 OpenShift 的多雲(Multi-cloud)混合雲(Hybird Cloud)部署吧(個人覺得拉)。

不過這不完全表示 KubeFed 在今後勢必會發展得很好,因為隨著 Kubernetes 發展,想必還會有更多的問題需要去發現、提出與解決的,比如說有狀態服務(Stateful Service)、Federation Host 叢集掛了怎麼辦等問題,這些問題若之後有相關社群的資訊,我也會更新上來。

Related Posts

References

Share Comments