歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
概覽
- 本文是《Kubernetes官方java客戶端》系列的第八篇,以下提到的java客戶端都是指client-jar.jar;
- 前文《Kubernetes官方java客戶端之七:patch操作》涉及的知識點、程式碼、操作都太多了,對作者和讀者都是莫大的折磨,到了本篇我們們輕鬆一下,寫幾段簡單流暢的程式碼,瞭解java客戶端對fluent style程式設計的支援,並且編碼完成後的驗證操作也很簡單;
關於fluent styel
- 也稱為fluid coding, fluent programming,是一種增強程式碼可讀性的風格,使得閱讀程式碼時更加自然流暢,特點是函式返回有關型別,使得多個函式的呼叫前後連結起來。
- 關於fluent style可以參考Martin Flowler於2005年發表的文章,地址是:https://martinfowler.com/bliki/FluentInterface.html ,使用fluent style前後的程式碼對比如下圖所示:
原始碼下載
- 如果您不想編碼,可以在GitHub下載所有原始碼,地址和連結資訊如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 連結 | 備註 |
---|---|---|
專案主頁 | https://github.com/zq2599/blog_demos | 該專案在GitHub上的主頁 |
git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該專案原始碼的倉庫地址,https協議 |
git倉庫地址(ssh) | git@github.com:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協議 |
- 這個git專案中有多個資料夾,本章的應用在kubernetesclient資料夾下,如下圖紅框所示:
實戰步驟概述
- 在父工程kubernetesclient下面新建名為fluent的子工程;
- fluent工程中只有一個類FluentStyleApplication,啟動的main方法以及fluent style的程式碼都在此類中;
- FluentStyleApplication.java提供四個web介面,功能分別是:新建namespace、新建deployment、新建service、刪除前面三個介面新建的所有資源;
- fluent工程編碼完成後,不需要做成映象部署在kubernetes環境內部,而是作為一個普通SpringBoot應用找個java環境啟動即可,與《Kubernetes官方java客戶端之三:外部應用 》一文的部署和啟動一致;
- 依次呼叫每個介面,驗證功能是否符合預期;
編碼
- 在父工程kubernetesclient下面新建名為fluent的maven子工程,pom.xml內容如下,需要注意的是排除掉spring-boot-starter-json,原因請參考《Kubernetes官方java客戶端之二:序列化和反序列化問題 》:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bolingcavalry</groupId>
<artifactId>kubernetesclient</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.bolingcavalry</groupId>
<artifactId>fluent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fluent</name>
<description>Demo project for fluent style</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.0.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
- 新建FluentStyleApplication.java,首先,該類作為啟動類,要有main方法:
public static void main(String[] args) {
SpringApplication.run(FluentStyleApplication.class, args);
}
- 定義常量NAMESPACE作為本次實戰的namespace:
private final static String NAMESPACE = "fluent";
- 用@PostConstruct註解修飾setDefaultApiClient方法,令其在例項化時執行一次,裡面做了一些全域性性的初始化設定,注意kubeConfigPath變數對應的config檔案路徑要正確:
/**
* 預設的全域性設定
* @return
* @throws Exception
*/
@PostConstruct
private void setDefaultApiClient() throws Exception {
// 存放K8S的config檔案的全路徑
String kubeConfigPath = "/Users/zhaoqin/temp/202007/05/config";
// 以config作為入參建立的client物件,可以訪問到K8S的API Server
ApiClient client = ClientBuilder
.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath)))
.build();
// 會列印和API Server之間請求響應的詳細內容,生產環境慎用
client.setDebugging(true);
// 建立操作類
Configuration.setDefaultApiClient(client);
}
- 接下來是建立namespace的web服務,如下所示,由於namespace在kubernetes的apiVersion是v1,因此建立的是V1Namespace例項:
@RequestMapping(value = "/fluent/createnamespace")
public V1Namespace createnamespace() throws Exception {
V1Namespace v1Namespace = new V1NamespaceBuilder()
.withNewMetadata()
.withName(NAMESPACE)
.addToLabels("label1", "aaa")
.addToLabels("label2", "bbb")
.endMetadata()
.build();
return new CoreV1Api().createNamespace(v1Namespace, null, null, null);
}
-
為了更清晰的展現fluent style效果,將上述程式碼與建立namespace的yaml檔案內容放在一起對比,如下圖所示,可見對照著yaml檔案就能將程式碼寫出來:
-
接下來是建立service的程式碼,為了便於和yaml對應起來,程式碼中特意加了縮排:
@RequestMapping(value = "/fluent/createservice")
public V1Service createservice() throws Exception {
V1Service v1Service = new V1ServiceBuilder()
// meta設定
.withNewMetadata()
.withName("nginx")
.endMetadata()
// spec設定
.withNewSpec()
.withType("NodePort")
.addToPorts(new V1ServicePort().port(80).nodePort(30103))
.addToSelector("name", "nginx")
.endSpec()
.build();
return new CoreV1Api().createNamespacedService(NAMESPACE, v1Service, null, null, null);
}
- 建立deployment的程式碼如下,因為內容較多所以相對複雜一些,請注意,由於deployment在kubernetes的apiVersion是extensions/v1beta1,因此建立的是ExtensionsV1beta1Deployment例項:
@RequestMapping(value = "/fluent/createdeployment")
public ExtensionsV1beta1Deployment createdeployment() throws Exception {
ExtensionsV1beta1Deployment v1Deployment = new ExtensionsV1beta1DeploymentBuilder()
// meta設定
.withNewMetadata()
.withName("nginx")
.endMetadata()
// spec設定
.withNewSpec()
.withReplicas(1)
// spec的templat
.withNewTemplate()
// template的meta
.withNewMetadata()
.addToLabels("name", "nginx")
.endMetadata()
// template的spec
.withNewSpec()
.addNewContainer()
.withName("nginx")
.withImage("nginx:1.18.0")
.addToPorts(new V1ContainerPort().containerPort(80))
.endContainer()
.endSpec()
.endTemplate()
.endSpec()
.build();
return new ExtensionsV1beta1Api().createNamespacedDeployment(NAMESPACE, v1Deployment, null, null, null);
}
- 從上述程式碼可見,withXXX和endXXX是成對出現的,請在程式設計的時候注意不要遺漏了endXXX,建議在寫withXXX的同時就把endXXX寫上;
- 最後一個方法是清理所有資源的,前面建立的deployment、service、namespace都在此一次性清理掉,實際操作中發現了一個尷尬的情況:刪除deployment和namespace時,傳送到API Server的刪除請求都收到的操作成功的響應,但kubernetes客戶端在反序列化響應內容時丟擲異常(日誌中顯示了詳細情況),鄙人能力有限暫未找到解決之道,因此只能用try catch來避免整個方法丟擲異常,好在kubernetes實際上已經刪除成功了,影響不大:
@RequestMapping(value = "/fluent/clear")
public String clear() throws Exception {
// 刪除deployment
try {
new ExtensionsV1beta1Api().deleteNamespacedDeployment("nginx", NAMESPACE, null, null, null, null, null, null);
} catch (Exception e)
{
log.error("delete deployment error", e);
}
CoreV1Api coreV1Api = new CoreV1Api();
// 刪除service
coreV1Api.deleteNamespacedService("nginx", NAMESPACE, null, null, null, null, null, null);
// 刪除namespace
try {
coreV1Api.deleteNamespace(NAMESPACE, null, null, null, null, null, null);
} catch (Exception e)
{
log.error("delete namespace error", e);
}
return "clear finish, " + new Date();
}
- 編碼已完成,啟動fluent工程,接下來開始驗證功能是否正常;
驗證
-
將fluent工程直接在IEDA環境啟動;
-
瀏覽器訪問:http://localhost:8080/fluent/createnamespace ,頁面會展示API Server返回的完整namespace資訊:
-
瀏覽器訪問:http://localhost:8080/fluent/createservice ,頁面會展示API Server返回的完整service資訊:
-
瀏覽器訪問:http://localhost:8080/fluent/createdeployment ,頁面會展示API Server返回的完整deployment資訊:
-
驗證前面幾個介面建立的服務是否可用,我這裡kubernetes的IP地址是192.168.50.135,因此訪問:http://192.168.50.135:30103 ,可以正常顯示nginx首頁:
-
SSH登入kubernetes伺服器檢視,通過kubernetes的java客戶端建立的資源都正常:
-
驗證完成後,瀏覽器訪問:http://localhost:8080/fluent/clear ,即可清理掉前面三個介面建立的資源;
- 至此,基於fluent style呼叫java客戶端的實戰就完成了,希望您能熟練使用此風格的API呼叫,使得編碼變得更加輕鬆流暢,順便預告一下,下一篇繼續做一些簡單輕鬆的操作,目標是熟悉java客戶端的常用操作;
你不孤單,欣宸原創一路相伴
歡迎關注公眾號:程式設計師欣宸
微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界...
https://github.com/zq2599/blog_demos