云原生开发者 如何利用 HTTP 客户端调用 Kubernetes API
关 注 微 信 公 众 号 《 云 原 生 C T O 》 更 多 云 原 生 干 货 等 你 来 探 索
专 注 于 云原生技术
分 享
提 供 优 质 云原生开发
视 频 技 术 培 训
面试技巧
, 及 技 术 疑 难 问 题 解答
云 原 生 技 术 分 享 不 仅 仅 局 限 于 Go
、 Rust
、 Python
、 Istio
、 containerd
、 CoreDNS
、 Envoy
、 etcd
、 Fluentd
、 Harbor
、 Helm
、 Jaeger
、 Kubernetes
、 Open Policy Agent
、 Prometheus
、 Rook
、 TiKV
、 TUF
、 Vitess
、 Argo
、 Buildpacks
、 CloudEvents
、 CNI
、 Contour
、 Cortex
、 CRI-O
、 Falco
、 Flux
、 gRPC
、 KubeEdge
、 Linkerd
、 NATS
、 Notary
、 OpenTracing
、 Operator Framework
、 SPIFFE
、 SPIRE
和 Thanos
等
云原生开发者 如何使用 HTTP 客户端调用 Kubernetes API
使用 CLI
(如 curl
)或 GUI
(如 postman
) HTTP
客户端 调用 Kubernetes API
有很多理由。例如,您可能需要对 Kubernetes
对象进行比 kubectl
提供的更细粒度的控制,或者想在尝试从代码访问 API
之前探索它。
本文不仅仅是一个方便的命令列表,而是一个深思熟虑的演练,揭示了一些您在从命令行调用 Kubernetes API
时可能会偶然发现的有趣问题。它涵盖以下主题:
-
如何获取
Kubernetes API
服务器地址 -
如何向客户端验证
API
服务器 -
如何使用证书向
API
服务器验证客户端 -
如何使用令牌向
API
服务器验证客户端 -
如何从
Pod
内部调用Kubernetes API
-
如何使用
curl
对Kubernetes
对象执行基本的CRUD
操作 -
如何使用
kubectl
的raw
模式直接访问Kubernetes API
-
如何查看哪些
API
请求kubectl
命令(如apply
)。
设置 Kubernetes 游乐场
如果你没有 Kubernetes
集群可以玩,这里是你可以使用 arkade
快速创建本地游乐场:
http://github.com/alexellis/arkade
$ curl -sLS http://get.arkade.dev | sudo sh $ arkade get minikube kubectl $ minikube start --profile cluster1
:warning: curl | sudo sh
很吓人。从 Internet
获取软件包并在笔记本电脑上运行它们。具体使用可参考上述 github
链接
如何获取 Kubernetes API 主机和端口
要调用任何 API
,您首先需要知道其服务器地址。对于 Kubernetes
,每个集群都有一个 API
服务器。因此,查找 API
主机和端口的最简单方法是查看 kubectl cluster-info
输出。例如,在我的 Vagrant
上,它会产生以下几行:
$ kubectl cluster-info Kubernetes control plane is running at http://192.168.58.2:8443 ...
该 cluster-info
命令显示在当前上下文中选择的集群的 API
地址。但是,如果您有多个集群怎么办?
查找 Kubernetes API
服务器地址的另一种方法是查看 kubeconfig
内容:
$ kubectl config view apiVersion: v1 clusters: - name: cluster1 cluster: ... server: http://192.168.58.2:8443 - name: cluster2 cluster: ... server: http://192.168.59.2:8443 ...
默认情况下, kubectl
查找目录中命名 config
的 $HOME/.kube
文件。那么,为什么不直接从这个文件中获取 API
地址呢?
原因是潜在的配置合并。 KUBECONFIG
通过将 env var
设置为以冒号分隔的位置列表,可以指定多个 kubeconfig
文件。 kubectl
在访问集群之前,会尝试将所有 kubeconfig
文件的内容合并到一个配置中。
因此,从上面的列表中选择正确的集群,让我们尝试向其 API
服务器发送请求:
$ KUBE_API=$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}')
如何使用 curl 调用 Kubernetes API
实际上,任何 HTTP
客户端( curl
、 httpie
、 wget
甚至 postman
)都可以,但我将在本节中坚持使用 curl
,因为我已经习惯了。
向客户端验证 API 服务器
让我们从查询 API
的 /version
端点开始:
$ curl $KUBE_API/version curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: http://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
而且……没用!:see_no_evil:
当我第一次偶然发现类似的错误时,我真的很困惑。但仔细想想,上述错误实际上是有道理的。默认情况下, Kubernetes
通过 HTTPS
公开其 API
,特别是为了向客户端保证 API
服务器的强身份。但是, minikube
使用自签名证书引导我的本地集群。因此, Kubernetes API
服务器的 TLS
证书原来是由 curl
未知的证书颁发机构 ( CA
) minikubeCA
签名的。由于 curl
无法信任它,因此请求失败。
默认情况下, curl
信任底层操作系统所信任的同一组 CA
。例如,在 Ubuntu
或 Debian
上,受信任的 CA
列表可以在 /etc/ssl/certs/ca-certificates.crt.
显然, minikube
不会将其证书添加到此文件中。
幸运的是, minikube
周到地将 CA
证书保存到 ~/.minikube/ca.crt
:
$ cat ~/.minikube/ca.crt | openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: CN = minikubeCA Validity Not Before: Dec 15 20:46:36 2021 GMT Not After : Dec 14 20:46:36 2031 GMT Subject: CN = minikubeCA Subject Public Key Info:
因此,要修复该 GET /version
请求,我只需要通过手动将其指向 minikubeCA
证书来使 curl
信任 API
服务器证书的颁发者:
$ curl --cacert ~/.minikube/ca.crt $KUBE_API/version { "major": "1", "minor": "22", "gitVersion": "v1.22.3", "gitCommit": "c92036820499fedefec0f847e2054d824aea6cd1", "gitTreeState": "clean", "buildDate": "2021-10-27T18:35:25Z", "goVersion": "go1.16.9", "compiler": "gc", "platform": "linux/amd64" }
耶!:tada:
使用证书向 API 服务器验证客户端
好吧,让我们尝试一些更复杂的东西。列出集群中的所有部署怎么样?
$ curl --cacert ~/.minikube/ca.crt $KUBE_API/apis/apps/v1/deployments { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "deployments.apps is forbidden: User \"system:anonymous\" cannot list resource \"deployments\" in API group \"apps\" at the cluster scope", "reason": "Forbidden", "details": { "group": "apps", "kind": "deployments" }, "code": 403 }
而且......它没有再次工作。
与明显未受保护的 /version
端点不同, Kubernetes
通常会限制对其 API
端点的访问。
从错误消息中可以清楚地看出,该请求已通过身份验证 User "system:anonymous"
,显然,该用户未授权列出部署资源。
失败的请求不包括任何身份验证方式(尽管如此,它已经过身份验证,但作为匿名用户),所以我需要提供一些额外的信息来获得所需的访问级别。
Kubernetes
支持多种身份验证机制,我将从使用客户端证书对请求进行身份验证开始。
多种身份验证机制: http://kubernetes.io/docs/reference/access-authn-authz/authentication/
但是等一下!什么是客户端证书?
当 minikube
引导集群时,它还创建了一个 user
。该用户获得了由同一个 minikubeCA
颁发机构签署的证书。由于 Kubernetes API
服务器信任此 CA
,因此在请求中提供此证书将使其作为所述用户进行身份验证。
Kubernetes 没有代表用户的对象。即,不能通过 API
调用将用户添加到集群中。但是,任何提供由集群的证书颁发机构签名的有效证书的用户都被视为已通过身份验证。 Kubernetes
从证书主题中的通用名称字段中获取用户名(例如, CN = minikube-user
)。然后, Kubernetes RBAC
子系统判断用户是否有权对资源执行特定操作。
用户证书通常可以在我们已经熟悉的 kubectl config view
输出中找到:
$ kubectl config view -o jsonpath='{.users[0]}' | python -m json.tool { "name": "cluster1", "user": { "client-certificate": "/home/vagrant/.minikube/profiles/cluster1/client.crt", "client-key": "/home/vagrant/.minikube/profiles/cluster1/client.key" } }
让我们快速检查证书内容以确保它是由同一个 CA
签名的:
$ cat ~/.minikube/profiles/cluster1/client.crt | openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: CN = minikubeCA Validity Not Before: Dec 26 06:35:56 2021 GMT Not After : Dec 26 06:35:56 2024 GMT Subject: O = system:masters, CN = minikube-user
以下是如何使用 curl
向 Kubernetes API
服务器发送由该证书认证的请求:
$ curl $KUBE_API/apis/apps/v1/deployments \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key { "kind": "DeploymentList", "apiVersion": "apps/v1", "metadata": { "resourceVersion": "654514" }, "items": [...] }
看起来有效:heart_eyes:
使用服务帐户令牌向 API 服务器验证客户端
另一种验证 API
请求的方法是使用包含有效服务帐户 JWT
令牌的不记名标头。
服务帐户: http://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
与用户非常相似,不同的服务帐户将具有不同级别的访问权限。让我们看看使用默认命名空间中的默认服务帐户可以实现什么:
$ JWT_TOKEN_DEFAULT_DEFAULT=$(kubectl get secrets \ $(kubectl get serviceaccounts/default -o jsonpath='{.secrets[0].name}') \ -o jsonpath='{.data.token}' | base64 --decode)
从一个简单的任务开始——列出 apps/v1
组中已知的 API
资源类型:
$ curl $KUBE_API/apis/apps/v1/ \ --cacert ~/.minikube/ca.crt \ --header "Authorization: Bearer $JWT_TOKEN_DEFAULT_DEFAULT" { "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "apps/v1", "resources": [...] }
提高标准 - 让我们尝试在默认命名空间中列出实际的部署对象:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments \ --cacert ~/.minikube/ca.crt \ --header "Authorization: Bearer $JWT_TOKEN_DEFAULT_DEFAULT" { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "deployments.apps is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"deployments\" in API group \"apps\" in the namespace \"default\"", "reason": "Forbidden", "details": { "group": "apps", "kind": "deployments" }, "code": 403 }
显然,用户 system:serviceaccount:default:default
甚至没有足够的能力在自己的命名空间中列出 Kubernetes
对象。
让我们尝试一个强大的 kube-system
服务帐户:
$ JWT_TOKEN_KUBESYSTEM_DEFAULT=$(kubectl -n kube-system get secrets \ $(kubectl -n kube-system get serviceaccounts/default -o jsonpath='{.secrets[0].name}') \ -o jsonpath='{.data.token}' | base64 --decode)
列出集群级资源:
$ curl $KUBE_API/apis/apps/v1/deployments \ --cacert ~/.minikube/ca.crt \ --header "Authorization: Bearer $JWT_TOKEN_KUBESYSTEM_DEFAULT" { "kind": "DeploymentList", "apiVersion": "apps/v1", "metadata": { "resourceVersion": "656580" }, "items": [...] }
是的,按预期工作:ok_hand:
如何从 Pod 内部调用 Kubernetes API
与任何其他 Kubernetes
服务非常相似, Kubernetes API
服务地址可通过环境变量提供给 Pod
:
$ kubectl run -it --image curlimages/curl --restart=Never mypod -- sh $ env | grep KUBERNETES KUBERNETES_SERVICE_PORT=443 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 KUBERNETES_SERVICE_HOST=10.96.0.1
Pod
通常还会将 Kubernetes CA
证书和服务帐户机密信息安装在 /var/run/secrets/kubernetes.io/serviceaccount/
. 因此,应用以上部分的知识, curl
从 Pod
调用 Kubernetes API
服务器的命令如下所示:
$ curl http://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/apis/apps/v1 \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
create、read、watch、update、patch和delete对象
Kubernetes API
支持对 Kubernetes Objects
进行以下操作:
http://github.com/kubernetes/community/blob/7f3f3205448a8acfdff4f1ddad81364709ae9b71/contributors/devel/sig-architecture/api-conventions.md#verbs-on-resources
GET /<resourcePlural> - Retrieve a list of type <resourceName>. POST /<resourcePlural> - Create a new resource from the JSON object provided by the client. GET /<resourcePlural>/<name> - Retrieves a single resource with the given name. DELETE /<resourcePlural>/<name> - Delete the single resource with the given name. DELETE /<resourcePlural> - Deletes a list of type <resourceName>. PUT /<resourcePlural>/<name> - Update or create the resource with the given name with the JSON object provided by client. PATCH /<resourcePlural>/<name> - Selectively modify the specified fields of the resource. GET /<resourcePlural>?watch=true - Receive a stream of JSON objects corresponding to changes made to any resource of the given kind over time.
API
是 RESTful
的,因此上述 HTTP
方法在资源操作上的映射应该看起来很熟悉。
即使文档仅提及 JSON
对象,如果 Content-Type
标头还是需要设置为 application/yaml
.
以下是使用 curl
和 YAML
清单创建新对象的方法:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key \ -X POST \ -H 'Content-Type: application/yaml' \ -d '--- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: curlimages/curl command: ["/bin/sleep", "365d"] '
以下是如何获取默认命名空间中的所有对象:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key
以及如何通过名称和命名空间获取对象:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key
一种更高级的检索 Kubernetes
资源的方法是持续观察它们的变化:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments?watch=true \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key
请注意,只能监视一组资源。但是,您可以通过提供标签或字段选择器将结果集缩小到单个资源。
以下是更新现有对象的方法:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key \ -X PUT \ -H 'Content-Type: application/yaml' \ -d '--- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: curlimages/curl command: ["/bin/sleep", "730d"] # <-- Making it sleep twice longer '
以下是如何修补现有对象:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key \ -X PATCH \ -H 'Content-Type: application/merge-patch+json' \ -d '{ "spec": { "template": { "spec": { "containers": [ { "name": "sleep", "image": "curlimages/curl", "command": ["/bin/sleep", "1d"] } ] } } } }'
请注意 UPDATE
和 PATCH
是相当棘手的操作。第一个受到各种版本冲突的影响,第二个的行为因使用的补丁策略而异。http://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/
最后但同样重要的是 - 以下是如何删除对象集合:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key \ -X DELETE
以下是如何删除单个对象:
$ curl $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \ --cacert ~/.minikube/ca.crt \ --cert ~/.minikube/profiles/cluster1/client.crt \ --key ~/.minikube/profiles/cluster1/client.key \ -X DELETE
如何使用 kubectl 调用 Kubernetes API
上面带有证书和令牌的诡计很有趣。至少经历一次是一个很好的练习,可以巩固对客户端和服务器移动部件的理解。但是,当你有一个可以使用的 kubectl
时,每天都这样做可能会有点矫枉过正。
使用 kubectl 代理调用 Kubernetes API
使用正确配置的 kubectl
工具,您可以通过使用 kubectl proxy
命令大大简化 API
访问。
如果你已经工作了 kubectl
,为什么还要直接调用 Kubernetes API
?
嗯,原因很多。例如,您可能正在开发一个控制器并希望在不编写额外代码的情况下使用 API
查询。或者,您可能对 kubectl
操纵资源时的幕后操作不满意,这使您希望对 Kubernetes
对象上的操作进行更细粒度的控制。
该命令在您的 localhost
和 Kubernetes API
服务器 kubectl proxy
之间创建一个代理服务器(或应用程序级网关) 。但它必须不止于此。不然怎么会这么方便?
代理 kubectl
从调用者那里卸载了相互的客户端-服务器身份验证责任。由于调用者和代理之间的通信是通过 localhost
进行的,因此它被认为是安全的。并且代理本身使用 kubeconfig
文件中选择的当前上下文中的信息来处理客户端-服务器身份验证。
$ kubectl config current-context cluster1 $ kubectl proxy --port=8080 &
启动代理服务器后,调用 Kubernetes API
服务器就变得简单多了:
$ curl localhost:8080/apis/apps/v1/deployments { "kind": "DeploymentList", "apiVersion": "apps/v1", "metadata": { "resourceVersion": "660883" }, "items": [...] }
使用 kubectl raw 模式调用 Kubernetes API
我最近学到的另一个很酷的技巧是一些 kubectl
命令支持的原始模式:
# Sends HTTP GET request $ kubectl get --raw /api/v1/namespaces/default/pods # Sends HTTP POST request $ kubectl create --raw /api/v1/namespaces/default/pods -f file.yaml # Sends HTTP PUT request $ kubectl replace --raw /api/v1/namespaces/default/pods/mypod -f file.json # Sends HTTP DELETE request $ kubectl delete --raw /api/v1/namespaces/default/pods
kubectl
是一个非常先进的工具,即使是简单的命令,比如 kubectl get
背后也有大量的代码。但是,当使用该 --raw
标志时,实现归结为将唯一的参数转换为 API
端点 URL
并调用原始 REST API
客户端。
http://github.com/kubernetes/kubectl/blob/c4379b82ccb7cd7cb496a38ba1e0dc1ec4700be6/pkg/cmd/get/get.go#L463-L469
这种方法的一些优点是:
-
原始
REST API
客户端使用相同的身份验证意味着烘焙命令将使用(在kubeconfig
文件中配置的任何内容) -
-f
这些命令通过标志支持传统的基于文件的清单输入。
但也有一个缺点——我找不到任何 PATCH
或 WATCH
支持,因此 curl
访问为您提供了更多功能。
Kubernetes API 调用等效于 kubectl 命令
我已经多次提到您可能对特定 kubectl
命令发出的实际请求序列不满意。但是你不读代码怎么能知道这个序列呢?
这是一个不错的技巧 - 您可以将 -v 6
标志添加到任何 kubectl
命令,日志将变得如此冗长,以至于您将开始看到向 Kubernetes API
服务器发出的 HTTP
请求。
例如,您可以通过这种方式了解到该 kubectl scale deployment
命令是通过对子资源的 PATCH
请求实现的 /deployments/<name>/scale
:
$ kubectl scale deployment sleep --replicas=2 -v 6 I0116 ... loader.go:372] Config loaded from file: /home/vagrant/.kube/config I0116 ... cert_rotation.go:137] Starting client certificate rotation controller I0116 ... round_trippers.go:454] GET http://192.168.58.2:8443/apis/apps/v1/namespaces/default/deployments/sleep 200 OK in 14 milliseconds I0116 ... round_trippers.go:454] PATCH http://192.168.58.2:8443/apis/apps/v1/namespaces/default/deployments/sleep/scale 200 OK in 12 milliseconds deployment.apps/sleep scaled
看看 kubectl apply -v 6
,结果可能非常有见地:wink:
想查看实际的请求和响应主体吗?将日志详细程度增加到8。
总结
第一次访问 ·Kubernetes API· 的需求可能很可怕 - 有很多新概念,如资源、 API
组、 kind
、对象、集群、上下文、证书,哦,天哪!但是一旦你在构建块上分解它并通过执行一些琐碎的任务(比如找出 API
服务器地址或使用 curl
调用一堆端点)获得一些实践经验,你很快就会意识到这个想法并不是真的一些新的东西——它只是多年来为我们服务的众所周知的机制的组合—— REST
架构风格、 TLS
证书、 JWT
令牌、对象方案等。
所以,不要害怕并运行一些查询!
参考地址 [1]
参考资料
参考地址: http://iximiuz.com/en/posts/kubernetes-api-call-simple-http-client/
- 用更云原生的方式做诊断|大规模 K8s 集群诊断利器深度解析
- 多个维度分析k8s多集群管理工具,到底哪个才真正适合你
- 使用 Kube-capacity CLI 查看 Kubernetes 资源请求、限制和利用率
- 使用 Go 在 Kubernetes 中构建自己的准入控制器
- 云原生数仓如何破解大规模集群的关联查询性能问题?
- 云原生趋势下的迁移与灾备思考
- 2022 年不容错过的六大云原生趋势!
- 使用 Prometheus 监控 Golang 应用程序
- 云原生时代下的机遇与挑战 DevOps如何破局
- 如何在云原生格局中理解Kubernetes合规性和安全框架
- 设计云原生应用程序的15条基本原则
- 使用 Operator SDK 为 Pod 标签编写Controller
- Kubernetes Visitor 模式
- 为什么云原生是第二次云革命
- 构建云原生安全的六个重要能力
- 扩展云原生策略的步骤有哪些?
- 七个值得关注的开源云原生工具
- K8S - 创建一个 kube-scheduler 插件
- 如何诊断 Kubernetes 应用程序中的 OOMKilled 错误
- 云原生 Kubernetes 分布式存储平台 Longhorn 初体验