Docker and Kubernetes are often mentioned together in modern software development—but they solve different problems. Docker makes it easy to package applications into containers. Kubernetes takes those containers and runs them at scale, across many servers, with reliability and automation.
Although my previous post on Docker and Kubernetes was part of an assignment, it got me thinking and curious about the DevOps space and about Docker and Kubernetes in general. Having used Docker previously, doing this research really broadened my understanding.
In this tutorial, we’ll:
- Build a simple app using Docker.
- Push the container image to Docker Hub (a registry).
- Deploy the app on Kubernetes.
- Scale it up and explore how Kubernetes manages it.
Along the way, I’ll explain each step, so you don’t just copy commands but actually understand what’s happening (this is feedback I got from a work with a client that said my article lacked technical depth and was just full of copy<=>paste code).
Prerequisites
Before we begin, make sure you have the following installed:
-
- To verify, on your terminal, run:
node -v
-
Docker Desktop (or Docker Engine)
To verify, run:
docker --version
-
Kubectl (Kubernetes CLI)
to Verify, run:
kubectl version --client
-
A Kubernetes cluster
- Beginners can use Minikube (local cluster)
- Or enable Kubernetes directly in Docker Desktop.
I like to think of Docker Hub as the GitHub for container images.
Create a Simple Node.js Application
We’ll create a Node.js app that returns a simple message.
Create a project folder:
mkdir docker-k8s-demo
cd docker-k8s-demo
Inside the folder, create a file named app.js
:
const http = require('http');
const port = 3000;
const requestHandler = (req, res) => {
res.end('Hello from Docker and Kubernetes!');
};
const server = http.createServer(requestHandler);
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Create a package.json
file:
{
"name": "docker-k8s-demo",
"version": "1.0.0",
"main": "app.js",
"dependencies": {}
}
At this point, we have a simple Node.js app.
Write a Dockerfile
A Dockerfile is just a plain text file (no extension, usually named Dockerfile
) that tells Docker how to build an image. It lives in the root of your project.
Create a file called Dockerfile
:
# Start from the Node.js base image
FROM node:18-alpine
# Set working directory inside container
WORKDIR /usr/src/app
# Copy project files into container
COPY package*.json ./
COPY app.js ./
# Install dependencies
RUN npm install
# Expose the app’s port
EXPOSE 3000
# Start the app
CMD ["node", "app.js"]
Build the image:
docker build -t myapp:1.0 .
-
-t myapp:1.0
→ names the imagemyapp
with version1.0
. -
.
means “use the Dockerfile in this directory.”
If this was ran properly and successful, you should see a success message like this on your terminal.
Run the docker container:
docker run -p 3000:3000 myapp:1.0
Visit http://localhost:3000. You should see:
Hello from Docker and Kubernetes!
Pushing the Image to Docker Hub
Kubernetes won’t know about your local image—it needs to pull it from a registry.
Log in to Docker Hub:
docker login
A success message like this should be seen on your terminal
Tag your image with your Docker Hub username:
docker tag myapp:1.0 your-username/myapp:1.0
Push it:
docker push your-username/myapp:1.0
Now your image is public in Docker Hub, ready for Kubernetes.
_You are seeing "Layer already exists" in this screenshot because I had run this command once before but it failed at execution due to a lag in my internet. _
Deploying on Kubernetes
A Kubernetes deployment needs a YAML file describing what to run.
Create deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: your-username/myapp:1.0
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
Apply it (run on your terminal):
kubectl apply -f deployment.yaml
Check pods:
kubectl get pods
_This sample script creates three replicas. In the previous article, I explained what replicas are and how they relate to the problem of scaling. _
After running, you should see three pods running in your terminal window.
On your terminal, you should see three pods active and running.
Check service:
kubectl get service myapp-service
- If using Docker Desktop or cloud Kubernetes, you’ll get an external IP.
- If using Minikube:
minikube service myapp-service
This opens the app in your browser.
Scale the Application with Kubernetes
In the example above, we scaled to three replicas. As a system or application grows, more replicas or instances of it are needed to effectively scale up to the demand.
This can be done easily by running this command with the number of replicas you need per time.
kubectl scale deployment myapp-deployment --replicas=5
Kubernetes will spin up two more pods automatically.
This workflow—build with Docker, deploy with Kubernetes—is the foundation of cloud-native applications. Docker ensures consistency across environments, and Kubernetes ensures reliability in production.
💡 Enjoyed this article? Let’s connect!
I’d love to hear your thoughts, feedback.
I am also open to collaborations on technical articles, documentation, or white papers! 🚀
Top comments (0)