Una din provocările administrării unui cluster de Kubernetes este găsirea rapidă și sigură a resurselor: noduri, poduri, servicii, persistent volumes, pvc, etc., precum și sortarea lor în funcție de anumite caracteristici ale fiecărui tip de resurse. Dacă în cazul unui cluster K8s cu câteva noduri și aplicații instalate a găsi ceea ce ne interesează nu este o problemă majoră, putând fi folosită clasica comandă kubectl
cu opțiunile sale, situația se schimbă în situația unor clustere cu zeci sau sute de noduri și mii de poduri și alte resurse: este practic imposibil să stai să "descrii" fiecare resursă în parte pentru a găsi ceea ce te interesează sau să le sortezi în funcție de anumite valori. Aici intervine JSONPath în Kubernetes.
Fiecare resursă din Kubernetes poate fi citită în format json. Astfel, navigând prin structura fișierului json (dicționare, liste, array-uri, etc.), putem selecta exact valoarea câmpului care ne interesează și să o afișăm pe ecran. Voi intra în amănunte în cele ce urmează.
Înainte de a începe vizualizarea și sortarea resurselor, este indicat să afișați structura resursei respective în format json (asta în cazul în care nu o știți pe de rost...) cu o comandă de forma: kubectl get nume_resursă -ojson
. De fiecare dată când folosim kubectl
, interacționăm cu kube-apiserver-ul clusterului de Kubernetes. Kube-apiserver-ul trimite rezultatul în format json, iar kubectl îl convertește într-un format ușor de citit de căte utilizator, format care este afișat pe ecran.
Să luăm exemplul unui nod care este și masterul unui cluster k8s construit în KVM. Comanda kubectl get nodes
ne afișează nodurile clusterului într-un format pe care îl putem înțelege instantaneu, foarte multă informație care vine în acest format fiind ascunsă, pentru a se face ușor de citit și de interpretat:
Pentru a putea vedea mai multe informații, putem folosi opțiunea -o wide
, printându-se informații adiționale, dar, din nou, ieșirea nu este completă (există multe detalii care nu sunt parte din output, cum ar fi resursele nodului, arhitectura hardware, distribuția Linux instalată, etc.):
Putem vedea detaliile lipsă dând comanda kubectl describe ...
, dar cum ar fi dacă le-am vedea pe toate într-un singur raport? Cum ar fi lista nodurilor cu numărul de procesoare de pe fiecare nod, sau lista tuturor taint-urilor de pe noduri. Nicio comandă builtin nu permite afișarea acestor rapoarte așa cum ne dorim. Aici ne pot ajuta interogările JSONPath - putem filtra și formata ieșirea după dorință.
Comanda kubectl get nodes -ojson
ne returnează rezultatul în format json, format care este mai greu de interpretat la prima interacțiune cu el:
Urmează studierea și familiarizarea cu formatul json, apoi identificarea căii fiecărei informație pe care o dorim în raportul final și construirea interogării folosind dot notation (de exemplu, conform imaginii de mai sus, pentru a ajunge la numele nodului, trebuie să trecem prin metadata și name - query-ul va fi $.metadata.name
; cheia si efectul taint-ul nodului se află într-un array la care ajungem trecând prin spec și taints - query-ul va fi $.items[*].spec.taints[*].effect
; pentru a afla imaginea folosită de containerul dintr-un pod, interogarea va fi $.items[0].spec.containers[0].image
).
La final, după construirea interogării, vom folosi acest query JSONPath împreună cu comanda kubectl
. Pentru a face acest lucru, vom folosi opțiunea -o=jsonpath
a comenzii kubectl, căreia îi vom trece ca argument query-ul pe care tocmai l-am construit la pasul anterior (trebuie doar să ne amintim că este obligatoriu să încapsulăm query-ul JSONPath într-o pereche de ghilimele simple și acolade - semnul $, care reprezintă rădăcina interogării, nu este obligatoriu, căci kubectl îl adaugă automat):
kubectl get nodes -o=jsonpath='{.items[*].metadata.name}'
kube-master worker0 worker1
Pentru a construi interogări corecte (dar și pentru a ne obișnui cu JSONPath), se poate folosi cu încredere evaluatorul online jsonpath.com, după care putem muta query-ul în interiorul unei comenzi kubectl.
Exemple de interogări JsonPath în Kubernetes
kubectl get nodes -o=jsonpath='{.items[*].metadata.name}'
kube-master worker0 worker1
- afișarea arhitecturii tuturor nodurilor din cluster:
kubectl get no -o=jsonpath='{.items[*].status.nodeInfo.architecture}'
amd64 amd64 amd64
- afișarea numărului de procesoare de pe fiecare nod:
kubectl get no -o=jsonpath='{.items[*].status.capacity.cpu}'
2 2 2
- afișarea imaginilor containerelor tuturor podurilor din namespace-ul default:
kubectl get po -o=jsonpath='{.items[*].spec.containers[0].image}'
nginx
Putem folosi mai multe interogări într-o singură comandă. De exemplu, combinăm primul query în care aflam numele nodurilor, cu cel în care afișăm numărul de procesoare de pe fiecare nod și vom avea rezultatul într-o singură comandă:
kubectl get nodes -o=jsonpath='{.items[*].metadata.name}{.items[*].status.capacity.cpu}'
kube-master worker0 worker12 2 2
Rezultatul, deși corect, nu arată prea bine...
Formatarea ieșirii jsonPath în Kubernetes
{"\n"} - adăugarea unei linii noi
Punând rezultatul comenzii anterioare pe câte o linie nouă, vom obține:
kubectl get nodes -o=jsonpath='{.items[*].metadata.name} {"\n"} {.items[*].status.capacity.cpu}'
kube-master worker0 worker1
2 2 2
Tot nu arată frumos...
Loops - Range
Vom căuta ca, pentru fiecare nod, să-i afișăm numele pe rând separat, în dreptul numelui nodului fiind și numărul de procesoare, separate printr-un TAB. Putem transcrie acest lucru în JSONPath folosind cuvintele cheie range și end pentru a crea liste iterabile:
- '{range .items[*]}' --> înseamnă că pentru fiecare item se vor executa interogările ulterioare
- urmeaza query-ul propriu-zis, format din celelalte 2 interogări inițiale separate prin TAB-uri, la final fiind câte o linie nouă
- '{end}' --> cuvânt cheie care finalizează bucla
{"\t"} - adăugarea unui TAB
La final, unind totul într-o singură linie și trecând-o ca parametru comenzii kubectl
, vom obține (altfel spus, pentru fiecare item - nod în cazul nostru - se afișează numele lui, se inserează un TAB, se afișează numărul de CPU, apoi se trece la o linie nouă și se reiau iterațiile):
kubectl get nodes -o=jsonpath='{range.items[*]} {.metadata.name} {"\t"} {.status.capacity.cpu} {"\n"} {end}'
JsonPath în Kubernetes pentru Custom Columns
Custom Columns este o opțiune care îmi place foarte mult: ușor de utilizat și cu rezultate curate. Comanda kubectl
cu opțiunea custom columns ar trebui să arate ca mai jos:
kubectl get nodes -o=custom-columns=<NUME COLOANĂ>:<JSONPath Query>
De notat că se poate exclude cuvântul cheie items din query, deoarece custom columns presupune că interogarea, prestabilit, este făcută pentru fiecare element din listă.
Să pornim tot de la exemplul anterior, care printează numele nodului și numărul de procesoare de pe fiecare nod, totul pe rând separat.
kubectl get nodes -o=custom-columns=NODE:.metadata.name,CPU:.status.capacity.cpu
Așa-i că este cea mai frumoasă ieșire a acestei comenzi de până acum? 🙂
Se pot adăuga oricâte coloane urmate de interogarea corespunzătoare, separate prin virgulă.
JSONPath în Kubernetes - sortarea
Comanda kubectl
vine și cu posibilitatea de sortare a rezultatului, folosind opțiunea --sort-by. Astfel, se sortează lista obținută în funcție de specificațiile unui anumit câmp, specificații care sunt, de fapt, expresii JSONPath (de exemplu '{.metadata.name}').
kubectl get nodes --sort-by=.metadata.name
kubectl get nodes --sort-by=.status.capacity.cpu
Alte exemple de interogări JSONPath în Kubernetes
- pentru fiecare nod din cluster se afișează efectul taint-ului (dacă există), precum și valoarea sa:
kubectl get nodes -o=custom-columns=NODE:.metadata.name,TAINT:.spec.taints[*].effect,KEY:.spec.taints[*].key
- pentru fiecare nod din cluster, se afișează efectul taint-ului precum și statusul său:
kubectl get nodes -o=custom-columns=NODE:.metadata.name,TAINT:.spec.taints[*].effect, STATUS:.status.conditions[4].type
- aproape același lucru ca mai sus, dar fără custom columns (pentru fiecare nod din cluster, se inserează un TAB, se afișează efectul taint-ului, se inserează un TAB, se afișează statusul său doar dacă are statusul READY, apoi se trece, pe o linie nouă, la următorul element:
kubectl get nodes -o=jsonpath='{range .items[*]} {.metadata.name} {"\t"} {.spec.taints[*].effect} {"\t"} {.status.conditions[?(@.type == "Ready")].type} {"\n"} {end}'
- afișarea tuturor Persistent Volumes din cluster, apoi sortarea lor după capacitate (custom columns combinat cu --sort-by):
kubectl get pv -o=custom-columns=NAME:.metadata.name,CAPACITY:.spec.capacity.storage --sort-by=.spec.capacity.storage
NAME CAPACITY
pv-log-4 40Mi
pv-log-1 100Mi
pv-log-2 200Mi
pv-log-3 300Mi - afișarea nodurilor și adreselor lor IP interne:
kubectl get nodes -o=custom-columns=NODE:.metadata.name,IntrenalIP:.status.addresses[0].address
- afișarea nodurilor, a distribuției instalate, a versiunii de kernel și a versiunii de Docker:
kubectl get nodes -o=custom-columns=NODE:.metadata.name,OS:.status.nodeInfo.osImage,KERNEL:.status.nodeInfo.kernelVersion, DOCKER:.status.nodeInfo.containerRuntimeVersion
Resurse:
- JSONPath support: https://kubernetes.io/docs/reference/kubectl/jsonpath/
- kubectl Cheat Sheet: https://kubernetes.io/docs/reference/kubectl/cheatsheet/#viewing-finding-resources
- Introduction to JsonPath: https://www.baeldung.com/guide-to-jayway-jsonpath
- JSONPath Online Evaluator: http://jsonpath.com/
Lasă un răspuns