はじめに
書式はCheat SheetとかJSONPath Supportに書いてあるのだが、解説をつけておくと(私のような)初心者には親切だと思われるので備忘録も兼ねて。
文字列の扱いと式の表現
-
-o jsonpath='<式>'
のように' '
で囲んで記載するのが基本の書式。 -
{ }
で囲まれない場合は、単なる文字列として扱われる。
$ kubectl get pods -o jsonpath='aiueo\n'
aiueo\n
- 式(expression)は
{ }
で囲む。 - 式内部での文字列は
" "
で囲む。" "
で囲まれていない場合は、ルートからの探索対象パスとして扱われる。 - 式として評価されると、
{"¥n"}
は改行になり、{"¥t"}
はタブになる。 - 式は複数個並べることで連結される。つまり、
aaa:bbb(改行)ccc(改行)
を記載しようとする場合は、以下のような記法が考えられる。
$ kubectl get pods -o jsonpath='aaa:bbb{"\n"}ccc{"\n"}'
aaa:bbb
ccc
$ kubectl get pods -o jsonpath='{"aaa"}{" : " }{"bbb\n"}{"ccc\n"}'
aaa : bbb
ccc
#JSONPathの探索の基本
実際は、kubectl get xxx
などの結果を元にした処理が求められる。
- JSONPathの探索は式(
{ }
で囲む)で行う。 - JSONPathでデータを抽出・整形する場合は、まずその構造がどうなってたどることができるか確認するのが最初にやるべきことである。よって、
kubectl get xxx -o yaml
のように出力した後、そのデータ構造を確認するとよい。 - ルート(一番上)は
$
で表すが、kubectlなどを実施する場合は特に何もしなくてもルートから始まることになっている。そのため、{$.metadata.name}
は{.metadata.name}
と書いても良い(kubectl実行時のサンプルでは、省略しているケースの方が多い気がする)。
$ kubectl get pod busybox -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
cni.projectcalico.org/podIP: 172.17.64.80/32
cni.projectcalico.org/podIPs: 172.17.64.80/32
k8s.v1.cni.cncf.io/networks-status: |-
[{
"name": "k8s-pod-network",
"ips": [
"172.17.64.80"
],
"dns": {}
}]
openshift.io/scc: anyuid
creationTimestamp: "2020-07-12T10:53:15Z"
labels:
app: newapp
run: busybox
name: busybox
namespace: syasuda
resourceVersion: "7232141"
selfLink: /api/v1/namespaces/syasuda/pods/busybox
uid: 08a975f5-c627-4565-a97d-5dc8ae731779
spec:
containers:
- image: busybox@sha256:2131f09e4044327fd101ca1fd4043e6f3ad921ae7ee901e9142e6e36b354a907
imagePullPolicy: IfNotPresent
(以下略)
$ kubectl get pod busybox -o jsonpath='PodName : {$.metadata.name}{"\n"}NameSpace : {$.metadata.namespace}{"\n"}'
PodName : busybox
NameSpace : syasuda
$ kubectl get pod busybox -o jsonpath='PodName : {.metadata.name}{"\n"}NameSpace : {.metadata.namespace}{"\n"}'
PodName : busybox
NameSpace : syasuda
#配列
# kubectl get pod busybox -o yaml
(途中略)
spec
(途中略)
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
(途中略)
上記は、spec.tolerations
配下にObjectが2つ存在している。これはtolerations
のように複数形で書かれていることから次に配列が来ることが暗示
されており、なおかつ実際に-
で配列要素を列挙している。つまり、以下のような形になっている。
spec
tolerations
- <toleration1>
- <toleration2>
'{@.spec.tolerations}'
だと配列そのものを返す。
$ kubectl get pod busybox -o jsonpath='{@.spec.tolerations}'
[map[effect:NoExecute key:node.kubernetes.io/not-ready operator:Exists tolerationSeconds:300] map[effect:NoExecute key:node.kubernetes.io/unreachable operator:Exists tolerationSeconds:300]]
'{@.spec.tolerations[*]}'
だと配列を構成するオブジェクト(今回は2つ)を返す。
$ kubectl get pod busybox -o jsonpath='{@.spec.tolerations[*]}'
map[effect:NoExecute key:node.kubernetes.io/not-ready operator:Exists tolerationSeconds:300] map[effect:NoExecute key:node.kubernetes.io/unreachable operator:Exists tolerationSeconds:300]
'{@.spec.tolerations[]}'
と'{@.spec.tolerations[0]}'
は同じ意味っぽい。
$ kubectl get pod busybox -o jsonpath='{@.spec.tolerations[]}'
map[effect:NoExecute key:node.kubernetes.io/not-ready operator:Exists tolerationSeconds:300]
$ kubectl get pod busybox -o jsonpath='{@.spec.tolerations[0]}'
map[effect:NoExecute key:node.kubernetes.io/not-ready operator:Exists tolerationSeconds:300]
ループ処理
一般的にはkubectl get pods
のように複数のオブジェクトを対象とした操作が多いと思われるが、例えばこちらは以下のようなデータ構造になっている。 こちらもitems
配下に配列があることを暗示するためにitems
と複数形になっており、なおかつ配列を表す-
という記号から始まっている。
items
- <Pod1>
- <Pod2>
- <Pod3>
この構造を元に、Pod名と取得するために'{.items[*].metadata.name}'
と記述すると、以下のようにJSONPathで表されたPod名一覧が順に並んで表示される。
$ kubectl get pods -o jsonpath='{.items[*].metadata.name}'
busybox busybox2 hello-world-2-build hello-world-2-deploy hello-world-2-jl5r8 hello-world-2-qzb6s hello-world-2-rjjcw hello-world-2-zdgqd hello-world-2-zp7lb
もし対応するNameSpaceも取得しようして'{.items[*].metadata.name}, {.items[*].metadata.name}'
と記載すると
$ kubectl get pods -o jsonpath='{.items[*].metadata.name}{.items[*].metadata.namespace}'
busybox busybox2 hello-world-2-build hello-world-2-deploy hello-world-2-jl5r8 hello-world-2-qzb6s hello-world-2-rjjcw hello-world-2-zdgqd hello-world-2-zp7lbsyasuda syasuda syasuda syasuda syasuda syasuda syasuda syasuda syasuda
のようにPod名一覧
+NameSpace一覧
のように出力されてしまう。つまり、配列要素(今回はPod)ごとにPod名やNameSpaceを出力しようとすると、こういうラフな書き方では役に立たない。代わりに、配列要素ごとに処理を行うループ処理が必要になる。items配下の配列要素ごとに改行などを入れるというループ作業を行いたい場合は{range} ~ {end}
を利用する。
for x in myArrays
x.metadata.name + " : " + x.metadata.namespace + "\n"
endfor
を書くつもりで、以下のような感じで(.items[*]は配列を構成するオブジェクト全体に相当。各配列要素を表す変数x
に相当するものは@
にて表現可能。
range .items[*]
@.metadata.name + " : " + @.metadata.namespace + "\n"
end
のように書くつもりで、{range .items[*]}{@.metadata.name}{" : "}{@.metadata.namespace}{"\n"}{end}
と表現する。
$ kubectl get pods -o jsonpath='{range .items[*]}{@.metadata.name} : {@.metadata.namespace}{"\n"}{end}'
busybox : syasuda
busybox2 : syasuda
hello-world-2-build : syasuda
hello-world-2-deploy : syasuda
hello-world-2-jl5r8 : syasuda
hello-world-2-qzb6s : syasuda
hello-world-2-rjjcw : syasuda
hello-world-2-stcwr : syasuda
hello-world-2-tc79g : syasuda
hello-world-2-zdgqd : syasuda
hello-world-2-zp7lb : syasuda
ただし、@
は付けなくても、自動的にそれぞれの配列要素をトップディレクトリとして扱ってくれているみたいである。
$ kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name} : {.metadata.namespace}{"\n"}{end}'
busybox : syasuda
busybox2 : syasuda
hello-world-2-build : syasuda
hello-world-2-deploy : syasuda
hello-world-2-jl5r8 : syasuda
hello-world-2-qzb6s : syasuda
hello-world-2-rjjcw : syasuda
hello-world-2-stcwr : syasuda
hello-world-2-tc79g : syasuda
hello-world-2-zdgqd : syasuda
hello-world-2-zp7lb : syasuda
また、ループは複数組み合わせることが可能であり、入れ子にすることが可能。
$ kubectl get pods -o jsonpath='{range .items[*]}{@.metadata.name} : {@.metadata.namespace}{"\n"}{range @.spec.containers[*]}{@.image}{"\n"}{end}{end}'
busybox : syasuda
busybox@sha256:2131f09e4044327fd101ca1fd4043e6f3ad921ae7ee901e9142e6e36b354a907
busybox2 : syasuda
busybox:1.32.0
hello-world-2-build : syasuda
quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:ade7f38cacf5f8abfbbfc6e484af0a3b4716a6911723a919b48790bd40b2ba89
hello-world-2-deploy : syasuda
quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:100b0636c019975cdb1dee15b501428404785dfd5f259973062c510226554a66
hello-world-2-jl5r8 : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-qzb6s : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-rjjcw : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-stcwr : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-tc79g : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-zdgqd : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
hello-world-2-zp7lb : syasuda
image-registry.openshift-image-registry.svc:5000/syasuda/hello-world@sha256:6275d631745919926f3fa0669d800a64ab10045a05c161f09e561d3d4c55275a
#フィルタリング
node情報でtype=ExternalIP
のIPアドレスを取得したい場合は、以下のように記載をする。
$ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'
10.240.0.10 10.240.0.11 10.240.128.10 10.240.128.11 10.240.64.11 10.240.64.12
これは何をやっているのかをどうやっているのかを探ってみる。
まず、kubectl get nodes -o yaml
の結果は以下の通り。
$ kubectl get nodes -o yaml
items
- xxxx
(省略)
status:
addresses:
- address: 10.240.64.12
type: InternalIP
- address: 10.240.64.12
type: ExternalIP
- address: 10.240.64.12
type: Hostname
(省略)
このことから以下のことがわかる。
-
items[*]
となっているので、特定の配列要素だけでなく、全ての配列要素が対象となる。 -
.items[*].status.addresses
となっており、再び配列が存在している。この配列addresses[0],addresses[1],addresses[2]......の中から、addresses["特定の条件"]という配列要素をaddresses[?(@.type=="ExternalIP")]
の箇所で指定している。 -
?
はifを表す。つまり、if(条件式)
のような感じで書いている。 -
@
は現在の配列要素を表す。 - よって、
.status.addresses[?(@.type=="ExternalIP")]
によって、"ExternalIP"を含む配列要素のみが選択される。この配列要素のaddress
が.status.addresses[?(@.type=="ExternalIP")].address
の対象になる。
.と..は何が違うか?
- .は単なる直下のchild
- ..は直下だけでなく、その下の方まで再帰的に探す。
$ kubectl get pods -o jsonpath='{.items[*].metadata.name}'
は(他に.metadata.name
が存在しないのであれば)以下のように書くこともできるかもしれない。
$ kubectl get pods -o jsonpath='{..metadata.name}'
#ソート
$ kubectl get services --sort-by=.metadata.name
#Custom-Columns
$ kubectl get pods -A -o=custom-columns='DATA:spec.containers[*].image'
$ kubectl get pods -A -o=custom-columns='DATA:spec.containers[?(@.image!="k8s.gcr.io/coredns:1.6.2")].image'