kubectl ile Verimli Çalışmak: Output Format'ları, jsonpath, custom-columns, Selector'lar, patch, events, k9s ve Plugin'ler
kubectl'i verimli kullanmak, okurken custom-columns, jsonpath, selector ve events ile tam istediğin cevabı almak, değiştirirken de doğru patch türünü ve güvenlik kilitlerini seçmek demek. JSON patch'in index yüzünden yanlış değişkeni ezip pod'u çökertmesini, server-side apply çatışmasını, kubectl edit drift'ini, kuberc ile onaylı delete'i, krew, k9s ve stern'i gerçek çıktılarla görüyoruz.
Sürüm notu: Bu yazıdaki örnekler kind 0.33 ile Kubernetes 1.37, kubectl 1.37, k9s 0.51.0, krew 0.5.0, stern 1.35.0, PowerShell 7.7 ve .NET 10 ile build edilen Bookstore API image'ıyla denenmiştir.
Önce Tahmin Et
Bookstore API Deployment'ının container'ında tek bir environment variable var: Database__Provider=InMemory. Önce kubectl patch ile, strategic merge türünde, log seviyesini belirleyen Logging__LogLevel__Default=Warning değişkenini ekliyorsun. Hemen ardından bu yeni değişkeni JSON patch ile Information yapmak istiyorsun ve listenin ikinci elemanı olduğunu düşünerek /spec/template/spec/containers/0/env/1/value yolunu yazıyorsun. İkinci patch hangi değişkeni değiştirir ve production'da bunun bedeli ne olur? Cevap yazının ortasında, gerçek bir CrashLoop'la birlikte.
Okumak ve Seçmek: Output Format'ları ve Selector'lar
Serinin ikinci yazısında get, describe, explain ve --dry-run=server'ı gördük. Bu yazı onun devamı: kubectl'i hem okurken hem değiştirirken hızlı ve güvenli kullanmak. Lab, sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) üzerinde: bookstore namespace'inde 1.0.0, bookstore-staging'de 1.1.0 çalışıyor. API'yi önceki yazıdaki gibi in-memory modda çalıştırıyorum.
Tablo yetmediğinde ilk araç -o custom-columns. Sütun adı ve JSONPath ifadesi verirsin, kubectl tabloyu senin için kurar. "Hangi namespace'te hangi image tag'i çalışıyor?" sorusunun tek satırlık cevabı:
$ kubectl get deploy -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,IMAGE:.spec.template.spec.containers[*].image,READY:.status.readyReplicas'
NAMESPACE NAME IMAGE READY
bookstore-staging api k8sseries-bookstore-api:1.1.0 2
bookstore api k8sseries-bookstore-api:1.0.0 3
kube-system coredns registry.k8s.io/coredns/coredns:v1.14.6 2
local-path-storage local-path-provisioner docker.io/kindest/local-path-provisioner:v20260820-69b56db7 1
Script'e girdi verecek, tab ile ayrılmış satırlar gerekiyorsa -o jsonpath ve range; cluster'daki bütün image'ları sayan versiyon da kısa:
$ kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}' | grep bookstore
bookstore-staging api-d7f5cd5f8-2vcp2 k8sseries-bookstore-api:1.1.0
bookstore-staging api-d7f5cd5f8-5c9bf k8sseries-bookstore-api:1.1.0
bookstore api-7dc5dc5b47-k7d4t k8sseries-bookstore-api:1.0.0
bookstore api-7dc5dc5b47-lrpdl k8sseries-bookstore-api:1.0.0
bookstore api-7dc5dc5b47-r5xhq k8sseries-bookstore-api:1.0.0
$ kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\n' | sort | uniq -c
3 docker.io/kindest/kindnetd:v20260820-69b56db7
1 docker.io/kindest/local-path-provisioner:v20260820-69b56db7
3 k8sseries-bookstore-api:1.0.0
2 k8sseries-bookstore-api:1.1.0
2 registry.k8s.io/coredns/coredns:v1.14.6
1 registry.k8s.io/etcd:3.7.0-0
1 registry.k8s.io/kube-apiserver:v1.37.0
1 registry.k8s.io/kube-controller-manager:v1.37.0
3 registry.k8s.io/kube-proxy:v1.37.0
1 registry.k8s.io/kube-scheduler:v1.37.0
$ kubectl -n bookstore get pods -l app.kubernetes.io/name=api -o jsonpath='{.items[0].status.conditions[?(@.type=="Ready")].status}'
True
Son örnekteki ?(@.type=="Ready") bir filtre: condition listesinde index'e güvenmek yerine tipine göre seçer. Windows'ta PowerShell'de tırnaklar farklı çalışır; JSONPath'i tek tırnak içinde verip iç tırnakları korumak gerekir, sort | uniq -c yerine de Group-Object kullanılır.
Seçmenin üç yolu var. -l label selector'dır (in, !=, ! destekler); --field-selector ise yalnız API server'ın indekslediği birkaç alanla çalışır (status.phase, spec.nodeName, metadata.name…); -A bütün namespace'leri tarar. Bir node'u boşaltmadan önce üzerinde ne çalıştığını görmek:
$ kubectl get pods -A --field-selector=spec.nodeName=k8sseries-lab-worker2,status.phase=Running -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name
NS NAME
bookstore-staging api-d7f5cd5f8-5c9bf
bookstore api-7dc5dc5b47-r5xhq
kube-system kindnet-2hhjh
kube-system kube-proxy-lwdq6
-w listeyi açık tutar ve değişiklikleri satır satır basar; --output-watch-events her satırın başına olayın tipini ekler. Bir pod'u sildiğimde:
$ kubectl -n bookstore get pods -w --output-watch-events
EVENT NAME READY STATUS RESTARTS AGE
ADDED api-7dc5dc5b47-2bpm8 1/1 Running 0 63s
ADDED api-7dc5dc5b47-mwrdh 1/1 Running 0 63s
ADDED api-7dc5dc5b47-tpvg6 1/1 Running 0 63s
MODIFIED api-7dc5dc5b47-2bpm8 1/1 Terminating 0 65s
ADDED api-7dc5dc5b47-fc77w 0/1 Pending 0 0s
...
DELETED api-7dc5dc5b47-2bpm8 0/1 Completed 0 66s
MODIFIED api-7dc5dc5b47-fc77w 1/1 Running 0 1s
Ne Oldu? events, explain ve get --raw
Takılan bir rollout'un hikâyesini en kısa yoldan kubectl events anlatır. describe'ın en altındaki event'lerden farkı, --for ile tek bir object'e, --types ile yalnız uyarılara süzebilmen ve zamana göre sıralı gelmesi. Var olmayan bir tag'e geçtim:
$ kubectl -n bookstore set image deploy/api api=k8sseries-bookstore-api:1.2.0
deployment.apps/api image updated
$ kubectl -n bookstore annotate deploy/api kubernetes.io/change-cause='set image api 1.2.0' --overwrite
deployment.apps/api annotated
$ kubectl -n bookstore rollout status deploy/api --timeout=25s
Waiting for deployment "api" rollout to finish: 1 out of 3 new replicas have been updated...
error: timed out waiting for the condition
$ kubectl -n bookstore events --for deploy/api
LAST SEEN TYPE REASON OBJECT MESSAGE
56s Normal ScalingReplicaSet Deployment/api Scaled up replica set api-7dc5dc5b47 from 0 to 3
26s Normal ScalingReplicaSet Deployment/api Scaled up replica set api-544f6fcf45 from 0 to 1
$ kubectl -n bookstore events --for pod/api-544f6fcf45-qx6g6
LAST SEEN TYPE REASON OBJECT MESSAGE
26s Normal Scheduled Pod/api-544f6fcf45-qx6g6 Successfully assigned bookstore/api-544f6fcf45-qx6g6 to k8sseries-lab-worker
24s Normal BackOff Pod/api-544f6fcf45-qx6g6 Back-off pulling image "k8sseries-bookstore-api:1.2.0"
24s Warning Failed Pod/api-544f6fcf45-qx6g6 Error: ImagePullBackOff
11s (x2 over 25s) Normal Pulling Pod/api-544f6fcf45-qx6g6 Pulling image "k8sseries-bookstore-api:1.2.0"
10s (x2 over 24s) Warning Failed Pod/api-544f6fcf45-qx6g6 Failed to pull image "k8sseries-bookstore-api:1.2.0": failed to pull and unpack image "docker.io/library/k8sseries-bookstore-api:1.2.0": failed to resolve reference "docker.io/library/k8sseries-bookstore-api:1.2.0": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
10s (x2 over 24s) Warning Failed Pod/api-544f6fcf45-qx6g6 Error: ErrImagePull
Deployment'ın event'leri yalnız "yeni ReplicaSet'i 1'e çıkardım" diyor; asıl sebep pod'un event'lerinde. Takılan rollout'ta zincir hep aynı: Deployment → yeni ReplicaSet → yeni pod. Bir not: event'ler object adına göre eşleşir ve varsayılan olarak bir saat saklanır; aynı adla silinip yeniden oluşturulan bir Deployment'ın eski event'leri de listede çıkar. Geri almak için:
$ kubectl -n bookstore rollout undo deploy/api
Warning: resource deployments/api was previously managed with 'kubectl apply'. Rolling back will not update the kubectl.kubernetes.io/last-applied-configuration annotation, which may cause unexpected behavior on future 'kubectl apply' operations. Consider using 'kubectl apply' with your previous configuration file instead.
deployment.apps/api rolled back
$ kubectl -n bookstore rollout history deploy/api
deployment.apps/api
REVISION CHANGE-CAUSE
2 set image api 1.2.0
3 <none>
Uyarıyı ciddiye al: set image ve rollout undo cluster'ı değiştirir, manifest dosyanı değiştirmez. Acil durumda doğru araçlar, ama hemen ardından aynı değişikliği Git'teki manifest'e de yapman gerekir.
İki okuma aracı daha. kubectl explain deployment.spec.strategy --recursive alt alanları tek ekranda ağaç olarak gösterir; kubectl'in cluster'dan aldığı şemayı okuduğu için CRD'lerde (Custom Resource Definition; Kubernetes'e senin eklediğin resource tipleri) de çalışır. kubectl get --raw ise API server'a doğrudan istek atar: /readyz?verbose control plane'in sağlık kontrollerini, /apis/apps/v1 o gruptaki resource'ları, /api/v1/namespaces/bookstore/pods?limit=1 ise sayfalamayı (continue token'ı) gösterir. Bir aracın hangi isteği attığını merak ettiğinde -v=8 URL'leri de yazdırır.
Değiştiren Komutlar: create, apply, set, scale, drain
kubectl create object yoksa oluşturur, varsa hata verir (AlreadyExists); kubectl replace object'in tamamını dosyadakiyle değiştirir ve dosyada olmayan alanları siler; kubectl apply dosyayı istenen durum olarak alır ve farkı uygular. Günlük işte apply, tek seferlik kurulum script'lerinde create kullan. replace'i yalnız ne sileceğini bildiğinde kullan.
Küçük değişiklik komutlarının çoğunda bir güvenlik kilidi var ve onu kullanmak alışkanlık olmalı. label ve annotate var olan bir değeri --overwrite olmadan değiştirmez; scale --current-replicas beklediğin sayı değilse hiçbir şey yapmaz; drain server dry run'la neyi çıkaracağını önceden söyler:
$ kubectl -n bookstore label deploy/api tier=backend
deployment.apps/api labeled
$ kubectl -n bookstore label deploy/api tier=api
error: 'tier' already has a value (backend), and --overwrite is false
$ kubectl -n bookstore scale deploy/api --current-replicas=2 --replicas=4
error: Expected replicas to be 2, was 3
$ kubectl drain k8sseries-lab-worker2 --ignore-daemonsets --dry-run=server
node/k8sseries-lab-worker2 cordoned (server dry run)
Warning: ignoring DaemonSet-managed Pods: kube-system/kindnet-2hhjh, kube-system/kube-proxy-lwdq6
evicting pod bookstore/api-db78cc5b8-2lnmt (server dry run)
evicting pod bookstore-staging/api-d7f5cd5f8-5c9bf (server dry run)
node/k8sseries-lab-worker2 drained (server dry run)
cordon node'a yeni pod gelmesini durdurur, drain buna ek olarak pod'ları eviction API'siyle çıkarır (PDB'lere uyarak), uncordon node'u yeniden açar. Drain'in PDB ve anti-affinity ile nasıl takıldığını önceki yazıda gördük.
kubectl patch: strategic, merge ve json
Önce Tahmin Et'in cevabı. Strategic merge patch, Kubernetes'in kendi type'larını bilen bir birleştirme: container listesini name'e göre eşleştirir, env listesine yeni elemanı ekler.
$ kubectl -n bookstore patch deploy api --type=strategic -p '{"spec":{"template":{"spec":{"containers":[{"name":"api","env":[{"name":"Logging__LogLevel__Default","value":"Warning"}]}]}}}}'
deployment.apps/api patched
$ kubectl -n bookstore get deploy api -o jsonpath='{.spec.template.spec.containers[0].env}'
[{"name":"Logging__LogLevel__Default","value":"Warning"},{"name":"Database__Provider","value":"InMemory"}]
Yeni değişken listenin başına eklendi. Aynı patch'i --type=merge ile, yani RFC (internet standartlarını tanımlayan belge) 7386'daki JSON merge patch biçimiyle göndermek de işe yaramıyor, çünkü merge patch listeleri birleştirmez, olduğu gibi değiştirir; container listesi image'sız tek bir elemana iner ve API server reddeder:
$ kubectl -n bookstore patch deploy api --type=merge -p '{"spec":{"template":{"spec":{"containers":[{"name":"api","env":[{"name":"Logging__LogLevel__Default","value":"Information"}]}]}}}}'
The Deployment "api" is invalid: spec.template.spec.containers[0].image: Required value
$ kubectl -n bookstore patch deploy api --type=json -p '[{"op":"replace","path":"/spec/template/spec/containers/0/env/1/value","value":"Information"}]'
deployment.apps/api patched
$ kubectl -n bookstore get deploy api -o jsonpath='{.spec.template.spec.containers[0].env}'
[{"name":"Logging__LogLevel__Default","value":"Warning"},{"name":"Database__Provider","value":"Information"}]
$ kubectl -n bookstore logs pod/api-65c947849d-kh28w | tail -3
Unhandled exception. System.InvalidOperationException: Connection string 'BookstoreDb' is missing. Set it with user-secrets or the ConnectionStrings__BookstoreDb environment variable (see README.md).
at Program.<Main>$(String[] args) in /src/src/Bookstore.Api/Program.cs:line 18
at Program.<Main>(String[] args)
RFC 6902'deki JSON patch ise index'le çalışır ve index 1 artık Database__Provider. Değeri "Information" olunca uygulama in-memory moddan çıktı, connection string aradı ve çöktü. Yeni pod hiç ready olmadığı ve maxUnavailable 0'a yuvarlandığı için eski üç pod trafiği taşımaya devam etti; readiness probe ve rolling update ayarı bu sefer kurtardı. JSON patch kullanacaksan önce test işlemiyle yolun beklediğin şeyi gösterdiğini doğrula; yanlış eleman varsa patch'in tamamı reddedilir:
$ kubectl -n bookstore patch deploy api --type=json -p '[{"op":"test","path":"/spec/template/spec/containers/0/env/1/name","value":"Logging__LogLevel__Default"},{"op":"replace","path":"/spec/template/spec/containers/0/env/1/value","value":"Information"}]'
The request is invalid: the server rejected our request due to an error in our request
Kural: listelerde strategic merge (Kubernetes'in kendi type'ları için) ya da server-side apply; merge patch'i yalnız skaler alanlar ve CRD'ler için; JSON patch'i test ile birlikte. PowerShell'de bu iç içe tırnaklarla uğraşmak yerine patch'i bir dosyaya yazıp --patch-file ile ver:
spec:
template:
spec:
containers:
- name: api
env:
- name: Logging__LogLevel__Default
value: Warning
$ kubectl -n bookstore-staging patch deploy api --patch-file log-level-patch.yaml
deployment.apps/api patched
Server-Side Apply, kubectl edit ve Drift
Server-side apply'da birleştirmeyi API server yapar ve her alanın bir sahibi (field manager) olur. Bu, aynı object'e birden fazla aracın yazdığı durumları görünür kılar:
$ kubectl -n bookstore apply --server-side -f api.yaml
deployment.apps/api serverside-applied
$ kubectl -n bookstore scale deploy/api --replicas=5
deployment.apps/api scaled
$ kubectl -n bookstore get deploy api --show-managed-fields -o jsonpath='{range .metadata.managedFields[*]}{.manager}{" "}{.operation}{" "}{.subresource}{"\n"}{end}'
kubectl Apply
kubectl Update scale
kube-controller-manager Update status
$ kubectl -n bookstore apply --server-side -f api.yaml
error: Apply failed with 1 conflict: conflict with "kubectl" with subresource "scale" using apps/v1: .spec.replicas
...
$ kubectl -n bookstore apply --server-side --force-conflicts -f api.yaml
deployment.apps/api serverside-applied
Çatışma bir hata değil, bir soru: "bu alanı başka biri de yönetiyor, ezeyim mi?" HPA'nın (yüke göre pod sayısını ayarlayan bileşen) yönettiği bir Deployment'ta cevap genelde hayır; doğrusu manifest'ten replicas'ı çıkarmak. --force-conflicts alanın sahipliğini alır ve dosyadaki değeri yazar.
kubectl edit de bir field manager olarak kaydedilir. Bir memory limit'ini elle 512Mi'ye çıkardım (etkileşimsiz olsun diye editor olarak sed verdim) ve Git'teki dosyayla farkına baktım:
$ KUBE_EDITOR="sed -i.bak -e 's/memory: 256Mi/memory: 512Mi/'" kubectl -n bookstore edit deploy/api
deployment.apps/api edited
$ kubectl -n bookstore diff --server-side -f api.yaml | grep -E '^[-+] '
W1115 10:35:11.692059 52103 diff.go:723] Object (apps/v1, Kind=Deployment: api) keeps changing, diffing without lock
Error from server (Conflict): Apply failed with 1 conflict: conflict with "kubectl-edit" using apps/v1: .spec.template.spec.containers[name="api"].resources.limits.memory
$ kubectl -n bookstore diff --server-side --force-conflicts -f api.yaml | grep -E '^[-+] |^[-+]{3}'
--- <tmp>/LIVE-685834022/apps.v1.Deployment.bookstore.api 2026-11-15 10:35:17
+++ <tmp>/MERGED-2491651083/apps.v1.Deployment.bookstore.api 2026-11-15 10:35:17
- generation: 4
+ generation: 5
- memory: 512Mi
+ memory: 256Mi
Cluster'daki durum artık Git'tekinden farklı (drift). GitOps ve Argo CD serisinin üçüncü yazısındaki self-heal açıksa Argo CD bu farkı görüp birkaç dakika içinde 256Mi'ye geri yazar; kapalıysa uygulama "OutOfSync" görünür ve bir sonraki sync elle yaptığın değişikliği siler. Her iki durumda da kubectl edit production'da kalıcı bir değişiklik yolu değil; acil bir müdahaleyse aynı değişikliği hemen manifest'e taşı.
Verimlilik: Completion, kuberc, kubectx ve krew
İlk adım alias ve completion (Terminal ve Command Line serisinin ikinci yazısı). PowerShell 7'de profile'a üç satır yeter; bunu PowerShell 7.7'de TabExpansion2 ile denedim, k get deployment -n bookstore-st sonrası Tab bookstore-staging'i tamamladı:
kubectl completion powershell | Out-String | Invoke-Expression
Set-Alias -Name k -Value kubectl
Register-ArgumentCompleter -CommandName k -ScriptBlock $__kubectlCompleterBlock
Bash'te karşılığı source <(kubectl completion bash), alias k=kubectl ve complete -o default -F __start_kubectl k (bash satırlarını bu yazı için ayrıca denemedim). kubectl 1.34'ten beri beta olan kuberc ise kubectl'in kendi tercih dosyası: komut başına varsayılan flag'ler ve alias'lar. İki varsayılan production'da çok işe yarıyor: her delete önce onay istesin, apply server-side olsun.
$ kubectl kuberc set --section defaults --command delete --option interactive=true
Updated /home/user/.kube/kuberc
$ kubectl kuberc set --section defaults --command apply --option server-side=true
Updated /home/user/.kube/kuberc
$ kubectl kuberc set --section aliases --name images --command get --prependarg deployments --option all-namespaces=true --option 'output=custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,IMAGE:.spec.template.spec.containers[*].image'
Updated /home/user/.kube/kuberc
$ kubectl images
NAMESPACE NAME IMAGE
bookstore-staging api k8sseries-bookstore-api:1.1.0
bookstore api k8sseries-bookstore-api:1.0.0
kube-system coredns registry.k8s.io/coredns/coredns:v1.14.6
local-path-storage local-path-provisioner docker.io/kindest/local-path-provisioner:v20260820-69b56db7
$ echo n | kubectl -n bookstore-staging delete deploy api
You are about to delete the following 1 resource(s):
deployment.apps/api
Do you want to continue? (y/N): deletion is cancelled
Dosya ~/.kube/kuberc'de durur (Windows'ta %USERPROFILE%\.kube\kuberc); KUBECTL_KUBERC=false ile kapatılır, CI'da bu yüzden script'lerin davranışı değişmez. Context ve namespace değiştirmek için kubectx ve kubens yaygın; krew üzerinden ctx ve ns plugin'leri olarak kurulabiliyor. krew, kubectl plugin'lerinin paket yöneticisi; plugin'i indirir, checksum'ını kontrol eder ve kubectl-<ad> binary'sini PATH'e koyar. Kurulum sonrası verdiği uyarı önemli:
$ kubectl krew install ctx ns tree
...
WARNING: You installed plugin "tree" from the krew-index plugin repository.
These plugins are not audited for security by the Krew maintainers.
Run them at your own risk.
$ kubectl ctx
kind-k8sseries-lab
staging
$ kubectl ctx staging
Switched to context "staging".
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
api 2/2 2 2 3m45s
$ kubectl ctx -
Switched to context "kind-k8sseries-lab".
Plugin, kubectl'in senin kubeconfig'inle, yani senin yetkilerinle çalışan bir programdır. Production cluster'a erişen makinede yalnız tanıdığın ve sürümünü sabitlediğin plugin'leri kur.
k9s ve stern
k9s, cluster'ı terminalde tam ekran gezdiren bir arayüz: resource tipleri arasında :deploy, :pods gibi komutlarla geçersin, l log'u, d describe'ı, shift-f port-forward'ı açar. Aşağıdaki ekran k9s -n bookstore --readonly ile açıldı ve bir pseudo-terminal'den metin olarak yakalandı (boş tablo satırlarını çıkardım, pod IP'lerini maskeledim):
Context: kind-k8sseries-lab [R] <0> all <d> Describe … ____ __ ________
Cluster: kind-k8sseries-lab <1> bookstore <?> Help <| |/ / __ \______
User: kind-k8sseries-lab <2> default <shift-j> Jump Owner <| /\____ / ___/
K9s Rev: v0.51.0 <l> Logs <| \ \ / /\___ \
K8s Rev: v1.37.0 <p> Logs Previous |____|\__ \/____//____ /
CPU: n/a <shift-f> Port-Forward \/ \/
MEM: n/a
┌───────────────────────────────────────────────── pods(bookstore)[3] ─────────────────────────────────────────────────┐
│ NAME↑ PF READY STATUS RESTARTS IP NODE AGE │
│ api-db78cc5b8-2lnmt ● 1/1 Running 0 <pod-ip> k8sseries-lab-worker2 49s │
│ api-db78cc5b8-5nm8p ● 1/1 Running 0 <pod-ip> k8sseries-lab-worker 60s │
│ api-db78cc5b8-khgsf ● 1/1 Running 0 <pod-ip> k8sseries-lab-control-plane 48s │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
<pod>
Başlıktaki [R] read-only modu gösteriyor: bu modda k9s silme, edit ve shell gibi değiştiren kısayolları kapatır. Production context'inde k9s'i bu modda açmak, yanlış tuşa basma riskini ortadan kaldırır. CPU ve MEM "n/a" çünkü bu cluster'da metrics-server yok.
stern ise birden fazla pod'un log'unu tek akışta, pod adıyla birlikte gösterir ve yeni açılan pod'ları kendiliğinden ekler. kubectl logs -l rollout sırasında yeni pod'ları kaçırır; stern kaçırmaz. Bir rollout restart sırasında:
$ stern -n bookstore api --color never --since 1s --template '{{.PodName}} {{.ContainerName}} {{.Message}}{{"\n"}}'
+ api-7dc5dc5b47-4bd4m › api
+ api-7dc5dc5b47-jctrr › api
+ api-7dc5dc5b47-kwqf5 › api
+ api-db78cc5b8-5nm8p › api
api-db78cc5b8-5nm8p api info: Microsoft.EntityFrameworkCore.Update[30100]
api-db78cc5b8-5nm8p api Saved 62 entities to in-memory store.
api-db78cc5b8-5nm8p api info: Microsoft.Hosting.Lifetime[14]
api-db78cc5b8-5nm8p api Now listening on: http://[::]:8080
...
api-7dc5dc5b47-kwqf5 api info: Microsoft.Hosting.Lifetime[0]
api-7dc5dc5b47-kwqf5 api Application is shutting down...
+ api-db78cc5b8-2lnmt › api
...
- api-7dc5dc5b47-kwqf5 › api
+ satırları stern'in izlemeye başladığı, - satırları bıraktığı pod'lar. Eski pod'un "Application is shutting down" satırı ile yeni pod'un "Now listening" satırı aynı akışta: onuncu yazıdaki graceful shutdown'ı canlı izlemenin en kolay yolu. Uzun süreli log arama ve saklama için bu araçlar değil, Observability serisindeki log backend'leri gerekir.
Üretimde Dikkat
Production'a erişimi geliştirme makinesinden ayır. Her cluster için ayrı kubeconfig dosyası tut ve KUBECONFIG'i yalnız o terminal oturumunda ver; tek dosyada yirmi context, yanlış cluster'da komut çalıştırmanın en kısa yolu. Prompt'ta context ve namespace'i göster (kube-ps1 ya da Oh My Posh ve Starship gibi araçların Kubernetes segmenti), production context'inde rengi değiştir. Production için kullanıcıya cluster-admin değil, okuma yetkisi ver; değişiklikler pipeline ya da GitOps üzerinden gelsin. kuberc'te delete --interactive varsayılanı ve k9s'in --readonly modu, kalan riski azaltır. Script'lerde ise kubectl çıktısını tablo olarak parse etme; sütunlar sürümler arasında değişebilir, -o jsonpath ve -o json değişmez.
Sık Düşülen Tuzaklar
Yanlış context'te delete: kubectl delete -f . ya da delete ns, onay sormadan çalışır; kuberc'teki interactive varsayılanı veya ayrı kubeconfig bunu yakalar. JSON patch'te index'e güvenmek: liste sırası her strategic merge'den sonra değişebilir. Merge patch ile liste güncellemeye çalışıp listenin tamamını ezmek. kubectl edit ya da set image ile yapılan değişikliği manifest'e taşımamak; GitOps onu geri alır, GitOps yoksa bir sonraki apply. HPA'nın yönettiği replicas'ı manifest'te tutup her apply'da replica sayısını sıfırlamak. Field selector'ı her alan için çalışır sanmak. Ve doğrulamadan kurulan krew plugin'leri: uyarı metni bunu açıkça söylüyor.
Küçük Bir Deneme
★☆☆ Kendi cluster'ında bütün namespace'lerdeki container image'larını tag'leriyle listeleyen bir -o custom-columns komutu yaz, sonra aynısını kuberc'te bir alias yap.
★★☆ Bir Deployment'a strategic merge patch ile iki environment variable ekle, sonra birinin değerini JSON patch ile değiştir. test işlemini önce yanlış, sonra doğru index'le dene ve --patch-file ile PowerShell'de aynı patch'i uygula.
★★★ Bir Deployment'ı server-side apply ile oluştur, bir alanı kubectl edit ile, bir alanı kubectl scale ile değiştir. --show-managed-fields çıktısından hangi alanın kime ait olduğunu çıkar, kubectl diff --server-side ile drift'i gör ve --force-conflicts olmadan çatışmayı çözmenin yolunu bul.
Bu yazıda kubectl tree plugin'ini kurduk ama kullanmadık. Bir sonraki ve serinin son yazısında onunla Deployment → ReplicaSet → Pod sahiplik zincirini göreceğiz; CRD ile Kubernetes'e yeni bir resource tipi ekleyip .NET ile küçük bir controller yazacak, finalizer'ların neden "Terminating"de takılan namespace'lere yol açtığını inceleyeceğiz.