标签 Shell 下的文章

Go标准库http与fasthttp服务端性能比较

本文永久链接 – https://tonybai.com/2021/04/25/server-side-performance-nethttp-vs-fasthttp

1. 背景

Go初学者学习Go时,在编写了经典的“hello, world”程序之后,可能会迫不及待的体验一下Go强大的标准库,比如:用几行代码写一个像下面示例这样拥有完整功能的web server:

// 来自https://tip.golang.org/pkg/net/http/#example_ListenAndServe
package main

import (
    "io"
    "log"
    "net/http"
)

func main() {
    helloHandler := func(w http.ResponseWriter, req *http.Request) {
        io.WriteString(w, "Hello, world!\n")
    }
    http.HandleFunc("/hello", helloHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

go net/http包是一个比较均衡的通用实现,能满足大多数gopher 90%以上场景的需要,并且具有如下优点:

  • 标准库包,无需引入任何第三方依赖;
  • 对http规范的满足度较好;
  • 无需做任何优化,即可获得相对较高的性能;
  • 支持HTTP代理;
  • 支持HTTPS;
  • 无缝支持HTTP/2。

不过也正是因为http包的“均衡”通用实现,在一些对性能要求严格的领域,net/http的性能可能无法胜任,也没有太多的调优空间。这时我们会将眼光转移到其他第三方的http服务端框架实现上。

而在第三方http服务端框架中,一个“行如其名”的框架fasthttp被提及和采纳的较多,fasthttp官网宣称其性能是net/http的十倍(基于go test benchmark的测试结果)。

fasthttp采用了许多性能优化上的最佳实践,尤其是在内存对象的重用上,大量使用sync.Pool以降低对Go GC的压力。

那么在真实环境中,到底fasthttp能比net/http快多少呢?恰好手里有两台性能还不错的服务器可用,在本文中我们就在这个真实环境下看看他们的实际性能。

2. 性能测试

我们分别用net/http和fasthttp实现两个几乎“零业务”的被测程序:

  • nethttp:
// github.com/bigwhite/experiments/blob/master/http-benchmark/nethttp/main.go
package main

import (
    _ "expvar"
    "log"
    "net/http"
    _ "net/http/pprof"
    "runtime"
    "time"
)

func main() {
    go func() {
        for {
            log.Println("当前routine数量:", runtime.NumGoroutine())
            time.Sleep(time.Second)
        }
    }()

    http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, Go!"))
    }))

    log.Fatal(http.ListenAndServe(":8080", nil))
}
  • fasthttp:
// github.com/bigwhite/experiments/blob/master/http-benchmark/fasthttp/main.go

package main

import (
    "fmt"
    "log"
    "net/http"
    "runtime"
    "time"

    _ "expvar"

    _ "net/http/pprof"

    "github.com/valyala/fasthttp"
)

type HelloGoHandler struct {
}

func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
    fmt.Fprintln(ctx, "Hello, Go!")
}

func main() {
    go func() {
        http.ListenAndServe(":6060", nil)
    }()

    go func() {
        for {
            log.Println("当前routine数量:", runtime.NumGoroutine())
            time.Sleep(time.Second)
        }
    }()

    s := &fasthttp.Server{
        Handler: fastHTTPHandler,
    }
    s.ListenAndServe(":8081")
}

对被测目标实施压力测试的客户端,我们基于hey这个http压测工具进行,为了方便调整压力水平,我们将hey“包裹”在下面这个shell脚本中(仅适于在linux上运行):

// github.com/bigwhite/experiments/blob/master/http-benchmark/client/http_client_load.sh

# ./http_client_load.sh 3 10000 10 GET http://10.10.195.181:8080
echo "$0 task_num count_per_hey conn_per_hey method url"
task_num=$1
count_per_hey=$2
conn_per_hey=$3
method=$4
url=$5

start=$(date +%s%N)
for((i=1; i<=$task_num; i++)); do {
    tm=$(date +%T.%N)
        echo "$tm: task $i start"
    hey -n $count_per_hey -c $conn_per_hey -m $method $url > hey_$i.log
    tm=$(date +%T.%N)
        echo "$tm: task $i done"
} & done
wait
end=$(date +%s%N)

