DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

Docker Image Kubernetes Browser

Goal

In this lab, we will follow a very common DevOps workflow:

Developer code → Docker Image → Docker Registry → Kubernetes Deployment → Kubernetes Service → Browser

By the end of this lab, you should understand:

  • how to create a small application
  • how to create a Docker image
  • how to test the container
  • how to push the Docker image to Docker Hub
  • how Kubernetes downloads the image
  • how Kubernetes creates Pods
  • how a Service exposes the application
  • how a user opens the application in a browser

Architecture

Application files
      ↓
Dockerfile
      ↓
docker build
      ↓
Docker Image
      ↓
Docker Hub
      ↓
Kubernetes Deployment
      ↓
Pod
      ↓
Service
      ↓
Browser
Enter fullscreen mode Exit fullscreen mode

Part 1 — Create the project

Do this on your local computer or EC2 instance where Docker and kubectl are installed.

Create a project directory:

mkdir docker-k8s-lab
cd docker-k8s-lab
Enter fullscreen mode Exit fullscreen mode

Check where you are:

pwd
Enter fullscreen mode Exit fullscreen mode

You should now be inside:

docker-k8s-lab
Enter fullscreen mode Exit fullscreen mode

Part 2 — Create a simple website

Create a file:

nano index.html
Enter fullscreen mode Exit fullscreen mode

Add:

<!DOCTYPE html>
<html>
<head>
    <title>Docker Kubernetes Lab</title>
</head>
<body>
    <h1>Hello from Docker and Kubernetes!</h1>
    <p>My application is running inside Kubernetes.</p>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Save the file.

If you use nano:

CTRL + O
Enter
CTRL + X
Enter fullscreen mode Exit fullscreen mode

Check the file:

cat index.html
Enter fullscreen mode Exit fullscreen mode

What did we do?

We created the application.

For this lab, our application is only one HTML page.

In a real company, this could be:

React application
Java application
Python application
Node.js application
.NET application
Enter fullscreen mode Exit fullscreen mode

As DevOps engineers, we usually do not write the business application itself.

Developers create the application.

Our responsibility is to package it, deploy it, automate it, monitor it, and keep it running.


Part 3 — Create the Dockerfile

Create:

nano Dockerfile
Enter fullscreen mode Exit fullscreen mode

Add:

FROM nginx:alpine

COPY index.html /usr/share/nginx/html/index.html
Enter fullscreen mode Exit fullscreen mode

Save it.

Check:

cat Dockerfile
Enter fullscreen mode Exit fullscreen mode

Explanation

FROM nginx:alpine
Enter fullscreen mode Exit fullscreen mode

This means:

Use the Nginx image as the base image.

Nginx will work as our web server.

Then:

COPY index.html /usr/share/nginx/html/index.html
Enter fullscreen mode Exit fullscreen mode

This copies our HTML page inside the Docker image.

The image will contain:

Linux
Nginx
Our index.html
Enter fullscreen mode Exit fullscreen mode

This is what Docker packaging means.


Part 4 — Build the Docker image

Run:

docker build -t docker-k8s-lab:v1 .
Enter fullscreen mode Exit fullscreen mode

Check the image:

docker images
Enter fullscreen mode Exit fullscreen mode

You should see something similar to:

REPOSITORY        TAG
docker-k8s-lab    v1
Enter fullscreen mode Exit fullscreen mode

What happened?

Docker read the:

Dockerfile
Enter fullscreen mode Exit fullscreen mode

and created a Docker image.

Think of the image as a package or template.

We have not deployed the application yet.

We only created the package.


Part 5 — Test the Docker image locally

Before sending our image to Kubernetes, we should test it.

Run:

docker run -d -p 8080:80 --name docker-k8s-test docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Check:

docker ps
Enter fullscreen mode Exit fullscreen mode

Open:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

If you are using an EC2 instance, use:

http://PUBLIC-IP:8080
Enter fullscreen mode Exit fullscreen mode

Make sure port 8080 is allowed in the Security Group if using EC2.

You should see:

Hello from Docker and Kubernetes!
Enter fullscreen mode Exit fullscreen mode

What does this mean?

-p 8080:80
Enter fullscreen mode Exit fullscreen mode

means:

Computer port 8080
        ↓
Container port 80
Enter fullscreen mode Exit fullscreen mode

Nginx listens on port:

80
Enter fullscreen mode Exit fullscreen mode

Part 6 — Stop the test container

We only used this container for testing.

Run:

