Skip to content
CertDirectory.io

Limited Time: 40% OFF all Linux Foundation Certifications. Save now

Back to the blog
Study notes17 min read

๐Ÿณโ˜ธ๏ธ Docker and Kubernetes - The Complete Guide

Wijayamuni Dewage Yashith8 Nov 2026 ยท Associate Software Engineer

From zero to hero, explained as if teaching a small child โ€” with simple examples and diagrams. 18 lessons โ€” Docker (1โ€“8), Kubernetes (9โ€“18).


๐Ÿ“‘ Table of Contents

Part 1 โ€” Docker

  1. What is Docker? + Image vs Container
  2. Installing it and running your first container
  3. Basic commands
  4. Docker Hub โ€” where to find images
  5. Dockerfile โ€” building your own image
  6. Volumes โ€” keeping your data safe
  7. Ports & Networking
  8. Docker Compose

Part 2 โ€” Kubernetes 9. Why do we need Kubernetes? 10. Cluster, Nodes & Control Plane 11. Pods 12. Deployments 13. Services 14. ConfigMaps & Secrets 15. Namespaces 16. Volumes & Persistent Storage 17. Ingress 18. Everything together โ€” a real app



๐Ÿณ Part 1 โ€” Docker


Lesson 1

What is Docker? + Image vs Container

The problem

A programmer builds an app on their own computer. On their machine it works perfectly. But when it's put on a different computer, it doesn't work. There's a famous phrase for this:

"But it works on my machine!"

The reason: every computer has a different environment (libraries, settings, versions). It's like cooking a chicken curry โ€” if one house has a different cooker and different ingredients than another, the curry won't turn out the same way.

The solution โ€” Docker

Docker is a tool that lets you pack your app and everything it needs (code, libraries, settings, environment) into one box. That box is called a container. Take that box to any computer, and it works exactly the same everywhere.

flowchart TB
    subgraph without["โŒ Without Docker"]
        A["Developer laptop<br/>App works"] -->|"move to server"| B["Different server<br/>App breaks ๐Ÿ’ฅ"]
        B -.-> C["'works on my machine!'"]
    end

    subgraph with["โœ… With Docker"]
        D["Container<br/>App + everything packed"] --> E["Laptop โœ…"]
        D --> F["Server โœ…"]
        G["Friend's PC โœ…"]
        D --> G
    end

The 2 most important words

Image Container
What is it? The recipe / blueprint The real, running app
State Read-only Alive, running
Example The cake recipe card The cake you baked
How many 1 You can make many from it

Golden rule: Image โ†’ (run) โ†’ Container


Lesson 2

Installing it and running your first container

Docker Engine

Docker Engine is the "engine" that creates, runs, and manages containers โ€” like a chef who cooks for the containers. You need to install it on your computer.

  • Windows / Mac: download and install the Docker Desktop app (docker.com)
  • Linux: install Docker Engine with a few commands

Check that it installed:

docker --version

Your first container

docker run hello-world

Here's what happens behind the scenes with that one command:

flowchart TB
    A["You: docker run hello-world"] --> B["Docker Engine: do I already have the image?"]
    B -->|"if not"| C["Download from Docker Hub"]
    C --> D["Create a container from the image"]
    B -->|"if yes"| D
    D --> E["Container runs<br/>message is printed โœ…"]

A real web server

docker run -d -p 8080:80 nginx

Browser โ†’ http://localhost:8080 โ†’ "Welcome to nginx!" ๐ŸŽ‰

  • -d โ†’ detached mode (runs in the background, your terminal stays free)
  • -p 8080:80 โ†’ port mapping (Lesson 7)
  • nginx โ†’ the image to run

๐Ÿ’ก You never installed nginx โ€” Docker fetched it from Docker Hub, set it up, and ran it for you. That's the power of Docker.


Lesson 3

Basic commands

The life of a container (lifecycle)