count=$(( $task_num * $count_per_hey ))
runtime_ns=$(( $end - $start ))
runtime=`echo "scale=2; $runtime_ns / 1000000000" | bc`
echo "runtime: "$runtime
speed=`echo "scale=2; $count / $runtime" | bc`
echo "speed: "$speed

该脚本的执行示例如下:

bash http_client_load.sh 8 1000000 200 GET http://10.10.195.134:8080
http_client_load.sh task_num count_per_hey conn_per_hey method url
16:58:09.146948690: task 1 start
16:58:09.147235080: task 2 start
16:58:09.147290430: task 3 start
16:58:09.147740230: task 4 start
16:58:09.147896010: task 5 start
16:58:09.148314900: task 6 start
16:58:09.148446030: task 7 start
16:58:09.148930840: task 8 start
16:58:45.001080740: task 3 done
16:58:45.241903500: task 8 done
16:58:45.261501940: task 1 done
16:58:50.032383770: task 4 done
16:58:50.985076450: task 7 done
16:58:51.269099430: task 5 done
16:58:52.008164010: task 6 done
16:58:52.166402430: task 2 done
runtime: 43.02
speed: 185960.01

从传入的参数来看,该脚本并行启动了8个task(一个task启动一个hey),每个task向http://10.10.195.134:8080建立200个并发连接,并发送100w http GET请求。

我们使用两台服务器分别放置被测目标程序和压力工具脚本:

  • 目标程序所在服务器:10.10.195.181(物理机,Intel x86-64 CPU,40核,128G内存, CentOs 7.6)
$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core) 

$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                40
On-line CPU(s) list:   0-39
Thread(s) per core:    2
Core(s) per socket:    10
座:                 2
NUMA 节点:         2
厂商 ID:           GenuineIntel
CPU 系列:          6
型号:              85
型号名称:        Intel(R) Xeon(R) Silver 4114 CPU @ 2.20GHz
步进:              4
CPU MHz:             800.000
CPU max MHz:           2201.0000
CPU min MHz:           800.0000
BogoMIPS:            4400.00
虚拟化:           VT-x
L1d 缓存:          32K
L1i 缓存:          32K
L2 缓存:           1024K
L3 缓存:           14080K
NUMA 节点0 CPU:    0-9,20-29
NUMA 节点1 CPU:    10-19,30-39
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_pt ssbd mba ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts pku ospke spec_ctrl intel_stibp flush_l1d

  • 压力工具所在服务器:10.10.195.133(物理机,鲲鹏arm64 cpu,96核,80G内存, CentOs 7.9)
# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (AltArch)

# lscpu
Architecture:          aarch64
Byte Order:            Little Endian
CPU(s):                96
On-line CPU(s) list:   0-95
Thread(s) per core:    1
Core(s) per socket:    48
座:                 2
NUMA 节点:         4
型号:              0
CPU max MHz:           2600.0000
CPU min MHz:           200.0000
BogoMIPS:            200.00
L1d 缓存:          64K
L1i 缓存:          64K
L2 缓存:           512K
L3 缓存:           49152K
NUMA 节点0 CPU:    0-23
NUMA 节点1 CPU:    24-47
NUMA 节点2 CPU:    48-71
NUMA 节点3 CPU:    72-95
Flags:                 fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma dcpop asimddp asimdfhm

我用dstat监控被测目标所在主机资源占用情况(dstat -tcdngym),尤其是cpu负荷;通过expvarmon监控memstats,由于没有业务,内存占用很少;通过go tool pprof查看目标程序中对各类资源消耗情况的排名。

下面是多次测试后制作的一个数据表格:


图:测试数据

3. 对结果的简要分析

受特定场景、测试工具及脚本精确性以及压力测试环境的影响,上面的测试结果有一定局限,但却真实反映了被测目标的性能趋势。我们看到在给予同样压力的情况下,fasthttp并没有10倍于net http的性能,甚至在这样一个特定的场景下,两倍于net/http的性能都没有达到:我们看到在目标主机cpu资源消耗接近70%的几个用例中,fasthttp的性能仅比net/http高出30%~70%左右。

那么为什么fasthttp的性能未及预期呢?要回答这个问题,那就要看看net/http和fasthttp各自的实现原理了!我们先来看看net/http的工作原理示意图:


图:nethttp工作原理示意图

http包作为server端的原理很简单,那就是accept到一个连接(conn)之后,将这个conn甩给一个worker goroutine去处理,后者一直存在,直到该conn的生命周期结束:即连接关闭。

下面是fasthttp的工作原理示意图:


图:fasthttp工作原理示意图

而fasthttp设计了一套机制,目的是尽量复用goroutine,而不是每次都创建新的goroutine。fasthttp的Server accept一个conn之后,会尝试从workerpool中的ready切片中取出一个channel,该channel与某个worker goroutine一一对应。一旦取出channel,就会将accept到的conn写到该channel里,而channel另一端的worker goroutine就会处理该conn上的数据读写。当处理完该conn后,该worker goroutine不会退出,而是会将自己对应的那个channel重新放回workerpool中的ready切片中,等待这下一次被取出

fasthttp的goroutine复用策略初衷很好,但在这里的测试场景下效果不明显,从测试结果便可看得出来,在相同的客户端并发和压力下,net/http使用的goroutine数量与fasthttp相差无几。这是由测试模型导致的:在我们这个测试中,每个task中的hey都会向被测目标发起固定数量的长连接(keep-alive),然后在每条连接上发起“饱和”请求。这样fasthttp workerpool中的goroutine一旦接收到某个conn就只能在该conn上的通讯结束后才能重新放回,而该conn直到测试结束才会close,因此这样的场景相当于让fasthttp“退化”成了net/http的模型,也染上了net/http的“缺陷”:goroutine的数量一旦多起来,go runtime自身调度所带来的消耗便不可忽视甚至超过了业务处理所消耗的资源占比。下面分别是fasthttp在200长连接、8000长连接以及16000长连接下的cpu profile的结果:

200长连接:

(pprof) top -cum
Showing nodes accounting for 88.17s, 55.35% of 159.30s total
Dropped 150 nodes (cum <= 0.80s)
Showing top 10 nodes out of 60
      flat  flat%   sum%        cum   cum%
     0.46s  0.29%  0.29%    101.46s 63.69%  github.com/valyala/fasthttp.(*Server).serveConn
         0     0%  0.29%    101.46s 63.69%  github.com/valyala/fasthttp.(*workerPool).getCh.func1
         0     0%  0.29%    101.46s 63.69%  github.com/valyala/fasthttp.(*workerPool).workerFunc
     0.04s 0.025%  0.31%     89.46s 56.16%  internal/poll.ignoringEINTRIO (inline)
    87.38s 54.85% 55.17%     89.27s 56.04%  syscall.Syscall
     0.12s 0.075% 55.24%     60.39s 37.91%  bufio.(*Writer).Flush
         0     0% 55.24%     60.22s 37.80%  net.(*conn).Write
     0.08s  0.05% 55.29%     60.21s 37.80%  net.(*netFD).Write
     0.09s 0.056% 55.35%     60.12s 37.74%  internal/poll.(*FD).Write
         0     0% 55.35%     59.86s 37.58%  syscall.Write (inline)
(pprof) 

8000长连接:

(pprof) top -cum
Showing nodes accounting for 108.51s, 54.46% of 199.23s total
Dropped 204 nodes (cum <= 1s)
Showing top 10 nodes out of 66
      flat  flat%   sum%        cum   cum%
         0     0%     0%    119.11s 59.79%  github.com/valyala/fasthttp.(*workerPool).getCh.func1
         0     0%     0%    119.11s 59.79%  github.com/valyala/fasthttp.(*workerPool).workerFunc
     0.69s  0.35%  0.35%    119.05s 59.76%  github.com/valyala/fasthttp.(*Server).serveConn
     0.04s  0.02%  0.37%    104.22s 52.31%  internal/poll.ignoringEINTRIO (inline)
   101.58s 50.99% 51.35%    103.95s 52.18%  syscall.Syscall
     0.10s  0.05% 51.40%     79.95s 40.13%  runtime.mcall
     0.06s  0.03% 51.43%     79.85s 40.08%  runtime.park_m
     0.23s  0.12% 51.55%     79.30s 39.80%  runtime.schedule
     5.67s  2.85% 54.39%     77.47s 38.88%  runtime.findrunnable
     0.14s  0.07% 54.46%     68.96s 34.61%  bufio.(*Writer).Flush

