Introduction
This article is part of an ongoing series designed to help you prepare for the Certified Kubernetes Application Developer (CKAD) exam through small, focused labs.
In this post, we’ll cover the requirements within the “Services and Networking” domain:
Demonstrate basic understanding of NetworkPolicies
Here we’ll demonstrate how to restrict ingress traffic using a Kubernetes NetworkPolicy.
You can start from the beginning of the series here: CKAD Preparation — What is Kubernetes.
Prerequisites
⚠️ Important: Verify that your Kubernetes cluster is configured with a CNI plugin that implements NetworkPolicy. Without such support, any defined policies will be silently ignored. I ran some local tests with Kubernetes on Docker Desktop and with minikube, but neither supports them natively. In the end I opted for a temporary environment using one of the KillerCoda Kubernetes Playgrounds
Getting the Resources
All manifests and examples mentioned in this post are available in the following repository:
git clone https://github.com/SupaaHiro/schwifty-lab.git
cd schwifty-lab/blog-posts/20251018-ckad
Creating the Redis Pod
To understand how Network Policies work, we need to create a couple of pods. Here’s the first one:
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
This pod creates a redis instance listening on port 6379.
Apply the manifest:
k create -f manifests/01-redis.yaml
Verify the Pod reaches the Running state:
k get pod -o=wide -l=app=redis --watch
Creating the Service
To make Redis accessible to other Pods, create a ClusterIP Service.
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
type: ClusterIP
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
Apply the manifest:
k create -f manifests/02-redis-svc.yaml
Once applied, check that the endpoint is correctly populated with:
k get svc -l=app=redis -o=wide
k describe svc redis
Example output:
Name: redis
Namespace: default
Labels: app=redis
Annotations: <none>
Selector: app=redis
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.105.198.164
IPs: 10.105.198.164
Port: <unset> 6379/TCP
TargetPort: 6379/TCP
Endpoints: 10.1.0.174:6379
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
Connectivity test script
To verify connectivity to Redis we’ll use a simple Python script.
The script tries to connect to the Redis service and prints a success or error message, with a configurable timeout (default 10 seconds).
import os
import redis
host = os.getenv("REDIS_HOST", "redis")
port = int(os.getenv("REDIS_PORT", "6379"))
timeout = int(os.getenv("REDIS_TIMEOUT", "10"))
try:
client = redis.Redis(
host=host,
port=port,
socket_connect_timeout=timeout,
socket_timeout=timeout
)
client.ping()
print(f"✅ Connected to Redis at {host}:{port} (timeout={timeout}s)")
except Exception as e:
print(f"❌ Failed to connect to Redis: {e}")
To avoid creating a dedicated Docker image, we will mount the script as a volume via a ConfigMap:
k create cm test-redis-ping --from-file=test-redis-ping.py=./src/test-redis-ping.py
Creating the Client Pod
Define a Pod called redis-client, based on python:3.12-alpine. The container will install the redis library on the fly and then run the script mounted from the ConfigMap.
apiVersion: v1
kind: Pod
metadata:
name: redis-client
labels:
app: redis-client
spec:
containers:
- name: python-tester
image: python:3.12-alpine
command:
- sh
- -c
- >
pip install --upgrade pip --root-user-action ignore > /dev/null &&
pip install redis --root-user-action ignore > /dev/null &&
python /scripts/test-redis-ping.py
env:
- name: REDIS_HOST
value: "redis"
- name: REDIS_PORT
value: "6379"
volumeMounts:
- name: script-volume
mountPath: /scripts
volumes:
- name: script-volume
configMap:
name: test-redis-ping
restartPolicy: Never
Apply the manifest:
k create -f manifests/03-redis-client.yaml
Wait for the Pod to be Running and check the logs:
k get pod -o=wide -l=app=redis-client --watch
k logs redis-client
If everything works, we should see:
✅ Connected to Redis at redis:6379
Defining the NetworkPolicy
Create two NetworkPolicy resources:
- default-deny-ingress — blocks all incoming traffic by default.
- redis-access — allows access to the Redis Pod only from Pods that have the label access: redis.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-access
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
access: redis
ports:
- protocol: TCP
port: 6379
Apply the manifest:
k create -f manifests/04-netpol.yaml
After applying, the redis-access policy will act like a “fence” around Pods with label app=redis, allowing connections only from Pods labeled with access=redis.
Testing the blocked connection
Currently the redis-client Pod does not have the label access: redis, so the connection should fail.
Remove and recreate the Pod, then check the logs:
k replace -f manifests/03-redis-client.yaml --force
k logs redis-client
The expected result is a timeout error:
❌ Failed to connect to Redis: Timeout connecting to server
Note: the error may take a few seconds to appear in the logs.
Enable access via label
Add the missing label “access: redis” to the redis-client Pod manifest and recreate it:
yq e '.metadata.labels.access = "redis"' -i manifests/03-redis-client.yaml
k replace -f manifests/03-redis-client.yaml --force
After the deploy, the connection should work again:
✅ Connected to Redis at redis:6379
🏁 Wrapping Up: What We’ve Covered
In this article we reviewed a practical workflow to test Kubernetes Ingress NetworkPolicy using a Redis server and a client Pod. Key takeaways:
- Ensure your cluster uses a CNI that implements NetworkPolicy; without CNI support, NetworkPolicy objects are ignored.
- Use a “default-deny” Ingress policy to block all incoming traffic by default, then add explicit allow rules to scope access (for example, by pod labels).
- NetworkPolicies are applied to Pods (via podSelector) — label your Pods intentionally to control access and to make policies predictable.
- Test connectivity with a lightweight client Pod and a configurable script (mounted via ConfigMap); check Pod logs for connection timeouts or success messages to validate policy effects.
Final cleanup
When you’re done with the experiments, remove all created resources (Pods, Service and NetworkPolicy) to clean up the environment:
k delete svc redis
k delete netpol default-deny-ingress redis-access
k delete po redis redis-client