<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自己動手實現(xiàn)一個 kubectl exec

          共 7751字,需瀏覽 16分鐘

           ·

          2021-05-11 18:32

          在日常工作中kubectl exec可以說是非常高頻使用的,如果你想自己了解相關原理,不妨自己動手寫一個。

          知識儲備:

          • websocket 阮一峰這篇《WebSocket 教程- 阮一峰的網(wǎng)絡日志》寫的比較詳細。

          • kubectl exec 原理

            • https://itnext.io/how-it-works-kubectl-exec-e31325daa910

            • https://erkanerol.github.io/post/how-kubectl-exec-works/

          如果你英文閱讀能還可以,這兩篇文章從原理方面介紹了exec是如何工作的。

          了解了以上知識之后,接下來我們就開始動手吧。

          首先來初始化一下項目,這里使用go mod作為依賴管理工具。k8s的client-go對機器版本是有要求的,所以在初始化的時候最好去官方那邊找一下可用的版本。如果遇到mod/k8s.io/[email protected]+incompatible/kubernetes/scheme/register.go:22:2: unknown import path "k8s.io/api/admissionregistration /v1alpha1": cannot find module providing package k8s.io/api/admissionregistration/v1alpha1

          這種報錯,可以嘗試強制指定版本,這個也是從kubebuilder那里學到的。

          go mod init k8sdemo

          module k8sdemo

          go 1.13

          require (
                  github.com/gorilla/websocket v1.4.2
                  golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
                  k8s.io/api v0.17.2
                  k8s.io/apimachinery v0.17.2
                  k8s.io/client-go v0.17.2
          )

          client-go的example目錄也有相關對象的CURD示例,我們可以先從這里入手,先熟悉相關操作,可以看到首先從kuebconfig讀取配置,然后初始化各種client的一個集合,最后創(chuàng)建了一個deployment實例。

           config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
           if err != nil {
            panic(err)
           }
           clientset, err := kubernetes.NewForConfig(config)
           if err != nil {
            panic(err)
           }

           deploymentsClient := clientset.AppsV1().Deployments(apiv1.NamespaceDefault)

          接下來我們看一下kubectl exec 究竟發(fā)送了什么請求,可以看到關鍵在于exec?command=date&container=nginx&stdin=true&stdout=true&tty=true

          kubectl  exec nginx-8486565b79-4hb5t -it -v 10 bash

          curl -k -v -XPOST  -H "User-Agent: kubectl/v1.16.2 (linux/amd64) kubernetes/c97fe50" 
          -H "X-Stream-Protocol-Version: v4.channel.k8s.io" 
          -H "X-Stream-Protocol-Version: v3.channel.k8s.io" 
          -H "X-Stream-Protocol-Version: v2.channel.k8s.io" 
          -H "X-Stream-Protocol-Version: channel.k8s.io" 
          'https://192.168.2.2:6443/api/v1/namespaces/default/pods/nginx-8486565b79-4hb5t/exec
          ?command=date&container=nginx&stdin=true&stdout=true&tty=true'

          現(xiàn)在就開始做吧,從上面的URL可以看到exec 是屬于pod的資源,

          // 初始化pod所在的corev1資源組,發(fā)送請求
          // PodExecOptions struct 包括Container stdout stdout  Command 等結(jié)構
          // scheme.ParameterCodec 應該是pod 的GVK (GroupVersion & Kind)之類的
          req := clientset.CoreV1().RESTClient().Post().
            Resource("pods").
            Name("nginx-8486565b79-4hb5t").
            Namespace("default").
            SubResource("exec").
            VersionedParams(&corev1.PodExecOptions{
             Command: []string{"bash"},
             Stdin:   true,
             Stdout:  true,
             Stderr:  true,
             TTY:     false,
            }, scheme.ParameterCodec)

          // remotecommand 主要實現(xiàn)了http 轉(zhuǎn) SPDY 添加X-Stream-Protocol-Version相關header 并發(fā)送請求
           exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())

          // 建立鏈接之后從請求的sream中發(fā)送、讀取數(shù)據(jù)
           if err = exec.Stream(remotecommand.StreamOptions{
            Stdin:  os.Stdin,
            Stdout: os.Stdout,
            Stderr: os.Stderr,
            Tty:    false,
           }); err != nil {
            fmt.Print(err)
           }

          以上只實現(xiàn)了單個命令,實際上我們更多的是使用-it進入交互式終端,這個應該怎么做呢?

          // 這里引入了ssh包 來做終端響應 golang.org/x/crypto/ssh/termina

          // 檢查是不是終端
           if !terminal.IsTerminal(0) || !terminal.IsTerminal(1) {
             fmt.Errorf("stdin/stdout should be terminal")
           }
           // 這個應該是處理Ctrl + C 這種特殊鍵位
           oldState, err := terminal.MakeRaw(0)
           if err != nil {
             fmt.Println(err)
           }
           defer terminal.Restore(0, oldState)

           // 用IO讀寫替換 os stdout 
           screen := struct {
            io.Reader
            io.Writer
           }{os.Stdin, os.Stdout}

          完整示例

          package main

          import (
           "flag"
           "fmt"
           "io"
           "os"
           "path/filepath"

           "golang.org/x/crypto/ssh/terminal"
           corev1 "k8s.io/api/core/v1"
           "k8s.io/client-go/kubernetes"
           "k8s.io/client-go/kubernetes/scheme"
           "k8s.io/client-go/tools/clientcmd"
           "k8s.io/client-go/tools/remotecommand"
           "k8s.io/client-go/util/homedir"
          )

          func main() {

           var kubeconfig *string
           if home := homedir.HomeDir(); home != "" {
            kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube""vm"), "(optional) absolute path to the kubeconfig file")
           } else {
            kubeconfig = flag.String("kubeconfig""""absolute path to the kubeconfig file")
           }
           flag.Parse()

           config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
           if err != nil {
            panic(err)
           }
           clientset, err := kubernetes.NewForConfig(config)
           if err != nil {
            panic(err)
           }

           // 初始化pod所在的corev1資源組,發(fā)送請求
           // PodExecOptions struct 包括Container stdout stdout  Command 等結(jié)構
           // scheme.ParameterCodec 應該是pod 的GVK (GroupVersion & Kind)之類的
           req := clientset.CoreV1().RESTClient().Post().
            Resource("pods").
            Name("nginx-8486565b79-4hb5t").
            Namespace("default").
            SubResource("exec").
            VersionedParams(&corev1.PodExecOptions{
             Command: []string{"bash"},
             Stdin:   true,
             Stdout:  true,
             Stderr:  true,
             TTY:     false,
            }, scheme.ParameterCodec)

           // remotecommand 主要實現(xiàn)了http 轉(zhuǎn) SPDY 添加X-Stream-Protocol-Version相關header 并發(fā)送請求
           exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())

           // 檢查是不是終端
           if !terminal.IsTerminal(0) || !terminal.IsTerminal(1) {
             fmt.Errorf("stdin/stdout should be terminal")
           }
           // 這個應該是處理Ctrl + C 這種特殊鍵位
           oldState, err := terminal.MakeRaw(0)
           if err != nil {
             fmt.Println(err)
           }
           defer terminal.Restore(0, oldState)

           // 用IO讀寫替換 os stdout
           screen := struct {
            io.Reader
            io.Writer
           }{os.Stdin, os.Stdout}

           // 建立鏈接之后從請求的sream中發(fā)送、讀取數(shù)據(jù)
           if err = exec.Stream(remotecommand.StreamOptions{
            Stdin:  screen,
            Stdout: screen,
            Stderr: screen,
            Tty:    false,
           }); err != nil {
            fmt.Print(err)
           }
          }

          原文鏈接:https://vsxen.github.io/2020/06/20/kubectl-exec/



          K8S 進階訓練營


           點擊屏末  | 即刻學習
          瀏覽 199
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情乱伦图片 | 三级片男人天堂 | 欧美性做爰又大又粗又长 | 操操综合网 | 天天操天天插天天干 |