作者 | Nicolas Fränkel
譯者 | 天道酬勤,責編 | 徐威龍
封圖 | CSDN 下載於視覺中國
在本文中,我們將開始開發自己的Kubernetes控制器。
技術棧可以是Python、NodeJS或Ruby。因為這個博客被命名為為“ Java極客”,因此選擇Java是很正常的。
作為一個用例,我們將實現sidecar模式:每當一個pod被調度時,sidecar pod也會隨之被調度。如果將前者刪除,則後者也必須刪除。
選擇合適的工具
為了用Java執行REST調用,首先需要生成綁定。有幾種方法可以做到這些。最繁瑣的操作是手動執行此操作:需要仔細掌握所有可能的JSON請求和響應組合,開發相應的Java對象,選擇JSON序列化框架以及HTTP客戶端。第二個最佳選擇是使用專有代碼生成器,例如Swagger(https://swagger.io/)或Apiary(https://apiary.io/)。這就要求API提供程序以一種可能的格式提供模型。不利的一面是,需要使用相關工具。有時,格式或多或少是開放的,例如OpenAPI規範(https://swagger.io/specification/)。在這種情況下,可以從實現該格式的工具中選擇工具。在最好的情況下,可能已經提供了綁定。
Kubernetes就是這種情況:該項目為各種語言提供了自己的綁定(https://kubernetes.io/docs/reference/using-api/client-libraries/)。問題在於語言包裝器與REST API非常接近,對於作者來說太熟悉了。例如,這是列出所有名稱空間中所有pod的方式:
ApiClient client = Config.defaultClient;
CoreV1Api core = new CoreV1Api(client);
V1PodList pods =
core.listPodForAllNamespaces(, , , , , , , );
注意:所有的參數需要傳遞。
這就是“包裝器代碼非常接近REST API”的意思。幸運的是,還有另一個選擇。Fabric8組織在Github上提供了流暢的Java API。與上述代碼等效的代碼是:
KubernetesClient client = new DefaultKubernetesClient;
PodList pods = client.pods.inAnyNamespace.list;
注意:無需傳遞無用的參數。
Fabric8組織在Github上提供了流暢的Java API:
https://github.com/fabric8io/kubernetes-client)
Fabric8快速概述
簡單來說,使用Fabric8的API,所有Kubernetes資源都可以在KubernetesClient實例上使用,例如:
client.namespaces
client.services
client.nodes
根據資源的性質,它的作用域可以是一個名稱空間,也可以不是:
client.pods.inAnyNamespace
client.pods.inNamespace("ns")
此時,可以調用這個動作:
列出所有名稱空間中的所有Pod:
client.pods.inAnyNamespace.list;
刪除名稱空間ns中的所有pod:
client.pods.delete(client.pods.inNamespace("ns").list.getItems);
創建一個名為ns的新名稱空間:
client.namespaces
.createNew
.withApiVersion("v1")
.withNewMetadata
.withName("ns")
.endMetadata
.done;
實現控制循環
注意,Kubernetes控制器只是一個控制循環,它監視集群的狀態,並將其與所需狀態進行協調。為了能夠調度/刪除事件,需要使用Observer模式。應用程序將訂閱此類事件,當他們發生時,相關回調將被觸發。
下圖是一個非常簡單的API圖:
要真正實現一個監視程序,只需執行以下幾行代碼:
public class DummyWatcher implements Watcher
{ @Override
public void eventReceived(Action action, Pod pod) {
switch (action) {
case ADDED: //注意1
break;
case MODIFIED: //注意2
break;
case DELETED: //注意3
break;
case ERROR: //注意4
break;
}
}
@Override
public void onClose(KubernetesClientException cause) {
//注意5
}
}
client.pods
.inAnyNamespace
.watch(DummyWatcher);
注意:
添加新pod時起作用
修改現有pod時起作用
刪除pod時起作用
發生錯誤時起作用
清理任何資源。如果客戶端正確關閉,cause將為
具體細節
現在,我們擁有了實現sidecar模式所需的一切。我不會展示全部代碼,它可以在GitHub上找到:https://github.com/nfrankel/jvm-controller,但需要重點強調一些關鍵內容。
1) 標記sidecar
從本質上講,觀察者需要在添加新的pod時添加一個sidecar pod,並在移除它時刪除它。這種基本方法行不通:如果安排了sidecar pod,則將觸發觀察者,並向sidecar添加新的sidecar pod。而且這種情況還會持續下去。因此,標記sidecar pod至關重要。檢測到此類pod時,不應觸發創建邏輯。
有幾種標記sidecar pod的方法:
在sidecar pod的名稱後加上特定的字符串,例如sidecar
添加如下特定標籤:
client.pods
.inNamespace("ns")
.createNew
.withNewMetadata
.addToLabels("sidecar", "true")
.endMetadata
.done;
2) 連同pod一起移除sidecar
pod應只有一個sidecar。如上所述,應在添加Pod時創建它,而在刪除後者時應刪除它。
因此,應將對主pod的引用添加到sidecar中。這樣,當pod被刪除時,如果它不是一個sidecar,我們應該找到分配的sidecar並將其刪除。
第一種簡單的方法是在刪除主pod時顯式刪除sidecar。但是,這是一項繁重的工作,需要花很多時間。Kubernetes允許將pod的生命週期綁定到另一個Pod的生命週期。然後,刪除邏輯由Kubernetes本身處理。這由ownerReference的概念支持。
該API使其易於實現:
client.pods
.inNamespace("ns")
.createNew
.withNewMetadata
.addNewOwnerReference
.withApiVersion("v1")
.withKind("Pod")
.withName(podName)
.withUid(pod.getMetadata.getUid)
.endOwnerReference
.endMetadata
.done;
3) 始終保持一個sidecar
添加一個sidecar並不意味著它將永遠保持這種方式。例如,可以刪除屬於部署的pod。部署的目標是重新創建一個pod,以達到所需的副本數量。
同樣,如果在保留主pod的同時刪除了sidecar,則應使用正確的引用生成一個新的sidecar。
結論
在這篇文章中,我們描述瞭如何在JVM上使用Java語言實現Kubernetes控制器。藉助Fabric8的API,實現的操作非常簡單。主要問題來自調度/刪除邏輯中的極端情況。在本系列的下一篇(也是最後一篇)文章中,我們將最終看到如何部署和運行代碼。
這篇文章的完整源代碼可以在Github上以Maven格式找到:
https://github.com/nfrankel/jvm-controller
希望這篇文章對你有用,歡迎評論區和我們討論。
原文:https://blog.frankel.ch/your-own-kubernetes-controller/2/
閱讀更多 CSDN 的文章