16000长连接:

(pprof) top -cum
Showing nodes accounting for 239.60s, 87.07% of 275.17s total
Dropped 190 nodes (cum <= 1.38s)
Showing top 10 nodes out of 46
      flat  flat%   sum%        cum   cum%
     0.04s 0.015% 0.015%    153.38s 55.74%  runtime.mcall
     0.01s 0.0036% 0.018%    153.34s 55.73%  runtime.park_m
     0.12s 0.044% 0.062%       153s 55.60%  runtime.schedule
     0.66s  0.24%   0.3%    152.66s 55.48%  runtime.findrunnable
     0.15s 0.055%  0.36%    127.53s 46.35%  runtime.netpoll
   127.04s 46.17% 46.52%    127.04s 46.17%  runtime.epollwait
         0     0% 46.52%       121s 43.97%  github.com/valyala/fasthttp.(*workerPool).getCh.func1
         0     0% 46.52%       121s 43.97%  github.com/valyala/fasthttp.(*workerPool).workerFunc
     0.41s  0.15% 46.67%    120.18s 43.67%  github.com/valyala/fasthttp.(*Server).serveConn
   111.17s 40.40% 87.07%    111.99s 40.70%  syscall.Syscall
(pprof)

通过上述profile的比对,我们发现当长连接数量增多时(即workerpool中goroutine数量增多时),go runtime调度的占比会逐渐提升,在16000连接时,runtime调度的各个函数已经排名前4了。

4. 优化途径

从上面的测试结果,我们看到fasthttp的模型不太适合这种连接连上后进行持续“饱和”请求的场景,更适合短连接或长连接但没有持续饱和请求,在后面这样的场景下,它的goroutine复用模型才能更好的得以发挥。

但即便“退化”为了net/http模型,fasthttp的性能依然要比net/http略好,这是为什么呢?这些性能提升主要是fasthttp在内存分配层面的优化trick的结果,比如大量使用sync.Pool,比如避免在[]byte和string互转等。

那么,在持续“饱和”请求的场景下,如何让fasthttp workerpool中goroutine的数量不会因conn的增多而线性增长呢?fasthttp官方没有给出答案,但一条可以考虑的路径是使用os的多路复用(linux上的实现为epoll),即go runtime netpoll使用的那套机制。在多路复用的机制下,这样可以让每个workerpool中的goroutine处理同时处理多个连接,这样我们可以根据业务规模选择workerpool池的大小,而不是像目前这样几乎是任意增长goroutine的数量。当然,在用户层面引入epoll也可能会带来系统调用占比的增多以及响应延迟增大等问题。至于该路径是否可行,还是要看具体实现和测试结果。

注:fasthttp.Server中的Concurrency可以用来限制workerpool中并发处理的goroutine的个数,但由于每个goroutine只处理一个连接,当Concurrency设置过小时,后续的连接可能就会被fasthttp拒绝服务。因此fasthttp的默认Concurrency为:

const DefaultConcurrency = 256 * 1024

本文涉及的源码可以在这里 github.com/bigwhite/experiments/blob/master/http-benchmark 下载。


“Gopher部落”知识星球正式转正(从试运营星球变成了正式星球)!“gopher部落”旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!部落目前虽小,但持续力很强。在2021年上半年,部落将策划两个专题系列分享,并且是部落独享哦:

  • Go技术书籍的书摘和读书体会系列
  • Go与eBPF系列

欢迎大家加入!

Go技术专栏“改善Go语⾔编程质量的50个有效实践”正在慕课网火热热销中!本专栏主要满足广大gopher关于Go语言进阶的需求,围绕如何写出地道且高质量Go代码给出50条有效实践建议,上线后收到一致好评!欢迎大家订
阅!

img{512x368}

我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网热卖中,欢迎小伙伴们订阅学习!

img{512x368}

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博:https://weibo.com/bigwhite20xx
  • 微信公众号:iamtonybai
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • “Gopher部落”知识星球:https://public.zsxq.com/groups/51284458844544

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

提高您的kubectl生产力(第三部分):集群上下文切换、使用别名减少输入和插件扩展

