使用 Go 從零開發(fā)并發(fā)布一個(gè) Kubectl 插件
作者:KaliArch(薛磊),某 Cloud MSP 服務(wù)商產(chǎn)品負(fù)責(zé)人,熟悉企業(yè)級高可用/高并發(fā)架構(gòu),包括混合云架構(gòu)、異地災(zāi),熟練企業(yè) DevOPS 改造優(yōu)化,熟悉 Shell/Python/Go 等開發(fā)語言,熟悉 Kubernetes、 Docker、 云原生、微服務(wù)架構(gòu)等
前言
十年云計(jì)算浪潮下,DevOps、容器、微服務(wù)等技術(shù)飛速發(fā)展,云原生成為潮流。企業(yè)云化從“ON Cloud” 走向 “IN Cloud”,成為“新云原生企業(yè)”,新生能力與既有能力立而不破、有機(jī)協(xié)同,實(shí)現(xiàn)資源高效、應(yīng)用敏捷、業(yè)務(wù)智能、安全可信。整個(gè)云原生概念很大,細(xì)化到可能是我們在真實(shí)場景中遇到的一些小問題,本文就針對日常工作中遇到的自己的小需求,及解決思路方法,分享給大家。
背景
在我日常使用 kubectl 查看 K8s 資源的時(shí)候,想直接查看對應(yīng)資源的容器名稱和鏡像名稱,目前 kubectl 還不支持該選型,需要我們 describe 然后來查看,對于集群自己比較多,不是很方便,因此萌生了自己開發(fā) kubectl 插件來實(shí)現(xiàn)該功能。
相關(guān)技術(shù)
首先需要調(diào)用 Kubernetes 需要使用 client-go 項(xiàng)目來實(shí)現(xiàn)對 Kubernetes 資源的獲取,對于插件使用 Golang 語言開發(fā),因?yàn)槭强蛻舳藞?zhí)行,為了方便集成到及命令行工具,采用和 K8s 相同的命令行腳手架工具 Cobra,最后將其開源發(fā)布到 GitHub。
Golang
在云原生開發(fā)中,Google 非常多的開源項(xiàng)目都是使用 Golang 開發(fā),其跨平臺編譯后可以發(fā)布到多個(gè)平臺,我們開發(fā)的插件基于 Golang,后續(xù)也就支持多平臺使用。
Cobra
Cobra 是一個(gè)命令行程序庫,其是一個(gè)用來編寫命令行的神器,提供了一個(gè)腳手架,用于快速生成基于 Cobra 應(yīng)用程序框架。我們可以利用 Cobra 快速的去開發(fā)出我們想要的命令行工具,非常的方便快捷。
Client-go
在 K8s 運(yùn)維中,我們可以使用 kubectl、客戶端庫或者 REST 請求來訪問 K8s API。而實(shí)際上,無論是 kubectl 還是客戶端庫,都是封裝了 REST 請求的工具。client-go 作為一個(gè)客戶端庫,能夠調(diào)用 K8s API,實(shí)現(xiàn)對 K8s 集群中資源對象(包括 deployment、service、Ingress、ReplicaSet、Pod、Namespace、Node 等)的增刪改查等操作。
krew
Krew 是 類似于系統(tǒng)的 apt、dnf 或者 brew 的 kubectl 插件包管理工具,利用其可以輕松的完成 kubectl 插件的全上面周期管理,包括搜索、下載、卸載等。
kubectl 其工具已經(jīng)比較完善,但是對于一些個(gè)性化的命令,其宗旨是希望開發(fā)者能以獨(dú)立而緊張形式發(fā)布自定義的 kubectl 子命令,插件的開發(fā)語言不限,需要將最終的腳步或二進(jìn)制可執(zhí)行程序以 kubectl- 的前綴命名,然后放到 PATH 中即可,可以使用 kubectl plugin list 查看目前已經(jīng)安裝的插件。
Github 發(fā)布相關(guān)工具
GitHub Action
如果你需要某個(gè) Action,不必自己寫復(fù)雜的腳本,直接引用他人寫好的 Action 即可,整個(gè)持續(xù)集成過程,就變成了一個(gè) Actions 的組合。Github[1]是做了一個(gè)商店的功能。這樣大家就可以自己定義自己的 Action,然后方便別人復(fù)用。同時(shí)也可以統(tǒng)一自己的或者組織在構(gòu)建過程中的一些公共流程。
gorelease
GoReleaser 采用 Golang 開發(fā),是一款用于 Golang 項(xiàng)目的自動(dòng)發(fā)布工具。無需太多配置,只需要幾行命令就可以輕松實(shí)現(xiàn)跨平臺的包編譯、打包和發(fā)布到 Github、Gitlab 等版本倉庫種。
插件規(guī)劃
插件命名為:kubectl-img 目前僅簡單實(shí)現(xiàn)一個(gè) image 命令,用于查看不同資源對象 (deployments/daemonsets/statefulsets/jobs/cronjobs) 的名稱,和對應(yīng)容器名稱,鏡像名稱。 支持 JSON 格式輸出。 最后將其作為 krew 插件使用。 可以直接根據(jù)名稱空間來進(jìn)行查看對應(yīng)資源。
開發(fā)
項(xiàng)目初始化
安裝 Cobra
在開發(fā)環(huán)境中安裝 Cobra,后去基于改命令行工具來生成項(xiàng)目腳手架,K8s 中很多組建也是用的改框架來生成的。
go?get?-v?github.com/spf13/cobra/cobra
初始化項(xiàng)目
$?cobra?init?--pkg-name?kubectl-img
$?ls
LICENSE?cmd?????main.go
$?tree
├──?LICENSE
├──?cmd
│???└──?root.go
└──?main.go
創(chuàng)建 go mod,下載相關(guān)包
go?mod?init?github.com/redhatxl/kubectl-img
增加子命令
增加一個(gè)子命令 image,在此為我們的插件添加子命令。
$?cobra?add?image
添加參數(shù)
通過子命令+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())
}
實(shí)現(xiàn) image 命令
注冊子命令,并修改命令使用說明。
var?imageCmd?=?&cobra.Command{
?Use:???"image",
?Short:?"show?resource?image",
?Long:??`show?k8s?resource?image`,
?RunE:??image,
}
func?init()?{
?rootCmd.AddCommand(imageCmd)
}
初始化 clientset
由于需要調(diào)用 K8s 資源,在此我們使用 Client-go 中的 ClientSet 來根據(jù)用戶輸入的不同 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
}
實(shí)現(xiàn)查看資源對象
利用反射實(shí)現(xiàn)根據(jù)不同資源類型查看具體對應(yīng)資源鏡像及鏡像名稱功能。
func?image(cmd?*cobra.Command,?args?[]string)?error?{
?clientSet?:=?kube.ClientSet(KubernetesConfigFlags)
?ns,?_?:=?rootCmd.Flags().GetString("namespace")
?//?生命一個(gè)全局資源列表
?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)
????}
???}
實(shí)現(xiàn)輸出
利用 Table 來對結(jié)果進(jìn)行輸出,同樣擴(kuò)展 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
}
最終項(xiàng)目結(jié)構(gòu):

