How to setup a static site on Kubernetes

Learning Kubernetes and doing all sorts of component setups but not having fun with silly examples. Then you are at the right place. In this topic, we will set up something that is more realistic, more enthusiastic, and more satisfactory for a Kubernetes beginner. Instead of running some CLI application or some HelloWorld web application from a terminal, we will build a static HTML CSS website using any free templates available online of your choice.

Of course, I will build a static template of my choice, but you can try your own template by following the tutorial. Just download your template from any of the free HTML CSS template sites and extract it. Then identify the files that are required for running the template as a static website. We will not include unnecessary files because doing that will increase the size of our docker image. Yes, you got it right, we will bundle our static template files as a docker image.

Steps involved in the process:

  1. Download a free HTML CSS template of your choice
  2. Try the template with Docker first before jumping to Kubernetes, because we will get lots of things copied from here to Kubernetes. (the Docker image & the Nginx configuration)
  3. Starting a local Kubernetes cluster (We will use Kind here to create a multi-node cluster)
  4. Setup Nginx Ingress service
  5. Build the docker image for the Kubernetes environment
  6. Import the newly build docker image into the Kubernetes cluster
  7. Create the Kubernetes Deployment component
  8. Create the Kubernetes Service component
  9. Do the DNS bypass and testing
  10. Create the Kubernetes Ingress component
  11. Clear everything to keep your system clean
Kubernetes Files / Directory Structure

Start a multi-node ingress-ready Kubernetes cluster using Kind.

Why a multi-node cluster? Because we are learning Kubernetes and its eco-system/features. As we know Kubernetes helps us expand our application horizontally to multiple servers for the sake of high availability and zero downtime, we will try the same with a multi-node cluster. We will replicate our application on different nodes available on the cluster.

We will create a more configured cluster than a simple one, so we will write the cluster configuration into a YAML file like the below one. Read this article “How to set up a Kubernetes cluster locally using Kind” if you are a beginner in Kubernetes with Kind.

cluster-ingress-ready.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker
  - role: worker

We will apply this cluster configuration using the following CLI command. This configuration will generate a Kubernetes cluster having one master/control-plane node and two worker nodes. We have also instructed about Ingress configuration in the file and port mappings from the host machine to the node to be used as the server.

ShellScript
kind create cluster --config .\kubernetes\kind\cluster-ingress-ready.yaml

You should get output something like the below. I am using Docker Desktop for Windows.

Creating cluster "kind" ...
 â€ĸ Ensuring node image (kindest/node:v1.25.3) đŸ–ŧ  ...
 ✓ Ensuring node image (kindest/node:v1.25.3) đŸ–ŧ
 â€ĸ Preparing nodes đŸ“Ļ đŸ“Ļ đŸ“Ļ   ...
 ✓ Preparing nodes đŸ“Ļ đŸ“Ļ đŸ“Ļ
 â€ĸ Writing configuration 📜  ...
 ✓ Writing configuration 📜
 â€ĸ Starting control-plane 🕹ī¸  ...
 ✓ Starting control-plane 🕹ī¸
 â€ĸ Installing CNI 🔌  ...
 ✓ Installing CNI 🔌
 â€ĸ Installing StorageClass 💾  ...
 ✓ Installing StorageClass 💾
 â€ĸ Joining worker nodes 🚜  ...
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Thanks for using kind! 😊

Next, initiate the ingress controller service.

Nginx ingress controller deployment on Kind cluster

To know more about what is Ingress please follow https://github.com/kubernetes/ingress-nginx.

ShellScript
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

You should get output something like the below.

namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

Wait for the ingress controller setup to complete.

ShellScript
kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=90s

You should get output something like the below.

pod/ingress-nginx-controller-6bccc5966-7rjtd condition met

Preparing Docker Image for Kubernetes

Next, load the desired docker image(s) on the Kubernetes cluster. You should prepare a docker image of the static site beforehand. We will use what we have tried in step #2 Try the template with Docker first before jumping to Kubernetes. We will build the docker image for sharing (we will share the image from our host machine to the Kubernetes cluster).

docker build -t static-sites/movies:latest ./Dockerfile

The above command will build the docker image from the Docker file given below. Don’t forget to check all paths mentioned in the Dockerfile. The host paths should exist as mentioned in the COPY commands in the file. The web directory contains all our static files for the website and nginx.conf is for the NGINX web server. Simply we have combined our static website files with the Nginx docker image and also our custom Nginx virtual host configuration.