docker stop docker-k8s-test
Enter fullscreen mode Exit fullscreen mode

Remove it:

docker rm docker-k8s-test
Enter fullscreen mode Exit fullscreen mode

Check:

docker ps
Enter fullscreen mode Exit fullscreen mode

Part 7 — Push the image to Docker Hub

Kubernetes must be able to download the Docker image.

Your local image:

docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

exists only on your machine.

Kubernetes usually downloads images from a registry such as:

Docker Hub
AWS ECR
Azure ACR
Google Artifact Registry
Enter fullscreen mode Exit fullscreen mode

For this lab we will use Docker Hub.


Part 8 — Login to Docker Hub

Run:

docker login
Enter fullscreen mode Exit fullscreen mode

Enter your Docker Hub username and password/token.


Part 9 — Tag the image

Replace:

YOUR_DOCKERHUB_USERNAME
Enter fullscreen mode Exit fullscreen mode

with your real username.

Run:

docker tag docker-k8s-lab:v1 YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Example:

docker tag docker-k8s-lab:v1 aisalkyn/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Check:

docker images
Enter fullscreen mode Exit fullscreen mode

Now you should see both names.


Part 10 — Push the image

Run:

docker push YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Example:

docker push aisalkyn/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Now the image is stored in Docker Hub.

DevOps concept

This is an important production idea.

Developer code
      ↓
Docker build
      ↓
Container Registry
      ↓
Kubernetes
Enter fullscreen mode Exit fullscreen mode

In AWS, instead of Docker Hub, we usually use:

Amazon ECR
Enter fullscreen mode Exit fullscreen mode

Part 11 — Check Kubernetes

Before deployment, make sure Kubernetes works.

Run:

kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

You should see:

NAME      STATUS
node-1    Ready
Enter fullscreen mode Exit fullscreen mode

The important word is:

Ready
Enter fullscreen mode Exit fullscreen mode

Also check:

kubectl cluster-info
Enter fullscreen mode Exit fullscreen mode

Part 12 — Create deployment.yaml

Now we tell Kubernetes:

"Run my Docker image."

Create:

nano deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Add:

apiVersion: apps/v1
kind: Deployment

metadata:
  name: docker-k8s-app

spec:
  replicas: 2

  selector:
    matchLabels:
      app: docker-k8s-app

  template:
    metadata:
      labels:
        app: docker-k8s-app

    spec:
      containers:
        - name: docker-k8s-app
          image: YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v1
          ports:
            - containerPort: 80
Enter fullscreen mode Exit fullscreen mode

Replace:

YOUR_DOCKERHUB_USERNAME
Enter fullscreen mode Exit fullscreen mode

with your username.

Example:

image: aisalkyn/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

Part 13 — Understand deployment.yaml

The first part:

apiVersion: apps/v1
kind: Deployment
Enter fullscreen mode Exit fullscreen mode

means:

Create a Kubernetes Deployment.

The name:

metadata:
  name: docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

means our Deployment will be called:

docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

Then:

replicas: 2
Enter fullscreen mode Exit fullscreen mode

means:

Run two copies of the application.

So Kubernetes will create approximately:

Pod 1
Pod 2
Enter fullscreen mode Exit fullscreen mode

Both run the same Docker image.

Then:

image: YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

means:

Kubernetes will go to Docker Hub and download this image.

Then:

containerPort: 80
Enter fullscreen mode Exit fullscreen mode

means:

The application inside the container listens on port 80.


Part 14 — Deploy to Kubernetes

Run:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Expected:

deployment.apps/docker-k8s-app created
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get deployments
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

You should see something similar to:

docker-k8s-app-xxxx   1/1   Running
docker-k8s-app-yyyy   1/1   Running
Enter fullscreen mode Exit fullscreen mode

What does 1/1 mean?

1/1
Enter fullscreen mode Exit fullscreen mode

means:

1 container expected
1 container ready
Enter fullscreen mode Exit fullscreen mode

The Pod is ready.


Part 15 — Check more details

Run:

kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

This shows additional information:

Pod IP
Node
Status
Enter fullscreen mode Exit fullscreen mode

You can also run:

kubectl describe pod POD-NAME
Enter fullscreen mode Exit fullscreen mode

Example:

kubectl describe pod docker-k8s-app-xxxxx
Enter fullscreen mode Exit fullscreen mode

Part 16 — Look at the logs

Run:

kubectl logs POD-NAME
Enter fullscreen mode Exit fullscreen mode

