K8s已经成为一线大厂分布式平台的标配技术。你是不是还在惆怅怎么掌握它?来这里,大型互联网公司一线工程师亲授,不来虚的,直接上手实战,3天时间带你搭建K8s平台,快速学会K8s,点击下方图片可了解培训详情。
我用50多行Bash代码实现了我称之为“世界上最简单的Kubernetes仪表板”,它被称为k1s,本文将介绍如何使用它以及它是如何工作的。 当然,“世界上最简单”并不是一个很严格的说辞。你可以在GitHub上的仓库weibeld/k1s[1]上找到完整代码和详细使用说明。概述 k1s大致如下图: 仪表板展示了任意命名空间(或跨所有命名空间)中任何类型的资源列表,并实时更新它。某些类型的资源会显示额外的信息,例如Pod的当前状态,Deployment中所需的副本数和实际数量。你可以运行多个k1s实例从而可以同时观察多个资源类型的更新。下图是运行三个仪表板实例的例子,一个用于Deployment,一个用户ReplicasSet,还有一个用于Pod(使用tmux运行)。然后对Deployment上执行一些伸缩操作和滚动更新。请注意,你可以实时观察Deployment、其管理的ReplicaSets和Pods之间的交互。这有可能提供许多认知,有助于了解Deployment和其他资源的工作原理。安装 如果你在macOS上使用homebrew,你可以这样安装:
$ brew install weibeld/core/k1s
如果是其他情况,你可以参考下面脚本安装它:
{
wget https://raw.githubusercontent.com/weibeld/k1s/master/k1s
chmod +x k1s
mv k1s /usr/local/bin
}
k1s依赖于机器上安装的以下工具:
jq:https://stedolan.github.io/jq/
watch:https://linux.die.net/man/1/watch
curl:https://curl.haxx.se/
kubectl:https://kubernetes.io/docs/tasks/tools/install-kubectl/
$ k1s [namespace] [resource-type]
两个额外的命令行参数分别是:
namespace:指定要观察的资源所在的Kubernetes命名空间(默认是default)
resource-type:指定要观察的Kubernetes资源类型(默认是pods)
deployments、deployment、deploy
replicasets、replicaset、rs
services、service、svc
#!/bin/bash
[[ "$1" = -v || "$1" = --version ]] && { echo "0.1.1"; exit; }for d in jq watch curl kubectl; do which "$d" >/dev/null || { echo "Missing dependency: $d"; exit 1; }; done
ns=${1:-default}; res=${2:-pods}c() { echo -e "\033[$1m"; }cc() { echo -e "\033[$1;1m"; }printf Loading && while true; do printf . && sleep 0.1; done &set -o pipefail
path=$(kubectl get "$res" "$([[ "$ns" = - ]] && echo --all-namespaces || echo -n=$ns)" -v 6 2>&1 >/dev/null | grep GET | tail -n 1 | sed -n 's#.*https://[^/]*\([a-z0-9/.-]*\).*#\1#p')
pid=$?kill -9 "$!" && wait "$!" 2>/dev/null
[[ "$pid" -ne 0 ]] && echo -e "\nInvalid resource type: $res" && exit 1
[[ $(echo -n "${path//[^\/]}" | wc -c) -lt 5 ]] && ns=-
res=${path##*/}exec 3port=$(head -n 1 's/.*:\([0-9]\{4,5\}\)\b.*/\1/')
file=$(mktemp)
cat <"$file"
$(cc 36) ____ ____ ____
||$(cc 33)k$(cc 36) |||$(cc 33)1$(cc 36) |||$(cc 33)s$(cc 36) || $(cc 0)Kubernetes Dashboard$(cc 36)
||__|||__|||__|| $(cc 0)Namespace: $ns$(cc 36)
|/__\|/__\|/__\| $(cc 0)Resources: $res$(c 0)
EOF
curl -N -s "http://localhost:$port$path?watch=true" |while read -r event; do
name=$(jq -r '.object.metadata.name' <<<"$event")case "$res" in
pods)
phase=$(jq -r '.object.status.phase' <<<"$event")
is_ready=$(jq -r 'if .object.status | has("conditions") then .object.status.conditions[] | if select(.type=="Ready").status=="True" then "1" else "" end else "" end' <<<"$event")
is_scheduled=$(jq -r 'if .object.status | has("conditions") then .object.status.conditions[] | if select(.type=="PodScheduled").status=="True" then "1" else "" end else "" end' <<<"$event")
[[ "$is_scheduled" && ! "$is_ready" ]] && info=NonReady || info=$phase
[[ "$info" = Running ]] && info=$(c 32)$info$(c 0) || info=$(c 33)$info$(c 0) ;;
deployments|replicasets|statefulsets)
spec=$(jq -r '.object.spec.replicas' <<<"$event")stat=$(jq -r '.object.status.readyReplicas // 0' <<<"$event")
[[ "$stat" = "$spec" ]] && info="$(c 32)($stat/$spec)$(c 0)" || info="$(c 33)($stat/$spec)$(c 0)" ;;esaccase $(jq -r .type <<<"$event") in
ADDED) echo "$name $info" >>"$file" ;;
MODIFIED) sed -i "s/^$name .*$/$name ${info//\//\\/}/" "$file" ;;
DELETED) sed -i "/^$name .*$/d" "$file";;esacdone &
watch -ctn 0.1 cat "$file"
下面我们将一步步阅读项目代码。不过首先我们先来讨论一下k1s所基于的思路。
基础机制以下是k1s工作的主要部分:
使用kubectl proxy用于轻松请求Kubernetes API服务(第20行)。
使用curl执行一个向Kubernetes API服务请求指定资源类型的watch监视请求(第32行)。这将向API服务器创建一个持久连接,该连接返回任意ADDED、MODIFIED或DELETED类型的JSON格式资源事件流。
使用jq解析每个事件并从中提取信息,例如事件相关的资源的名称。将该信息作为额外的记录行写入到文件系统上的一个文件(称之为状态文件)。如果资源在文件中已存在,则更新它,如果资源被删除,则从状态文件中删除具体行(第48-50行)。
使用watch定期更新显示该状态文件(第53行)。这给人一种动态全屏终端应用的印象。
第三行提供了-v/--version选项用于打印k1s的版本。
第四行检查所有依赖是否已在本机安装,如果没有则显示错误信息并退出。
第五行将命令行参数读进变量(namespace和resource-type),如果这些参数没有提供则使用默认值。
第12行启用Bash的pipefail选项,如果管道语句中包含的任何一条命令返回一个非零的退出码,那么该选项就会使整个管道语句返回一个非零的退出码。这是后续命令正常执行的前提。
第13行执行了请求的资源类型和命令空间的kubectl get请求并从输出中提取相应的Kubernetes API路径。这里的诀窍是通过提高kubectl的输出级别(-v=6),kubectl会输出它向Kubernetes API服务器发出的HTTP GET请求的确切URL。通过这个URL,可通过grep和sed提取出来。
第14行记录了13行中kubectl get的退出码,如果请求成功则退出码为零,否则为非零。退出码非零的很大可能是用户提交了一个无效的资源类型(例如使用dep替代deploy/deployment)。
第15行杀掉第10行开启的加载指示器后台进程。注意加载指示器的进程ID(PID)被指定为Bash内置变量$!。在Bash中$!变量始终保存已启动的最后一个后台进程的进程ID。因为从第10行后没有其他后台进程启动,所以该变量保证是指向加载指示器进程。
第16行检查第13行kubectl get请求的退出码,如果退出码非零,则假设用户提供了一个无效的资源类型规范,之后脚本以相应的错误信息终止。
第17行检查提取的API路径是否包含了命名空间部分。如果没有,则代表着用户请求跨所有命名空间的资源类型或者是非命名空间级别的资源类型。在这种情况下,命名空间将会被设置并在仪表板中显示为为-。这修复了当用户指定了非命名空间级别资源却同时指定命名空间的情况。
第18行将从API路径中提取的用户请求的资源类型设置为相应资源的复数形式,该值会显示在仪表板的标题上。这意味着如果用户指定deploy/deployment来监视Deployments,那么仪表板会在标题横幅处统一显示为deployments。
第20行启动kubectl proxy作为后台进程,该命令为Kubernetes API服务创建一个本地代理,并在本机监听一个随机端口(通过-p=0指定)。该后台进程的输出被新文件描述符3捕获。
第21行从上一行的kubectl proxy的输出文件描述符中提取端口。知道这个端口号在后续代码中才能构建Kubernetes API服务的监视请求。
第23行创建状态文件
第24-30行将标题横幅写入到状态文件。标题横幅由(彩色的)k1s logo和其他一些信息组成,包括正在使用的命名空间和资源类型。
第32行使用curl发起监视请求。URL由之前确定的Kubernetes API路径和kubectl proxy端口号组成。监视请求返回的每个响应都是对应于一个资源事件的JSON单行。curl使用了-N选项禁止输出缓冲,这样每一行在收到后就会立即输出。
第33行开启一个while循环读取curl的每一行输出到名为event的变量。该循环会无限运行,只有当用户键入Ctrl-C退出仪表板时才被终止。
第34行提取事件相关的资源的名称(即创建、修改或删除的资源),提取操作使用jq完成。
第35行使用case语句根据正在监视的资源类型执行不同的操作。其目的是对某些资源类型进行特殊处理,并确定和提取它们的附加信息。
第37行提取Pod的当前阶段。阶段是对Pod状态的高级指示,如Running或Pending。
第38行确定Pod的Ready条件是否设置为True。如果是的话,那么Pod中的所有容器都通过了就绪探测,Pod可以为用户请求提供服务。
第39行确定Pod的PodScheduled条件是否设置为True。如果是的话,那么Pod已被Kubernetes调度器分配了一个节点。
第40行确定k1s将显示的Pod的状态。默认情况下,k1s显示Pod的阶段。但是,在下面的情况下,行为会有所不同:如果Pod已经被调度,但目前还没有准备好,那么k1s显示NonReady而不是phase字段(此时phase为Pending)。这使得它更容易区分尚未调度的Pod和仅仅是临时暂停或终止的Pod。
第41行为Pod的状态指示器着色,以获得更好的视觉效果:如果状态为Running则为绿色,其他情况为黄色。
第43行确定Pod控制器指定的(需要的)副本的数量
第44行确定Pod控制器当前可用(准备好)的副本数量。
第45行格式化并着色副本指示器:如果可用的副本数量符合要求,则为绿色,否则为黄色。
第47行启动一个case语句根据事件的类型采取不同的操作,事件类型可以是ADDED、MODIFIED或是DELETED。
第48行处理ADDED事件:如果添加了一个资源,它的名称(以及之前确定的任何附加的资源类型特定信息)将被简单地追加为一个新行到状态文件中。
第49行处理MODIFIED事件:如果一个资源被修改(如Pod的相位改变),状态文件中该资源的现有行将被一个由新信息组成的新行取代。
第50行处理DELETED事件:如果资源被删除,状态文件中相应行也会被删除。
可能目前最大的限制是,当仪表盘中的项目列表超过终端窗口的高度时,窗口底部以外的所有输出都会被截断,没有办法滚动。这是因为watch不允许在超过终端高度的文件中滚动。如果你知道什么简单的方法来处理这个问题,请在评论中告诉我。
如果能显示更多的附加信息,包括其他资源类型的信息(如服务和端点),将会很有意思。然而,这有可能是一个无底洞,因为总是有更多的东西可以显示,而且不同的人会关注于不同的东西。然而,由于目前的代码是如此简短简单,所以应该很容易为特定的用例或调查来按需定制。
该工具依赖于其他工具的输出格式,尤其是kubectl。这意味着,如果这些工具的输出格式发生变化,那么k1s也需要进行调整。不过考虑到k1s代码的简洁性,这应该是一个相对快速的任务。
https://github.com/weibeld/k1s
https://github.com/weibeld/k1s#dependencies
https://stackoverflow.com/a/20018118/4747193
https://github.com/weibeld/k1s/issues
https://github.com/weibeld/k1s/pulls