flowchart LR
    A["Image<br/>the recipe"] -->|"docker run"| B["Running"]
    B -->|"docker stop"| C["Stopped"]
    C -->|"docker start"| B
    C -->|"docker rm"| D["Removed ๐Ÿ—‘๏ธ"]

Everyday commands

What you want to do Command
See running containers docker ps
See all (even stopped) docker ps -a
Stop one docker stop <name>
Start it again docker start <name>
Remove one docker rm <name>
See your images docker images
Remove an image docker rmi <image>
See what's going on inside docker logs <name>
Go inside a running container docker exec -it <name> bash

๐Ÿ’ก stop = a temporary pause. start = start again. rm = delete completely.


Lesson 4

Docker Hub โ€” where to find images

Docker Hub is the big "app store" for Docker images (like the Play Store, but for images). People all over the world upload images, and anyone can download them for free.

flowchart TB
    HUB["๐Ÿช Docker Hub<br/>nginx | mysql | python | node ..."]
    HUB -->|"pull (download)"| PC["Your computer<br/>takes it and runs it"]
    MINE["The image you built"] -->|"push (upload)"| HUB
  • Pull = download an image from Docker Hub โ†’ docker pull mysql
  • Push = upload an image you built, to share it

Image name and tag

nginx:1.27
  โ”‚     โ”‚
  โ”‚     โ””โ”€โ”€ tag (the version)
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ the image's name
  • A tag is like a version โ€” python:3.12, mysql:8.0
  • If you don't give a tag โ†’ it automatically uses latest
  • โš ๏ธ In real projects it's better to specify an exact version (latest changes over time)

Official images (nginx, mysql, python) = verified by Docker and trustworthy. Use these whenever you can.


Lesson 5

Dockerfile โ€” building your own image โญ

This is the heart of Docker. A Dockerfile is the sheet where you write the recipe for building an image โ€” a list of steps.

Example project (3 files)

app.py (a small Python web app):

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return 'Hello from my own Docker image! ๐ŸŽ‰'

app.run(host='0.0.0.0', port=5000)

requirements.txt:

flask

Dockerfile โญ:

FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

Every line explained

Keyword Meaning (simply)
FROM python:3.12 Which base image to start from (like a ready-made cake mix)
WORKDIR /app The working folder inside the container (like clearing the kitchen counter)
COPY . . Copy my files into the container (bringing in the ingredients)
RUN pip install ... Commands to run while building the image (installing things)
EXPOSE 5000 Which port the app works on (a signboard)
CMD ["python","app.py"] The main thing to start when the container runs

๐Ÿง  RUN = while building the image (happens once). CMD = every time the container runs.

Build and run

docker build -t myapp .
docker run -d -p 5000:5000 myapp

Browser โ†’ http://localhost:5000 โ†’ "Hello from my own Docker image!" ๐Ÿ†

flowchart LR
    A["Dockerfile<br/>recipe (text)"] -->|"docker build"| B["Image<br/>myapp (read-only)"]
    B -->|"docker run"| C["Container<br/>app is running"]

Lesson 6

Volumes โ€” keeping your data safe

The problem

A container is temporary (like a whiteboard). When you run docker rm, all the data inside is lost. For a database, that's a huge problem!

The solution โ€” Volume

A volume is a place outside the container (on your computer) where data is saved โ€” like an external hard disk. Even if the container is gone, the data in the volume stays exactly as it was.

flowchart TB
    subgraph nov["โŒ Without a volume"]
        A["Container<br/>data inside"] -->|"docker rm"| B["data is gone ๐Ÿ’ฅ"]
    end

    subgraph wv["โœ… With a volume"]
        C["Container"] -->|"connect"| D["Volume<br/>saved on the computer<br/>(like a hard disk)"]
        C -->|"docker rm โ†’ new container"| E["Old data is back! โœ…"]
        D --> E
    end

Using it โ€” the -v flag

