乐趣区

关于云计算:使用-Go-从零开发并发布一个-Kubectl-插件

作者:KaliArch(薛磊),某 Cloud MSP 服务商产品负责人,相熟企业级高可用 / 高并发架构,包含混合云架构、异地灾,纯熟企业 DevOPS 革新优化,相熟 Shell/Python/Go 等开发语言,相熟 Kubernetes、Docker、云原生、微服务架构等。

前言

十年云计算浪潮下,DevOps、容器、微服务等技术飞速发展,云原生成为潮流。企业云化从“ON Cloud”走向“IN Cloud”,成为“新云原生企业”,新生能力与既有能力立而不破、有机协同,实现资源高效、利用麻利、业务智能、平安可信。整个云原生概念很大,细化到可能是咱们在实在场景中遇到的一些小问题,本文就针对日常工作中遇到的本人的小需要,及解决思路办法,分享给大家。

背景

在我日常应用 kubectl 查看 K8s 资源的时候,想间接查看对应资源的容器名称和镜像名称,目前 kubectl 还不反对该选型,须要咱们 describe 而后来查看,对于集群本人比拟多,不是很不便,因而萌发了本人开发 kubectl 插件来实现该性能。

相干技术

首先须要调用 Kubernetes 须要应用 client-go 我的项目来实现对 Kubernetes 资源的获取,对于插件应用 Golang 语言开发,因为是客户端执行,为了不便集成到及命令行工具,采纳和 K8s 雷同的命令行脚手架工具 Cobra,最初将其开源公布到 GitHub。

Golang

在云原生开发中,Google 十分多的开源我的项目都是应用 Golang 开发,其跨平台编译后能够公布到多个平台,咱们开发的插件基于 Golang,后续也就反对多平台应用。

Cobra

Cobra 是一个命令行程序库,其是一个用来编写命令行的神器,提供了一个脚手架,用于疾速生成基于 Cobra 应用程序框架。咱们能够利用 Cobra 疾速的去开发出咱们想要的命令行工具,十分的方便快捷。

Client-go

在 K8s 运维中,咱们能够应用 kubectl、客户端库或者 REST 申请来拜访 K8s API。而实际上,无论是 kubectl 还是客户端库,都是封装了 REST 申请的工具。client-go 作为一个客户端库,可能调用 K8s API,实现对 K8s 集群中资源对象(包含 deployment、service、Ingress、ReplicaSet、Pod、Namespace、Node 等)的增删改查等操作。

krew

Krew 是 相似于零碎的 apt、dnf 或者 brew 的 kubectl 插件包管理工具,利用其能够轻松的实现 kubectl 插件的全下面周期治理,包含搜寻、下载、卸载等。

kubectl 其工具曾经比较完善,然而对于一些个性化的命令,其主旨是心愿开发者能以独立而缓和模式公布自定义的 kubectl 子命令,插件的开发语言不限,须要将最终的脚步或二进制可执行程序以 kubectl- 的前缀命名,而后放到 PATH 中即可,能够应用 kubectl plugin list 查看目前曾经装置的插件。

Github 公布相干工具

  • GitHub Action

如果你须要某个 Action,不用本人写简单的脚本,间接援用别人写好的 Action 即可,整个继续集成过程,就变成了一个 Actions 的组合。Github 是做了一个商店的性能。这样大家就能够本人定义本人的 Action,而后不便他人复用。同时也能够对立本人的或者组织在构建过程中的一些公共流程。

  • gorelease

GoReleaser 采纳 Golang 开发,是一款用于 Golang 我的项目的主动公布工具。无需太多配置,只须要几行命令就能够轻松实现跨平台的包编译、打包和公布到 Github、Gitlab 等版本仓库种。

插件布局

  • 插件命名为:kubectl-img
  • 目前仅简略实现一个 image 命令,用于查看不同资源对象 (deployments/daemonsets/statefulsets/jobs/cronjobs) 的名称,和对应容器名称,镜像名称。
  • 反对 JSON 格局输入。
  • 最初将其作为 krew 插件应用。
  • 能够间接依据名称空间来进行查看对应资源。

开发

我的项目初始化

  • 装置 Cobra

在开发环境中装置 Cobra,后去基于改命令行工具来生成我的项目脚手架,K8s 中很多组建也是用的改框架来生成的。

go get -v github.com/spf13/cobra/cobra
  • 初始化我的项目
$ cobra init --pkg-name kubectl-img
$ ls
LICENSE cmd     main.go
$ tree
├── LICENSE
├── cmd
│   └── root.go
└── main.go
  • 创立 go mod,下载相干包
