Skip to main content

Command Palette

Search for a command to run...

CKAD Preparation: Kubernetes Fundamentals

Pods, Services, Deployments, Networking Pitfalls, and Troubleshooting Lessons from My CKAD Journey

Updated
β€’7 min read
CKAD Preparation: Kubernetes Fundamentals
D

πŸ‘‹ Hi! I’m Dani, a passionate automation, Ansible, DevOps, and Cloud technologies enthusiast. I currently work as a Middleware Solutions Architect at Atradius, leading middleware automation and optimizing IT infrastructure. πŸ’‘ My Story: I started my career specializing in Oracle Middleware, working with technologies such as WebLogic, Oracle Database, Oracle iPlanet Web Server, and Oracle JDK. Over time, my focus shifted towards deployment automation, continuous integration, and process optimization in complex enterprise environments. πŸš€ Impact & Achievements: βœ… Direct the automation of Oracle Fusion Middleware (FMW) with Ansible, streamlining the installation, configuration, and patching processes for Oracle WebLogic, SOA Suite, and OSB. βœ… Lead IBM WebSphere Application Server (WAS) automation with Ansible and AWX, including installation, configuration, certificates, and deployments, reducing implementation times by 70%. βœ… Integrated Azure DevOps with AWX, eliminating manual deployment tasks and reducing human intervention to a simple approval step. βœ… Mentor and train teams on Ansible automation, fostering continuous improvement and knowledge transfer. πŸ€πŸ₯Ž In my free time, I enjoy playing padel and basketball, always looking for new challenges and improvements, both in sports and technology. I also love building web applications with Oracle APEX, bringing ideas to life through low-code development. πŸ“₯ Let’s connect! πŸ“§ Email: dbenitez.vk@gmail.com πŸ”— LinkedIn: https://www.linkedin.com/in/danielbenitezaguila πŸ’» GitHub: https://github.com/dbeniteza

Introduction

As part of my CKAD preparation, I decided to step back from advanced Kubernetes topics and revisit the fundamentals. Instead of relying solely on theory, I deployed and tested everything on a real AKS cluster, documenting the lessons learned along the way. In this article, I cover Pods, Services, Deployments, multi-container Pods, networking concepts, and common troubleshooting scenarios that helped me build a stronger Kubernetes foundation. Whether you're studying for CKAD or just starting your Kubernetes journey, these exercises provide practical insights into how Kubernetes behaves in the real world.

Environment

All exercises were performed on:

  • Azure Kubernetes Service (AKS)

  • Kubernetes v1.34

  • kubectl CLI

  • Single-node lab cluster optimized for CKAD practice A useful command when exploring Kubernetes is:

kubectl api-resources

This shows all resource types supported by the cluster, including Pods, Services, Deployments, ConfigMaps, ReplicaSets, and more.


Creating My First Pod

The simplest Kubernetes workload is a Pod. Example:

apiVersion: v1
kind: Pod
metadata:
  name: demo-web
spec:
  containers:
  - name: web
    image: nginx

Create it:

kubectl apply -f pod.yaml
pod/demo-web created

Verify it:

kubectl get pods
NAME       READY   STATUS    RESTARTS   AGE
demo-web   1/1     Running   0          64s

The two fields I check first are:

  • STATUS should be Running

  • READY should show 1/1

If READY is not equal to the number of containers in the Pod, further investigation is required.


Pod Networking: A Common Beginner Mistake

After creating the Pod, I retrieved its IP address:

kubectl get pod -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
demo-web   1/1     Running   0          10m   10.244.0.22   aks-agentpool-38340810-vmss000001   <none>           <none>

I then tried:

curl http://10.244.0.22
curl: (28) Failed to connect to 10.244.0.22 port 80 after 129171 ms: Could not connect to server

The request failed. At first I assumed nginx wasn't working. The real reason was much simpler:

Pod IPs are internal cluster addresses and are not automatically reachable from outside Kubernetes.

To verify the application, I executed a shell inside the container:

kubectl exec -it demo-web -- sh
``
# curl http://10.244.0.22
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

Lesson learned:

  • Pod IPs are cluster-internal.

  • Running means scheduled and executing.

  • Running does not mean externally accessible.


Introducing Services

Pods are ephemeral.

A Service provides a stable endpoint for accessing workloads.

Example:

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
  - port: 80

Apply it:

kubectl apply -f service.yaml
service/web-service created

Check it:

kubectl get svc
NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
web-service    ClusterIP   10.0.203.69   <none>        80/TCP    10s
kubernetes     ClusterIP   10.0.0.1      <none>        443/TCP   23h

ClusterIP service receives an internal cluster address.

Applications inside Kubernetes can communicate with the service using: http://10.0.203.69


Understanding NodePort on AKS

One of my biggest learning moments came from NodePort services.

I created a service similar to:

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  type: NodePort    
  ports:
  - port: 80

Kubernetes assigned a port in the 30000+ range.

My expectation was NodeIP:NodePort would be reachable immediately. Not on AKS.

The nodes in my cluster had:

kubectl get svc
NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
web-service    NodePort    10.0.217.113   <none>        80:32765/TCP   61s
kubernetes     ClusterIP   10.0.0.1       <none>        443/TCP        23h

EXTERNAL-IP means:

  • NodePort existed.

  • The nodes only had private addresses.

  • Requests from outside the cluster could not reach them. The easiest workaround for testing is:

kubectl port-forward svc/web-service 8080:80

Then curl http://localhost:8080. This is a useful concept to understand before working with LoadBalancers and Ingress controllers


Multi-Container Pods

A Pod can contain more than one container.

Example structure:

apiVersion: v1
kind: Pod
metadata:
  name: multicont
  labels:
    type: webserver
spec:
  containers:
  - name: web
    image: nginx:alpine
    ports:
    - containerPort: 80

  - name: cache
    image: redis:alpine
    ports:
    - containerPort: 6379

When running:

kubectl get pods
NAME       READY   STATUS              RESTARTS   AGE
multicont  2/2     Running             0          5s

You may see READY 2/2, this means:

  • Both containers are running.

  • Both containers passed readiness checks. This is your first exposure to the sidecar pattern.


Deployments and ReplicaSets

Managing Pods directly is useful for learning but rarely used in production.

Deployments manage Pods through ReplicaSets.

Create a Deployment:

kubectl create deployment web-app --image=nginx
deployment.apps/web-app created

Inspect the hierarchy:

kubectl get deploy,rs,po
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web-app    1/1     1            1           28s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/web-app-5c9c5f7865    1         1         1       4m53s
 
NAME                            READY   STATUS    RESTARTS   AGE
pod/web-app-5c9c5f7865-tx88r    1/1     Running   0          28s

Output will show:

Deployment

-> ReplicaSet

-> Pod

One interesting experiment was deleting the ReplicaSet:

kubectl delete rs web-app-5c9c5f7865

A replacement ReplicaSet was automatically created.

The Deployment reconciled the desired state and recreated the workload.

This demonstrates one of the most important Kubernetes principles:

Kubernetes continuously works to restore the declared desired state.


Troubleshooting ImagePullBackOff

One of the first troubleshooting exercises involved a failing Pod.

The image contained a typo:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mountain-region
    image: nginx:stable-alpin

Notice the mistake alpin instead of alpine. Checking the Pod:

kubectl get pod mypod
NAME     READY   STATUS         RESTARTS   AGE
mypod    0/1     ErrImagePull   0          3s


kubectl describe pod mypod
Name:             mypod
Namespace:        default
Priority:         0
Service Account:  default
Node:             aks-agentpool-38340810-vmss000001/10.224.0.4
Start Time:       Tue, 16 Jun 2026 15:44:06 +0000
Labels:           <none>
Annotations:      <none>
Status:           Pending
[…]
Events:
  Type     Reason     Age   From               Message
  ----     ------     ----  ----               -------
  Normal   Scheduled  14s   default-scheduler  Successfully assigned default/break1 to aks-agentpool-38340810-vmss000001
  Normal   Pulling    14s   kubelet            spec.containers{mountain-region}: Pulling image "nginx:stable-alpin"
  Warning  Failed     13s   kubelet            spec.containers{mountain-region}: Failed to pull image "nginx:stable-alpin": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/nginx:stable-alpin": failed to resolve reference "docker.io/library/nginx:stable-alpin": docker.io/library/nginx:stable-alpin: not foundound
  Warning  Failed     13s   kubelet            spec.containers{mountain-region}: Error: ErrImagePull
  Normal   BackOff    13s   kubelet            spec.containers{mountain-region}: Back-off pulling image "nginx:stable-alpin"
  Warning  Failed     13s   kubelet            spec.containers{mountain-region}: Error: ImagePullBackOff

revealed:

ErrImagePull

ImagePullBackOff

The events section clearly showed the image could not be found.

After correcting the tag, the Pod started successfully.


My Kubernetes Troubleshooting Checklist

When a workload fails, I now use this quick reference:

Status

Typical Cause

Pending

Scheduling or resource problem

ErrImagePull

Image cannot be downloaded

ImagePullBackOff

Repeated image pull failure

CrashLoopBackOff

Container exits repeatedly

Running 0/1

Readiness issue

Running 1/1

Healthy container

And the first command I run is always: kubectl describe pod <pod-name>

Most problems become obvious after reviewing the Events section.


Key Takeaways

After completing these exercises, a few concepts became much clearer:

  • Pods are not automatically reachable from outside the cluster.

  • Services provide stable network endpoints.

  • AKS NodePorts are not automatically exposed to the internet.

  • Deployments manage ReplicaSets, which manage Pods.

  • Kubernetes continuously reconciles desired state.

  • kubectl describe is one of the most valuable troubleshooting tools.

  • ImagePullBackOff almost always points to an image or registry issue.

These may seem like simple exercises, but they provide the foundation for more advanced CKAD topics such as ConfigMaps, Probes, Jobs, Security Contexts, and Ingress resources.

The stronger the fundamentals, the easier the troubleshooting becomes later on.