Example:

kubectl logs docker-k8s-app-xxxxx
Enter fullscreen mode Exit fullscreen mode

For Nginx, logs may appear after someone accesses the website.

Logs are very important for DevOps engineers because we use them when troubleshooting applications.


Part 17 — Why can't we open the Pod directly?

We now have:

Pod 1
Pod 2
Enter fullscreen mode Exit fullscreen mode

But Pods are temporary.

Their IP addresses can change.

For example:

Pod dies
↓
Kubernetes creates a new Pod
↓
New Pod gets a different IP
Enter fullscreen mode Exit fullscreen mode

Because of this, users should not connect directly to Pods.

We need a:

Service
Enter fullscreen mode Exit fullscreen mode

Part 18 — Create service.yaml

Create:

nano service.yaml
Enter fullscreen mode Exit fullscreen mode

Add:

apiVersion: v1
kind: Service

metadata:
  name: docker-k8s-service

spec:
  selector:
    app: docker-k8s-app

  ports:
    - port: 80
      targetPort: 80

  type: LoadBalancer
Enter fullscreen mode Exit fullscreen mode

Save it.


Part 19 — Understand the Service

This line:

selector:
  app: docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

means:

Send traffic to Pods that have this label:

app: docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

Remember our Deployment also has:

labels:
  app: docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

This is how the Service finds the Pods.


Part 20 — Understand the ports

port: 80
targetPort: 80
Enter fullscreen mode Exit fullscreen mode

Think:

User
 ↓
Service port 80
 ↓
Pod port 80
 ↓
Nginx
Enter fullscreen mode Exit fullscreen mode

Part 21 — Create the Service

Run:

kubectl apply -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get services
Enter fullscreen mode Exit fullscreen mode

or:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

You may see:

NAME                 TYPE           EXTERNAL-IP
docker-k8s-service   LoadBalancer   ...
Enter fullscreen mode Exit fullscreen mode

Part 22 — Open the application

If you are using a cloud Kubernetes cluster such as EKS, AKS, or GKE:

Run:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Find:

EXTERNAL-IP
Enter fullscreen mode Exit fullscreen mode

It may look like:

abc123.us-east-1.elb.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

Open it in your browser:

http://EXTERNAL-IP
Enter fullscreen mode Exit fullscreen mode

Now you should see:

Hello from Docker and Kubernetes!
Enter fullscreen mode Exit fullscreen mode

Congratulations.

Your application flow is now:

Browser
   ↓
LoadBalancer
   ↓
Kubernetes Service
   ↓
Pod
   ↓
Container
   ↓
Nginx
   ↓
index.html
Enter fullscreen mode Exit fullscreen mode

If you are using Minikube

Instead of LoadBalancer, run:

minikube service docker-k8s-service
Enter fullscreen mode Exit fullscreen mode

Or:

minikube service docker-k8s-service --url
Enter fullscreen mode Exit fullscreen mode

Then open the URL.


If you are using kind

LoadBalancer normally does not automatically give you a public IP.

For a simple lab, use port-forward:

kubectl port-forward service/docker-k8s-service 8080:80
Enter fullscreen mode Exit fullscreen mode

Then open:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Keep that terminal open while testing.


Part 23 — Watch how traffic reaches different Pods

Check Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

You should have two Pods.

The Service can send requests to either Pod.

Architecture:

                  ┌── Pod 1
Browser → Service ┤
                  └── Pod 2
Enter fullscreen mode Exit fullscreen mode

This is one reason Kubernetes is useful.


Part 24 — Scale the application

Change:

replicas: 2
Enter fullscreen mode Exit fullscreen mode

to:

replicas: 4
Enter fullscreen mode Exit fullscreen mode

Then run:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

You should now have four Pods.

You can also scale using a command:

kubectl scale deployment docker-k8s-app --replicas=4
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

What did Kubernetes do?

You told Kubernetes:

I want 4 application instances.
Enter fullscreen mode Exit fullscreen mode

Kubernetes created enough Pods to reach the desired state.


Part 25 — Test self-healing

Check Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Choose one Pod.

Delete it:

kubectl delete pod POD-NAME
Enter fullscreen mode Exit fullscreen mode

Immediately run:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

You should see Kubernetes creating a new Pod.

Why?

Because Deployment says:

replicas: 4
Enter fullscreen mode Exit fullscreen mode

Kubernetes constantly compares:

Desired state = 4 Pods
Actual state = 3 Pods
Enter fullscreen mode Exit fullscreen mode