go mod init github.com/redhatxl/kubectl-img

减少子命令

减少一个子命令 image,在此为咱们的插件增加子命令。

$ cobra add image

增加参数

通过子命令 +flag 模式,显示不同的资源镜像名称。

func Execute() {cobra.CheckErr(rootCmd.Execute())
}

func init() {KubernetesConfigFlags = genericclioptions.NewConfigFlags(true)
    imageCmd.Flags().BoolP("deployments", "d", false, "show deployments image")
    imageCmd.Flags().BoolP("daemonsets", "e", false, "show daemonsets image")
    imageCmd.Flags().BoolP("statefulsets", "f", false, "show statefulsets image")
    imageCmd.Flags().BoolP("jobs", "o", false, "show jobs image")
    imageCmd.Flags().BoolP("cronjobs", "b", false, "show cronjobs image")
    imageCmd.Flags().BoolP("json", "j", false, "show json format")
    KubernetesConfigFlags.AddFlags(rootCmd.PersistentFlags())
}

实现 image 命令

注册子命令,并批改命令应用阐明。

var imageCmd = &cobra.Command{
    Use:   "image",
    Short: "show resource image",
    Long:  `show k8s resource image`,
    RunE:  image,
}

func init() {rootCmd.AddCommand(imageCmd)
}

初始化 clientset

因为须要调用 K8s 资源,在此咱们应用 Client-go 中的 ClientSet 来依据用户输出的不同 flag 来获取不同的资源镜像。

// ClientSet k8s clientset
func ClientSet(configFlags *genericclioptions.ConfigFlags) *kubernetes.Clientset {config, err := configFlags.ToRESTConfig()
    if err != nil {panic("kube config load error")
    }
    clientSet, err := kubernetes.NewForConfig(config)
    if err != nil {panic("gen kube config error")
    }
    return clientSet
}

实现查看资源对象

利用反射实现依据不同资源类型查看具体对应资源镜像及镜像名称性能。