本文翻译自《Boosting your kubectl productivity》

第一部分:什么是kubectl?
第二部分:命令完成、资源规范快速查看和自定义列输出格式什么是kubectl?

4. 轻松切换集群和名称空间

当kubectl必须向Kubernetes API发出请求时,它会读取系统上所谓的kubeconfig文件,以获取它需要访问的所有连接参数并向API服务器发出请求。

默认的kubeconfig文件是~/.kube/config。此文件通常由某个命令自动创建或更新(例如,aws eks update-kubeconfig或者gcloud container clusters get-credentials,如果您使用托管Kubernetes服务)。

使用多个集群时,您的kubeconfig文件中配置了多个集群的连接参数。这意味着,您需要一种方法来告诉kubectl 您希望它连接到哪个集群。

在集群中,您可以设置多个名称空间(名称空间是物理集群中的一种“虚拟”集群)。Kubectl也会从kubeconfig文件确定用于请求的命名空间。因此,您需要一种方法来告诉kubectl 您希望它使用哪个命名空间。

本节将介绍kubectl切换集群上下文的原理以及它是如何轻松完成的。

请注意,您还可以在KUBECONFIG环境变量中列出多个kubeconfig文件。在这种情况下,所有这些文件将在执行时合并为单个有效配置。您还可以使用–kubeconfig指定kubectl命令的选项以覆盖默认的kubeconfig文件。请参阅官方文档

Kubeconfig文件

让我们看看kubeconfig文件实际包含的内容:

img{512x368}

如您所见,kubeconfig文件由一组上下文组成。上下文包含以下三个元素:

  • 集群(cluster):集群的API服务器的URL
  • 用户(user):集群的特定用户的身份验证凭据
  • 命名空间(namespace):连接到集群时使用的命名空间

实际上,人们经常在他们的kubeconfig文件中为每个集群的配置一个上下文。但是,你也可以为每个集群配置多个上下文,其用户或命名空间不同。但这似乎不太常见,因此通常在集群和上下文之间存在一对一的映射。

在任何给定时间,其中一个上下文被设置为当前上下文(通过kubeconfig文件中的专用字段):

img{512x368}

当kubectl读取kubeconfig文件时,它总是使用当前上下文中的信息。因此,在上面的例子中,kubectl将连接到Hare集群。

因此,要切换到另一个集群,您只需更改kubeconfig文件中的当前上下文:

img{512x368}

在上面的示例中,kubectl现在将连接到Fox集群。

要切换到同一集群中的另一个命名空间,您可以更改当前上下文的命名空间元素的值:

img{512x368}

在上面的示例中,kubectl现在将使用Fox群集中的Prod命名空间(而不是之前设置的Test命名空间)。

请注意,kubectl还提供了–cluster,–user和–namespace,以及–context允许您覆盖单个元素和当前上下文本身的选项,无论kubeconfig文件中设置了什么。见kubectl options。

理论上,您可以通过手动编辑kubeconfig文件来执行这些更改。但当然这很乏味。以下部分介绍了允许您自动执行这些更改的各种工具。

使用kubectx

kubectx是一种非常流行的用于在集群和命名空间之间切换的工具。

此工具提供允许您分别更改当前上下文和命名空间的命令kubectx和kubens命令。

如上所述,如果每个集群只有一个上下文,则更改当前上下文意味着更改集群。

在这里,您可以看到这两个命令:

img{512x368}

在表象之下,这些命令只是编辑kubeconfig文件,如上一节中所述。

要安装kubectx,只需按照GitHub页面上的说明操作即可

kubectx和kubens都通过完成交办提供命令完成(command completion)。这允许您自动完成上下文名称和名称空间,这样您就不必完全键入它们。您也可以在GitHub页面上找到设置完成的说明。

kubectx的另一个有用功能是交互模式。这与fzf工具结合使用,您必须单独安装(事实上,安装fzf,将自动启用kubectx交互模式)。交互模式允许您通过交互式模糊搜索界面(由fzf提供)选择目标上下文或命名空间。

使用shell别名