docker run -d -v mydata:/var/lib/mysql mysql
-v  mydata : /var/lib/mysql
       โ”‚           โ”‚
       โ”‚           โ””โ”€โ”€ where data is saved inside the container
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ the volume's name (on your computer)

2 types of volumes

Named Volume Bind Mount
Left side is A name (mydata) A real folder path (/home/...)
Who manages it Docker You
Best used for Databases, important data While developing code

Volume commands

What you want to do Command
List volumes docker volume ls
Create one docker volume create mydata
Remove one docker volume rm mydata

๐Ÿง  Golden rule: Give every container that holds a database, user files, or other important data a volume.

How exactly does the folder connect?

-v SOURCE:TARGET โ€” left = the real location, right = the location inside the container. This is not a copy โ€” it's a live link. Both sides point to the very same files.

flowchart LR
    A["Folder on your computer<br/>/home/kamal/myshop/uploads<br/>(SOURCE = left)"] <-->|"the same files (live link)"| B["Folder inside the container<br/>/app/uploads<br/>(TARGET = right)"]
  • Named volume โ†’ managed by Docker (on Linux: /var/lib/docker/volumes/...). Best for databases.
  • Bind mount โ†’ a real folder of yours. Awesome while developing code:
    # Linux/Mac
    docker run -d -p 5000:5000 -v $(pwd):/app myapp
    # Windows PowerShell
    docker run -d -p 5000:5000 -v ${PWD}:/app myapp
    
    $(pwd) = "the folder I'm in right now". If you edit the code, it's instantly updated inside the container too!

๐Ÿ’ก Every program has a place inside the container where it stores its data:

  • MySQL โ†’ /var/lib/mysql
  • PostgreSQL โ†’ /var/lib/postgresql/data
  • MongoDB โ†’ /data/db

Lesson 7

Ports & Networking

What is a port?

An apartment building has one address (IP), with a number of flats inside (ports). A port = a specific door.

  • Web โ†’ port 80 / 443
  • MySQL โ†’ 3306
  • Our Flask app โ†’ 5000

The -p flag

A container is closed off โ€” to open a door to the outside, you need -p.

-p  8080 : 80
     โ”‚      โ”‚
     โ”‚      โ””โ”€โ”€ the port inside the container (where the app runs)
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ the port on your computer (the door from outside)
flowchart LR
    A["Browser<br/>localhost:8080"] --> B["Computer's door<br/>port 8080<br/>(left)"]
    B -->|"-p 8080:80"| C["Inside the container<br/>port 80<br/>nginx"]
  • The left and right numbers don't have to match: -p 3000:80 โ†’ localhost:3000
  • If a door is already in use, pick another number: -p 9090:80

Two containers talking to each other โ€” Docker Network

A Docker network is a private room just for a few containers. Containers inside it recognise each other by name (no IP needed).

docker network create shop-net
docker run -d --name shop-db --network shop-net mysql:8.0
docker run -d --name web-app --network shop-net myapp
flowchart LR
    subgraph net["Docker network: shop-net (private room)"]
        A["web-app<br/>Flask"] -->|"talks via the name 'shop-db'"| B["shop-db<br/>MySQL"]
    end

For web-app to talk to shop-db, it simply uses the name shop-db itself as the address.

-p vs --network

-p (port mapping) --network
What for? Outside world โ†’ container Container โ†’ container
Example browser โ†’ web app web app โ†’ database

Lesson 8

Docker Compose

The problem

Running a multi-container app with a pile of commands is long, hard, and error-prone.

The solution โ€” Compose

Docker Compose = the conductor of an orchestra. You write all your containers in one file (docker-compose.yml) and run them with a single command.

docker-compose.yml

services:
  web-app:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - shop-db

  shop-db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: mypass123
      MYSQL_DATABASE: shopdb
    volumes:
      - shop-data:/var/lib/mysql

volumes:
  shop-data:
  • services: โ†’ the containers
  • build: . โ†’ build from the Dockerfile
  • image: โ†’ pull from Docker Hub
  • ports / environment / volumes โ†’ the same as -p, -e, -v in the commands, just written neatly
  • depends_on โ†’ what needs to start first

