本文发布于Cylon的收藏册,转载请著名原文链接~


kubernetes集群工具 kubect 提供了一种强大的数据提取的模式,jsonpath,相对于 yaml 来说,jsonpath 拥有高度的自制提取功能,以及一些更便于提取字段的模式,使得过去 kubernetes 资源信息时更便捷,在本文中将解开 jsonpath 的神秘面纱。

什么是jsonpath

JSONPath 是一种用于查询 JSON 数据结构中特定元素的查询语言。它类似于 XPath 用于 XML 数据的查询。JSONPath 允许您以一种简单而灵活的方式从 JSON 对象中提取数据,而不需要编写复杂的代码来解析 JSON 结构。

JSONPath 使用路径表达式来指定您要检索的 JSON 数据的位置。这些路径表达式类似于文件系统中的路径,但用于导航 JSON 结构。以下是一些常见的 JSONPath 表达式示例:

  1. $:表示 JSON 根对象。
  2. $.store:表示从根对象中获取名为 “store” 的属性。
  3. $.store.book:表示从根对象中获取 “store” 属性中的 “book” 属性。
  4. $.store.book[0]:表示获取 “store” 属性中的 “book” 属性的第一个元素。
  5. $.store.book[?(@.price < 10)]:表示选择 “store” 属性中的 “book” 属性中价格小于 10 的所有元素。
Function Description Example Result
text the plain text kind is {.kind} kind is List
@ the current object {@} the same as input
. or [] child operator {.kind} or {[‘kind’]} List
.. recursive descent {..name} 127.0.0.1 127.0.0.2 myself e2e
* wildcard. Get all objects {.items[*].metadata.name} [127.0.0.1 127.0.0.2]
[start:end :step] subscript operator {.users[0].name} myself
[,] union operator {.items[*][‘metadata.name’, ‘status.capacity’]} 127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]
?() filter {.users[?(@.name==“e2e”)].user.password} secret
range, end iterate list {range .items[*]}[{.metadata.name}, {.status.capacity}] {end} [127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]]
quote interpreted string {range .items[*]}{.metadata.name}{’\t’}{end} 127.0.0.1 127.0.0.2

JSONPath 支持各种操作符和函数,以便更复杂地筛选和操作 JSON 数据。它在 JSON 数据的导航和过滤方面非常强大,通常用于从 JSON 数据中提取所需的信息。

JSONPath 在各种编程语言和工具中都有实现,包括 JavaScript、Python、Java 等,因此您可以根据需要选择适合您项目的工具来使用 JSONPath 查询 JSON 数据。

kubectl中对jsonpath的支持

例如,通常在生产环境中处理 Kubernetes 问题时,您将需要查看数百个节点和数千个 Pod 的信息,例如 Deployment, Pod, Replicat, Service, Secret 等资源信息,但要获取这些类型的资源,通常会使用 kubectl 命令,然而在50%以上的高级场景下,是过滤信息并进行整理。在这种场景下,使用 kubectl + shell 命令进行整理的却没有 jsonpath 来的实在。假设在一个大规模集群中,例如 10 万个 Node 节点,这时如果你想获得一些节点信息,或者 Pod 信息,再或者某些需要循环的条件,这时候多次的请求对你在统计数据上造成的时间成本及频繁请求API都会造成压力,这个时候 jsonpath 的功能就很好的解决了这个问题,通过一次请求,快速循环可以在很短时间内得出结果,并减少了大量请求 kube-apiserver 的压力。

kubectl jsonpath 示例

仅获取某个资源的名称

# 语法
kubectl -n <my_namespace> get deploy/<my_deployment> -o jsonpath='{.metadata.name}'

获取一个 deployment的 信息

$ kubectl get deploy/traefik -o jsonpath='{.metadata.name}'
traefik

通常使用 json path 不会获取一个资源的信息,而是获取所有资源的信息,例如获取所有 Pod 的 name

$ kubectl get pod -A -o jsonpath="{.items[*]['metadata.name']}"
echo-hello-world-task-run-1-pod-ksdtm echo-hello-world-task-run-pod-4sx9n traefik-679bf6459c-sz9jv calico-kube-controllers-577f77cb5c-kwhph calico-node-59d5x calico-node-82zgm coredns-6b9bb479b9-wnc8n minio spin-clouddriver-88df48858-dfzkg spin-deck-5dc8f847b8-m4tbk spin-echo-69868fd866-nxn8g spin-front50-54fb4b6d67-jfkds spin-gate-7b6f4d4566-gdjkd spin-orca-7765fb5c96-9gmvr spin-redis-8485df6b88-bgrgm spin-rosco-6d77f8cb-bf2nj tekton-pipelines-controller-5cdb46974f-8rjbx tekton-pipelines-webhook-6479d769ff-756gq

Tips:jsonpath 的输出是以字符串方式输出,不会携带换行之类的

