Deploying a Dockerized Golang API on Kubernetes with PostgreSQL, MySQL

victor steven
Level Up Coding
Published in
10 min readSep 9, 2019

--

Welcome! In this article, we will deploy our API to Kubernetes.

This is the third part of the article series.

Get article 1 here and the github here

Get article 2 here and the github here

Introduction

Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications (kubernetes.io)

Kubernetes Features

  • Service discovery and load balancing
  • Automatic bin packing
  • Storage Orchestration
  • Self-healing
  • Automated rollouts and rollbacks
  • Secret and Configuration Management
  • Batch Execution
  • Horizontal Scaling

You can read about all these here.

We will deploy our awesome API on a local kubernetes cluster using minikube.

Let’s Begin

You can get the code for this article here.

But if you want to follow step by step from article 2, where we dockerized our application, clone the repo.

Step 1: Basic Installations

Minikube

Minikube is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster inside a Virtual Machine (VM) on your laptop for users looking to try out Kubernetes or develop with it day-to-day (kubernetes.io).

To install and setup minikube, follow this guide.

Kubectl

Kubectl (Kubernetes command-line tool): allows you to run commands against Kubernetes clusters. You can use kubectl to deploy applications, inspect and manage cluster resources, and view logs.

To install and set up kubectl, follow this guide.

Virtualbox

This is required in order to start and run minikube

If you are on macOS, install using it brew:

brew cask install virtualbox

It is also pretty simple to install on any other OS, you can browse about that.

Step 2: Understanding Kubernetes Terms

It is important to understand kubernetes terms. These are some:

  • Deployment

A Deployment runs multiple replicas of your application and automatically replaces any instances that fail or become unresponsive. In this way, Deployments help ensure that one or more instances of your application are available to serve user requests. Deployments are managed by the Kubernetes Deployment controller. Deployments represent a set of multiple, identical Pods with no unique identities(gcloud).

  • Pod

A Kubernetes pod is a group of containers that are deployed together on the same host. In a Kubernetes cluster, a pod represents a running process. Inside a pod, you can have one or more containers. Those containers all share a unique network IP, storage, network, and any other specification applied to the pod. (bmc.com)

  • PersistentVolume

A Persistent Volume (PV) is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes. It is useful for our database deployment.

  • PersistentVolumeClaim

A PersistentVolumeClaim (PVC) is a request for storage by a user. It is similar to a pod. Pods consume node resources and PVCs consume PV resources. Pods can request specific levels of resources (CPU and Memory).

  • Service

An abstract way to expose an application running on a set of Pods as a network service. It can be defined as an abstraction on the top of the pod which provides a single IP address and DNS name by which pods can be accessed. With Service, it is very easy to manage load balancing configuration. It helps pods to scale very easily. (tutorialspoint)

More of these definitions were obtained from the official guide

Step 3: Deployment for Database

Our database (postgresql/msyql) will need a PersistentVolume(PV) and a PersistentVolumeClaim(PVC) defined above.

Choose the database you are concerned about. Though, I will explain the process using both.

a. Using PostgreSQL

i. PersistentVolume(PV)

In the project root that you cloned, create a file: postgres-db-pv.yaml

touch postgres-db-pv.yaml

The file content with explanation of terms:

postgres-db-pv.yaml

ii. PersistentVolumeClaim (PVC)

In the root directory, touch:

touch postgres-db-pvc.yaml

The content and explanation:

postgres-db-pvc.yaml

iii. Deployment

In the root directory, create postgres-db-deployment.yaml:

touch postgres-db-deployment.yaml

The content and explanation:

postgres-db-deployment.yaml

iv. Service

Still in the root, touch:

touch  postgres-db-service.yaml

This is the content with explanation:

postgres-db-service.yaml

We have completed the number of YAML files needed to deploy our database to the Kubernetes cluster.

v. Secret file

Observe in (iii) file, we defined envFrom. We need to create that secret file and populate it with environment variables that will be used for our database.

Still in the root directory, create another new file:

touch postgres-secret.yaml
postgres-secret.yaml

You can populate the secret with your own personal data.

At this point, it is assumed that you have minikube and kubectl installed as described in step 1.

Start minikube:

From the project directory root, run:

minikube start

When all goes well, your output will look like this

Possible issues

