Extraindo informações úteis do seu cluster Kubernetes com custom-columns e jq
Como montar consultas customizadas para os objetos do seu cluster Kubernetes e como criar um repertório de consultas.
Imagem por: max_duz em: unsplash.com/photos/qAjJk-un3BI
É comum ao trabalharmos com k8s realizarmos diversas consultas aos nossos objetos do cluster como nodes, deployments, builds, pods e nem sempre temos o conjunto de informações que necessitamos, expostas por padrão via kubeclt get
, tendo que nesses casos recorrer a busca de todo o objeto trazendo informações além das desejadas.
Usando a opção de saída custom-columns
do kubectl
e a ferramenta jq
podemos criar consultas que trazem especificamente o que desejamos. Nesse artigo vamos explorar os dois e aprender como criar um repertório de consultas customizadas.
Problema
Vamos considerar dois cenários comuns em que precisamos buscar informações de um cluster kubernetes:
- Recuperar informações da saúde dos nós do cluster como gargalos de memória, cpu, disco.
- Recuperar informações de variáveis de ambiente (
env
) e limites de recursos (resource
elimits
) dedeployments
no cluster.
Para recuperar informações dos objetos do cluster, de maneira geral, podemos utilizar o comando:
kubectl get <OBJETO>
Para consultar os nodes podemos executar o comando:
~ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
ip-10-0-135-204.xyz.compute.internal Ready master 11d v1.21.1+6438632 10.0.135.204 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
ip-10-0-142-176.xyz.compute.internal Ready worker 11d v1.21.1+6438632 10.0.142.176 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
ip-10-0-160-187.xyz.compute.internal Ready master 11d v1.21.1+6438632 10.0.160.187 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
ip-10-0-176-188.xyz.compute.internal Ready worker 11d v1.21.1+6438632 10.0.176.188 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
ip-10-0-214-226.xyz.compute.internal Ready master 11d v1.21.1+6438632 10.0.214.226 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
ip-10-0-219-74.xyz.compute.internal Ready worker 11d v1.21.1+6438632 10.0.219.74 <none> RHEL CoreOS 48.84.202110270303-0 4.18.0-305.19.1.el8_4.x86_64 cri-o://1.21.3-8.rhaos4.8.git7415a53.el8
Para consultar os deployments podemos utilizar o comando:
# também é possível usar o -o wide para recuperar mais informações
~ kubectl get deployments --all-namespaces
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
openshift-apiserver-operator openshift-apiserver-operator 1/1 1 1 11d
openshift-apiserver apiserver 3/3 3 3 11d
openshift-cluster-storage-operator cluster-storage-operator 1/1 1 1 11d
openshift-cluster-storage-operator csi-snapshot-controller 2/2 2 2 11d
openshift-cluster-version cluster-version-operator 1/1 1 1 11d
openshift-console-operator console-operator 1/1 1 1 11d
openshift-console console 2/2 2 2 11d
openshift-image-registry cluster-image-registry-operator 1/1 1 1 11d
openshift-ingress-operator ingress-operator 1/1 1 1 11d
openshift-ingress router-default 2/2 2 2 11d
openshift-insights insights-operator 1/1 1 1 11d
Os dois comandos, apesar de trazerem bastante informações, não contém as informações que estamos buscando. Para recuperar as informações que precisamos, podemos recuperar os objetos completos, no formato yaml
ou json
, através do comando: kubectl get deployments --all-namespaces -o json
A saída do comando é a que segue:
{
"apiVersion": "v1",
"items": [
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "openshift-apiserver-operator",
"namespace": "openshift-apiserver-operator"
},
"spec": {
"template": {
"spec": {
"containers": [
{
"env": [
{
"name": "IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:f532d4e20932e1e6664b1b7003691d44a511bb626bc339fd883a624f020ff399"
},
{
"name": "OPERATOR_IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:a24bdc7bae31584af5a7e0cb0629dda9bb2b1d613a40e92e227e0d13cb326ef4"
},
{
"name": "OPERATOR_IMAGE_VERSION",
"value": "4.8.19"
},
{
"name": "OPERAND_IMAGE_VERSION",
"value": "4.8.19"
},
{
"name": "KUBE_APISERVER_OPERATOR_IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:0e56e34f980552a7ce3d55429a9a265307dc89da11c29f6366b34369cc2a9ba0"
}
],
"resources": {
"requests": {
"cpu": "10m",
"memory": "50Mi"
}
}
}
]
}
}
}
},
// outras informações...
],
"kind": "List",
"metadata": {
"resourceVersion": "",
"selfLink": ""
}
}
Utilizando custom-columns para consultar nodes
Vamos explorar a opção de saída custom-columns
do comando kubectl get
para recuperar apenas informações que precisamos. A opção custom-columns
nos permite definir quais dados serão extraidos através do mapeamento do título da coluna e o campo desejado.
Utilizando o json do node como base para montar nossa consulta
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"name": "ip-10-0-219-74.xyz.compute.internal"
},
"status": {
"addresses": [
{
"address": "10.0.219.74",
"type": "InternalIP"
},
{
"address": "ip-10-0-219-74.xyz.compute.internal",
"type": "Hostname"
},
{
"address": "ip-10-0-219-74.xyz.compute.internal",
"type": "InternalDNS"
}
],
"conditions": [
{
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "327671fc-3d6f-4bc4-ab5f-fa012687e839",
"containerRuntimeVersion": "cri-o://1.21.3-8.rhaos4.8.git7415a53.el8",
"kernelVersion": "4.18.0-305.19.1.el8_4.x86_64",
"kubeProxyVersion": "v1.21.1+6438632",
"kubeletVersion": "v1.21.1+6438632",
"machineID": "ec2e23b2f3d554c78f67dc2e30ba230a",
"operatingSystem": "linux",
"osImage": "Red Hat Enterprise Linux CoreOS 48.84.202110270303-0 (Ootpa)",
"systemUUID": "ec2e23b2-f3d5-54c7-8f67-dc2e30ba230a"
}
}
}
Uma consulta simples utilizando custom-columns
para retornar o nome dos nodes do cluster:
~ kubectl get nodes -o custom-columns="Name:.metadata.name"
Name
ip-10-0-135-204.xyz.compute.internal
ip-10-0-142-176.xyz.compute.internal
ip-10-0-160-187.xyz.compute.internal
ip-10-0-176-188.xyz.compute.internal
ip-10-0-214-226.xyz.compute.internal
ip-10-0-219-74.xyz.compute.internal
Para consultar valores de um grupo, como por exemplo, os endereços dos nodes (InternalIP, Hostname, InternalDNS) podemos usar a notação .status.addresses[*].address
~ kubectl get nodes -o custom-columns="Name:.metadata.name,Addresses:.status.addresses[*].address"
Name Addresses
ip-10-0-135-204.xyz.compute.internal 10.0.135.204,ip-10-0-135-204.xyz.compute.internal,ip-10-0-135-204.xyz.compute.internal
ip-10-0-142-176.xyz.compute.internal 10.0.142.176,ip-10-0-142-176.xyz.compute.internal,ip-10-0-142-176.xyz.compute.internal
ip-10-0-160-187.xyz.compute.internal 10.0.160.187,ip-10-0-160-187.xyz.compute.internal,ip-10-0-160-187.xyz.compute.internal
ip-10-0-176-188.xyz.compute.internal 10.0.176.188,ip-10-0-176-188.xyz.compute.internal,ip-10-0-176-188.xyz.compute.internal
ip-10-0-214-226.xyz.compute.internal 10.0.214.226,ip-10-0-214-226.xyz.compute.internal,ip-10-0-214-226.xyz.compute.internal
ip-10-0-219-74.xyz.compute.internal 10.0.219.74,ip-10-0-219-74.xyz.compute.internal,ip-10-0-219-74.xyz.compute.internal
Caso queiramos valores específicos de um grupo, podemos utilizar o índice desejado para tal, logo, para montar nossa consulta de saúde dos nodes:
~ kubectl get nodes -o custom-columns="Name:.metadata.name,InternalIP:.status.addresses[0].address,Kernel:.status.nodeInfo.kernelVersion,MemoryPressure:.status.conditions[0].status,DiskPressure:.status.conditions[1].status,PIDPressure:.status.conditions[2].status,Ready:.status.conditions[3].status"
Name Kernel InternalIP MemoryPressure DiskPressure PIDPressure Ready
ip-10-0-135-204.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.135.204 False False False True
ip-10-0-142-176.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.142.176 False False False True
ip-10-0-160-187.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.160.187 False False False True
ip-10-0-176-188.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.176.188 False False False True
ip-10-0-214-226.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.214.226 False False False True
ip-10-0-219-74.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.219.74 False False False True
Criando um repertório de consultas
Com nossa consulta customizada pronta, podemos guardar o mapeamento dos campos para facilitar o reuso. O arquivo segue um formato específico de cabeçalhos e valores:
HEADER1 HEADER2 HEADER3
.field.value1 .field.value2 .field.value3
Para nossa consulta, o arquivo, que vamos chamar de cluster-nodes-health.txt
, seria:
Name Kernel InternalIP MemoryPressure DiskPressure PIDPressure Ready
.metadata.name .status.nodeInfo.kernelVersion .status.addresses[0].address .status.conditions[0].status .status.conditions[1].status .status.conditions[2].status .status.conditions[3].status
E podemos realizar a consulta utilizando a opção custom-columns-file
:
~ kubectl get nodes -o custom-columns-file=cluster-nodes-health.txt
Name Kernel InternalIP MemoryPressure DiskPressure PIDPressure Ready
ip-10-0-135-204.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.135.204 False False False True
ip-10-0-142-176.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.142.176 False False False True
ip-10-0-160-187.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.160.187 False False False True
ip-10-0-176-188.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.176.188 False False False True
ip-10-0-214-226.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.214.226 False False False True
ip-10-0-219-74.xyz.compute.internal 4.18.0-305.19.1.el8_4.x86_64 10.0.219.74 False False False True
Utilizando jq
para consultar env de deployments
Para consultar os envs
vamos explorar o utilitário jq
, com ele iremos buscar os objetos como json e filtrá-los para mostrar apenas as informações que desejamos.
Sobre o jq
O jq
é um processador JSON de linha de comando leve e flexível.
Conforme o própria página do jq o descreve:
"jq é como o sed para dados JSON - você pode usá-lo para dividir, filtrar, mapear e transformar dados estruturados com a mesma facilidade que sed, awk, grep."
Ele pode ser encontrado em: stedolan.github.io/jq
Estruturando o comando jq
Vamos mostrar uma consulta básica com o jq
. Que intera sobre os deployments .items[]
e extrair apenas o nome deles .metadata.name
.
~ kubectl get deployments --all-namespaces -o json | jq -r '.items[] | .metadata.name '
openshift-apiserver-operator
apiserver
authentication-operator
# outros projetos...
Vamos evoluir nossa consulta para montar um json com as informações de name
, namespace
e env
:
~ kubectl get deployments --all-namespaces -o json | jq -r '.items[] | { namespace: .metadata.namespace, name: .metadata.name, env: .spec.template.spec.containers[].env}'
{
"namespace": "openshift-apiserver-operator",
"name": "openshift-apiserver-operator",
"env": [
{
"name": "IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:f532d4e20932e1e6664b1b7003691d44a511bb626bc339fd883a624f020ff399"
},
{
"name": "OPERATOR_IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:a24bdc7bae31584af5a7e0cb0629dda9bb2b1d613a40e92e227e0d13cb326ef4"
},
{
"name": "OPERATOR_IMAGE_VERSION",
"value": "4.8.19"
},
{
"name": "OPERAND_IMAGE_VERSION",
"value": "4.8.19"
},
{
"name": "KUBE_APISERVER_OPERATOR_IMAGE",
"value": "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:0e56e34f980552a7ce3d55429a9a265307dc89da11c29f6366b34369cc2a9ba0"
}
]
}
{
"namespace": "openshift-apiserver",
"name": "apiserver",
"env": [
{
"name": "POD_NAME",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.name"
}
}
},
{
"name": "POD_NAMESPACE",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.namespace"
}
}
}
]
}
{
"namespace": "openshift-apiserver",
"name": "apiserver",
"env": [
{
"name": "POD_NAME",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.name"
}
}
},
{
"name": "POD_NAMESPACE",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.namespace"
}
}
}
]
}
// campos omitidos....
Para deixar nosso json num formato válido, vamos encapsular os resultados em um array []
e usar função map
do jq
.
~ kubectl get deployments --all-namespaces -o json | jq -r '.items | [ map(.) | .[] | { namespace: .metadata.namespace, name: .metadata.name, env: .spec.template.spec.containers[].env }]'
// exemplo resumido de output
[
{
"namespace": "openshift-operator-lifecycle-manager",
"name": "catalog-operator",
"env": [
{
"name": "RELEASE_VERSION",
"value": "4.8.19"
}
]
},
{
"namespace": "openshift-operator-lifecycle-manager",
"name": "olm-operator",
"env": [
{
"name": "RELEASE_VERSION",
"value": "4.8.19"
},
{
"name": "OPERATOR_NAMESPACE",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.namespace"
}
}
},
{
"name": "OPERATOR_NAME",
"value": "olm-operator"
}
]
},
{
"namespace": "openshift-operator-lifecycle-manager",
"name": "packageserver",
"env": [
{
"name": "OPERATOR_CONDITION_NAME",
"value": "packageserver"
}
]
}
]
Consulta com jq
file
Assim como no custom-columns
, com o jq
temos a opção de passar um arquivo contendo nosso filtro ao invés de dados inline. Desse modo, vamos criar um arquivo chamado jq-deployments-envs.txt
com o conteúdo:
.items | [ map(.) | .[] | { namespace: .metadata.namespace, name: .metadata.name, env: .spec.template.spec.containers[].env }]
E nossa consulta pode ser executada com o comando:
~ kubectl deployments --all-namespaces -o json | jq -f jq-deployments-envs.txt
Conclusão
Com a opção nativa do kubectl
, custom-columns
, e o utilitário jq
é possível extrair informações customizadas de um cluster Kubernetes. Além disso, com a opção de usar arquivos para montar as consultas podemos criar diversas visualizações úteis para o cluster e armazená-los no controle de versão para compartilhamento com outros integrantes do time ou para a comunidade.
Referências
kubernetes.io/docs/reference/kubectl/overvi..
kubernetes.io/pt-br/docs/reference/kubectl/..
stedolan.github.io/jq/tutorial
kubernetes.io/docs/tasks/access-application..
kubernetes.io/docs/reference/kubectl/jsonpath
gist.github.com/so0k/42313dbb3b547a0f51a547..
starkandwayne.com/blog/silly-kubectl-trick-..
michalwojcik.com.pl/2021/07/04/yaml-jsonpat..
laury.dev/snippets/combine-kubectl-jsonpath..