@ 的用法

@ 表示当前对象,例如 kubectl get pods 这获取的是一个 list 列表,那么 @ 就代表这个 list,例如

$ kubectl get pods -o=jsonpath='{@}'|jq
{
  "apiVersion": "v1",
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Pod",
      "metadata": {
        "annotations": {
          "cni.projectcalico.org/containerID": "aedb0d3f11b2572d82a7ccb456cec393f88de2c8befa4d19e69a577bb8c0e20f",
          "cni.projectcalico.org/podIP": "",
          "cni.projectcalico.org/podIPs": "",
          "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"tekton.dev/v1beta1\",\"kind\":\"Task\",\"metadata\":{\"annotations\":{},\"name\":\"echo-hello-world\",\"namespace\":\"default\"},\"spec\":{\"steps\":[{\"args\":[\"Hello World\"],\"command\":[\"echo\"],\"image\":\"busybox\",\"name\":\"echo\"}]}}\n",
          "pipeline.tekton.dev/release": "v0.19.0",
          "tekton.dev/ready": "READY"
        },
        "creationTimestamp": "2023-06-26T15:05:54Z",
        "labels": {
          "app.kubernetes.io/managed-by": "tekton-pipelines",
          "tekton.dev/task": "echo-hello-world",
          "tekton.dev/taskRun": "echo-hello-world-task-run-1"
        },
        "managedFields": [
          {
            "apiVersion": "v1",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:metadata": {
                "f:annotations": {
                  "f:cni.projectcalico.org/containerID": {},
                  "f:cni.projectcalico.org/podIP": {},
                  "f:cni.projectcalico.org/podIPs": {}
                }
              }
            },
            "manager": "calico",
            "operation": "Update",
            "time": "2023-06-26T15:05:55Z"
          },
          {
            "apiVersion": "v1",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:metadata": {
                "f:annotations": {
                  ".": {},
                  "f:kubectl.kubernetes.io/last-applied-configuration": {},
                  "f:pipeline.tekton.dev/release": {},
                  "f:tekton.dev/ready": {}
 ...
        "nodeName": "node01",
        "preemptionPolicy": "PreemptLowerPriority",
        "priority": 0,
        "restartPolicy": "Never",
        "schedulerName": "default-scheduler",
        "securityContext": {},
        "serviceAccount": "default",
        "serviceAccountName": "default",
        "terminationGracePeriodSeconds": 30,
        "tolerations": [
          {
            "effect": "NoExecute",
            "key": "node.kubernetes.io/not-ready",
            "operator": "Exists",
            "tolerationSeconds": 300
          },
          {
            "effect": "NoExecute",
            "key": "node.kubernetes.io/unreachable",
            "operator": "Exists",
            "tolerationSeconds": 300
          }

. 和 [] 的用法

.[] 是子操作符,用于获取到列表的元素,返回值也是 list,例如获取 Pod 列表中第一个 Pod,下面是一个 [] 的使用示例,可以获取某个元素

kubectl get pods -o=jsonpath='{.items[0]}' | jq
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "annotations": {
      "cni.projectcalico.org/containerID": "aedb0d3f11b2572d82a7ccb456cec393f88de2c8befa4d19e69a577bb8c0e20f",

...

  },
  "status": {
    "conditions": [
      {
        "lastProbeTime": null,
     
     ...
     
    "hostIP": "10.0.0.5",
    "initContainerStatuses": [
      {
        "containerID": "docker://8129d155a9e9f204a22947ae3268513a1bf4d1ce98012120ab30cfd0eca04564",
        "image": "sha256:5d54c55f19bc6fdda7629a4f2015255ec1bed2750a81909817bede45a4d360b5",
        "imageID": "docker-pullable://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/entrypoint@sha256:67fceb87f3f76baefcfdb35fd04d0ebfc8d91117dccb7f3194056d6727bac636",
        "lastState": {},
        "name": "place-tools",
        "ready": true,
        "restartCount": 0,
        "state": {
          "terminated": {
            "containerID": "docker://8129d155a9e9f204a22947ae3268513a1bf4d1ce98012120ab30cfd0eca04564",
            "exitCode": 0,
            "finishedAt": "2023-06-26T15:05:55Z",
            "reason": "Completed",
            "startedAt": "2023-06-26T15:05:55Z"
          }
        }
      }
    ],
    "phase": "Succeeded",
    "podIP": "10.244.196.131",
    "podIPs": [
      {
        "ip": "10.244.196.131"
      }
    ],
    "qosClass": "BestEffort",
    "startTime": "2023-06-26T15:05:54Z"
  }
}

. 是 [] 的子操作符,可以获取某一个元素下的某个值,与 json 中语法相同,例如获取 Pod 列表中==第一个 Pod 的名称==

$ kubectl get pods -o=jsonpath='{.items[0].metadata.name}'
echo-hello-world-task-run-1-pod-ksdtm

下标操作符 :

下标操作符 “:” 可以视为一个切片,获取列表中某一些元素,语法为 [start:end :step]

例如获取前三个元素的名称

kubectl get pods -o=jsonpath='{.items[0:2].metadata.name}'

获取最后两个元素

$ kubectl get pods -n spinnaker -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
spin-clouddriver-88df48858-dfzkg
spin-deck-5dc8f847b8-m4tbk
spin-echo-69868fd866-nxn8g
spin-front50-54fb4b6d67-jfkds
spin-gate-7b6f4d4566-gdjkd
spin-orca-7765fb5c96-9gmvr
spin-redis-8485df6b88-bgrgm
spin-rosco-6d77f8cb-bf2nj

$ kubectl get pods -n spinnaker -o jsonpath='{range .items[-2:]}{.metadata.name}{"\n"}{end}'
spin-redis-8485df6b88-bgrgm
spin-rosco-6d77f8cb-bf2nj

过滤表达式

如果设置了 limit 参数则打印其名称

kubectl get pods -n spinnaker -o jsonpath='{.items[?(@.spec.containers[*].resources.limits.memory != "")].metadata.name}'

价格大于 10 的 元素的 name

"$.store.book[?(@.price > 10)].name"

json path 支持下列过滤操作符参考 [2]

Operator Description
== 等于。字符串值必须用单引号(而不是双引号)括起来:[?(@.color=='red')]。注意:数字与字符串比较的工作方式因播放引擎而异。在 TestEngine 中,1 不等于 “1”。在 ReadyAPI 1.9 及更早版本中,1 等于 “1”。
!= 不等于,字符串值必须用单引号括起来:[?(@.color!='red')]
> 大于
>= 大于或等于
< 小于
<= 小于或等于
=~ 匹配 JavaScript 正则表达式。例如,[?(@.description =~ /cat.*/i)] 匹配描述以 cat 开头的项目(不区分大小写)。注意:如果使用 ReadyAPI 1.1 作为播放引擎,则不支持。
! 用于否定过滤器:[?(!@.isbn)] 匹配不具有 isbn 属性的项目。注意:如果使用 ReadyAPI 1.1 作为播放引擎,则不支持。
&& 逻辑与 AND,用于组合多个过滤表达式: [?(@.category=='fiction' && @.price < 10)]
|| 逻辑或 OR Logical OR, 用于组合多个过滤表达式: `[?(@.category==‘fiction’
in 检查左侧值是否存在于右侧列表中。类似于 SQL IN 运算符。字符串比较区分大小写。 [?(@.size in ['M', 'L'])] [?('S' in @.sizes)] 注意:仅由 TestEngine 播放引擎支持。
nin 与 in 相反。检查左侧值是否不存在于右侧列表中。字符串比较区分大小写。 [?(@.size nin ['M', 'L'])] [?('S' nin @.sizes)] 注意:仅由 TestEngine 播放引擎支持。
contains 检查字符串是否包含指定的子字符串(区分大小写),或者数组是否包含指定的元素。
[?(@.name contains 'Alex')] [?(@.numbers contains 7)] [?('ABCDEF' contains @.character)]
注意:仅由 TestEngine 播放引擎支持。
size 检查数组或字符串是否具有指定的长度。 [?(@.name size 4)] 注意:仅由 TestEngine 播放引擎支持。
empty true 匹配空数组或字符串。 [?(@.name empty true)] 注意:仅由 TestEngine 播放引擎支持。
empty false 匹配非空数组或字符串。 [?(@.name empty false)] 注意:仅由 TestEngine 播放引擎支持。

正则表达式

kubectl jsonpath 不支持正则表达式,如果需要使用正则表达式可以使用 jq 替换

例如获取 spin 开头的 Pod 是否配置了 resources

kubectl get pods -n spinnaker -o json | jq -r '.items[] | select(.metadata.name | test("spin-")).spec.resources'

example

$ kubectl get pods -n spinnaker -o json | jq -r '.items[] | select(.metadata.name | test("spin-")).spec.resources'
null
null
null
null
null
null
null
null

kubectl example 示例

获取 Pod 为 web 的 Pod name

kubectl get pods -o  jsonpath  =  '{.items[?(@.metadata.labels.name=="web")].metadata.name}' 

过滤一个元素

过滤 Node 地址模式

kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")]}'

过滤元素并只打印想要的属性

kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}'

从数组中获取每个元素的单个字段

kubectl get pods -o jsonpath={$.items[*].status.hostIP}

换行

jsonpath 在获取元素后,是一个单行字符串,如果需要换行操作可以使用下面示例

$ kubectl get pods -o jsonpath='{range .items[*]}{.status.hostIP}{"\n"}{end}'
10.0.0.5
10.0.0.5
10.0.0.5

循环并获得多个元素

如果想获取两个元素,可以使用下面示例

$ kubectl get pods -o jsonpath={range .items[*]}{.status.hostIP}{"\t"}{.status.phase}{"\n"}{end}
10.154.196.228	Running
10.154.202.136	Running
10.154.201.54	Running

递归

如果想获取一个元素下所有相同名称的字段,可以使用下面示例

递归获取所有 name 字段

$ kubectl get pod -A -o jsonpath='{..name}'
step-echo place-tools echo-hello-world-task-run-1-pod-ksdtm echo-hello-world-task-run-1 tekton-internal-workspace tekton-internal-home tekton-internal-results tekton-internal-tools tekton-internal-downward tekton-creds-init-home-tlvg8 default-token-6762j step-echo HOME tekton-internal-tools tekton-internal-downward tekton-creds-init-home-tlvg8 tekton-internal-workspace tekton-i..
...

巧用递归简化语句,如果一个元素 (元素名称) 是独有的,那么可以使用递归直接获取到这个元素的值,例如,过去所有 containers

$ kubectl get pods -A -o=jsonpath='{range .items[*]}{"pod: "}{.metadata.name} {"\n"}{range ..containers[*]}{"\tcontainer: "}{.name}{"\n\timage: "}{.image}{"\n"}{end}{end}'
pod: echo-hello-world-task-run-1-pod-ksdtm 
	container: step-echo
	image: busybox
pod: echo-hello-world-task-run-pod-4sx9n 
	container: step-echo

获取每个 Pod limit 值

kubectl  get pod -A -o jsonpath='{range $.items[*]}{"Pod: "}{.metadata.name}{"\n"} {"  limit_mem: "}{.spec.containers[*].resources.limits.memory}{"\n"}{end}'
Pod: echo-hello-world-task-run-1-pod-ksdtm
   limit_mem: 
Pod: echo-hello-world-task-run-pod-4sx9n
   limit_mem: 
Pod: traefik-679bf6459c-sz9jv
   limit_mem: 
Pod: calico-kube-controllers-577f77cb5c-kwhph
   limit_mem: 
Pod: calico-node-59d5x
   limit_mem: 
Pod: calico-node-82zgm
   limit_mem: 
Pod: coredns-6b9bb479b9-wnc8n
   limit_mem: 170Mi
Pod: minio
   limit_mem: 
Pod: spin-clouddriver-88df48858-dfzkg
   limit_mem: 
Pod: spin-deck-7fbf94d8bc-k7zrk
   limit_mem: 200Mi
Pod: spin-echo-69868fd866-nxn8g
   limit_mem: 
Pod: spin-front50-54fb4b6d67-jfkds
   limit_mem: 
Pod: spin-gate-6d7dbf74b9-4l89r
   limit_mem: 600Mi
Pod: spin-orca-7765fb5c96-9gmvr
   limit_mem: 
Pod: spin-redis-8485df6b88-bgrgm
   limit_mem: 
Pod: spin-rosco-6d77f8cb-bf2nj
   limit_mem: 
Pod: tekton-pipelines-controller-5cdb46974f-8rjbx
   limit_mem: 
Pod: tekton-pipelines-webhook-6479d769ff-756gq
   limit_mem: 500Mi

获取容器的 IP

获取单独一个 Pod

kubectl  get pod nginx-67d5fc57d8-jkfjp -n quota-example  -o jsonpath='{.status.podIPs[].ip}{"\n"}'

循环获取

kubectl  get pod  -o jsonpath='{range $.items[*]}{.status.podIPs[].ip}{"\n"}{end}'
10.244.196.131
10.244.196.129
10.244.196.186

获取所有的 Container ID 和 Pod IP

kubectl get pods --all-namespaces    -o=jsonpath='{range .items[*]}[{.status.containerStatuses[0].containerID}, {.status.podIP}]{"\n"}{end}'

获取所有的容器名称和镜像名称

kubectl get pods -n kube-system -o=jsonpath='{range .items[*]}[{.metadata.name},{.status.containerStatuses[0].image}]{"\n"}{end}'

获取所有状态条件中的类型

kubectl get pod cm-test-pod -o jsonpath='{.status.conditions[*].type}'

获取 Pod 的 apiversion

kubectl get pod cm-test-pod -o jsonpath='{.apiVersion}'

从第一个状态条件开始到最后一个结束,每隔2个获取一次

kubectl get pod cm-test-pod -o jsonpath='{.status.conditions[0:3:2].type}'

Reference

[1] jsonpath

[2] JSONPath Syntax

[3] k8s学习-kubectl命令行 jsonpath的使用

本文发布于Cylon的收藏册,转载请著名原文链接~

链接:https://www.oomkill.com/2023/09/kubectl-jsonpath/

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。