Possible issues that might encounter with minikube not starting:

  • Permission: Remember we need virtualbox. If you are having issues with incorrect installation of virtualbox on macOS, unblock it here:
System Preferences -> Security & Privacy -> Allow -> Then allow the software corporation (in this case Oracle)

When you are done, restart your system.

  • Timeout: If you are experiencing timeout error as a result of not able to reach some IP address, you have to use proxy to get minikube to start. This is a guide to use proxy: here or this. To avoid using proxy, you can switch to a better internet connectivity.

Ensure that minikube started successfully before continuing.

Now, we can start deploying our Postgres database.

Let’s set the environmental variables that we be used.

Run:

kubectl create -f postgres-secret.yaml

After creating the secret, we confirmed that the creation process was successful by running:

kubectl get secrets

Observe the “DATA” has 10 items, exactly the number of items we have in our secret file.

To describe each element in the postgres-secret, run:

kubectl describe secrets  postgres-secret

The output is:

Now, let’s apply the commands we have in each .yaml file. These should be run one after the other:

kubectl apply -f postgres-db-pv.yamlkubectl apply -f postgres-db-pvc.yamlkubectl apply -f postgres-db-deployment.yamlkubectl apply -f postgres-db-service.yaml

A pod is created. To view the pod run:

kubectl get pods

You could see the status, The container is still creating, note that part of the creation process, a postgres image is pulled from docker, so this might take some time.

You can run the above command a couple of times to check the status of the pod, if everything work as expected you will see this:

The pod created successfully!

Possible Issues

If the status of your pod has error messages such as:

  • CrashLoopBackOff: This error means that you have a pod starting, crashing, starting again, and then crashing again.
  • And others.

If you see any of these, you can check exactly was the cause is, using any of the two commands:

kubectl describe pod <pod_name>kubectl logs <pod_name>

The pod name in my case is: fullstack-postgres-5c9cb99b75–5q8zt

b. Using MySQL

If you don’t use mysql, kindly skip this part, else follow along.

The process is very similar when using PostgreSQL, I will just give only the commands and the files that are needed. All the .yaml files are created in the root directory of our project.

i. PersistentVolume(PV)

touch mysql-db-pv.yaml

Content and Explanation:

mysql-db-pv.yaml

ii. PersistentVolumeClaim(PVC)

touch mysql-db-pvc.yaml

Content and Explanation:

mysql-db-pvc.yaml

iii. Deployment

touch mysql-db-deployment.yaml

Content and Explanation:

mysql-db-deployment.yaml

iv. Service:

touch mysql-db-service.yaml

Content and Explanation:

mysql-db-service.yaml

v. Secret File:

touch mysql-secret.yaml

Content and Explanation:

mysql-secret.yaml

Now Start minikube to deploy the mysql database:

minikube start

Screenshots and explanations have already been provided in the explanation when using PostgreSQL. If you have any issues whatsoever, refer to step 3a to resolve them.

You can continue if your minikube started successfully.

It is advisable to run the secret .yaml file first, because database credentials will be gotten from there.

kubectl create -f mysql-secret.yaml

Then run the other .yaml files:

kubectl apply -f mysql-db-pv.yamlkubectl apply -f mysql-db-pvc.yamlkubectl apply -f mysql-db-deployment.yamlkubectl apply -f mysql-db-service.yaml

Check the status of the pod:

kubectl get pods

If the status shows any error message, refer to step 3a on how to resolve them.

Step 4: Push the API to dockerhub

Before we deploy the API to the kubernetes, we need to build the docker image and deploy to dockerhub, then get the image from dockerhub while deploying to the kubernetes. Note that we don’t use docker compose here because we will be needing only one container for just the API.

a. Pushing Image to Dockerhub with PostgreSQL:

Remember we used postgres-secret.yaml to save secrets that our database and API need? Let the same data used there also be used in your .env file.

From personal observation, remove all comments from the .env file and make sure that the key, value pair matches well.

This is my env:

API_PORT=8080
DB_HOST=fullstack-postgres
DB_DRIVER=postgres
API_SECRET=98hbun98h
DB_USER=steven
DB_PASSWORD=password
DB_NAME=fullstack_api
DB_PORT=5432

Also note that we are using the Dockerfile we have always used. You will find it in the project root.

Next, build the image now using:

docker build -t <app-name> .

This is my case:

docker build -t fullstack-kubernetes .

When the build is complete, you can go to dockerhub and create a repository to push the above built image. I should mention that you need to signup in case you don’t have an account.

In my case, I created a repository called fullstack

I am going to tag the image we built to that repository using:

docker tag <image-name> <dockerhub-username>/<repository-name>:<tag-name>

So, mine will be:

docker tag fullstack-kubernetes victorsteven/fullstack:1.3.2

My tag started from 1.0.0, now is has grown to 1.3.2.

Next, push to dockerhub.

If you are authenticated yet, run:

docker login

Fill in your docker hub username and password.

Push using:

docker push <dockerhub-username>/<repository-name>:<tag_name>

This is mine:

docker push victorsteven/fullstack:1.3.2

Great!

b. Pushing Image to Dockerhub with MySQL:

The process above for PostgreSQL also applies to MySQL, all that needs to be changed is the .env file.

For MySQL:

API_PORT=8080
DB_HOST=fullstack-mysql
DB_DRIVER=mysql
API_SECRET=98hbun98h
DB_USER=steven
DB_PASSWORD=password
DB_NAME=fullstack_api
DB_PORT=3306

Since I am explaining with two databases, the tag I will use to push the MySQL app will be 1.3.3

Remember to follow the steps in (step 4a)

Step 5: Deploying the App to Kubernetes

We have our application image chilling on dockerhub, and it is now time to use it in the kubernetes deployment .yaml file.

a. Using PostgreSQL database

i. Deployment:

In the root directory touch:

touch app-postgres-deployment.yaml

The Content and Explanation:

app-postgres-deployment.yaml

ii. Service:

In the root directory touch:

touch app-postgres-service.yaml

The Content and Explanation

app-postgres-service.yaml

Applying:

kubectl apply -f app-postgres-deployment.yamlkubectl apply -f app-postgres-service.yaml

Let’s get our pods:

kubectl get pods

You should have two running pods after sometime. What you might see initially as the status is “ContainerCreating” then if successful, you should see the image below:

Cool!

Possible Issues

If you encounter any issues whatever, you can use any of these commands to debug:

kubectl describe pod <pod-name>kubectl logs <pod-name>

iii. Testing the application:

The very reason why we needed Service. A service is created for exposing the pods.

Get the list of services:

kubectl get services
services

We are going to get the URL that was exposed to us for the fullstack-app-postgres:

minikube service fullstack-app-postgres --url

This is my output: http://192.168.99.145:32243

With the URL above, I can now access my API deployed on kubernetes. How cool is that? Very cool.

You can visit: “http://192.168.99.145:32243/users” to get all users.

Visit: “http://192.168.99.145:32243/posts” to get all posts.

Refer to article 1 to get all endpoints we defined.

b. Using MySQL database

If you are not using mysql database, kindly skip this.

i. Deployment:

In the root directory touch:

touch app-mysql-deployment.yaml

The Content and the Explanation:

app-mysql-deployment.yaml

ii. Service:

In the root directory, touch:

touch app-mysql-service.yaml

The Content and Explanation:

app-mysql-service.yaml

Run the following commands:

kubectl create -f mysql-secret.yamlkubectl apply -f mysql-db-pv.yamlkubectl apply -f mysql-db-pvc.yamlkubectl apply -f mysql-db-deployment.yamlkubectl apply -f mysql-db-service.yaml

Check pod status:

kubectl get pods

Note, pod creating can take some time, monitor the status, to know the state of your pod. When the pod successfully creates, you will get output like:

Get the services in the cluster:

kubectl get services

Get the url exposed using:

minikube service <service-name> --url

From the above screenshot, we service name to use is “fullstack-app-mysql”

So,

minikube service fullstack-app-mysql --url

Gives me: http://192.168.99.146:32535

There you go, fire that URL in Postman, and interact with the API.

Step 6: Shutting Down Minikube

After you have finished your test, you can stop minikube using:

minikube stop

You can also delete the minikube using:

minikube delete

Conclusion

Wow. You could see that Deploying our API on Kubernetes is not that hard.

Get the code here

In future articles:

  • We will deploy our Kubernetes Cluster using Google Cloud, Amazon EKS or Digital Ocean.
  • We will integrate Travis
  • Consume our API with React/VueJS.

Stay Tuned!

Thanks.

--

--