集成 krew
需要將最終的腳步或二進(jìn)制可執(zhí)行程序以 kubectl- 的前綴命名,然后放到 PATH 中即可,可以使用 kubectl plugin list 查看目前已經(jīng)安裝的插件。
$?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
#?將自己開發(fā)的插件重新命名為kubectl-img放到可執(zhí)行路基下
$?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

開源發(fā)布
完成代碼編寫后,為了更多朋友學(xué)習(xí)交流,將其發(fā)布到 GitHub 上。
Github Action
在項(xiàng)目根目錄下創(chuàng)建 .github/workflows/ci.yml,文件內(nèi)容如下
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 項(xiàng)目報(bào)告:https://goreportcard.com/

GoReleaser
對于 Golang 項(xiàng)目,可以使用 GoReleaser 來發(fā)布一個(gè)漂亮的 Release。
由于使用的的 macOS ,這里使用 brew 來安裝:
brew?install?goreleaser
在項(xiàng)目根目錄生成 .goreleaser.yml 配置:
goreleaser?init
配置好了以后要記得往 .gitignore 加上 dist,因?yàn)?goreleaser 會默認(rèn)把編譯編譯好的文件輸出到 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 ,可以在這里申請,申請好之后運(yùn)行下面的命令配置 GITHUB_TOKEN
export?GITHUB_TOKEN=<YOUR_TOKEN>
確保沒有問題,那么就可以操作 Git 和 GoReleaser 來發(fā)布 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
查看發(fā)布好的 Release

在項(xiàng)目 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 展示神器
這里介紹一個(gè)展示 Badges 的神器:https://shields.io/ 。這個(gè)網(wǎng)站提供各種各樣的 Badges ,如果你愿意,完全可以把你的 GitHub README.md 填滿,有興趣的同學(xué)可以自取。

總結(jié)
目前實(shí)現(xiàn)的比較簡單,以此來拋磚引玉的功能,后期可以進(jìn)行更多功能或其他插件的開發(fā),自己動(dòng)手豐衣足食。從技術(shù)角度看,以容器、微服務(wù)以及動(dòng)態(tài)編排為代表的云原生技術(shù)蓬勃發(fā)展,成為賦能業(yè)務(wù)創(chuàng)新的重要推動(dòng)力,并已經(jīng)應(yīng)用到企業(yè)核心業(yè)務(wù)。從市場角度看,云原生技術(shù)已在金融、制造、互聯(lián)網(wǎng)等多個(gè)行業(yè)得到廣泛驗(yàn)證,支持的業(yè)務(wù)場景也愈加豐富,行業(yè)生態(tài)日漸繁榮。
本文從日常工作中最小的切入點(diǎn),從 0 到 1 實(shí)戰(zhàn) K8s 插件開發(fā)并開源的思路及過程,希望相關(guān)同學(xué)可以一塊交流學(xué)習(xí)。最近由于業(yè)務(wù)開發(fā) Operator,也在研讀 K8s 控制器相關(guān)代碼,并做了一些自己的筆記,有興趣的可以一塊交流學(xué)習(xí),博客地址 :kaliarch blog[2]。
其他
kubectl-img[3]
引用鏈接
Github: https://link.zhihu.com/?target=https%3A//github.com/
[2]kaliarch blog: https://redhatxl.github.io/cloud-native/develop/01-k8s%20%E5%BC%80%E5%8F%91%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5%E4%BB%8B%E7%BB%8D/
[3]kubectl-img: https://github.com/redhatxl/kubectl-img