๐Ÿง  None of this is new โ€” it's the very same things from the commands, written in an orderly way!

flowchart LR
    A["docker-compose.yml<br/>web-app + shop-db<br/>+ volume + network"] -->|"docker compose up"| B["Automatically: network + volume<br/>+ build + run<br/>the whole app โœ…"]

Commands

What you want to do Command
Run everything (in the background) docker compose up -d
Stop and remove everything docker compose down
View logs docker compose logs
Running services docker compose ps
Rebuild and run again docker compose up -d --build


โ˜ธ๏ธ Part 2 โ€” Kubernetes


Lesson 9

Why do we need Kubernetes?

Docker Compose is limited to a single computer. As your app grows, problems appear:

  1. ๐Ÿ’ฅ One computer isn't enough โ€” you need many servers
  2. ๐Ÿ˜ด If a container crashes, it doesn't come back automatically
  3. ๐Ÿ“Š How do you add or remove containers depending on the number of customers?
  4. ๐Ÿ”ฅ What if an entire server dies?

The solution โ€” Kubernetes (K8s)

Kubernetes = a "ship's captain" โš“ that automatically manages lots of containers across lots of servers. (K + 8 letters + s = K8s)

  • ๐Ÿงญ Distributes โ€” spreads containers properly across servers
  • โค๏ธโ€๐Ÿฉน Self-healing โ€” a dead one is automatically brought back
  • ๐Ÿ“ˆ Scaling โ€” more or fewer, depending on customers
  • ๐Ÿ”€ Load balancing โ€” spreads traffic properly
  • ๐Ÿ”„ Rolling updates โ€” switch versions with no downtime
flowchart TB
    subgraph c["Docker Compose"]
        A["Just one computer<br/>if it crashes โ†’ rebuild by hand"]
    end
    subgraph k["Kubernetes (the Captain manages automatically)"]
        B["Server 1<br/>web, web"]
        C["Server 2<br/>web, database"]
        D["Server 3<br/>web"]
    end

โš ๏ธ Kubernetes โ‰  a replacement for Docker. Docker creates containers, Kubernetes manages them โ€” they work together. For small projects Compose is enough; K8s is worth it for big, serious apps.


Lesson 10

Cluster, Nodes & Control Plane

  • Cluster = the entire Kubernetes setup (the whole restaurant)
  • Node = a computer/server (a member of the cluster)
  • Control Plane = the brain / manager (makes decisions, doesn't do the work itself)
  • Worker Nodes = the chefs (they actually run the containers)
flowchart TB
    subgraph cluster["CLUSTER (the whole setup)"]
        CP["Control Plane (the Captain / the brain)<br/>makes decisions โ€” doesn't run containers"]
        CP -->|"'do this'"| W1["Worker Node 1<br/>container, container"]
        CP --> W2["Worker Node 2<br/>container, database"]
        CP --> W3["Worker Node 3<br/>container"]
    end

kubectl โ€” your walkie-talkie

The tool you use to give commands to the control plane (the captain). (Pronounced "cube-control".)

kubectl get nodes    # see the nodes in the cluster
kubectl get pods     # see the running pods

docker = for one computer. kubectl = for the entire cluster.

A cluster to learn on

On your own laptop: Minikube, kind, or Docker Desktop (enable Kubernetes).

๐Ÿ’ฌ Who does kubectl talk to?

kubectl is only for you (from outside) โ†’ giving orders to the captain (Control Plane). The captain โ†” worker conversation isn't done with kubectl โ€” that happens automatically inside the cluster, via components like kubelet.

flowchart LR
    U["You (laptop)"] -->|"kubectl"| CP["Control Plane (Captain)"]
    CP <-->|"automatic (not kubectl)"| W["Worker Nodes<br/>(kubelet)"]

Lesson 11

Pods

Kubernetes doesn't manage containers directly โ€” it puts the container in a Pod and manages the Pod.

Pod = the smallest unit that K8s manages, wrapping one container (or several) โ€” like a pea pod ๐Ÿซ›.

flowchart LR
    subgraph p1["Pod (usually)"]
        C1["container<br/>(web-app)"]
    end
    subgraph p2["Pod (sometimes)"]
        C2["app"]
        C3["helper"]
    end
  • Usually a pod has just one container (99% of the time)
  • Containers in the same pod โ†’ share network + storage (they talk over localhost)
  • โš ๏ธ Pods are temporary โ€” they die, new ones come, and the IP changes

Pod YAML (for learning/testing only)

apiVersion: v1
kind: Pod
metadata:
  name: my-web-pod
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - containerPort: 80
kubectl apply -f my-pod.yaml
kubectl get pods

โš ๏ธ In real projects you don't create pods directly (if one crashes, it doesn't come back). You use a Deployment (next lesson).

