To add a more redundancy to my project and finally getting experience with docker and Kubernetes, I decided to deploy Darling in a Kubernetes cluster. It was a quite challenging task but totally worth the time. I learned to build my own Docker image from scratch and upload it to a private repository. Then I pulled it to my Kubernetes master and deployed it to my pods and exposes it it with an NGINX ingress controller to the internet.

Where I really was struggling was to issue a certificate with the cert-manager. But right there was my steepest learning curve. I got to know most of the kubectl commands and how to use the for troubleshooting. Additionally I completely understand how yaml files work and why they are important in Kubernetes. However, I did not manage to successfully issue a certificate but I was able to locate the issue. A made a few adjustments to my cloud architecture to fix my issue which not only made me successfully issue a certificate but also made my infrastructure more scalable.

With this concept I could expose only one service to the internet. But what if I want to expose more services? Since I have only one Public IP I am required to act smart. Well not too smart... It is actually quite an easy step. We add an NGINX reverse proxy on top of the network and configure server blocks. Additionally the reverse proxy issues a wildcard certificate for my domain so I never have to deal with them again. Maybe I could add a load balancer on top and configure some more reverse proxies if they get sweaty. But this is only a playful thought for now. I need to have something during my PDCA cycle. So my network infrastructure now looks like this.

This allows me to expose as much services as I want. Nice! I know, I could get rid of the new Kubernetes master and add my application to the first Kubernetes master. However, I added him to clearly visualize what I am trying to archive here.

Let me show you how it's done

Okay enough theory. Let's get our hands dirty. We start creating a docker Image of Darling for this we need a "Dockerfile" that holds all the instructions for docker.

//Use NGINX as template
FROM nginx

//Update the system and install dependencies
RUN apt update -y && apt upgrade -y
RUN apt install build-essential python3-dev python3-pip -y

//Install more dependencies
COPY requirements.txt ./
RUN python3 -m pip install --upgrade pip
RUN python3 -m pip install -r requirements.txt
RUN python3 -m pip install uwsgi

//Copy the application to the current path
COPY ./app ./

//Expose port 8080 so we can visit the application website
EXPOSE 8080

//Start uwsgi and listen on all interfaces on port 8080
CMD ["uwsgi", "--socket", "0.0.0.0:8080", "--protocol=http", "-w", "main:app", "--processes", "4", "--threads", "2"]

So now we have our instructions ready and build the image with

sudo docker build --tag username/darling ./

To upload the image to Dockerhub

docker push username/darling:latest

As we now have our application ready for deployment, we need to install Kubernetes. I followed multiple tutorials to gather as much information as possible but there is one that does it pretty much. Here's the link

For installing Kubernetes I used this command

sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-get install keepalived
sudo systemctl enable keepalived && systemctl start keepalived

Some more commands

//Initialize kubernetes
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.0.xx

//Copy Kubernetes configuration
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

//Apply the Weave Net network driver
//Flannel caused a lot of issues in my network so I used Weave Net
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

//If you still want to apply Flannel you can use
//kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

So if everything went well you should be able to execute "kubectl get nodes"

Here you can see the master node and the worker node. Both should have the status "Ready".

Tip: If anything is not on status "Ready" use kubectl describe <component> <component_name>. This will give you a detailed list of how the component is configured and events that occurred recently. Also keep in mind some components require namespaces which you can query with kubectl get <component> --all-namespaces or kubectl get <component> -n <namespace>

KUBERNETES IS READY! Now we need to create manifests in form of YAML files to pull our image and deploy it to our pods.

Since we do not use an ingress controller we only need a Service and a Deployment file. But I will show it how It's done.

If you are pulling your image from a private repository, you first need to create a docker-registry. It can be done one one simple command.

kubectl create secret docker-registry regcred --docker-server=https://index.docker.io/v1/ --docker-username=USERNAME --docker-password=PASSWORD --docker-email=EMAIL_ADDRESS

I created 2 files called darling-deployment.yaml and darling-service.yaml

darling-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: darling-deployment
  labels:
    app: darling-deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: darling-deployment
  template:
    metadata:
      labels:
        app: darling-deployment
    spec:
      containers:
      - name: darling-deployment
        image: username/darling
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
      imagePullSecrets:
      - name: regcred

darling-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: darling-service
  annotations: 
  labels:
    app: darling-service
spec:
  type: LoadBalancer
  externalIPs:
  - 192.168.0.xx
  ports:
  - port: 8080
    name: http
    targetPort: 8080
  selector:
    app: darling-deployment

Now we created our instructions for Kubernetes so it can deploy our services in a blink of an eye. Use the following commands to deploy the service

kubectl create -f darling-deployment.yaml
kubectl create -f darling-service.yaml

If everything went well, it should look like this

Now we are able to visit darling at http://192.168.0.xx:8080

Nice! Now we successfully deployed Darling to our pods and exposed it to the LAN. Now if you want to use an ingress controller I advise you to use this tutorial. Have fun there. But for now we use a separate NGINX reverse proxy to expose Darling to the WAN. Make sure you have an A record for the domain at your domain hoster's control panel.

We head over to our reverse proxy and install NGINX and create a new config file in /etc/nginx/sites-enabled/

#Listen on port 80 and redirect every traffic to HTTPS
server {
        server_name *.buerge.io
        listen 80 ssl;
        return 301 https://$host$request_uri;
}

#Apply our wildcard certificate for every subdomain
server {
        server_name *.buerge.io
        rewrite ^ https://$host$request_uri? permanent;
        listen 443 ssl;

    ssl_certificate /etc/letsencrypt/live/darling.buerge.io/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/darling.buerge.io/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


}

#Redirect https://darling.buerge.io to our Kubernetes cluster
server {
        server_name darling.buerge.io;
        listen 443 ssl;

        location / {
                include proxy_params;
                proxy_pass http://192.168.0.xx:8080;
        }
}

#Check if the config works and redirect https://www.darling.buerge.io to the development server
server {
        server_name www.darling.buerge.io;
        listen 443 ssl;
        location / {
                include proxy_params;
                proxy_pass http://unix:/home/ubuntu/darling/darling.sock;
        }
}

Now restart NGINX with "sudo systemctl nginx restart" to apply the changes and we're done. That's it. Nothing more to add. You did it. Good bye!

Last modified: October 18, 2020

Author

Comments

Write a Reply or Comment

Your email address will not be published.