使用Curl对Kubernetes进行调试!
作者 | 祝祥
来源 | 新钛云服(ID:newtyun)
转载请联系授权(微信ID:zlm935177782)
Kubernetes是一个开源的、用于管理云平台中多个主机上的容器化应用的系统。其目标是让部署容器化的应用简单并且高效(powerful),它提供了应用部署,规划,更新,维护的一种机制。
Kubernetes一个核心的特点就是能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着(比如用户想让apache一直运行,用户不需要关心怎么去做,Kubernetes会自动去监控,然后去重启,新建,总之,让apache一直提供服务)。
管理员可以加载一个微型服务,让规划器来找到合适的位置,同时,Kubernetes也系统提升工具以及人性化方面,让用户能够方便的部署自己的应用(就像canary deployments)。
现在Kubernetes着重于不间断的服务状态(比如web服务器或者缓存服务器)和原生云平台应用(Nosql),在不久的将来会支持各种生产云平台中的各种服务,例如,分批,工作流,以及传统数据库。
对于有些人而言,Kubernetes是一个非常复杂调度系统。
…因为它很复杂,所以配置非常冗长。
…由于配置YAML太多,我们需要配合其他多种工具软件来处理该配置。
但是,在本文中,我认为那些认为kubernetns复杂的人,是因为没有找到合理的方式去学习以及使用kubernetns,就像早期的操作系统一样。
Kubernetes对于如何管理配置有一个简单而巧妙的方式,本文也将讨论这种方式。
因为这种方式简单,一致,所以我们可以管理比以往更多的配置!现在,我们不仅可以管理更多的配置,也可以构建复杂的系统。
当然,配置本身可能很复杂。因此,在这篇文章中,我将跳过配置。我将仅专注于kubernetes API机制以及如何探索该API。
以这种方式构建API可以使许多工具受益。
是啥方法?
为了解释这种简单而高效的方式,让我们从Unix系统的最核心思想开始:
Everything is a file.
更确切地说,一切皆文本。Unix程序读取和写入文本。文件系统是用于查找要读取的文本的API。并非所有这些文本都是文件!
- ~/hello-world.txt 是一个文本文件
- /dev/null 是一个空文本流
- /proc 是用于阅读有关进程的一组文本流
通过下面的漫画图,让我们仔细看看/proc。
您可以通过以下的方式来查看以及了解系统上正在运行什么/proc,例如:
- 正在运行多少个进程(ls /proc——列出进程)
- 哪些命令行启动了流程PID(cat /proc/PID/cmdline——获取进程)
- 进程PID正在使用多少内存(cat /proc/PID/status——获取进程状态)
什么是Kubernetes API?
Kubernetes API是集群系统中的重要组成部分,Kubernetes中各种资源(对象)的数据通过该API接口被提交到后端的持久化存储(etcd)中,Kubernetes集群中的各部件之间通过该API接口实现解耦合,同时Kubernetes集群中一个重要且便捷的管理工具kubectl也是通过访问该API接口实现其强大的管理功能的。
Kubernetes API 是基于资源的(RESTful)、通过 HTTP 提供的编程接口。API 支持通过标准的 HTTP 动词(POST、PUT、PATCH、DELETE 和 GET) 检视、创建、更新和删除主要资源,为很多允许细粒度权限控制的对象提供子资源 (如将 Pod 绑定到节点上),并且出于便利性或效率考虑,支持并提供这些资源的 不同表示形式。
Kubernetes API 还通过 "watch" 和一致性的列表支持高效的资源变更通知, 从而允许其他组件对资源的状态进行高效的缓存和同步。
Kubernetes API适用/proc于分布式系统。
一切都是基于HTTP的资源。我们可以使用一些HTTP GET命令探索每个Kubernetes资源。
如果要继续进行下去,您需要熟悉以下的几种工具:
- kind ——一个使用 Docker 容器 “nodes” 运行 Kubernetes 集群的工具
- curl ——用于发送HTTP请求的CLI工具
- jq ——用于解析JSON的CLI工具
- kubectl——命令管理Kubernetes 集群,帮助curl认证
下面,让我们从快速创建集群开始:
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.19.1) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a nice day! 👋
$ kubectl proxy &
Starting to serve on 127.0.0.1:8001
本质上kubectl proxy为访问kubernetes apiserver的REST api充当反向代理角色,这里反向代理的作用与通常意义上的反向代理作用相同,比如提供统一入口进行访问控制、监控、管理,在代理中管理后端,在代理中进行认证等。
当然可以不经过kubectl proxy反向代理直接访问kubernetes apiserver的REST api,但是需要手动管理kubernetes apiserver的地址、手动获取token、手动将token加请到请求的头部,相对来说要繁琐而已。
Kubernetes API比/proc具有更多的层次结构。按版本,名称空间和资源类型将其分为多个文件夹。API路径格式如下:
/api/[version]/namespaces/[namespace]/[resource]/[name]
在一个新的kind集群上,可以看到的命名空间kube-system中已经有一些Pod正在运行 。让我们列出集群中的所有系统进程:
$ curl -s http://localhost:8001/api/v1/namespaces/kube-system/pods | head -n 20
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/kube-system/pods",
"resourceVersion": "1233"
},
"items": [
{
"metadata": {
"name": "coredns-f9fd979d6-5zxtx",
"generateName": "coredns-f9fd979d6-",
"namespace": "kube-system",
"selfLink": "/api/v1/namespaces/kube-system/pods/coredns-f9fd979d6-5zxtx",
"uid": "a30e70cc-2b53-4511-a5de-57c80e5b68ad",
"resourceVersion": "549",
"creationTimestamp": "2021-03-04T15:51:21Z",
"labels": {
"k8s-app": "kube-dns",
"pod-template-hash": "f9fd979d6"
上述命令会返回大量的内容!我们可以使用jq来提取对象的名称。
$ curl -s http://localhost:8001/api/v1/namespaces/kube-system/pods | jq '.items[].metadata.name'
"coredns-f9fd979d6-5zxtx"
"coredns-f9fd979d6-bn6jz"
"etcd-kind-control-plane"
"kindnet-fcjkd"
"kube-apiserver-kind-control-plane"
"kube-controller-manager-kind-control-plane"
"kube-proxy-sn64n"
"kube-scheduler-kind-control-plane"
/pods罗列出了所有的进程,就像ls /proc一样。如果我们要查看一个特定的进程,可以查询/pods/POD_NAME。
$ curl -s http://localhost:8001/api/v1/namespaces/kube-system/pods/kube-apiserver-kind-control-plane | head -n 10
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "kube-apiserver-kind-control-plane",
"namespace": "kube-system",
"selfLink": "/api/v1/namespaces/kube-system/pods/kube-apiserver-kind-control-plane",
"uid": "a8f893b7-1cdb-48fd-9505-87d71c81adcb",
"resourceVersion": "458",
"creationTimestamp": "2021-03-04T15:51:17Z",
然后,我们可以再次使用jq来获取特定字段。
$ curl -s http://localhost:8001/api/v1/namespaces/kube-system/pods/kube-apiserver-kind-control-plane | jq '.status.phase'
"Running"
kubectl又是怎么做的?
上面的所有操作本质上都是可以使用kubectl来完成。kubectl提供了更友好的界面。但是,如果您想知道kubectl正在调用什么API ,可以使用以下命令加上-v 6来运行它:
$ kubectl get -v 6 -n kube-system pods kube-apiserver-kind-control-plane
I0304 12:47:59.687088 3573879 loader.go:375] Config loaded from file: /home/nick/.kube/config
I0304 12:47:59.697325 3573879 round_trippers.go:443] GET https://127.0.0.1:44291/api/v1/namespaces/kube-system/pods/kube-apiserver-kind-control-plane 200 OK in 5 milliseconds
NAME READY STATUS RESTARTS AGE
kube-apiserver-kind-control-plane 1/1 Running 0 116m
有关更高级别的调试,请使用-v 8来查看完整的响应结果。
最后:
本文的关键不是要抛弃kubectl而是使用curl来与Kubernetes进行交互。就像您不应该抛弃ps而仅使用ls / proc一样。
但是我发现像这样剖析Kubernetes有助于对系统的深入了解有很大的帮助:
- 一切都是基于HTTP的资源。
- 每个对象的读取和写入方式都相同。
- 所有对象状态都是可读的。
就像我们可以将Unix工具一起管道化(例如jq)一样,我们可以定义新的Kubernetes对象并将其与现有对象组合。
这些方式可能看起来会有点简单,有点傻,但是却往往是最有效的方式。