Dockerfile
FROM nginx
COPY ./web /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

Nginx virtual host configuration is required to access our static website using a proper domain name.

nginx.conf
server {
    listen       80;
    server_name  one-movies.local;
    root   /usr/share/nginx/html;
    location / {
        index  index.html index.htm;
    }
}

Now our docker image is ready for deployment in the Kubernetes cluster. So let’s now import this image into the cluster.

Loading Docker Image on Kubernetes Cluster

As we learned above that we are not going to run just a simple HelloWorld-like example with Kubernetes. That is why we merged the static site content files along with the Nginx official image and created our custom image. So now instead of using the official Nginx image, we will use the customized static-site image that will give us more satisfaction while learning Kubernetes. This requires us to load our previously created packed docker image on the Kubernetes cluster. And we will do so by running the following shell command.

ShellScript
kind load docker-image static-sites/movies:latest

This local docker image will be imported into each of the Kubernetes clusters like below.

Image: "" with ID "sha256:cd090aafc1f288d50aa56f899fb0f541817a316d9f7c15f9635ee581f38151db" not yet present on node "kind-worker2", loading...
Image: "" with ID "sha256:cd090aafc1f288d50aa56f899fb0f541817a316d9f7c15f9635ee581f38151db" not yet present on node "kind-control-plane", loading...
Image: "" with ID "sha256:cd090aafc1f288d50aa56f899fb0f541817a316d9f7c15f9635ee581f38151db" not yet present on node "kind-worker", loading...

Create a Kubernetes deployment component

Kubernetes deployment components are the main components that hold the docker image and create containers on cluster nodes. Like in docker we don’t directly play with containers in the Kubernetes environment. We rather deal with deployment components.

Note how we are using the previously build docker image in the following deployment configuration file. We also mention that the docker image will never be tried to be pulled from online as it is available locally, as we loaded in the previous step.

movies-deployement.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: movies-deployment
  labels:
    app: movies
spec:
  replicas: 1
  selector:
    matchLabels:
      app: movies
  template:
    metadata:
      labels:
        app: movies
    spec:
      containers:
        - name: movies
          image: static-sites/movies:latest
          imagePullPolicy: Never
          ports:
            - containerPort: 80

Try to create the deployment by applying the above configuration file.

ShellScript
kubectl apply -f .\kubernetes\deployment\movies-deployement.yaml

The deployment will be created and we should get the following intimation.

deployment.apps/movies-deployment created

Create a Kubernetes service component

Instead of directly accessing the pod using port forwarding, we add one more abstraction layer. Also, this acts as a load balancer to connect to a more healthy/free pod in case we have multiple pods running the same app. And that is the beauty of Kubernetes (Container Orchestration Tool).

movies-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: movies
  name: movies-service
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: movies
  type: ClusterIP

Try to create the service by applying the above configuration file.

ShellScript
kubectl apply -f .\kubernetes\service\movies-service.yaml

The service will be created and we should get the following intimation.

service/movies-service created

Create a Kubernetes ingress component

The Ingress component is required to access the application using a domain name from the outside world of the cluster (browser). This is the one and only public interface for the outside world to access our application and hence our application is safe. The Ingress component talks to our service component on the specified port.

movies-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
  name: my-ingress
spec:
  rules:
    - host: one-movies.local
      http:
        paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: movies-service
                port:
                  number: 80

To create the ingress rules apply the above configuration file.

ShellScript
kubectl apply -f .\kubernetes\ingress\nginx\movies-ingress.yaml

And we should get something like the one below.

ingress.networking.k8s.io/my-ingress created

All done now, we are ready to test the site. Before that just do a DNS bypass by adding one entry in your hosts file for the specified domain you have given in nginx.config.

127.0.0.1 one-movies.local

Teardown everything

ShellScript
 kubectl delete -f .\kubernetes\ingress\nginx\movies-ingress.yaml
 kubectl delete -f .\kubernetes\service\movies-service.yaml
 kubectl delete -f .\kubernetes\deployment\movies-deployement.yaml
 kind delete cluster

After testing all these don’t forget to clear everything we just created and stop docker.

Leave a Comment

Share via
Copy link
Powered by Social Snap