歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類匯總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
系列文章鏈接
- client-go實戰之一:準備作業
- client-go實戰之二:RESTClient
- client-go實戰之三:Clientset
- client-go實戰之四:dynamicClient
- client-go實戰之五:DiscoveryClient
關于Clientset
- 本篇是《client-go實戰》系列的第三篇,前文學習了最基礎的客戶端Restclient,盡管咱們實戰的需求很簡單(獲取指定namespace下所有pod的資訊),但還是寫了不少代碼,如下圖,各種設定太麻煩,例如api的path、Group、Version、回傳的資料結構、編解碼工具等:

- 如果業務代碼中,需要操作kubernetes資源的代碼都寫成上圖的樣子,相信您是難以忍受的,應該會做一些封裝以簡化代碼,不過client-go已經給出了簡化版客戶端,就省去了咱們自己動手的麻煩,也就是本文的主題:Clientset
本篇概覽
- 本篇目標是學習如何使用Clientset,因此毫無難度,咱們先來個原始碼速讀,快速搞清楚Clientset到底是啥,然后確認需求,最后快速編碼和驗證就完事兒了;
原始碼速度
- 之所以是速讀而非精度,是因為Clientset內容簡單容易理解,快速掌握其原理即可用于實戰;
- Clientset原始碼閱讀的切入點就是其名字中的set,這是個集合,里面有很多東西,看一下Clientset資料結構的原始碼(限于篇幅只展示了一部分):
type Clientset struct {
*discovery.DiscoveryClient
admissionregistrationV1 *admissionregistrationv1.AdmissionregistrationV1Client
admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
internalV1alpha1 *internalv1alpha1.InternalV1alpha1Client
appsV1 *appsv1.AppsV1Client
appsV1beta1 *appsv1beta1.AppsV1beta1Client
appsV1beta2 *appsv1beta2.AppsV1beta2Client
authenticationV1 *authenticationv1.AuthenticationV1Client
...
- 如果您還沒有理解上述原始碼的含義,再請看下圖,左邊是kubernetes的Group、Version資訊,右邊依舊是Clientset資料結構的原始碼,通過箭頭可見:kubernetes的Group和Version的每個組合,都對應Clientset資料結構的一個欄位:

- Clientset果然名副其實,是所有Group和Version組合物件的集合,不過Group和Version組合物件到底是啥呢?以appsV1欄位為例,去看看其型別appsv1.AppsV1Client,如下圖,AppsV1Client只有一欄位,就是咱們熟悉的restClient,所以RESTClient是Clientset的基礎,這話沒毛病,另外注意紅框2中的Deployments方法,回傳的是DeploymentInterface介面實作:

- 順藤摸瓜去看DeploymentInterface,打開deployment.go檔案后真相大白,介面定義的和實作一目了然:

- 挑一個介面實作的代碼看看,就選新建deployment的方法吧,如下,和我們使用RESTClient編碼差不多:
func (c *deployments) Create(ctx context.Context, deployment *v1.Deployment, opts metav1.CreateOptions) (result *v1.Deployment, err error) {
result = &v1.Deployment{}
err = c.client.Post().
Namespace(c.ns).
Resource("deployments").
VersionedParams(&opts, scheme.ParameterCodec).
Body(deployment).
Do(ctx).
Into(result)
return
}
- 至此,您對Clientset應該心里有數了:其實就是把我們使用RESTClient操作資源的代碼按照Group和Version分類再封裝而已,這不像技識訓,更像體力活---所以,這種體力活是誰做的呢?如下圖紅框所示,原始碼中已經注明這些代碼是工具client-gen自動生成的:

- 至此,Clientset的原始碼速度就算完成了,咱們已經知道了Clientset的內幕,接下來開始嘗試使用它;
需求確認
- 本次編碼實戰的需求如下:
- 寫一段代碼,檢查用戶輸入的operate引數,該引數默認是create,也可以接受clean;
- 如果operate引數等于create,就執行以下操作:
- 新建名為test-clientset的namespace
- 新建一個deployment,namespace為test-clientset,鏡像用tomcat,副本數為2
- 新建一個service,namespace為test-clientset,型別是NodePort
- 如果operate引數等于clean,就洗掉create操作中創建的service、deployment、namespace等資源:
- 以上需求使用Clientset客戶端實作,完成后咱們用瀏覽器訪問來驗證tomcat是否正常;
原始碼下載
- 本篇實戰中的原始碼可在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) | [email protected]:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協議 |
- 這個git專案中有多個檔案夾,client-go相關的應用在client-go-tutorials檔案夾下,如下圖紅框所示:

- client-go-tutorials檔案夾下有多個子檔案夾,本篇對應的原始碼在clientsetdemo目錄下,如下圖紅框所示:

編碼
- 新建檔案夾restclientdemo,在里面執行以下命令,新建module:
go mod init clientsetdemo
- 添加k8s.io/api和k8s.io/client-go這兩個依賴,注意版本要匹配kubernetes環境:
go get k8s.io/[email protected]
go get k8s.io/[email protected]
- 新建main.go,內容如下,已經都添加了詳細的注釋,就不贅述了:
package main
import (
"context"
"flag"
"fmt"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/utils/pointer"
"path/filepath"
)
const (
NAMESPACE = "test-clientset"
DEPLOYMENT_NAME = "client-test-deployment"
SERVICE_NAME = "client-test-service"
)
func main() {
var kubeconfig *string
// home是家目錄,如果能取得家目錄的值,就可以用來做默認值
if home:=homedir.HomeDir(); home != "" {
// 如果輸入了kubeconfig引數,該引數的值就是kubeconfig檔案的絕對路徑,
// 如果沒有輸入kubeconfig引數,就用默認路徑~/.kube/config
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
// 如果取不到當前用戶的家目錄,就沒辦法設定kubeconfig的默認目錄了,只能從入參中取
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
// 獲取用戶輸入的操作型別,默認是create,還可以輸入clean,用于清理所有資源
operate := flag.String("operate", "create", "operate type : create or clean")
flag.Parse()
// 從本機加載kubeconfig組態檔,因此第一個引數為空字串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
// kubeconfig加載失敗就直接退出了
if err != nil {
panic(err.Error())
}
// 實體化clientset物件
clientset, err := kubernetes.NewForConfig(config)
if err!= nil {
panic(err.Error())
}
fmt.Printf("operation is %v\n", *operate)
// 如果要執行清理操作
if "clean"==*operate {
clean(clientset)
} else {
// 創建namespace
createNamespace(clientset)
// 創建deployment
createDeployment(clientset)
// 創建service
createService(clientset)
}
}
// 清理本次實戰創建的所有資源
func clean(clientset *kubernetes.Clientset) {
emptyDeleteOptions := metav1.DeleteOptions{}
// 洗掉service
if err := clientset.CoreV1().Services(NAMESPACE).Delete(context.TODO(), SERVICE_NAME, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
// 洗掉deployment
if err := clientset.AppsV1().Deployments(NAMESPACE).Delete(context.TODO(), DEPLOYMENT_NAME, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
// 洗掉namespace
if err := clientset.CoreV1().Namespaces().Delete(context.TODO(), NAMESPACE, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
}
// 新建namespace
func createNamespace(clientset *kubernetes.Clientset) {
namespaceClient := clientset.CoreV1().Namespaces()
namespace := &apiv1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: NAMESPACE,
},
}
result, err := namespaceClient.Create(context.TODO(), namespace, metav1.CreateOptions{})
if err!=nil {
panic(err.Error())
}
fmt.Printf("Create namespace %s \n", result.GetName())
}
// 新建service
func createService(clientset *kubernetes.Clientset) {
// 得到service的客戶端
serviceClient := clientset.CoreV1().Services(NAMESPACE)
// 實體化一個資料結構
service := &apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: SERVICE_NAME,
},
Spec: apiv1.ServiceSpec{
Ports: []apiv1.ServicePort{{
Name: "http",
Port: 8080,
NodePort: 30080,
},
},
Selector: map[string]string{
"app" : "tomcat",
},
Type: apiv1.ServiceTypeNodePort,
},
}
result, err := serviceClient.Create(context.TODO(), service, metav1.CreateOptions{})
if err!=nil {
panic(err.Error())
}
fmt.Printf("Create service %s \n", result.GetName())
}
// 新建deployment
func createDeployment(clientset *kubernetes.Clientset) {
// 得到deployment的客戶端
deploymentClient := clientset.
AppsV1().
Deployments(NAMESPACE)
// 實體化一個資料結構
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: DEPLOYMENT_NAME,
},
Spec: appsv1.DeploymentSpec{
Replicas: pointer.Int32Ptr(2),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app" : "tomcat",
},
},
Template: apiv1.PodTemplateSpec{
ObjectMeta:metav1.ObjectMeta{
Labels: map[string]string{
"app" : "tomcat",
},
},
Spec: apiv1.PodSpec{
Containers: []apiv1.Container{
{
Name: "tomcat",
Image: "tomcat:8.0.18-jre8",
ImagePullPolicy: "IfNotPresent",
Ports: []apiv1.ContainerPort{
{
Name: "http",
Protocol: apiv1.ProtocolSCTP,
ContainerPort: 8080,
},
},
},
},
},
},
},
}
result, err := deploymentClient.Create(context.TODO(), deployment, metav1.CreateOptions{})
if err!=nil {
panic(err.Error())
}
fmt.Printf("Create deployment %s \n", result.GetName())
}
資料結構初始化的煩惱
- 看過或者上述代碼后您可能在煩惱:創建資源時,資料結構的欄位太多太復雜根本記不住,對應的代碼不好寫,這里分享一個我的做法,如下圖,我在開發的時候一共有兩個視窗,左側是官方的yaml示例,右側用了GoLand的分屏功能,分屏的左側是我寫代碼的視窗,右側是資料結構定義,此時內容不會搞錯,資料結構也能對應上,寫起來就舒服多了:

驗證
-
代碼寫完后,執行go run main.go,即可創建namespace、deployment、service等資源;
-
查看kubernetes上資源是否成功創建:
[root@hedy ~]# kubectl get pods -n test-clientset
NAME READY STATUS RESTARTS AGE
client-test-deployment-7677cc9669-kd7l7 1/1 Running 0 178m
client-test-deployment-7677cc9669-kt5rv 1/1 Running 0 178m
[root@hedy ~]# kubectl get service -n test-clientset
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
client-test-service NodePort 10.109.189.151 <none> 8080:30080/TCP 178m
- 瀏覽器訪問kubernetes服務器的30080埠,可見熟悉的tomcat首頁:

-
執行命令go run main.go -operate clean即可洗掉剛才創建的所有資源;
-
至此Clientset的學習和實戰就結束了,總得來說這是個大部分都是自動生成代碼的客戶端,邏輯簡單容易理解,多用幾次熟練后,就能隨心所欲的操控kubernetes的資源了;
關于容器和鏡像的環境
如果您不想自己搭建kubernetes環境,推薦使用騰訊云容器服務TKE:無需自建,即可在騰訊云上使用穩定, 安全,高效,靈活擴展的 Kubernetes 容器平臺;
如果您希望自己的鏡像可以通過外網上傳和下載,推薦騰訊云容器鏡像服務TCR:像資料加密存盤,大鏡像多節點快速分發,跨地域鏡像同步
你不孤單,欣宸原創一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 資料庫+中間件系列
- DevOps系列
歡迎關注公眾號:程式員欣宸
微信搜索「程式員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/298498.html
標籤:其他
上一篇:python初學者指南第一至三章