Hierarchy โ€” Russian dolls ๐Ÿช†

flowchart TB
    CL["CLUSTER"] --> N["NODE (a computer)"]
    N --> P["POD (many on one node)"]
    P --> C["CONTAINER (usually one per pod)"]

Cluster โŠƒ Nodes โŠƒ Pods โŠƒ Containers โ€” like building โŠƒ apartments โŠƒ families. (One node can hold many pods!)


Lesson 12

Deployments โญ

The most important, everyday-use thing in Kubernetes.

Deployment = the manager that looks after pods (like a thermostat ๐ŸŒก๏ธ). You say "what I want" โ€” it takes care of everything else.

Desired State

You say: "I always want 3 of this pod" (replicas: 3).

  • If one dies โ†’ Kubernetes automatically creates a new one (back to 3)
  • Just like a thermostat holding the temperature at 24ยฐC
flowchart TB
    D["Deployment (manager)<br/>replicas: 3"] --> P1["Pod 1 โœ…"]
    D --> P2["Pod 2 ๐Ÿ’ฅ crash"]
    D --> P3["Pod 3 โœ…"]
    D -->|"'want 3, have 2' โ†’ auto"| P2b["Pod 2 (new!) โœ…"]

Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: myapp:1.0
          ports:
            - containerPort: 5000
  • replicas: 3 โญ = "I want 3 pods"
  • template: = the pods to be created should look like this
  • labels / selector = a "sticker" ๐Ÿท๏ธ on the pods (this is how the Deployment finds its own pods)

Commands

kubectl apply -f deployment.yaml          # create/update
kubectl get deployments
kubectl get pods
kubectl scale deployment web-app --replicas=10   # scale! ๐Ÿ“ˆ
kubectl rollout undo deployment web-app          # rollback ๐Ÿ”„

Self-healing test: kubectl delete pod <name> โ†’ kubectl get pods โ†’ a new one has appeared! ๐Ÿคฏ

๐Ÿ”„ Rolling Updates

Change the image in the YAML to myapp:2.0 and run kubectl apply โ†’ the old pods are gradually removed and replaced with new ones โ†’ switching versions with no downtime.

๐Ÿง  Golden rule: In Kubernetes you never create a pod directly โ€” always use a Deployment.


Lesson 13

Services

2 problems

  1. A pod's IP keeps changing โ€” how do you talk to it?
  2. There are 3 pods โ€” which one gets the traffic?

The solution โ€” Service

Service = a front desk ๐Ÿ“ž that sits in front of the pods, has a fixed address, and distributes traffic (like an office receptionist).

  • Even if the pods' IPs change, the Service's address stays fixed โœ…
  • Traffic is spread properly across the pods (load balancing) โœ…
  • It finds the pods using a label (sticker)
flowchart LR
    U["Users"] --> S["Service<br/>fixed address<br/>(front desk)"]
    S -->|"distributes"| P1["Pod 1"]
    S --> P2["Pod 2"]
    S --> P3["Pod 3"]

