Docker and Kubernetes: Container Orchestration
Prerequisite Knowledge
This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.
Previously Covered in This Subject
- Cron Jobs — covered in Lecture 5 (Cron Jobs and Scheduling): the five-field cron syntax used here with Kubernetes is the same one introduced earlier with Jenkins.
18.1 Why We Need Container Orchestration
18.1.1 The Problem After You Build a Service
Hook: You wrote the service. Now what? Once you create any service — a microservice or anything else — you need to deploy it somewhere, and deployment brings a long list of follow-up jobs. Someone has to handle the security part, the networking part, the deployment itself, and the availability part (making sure the service is usable whenever a user needs it). Every one of those jobs is real work, and doing them by hand for every service you ship does not scale.
The tool that provides whatever you normally need for any deployment process is Kubernetes. With it you can deploy your service, apply security settings, configure networking, create multiple instances of the same service (which is what makes availability high — the service is usable whenever you need it), tunnel traffic into it, and load balance across instances. A lot of things you would otherwise build yourself come built in. In other words, Kubernetes is the operations team you do not have to hire: it automates the deployment chores that would otherwise consume a whole human team on every release.
Vocabulary correction — orchestration, not "sophistication." This part of the DevOps process is called orchestration — coordinating many running containers as a single system — and not "sophistication." The correct term is orchestration, because the job is organizing and coordinating the containers that hold your services: deciding which container runs where, when it starts, and what happens when it fails. Nothing about the task is about making containers fancy; it is about running many of them together as one coordinated system, the same way a conductor coordinates many musicians without playing any instrument itself.
18.1.2 The Session Roadmap
The plan for the session is a complete end-to-end walkthrough, done locally:
- Create a microservice on your own machine.
- Run it and verify it works.
- Containerize it with Docker — wrap the code and its dependencies into a Docker image.
- Put the Docker container into Kubernetes, using the local Kubernetes called Minikube.
Minikube is mainly for local purposes — it is the "localhost" of Kubernetes. Just as localhost stands in for a real server while you develop web applications, Minikube stands in for a real Kubernetes cluster: whatever you would test in the cloud, you can test locally in the same way. The machine you are using becomes the node (in the demo, a Mac is the node), local Docker holds the container, and that container then moves into the Kubernetes part.
18.1.3 What Kubernetes Gives You
Before any hands-on work, the session walks through a diagram of the whole system so the vocabulary lands first. Picture the diagram: at the top sits the end user — the person who will actually access the microservice, for example a customer using a banking system — and two arrows leave the user, showing the two ways that user reaches the service:
- Via the cloud, where the microservice is hosted on cloud infrastructure (servers owned by a provider);
- Via on-premises systems, where the microservice runs on physical servers inside the organization.
Either path reaches the same services. The user sits in the external domain; everything we manage lives in the internal domain, where Kubernetes has its own jargon: namespace, cluster, node, worker node, API server, controller, scheduler, etcd, kube-proxy, kubelet, container and volume. That list looks long but it is simple — each word maps to one concrete thing, and the next section goes through them one by one.
Recap: building a service is only the first step; Kubernetes automates the deployment, security, networking and availability jobs that follow, and orchestration (not "sophistication") is the name for coordinating the containers that run those services. Next: every word of the Kubernetes vocabulary, one at a time.
Real-world connection: Kubernetes is the standard tool behind nearly every modern deployment pipeline. Banks, e-commerce platforms and streaming services run their microservices on Kubernetes clusters in the cloud, and the same concepts shown locally on Minikube carry over directly to cloud deployments — a developer who masters the local workflow can operate production clusters with the same YAML files and commands. Whatever you test on your own laptop today is, at a small scale, exactly what runs behind a banking app tomorrow.
18.2 The Kubernetes Vocabulary: From Namespace Down to Pod
18.2.1 Namespaces and Clusters
Namespace — the project name. A namespace is just a name: the name of your project. When you create a banking application, you put the bank name as the namespace. It is like the project name or package name in your own code — the name that tells every tool which project a thing belongs to. Nothing more: the namespace is a temporary label given to the entire project. In the demo, the namespace is simply default, because every Kubernetes cluster needs a home for the things you create.
A cluster is the physical reality. Whatever we call "cloud," it is still a system sitting on hardware somewhere — when you type google.in or google.co.in (the Indian server), the request lands on a physical server in India. The cloud is, finally, a server. A cluster is the geographical location of that hardware: Indian servers, US servers, and so on. We deliberately distribute systems across continents — some in Asia, some in Europe, some in the Americas. Why? Disaster resilience: if a flood or some physical calamity hits one continent, the servers in other continents keep running, data stays ready, and users are not impacted by the physical disaster. That is the clustering concept.
| Namespace | Cluster | |
|---|---|---|
| What it is | A logical name — a label | Physical hardware, located somewhere |
| Question it answers | "Which project does this belong to?" | "Where do the servers actually live?" |
| Example | banking or default |
The server rack in India, in the US, in Europe |
| Analogy | The folder name in your code | The building that holds the computers |
Real-world: the "cloud" always boils down to physical machines somewhere, and geo-distributed clusters are how large providers survive regional outages — AWS regions, Azure regions and Google regions all follow exactly this pattern of spreading hardware across continents.
18.2.2 Nodes: Virtual Machines on Physical Hardware
Below the cluster sit nodes. A node is a virtual machine (VM) running inside the physical server. The physical server has all the hardware; on top of it you allocate a specific set of RAM, a specific set of processors, a specific set of resources to each virtualized system. If the physical machine has 2000 GB (2 TB) of RAM, you might allocate only 20 GB to one node. That kind of virtualized system is a node.
Worked example — carving a node out of a physical server. Suppose a physical server has 2000 GB (2 TB) of RAM in total. The admin creates a node and allocates it 20 GB. The maths is a plain subtraction: GB remain on the physical box, available to be split among other nodes — the hard disk, the CPU cores and the network cards get split the same way. A node of 20 GB RAM, carved out of a 2 TB physical machine, leaves 1980 GB for everyone else. Sense-check: a node's RAM can never exceed the physical RAM, and the leftover shrinks by exactly what each node takes — virtualization divides one physical box into several smaller, isolated boxes.
Every node is split into two roles: the master node and the worker node. This follows a master-servant philosophy covered earlier in the course: the master is the one that controls everything, and the worker is the node that actually performs the operations we need done. The master decides; the worker executes. In Minikube the same machine plays both roles, which is why a one-laptop demo can run an entire cluster.
18.2.3 Inside the Master Node: API Server, Scheduler, etcd and Controller
The master node contains several entities, all integrated with each other and all talking over APIs: the API server, the controller, the scheduler, and etcd.
API server — this is the network gateway. When a user tries to access something, the request arrives at the API server first. It handles the firewall, the load balancer, the APIs, and internal networking; it is the network-and-security part of the system. Whenever a request comes in, the API server tells the controller where to send it and what to look into.
Scheduler — this entity is for scheduling jobs, mainly batch jobs. If you want something to run at 12 o'clock at night or 2 o'clock at night, the scheduler handles it. In banking, batch processing is common — the lecture names a batch process (described as NIF processing) that runs once every four hours. That kind of batch processing is handled by the scheduler: the scheduler is the timekeeper of the cluster, deciding when a job starts and making sure it is not forgotten.
etcd — this is a register: a key-value pair store that keeps track of all the IPs and ports — where the load balancer is, which service is running on which IP, which IP is connected to which port. All of that information lives in etcd as key-value pairs: "this service is running in this IP; this IP is connected to this port." If the cluster is a city, etcd is the phone book: one glance and you know who lives where.
Controller — the entity that ties everything together. It takes information from the API server, schedules via the scheduler, stores all the information in etcd, and reads it back whenever it needs something. The controller coordinates the whole master node; it is the brain that decides what must happen and then checks that it actually happens.
| Component | Job in one line | Memory hook |
|---|---|---|
| API server | Network gateway: firewall, load balancer, APIs, internal networking | The front door and the guard |
| Scheduler | Decides when jobs run, especially batch jobs (e.g. NIF processing every 4 hours) | The clock |
| etcd | Key-value store of every IP and port in the cluster | The phone book / register |
| Controller | Coordinates everything; reads from and writes to etcd | The brain |
18.2.4 Kubelet and Kube-Proxy: The Smaller Twins
Two more names do the same jobs at worker scale. Kubelet is a smaller controller compared to the parent controller: the master node has the controller, the worker node has a kubelet. Both are similar and do similar work — both control. Likewise, kube-proxy (KProxy) and the API server are nearly similar: what the API server does in a bigger way, kube-proxy does in a smaller way. Kube-proxy is mainly for the networking part — it tracks which service is running on what IP. That information passes to the controller, which passes it back to the API server, and it gets stored in etcd.
The pattern is worth seeing: at master scale you find the controller and the API server; at worker scale you find the kubelet and the kube-proxy. Each worker-side twin is the small version of one master-side component, living on the worker node and reporting upward.
18.2.5 Pods, Containers and Volumes
Coming down to the worker node: what does a worker actually run? The container — the Docker container you create, your code — plus a small database called a volume. These two are packaged together into a pod.
The professor's analogy — the bean pod. The name comes from an everyday image: a bean pod. Open a bean pod and inside there are multiple beans, each with its own packaging; the same way, a pod is a self-contained package that can run in parallel with others. Every pod has its own packaging, and one node can hold multiple pods. That is all a pod is: your code and your database, wrapped together and ready to run side by side with other pods.
While a pod is running, it sends updates to the kubelet — whether it is running or not, whether it has a problem, whether it hung, whether it needs more memory. The kubelet passes everything to the controller. Based on that, the controller speaks to the node, and the node can get more memory from the cluster if needed — it can extend the memory allocated to pods. The full integration loop is:
with kube-proxy feeding networking information in the other direction toward etcd. Every health signal from a pod travels up this chain, and every decision about resources travels back down it.
18.2.6 Pools and Multiple Master Nodes
Worker nodes can be grouped into pools — a pool is a group of entities, a group of nodes running in parallel. You can create multiple nodes inside a single pool, or one single node in its own pool; both work. Similarly, you can keep a single master node across a cluster, or multiple master nodes across multiple clusters. The recommended setup is one individual master node for each individual cluster; only when resources are tight do you share a common master.
The summary picture: a microservice is bundled into a container, the container is bundled into Kubernetes, and alongside them a job (a cron job) runs. Inside a namespace there is a cluster — the physical memory — and inside the cluster a node, which is the virtual memory where you allocate fewer resources than the physical box has. The control command-line tool, kubectl — the "kube controller" — is the controller for the entire node, connected to the kubelet.
Exam note: no exam-specific guidance — marks, question patterns, or topic distributions — was given in this session. The essential takeaway is the vocabulary: namespaces, clusters, nodes, pods and the master node components. An exam question on Kubernetes starts here.
The takeaway, put simply: if you understand what the namespace is, what the cluster is, what master and worker nodes are, what the API server, controller, scheduler, etcd, pod, container, volume, kube-proxy and kubelet each do, then Kubernetes is nothing — very, very easy.
Recap: the whole vocabulary is a stack of containers. Namespace = project name; cluster = physical hardware; nodes = virtual machines with master and worker roles; pods = your container plus a volume, like beans in a bean pod. The kubelet and kube-proxy are the worker-side twins of the controller and API server, and etcd remembers every IP and port. With the words in place, the next step is to build something real to deploy.
No questions came after this walkthrough, so the session moved directly to the practical part: from words to a working service.
18.3 The Microservice We Deploy
18.3.1 The Service Code
The demo project contains three microservices: authentication, profile, and transaction. The session focuses on the authentication one. A microservice (a small, single-purpose service that does one job — from the microservice architecture covered earlier in the course) here is just a single JavaScript file built with Node.js, using Express.js mainly for the UI part, opened in Visual Studio Code alongside the Dockerfile and the Kubernetes YAML files. The service listens on port 2300 on the localhost IP — the IP and the port are the two places where the service runs. Whenever somebody calls localhost:2300/ (that is, localhost, colon, 2300, slash), the service sends back the response string "Docker authentication service". In the console it logs something like "authentication service up and running" with the IP, the port, and the version number of the file.
The service is deliberately tiny: the point of this session is Docker and Kubernetes, not microservices, so nothing clever happens in the code. The less code there is, the easier every later step is to check — a one-file service means a one-container story.
18.3.2 Running and Verifying Locally
Worked example — running the authentication service and verifying the response.
- Start the service. Run
node authentication.jsin the project folder. - Read the console. The log prints "authentication service up and running" on http://0.0.0.0:2300, plus the version number of the file. The address 0.0.0.0 means the service listens on every network interface of the machine, not only one address.
- Verify in the browser. Open http://localhost:2300/ — the service answers with the response string "Docker authentication service".
- Result: the local microservice runs without any problem. The response proves the code path works end to end: request lands on port 2300, the service handles it, the text comes back.
Sense-check: a service that answers on its port with the expected text is a known-good baseline — every later step (the Docker image, the Kubernetes deployment) will be checked against this same response.
This first verification matters because it separates "the code works" from "the container works": from here on, every deployment step is checked against a known-good service. If something fails after Docker enters the picture, the fault is in the packaging or the deployment — not in the code, because the code already proved itself.
18.4 Docker: Packaging the Service Into an Image
18.4.1 Why Bundle Into an Image
Running Node.js needs dependencies — the libraries and packages the code imports. If you push plain code around, every machine that runs it must run npm install and download all the packages again. Instead, Docker bundles everything — code plus dependencies — into a single independent executable: the Docker image. Think of an executable like an .exe, or a zip file with everything already inside. With an image, you never need to run npm install again or download packages at each new environment; the dependencies are already present.
This is the "write once, run anywhere" idea: because the image carries its own runtime and libraries, the same image runs on a laptop, a test server, and a cloud VM without re-installing anything. The environment no longer differs between machines, because every machine runs the exact same bundle.
Image — the single independent executable. An image is the full package of your code plus every dependency plus the runtime (here, Node.js 16 on Linux), frozen into one artifact. When you build an image you capture the environment itself, not just the code; when you run it, every machine runs an identical environment.
18.4.2 The Dockerfile
The image is built from a Dockerfile, a plain text recipe. The demo Dockerfile walks through these steps:
- Use Node version 16 as the base —
node:16is a Linux-based image. - Set the working directory to
/usr/src/app(Linux convention). - Copy
package.jsoninto it — this file contains all the dependencies. - Run
npm install— this opens package.json, takes all the dependencies, and installs them. - Copy everything else into the folder.
- Expose port 2300.
- Start the entry file (
authentication.js) — when the container comes up, it executes this automatically and the service starts listening on 2300.
While the build runs, the command line prints each step: it picks up node 16, takes the working directory, copies the package file, runs npm install, copies the information, and renames the result to the name given in the build command. Each line of the Dockerfile is one layer of the image — that is why the build log shows the steps one after another.
18.4.3 Building the Image
The build command is docker build with a name and a version number, and the dot at the end standing for the current directory:
docker build -t authentication-service:v15 .
Worked example — building the image with version tag v15.
- Run the build. From the project folder:
docker build -t authentication-service:v15 .— the-tflag gives the image its name (authentication-service) and its version tag (v15); the trailing dot tells Docker to use the current directory as the build context. - Watch the build log. Docker pulls the
node:16base, sets/usr/src/appas the working directory, copiespackage.json, runsnpm install, copies the rest of the project, exposes port 2300, and sets the start command toauthentication.js. - Verify with the CLI.
docker imageslists the images on the machine — v15 is the one just created, and v1 already existed from an earlier build. - Verify with the UI. Docker Desktop shows the same two images side by side.
Result: the image authentication-service:v15 is built, listed, and ready to run. Sense-check: an image that appears in docker images under its exact name and tag is a finished artifact — the packaging step worked.
Pitfall — the invisible :latest tag. Giving the version number matters. If you do not give one, Docker automatically tags the image with :latest. That is a habit to avoid: it becomes a problem later, when you want to create the next version of the same service and cannot manage the versions properly — every build silently overwrites the same latest, and you can no longer tell which version is actually deployed. Always name the version yourself.
18.4.4 Running the Container with a Port Tunnel
Two ways to run the container: press Run in the UI, or use the command line. The command line is better because you can give names, change label names, and run on a different port — every parameter can be changed:
docker run --name dock_authentication -p 35350:2300 authentication-service:v15
The container gets the name dock_authentication, and the port flag creates a tunnel from external port 35350 to internal port 2300:
Why tunnel at all, when the service already listens on 2300? Security. The service runs on 2300 inside, but people on the outside reach it through 35350 — this is the internal port and this is the external port. Nobody outside knows the service is running on 2300. If a hacker tries to hit 35350, they still cannot reach the service running on 2300; the real port stays hidden. The tunnel carries traffic from external 35350 into internal 2300, and only that direction: the outside world sees a door, never the room behind it.
Worked example — running the container with a name and a port tunnel.
- Run:
docker run --name dock_authentication -p 35350:2300 authentication-service:v15. - Watch it start. The container starts (14 seconds ago in the demo), gets the given name
dock_authentication, and shows the same console.log output inside — "authentication service up and running" — because the image runs the same code as the local service. - Inspect the ports.
docker inspect dock_authenticationreveals the container is listening on TCP 2300 inside, while traffic from outside is routed through 35350. The entire project was copied into the image, so the container has everything it needs.
Result: the container runs under its own name, listening on internal port 2300, reachable through external port 35350. Sense-check: the container behaves exactly like the local service from 18.3, proving the image preserved the known-good behavior.
18.4.5 Verifying, Logging and Cleaning Up Containers
Verification follows the port story: localhost:2300 shows nothing (the service is not running there), while localhost:35350 responds — the container works, internally on 2300 and externally on 35350.
Managing containers:
docker ps— "docker process" — lists all running Docker processes.docker logs <container-id>— shows the logs of one specific container by its ID (the same logs the UI shows).docker kill <container-id>— kills the container directly using the process ID (the demo deliberately did not kill it, since it was still needed for the Kubernetes step).curl <url>— when you work on servers that only have black screens and no UI, this is how you check output: curl the URL and everything shows in the terminal — the response and the history of the service.
18.4.6 Student Questions and Answers
Q: In the build command there is a dot at the end — what does it mean?
A: The dot is the present working directory. Whatever is present in the current directory gets pulled and built; that is what the build command is being told to do. If you want, you can give the entire path instead — the dot just saves you typing it. For now, Docker and the Dockerfile are the only two prerequisites for everything done so far.
18.5 Kubernetes in Practice: Minikube, YAML and kubectl
18.5.1 Starting Minikube
The Kubernetes tool used locally is Minikube. Running minikube start boots it: it starts on the machine's chipset, pulls the Minikube image, creates a Docker container for Kubernetes, and enables all the Kubernetes services — including the dashboard — step by step. When it is ready, kubectl becomes available. kubectl is short for "kube controller" — the controller we will use for all running operations from now on. Where the dashboard is the visual face of the cluster, kubectl is the command-line face: the same operations work through either one.
Two things are up as soon as Minikube starts: the cluster and the namespace. The cluster is named minikube — the physical machine itself gets that name, so "my Mac is called minikube now" — and the namespace, the project name, is default. minikube service --all shows Kubernetes running internally on an IP and port. Logs show everything happening inside Minikube.
18.5.2 The Service YAML
For Docker we had the Dockerfile; for Kubernetes we have YAML files. YAML is a kind of XML file — the configuration format was made into YAML — and it contains small instructions: each file declares, in plain text, what Kubernetes should create. The service YAML has the parts that will repeat in every file from now on: an apiVersion, a kind, metadata, a name, a spec, a selector, an app and a port.
- apiVersion: v1 in the demo — the number is your own choice; the demo even used a v15-style label because the image was v15.
- kind: Service — the service runs in the background (backend).
- metadata: the name of the service; Kubernetes will know this name as your service.
- spec: which Docker service it should run. Point it at the image already in Docker —
authentication-service:v15— and tell it to pick that up and run it as a Kubernetes service. - ports: external 35350, internal (target) 2300.
On top of that, Kubernetes creates its own wrapper port, so there are three ports total: when you hit the Kubernetes port, the API server hits the Docker port, which hits the original application port. The request path from the Kubernetes service port through 35350 to 2300 is:
The file also requests a load balancer, for the reason of keeping multiple instances of the service available — the load balancer spreads incoming traffic across whatever instances exist.
18.5.3 The Deployment YAML
The service describes the door; the deployment says where the container physically goes. The deployment YAML uses apps/v1 as the apiVersion and kind: Deployment. It has metadata with a name — you can reuse the service name or add something like a deployment- prefix; the label part is mainly for your own understanding. But the next part is very important: the selector with the app label, which is what tells Kubernetes which Docker service to pick up.
Below that comes the template — again the same structure — and inside it the container part: which container to deploy. The container already exists in Docker; the deployment gives the image name (v15), states it runs on 2300, and creates a container from it. So read the file from bottom to top: this is the container → the container gets deployed → and then it runs as a service. Three steps.
The demo also sets resources for the deployment instance:
- limits: half a CPU and 128 MB of memory;
- requests: maximum half a CPU and a maximum request of 64 MB.
The limits say "never let this instance exceed this budget"; the requests say "reserve at least this much for it". Both protect the machine the cluster runs on.
18.5.4 Applying the Files with kubectl
Applying means creating the Kubernetes objects: kubectl apply -f <path> — the -f flag means file or folder. The demo has a folder (kube) holding all the YAML files, so the apply command points at the folder:
kubectl apply -f kube
On apply, everything defined in the folder gets created at once — service, deployment, pod, namespace, cron job, service account, cluster role, role binding, persistent volume, horizontal pod autoscaler. kubectl get service then shows the authentication service running on port 51458. The full route is:
the kubectl service port → the Docker port → the application port. Opening the browser at that service address confirms it works.
Worked example — applying the YAML folder and verifying the service on port 51458.
- Apply the folder:
kubectl apply -f kube— every YAML file inside is read and every object it defines is created in one go. - List the services:
kubectl get serviceshows the authentication service, and Kubernetes has assigned it the service port 51458. - Trace the path: a request at the service port travels — the Kubernetes service port, then the Docker port, then the application port where the container listens.
- Verify in the browser: open the service address and the "Docker authentication service" response appears — the same known-good response from 18.3, now served through two extra hops.
Result: the service is reachable at port 51458, exactly as defined in the YAML. Sense-check: the response text is identical to the local run, so the whole pipeline — code → image → container → Kubernetes service — preserved the behavior end to end.
18.5.5 The Kubernetes Dashboard
minikube dashboard opens the visual UI. The dashboard is the overall report of the cluster: the cron job is running, a deployment is running, a job is running, a pod is running, and a replica set exists — the replica set is the availability story, multiple available replica sets keeping the service up. The list of applied objects shows everything that came from the kubectl apply: deployment, service, pod, namespace, cron job, service account, cluster role, role binding, persistent volume, horizontal pod autoscaler. Picture the dashboard as a cockpit: one screen shows every object the cluster owns and the live state of each.
18.5.6 Student Questions and Answers: How Much CPU and Memory?
Q: How do I identify how much CPU and memory to allocate — how do I decide the limits?
A: You know your own machine: this Mac has an 8-core processor and 16 GB of RAM, so the limits were set from what the hardware can actually give. Similarly you will know your own server's limitations when you procure it, and you allocate based on that. There is no hard-and-fast rule that says service X must get Y — it is trial and error. When you do not specify limits, the container starts with a default CPU set that the dashboard shows; keep that default as the minimum limit, set it in the file, and adjust from what you observe in actual usage. Limits are also not mandatory: giving them just makes life easier. If you have little space and some unnecessary services are running, containers eat a lot of space and you pay more. On a full-fledged cloud you may not need to limit at all; without limits a container takes however much it can, and on a laptop that crashes the system — which is why the demo limits resources on the Mac.
18.5.7 Student Questions and Answers: Why the Final Port Is Random
Q: Why is the final port number random instead of fixed?
A: A fixed port invites hacking attempts — an attacker can run a batch process that keeps hitting that specific port. The safe practice is to leave the choice to Kubernetes: it generates a random port every time, your application binds to that port automatically, and external traffic cannot predict where to go in. The random port lives between the API server and the kube-proxy.
18.6 Cron Jobs: Scheduled Work in Kubernetes
18.6.1 The Cron Job YAML
A cron job runs scheduled work on a schedule. The YAML again has an apiVersion, kind: CronJob, and metadata with a name (cron in the demo) and the namespace (default). The important part is the spec:
- schedule:
* * * * *— five stars mean every minute, once per minute. This is the same cron syntax introduced earlier with Jenkins: the five fields are minute, hour, day of month, month, and day of week, and a star in a field means "any". - job template → container: the container is named cron and uses the image
busybox. BusyBox is a Linux process package — a kind of package that ships with Linux, used just for processing — which is why it is the image of choice here: it is tiny and contains the few standard commands a scheduled job needs. If busybox is not available locally, the pull policy tells Kubernetes to pull it. - command: run a shell and give it parameters: print the date and print the present working directory (
pwd). - restartPolicy: OnFailure — if the job fails, restart it. If you kill the cron job, it will automatically start again.
18.6.2 Watching the Cron Job in the Dashboard
In the dashboard the cron job appears under its given name with the busybox 1.28 image and the schedule displayed. It is not suspended — it is still running; the last run was 23 seconds ago, and since it schedules every minute it will start again after one minute. From the UI you can trigger it now, edit the YAML directly, or delete it. The manually started run ("cron-manual") shows as running from 40 seconds ago, with the details a student needs to read: which namespace it runs in, the job ID, which controller it is connected to, and the job name.
18.6.3 Logs and the Job Lifecycle
Status is a live signal: sometimes the job shows complete, sometimes pending, sometimes failed — the dashboard displays whatever the current state is (complete shows as true here). The cron job uses exactly one pod — it does not need more than one. The pod name is listed, and the pod carries two labels because of high availability: one replica is active, one is passive; if the active one goes down, the other automatically takes over. The pod runs on the minikube node.
Inside the pod details, the lifecycle is explicit: initialized — true; ready — no, because the job already executed and completed; container ready — no, because it already completed and the pod got destroyed; scheduling — done, and next minute it runs again. There is no active pod because the cron job runs every minute and destroys the pod each time — that is expected behavior, not an error.
Worked example — a cron job that runs every minute and prints the date and the working directory.
- Define the job. The YAML declares
kind: CronJob, schedule* * * * *(every minute), imagebusybox, commandsh -c date; pwd, andrestartPolicy: OnFailure. - Watch it fire. The dashboard shows the cron job under its name with the busybox 1.28 image and the schedule; the last run was 23 seconds ago, and the next starts after one minute.
- Trigger it manually. A manual run ("cron-manual") starts immediately; its details list the namespace (default), the job ID, the controller, and the job name.
- Read the logs. The log prints the date — a manual trigger shows a single date; the automated schedule stacks up one date per minute. The present working directory does not print: the busybox container cannot read it internally, whereas on a local machine it would show.
- Follow the lifecycle. Each run: initialized true → scheduled → runs → completes → pod destroyed. Ready shows no, because the pod already completed and got destroyed — expected for a job that runs and finishes every minute.
Result: one scheduled job, one date per minute in the logs, no pod left behind. Sense-check: a cron job that completes and destroys its pod every minute matches the schedule exactly — if pods accumulated, the schedule would be broken.
The logs tell the same story: the date prints, but the present working directory does not — the busybox container cannot read it internally, whereas on a local machine it would show. A manual trigger runs once, so the log shows a single date; an automated schedule shows one date per minute, stacked up.
18.7 Reading the Cluster from the Command Line
18.7.1 Pods and Their Four Statuses
kubectl get pods lists every pod with its state, and the demo cluster conveniently shows all four states at once:
Worked example — listing pods and reading the four statuses.
- Run:
kubectl get pods. - Read the states — the demo cluster happens to show all four at once:
| Status | Pod in the demo | What it means |
|---|---|---|
| Running | counter pod, 3 out of 3 | The pod is up and all its containers are running |
| Failed | CPU demo pod, in crash loop backoff | It needs one pod but zero are running — some problem is present; Kubernetes keeps retrying with a backoff |
| Completed | cron-manual | Not an error: the job finished its work; one pod is still pending but everything completed, so no pod stays allocated |
| Pending | test task (deliberately left pending) | The pod is waiting for a node, resources, or an event |
- Check the backend: the backend service is running one out of one.
Result: reading a pod list comes down to four statuses — running, completed, failed, and pending. Sense-check: each status answers one question — "is it up?", "did it finish?", "did it break?", "is it still waiting?" — and a healthy cluster shows mostly Running and Completed.
So when you look at pods, you will see exactly four statuses: running, completed, failed, and pending.
18.7.2 Nodes and Services
kubectl get nodes shows one node, named minikube, ready, acting as the control plane — the control plane is the master node, the one controlling all these things. It has been up for 58 minutes, with the Minikube version number attached. Kubernetes containers themselves do not show here — they are part of Docker, so the node list shows the machine, not the containers inside it.
kubectl get services lists the running services: the authentication service is running as a LoadBalancer, and Kubernetes itself runs as a ClusterIP. Those are the two service types in play. A LoadBalancer exposes the service to the outside world and balances traffic across instances; a ClusterIP is an internal address only reachable inside the cluster — which is exactly how Kubernetes exposes its own components.
18.7.3 Deeper Inspection: Describe, Wide Output and Cluster Dumps
When there is no dashboard, a single command replaces it: kubectl cluster-info with a dump flag takes a dump of the entire cluster — all the logs, whatever is running, the cron job that ran (you see it gathering information every minute), all the policies. A system engineer can grab the whole picture from one command.
For one pod, kubectl describe pod <name> gives the status, how many times it restarted, and how many minutes it took. Adding the -o wide flag to the get command gives even more: the pod's IP and which node it is running on. For the node itself, kubectl describe node minikube shows the node's allocatable limits, whether it has enough memory, and confirms there is no disk pressure — the health of the node in one screen. Raw logs are also available if needed.
| Command | What it gives you |
|---|---|
kubectl get pods |
Every pod and its status (running, completed, failed, pending) |
kubectl get nodes |
Every node, its role, and whether it is ready |
kubectl get services |
Every service and its type (LoadBalancer, ClusterIP) |
kubectl describe pod <name> |
Status, restart count, run duration of one pod |
kubectl get pods -o wide |
Pod IPs and the node each pod runs on |
kubectl describe node <name> |
Allocatable limits, memory health, disk pressure |
kubectl cluster-info --dump |
A full dump of the cluster: logs, running objects, policies |
18.7.4 Data Persistence Across Pod Restarts
Q: What happens to the data when a pod goes down — is it lost?
A: Pods do not store data, so you should never run just one pod — create replica pods, because if one pod goes down another automatically takes over. Kubernetes also has its own mechanism: when a pod goes down, the information that was in it is persisted in another location, and when the pod comes back up Kubernetes hands the data back. You can test it: crash the pod, start it again, and the data will still be there — Kubernetes keeps it in internal storage, the way a database like Oracle DB does. Backup and restore are automatic; you only lose data if the physical server itself goes down.
18.8 How Docker and Kubernetes Work Together
18.8.1 Versioning Images and the Zip-File Independence
Suppose you create a Docker image and later change your code. The question is whether you upgrade the existing image version or create a new one. The answer: go for the next version — never upgrade the existing one in place.
The reasoning is a simple picture. The service V1 runs, and it was containerized into an image. That image carries all its dependencies inside itself, so it has no connectivity to the source code anymore. If you now update the source — service V1 only — the change touches only the source side; the already-built image is not impacted, because its copy was already handed to Kubernetes. Only when you build version 2 and tell Kubernetes you updated something does the deployed service move to V2.
Q: When I update my code and rebuild the Docker image, should I update the existing image version?
A: No — create the next version instead of overwriting. Once a service has been built, versioned, and pushed to Kubernetes, it is totally independent, like a zip file. It is exactly like a normal folder: you compress the folder into a zip and that zip is your Kubernetes service; add a new file to the folder afterwards and the zip does not change — nothing you do to the folder updates the zip. Build version 2 and tell Kubernetes about it, and only then does the deployed service move to V2. And if you overwrite a version in place, it would not even impact Kubernetes — but the clean practice is always a new version number.
18.8.2 Does Kubernetes Still Need Docker?
Q: Does the Docker engine play any role after deployment?
A: Yes — without the Docker engine, Kubernetes cannot run. Docker is needed for containerization and Kubernetes for orchestration; the two sit in parallel with each other, peers to one another. Docker is the tool that converts your microservice into a running container, and Kubernetes orchestrates those containers. In the Kubernetes file we point at the image built with Docker and tell Kubernetes to take it and run it as a service and a deployment. No Docker container, nothing to deploy.
The division of labour is the whole story of this session: Docker makes the containers, Kubernetes coordinates them. Remove either one and the system collapses — without Kubernetes the containers run unmanaged, and without Docker there are no containers to manage.
18.8.3 What "Running Container" Means
Q: What does "running container" mean — can you elaborate?
A: Here is the picture. Docker has the image v15, and that image is running as a container that listens on 2300 inside and 35350 outside. In the Kubernetes file you saw exactly that: the file says the Docker service running as part of the container should be taken and converted into a service. The deployment does exactly that — the container that Docker runs becomes the unit Kubernetes schedules. Without that Docker container, there would be nothing to turn into a service.
So "running container" is not a vague phrase: it is the concrete state in which an image is executing, with ports bound (2300 inside, 35350 outside), logs flowing, and a name on it — precisely the state the Kubernetes deployment picks up and schedules.
18.8.4 Stopping Docker After Deployment
Q: If I stop the Docker service, will the Kubernetes deployment stop too?
A: No — after the deployment completes, the two have no connectivity. The demo stopped the dock_authentication container in Docker, and the deployment in the dashboard kept running. Kubernetes keeps its own internal memory of the container and does not go back to Docker for information. The connection matters once, when the image is taken from Docker; from that point on Kubernetes is on its own.
Worked example — stopping the Docker container to show the deployed service keeps running independently.
- Note the deployment state: the authentication service is up in the Kubernetes dashboard.
- Stop the Docker container: the demo stops the
dock_authenticationcontainer in Docker. - Re-check the deployment: the deployment in the dashboard keeps running — Kubernetes does not go back to Docker for information.
Result: the Kubernetes deployment is independent of the Docker container it once came from. Sense-check: the deployed image is like a zip file handed over once — after the handover, the state of the sender no longer matters.
18.8.5 How the Two Communicate: The Docker Daemon and Kubernetes-in-Docker
The direct connection between Docker and Kubernetes works through the Docker daemon: Kubernetes interacts directly with it, once, based on the Linux processes involved. The proof is visible in Docker Desktop itself — the Kubernetes services appear inside Docker: the Kubernetes etcd service, the Kubernetes scheduler, the Kubernetes controller, core DNS, kube-proxy, and storage provisioning all run as part of your Docker installation. The entire Kubernetes control plane runs inside Docker. Docker ships a Kubernetes extension, and with it enabled the two are directly connected, which is why no extra wiring is needed. If you ran Kubernetes on one machine and Docker on another (different versions, different hosts), you would connect them by giving the IP and port of one to the other.
Q: How do Docker and Kubernetes communicate with each other?
A: Kubernetes talks directly to the Docker daemon and runs inside Docker itself. Open Docker Desktop with the Kubernetes extension enabled and you see the whole control plane — the etcd service, the scheduler, the controller, core DNS, kube-proxy, storage provisioning — running as containers in your Docker installation. That is why no extra wiring is needed between the two tools on one machine.
18.8.6 Registries and Managed Services: Docker Hub and Amazon EKS
Docker Hub is Docker's remote repository — a store of images living on a remote server at hub.docker.com. An image in Docker Hub is referenced directly in the Kubernetes YAML by its image name.
Worked example — pulling the CPU demo image from Docker Hub by naming it in the Kubernetes YAML.
- Name the image in the YAML: write the CPU demo image name in the Kubernetes file — Kubernetes looks it up in a registry by that name.
- Apply the file:
kubectl applyon the YAML. - Watch the pull: the pod shows a "pulling image" state while Kubernetes fetches it from Docker Hub — the image travels over the network from the registry into the cluster.
Result: Kubernetes pulls the image from Docker Hub, no local Docker build involved. Sense-check: the image name in the YAML is the address Kubernetes uses — name it, apply it, and the pull state confirms the registry is doing the serving.
This is the registry — that is the exact word for the store. Two consequences follow. You do not need Docker itself running to deploy an image from a registry — you only need the container image existing somewhere reachable. And a registry is how real teams share images: build once, push to the registry, deploy from there.
Q: Where does Kubernetes get the image when Docker is not running?
A: From a registry like Docker Hub, which Kubernetes pulls the image from. The image exists somewhere reachable — a remote server at hub.docker.com — and Kubernetes fetches it by the name written in the YAML. The local Docker engine is not needed for that step.
Q: How is something like Amazon EKS different from running Kubernetes myself?
A: EKS takes Kubernetes and gives it to you as a managed service — there is no big difference in how you work with it. Instead of hosting your own Kubernetes service, you use Amazon for that service, and the same YAML files and commands apply.
18.9 Scaling: Replicas, Autoscaling and Load
18.9.1 Why Docker Cannot Scale
The basic difference between a Docker container and Kubernetes is scaling. Docker lets you create containers, but each image is heavy — the demo's images run to about 867 MB — and every container eats a lot of RAM and hard disk. Two limitations surface when you try to run the same image multiple times. First, you cannot give the same label twice: every time you need a different label. Second — the demo tried to start the same container a second time and got the refusal "container is running — you cannot create the same container." Docker cannot create multiple containers from the same logic.
Scope — what Docker can and cannot do. Docker is excellent at making one container: build an image, run it, tunnel ports, stop it. It is not a scaling tool: the image is heavy (867 MB in the demo), every copy needs its own label, and Docker refuses to start the same container twice. Scaling is not possible in Docker, and that is exactly why Kubernetes exists — with its auto-scaling options, Kubernetes spawns copies freely.
18.9.2 The Horizontal Pod Autoscaler YAML
Scaling can be done three ways: via the command line, via the YAML file, or via the dashboard UI. The YAML route is the horizontal pod autoscaler, which sets:
- minReplicas: 1 — keep at least one replica;
- maxReplicas: 10 — the ceiling; when many tasks hit the single replica and it gets heavy, Kubernetes automatically starts spawning more replicas, and it can go up to 10 but never beyond;
- targetCPUUtilizationPercentage: 50 — use at most 50% of the CPU.
The CPU numbers appear in two equivalent forms:
half a CPU, written either as the milli-unit form 500m (the kind of presentation used for RAM-style resources) or as the decimal 0.5. Both are the same value — the "m" simply means milli, so 500m means 500 thousandths of a CPU.
The contrast with no limits: previously, if you did not give the replica rule and the pod got very heavy, it crashed. With replicas, the pod does not crash — Kubernetes creates one more pod and hands it part of the load; the load gets balanced and scaling happens. If the CPU crosses the target, pods get closed automatically.
18.9.3 Scaling from the Dashboard and the Command Line
In the dashboard, a deployment shows desired replicas versus actual replicas, and a scale control increments them. The same thing runs on the command line, in the default namespace (default is nothing but the namespace name), targeting the replica set of the specific service:
kubectl scale deployment authentication-service --replicas=8
Worked example — scaling replicas from the dashboard and the command line.
- Scale from the command line:
kubectl scale deployment authentication-service --replicas=8in the default namespace — default is nothing but the namespace name, and the target is the replica set of the authentication service. - Watch the statuses roll: with
kubectl get pods -w(watch mode) the show is live — after incrementing the desired count, each newly created pod is a different pod, and the statuses roll through pending → running → terminating. The list never shows something like "pod 1 of 12", because each spawned pod is its own distinct pod. - Scale from the dashboard: the deployment view shows desired versus actual replicas and a scale control that increments them — the same operation as the command, through the UI.
- Push the limit: the demo scaled the deployment up to 100 to watch the behavior — dozens of distinct pods spawned, each with its own status transitions. If you push beyond the CPU limit, the autoscaler stops and replicas start crashing.
Result: replicas climbed to eight step by step in watch mode, and scaling up to 100 spawned dozens of distinct pods. Sense-check: every new pod passes through pending → running → terminating, so the count of "desired" always leads the count of "running" during a scale-up.
18.9.4 What Happens When Replicas Are Not Needed
Replicas shut down when there is no load. The default load balancing strategy is one active, one passive; when no heavy traffic justifies extra pods, Kubernetes ends them and kubectl get pods shows only one — the replica set is reducing. Terminating is a normal status on its own; you see it in watch mode, not in the plain listing.
Replicas do not run on the same port. They share the IP, but each has a different internal port; the port is internal to Kubernetes and not shown outside, which is why only the IP is visible. In the demo, everything terminated quickly because there was no heavy load for that much service — then the class scaled to 100 to watch the behavior: dozens of distinct pods spawned, each with its own status transitions, and when the load never came, Kubernetes terminated all of them and kept one.
This is the deeper point: Kubernetes takes control of whether replicas are needed. It waits — no request came in, so it terminated the extras and reduced memory usage. It controls the OS, the network, everything. With many pods spawned, RAM went up, so it automatically killed everything except one service; if the load is needed it keeps the pods, otherwise it removes them.
18.9.5 Scaling by Editing the YAML File
The YAML path: delete the existing deployment and service (kubectl delete on each), which sends the pod into terminating automatically, then edit the deployment YAML — the demo changed replicas from 1 to 10 — and kubectl apply again. The dashboard then shows ten replicas, exactly as requested.
Worked example — editing the deployment YAML to ten replicas and applying it again.
- Delete the existing objects:
kubectl delete deployment authentication-serviceandkubectl delete service <name>— the pod goes into terminating automatically. - Edit the YAML: change
replicasfrom 1 to 10 in the deployment file. - Apply again:
kubectl apply -f kuberecreates the deployment with the new replica count.
Result: the dashboard shows ten replicas, exactly as requested. Sense-check: the desired state in the YAML (10 replicas) becomes the actual state — Kubernetes converges the cluster to what the file declares.
Publishing images follows the same discipline: build, then docker push <image-name>, which pushes the built image to Docker Hub — with a login required, and (in the demo's free tier) a limit of four images in the repository. Other lifecycle commands exist too: rollout and undo for deployment history, and kubectl get autoscale shows whether the horizontal autoscaler exists — in the demo it already existed but was not spawning, for the same reason as before: no load. If you set minReplicas to 10, it will spawn ten.
18.9.6 Student Questions and Answers
Q: In a real project, when does the development team deploy?
A: The real-world flow is exactly what the demo did: the development team first creates the Docker images, uploads them onto the registry, and then deploys from there. Creating images, pushing to a registry, then deploying is the correct, standard way.
Q: Do I need internet access to run all of this locally?
A: No. Minikube runs completely locally — you do not even need Wi-Fi. Internet is only needed when your container lives in a remote registry and Kubernetes has to pull it, which is why downloading an image like busybox needs the network. For a locally built image, offline works fine: for testing, these two tools and nothing else are enough.
Exam note: this session stressed YAML files and kubectl commands as the core knowledge for Kubernetes. The three scaling routes (command line, YAML, dashboard) are the same operation — change the desired replica count and watch Kubernetes converge to it.
18.10 Adding Worker Nodes
18.10.1 Adding a Node to a Running Cluster
The diagram showed multiple nodes, but the running cluster has only one master node controlling everything; Minikube itself handles both the master and the worker roles. To get a dedicated worker node, use minikube node add — note the command difference: minikube start is only for first-time startup; when a cluster is already running, you add a node rather than start one. The demo created worker node minikube-m02 inside the cluster minikube. You can create multiple nodes either at start time (declaring how many nodes you want) or at runtime with add — both work.
Worked example — adding a worker node to the running cluster and verifying both planes.
- Add the node:
minikube node addcreates worker nodeminikube-m02inside the running clusterminikube— adding, not starting, because the cluster is already up. - Verify the control plane: the master node reports: host running, kubelet with no issue, API server running, kube configuration working properly.
- Verify the worker plane: the new worker node reports host running and kubelet running.
- Confirm in the dashboard: the node view now shows two nodes —
minikubeandminikube-m02.
Result: a second node joins the cluster, and both the control plane and the worker plane report healthy. Sense-check: a node that reports its kubelet running is ready to receive pods — the kubelet is the worker-side component that actually runs them.
18.10.2 Verifying the New Node
Checking status shows the full picture. The control plane (the master node) reports: host running, kubelet with no issue, API server running, kube configuration working properly. The worker node reports host running and kubelet running. Both planes — the control plane and the worker plane — are up.
18.10.3 Scheduling Pods Across Nodes
The dashboard's node view now shows two nodes: minikube and minikube-m02. When many processes are spawned, the new node gets used — pods that were sitting pending get allocated to a node, and the workload spreads: some pods land on minikube, some on minikube-02. Spawning processes showed the new node come to life in real time. One thing cannot be shown on this hardware: multiple clusters, because there is only one machine — but nodes and pods across them are exactly as shown.
18.11 Advanced Topics: Persistent Volumes and Service Accounts
18.11.1 Persistent Volumes
A persistent volume is physical memory — not virtual. In the demo, a path was created (a mounted area under /mnt/data on the local disk) and declared in Kubernetes as a persistent volume with 1 GB of storage allocated, read/write. These volumes are used where the code needs to work with a database or produce data that must survive — that is the kind of place you create physical memory.
Worked example — a persistent volume of one GB, claimed by 500 MB, read through Nginx with cat.
- Create the volume: a path under /mnt/data on the local disk is mounted and declared in Kubernetes as a persistent volume — 1 GB of storage, read/write.
- Claim a slice: a persistent volume claim takes a slice of the volume — one service claims 500 MB of the 1 GB and uses it. The dashboard shows the volume claiming manually, already bound, using 1 GB of physical memory.
- Write and read back: the demo pod writes a file into its claimed storage and reads it back.
- Prove it with Nginx: an Nginx pod mounts the volume at /usr/share/nginx/html; running
caton that path shows an HTML file — a physical file that was created on the local hard disk and pushed into Kubernetes.
Result: 500 MB of the 1 GB volume is claimed, and the HTML file survives on physical disk. Sense-check: the file is visible through the mounted path after the pod restarts — that is exactly what "persistent" means, storage independent of any single pod.
You can create your own files and point them at the volume the same way.
18.11.2 Service Accounts
Service accounts are third-party integrations for doing more things. Examples: a Docker service or a mailing service — say you want to mail someone whenever something goes down or some event happens in the cluster. That is configured through the YAML file: declare "this is my mailing service, mail me when something goes down." Service accounts are third-party accounts you attach to workloads; you can create your own (the mailing account is the running example). In the demo cluster the list of service accounts was empty — nothing had been created yet, but the mechanism is ready.
Recap: persistent volumes give the cluster physical memory that outlives any pod — 1 GB on disk, claimed in slices (500 MB in the demo), mounted into pods like the Nginx HTML folder. Service accounts attach third-party identities — a mailing service that mails you when something goes down — to workloads through the YAML. Together they close the two gaps the demo showed: state that survives, and identity for outside integrations.
18.12 Cleanup and Final Advice
18.12.1 Shutting Everything Down
Minikube and Docker eat hard disk like anything, so do not leave them running after you finish work. The two closing steps:
minikube stop
minikube delete --all
Stop Minikube first, then delete — delete --all clears the entire Minikube installation. In Docker, delete the images you created (removable from the UI), and finally go into Docker settings and disable Kubernetes. Disabling destroys all the Kubernetes packages Docker created, and your memory comes back down — otherwise the hard disk gets eaten: the demo machine showed everything sitting near 1 GB of usage, which is heavy on a 256 GB hard disk that needs clearing every now and then.
Pitfall — the hungry hard disk. Minikube and Docker keep consuming disk space while they run, so leaving them on after work slowly eats the machine. The cleanup sequence matters: stop Minikube, delete the installation (minikube delete --all), remove the images you built in Docker, and disable the built-in Kubernetes in Docker settings — only then does the memory come back down.
18.12.2 What You Need to Practice
If you are well-versed with the commands, Kubernetes is very, very easy — the only things to know are the YAML files and the commands, nothing beyond that. Two tools are enough to practice everything: install Docker Desktop and install Minikube, then play. Recent versions of Docker ship Kubernetes built in: enable it in the settings, and you can see the system containers Kubernetes creates (the master node and worker node, and all the internal logs). Everything running in Docker and Kubernetes is Linux-based, and that is deliberate: with Linux you have more control over your network and hardware.
Jenkins integration was not shown live because Jenkins needs network access — it belongs in the networking part of the course. The command file, the images, and the project are shared so you can execute everything yourself and compare. The set of YAML files covers far more than this session: each file exists for a different purpose, mostly from the four-week Kubernetes work — play with all of them and each will teach something; ask if anything will not execute. If you are stuck on any file, reach out and it gets explained.
Recap and practice plan: the whole session distils to two skills — reading and writing YAML files, and running kubectl commands. Install Docker Desktop and Minikube, run every command from the shared command file, and play with each YAML file; everything that failed to run here (Jenkins) belongs to the networking part of the course, not to this toolchain.
Exam Guidance Summary
No exam-specific guidance — marks, question patterns, or topic distributions — was given in this session, so there is nothing to report on the exam itself. What the session did stress, repeatedly, are the things a student should take away:
- The Kubernetes vocabulary is the foundation: namespace, cluster, node, master node, worker node, API server, controller, scheduler, etcd, kubelet, kube-proxy, pod, container, and volume. The session stated the rule directly: understand these words and Kubernetes is easy.
- The practical skill to practice is YAML files and kubectl commands — the demo called these the only things you need to know, beyond nothing.
- The shared command file and project files are the practice material: install Docker Desktop and Minikube, execute every command, and play with each YAML file.
If an exam question on Kubernetes appears, expect it to start from the vocabulary (namespaces, clusters, nodes, pods, master node components) and to test the YAML-and-command workflow shown in the demo.
Key Industry Applications
- Banking system access model: end users reach microservices either through cloud-hosted instances or through on-premises physical servers, and both paths serve the same services.
- Batch scheduling in banking: batch processing (described as NIF processing) runs on a fixed cadence — every four hours — and is exactly what the Kubernetes scheduler handles.
- Geo-distributed clusters for disaster resilience: systems spread across Asia, Europe and the Americas keep serving users when a flood or calamity hits one continent.
- Port tunneling as a security practice: exposing 35350 while the service stays hidden on 2300, so attackers hammering the exposed port cannot reach the real service.
- Random ephemeral ports to resist probing: letting Kubernetes assign the port each time prevents attackers from targeting a known fixed port with a batch process.
- The registry workflow: development teams create Docker images, upload them to a registry (Docker Hub at hub.docker.com), and deploy from there — the exact word for the store is registry.
- Managed Kubernetes as a service: Amazon EKS takes Kubernetes and provides it as a managed service, so teams skip hosting Kubernetes themselves.
- Offline local testing: Minikube runs fully locally (no Wi-Fi needed); internet is required only when an image must be pulled from a remote registry, such as busybox.
- Container weight matters: images at roughly 867 MB explain why scaling decisions and resource limits are real budget questions in production.
- High availability through replicas: multiple instances (replica sets) with one active and one passive member keep services up when an instance fails.
- Persistent volumes for state: 1 GB physical volumes claimed per workload (500 MB in the demo) are how stateful work — databases, files like the Nginx HTML — survives pod restarts.
The pattern behind all of these: every concept in the session exists because a real production system must keep running under attack, under failure, and under load — tunneling and random ports against attackers, geo-distributed clusters against disasters, replicas against pod failures, and persistent volumes against data loss.
ITD Lecture 18 notes · Docker and Kubernetes: Container Orchestration
Sections Breakdown
Deployment brings security, networking and availability work once a service is built; Kubernetes automates these jobs, and the correct term is orchestration, not sophistication.
The full vocabulary from namespace and cluster down to pod: nodes, the master node's API server, scheduler, etcd and controller, the worker-side kubelet and kube-proxy, and pods as container plus volume.
The demo service: a single Node.js authentication microservice with Express.js, listening on port 2300, run and verified locally before any container work.
Building authentication-service:v15 from a Dockerfile, running it as a container with a port tunnel from 35350 to 2300, and managing containers with docker ps, logs, kill and curl.
Starting Minikube, writing service and deployment YAML files, applying them with kubectl, and reaching the service on port 51458, with the dashboard as the visual report.
A cron job with the five-star schedule, the busybox image, and the job lifecycle: each run completes and destroys its pod.
kubectl get, describe and cluster-info: the four pod statuses, nodes and service types, and why pod data survives restarts.
Versioning images like zip files, the Docker engine's role, the Docker daemon connection, registries like Docker Hub, and managed Kubernetes with Amazon EKS.
Why Docker cannot scale, the horizontal pod autoscaler, scaling from the dashboard, command line or YAML, and how Kubernetes ends extra replicas when there is no load.
Adding a worker node to a running Minikube cluster and watching pods spread across both nodes.
Persistent volumes as physical memory and service accounts as third-party integrations attached through the YAML.
The cleanup routine that protects the hard disk, and the practice plan: YAML files and kubectl commands with Docker Desktop and Minikube.
The exam-relevant core of the session: the Kubernetes vocabulary and the YAML-and-command workflow.
How the session's concepts map to production: security through tunneling and random ports, geo-distributed clusters, replicas, registries, and persistent volumes.
Exam Revision Notes
Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.
Why We Need Container Orchestration
Must-know: Deploying a service means handling security, networking, deployment and availability. Kubernetes provides all of these; the process is called orchestration (coordinating running containers), not sophistication.
⚠️ Top pitfall: Calling this part of DevOps 'sophistication' — the correct term is orchestration, because the job is coordinating containers as one system.
Self-check: Name four jobs Kubernetes handles for you after you deploy a service.
Connects to: Section 18.2, Section 18.4, Section 18.5
The Kubernetes Vocabulary: From Namespace Down to Pod
Must-know: The full vocabulary: namespace = project name, cluster = physical hardware, nodes = VMs (master controls, worker executes), API server = network gateway, scheduler = batch-job clock, etcd = key-value register of IPs and ports, controller = coordinator, kubelet and kube-proxy = worker-scale twins, pod = container + volume.
⚠️ Top pitfall: Confusing the namespace (a logical project label) with the cluster (physical hardware); forgetting that the cloud is finally a physical server somewhere.
Self-check: Which master-node component keeps the register of every IP and port, and which component is the network gateway?
Connects to: Section 18.1, Section 18.4, Section 18.5
The Microservice We Deploy
Must-know: The service is one Node.js/Express.js file, listening on localhost port 2300, answering 'Docker authentication service' at localhost:2300/. Verifying it locally first separates 'the code works' from 'the container works'.
⚠️ Top pitfall: Skipping the local verification: without a known-good baseline you cannot tell later whether the code, the image, or the deployment is at fault.
Self-check: Which port does the authentication service listen on, and which string does it answer with?
Connects to: Section 18.1, Section 18.4, Section 18.5
Docker: Packaging the Service Into an Image
Must-know: docker build -t name:version . builds an image from the current directory; without a version tag Docker adds :latest and version management breaks. docker run -p external:internal maps a port tunnel; the internal port stays hidden from attackers.
⚠️ Top pitfall: Forgetting the version tag (silent :latest overwrites) and confusing the internal port (2300, where the service really listens) with the external port (35350, the only door the outside world sees).
Self-check: What does the dot at the end of the docker build command mean, and why does the container listen on 2300 but serve on 35350?
Connects to: Section 18.3, Section 18.5, Section 18.8
Kubernetes in Practice: Minikube, YAML and kubectl
Must-know: minikube start boots the local cluster (named minikube, namespace default); kubectl = kube controller. YAML files declare objects; kubectl apply -f <folder> creates them all at once. The request path is Kubernetes service port -> Docker port -> application port (51458 -> 35350 -> 2300).
⚠️ Top pitfall: Running a container without resource limits — it takes however much it can and can crash the laptop; and trusting a fixed port, which invites hacking attempts.
Self-check: What do the limits and requests in the deployment YAML protect, and why does Kubernetes assign a random final port?
Connects to: Section 18.2, Section 18.4, Section 18.7, Section 18.9
Cron Jobs: Scheduled Work in Kubernetes
Must-know: kind: CronJob + schedule * * * * * = run every minute (same cron syntax as Jenkins). The container uses busybox and runs date; pwd; restartPolicy: OnFailure restarts failed jobs. Each run creates one pod, completes it, and destroys it.
⚠️ Top pitfall: Reading 'ready: no' as an error — for a completed cron job the pod is destroyed by design; and expecting pwd to print inside busybox, which cannot read the working directory internally.
Self-check: What does the schedule * * * * * mean, and why is there no active pod between runs?
Connects to: Section 18.2, Section 18.5, Section 18.7
Reading the Cluster from the Command Line
Must-know: A pod list shows exactly four statuses: running, completed, failed, pending. Completed is not an error. Never run a single pod — pods do not store data; replica pods take over and Kubernetes persists the data internally, like Oracle DB, handing it back on restart.
⚠️ Top pitfall: Running one pod only (data loss on failure) and misreading Completed as an error — completed pods finished their work and no pod stays allocated.
Self-check: What are the four pod statuses, and what happens to the data when a pod goes down?
Connects to: Section 18.5, Section 18.6, Section 18.9
How Docker and Kubernetes Work Together
Must-know: Never overwrite an image version — build the next version; the deployed image is independent like a zip file. Without the Docker engine Kubernetes cannot run. Kubernetes talks directly to the Docker daemon and the whole control plane runs inside Docker. A registry (e.g. Docker Hub) is where images are pulled from by name; EKS is Kubernetes as a managed service with the same YAML and commands.
⚠️ Top pitfall: Upgrading an existing image in place (overwriting) instead of creating the next version; thinking stopping Docker stops Kubernetes — after deployment the two have no connectivity.
Self-check: If you stop the Docker container, does the Kubernetes deployment stop? Why?
Connects to: Section 18.4, Section 18.5, Section 18.9
Scaling: Replicas, Autoscaling and Load
Must-know: Docker cannot create multiple containers from the same logic (no shared labels, heavy ~867 MB images). Kubernetes scales with replicas and the horizontal pod autoscaler: 500m = 0.5 (half a CPU), minReplicas 1, maxReplicas 10, target CPU 50%. kubectl scale deployment <name> --replicas=N works in watch mode; Kubernetes ends extra replicas when there is no load. Real flow: create images, push to registry, deploy.
⚠️ Top pitfall: Expecting Docker to scale (it refuses the same container twice); misreading Terminating as an error — it is a normal status visible in watch mode when replicas are no longer needed.
Self-check: Why is scaling impossible in Docker, and what does the autoscaler do when the CPU crosses the target?
Connects to: Section 18.4, Section 18.5, Section 18.7, Section 18.8
Adding Worker Nodes
Must-know: minikube start is for first-time startup only; minikube node add adds a node to a running cluster. The control plane reports host, kubelet, API server and kube configuration; the worker plane reports host and kubelet. New pods spread across nodes.
⚠️ Top pitfall: Running minikube start on an already-running cluster instead of minikube node add to create a worker node.
Self-check: Which command adds a worker node to a running Minikube cluster, and what do both planes report?
Connects to: Section 18.2, Section 18.5, Section 18.9
Advanced Topics: Persistent Volumes and Service Accounts
Must-know: A persistent volume is physical memory (e.g. 1 GB under /mnt/data); a persistent volume claim takes a slice (500 MB) and a pod mounts it (Nginx at /usr/share/nginx/html, read with cat). Service accounts are third-party integrations (e.g. a mailing service) attached to workloads through the YAML.
⚠️ Top pitfall: Treating persistent volumes as virtual memory — they are physical disk that survives pod restarts; and forgetting that service accounts are declared in YAML, not created by hand in the cluster.
Self-check: How does a pod get access to a persistent volume, and what is a service account used for?
Connects to: Section 18.2, Section 18.7, Section 18.8
Cleanup and Final Advice
Must-know: Cleanup: minikube stop, then minikube delete --all; remove Docker images and disable Kubernetes in Docker settings to reclaim disk. Kubernetes knowledge = YAML files + kubectl commands. Install Docker Desktop and Minikube and play with every file. Jenkins needs network access and belongs to the networking part of the course.
⚠️ Top pitfall: Leaving Minikube and Docker running after work — they eat the hard disk (near 1 GB of usage on a 256 GB disk); skipping the disable-Kubernetes step in Docker settings.
Self-check: What are the two closing commands, and in what order should cleanup run?
Connects to: Section 18.5, Section 18.9
Exam Guidance Summary
Must-know: The Kubernetes vocabulary (namespace, cluster, node, master and worker nodes, API server, controller, scheduler, etcd, kubelet, kube-proxy, pod, container, volume) and the YAML files and kubectl commands workflow.
Connects to: Section 18.2, Section 18.5, Section 18.9
Key Industry Applications
Must-know: Real production systems combine security (tunneling, random ports), resilience (geo-distributed clusters, replicas) and state (persistent volumes) — the registry workflow (create images, push, deploy) is the standard path.
Connects to: Section 18.1, Section 18.2, Section 18.4, Section 18.9, Section 18.11
Was this lecture useful?
BitsNotes AI Assistant
Subject Notes AssistantConfigure AI Chat
Choose how to access the chatbotSigned in as
Powered by BitsNotes — 20 messages per day. No API key needed. Want unlimited access? Use "Bring Your Own Key" mode.
Sign in to use AI Chat
Get 20 free AI messages per day to ask questions about your lecture notes. Sign in with Google or GitHub — it takes 5 seconds.
Sign In to BitsNotesSwitch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.