func image(cmd *cobra.Command, args []string) error {clientSet := kube.ClientSet(KubernetesConfigFlags)
    ns, _ := rootCmd.Flags().GetString("namespace")
    // 生命一个全局资源列表
    var rList []interface{}

    if flag, _ := cmd.Flags().GetBool("deployments"); flag {deployList, err := clientSet.AppsV1().Deployments(ns).List(context.Background(), v1.ListOptions{})
        if err != nil {fmt.Printf("list deployments error: %s", err.Error())
        }
        rList = append(rList, deployList)
    }
  ...
      deployMapList := make([]map[string]string, 0)
    for i := 0; i < len(rList); i++ {switch t := rList[i].(type) {
        case *kv1.DeploymentList:
            for k := 0; k < len(t.Items); k++ {for j := 0; j < len(t.Items[k].Spec.Template.Spec.Containers); j++ {deployMap := make(map[string]string)
                    deployMap["NAMESPACE"] = ns
                    deployMap["TYPE"] = "deployment"
                    deployMap["RESOURCE_NAME"] = t.Items[k].GetName()
                    deployMap["CONTAINER_NAME"] = t.Items[k].Spec.Template.Spec.Containers[j].Name
                    deployMap["IMAGE"] = t.Items[k].Spec.Template.Spec.Containers[j].Image
                    deployMapList = append(deployMapList, deployMap)
                }
            }

实现输入

利用 Table 来对后果进行输入,同样扩大 JSON 输入

func GenTable(mapList []map[string]string) *table.Table {t, err := gotable.Create(title...)
    if err != nil {fmt.Printf("create table error: %s", err.Error())
        return nil
    }
    t.AddRows(mapList)
    return t
}

最终我的项目构造:

集成 krew

须要将最终的脚步或二进制可执行程序以 kubectl- 的前缀命名,而后放到 PATH 中即可,能够应用 kubectl plugin list 查看目前曾经装置的插件。

$ kubectl plugin list
The following compatible plugins are available:=
/usr/local/bin/kubectl-debug
  - warning: kubectl-debug overwrites existing command: "kubectl debug"
/usr/local/bin/kubectl-v1.10.11
/usr/local/bin/kubectl-v1.20.0
/Users/xuel/.krew/bin/kubectl-df_pv
/Users/xuel/.krew/bin/kubectl-krew

# 将本人开发的插件重新命名为 kubectl-img 放到可执行路基下
$ cp kubectl-img /Users/xuel/.krew/bin/kubectl-img

$ kubectl plugin list
The following compatible plugins are available:=
/usr/local/bin/kubectl-debug
  - warning: kubectl-debug overwrites existing command: "kubectl debug"
/usr/local/bin/kubectl-v1.10.11
/usr/local/bin/kubectl-v1.20.0
/Users/xuel/.krew/bin/kubectl-df_pv
/Users/xuel/.krew/bin/kubectl-krew
/Users/xuel/.krew/bin/kubectl-img

应用

$ kubectl img image -h
show k8s resource image

Usage:
  kubectl-img image [flags]

Flags:
  -b, --cronjobs       show cronjobs image
  -e, --daemonsets     show daemonsets image
  -d, --deployments    show deployments image
  -h, --help           help for image
  -o, --jobs           show jobs image
  -j, --json           show json format
  -f, --statefulsets   show statefulsets image

Global Flags:
      --as string                      Username to impersonate for the operation
      --as-group stringArray           Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
      --cache-dir string               Default cache directory (default "/Users/xuel/.kube/cache")
      --certificate-authority string   Path to a cert file for the certificate authority
      --client-certificate string      Path to a client certificate file for TLS
      --client-key string              Path to a client key file for TLS
      --cluster string                 The name of the kubeconfig cluster to use
      --context string                 The name of the kubeconfig context to use
      --insecure-skip-tls-verify       If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
      --kubeconfig string              Path to the kubeconfig file to use for CLI requests.
  -n, --namespace string               If present, the namespace scope for this CLI request
      --request-timeout string         The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default"0")
  -s, --server string                  The address and port of the Kubernetes API server
      --tls-server-name string         Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used
      --token string                   Bearer token for authentication to the API server
      --user string                    The name of the kubeconfig user to use
  • 查看资源
# View the images of all deployments of the entire kubernetes cluster
kubectl img image --deployments
# View the images of all deployments of the entire kubernetes cluster
kubectl img image --deployments -n default

  • 查看所有资源
# view all resource
kubectl img image -bedof

  • JSON 格局输入
# Table display is used by default
kubectl img image --deployments -n default -j

开源公布

实现代码编写后,为了更多敌人学习交换,将其公布到 GitHub 上。

Github Action

在我的项目根目录下创立 .github/workflows/ci.yml,文件内容如下

name: ci
on:
  push:
  pull_request:
jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Setup Go
        uses: actions/setup-go@v1
        with:
          go-version: 1.16
      - name: GoReleaser
        uses: goreleaser/goreleaser-action@v1
        with:
          version: latest
          args: release --snapshot --rm-dist

GO Report Card

增加 Go 我的项目报告:https://goreportcard.com/

GoReleaser

对于 Golang 我的项目,能够应用 GoReleaser 来公布一个丑陋的 Release。

因为应用的的 macOS,这里应用 brew 来装置:

brew install goreleaser

在我的项目根目录生成 .goreleaser.yml 配置:

goreleaser init

配置好了当前要记得往 .gitignore 加上 dist,因为 goreleaser 会默认把编译编译好的文件输入到 dist 目录中。

# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
  hooks:
    # You may remove this if you don't use go modules.
    - go mod tidy
    # you may remove this if you don't need go generate
    - go generate ./...
builds:
  - env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
archives:
  - replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{incpatch .Version}}-next"
changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'

project_name: kubectl-img

GoReleaser 配置好后,能够先编译测试一下:

留神: 首次应用 GoReleaser 要配置 GITHUB_TOKEN,能够在这里申请,申请好之后运行上面的命令配置 GITHUB_TOKEN

export GITHUB_TOKEN=<YOUR_TOKEN>

确保没有问题,那么就能够操作 Git 和 GoReleaser 来公布 Release 了。

git add .
git commit -m "add goreleaser"
git tag -a v0.0.2 -m "First release"
git push origin main
git push origin v0.0.2

全副搞定后,一行命令腾飞:

$ goreleaser release --rm-dist
   • releasing...
   • loading config file       file=.goreleaser.yaml
   • loading environment variables
   • getting and validating git state
      • building...               commit=98703b3b9d9ac7f4661c5669c1e164d2cf3675d2 latest tag=v1.0.0
   • parsing tag
   • running before hooks
      • running                   hook=go mod tidy
      • running                   hook=go generate ./...
   • setting defaults
      • DEPRECATED: skipped windows/arm64 build on Go < 1.17 for compatibility, check https://goreleaser.com/deprecations/#builds-for-windowsarm64 for more info.
   • checking distribution directory
      • --rm-dist is set, cleaning it up
   • loading go mod information
   • build prerequisites
   • writing effective config file
      • writing                   config=dist/config.yaml
   • generating changelog
      • writing                   changelog=dist/CHANGELOG.md
   • building binaries
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_linux_386/kubectl-img
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_linux_amd64/kubectl-img
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_darwin_arm64/kubectl-img
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_linux_arm64/kubectl-img
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_windows_amd64/kubectl-img.exe
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_windows_386/kubectl-img.exe
      • building                  binary=/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/kubectl-img/dist/kubectl-img_darwin_amd64/kubectl-img
   • archives
      • creating                  archive=dist/kubectl-img_1.0.0_Linux_i386.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Darwin_x86_64.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Linux_x86_64.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Windows_x86_64.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Linux_arm64.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Windows_i386.tar.gz
      • creating                  archive=dist/kubectl-img_1.0.0_Darwin_arm64.tar.gz
   • calculating checksums
   • storing release metadata
      • writing                   file=dist/artifacts.json
      • writing                   file=dist/metadata.json
   • publishing
      • scm releases
         • creating or updating release repo=redhatxl/kubectl-img tag=v1.0.0
         • release updated           url=https://github.com/redhatxl/kubectl-img/releases/tag/v1.0.0
         • uploading to release      file=dist/checksums.txt name=checksums.txt
         • uploading to release      file=dist/kubectl-img_1.0.0_Linux_i386.tar.gz name=kubectl-img_1.0.0_Linux_i386.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Linux_x86_64.tar.gz name=kubectl-img_1.0.0_Linux_x86_64.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Windows_i386.tar.gz name=kubectl-img_1.0.0_Windows_i386.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Linux_arm64.tar.gz name=kubectl-img_1.0.0_Linux_arm64.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Darwin_x86_64.tar.gz name=kubectl-img_1.0.0_Darwin_x86_64.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Windows_x86_64.tar.gz name=kubectl-img_1.0.0_Windows_x86_64.tar.gz
         • uploading to release      file=dist/kubectl-img_1.0.0_Darwin_arm64.tar.gz name=kubectl-img_1.0.0_Darwin_arm64.tar.gz
   • announcing
   • release succeeded after 183.24s

查看公布好的 Release

在我的项目 README 中增加不同平台的装置形式。

Linux

export release=v1.0.0
curl -L -o kubectl-img.tar.gz https://github.com/redhatxl/kubectl-img/releases/download/${release}/kubectl-img_${release}_Linux_arm64.tar.gz
tar -xvf kubectl-img.tar.gz
cp kubectl-img /usr/local/bin/kubectl-img
# use kubectl krew
cp kubectl-img $HOME/.krew/bin

OSX

export release=v1.0.0
curl -L -o kubectl-img.tar.gz https://github.com/redhatxl/kubectl-img/releases/download/${release}/kubectl-img_${release}_Darwin_x86_64.tar.gz
tar -xvf kubectl-img.tar.gz
mv kubectl-img /usr/local/bin/kubectl-img
# use kubectl krew
cp kubectl-img $HOME/.krew/bin

Windows

In PowerShell v5+

$url = "https://github.com/redhatxl/kubectl-img/releases/download/v1.0.0/kubectl-img_1.0.0_Windows_x86_64.tar.gz"
$output = "$PSScriptRoot\kubectl-img.zip"

Invoke-WebRequest -Uri $url -OutFile $output
Expand-Archive "$PSScriptRoot\kubectl-img.zip" -DestinationPath "$PSScriptRoot\kubectl-img"

Badges 展现神器

这里介绍一个展现 Badges 的神器:https://shields.io/。这个网站提供各种各样的 Badges,如果你违心,齐全能够把你的 GitHub README.md 填满,有趣味的同学能够自取。

总结

目前实现的比较简单,以此来抛砖引玉的性能,前期能够进行更多功能或其余插件的开发,本人入手饥寒交迫。从技术角度看,以容器、微服务以及动静编排为代表的云原生技术蓬勃发展,成为赋能业务翻新的重要推动力,并曾经利用到企业外围业务。从市场角度看,云原生技术已在金融、制作、互联网等多个行业失去宽泛验证,反对的业务场景也更加丰盛,行业生态日渐凋敝。

本文从日常工作中最小的切入点,从 0 到 1 实战 K8s 插件开发并开源的思路及过程,心愿相干同学能够一块交流学习。最近因为业务开发 Operator,也在研读 K8s 控制器相干代码,并做了一些本人的笔记,有趣味的能够一块交流学习,博客地址 :kaliarch blog。

其余

  • kubectl-img

本文由博客一文多发平台 OpenWrite 公布!

退出移动版