kubectl jsonpath — Filtering Service Fields¶
Reference: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ kubectl jsonpath reference: https://kubernetes.io/docs/reference/kubectl/jsonpath/
The Task¶
Update a script svc-filter.sh to include a command that filters and displays the targetPort of a service named redis-service using jsonpath only.
What is jsonpath?¶
jsonpath is a query language for JSON, similar to how XPath works for XML. kubectl's -o jsonpath flag lets you extract specific fields from a Kubernetes object's JSON output instead of getting the full YAML or table format.
Why use it over -o yaml | grep?
- grep is fragile — it matches lines, not structure. If the field moves or the format changes, grep breaks silently
- jsonpath is structural — it navigates the actual JSON object tree. It's precise and reliable
- Scripting: jsonpath gives you a clean value with no extra text to strip out
-o yaml vs -o jsonpath — Understanding the Output Structure¶
First, look at what a Service actually looks like:
Abbreviated output:
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
ports:
- port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
type: ClusterIP
The targetPort lives at: spec → ports → [0] (first port) → targetPort
In jsonpath notation that's: {.spec.ports[0].targetPort}
The Command¶
Full breakdown:
| Part | What it does |
|---|---|
kubectl get service |
Fetch a Service object |
redis-service |
Name of the specific service |
-o jsonpath='{...}' |
Output mode: extract the field matching this jsonpath expression |
{.spec.ports[0].targetPort} |
The jsonpath query — navigate to targetPort |
jsonpath expression breakdown:
| Segment | What it means |
|---|---|
{ } |
jsonpath expression delimiters — required |
. |
Start from the root of the JSON object |
.spec |
Navigate into the spec field |
.ports |
Navigate into the ports field (this is an array) |
[0] |
Take the first element of the array (zero-indexed) |
.targetPort |
Get the targetPort field from that element |
Output: 6379 (just the value, no extra text)
kubectl get svc vs kubectl get service¶
Both are identical — svc is just the short name:
kubectl get svc redis-service -o jsonpath='{.spec.ports[0].targetPort}'
kubectl get service redis-service -o jsonpath='{.spec.ports[0].targetPort}'
Same result either way. In scripts, service is more readable. In the exam, svc is faster to type.
The Script¶
svc-filter.sh:
Or if you want a newline after the output (cleaner for terminal display):
{"\n"} — jsonpath supports escape sequences. "\n" adds a newline character after the value. Without it the shell prompt appears right after the value on the same line.
jsonpath — Broader Reference¶
Accessing Nested Fields¶
# Pod's IP address
kubectl get pod nginx -o jsonpath='{.status.podIP}'
# Node's internal IP
kubectl get node node01 -o jsonpath='{.status.addresses[0].address}'
# Container image in a pod
kubectl get pod nginx -o jsonpath='{.spec.containers[0].image}'
# Service account used by a pod
kubectl get pod nginx -o jsonpath='{.spec.serviceAccountName}'
Iterating Over Arrays with [*]¶
When a resource has multiple items in an array, [0] gets the first. [*] iterates over all:
# All container names in a pod
kubectl get pod multi-container -o jsonpath='{.spec.containers[*].name}'
# All ports of a service
kubectl get service redis-service -o jsonpath='{.spec.ports[*].port}'
# All node names in the cluster
kubectl get nodes -o jsonpath='{.items[*].metadata.name}'
.items[*] — when you query multiple resources (kubectl get nodes, kubectl get pods), the result is a list wrapped in .items[]. Access individual resources with .items[N] or iterate with .items[*].
Adding Separators Between Items¶
# Node names separated by newlines
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
{range .items[*]}...{end} — loops over the array, executing the expression inside for each element. Like a for loop in jsonpath.
Multiple Fields at Once¶
# Node name and its status
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[-1].type}{"\n"}{end}'
{"\t"} — tab separator between fields. Combine with {"\n"} for TSV-style output.
Common Field Paths — Cheatsheet¶
| What you want | jsonpath |
|---|---|
| Pod IP | {.status.podIP} |
| Pod phase (Running/Pending/Failed) | {.status.phase} |
| Container image | {.spec.containers[0].image} |
| All container images | {.spec.containers[*].image} |
| Node internal IP | {.status.addresses[?(@.type=="InternalIP")].address} |
| Service ClusterIP | {.spec.clusterIP} |
| Service port | {.spec.ports[0].port} |
| Service targetPort | {.spec.ports[0].targetPort} |
| Service type | {.spec.type} |
| All node names | {.items[*].metadata.name} |
| PVC storage request | {.spec.resources.requests.storage} |
| ConfigMap value | {.data.<key-name>} |
| Secret value (base64) | {.data.<key-name>} |
Filter with ?() (Conditional)¶
# Get the InternalIP type address specifically (not ExternalIP)
kubectl get node node01 -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}'
?(@.type=="InternalIP") — filters array elements where the type field equals "InternalIP". @ refers to the current element being evaluated.
Port vs TargetPort vs NodePort — Clarified¶
Services have up to three port fields that are commonly confused:
| Field | What it is | Who uses it |
|---|---|---|
port |
The port the Service exposes | Other pods inside the cluster call this port |
targetPort |
The port on the pod the traffic is forwarded to | Must match the port your app listens on inside the container |
nodePort |
The port on every node's IP (only for NodePort/LoadBalancer services) | External traffic hits <NodeIP>:<nodePort> |
spec:
ports:
- port: 80 # Service port — pods in the cluster connect to port 80
targetPort: 8080 # Traffic forwarded to port 8080 on the pod
nodePort: 30080 # External access via <NodeIP>:30080 (NodePort services only)
The exercise asks for targetPort — the port the app inside the container listens on.
Context Switch Reminder¶
Before running any commands in an exam task, set the context:
Every CKA task starts with this line (with the appropriate context name for that task's cluster). Running commands against the wrong cluster context = wrong answers even if your commands are correct.