Published on July 30, 2024

Deploying an api to a kubernetes cluster

 7 minutes

Deploying an api to a kubernetes cluster

This guide is a quick walk-through on how to deploy an express api to a kubernetes cluster, you can pull the example application from a github repository here.

git clone https://github.com/eiberham/dragonball.git

If you take a look at the source code you'll see that the application uses mongodb by means of persistent storage and redis as a cache or volatile storage, normally third party services like these live in their own pods, so we're going to start defining the most relevant one first and then we'll follow up with the rest.

From the docs

minikube quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. We proudly focus on helping application developers and new Kubernetes users.

From the docs

Helm helps you manage Kubernetes applications — Helm Charts help you define, install, and upgrade even the most complex Kubernetes application. Charts are easy to create, version, share, and publish — so start using Helm and stop the copy-and-paste.

Before anything make sure you have installed minikube and helm in your machine and proceed to create a kubernetes cluster:

brew update brew install minikube brew install helm minikube start --vm=true

Express

First off we need to conteinerize the application by building a docker image, on the root folder you'll find a dockerfile. Later on, when creating the deployment objects we will need it.

dockerfile

FROM node:10.16.0 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --only=prod COPY . . RUN npm install -g nodemon EXPOSE 3000 CMD npm run dev

In order to build the image get into the application's root directory, run this command, log into your dockerhub account and push the image

cd dragonball docker build -t eiberham/dragonball:v1 . docker login docker push eiberham/dragonball:v1

Once completed we have to define our express deployment and service objects. For the service we have to define its type as load balancer so that it can redirect traffic to the right pod based on network load.

express.yml

apiVersion: v1 kind: Service metadata: name: express spec: selector: app: express ports: - name: "3000" port: 3000 targetPort: 3000 type: LoadBalancer --- apiVersion: apps/v1 kind: Deployment metadata: name: express spec: replicas: 1 selector: matchLabels: app: express template: metadata: labels: app: express spec: containers: - image: eiberham/dragonball:v1 name: dragonball ports: - containerPort: 3000 imagePullPolicy: Always

Ultimately, as we have a service of type:LoadBalancer we'd need to expose the service by using ingress, but before let us enable the ingress add-on in minikube :

minikube addons enable ingress

ingress.yml

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: entrance annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - host: localhost http: paths: - path: / pathType: Prefix backend: service: name: express port: number: 3000

Mongo

The mongo instance will have authentication enabled so we'll need to provide username and password. Let us create the secrets.yml file that is going to hold our credentials:

secrets.yml

apiVersion: v1 kind: Secret metadata: name: mongo-secret type: Opaque stringData: USERNAME: eiberham PASSWORD: eiberham

Now run:

kubectl apply -f secrets.yml

Since the idea is to we create the bash script that will prepopulate the database, we're going to create a seeding bash script. For now, the most important thing to have is a user who can authenticate and issue requests to protected routes:

seeding.sh

mongosh "mongodb://127.0.0.1:27017/dragonball" --username $USERNAME -p $PASSWORD --authenticationDatabase dragonball <<EOF db.users.insertOne({ "username" : "admin", "password" : "$2b$10$al8KvO3PCchoB/nmwU6XZ.HjpmGRSw48SS8U8P0IjRuQlfkJKISUK", "name" : "Admin", "profile" : 2.0 }) db.characters.insertOne({ name: "Goku", description: "Goku is the main protagonist of the dragon ball series", avatar: "https://someimageurl.com" }) EOF

In the code above we have defined an insert for the users collection using admin as username and password as well as an insert for the characters collection.

kubectl create configmap seeding-configmap --from-file=seeding.sh

In order to create the deployment/service for the mongo database we will use helm, but beforehand we have to seed the database.

values.yml

mongodb: auth: usernames: - eiberham passwords: - eiberham databases: - dragonball initdbScripts: enabled: true configMapName: seeding-configmap extraEnvVars: - name: USERNAME valueFrom: secretkeyRef: name: mongo-secret key: username - name: PASSWORD valueFrom: secretkeyRef: name: mongo-secret key: password

Now it's time to install the mongo chart. If you wish to look

noglob helm install mongo oci://registry-1.docker.io/bitnamicharts/mongodb \ --set auth.usernames[0]=$(kubectl get secret mongo-secret -o jsonpath='{.data.USERNAME}' | base64 --decode) \ --set auth.passwords[0]=$(kubectl get secret mongo-secret -o jsonpath='{.data.PASSWORD}' | base64 --decode) \ --set auth.databases[0]=dragonball \ --set initdbScriptsConfigMap=seeding-configmap \ --set extraEnvVarsSecret=mongo-secret \ --values values.yml --debug

You can either check the pod logs to make sure there was no error or log into the pod and query any seeded collection. Plus the seeding script should be located at inside the pod.

kubectl logs <<pod>> kubectl exec it <<pod>> /bin/bash mongosh "mongodb://127.0.0.1:27017/dragonball" --username eiberham -p eiberham --authenticationDatabase dragonball db.characters.find()

Redis

Finally we need a redis instance, so we will install a helm chart with authentication disabled and just one replica:

noglob helm install redis oci://registry-1.docker.io/bitnamicharts/redis \ --set auth.enabled=false \ --set replica.replicaCount=1

By having every piece of the puzzle in place proceed to issue the following command to get the express url:

minikube service express --url

That should give us the express service's url and port. Now if you issue a curl to the auth endpoint it should work:

curl -X POST https://192.168.64.30:30136/api/auth -k -d '{ "user":"admin", "password":"admin" }' -H 'Content-Type: application/json'

Comments