3 types of Service

Type What it's for When
ClusterIP (default) Internal only ๐Ÿ”’ web-app โ†’ database
NodePort Opened to the outside (simply) ๐Ÿšช while testing
LoadBalancer The real internet ๐ŸŒ a production website

Service YAML

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP
  selector:
    app: web          # to the pods that have the 'app: web' sticker
  ports:
    - port: 80
      targetPort: 5000
kubectl apply -f service.yaml
kubectl get services

๐Ÿ—๏ธ Deployment + Service = the basic set for any Kubernetes app.


Lesson 14

ConfigMaps & Secrets

Hard-coding settings/passwords into your code is bad ๐Ÿ˜ฌ. So we keep them separate:

  • ConfigMap ๐Ÿ“‹ = settings that aren't secret (db name, mode, URL)
  • Secret ๐Ÿ” = secret things (passwords, API keys)
flowchart LR
    CM["ConfigMap ๐Ÿ“‹<br/>db name, mode, URL"] -->|"inject"| POD["Pod (app)<br/>gets settings from them<br/>(not hard-coded in the code)"]
    SEC["Secret ๐Ÿ”<br/>password, API key"] -->|"inject"| POD

YAML

ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DB_NAME: shopdb
  APP_MODE: production

Secret:

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
stringData:
  DB_PASSWORD: mypass123

โš ๏ธ Important: By default a Secret is only "hidden" using base64 โ€” that is not real encryption. Anyone can decode it. In production, enable encryption / use tools like Vault.

Injecting into a Pod (environment variables)

containers:
  - name: web
    image: myapp:1.0
    env:
      - name: DB_NAME
        valueFrom:
          configMapKeyRef:
            name: app-config
            key: DB_NAME
      - name: DB_PASSWORD
        valueFrom:
          secretKeyRef:
            name: app-secret
            key: DB_PASSWORD
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl get configmaps
kubectl get secrets

Lesson 15

Namespaces

Namespace = a separate "room" ๐Ÿ—‚๏ธ inside the cluster โ€” to keep things separated and tidy (like rooms in an office building).

  • Names only need to be unique within the same namespace (the same name is fine in different namespaces)
  • Most common use: separating dev / test / production
flowchart TB
    subgraph CL["CLUSTER"]
        subgraph dev["Namespace: dev"]
            D1["web-app"]
            D2["database"]
        end
        subgraph test["Namespace: test"]
            T1["web-app"]
            T2["database"]
        end
        subgraph prod["Namespace: production"]
            P1["web-app"]
            P2["database"]
        end
    end

(All 3 namespaces have a "web-app" โ€” no conflict!)

Ones that already exist

  • default โ†’ where things go if you don't specify a namespace
  • kube-system โ†’ Kubernetes' own stuff (โš ๏ธ don't touch)
  • kube-public โ†’ public stuff

Commands โ€” the -n flag matters!

kubectl get namespaces
kubectl create namespace dev
kubectl get pods -n dev           # pods in the dev namespace
kubectl get pods -n production    # pods in production

โš ๏ธ If you don't add -n, it always looks at default. If you can't see the pods in dev, add -n dev!

In YAML: metadata.namespace: dev


Lesson 16

Volumes & Persistent Storage

Since pods are temporary, you need storage to keep data safe (like Docker volumes). Kubernetes separates this into the request and the actual storage:

  • PersistentVolume (PV) ๐Ÿ—„๏ธ = the actual storage (the books ๐Ÿ“š)
  • PersistentVolumeClaim (PVC) ๐Ÿ“ = the "I need storage" request (the application form)
  • StorageClass โšก = automatically creates PVs for you (in the cloud)
flowchart LR
    POD["Pod (app)<br/>needs to save data"] -->|"use"| PVC["PVC (request)<br/>'I need 5GB' ๐Ÿ“"]
    PVC -->|"K8s matches"| PV["PV (actual storage)<br/>hard disk / cloud ๐Ÿ—„๏ธ"]