So Kubernetes creates another Pod.

This is called:

self-healing
Enter fullscreen mode Exit fullscreen mode

Part 26 — Update the application

Change the website:

nano index.html
Enter fullscreen mode Exit fullscreen mode

Change:

<h1>Hello from Docker and Kubernetes!</h1>
Enter fullscreen mode Exit fullscreen mode

to:

<h1>Version 2 is running in Kubernetes!</h1>
Enter fullscreen mode Exit fullscreen mode

Now build a new image:

docker build -t docker-k8s-lab:v2 .
Enter fullscreen mode Exit fullscreen mode

Tag it:

docker tag docker-k8s-lab:v2 YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v2
Enter fullscreen mode Exit fullscreen mode

Push it:

docker push YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v2
Enter fullscreen mode Exit fullscreen mode

Part 27 — Update Kubernetes

Open:

nano deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Change:

image: YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v1
Enter fullscreen mode Exit fullscreen mode

to:

image: YOUR_DOCKERHUB_USERNAME/docker-k8s-lab:v2
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Watch the rollout:

kubectl rollout status deployment/docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

Check Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Refresh the browser.

You should now see:

Version 2 is running in Kubernetes!
Enter fullscreen mode Exit fullscreen mode

This is a basic Kubernetes rolling deployment.


Part 28 — See rollout history

Run:

kubectl rollout history deployment/docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

This shows deployment revisions.

If the new version has a problem, DevOps engineers may need to roll back.


Part 29 — Roll back

Run:

kubectl rollout undo deployment/docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl rollout status deployment/docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

Refresh your browser.

The previous version should return.


Part 30 — Troubleshooting commands every DevOps engineer should know

Check Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Check Deployment:

kubectl get deployment
Enter fullscreen mode Exit fullscreen mode

Check Service:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

See more information:

kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

Describe Pod:

kubectl describe pod POD-NAME
Enter fullscreen mode Exit fullscreen mode

View logs:

kubectl logs POD-NAME
Enter fullscreen mode Exit fullscreen mode

View Deployment:

kubectl describe deployment docker-k8s-app
Enter fullscreen mode Exit fullscreen mode

View Service:

kubectl describe service docker-k8s-service
Enter fullscreen mode Exit fullscreen mode

Watch Pods:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Common problems

ImagePullBackOff

If you see:

ImagePullBackOff
Enter fullscreen mode Exit fullscreen mode

check:

kubectl describe pod POD-NAME
Enter fullscreen mode Exit fullscreen mode

Possible reasons:

Wrong Docker image name
Wrong tag
Image was not pushed
Private Docker Hub repository
Authentication problem
Enter fullscreen mode Exit fullscreen mode

CrashLoopBackOff

If you see:

CrashLoopBackOff
Enter fullscreen mode Exit fullscreen mode

check:

kubectl logs POD-NAME
Enter fullscreen mode Exit fullscreen mode

This usually means the application starts and crashes repeatedly.


Pending

If Pod status is:

Pending
Enter fullscreen mode Exit fullscreen mode

run:

kubectl describe pod POD-NAME
Enter fullscreen mode Exit fullscreen mode

Possible reasons:

Not enough CPU
Not enough memory
Scheduling problem
No available node
Enter fullscreen mode Exit fullscreen mode

Browser does not open

Check:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe svc docker-k8s-service
Enter fullscreen mode Exit fullscreen mode

Also check the Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

They should be:

Running
Enter fullscreen mode Exit fullscreen mode

and:

1/1
Enter fullscreen mode Exit fullscreen mode

Clean up

When you finish the lab:

kubectl delete -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl delete -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get pods
kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Final DevOps workflow

Remember this flow:

1. Developer writes application
2. DevOps creates/builds Docker image
3. DevOps tests Docker image
4. Image is pushed to registry
5. Kubernetes Deployment pulls image
6. Deployment creates Pods
7. Service finds Pods using labels
8. Service exposes the application
9. User opens application in browser
10. DevOps monitors, troubleshoots, scales, updates, and rolls back
Enter fullscreen mode Exit fullscreen mode

The most important architecture to remember is:

Developer
   ↓
GitHub
   ↓
CI/CD
   ↓
Docker Image
   ↓
Registry
   ↓
Kubernetes Deployment
   ↓
Pods
   ↓
Service
   ↓
Load Balancer / Ingress
   ↓
Browser
Enter fullscreen mode Exit fullscreen mode

Top comments (0)