# client-go示例 访问kubernetes集群有以下几种方式: | 方式 | 特点 | 支持者 | | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | | Kubernetes dashboard | 直接通过Web UI进行操作,简单直接,可定制化程度低 | 官方支持 | | kubectl | 命令行操作,功能最全,但是比较复杂,适合对其进行进一步的分装,定制功能,版本适配最好 | 官方支持 | | [client-go](https://github.com/kubernetes/client-go) | 从kubernetes的代码中抽离出来的客户端包,简单易用,但需要小心区分kubernetes的API版本 | 官方支持 | | [client-python](https://github.com/kubernetes-incubator/client-python) | python客户端,kubernetes-incubator | 官方支持 | | [Java client](https://github.com/fabric8io/kubernetes-client) | fabric8中的一部分,kubernetes的java客户端 | Red Hat | 下面,我们基于[client-go](https://github.com/kubernetes/client-go),对Deployment升级镜像的步骤进行了定制,通过命令行传递一个Deployment的名字、应用容器名和新image名字的方式来升级。 [kubernetes-client-go-sample](https://github.com/rootsongjc/kubernetes-client-go-sample) 项目的 `main.go` 代码如下: ```go package main import ( "flag" "fmt" "os" "path/filepath" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) func main() { var kubeconfig *string if home := homeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") } else { kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } deploymentName := flag.String("deployment", "", "deployment name") imageName := flag.String("image", "", "new image name") appName := flag.String("app", "app", "application name") flag.Parse() if *deploymentName == "" { fmt.Println("You must specify the deployment name.") os.Exit(0) } if *imageName == "" { fmt.Println("You must specify the new image name.") os.Exit(0) } // use the current context in kubeconfig config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err.Error()) } // create the clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } deployment, err := clientset.AppsV1beta1().Deployments("default").Get(*deploymentName, metav1.GetOptions{}) if err != nil { panic(err.Error()) } if errors.IsNotFound(err) { fmt.Printf("Deployment not found\n") } else if statusError, isStatus := err.(*errors.StatusError); isStatus { fmt.Printf("Error getting deployment%v\n", statusError.ErrStatus.Message) } else if err != nil { panic(err.Error()) } else { fmt.Printf("Found deployment\n") name := deployment.GetName() fmt.Println("name ->", name) containers := &deployment.Spec.Template.Spec.Containers found := false for i := range *containers { c := *containers if c[i].Name == *appName { found = true fmt.Println("Old image ->", c[i].Image) fmt.Println("New image ->", *imageName) c[i].Image = *imageName } } if found == false { fmt.Println("The application container not exist in the deployment pods.") os.Exit(0) } _, err := clientset.AppsV1beta1().Deployments("default").Update(deployment) if err != nil { panic(err.Error()) } } } func homeDir() string { if h := os.Getenv("HOME"); h != "" { return h } return os.Getenv("USERPROFILE") // windows } ``` 我们使用`kubeconfig`文件认证连接Kubernetes集群,该文件默认的位置是`$HOME/.kube/config`。 该代码编译后可以直接在Kubernetes集群之外,任何一个可以连接到API server的机器上运行。 **编译运行** ```bash $ go get github.com/rootsongjc/kubernetes-client-go-sample $ cd $GOPATH/src/github.com/rootsongjc/kubernetes-client-go-sample $ go build main.go ``` 该命令的用法如下。 ```bash $ ./main -app string application name (default "app") -deployment string deployment name -image string new image name -kubeconfig string (optional) absolute path to the kubeconfig file (default "/Users/jimmy/.kube/config") ``` **使用不存在的image更新** ```bash $ ./main -deployment filebeat-test -image harbor-001.jimmysong.io/library/analytics-docker-test:Build_9 Found deployment name -> filebeat-test Old image -> harbor-001.jimmysong.io/library/analytics-docker-test:Build_8 New image -> harbor-001.jimmysong.io/library/analytics-docker-test:Build_9 ``` 查看Deployment的event。 ```bash $ kubectl describe deployment filebeat-test Name: filebeat-test Namespace: default CreationTimestamp: Fri, 19 May 2017 15:12:28 +0800 Labels: k8s-app=filebeat-test Selector: k8s-app=filebeat-test Replicas: 2 updated | 3 total | 2 available | 2 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 1 max unavailable, 1 max surge Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True ReplicaSetUpdated OldReplicaSets: filebeat-test-2365467882 (2/2 replicas created) NewReplicaSet: filebeat-test-2470325483 (2/2 replicas created) Events: FirstSeen LastSeen Count From SubObjectPath Type ReasoMessage --------- -------- ----- ---- ------------- -------- ------------ 2h 1m 3 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set filebeat-test-2365467882 to 2 1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set filebeat-test-2470325483 to 1 1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set filebeat-test-2470325483 to 2 ``` 可以看到老的ReplicaSet从3个replica减少到了2个,有2个使用新配置的replica不可用,目前可用的replica是2个。 这是因为我们指定的镜像不存在,查看Deployment的pod的状态。 ```bash $ kubectl get pods -l k8s-app=filebeat-test NAME READY STATUS RESTARTS AGE filebeat-test-2365467882-4zwx8 2/2 Running 0 33d filebeat-test-2365467882-rqskl 2/2 Running 0 33d filebeat-test-2470325483-6vjbw 1/2 ImagePullBackOff 0 4m filebeat-test-2470325483-gc14k 1/2 ImagePullBackOff 0 4m ``` 我们可以看到有两个pod正在拉取image。 **还原为原先的镜像** 将image设置为原来的镜像。 ```bash $ ./main -deployment filebeat-test -image harbor-001.jimmysong.io/library/analytics-docker-test:Build_8 Found deployment name -> filebeat-test Old image -> harbor-001.jimmysong.io/library/analytics-docker-test:Build_9 New image -> harbor-001.jimmysong.io/library/analytics-docker-test:Build_8 ``` 现在再查看Deployment的状态。 ```bash $ kubectl describe deployment filebeat-test Name: filebeat-test Namespace: default CreationTimestamp: Fri, 19 May 2017 15:12:28 +0800 Labels: k8s-app=filebeat-test Selector: k8s-app=filebeat-test Replicas: 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 1 max unavailable, 1 max surge Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: NewReplicaSet: filebeat-test-2365467882 (3/3 replicas created) Events: FirstSeen LastSeen Count From SubObjectPath Type ReasoMessage --------- -------- ----- ---- ------------- -------- ------------ 2h 8m 3 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set filebeat-test-2365467882 to 2 8m 8m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set filebeat-test-2470325483 to 1 8m 8m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set filebeat-test-2470325483 to 2 2h 1m 3 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set filebeat-test-2365467882 to 3 1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set filebeat-test-2470325483 to 0 ``` 可以看到available的replica个数恢复成3了。 其实在使用该命令的过程中,通过kubernetes dashboard的页面上查看Deployment的状态更直观,更加方便故障排查。 ![使用kubernetes dashboard进行故障排查](../images/kubernetes-client-go-sample-update.jpg) 这也是dashboard最大的优势,简单、直接、高效。