YAML

PVC (the request):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-storage
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

Using it in a Pod/Deployment:

spec:
  containers:
    - name: db
      image: mysql:8.0
      volumeMounts:
        - name: data
          mountPath: /var/lib/mysql      # where MySQL keeps its data inside the container
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: my-storage
kubectl apply -f pvc.yaml
kubectl get pvc
kubectl get pv
kubectl get storageclass
Docker Kubernetes
Keeping data safe Volume PV
How you ask for it -v name:/path PVC
Auto-creates it โ€” StorageClass

Lesson 17

Ingress

The problems

  1. ๐Ÿ’ธ One LoadBalancer per app โ†’ very expensive
  2. You can't split traffic by URL (/products, /blog)

The solution โ€” Ingress

Ingress ๐Ÿšฆ = the smart traffic policeman standing at the entrance to the cluster. It takes in traffic through a single door, looks at the URL, and directs it to the right Service.

flowchart LR
    I["Internet<br/>users"] --> ING["Ingress ๐Ÿšฆ<br/>looks at the URL<br/>(one door)"]
    ING -->|"/products"| S1["products Service"]
    ING -->|"/blog"| S2["blog Service"]
    ING -->|"/api"| S3["api Service"]

โš ๏ธ Ingress = just the rules. You need to install an Ingress Controller to actually carry them out (the most popular: nginx-ingress).

  • Ingress = the rulebook ๐Ÿ“–
  • Ingress Controller = the actual policeman ๐Ÿ‘ฎ

Ingress YAML

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
spec:
  rules:
    - host: shop.com
      http:
        paths:
          - path: /products
            pathType: Prefix
            backend:
              service:
                name: products-service
                port:
                  number: 80
          - path: /blog
            pathType: Prefix
            backend:
              service:
                name: blog-service
                port:
                  number: 80
kubectl apply -f ingress.yaml
kubectl get ingress

๐Ÿ”’ HTTPS (SSL/TLS) can also be managed in the Ingress, all in one place.


Lesson 18

Everything together โ€” a real app ๐ŸŽ“

An online shop โ€” every concept we've learned, working together:

flowchart TB
    U["Users (internet) ๐ŸŒ"] --> ING["Ingress ๐Ÿšฆ (shop.com)"]
    ING --> WS["web Service ๐Ÿšช"]
    WS --> WD

    subgraph WD["web Deployment (replicas: 3) โค๏ธโ€๐Ÿฉน"]
        W1["web pod"]
        W2["web pod"]
        W3["web pod"]
    end

    CFG["ConfigMap ๐Ÿ“‹<br/>Secret ๐Ÿ”"] -->|"settings"| WD
    WD -->|"via the name 'db-service'"| DS["db Service ๐Ÿšช (ClusterIP)"]
    DS --> DB["db pod (MySQL) ๐Ÿ—„๏ธ"]
    DB --> PVC["PVC โ†’ PV ๐Ÿ’พ<br/>(data is safe)"]

The journey of a request ๐Ÿšถ

  1. ๐ŸŒ A customer comes to shop.com
  2. ๐Ÿšฆ Ingress โ†’ looks at the URL and sends it to the web Service
  3. ๐Ÿšช web Service โ†’ picks one of the 3 pods (load balancing)
  4. ๐Ÿ“ฆ web pod processes it (settings/password from the ConfigMap + Secret)
  5. ๐Ÿ—„๏ธ If it needs the db โ†’ goes to the db Service using the name db-service
  6. ๐Ÿ’พ db pod โ†’ data is kept safe on the PVC โ†’ PV
  7. โ†ฉ๏ธ The answer goes back to the customer โ€” the website appears! ๐ŸŽ‰

Deployment order