实际上,您并不需要单独的工具来更改当前上下文和命名空间,因为kubectl也提供了执行此操作的命令。特别是,该kubectl config命令提供了用于编辑kubeconfig文件的子命令。这里是其中的一些:

  • kubectl config get-contexts:列出所有上下文
  • kubectl config current-context:获取当前上下文
  • kubectl config use-context:更改当前上下文
  • kubectl config set-context:更改上下文的元素

但是,直接使用这些命令并不是很方便,因为它们很难输入。但是你可以做的是将它们包装成可以更容易执行的shell别名。

我基于这些命令创建了一组别名,这些命令提供了与kubectx类似的功能。在这里你可以看到他们的行动:

img{512x368}

请注意,别名使用fzf来提供交互式模糊搜索界面(如kubectx的交互模式)。这意味着,您需要安装fzf才能使用这些别名。

以下是别名的定义:

# Get current context
alias krc='kubectl config current-context'
# List all contexts
alias klc='kubectl config get-contexts -o name | sed "s/^/  /;\|^  $(krc)$|s/ /*/"'
# Change current context
alias kcc='kubectl config use-context "$(klc | fzf -e | sed "s/^..//")"'

# Get current namespace
alias krn='kubectl config get-contexts --no-headers "$(krc)" | awk "{print \$5}" | sed "s/^$/default/"'
# List all namespaces
alias kln='kubectl get -o name ns | sed "s|^.*/|  |;\|^  $(krn)$|s/ /*/"'
# Change current namespace
alias kcn='kubectl config set-context --current --namespace "$(kln | fzf -e | sed "s/^..//")"'

要安装这些别名,你只需要在上面定义添加到您的~/.bashrc或~/.zshrc文件,并重新加载你的shell(source ~/.bashrc or source ~/.zshrc)!

使用插件

Kubectl允许安装可以像本机命令一样调用的插件。例如,您可以安装名为kubectl-foo的插件,然后将其调用为kubectl foo。

Kubectl插件将在本文的后续部分中详细介绍。

能够像这样更改当前上下文和命名空间不是很好吗?例如,运行kubectl ctx以更改上下文,kubectl ns更改名称空间?

我创建了两个允许这样做的插件:

在内部,插件构建在上一节的别名之上。

在这里你可以看到插件的实际效果:

img{512x368}

请注意,插件使用fzf来提供交互式模糊搜索界面。这意味着,您需要安装fzf才能使用这些插件。

要安装插件,你只需要将名为的shell脚本kubectl-ctxkubectl-ns的脚本下载以到PATH下的任何目录中,并使他们具备可执行权限(例如,使用chmod +x)。紧接着,你就应该能够使用kubectl ctx和kubectl ns!

5. 使用自动生成的别名减少输入

Shell别名通常是减少手工输入的好方法。该kubectl-aliases项目就是以这个想法为核心,并提供800多个kubectl命令别名。

您可能想知道如何记住800个别名?实际上,您不需要记住它们,因为它们都是根据一个简单的方案生成的,下面将显示一些示例别名:

img{512x368}

如您所见,别名由组件(component)组成,每个组件代表kubectl命令的特定元素。每个别名可以有一个用于基本命令,操作和资源的组件,以及用于选项的多个组件,您只需根据上述方案从左到右“填充”这些组件。

请注意,目前完全详细的方案在GitHub页面上。在那里,您还可以找到别名的完整列表

例如,别名kgpooyamlall代表命令kubectl get pods -o yaml –all-namespaces:

img{512x368}

请注意,大多数选项组件的相对顺序无关紧要。所以,kgpooyamlall相当于kgpoalloyaml。

您不需要将所有组件用于别名。例如k,kg,klo,ksys,或者kgpo是有效的别名也。此外,您可以在命令行中将别名与其他单词组合使用。

例如,您可以k proxy用于运行kubectl proxy:

img{512x368}

或者您可以kg roles用于运行kubectl get roles(目前不存在Roles资源的别名组件):

img{512x368}

要获取特定Pod,您可以使用kgpo my-pod以运行kubectl get pod my-pod:

img{512x368}

请注意,某些别名甚至需要在命令行上的进一步参数。例如,kgpol别名代表kubectl get pods -l。该-l选项需要一个参数(标签规范)。所以,你必须使用这个别名,例如,像这样:

img{512x368}

出于这个原因,你可以使用a,f以及l只在一个别名的结尾部分。

