We closed the previous lesson saying you already had a cluster, a CLI and the language of manifests, and that all that was missing was the patient. Here it is. This lesson introduces Rutas Norte S.L. in depth, the fictional company whose coach ticket sales platform we are going to deploy and operate on Kubernetes across the eleven remaining modules. It is not a decorative example: every concept you learn from now on will enter the stage because Rutas Norte needs it, and by the end of the course you will have a complete platform —secure, observable and autoscaled— built piece by piece. You will see the company's starting position, each of its six components, the target architecture, the map of which module contributes what, and the conventions we will always follow. And you will finish with the first real deployment: web-store running in your cluster and responding in your browser.
Contents
- The company and its current problem
- The six components of the platform
- The target architecture on Kubernetes
- Roadmap: what each module contributes
- Project conventions
- The first deployment:
web-storeinrutas-norte-dev - Why this pod is fragile
- The company and its current problem
Rutas Norte S.L. is an intercity coach operator that sells its tickets online through the Rutas Norte platform. Thirteen people in the technical department, around 4,000 bookings a day on average and 25,000 on peak days.
Its current infrastructure:
- Two rented machines with Docker Compose. One serves the website and the API; the other, the database.
- Manual deployments: somebody connects over SSH, runs
docker compose pull && docker compose up -dand crosses their fingers. There is a service outage of one to two minutes, so deployments only happen in the small hours of Tuesday. - No self-healing: in March, the API container died at 02:40 from a memory leak and nobody noticed until 07:15. Four and a half hours with no ticket sales.
- No elasticity: at bank-holiday and holiday peaks, traffic multiplies sixfold. The answer so far has been to rent a machine sized for August and pay for it all twelve months.
- Secrets everywhere: the PostgreSQL password lives in a
.envfile that has been shared by email. That database contains the name, ID number, phone and email of every customer. - No observability: logs are in files inside each container. Diagnosing a problem means going in over SSH and searching with
grep.
The technical leadership has approved the migration to Kubernetes with four measurable goals: zero downtime on deployments, automatic recovery from failures, elastic capacity at the peaks and traceability and access control over personal data. Those four goals are, one by one, the syllabus ahead of you.
- The six components of the platform
These names will stay with you throughout the course. Keep them exactly as they are.
2.1. web-store
- What it is: the public front end. An SPA served by
nginxwith the search results, the seat picker and the payment process. - State: stateless. Any replica serves any request.
- Image:
registry.rutasnorte.example/web-store:1.8.0 - Domain:
www.rutasnorte.example - What it needs from the cluster: several replicas, HTTP/HTTPS exposure to the outside world, injected configuration (the API URL varies per environment) and downtime-free updates.
2.2. bookings-api
- What it is: the REST API in Node.js. It checks seat availability, calculates prices, creates bookings and issues tickets. It is the heart of the business.
- State: stateless. All persistence lives in PostgreSQL and Redis.
- Image:
registry.rutasnorte.example/bookings-api:2.4.0 - Domain:
api.rutasnorte.example - What it needs from the cluster: replicas, autoscaling by load, database credentials as a secret, health probes, resource limits and restricted access to the database.
2.3. bookings-postgres
- What it is: PostgreSQL 16 with the tables for bookings, customers, routes and departures.
- State: stateful. It is the system's critical component.
- Image:
postgres:16 - What it needs from the cluster: persistent storage that survives the pod being recreated, a stable network identity, credentials managed as a secret, backups and a network policy that stops just anyone connecting to it.
2.4. redis-cache
- What it is: an in-memory cache of seat availability by departure and date. It absorbs most of the search queries and stops PostgreSQL being overwhelmed.
- State: stateful, but expendable. If it is lost, it is repopulated from the database; only performance degrades, and only for a few minutes.
- Image:
redis:7.2-alpine - What it needs from the cluster: a stable name, strict memory limits and eviction policy configuration.
2.5. notifications-worker
- What it is: a background process that consumes a queue and sends booking confirmation emails and journey reminders.
- State: stateless, but it receives no inbound traffic: it does not need to be reachable from outside.
- Image:
registry.rutasnorte.example/notifications-worker:1.2.0 - What it needs from the cluster: replicas scaled by queue length rather than by CPU, SMTP credentials as a secret, and a graceful shutdown so no send is lost halfway.
2.6. occupancy-reports
- What it is: a scheduled task that every night at 02:30 calculates the previous day's occupancy by line and departure and leaves a report for the commercial department.
- State: stateless, finite in execution: it starts, works for a few minutes and finishes.
- Image:
registry.rutasnorte.example/occupancy-reports:1.0.3 - What it needs from the cluster: scheduled execution, retry control, a limit on concurrent runs and a history of results.
Comparative summary
| Component | State | Inbound traffic | Kubernetes object that will govern it | Module |
|---|---|---|---|---|
web-store |
Stateless | Public (HTTP) | Deployment + Service + Ingress | 2, 4 |
bookings-api |
Stateless | Public (HTTP) and internal | Deployment + Service + Ingress + HPA | 2, 4, 9 |
bookings-postgres |
Stateful | Internal only, restricted | StatefulSet + PVC + headless Service | 5, 6 |
redis-cache |
Expendable state | Internal only | Deployment or StatefulSet + Service | 2, 5 |
notifications-worker |
Stateless | None | Deployment (no Service) + KEDA | 2, 9 |
occupancy-reports |
Stateless, finite | None | CronJob | 6 |
That table already contains a design lesson: the type of Kubernetes object you need follows from two questions —is it stateful? and does it receive traffic?— before you write a single line of YAML.
- The target architecture on Kubernetes
This is the destination we will reach by the end of module 11.
flowchart TB
U["Customers<br/>browser and mobile"]
DNS["DNS<br/>www / api .rutasnorte.example"]
U --> DNS
subgraph CL["Kubernetes cluster"]
ING["Ingress Controller<br/>TLS with cert-manager"]
subgraph NS["namespace rutas-norte-pro"]
SVCW["Service web-store"]
SVCA["Service bookings-api"]
TW1["Pod web-store"]
TW2["Pod web-store"]
API1["Pod bookings-api"]
API2["Pod bookings-api"]
API3["Pod bookings-api"]
SVCR["Service redis-cache"]
RED["Pod redis-cache"]
SVCP["Service bookings-postgres"]
PG["StatefulSet<br/>bookings-postgres-0"]
PVC[("PVC 20 GiB")]
WK1["Pod notifications-worker"]
WK2["Pod notifications-worker"]
CJ["CronJob<br/>occupancy-reports"]
end
end
DNS --> ING
ING --> SVCW
ING --> SVCA
SVCW --> TW1
SVCW --> TW2
SVCA --> API1
SVCA --> API2
SVCA --> API3
API1 --> SVCR
API2 --> SVCR
API3 --> SVCP
SVCR --> RED
SVCP --> PG
PG --- PVC
WK1 --> SVCP
WK2 --> SVCP
CJ --> SVCP
Points worth reading in the diagram:
- A single front door: the Ingress Controller terminates TLS and routes by domain to the matching Service.
- No component knows any IPs: everything communicates through the Service name, resolved by the cluster's internal DNS.
bookings-postgresis the only one with a persistent disk, and it must only be reachable frombookings-api, the worker and the CronJob. That will be enforced with a NetworkPolicy in module 4.notifications-workerhas no Service: nobody talks to it, it consumes work and goes out to talk to others.
- Roadmap: what each module contributes
This table is the course's contract: it says which piece of Rutas Norte we add or improve in each module.
| Module | What is added or improved in Rutas Norte |
|---|---|
| 1. Introduction | The practice cluster, the conventions, the rutas-norte-dev namespace and the first web-store pod |
| 2. Core components | web-store and bookings-api as Deployments with replicas; internal Services; downtime-free version updates and rollback; separation into namespaces per environment |
| 3. Configuration and secrets | Store and API configuration in ConfigMaps; the PostgreSQL password and SMTP credentials in Secrets; requests and limits on every component; quotas per environment; dedicated ServiceAccounts |
| 4. Networking | Publishing www.rutasnorte.example and api.rutasnorte.example with Ingress and TLS; internal DNS between components; a NetworkPolicy isolating bookings-postgres |
| 5. Storage | A persistent volume for the database via PVC and StorageClass; volume expansion; backup and restore of the bookings |
| 6. Advanced concepts | bookings-postgres migrated to a StatefulSet; occupancy-reports as a CronJob; an init container that waits for the database; a metrics sidecar; a log agent as a DaemonSet |
| 7. Monitoring and logging | Liveness and readiness probes on every component; kubectl top; Prometheus and Grafana with a bookings-per-minute dashboard; centralized logs; incident debugging |
| 8. Security | RBAC per team and environment; unprivileged containers with a read-only filesystem; Pod Security Standards; network hardening; image signing and scanning |
| 9. Scaling and performance | HPA on bookings-api for the bank-holiday peaks; VPA for sizing; node autoscaling; scaling notifications-worker by queue length with KEDA; PodDisruptionBudgets |
| 10. Ecosystem | Packaging the platform with Helm; per-environment variants with Kustomize; continuous deployment with GitOps; moving to a managed cluster |
| 11. Real-world cases | The full production rollout; a CI/CD pipeline; a canary deployment of an API version; daily operations, runbooks and cost control |
| 12. Certification | A cross-cutting review aimed at CKA, CKAD and CKS using the platform as a test bed |
- Project conventions
These rules apply in every lesson. Adopt them from now on; they are also real-world good practice.
5.1. Namespaces per environment
| Namespace | Use | Who deploys |
|---|---|---|
rutas-norte-dev |
Development. Fictional data, reduced resources | Anyone on the team |
rutas-norte-pre |
Pre-production. A faithful replica of production for validation | The automated pipeline |
rutas-norte-pro |
Production. Real customer data | Only the pipeline, with approval |
Throughout module 1 and much of module 2 we will work only in rutas-norte-dev.
5.2. Labelling scheme
Every object carries at least these three labels:
metadata:
labels:
app: web-store # the component
app.kubernetes.io/part-of: rutas-norte # the whole platform
environment: dev # the environmentThis enables queries like these, which are worth their weight in gold as the cluster grows:
kubectl get all -l app.kubernetes.io/part-of=rutas-norte -n rutas-norte-dev
kubectl get pods -l environment=pro -A
kubectl logs -l app=bookings-api -n rutas-norte-dev --tail=505.3. Registry and images: immutable tags
The company's image registry is registry.rutasnorte.example. The project's most important rule when it comes to images:
Every version is published with a unique, immutable tag that is never reused.
latestis forbidden in every environment.
| Reference | Valid? | Why |
|---|---|---|
registry.rutasnorte.example/bookings-api:2.4.0 |
Yes | A semantic version, immutable |
registry.rutasnorte.example/bookings-api:2.4.0-a3f9c1b |
Yes, better | It includes the commit: full traceability |
registry.rutasnorte.example/bookings-api@sha256:9f3a1c... |
Yes, the strictest | A digest: impossible to impersonate |
registry.rutasnorte.example/bookings-api:latest |
No | Two replicas of the same Deployment can end up running different code; rollback stops being reliable and you do not know what is in production |
5.4. Repository structure
The one we defined in the lesson Objects, YAML Manifests and the Declarative Model:
rutas-norte/
└── k8s/
├── base/ # common definition
├── environments/
│ ├── dev/
│ ├── pre/
│ └── pro/
├── local-environment/
└── README.mdAnd the golden rule that goes with it: if a change is not in Git, it does not exist. No kubectl edit as a deployment method.
- The first deployment:
web-store in rutas-norte-dev
web-store in rutas-norte-devEnough theory. Let us deploy the first real piece of Rutas Norte.
Step 1: check the cluster
rutas-norte
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
rutas-norteStep 2: create the namespace declaratively
# k8s/base/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: rutas-norte-dev
labels:
app.kubernetes.io/part-of: rutas-norte
environment: devNote that we created it with a manifest, not with kubectl create namespace. That is a deliberate decision: the namespace is part of the project's definition and must be in Git like everything else.
And now, the productivity tip from lesson 01-05, which will save you typing -n rutas-norte-dev hundreds of times:
Step 3: write the pod manifest
# k8s/base/web-store-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web-store
namespace: rutas-norte-dev
labels:
app: web-store
app.kubernetes.io/part-of: rutas-norte
environment: dev
annotations:
rutasnorte.example/owner: [email protected]
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"A field-by-field review, leaning on what we have learned:
apiVersion: v1andkind: Pod: the Pod belongs to the core group, which is why it carries no group prefix.metadata.name: a unique name within the namespace.metadata.labels: the project's three labels. In module 2 they will be the ones the Service uses to find its pods.metadata.annotations: informational metadata, not queryable.spec.containers: a list. Here a single element, as will be usual.image: nginx:1.27-alpine: we use the publicnginximage becauseregistry.rutasnorte.exampleis a fictional registry and does not exist. Throughout the course, whenever an image fromregistry.rutasnorte.exampleappears, mentally substitute —and substitute in your cluster— its public equivalent. An explicit tag, neverlatest, in line with the project convention.ports: this is informational documentation; it opens nothing by itself. Real exposure will come with the Service (module 2).resources:requestsis what the scheduler reserves in order to decide which node it fits on;limitsis the ceiling the kubelet enforces. We will study them thoroughly in module 3, but it is worth setting them from day one.
Step 4: validate and apply
kubectl apply -f k8s/base/web-store-pod.yaml --dry-run=client
kubectl apply -f k8s/base/web-store-pod.yamlStep 5: watch it start
NAME READY STATUS RESTARTS AGE
web-store 0/1 Pending 0 0s
web-store 0/1 ContainerCreating 0 1s
web-store 1/1 Running 0 4sYou are watching, live, the walkthrough from the lesson Kubernetes Architecture: Pending while the scheduler picks a node, ContainerCreating while the kubelet pulls the image and prepares the networking, Running when the container is alive. Stop it with Ctrl+C.
Step 6: inspect
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
web-store 1/1 Running 0 45s 10.244.0.21 rutas-norte <none>Name: web-store
Namespace: rutas-norte-dev
Node: rutas-norte/192.168.49.2
Labels: app=web-store
app.kubernetes.io/part-of=rutas-norte
environment=dev
Status: Running
IP: 10.244.0.21
Containers:
nginx:
Image: nginx:1.27-alpine
Port: 80/TCP
State: Running
Ready: True
Restart Count: 0
Limits: cpu: 200m, memory: 128Mi
Requests: cpu: 50m, memory: 64Mi
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 50s default-scheduler Successfully assigned rutas-norte-dev/web-store to rutas-norte
Normal Pulled 49s kubelet Container image "nginx:1.27-alpine" already present on machine
Normal Created 49s kubelet Created container nginx
Normal Started 49s kubelet Started container nginxThe four events tell the whole story: the scheduler assigned a node, and then the kubelet obtained the image, created the container and started it. Exactly the division of responsibilities we studied.
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/08/05 10:22:14 [notice] 1#1: nginx/1.27.0
2026/08/05 10:22:14 [notice] 1#1: start worker processesStep 7: see it in the browser
The pod has the IP 10.244.0.21, but that IP only exists inside the cluster. To reach it from your machine we use port-forward:
Open http://localhost:8080 in your browser: you will see the nginx welcome page. In Rutas Norte, that would be the ticket store's home page. Leave the command running while you test and stop it with Ctrl+C.
From another terminal you can also check it without a browser:
Congratulations: you have just deployed the first Rutas Norte component on Kubernetes.
- Why this pod is fragile
Before celebrating too much, let us run the experiment that gives module 2 its meaning.
It has vanished and it does not come back. Compare that with what we said in the concepts lesson: a loose pod has no controller watching it. Nobody runs a reconciliation loop over it, because the desired state you declared was literally "let this pod exist", and by deleting it you also deleted that declaration.
The four problems with a loose pod:
| Problem | Consequence for Rutas Norte |
|---|---|
| It is not recreated if it dies | It reproduces exactly the four-and-a-half-hour overnight outage the company suffered in March |
| It cannot be scaled | To have three copies you would have to write three manifests with three different names |
| It cannot be updated without downtime | Changing the image means deleting and recreating: there is an interval with no service |
| It has no stable address | Every recreation changes the IP, and nobody knows where to connect |
The solution is the hierarchy you already know from the conceptual map: Deployment → ReplicaSet → Pod. A Deployment declares "I want 3 replicas of this image", the ReplicaSet keeps them alive come what may, and a Service gives them a stable name.
Recreate the pod to leave the cluster as it was and close the module with the platform up and running:
Common Mistakes and Tips
- Applying in the wrong namespace. If
kubectl get podsshows nothing, check the namespace withkubectl config view --minify | grep namespaceor use-A. Declaringmetadata.namespaceexplicitly in every manifest, as we do here, removes the problem at the root. - Using
registry.rutasnorte.exampleliterally. It is a fictional registry: it does not exist. The pod would sit inImagePullBackOff. In the practical exercises always use the equivalent public image (nginx,postgres,redis,node). - Believing
ports.containerPortexposes the pod. It does nothing by itself: it is documentation. Real access comes withport-forward(for testing), a Service (module 2) or an Ingress (module 4). - Settling for
port-forwardas the solution. It is a single-user debugging tool, tied to your terminal. It is never a way to expose a service. - Creating loose pods and getting used to it. This has been a deliberate exercise to understand the fragility. From module 2 onwards, everything goes in Deployments.
- Forgetting
resources. Withoutrequests, the scheduler cannot plan properly; withoutlimits, a runaway container can take down its neighbours. Set them from the start even if the tuning comes later. - Tip: create the repository with the
k8s/structure right now and make a commit withnamespace.yamlandweb-store-pod.yaml. Working this way from the very first lesson is the difference between finishing the course with a reproducible platform and finishing with a cluster full of things nobody knows how were created.
Exercises
Exercise 1: Designing the platform on paper
Without writing any manifest, complete a table with the six Rutas Norte components and, for each one, answer: (a) is it stateful?, (b) does it receive inbound traffic and from where?, (c) which Kubernetes object will govern it?, and (d) what would happen today, with Docker Compose, if the machine hosting it were shut down? Then justify in three lines why redis-cache and bookings-postgres, both being "stateful", get very different treatment.
Exercise 2: Deploying and exploring redis-cache
Following every project convention, create the manifest k8s/base/redis-cache-pod.yaml for a redis-cache pod in rutas-norte-dev with the public image redis:7.2-alpine, port 6379, the project's three labels and requests of 50m of CPU and 64Mi of memory. Then:
- Validate it without touching the cluster and apply it.
- Check that it is
Runningand find out its IP and its node. - Get into the container and check that Redis responds by running
redis-cli ping. - Show only the Rutas Norte platform pods using the common label.
Exercise 3: Demonstrating the fragility and anticipating the solution
- Simulate an application crash by killing the main process of the
web-storecontainer withkubectl exec, and watch what happens with--watch. Does the container come back? Does theRESTARTScounter change? Does the pod's IP change? - Now delete the whole pod with
kubectl delete podand watch again. Does it come back? - Explain the difference between the two cases, stating which component acts in each.
- Write in two sentences which object you would need for the second case to also recover on its own.
Solutions
Solution 1
| Component | (a) State | (b) Inbound traffic | (c) Object | (d) If the machine goes down today |
|---|---|---|---|---|
web-store |
No | Public, from the internet | Deployment + Service + Ingress | The website stops responding entirely |
bookings-api |
No | Public and internal | Deployment + Service + Ingress + HPA | Bookings can neither be queried nor created |
bookings-postgres |
Yes | Internal, restricted | StatefulSet + PVC | A total outage and a risk of data loss if the disk is not replicated |
redis-cache |
Yes, expendable | Internal | Deployment or StatefulSet + Service | Performance degradation; the service continues with direct database queries |
notifications-worker |
No | None | Deployment (no Service) | Confirmation emails pile up unsent |
occupancy-reports |
No, finite | None | CronJob | The overnight report is not generated |
redis-cache and bookings-postgres get different treatment because Redis's state is rebuildable: if it is lost, it is repopulated from the database and only performance degrades. PostgreSQL's state is the business's source of truth: losing it means losing bookings and customers' personal data. That is why PostgreSQL demands persistent storage, a stable identity, backups and network isolation, whereas Redis only needs a memory limit and an eviction policy.
Solution 2
# k8s/base/redis-cache-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-cache
namespace: rutas-norte-dev
labels:
app: redis-cache
app.kubernetes.io/part-of: rutas-norte
environment: dev
spec:
containers:
- name: redis
image: redis:7.2-alpine
ports:
- name: redis
containerPort: 6379
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"kubectl apply -f k8s/base/redis-cache-pod.yaml --dry-run=client
kubectl apply -f k8s/base/redis-cache-pod.yaml
kubectl get pod redis-cache -o wide
kubectl exec -it redis-cache -- redis-cli ping
kubectl get pods -l app.kubernetes.io/part-of=rutas-norteSolution 3
The container does come back: RESTARTS goes from 0 to 1 and the pod's IP does not change, because it is the same pod. The one that recovers it is the kubelet, applying the restartPolicy: Always a pod has by default. The kubelet restarts containers inside an existing pod.
The pod does not come back. There is no controller watching over its existence.
-
The difference lies in who acts and on what: the kubelet watches the containers inside the pods assigned to it and restarts them if they die, but it cannot recreate a pod that no longer exists as an object in the API. Recreating pods is the job of a kube-controller-manager controller, and a loose pod has none associated with it: by deleting it, you deleted the desired state itself.
-
You need a Deployment, which creates a ReplicaSet whose controller permanently maintains the declared number of replicas. With it, the desired state stops being "let this specific pod exist" and becomes "let N pods with these characteristics exist", so deleting one immediately triggers the creation of another.
Conclusion
You now know the project that gives the whole course its meaning. Rutas Norte S.L. is a company with entirely real problems —overnight outages with no response, demand peaks on bank holidays and in the holiday season, deployments with service downtime and badly protected customer personal data— and its platform is made up of six pieces with clearly different needs: two stateless front ends, a critical stateful database, an expendable cache, a worker with no inbound traffic and a scheduled nightly task. You have the target architecture, the map of which module contributes what, and the conventions —namespaces per environment, the labelling scheme, immutable image tags and a k8s/ structure versioned in Git— that we will apply without exception.
And, above all, you have already deployed your first component: web-store running in rutas-norte-dev, inspected with get, describe and logs, and visible in your browser through port-forward. You have also seen its weak spot first-hand: a loose pod that, once deleted, never comes back.
With this you close module 1. You know what Kubernetes is, how it is built, what every term means, you have a working cluster, you have command of kubectl and you understand the declarative model. Module 2, Core Kubernetes Components, starts exactly where this lesson ends: we will turn that fragile pod into a Deployment with several replicas that recovers on its own, we will first learn what exactly a Pod is inside and how a ReplicaSet keeps it alive, and we will give web-store and bookings-api a stable address with Services. The Rutas Norte platform really starts to take shape.
Kubernetes Course
Module 1: Introduction to Kubernetes
- What Is Kubernetes?
- Kubernetes Architecture
- Key Concepts and Terminology
- Setting Up a Kubernetes Cluster
- The Kubernetes CLI: kubectl
- Objects, YAML Manifests and the Declarative Model
- The Course Project: the Rutas Norte Platform
Module 2: Core Kubernetes Components
- Pods
- ReplicaSets
- Deployments
- Updates, Rollbacks and Deployment Strategies
- Services
- Namespaces
- Labels, Selectors and Annotations
Module 3: Configuration and Secret Management
- ConfigMaps
- Secrets
- Environment Variables
- Resource Quotas and Limits
- LimitRanges and Quality of Service (QoS) Classes
- ServiceAccounts and API Access from Pods
Module 4: Networking in Kubernetes
- Cluster Networking
- Service Types
- Internal DNS and Service Discovery
- Ingress Controllers
- TLS and Certificate Management with cert-manager
- Network Policies
Module 5: Storage in Kubernetes
- Volumes
- Persistent Volumes
- Persistent Volume Claims
- Storage Classes
- Dynamic Provisioning, Expansion and Snapshots
- Backup and Restore of Persistent Data
Module 6: Advanced Kubernetes Concepts
- StatefulSets
- DaemonSets
- Jobs and CronJobs
- Init Containers, Sidecars and Multi-Container Patterns
- Scheduling: Affinity, Taints and Tolerations
- Custom Resource Definitions (CRDs)
- Operators and the Controller Pattern
Module 7: Monitoring and Logging
- Health Checks and Probes
- Metrics Server and kubectl top
- Monitoring with Prometheus
- Visualization and Alerting with Grafana and Alertmanager
- Centralized Logging with Elasticsearch, Fluentd and Kibana (EFK)
- Application Debugging and Cluster Events
Module 8: Kubernetes Security
- Role-Based Access Control (RBAC)
- Security Contexts and Container Hardening
- Pod Security Policies and Pod Security Standards
- Network Security
- Image Security
- Auditing, Scanning and Vulnerability Management
Module 9: Scaling and Performance
- Horizontal Pod Autoscaling
- Vertical Pod Autoscaling
- Cluster Autoscaling
- Event-Driven and Custom-Metric Scaling with KEDA
- High Availability: PodDisruptionBudgets and Topology
- Performance Tuning
Module 10: Kubernetes Ecosystem and Tooling
- Minikube and Local Environments with kind
- Kubeadm
- Helm
- Kustomize
- GitOps with Argo CD and Flux
- Managed Kubernetes: EKS, AKS and GKE
Module 11: Case Studies and Real-World Applications
- Deploying a Web Application
- Running Stateful Applications
- CI/CD with Kubernetes
- Deployment Strategies: Blue-Green and Canary
- Multi-Cluster Management
- Production Operations: Incidents, Runbooks and Costs