File What it's for Lesson
configmap.yaml settings 14
secret.yaml password 14
pvc.yaml database storage 16
db-deployment.yaml + db-service.yaml database 12, 13
web-deployment.yaml + web-service.yaml web app 12, 13
ingress.yaml internet access 17
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f pvc.yaml
kubectl apply -f db-deployment.yaml
kubectl apply -f db-service.yaml
kubectl apply -f web-deployment.yaml
kubectl apply -f web-service.yaml
kubectl apply -f ingress.yaml

๐Ÿ’ก Put everything in one folder and run kubectl apply -f . to do it all at once!



Commands Cheat Sheet

๐Ÿณ Docker

# Images & containers
docker --version
docker run hello-world
docker run -d -p 8080:80 nginx       # detached + port mapping
docker ps                             # running containers
docker ps -a                          # all of them
docker stop <name>                    # stop
docker start <name>                   # start again
docker rm <name>                      # remove
docker logs <name>                    # view logs
docker exec -it <name> bash           # go inside

# Images
docker images                         # list images
docker pull mysql                     # download
docker rmi <image>                    # remove an image
docker build -t myapp .               # Dockerfile โ†’ image

# Volumes
docker volume ls
docker volume create mydata
docker run -d -v mydata:/var/lib/mysql mysql
docker run -d -v $(pwd):/app myapp    # bind mount

# Network
docker network create shop-net
docker run -d --name web --network shop-net myapp

# Compose
docker compose up -d
docker compose down
docker compose logs
docker compose ps
docker compose up -d --build

โ˜ธ๏ธ Kubernetes (kubectl)

# View
kubectl get nodes
kubectl get pods
kubectl get deployments
kubectl get services
kubectl get ingress
kubectl get configmaps
kubectl get secrets
kubectl get pvc
kubectl get pv
kubectl get namespaces

# Create / update
kubectl apply -f file.yaml
kubectl apply -f .                    # everything in the folder

# Deployment
kubectl scale deployment web-app --replicas=10
kubectl rollout undo deployment web-app

# Namespace
kubectl create namespace dev
kubectl get pods -n dev               # -n = which namespace

# Debug
kubectl logs <pod>
kubectl describe pod <pod>
kubectl delete pod <pod>              # (the Deployment will recreate it)

What's next?

Going deeper into Kubernetes:

  • ๐Ÿฉบ Probes (liveness/readiness) โ€” checking whether a pod is healthy
  • ๐Ÿ“Š HPA (Horizontal Pod Autoscaler) โ€” automatically increasing/decreasing the number of pods
  • ๐Ÿ“ฆ Helm โ€” the "package manager" for Kubernetes
  • ๐Ÿ” RBAC โ€” who is allowed to do what (security)

Real-world work:

  • ๐Ÿ”„ CI/CD โ€” push code โ†’ automatically build & deploy
  • ๐Ÿ“ˆ Monitoring โ€” Prometheus + Grafana
  • โ˜๏ธ Managed Kubernetes โ€” AWS (EKS), Google (GKE), Azure (AKS)

The most important thing โ€” hands-on experience! ๐Ÿ› ๏ธ Install Docker Desktop or Minikube on your laptop and build this shop app by hand. You learn far more by doing than by reading โ€” it's what really sticks in the mind.


๐ŸŽ“ Summary of the core ideas

Concept In one sentence
Image The app's recipe/blueprint (read-only)
Container The real, running app made from that recipe
Dockerfile The recipe sheet that builds the image
Volume External storage to keep data safe
Docker Compose Multi-container โ†’ one file, one command
Kubernetes The captain that auto-manages lots of containers
Cluster The entire K8s setup
Node A computer/server
Pod The smallest unit wrapping containers
Deployment The manager that looks after pods (self-heal, scale)
Service A fixed address for pods + load balancing
ConfigMap / Secret Settings / secrets (kept separate from the code)
Namespace A separate room inside the cluster
PV / PVC The actual storage / the request for storage
Ingress The policeman that routes traffic by URL

๐ŸŽ‰ Congratulations! You've learned Docker and Kubernetes from start to finish. Now go get some hands-on experience! ๐Ÿ’ช