jqで以下のことをやりたくて、ちょっと悩んだ末にできたのでメモ。
やりたかったこと
以下のような配列を含む1つのオブジェクトを、
{
"pod": {
"name": "mypod"
},
"containers": [
{
"name": "mycontainer1",
"usage": {
"cpu": "50"
}
},
{
"name": "mycontainer2",
"usage": {
"cpu": "100"
}
}
]
}
以下のような複数のオブジェクトにしたい。
{
"pod_name": "mypod",
"container_name": "mycontainer1",
"container_cpu": "50"
}
{
"pod_name": "mypod",
"container_name": "mycontainer2",
"container_cpu": "100"
}
やり方
jq ManualのObject Constructionのところに解説があるが、新たなオブジェクトを作り、このときにcontainers
配列の皮むき([]
)を行うことで、別々のオブジェクトに展開できる。
入れ子の配列の展開
cat sample.json | \
jq '{
pod,
container: .containers[]
}'
{
"pod": {
"name": "mypod"
},
"container": {
"name": "mycontainer1",
"usage": {
"cpu": "50"
}
}
}
{
"pod": {
"name": "mypod"
},
"container": {
"name": "mycontainer2",
"usage": {
"cpu": "100"
}
}
}
(補足)pod
のところは省略した形式で、元のフィールドをそのまま渡しているが、ちゃんと書くこともできる。
cat sample.json | \
jq '{
pod: { name: .pod.name },
container: .containers[]
}'
オブジェクトの生成
展開した結果から欲しい情報をピックアップして新たなオブジェクトにする。
cat sample.json | \
jq '{
pod,
container: .containers[]
} |
{
pod_name: .pod.name,
container_name: .container.name,
container_cpu: .container.usage.cpu
}'
{
"pod_name": "mypod",
"container_name": "mycontainer1",
"container_cpu": "50"
}
{
"pod_name": "mypod",
"container_name": "mycontainer2",
"container_cpu": "100"
}
注意点
中間オブジェクトを経由せずにオブジェクトをつくると結果がかけ算になりオブジェクトの数が多くなってしまう。
cat sample.json | \
jq '{
pod_name: .pod.name,
container_name: .containers[].name,
container_cpu: .containers[].usage.cpu
}'
{
"pod_name": "mypod",
"container_name": "mycontainer1",
"container_cpu": "50"
}
{
"pod_name": "mypod",
"container_name": "mycontainer1",
"container_cpu": "100"
}
{
"pod_name": "mypod",
"container_name": "mycontainer2",
"container_cpu": "50"
}
{
"pod_name": "mypod",
"container_name": "mycontainer2",
"container_cpu": "100"
}
中間オブジェクトを経由せずに配列をつくると元々の配列の要素がそのまま並んでしまう。
cat sample.json | \
jq '[
.pod.name,
.containers[].name,
.containers[].usage.cpu
]'
[
"mypod",
"mycontainer1",
"mycontainer2",
"50",
"100"
]
おまけ
もともとやりたかったことは、KubernetesのMetric APIから以下のようなjsonが返ってくるので、これをcsvにすること。以下はIBM Cloud Private v3.1.2の場合。
{
"kind": "PodMetricsList",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/metrics.k8s.io/v1beta1/pods"
},
"items": [
{
"metadata": {
"name": "audit-logging-fluentd-ds-pgp88",
"namespace": "kube-system",
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/audit-logging-fluentd-ds-pgp88",
"creationTimestamp": "2019-04-10T01:22:42Z"
},
"timestamp": "2019-04-10T01:22:17Z",
"window": "30s",
"containers": [
{
"name": "fluentd",
"usage": {
"cpu": "2513099n",
"memory": "62276Ki"
}
}
]
},
(以下略)
トークンの取得
cloudctl login
した後、cloudclt tokens
でトークンを取得する。
ID_TOKEN=$(LANG=C cloudctl tokens | grep "ID token:" | awk '{print $3}')
Metric APIからのデータの取得
curlでjsonデータを取得して変数に入れる。
CLUSTER="mycluster.icp"
JSON=$(curl -s -k -H "Authorization: Bearer ${ID_TOKEN}" \
"https://${CLUSTER}:8001/apis/metrics.k8s.io/v1beta1/pods" \
| jq -c '.')
jqを使ってjsonをcsvに変換
jqを使って配列を複数のオブジェクトに展開した中間データを作成し、そこから欲しいデータをピックアップしてそれぞれを配列にし、@csv
を使ってcsvにする。ついでにrtrimstr(str)
関数を使って単位を除く。
echo $JSON | \
jq -r '.items[] |
{
metadata,
timestamp,
container: .containers[]
} |
[
.timestamp,
.metadata.namespace,
.metadata.name,
.container.name,
( .container.usage.cpu | rtrimstr("n") ),
( .container.usage.memory | rtrimstr("Ki") )
] | @csv'
"2019-04-10T02:35:25Z","kube-system","monitoring-prometheus-collectdexporter-57b8c6ff5c-dclw4","collectd-exporter","1488183","11416"
"2019-04-10T02:35:25Z","kube-system","monitoring-prometheus-collectdexporter-57b8c6ff5c-dclw4","router","54112","4532"
"2019-04-10T02:35:09Z","kube-system","logging-elk-filebeat-ds-2x49r","filebeat","4745057","28692"
"2019-04-10T02:35:20Z","kube-system","monitoring-prometheus-elasticsearchexporter-7f5967f56-d68sd","router","12534","8312"
"2019-04-10T02:35:20Z","kube-system","monitoring-prometheus-elasticsearchexporter-7f5967f56-d68sd","elasticsearchexporter","3207930","13132"
"2019-04-10T02:35:10Z","kube-system","k8s-etcd-9.188.124.130","etcd","50751515","261432"
(以下略)