一般来说,一旦你掌握了这个方案,就可以直观地从你想要执行的命令中推断出别名,并节省大量的输入!

安装

要安装kubectl-别名,你只需要下载.kubectl-aliasesGitHub文件,并在你的~/.bashrc或~/.zshrc文件生效它:

source ~/.kubectl_aliases

重新加载shell后,您应该能够使用所有800个kubectl别名!

命令完成

如您所见,您经常在命令行上向别名添加更多单词。例如:

$kgpooyaml test-pod-d4b77b989

如果你使用kubectl命令完成,那么你可能习惯于自动完成资源名称之类的事情。但是当你使用别名时,你还可以这样做吗?

这是一个重要的问题,因为如果它不起作用,那将消除这些别名的一些好处!

答案取决于您使用的shell。

对于Zsh,完成对于别名是开箱即用的。

不幸的是,对于Bash,默认情况下,对于别名,完成功能不起作用。好消息是它可以通过一些额外的步骤来完成。下一节将介绍如何执行此操作。

在Bash中启用别名的完成

Bash的问题在于它尝试在别名上尝试完成(每当你按Tab键),而不是在别名命令(如Zsh)上。由于您没有所有800个别名的完成脚本,因此不起作用。

complete-alias项目提供了解决这个问题的通用解决方案。它使用别名的完成机制,在内部将别名扩展到别名命令,并返回扩展命令的完成建议。这意味着,它使别名的完成行为与别名命令完全相同。

在下文中,我将首先解释如何安装complete-alias,然后如何配置它以启用所有kubectl别名的完成。

安装complete-alias

首先,complete-alias依赖于bash-completion。因此,您需要确保在安装complete-alias之前安装了bash-completion。早先已经为Linux和macOS提供了相关说明。

对于macOS用户的重要注意事项:与kubectl完成脚本一样,complete-alias不适用于Bash 3.2,这是macOS上Bash的默认版本。特别是,complete-alias依赖于bash-completion v2(brew install bash-completion@2),它至少需要Bash 4.1。这意味着,要在macOS上使用complete-alias,您需要安装较新版本的Bash。

要安装complete-alias,您只需bash_completion.sh从GitHub存储库下载脚本,并将其在您的~/.bashrc文件中source:

source ~/bash_completion.sh

重新加载shell后,应正确安装complete-alias。

启用kubectl别名的完成

从技术上讲,complete-alias提供了_complete_aliasshell函数。此函数检查别名并返回别名命令的完成建议。

要将其与特定别名挂钩,您必须使用completeBash内置来设置别名_complete_alias的完成功能。

举个例子,我们k来看一下代表kubectl命令的别名。要设置_complete_alias此别名的完成功能,您必须执行以下命令:

$complete -F _complete_alias k

这样做的结果是,无论何时在k别名上自动完成,_complete_alias都会调用该函数,该函数检查别名并返回kubectl命令的完成建议。

作为另一个例子,让我们采用kg代表的别名kubectl get:

$complete -F _complete_alias kg

同样,这样做的结果是,当您自动完成时kg,您将获得与之相同的完成建议kubectl get。

请注意,可以以这种方式对系统上的任何别名使用complete-alias。

因此,要启用所有 kubectl别名的完成,您只需为每个别名运行上述命令。以下代码片段完全相同(假设您安装了kubectl-aliases ~/.kubectl-aliases):

for _a in $(sed '/^alias /!d;s/^alias //;s/=.*$//' ~/.kubectl_aliases); do
  complete -F _complete_alias "$_a"
done

只需将此片段添加到您的~/.bashrc文件中,重新加载您的shell,现在您应该可以使用所有800 kubectl别名的完成!

6. 使用插件扩展kubectl

版本1.12开始,kubectl包含一个插件机制,允许您使用自定义命令扩展kubectl。

以下是kubectl插件的示例,可以调用为kubectl hello:

$ kubectl hello
Hello, I'm a kubectl plugin!

kubectl插件机制将严格遵循Git插件机制的设计。

本节将向您展示如何安装插件,您可以在哪里找到现有的插件,以及如何创建自己的插件。

安装插件

Kubectl插件作为简单的可执行文件分发,其名称的形式为kubectl-x。前缀kubectl-是必需的,接下来是允许调用插件的新kubectl子命令。

例如,上面显示的hello插件将作为名为的文件分发kubectl-hello。

安装插件,您只需将kubectl-x文件复制到您的任何目录中PATH并使其可执行(例如,使用chmod +x)。之后,您可以立即调用该插件kubectl x。

您可以使用以下命令列出系统上当前安装的所有插件:

$kubectl plugin list

如果您有多个具有相同名称的插件,或者存在不可执行的插件文件,则此命令还会显示警告。

使用krew查找和安装插件

Kubectl插件可以像软件包一样共享和重用。但是在哪里可以找到其他人共享的插件?

krew项目旨在提供一个统一的解决方案,共享,查找,安装和管理kubectl插件。该项目将自己称为“kubectl插件的包管理器”(名称krew是brew的提示)。

Krew 以kubectl插件索引为中心,您可以从中选择和安装。

$ kubectl krew search | less
$ kubectl krew search view
$ kubectl krew info view-utilization
$ kubectl krew install view-utilization
$ kubectl krew list

如您所见,krew本身是一个kubectl插件。这意味着,安装krew本质上就像安装任何其他kubectl插件一样。您可以在GitHub页面上找到krew的详细安装说明。

最重要的krew命令如下:

# Search the krew index (with an optional search query)
$ kubectl krew search [<query>]
# Display information about a plugin
$ kubectl krew info <plugin>
# Install a plugin
$ kubectl krew install <plugin>
# Upgrade all plugins to the newest versions
$ kubectl krew upgrade
# List all plugins that have been installed with krew
$ kubectl krew list
# Uninstall a plugin
$ kubectl krew remove <plugin>

请注意,使用krew安装插件并不妨碍以传统方式安装插件。即使你使用krew,你仍然可以通过其他方式安装你在其他地方找到的插件(或自己创建)。

请注意,该kubectl krew list命令仅列出已使用krew安装的插件,而该kubectl plugin list命令列出了所有插件,即使用krew安装的插件和以其他方式安装的插件。

在其他地方寻找插件

Krew仍然是一个年轻的项目,目前krew索引中只有大约30个插件。如果你在那里找不到你需要的东西,你可以在其他地方寻找插件,例如,在GitHub上。

我建议查看kubectl-plugins GitHub主题。你会发现有几十个可用的插件值得一看。

创建自己的插件

当然,您可以创建自己的kubectl插件,这很容易实现。

您只需创建一个可执行文件,执行您想要的操作,为其命名kubectl-x,然后按上述方法安装它。

可执行文件可以是任何类型,Bash脚本,编译的Go程序,Python脚本,它确实无关紧要。唯一的要求是它可以由操作系统直接执行。

我们现在创建一个示例插件。在上部分中,您使用kubectl命令列出每个pod的容器镜像。您可以轻松地将此命令转换为可以调用的插件,比如说kubectl img。

为此,只需创建一个名为kubectl-img以下内容的文件:

#!/bin/bash
kubectl get pods -o custom-columns='NAME:metadata.name,IMAGES:spec.containers[*].image'

现在使文件可执行,chmod +x kubectl-img并将其移动到您的任何PATH中的目录。之后,您可以立即开始使用该插件kubectl img!

如上所述,kubectl插件可以用任何编程语言或脚本语言编写。如果使用shell脚本,则可以从插件轻松调用kubectl。但是,您可以使用实际编程语言编写更复杂的插件,例如,使用Kubernetes客户端库。如果使用Go,您还可以使用cli-runtime库,它专门用于编写kubectl插件。

分享你的插件

如果您认为其中一个插件可能对其他人有用,请随时在GitHub上分享。确保将其添加到kubectl-plugins主题中,以便其他人可以找到它。

您还可以请求将您的插件添加到krew索引中。您可以在krew GitHub存储库中找到有关如何执行此操作的说明。

命令完成

目前,插件机制遗憾的是还不支持命令完成。这意味着您需要完全键入插件名称以及插件的任何参数。

但是,在kubectl GitHub存储库中有一个处于open状态的功能请求issue。因此,此功能有可能在将来的某个时间得到实现。

以上就是有关kubectl高效使用的所有内容了!


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats