My Journey to Deploying my First (React) Application with Kubernetes
Welcome, let me start off with a quick introduction and give you some background. 👋🏼
I recently became a Developer Advocate but previously, I was a Front End Developer for two years. Most of my experience was building out the front end of enterprise applications on a team. I used various front end languages, frameworks, and tools depending on the project I was on. All of the DevOps and backend were managed by other teams, which I had little to no insight on. On my new team, we’re responsible for both the front and backend, as well as our DevOps pipeline. I wanted to understand the effort and work that went into deploying an app on Kubernetes in order to become an effective member on the team. Feel free to follow along my journey and apply it to deploying your first application as a fellow newbie!
The Research Phase
Having very little knowledge of Kubernetes, my first step was to understand what Kubernetes was. I had a Google session on it and discovered that I needed to containerize my app before it could be deployed. The tool I found that could help me with the containerization piece was Docker. I had heard good things about Docker before and it seemed to have a lot of community support so I went ahead with that choice.
I started off with researching the job of a container and how Docker and Kubernetes could work hand in hand. I’ll quickly summarize my findings in my research to give you some context. A container is made up of a container image. A container image consists of an executable package that has everything a system will need to run it such as code, runtime, settings, and configuration. There can be multiple containers running on the same machine, each running it’s own processes. Docker is a tool that helps developers containerize their applications to deploy and run them. It packages up an application and deploys it to the container. Kubernetes aka K8s handles the management and deployment of containerized applications.
If you’d like to follow the next steps in my journey, you’ll need the following tools:
- Clone and fork my example repo
- Create a Docker account
- Create an IBM Cloud account
- Download the IBM Cloud CLI
- Once you’ve installed the IBM Cloud CLI, run the following commands:
bx plugin install container-service -r Bluemix
bx plugin install container-registry -r Bluemix
The Docker Phase
My goal was to deploy one of my React side projects. It’s a simple application that uses Create React App as a foundation with Redux built on top of it. It calls the OMDb API to get movie information based on a title from the user. Tutorials on dockerizing an application were pretty straightforward. To be honest, it was probably the easiest part of my journey because of all the resources online and big Docker community support.
First, I needed to create a Dockerfile in my directory to build a Docker image from my application. The Dockerfile defines the environment that will be inside the container and lists configuration details such as dependencies, settings, etc. I also included a .dockerignore file to ignore my node_modules.
With my image configuration ready, I built my application to create the Docker image. I realized that building and creating my Docker image was just like building and running the application on my local machine. The difference was that the tool it was using was Docker (rather than a build tool like Gulp) and it was building and running an image rather than an application in a Git repository.
docker build -t <your_docker_username>/react-redux-api-example .
To make sure my image was actually created, I listed all my docker images:
My last step for this task was to run the image in my container so I could access it through the browser.
docker run -p 3000:3000 -d <your_docker_username>/react-redux-api-example
My app was now running at http://localhost:3000. This part was a little confusing to me because there was nothing in the terminal that indicated it was live and accessible. I did find a way to output the response from my application so I had some indication it was running. After I ran the above command, the container id is outputted to the terminal. With this container id, I could get the logs with the response from my app.
docker logs <container_id>
Do you know that feeling of accomplishment you get when you’ve successfully done something you think would be really difficult? I was feeling that right about at this point.
From what I had heard before, I thought this was going to be a much bigger and harder task than it actually was.
Surprisingly, I didn’t run into any issues and had my container up and running in a really short amount of time.
The Kubernetes Phase
The Kubernetes phase turned out to be a little more challenging with plenty of debugging involved.
The next step was to create a Kubernetes cluster on the IBM Cloud. A cluster is made up of nodes and those nodes have the required services (Docker, kubelet and kube-proxy) to run pods. The nodes consist of pods, which contain a group of one or more containers. The pods are composed of shared resources and instructions on how to run the containers.
I created the cluster through the IBM Cloud UI using these docs for steps 1–3. The cluster did take a while to be provisioned but one thing I did like was that it showed where it was at in the provisioning process at each step. I’ve worked with other services that give no indication of how long it will take to be provisioned so this was nice to get a sense of the provisioning time.
When the cluster was provisioned, I was able to create my Docker image in the IBM Cloud. To do this, I needed to create a namespace because it’s required to upload or create a Docker image. Namespaces are groups within an IBM account that can store Docker images.
bx cr namespace-add <your_namespace>bx cr namespaces
Next, I needed to build that Docker image in the IBM Cloud. This was basically building my existing Docker image that I created above but within the IBM Cloud ecosystem.
bx cr build -t registry.<region>.bluemix.net/<your_namespace>/react-redux-api-example:v1 .
I created my cluster in the us-south region so my region for the above step was
ng. Once my image was created, I double checked that my image creation was successful.
bx cr images
Everything was good at this point, my image was successfully created and my next step was to push that image to the Cloud! I easily did it with these two basic commands.
docker tag react-redux-api-example registry.<region>.bluemix.net/<your_namespace>/react-redux-api-example:v2docker push registry.<region>.bluemix.net/<your_namespace>/react-redux-api-example:v2
In the previous step, I created a version 1 of my image so with the v2 tag, I created version 2. It’s the same image, just different versions.
The last step of my journey was to actually deploy my created Docker image to Kubernetes but first I had to configure my Kubernetes command. The commands below allowed me to get and set my config variables.
bx cs cluster-config <cluster_name>
I copied and pasted the output of the above command in my terminal to set those environment variables. I was on my way to the deployment phase! I created a deployment of my image from my registry and proceeded to expose that deployment as a NodePort.
kubectl run react-redux-api-example-deployment — image= registry.<region>.bluemix.net/<your_namespace>/react-redux-api-example:v1
kubectl expose deployment/react-redux-api-example-deployment — type=NodePort — name=react-redux-api-example-service — -port=3000
In all the tutorials I followed, they used NodePort over LoadBalancers. The main reason it seemed was that NodePorts were the only option available with a free cluster. Honestly, I just wanted to get my app deployed so I didn’t do my due diligence in finding the pros and cons of each one. Looking back now, I probably should’ve known the difference between the two.
When a NodePort is used, Kubernetes automatically allocates a port and each node gets forwarded to that port. When a LoadBalancer is used, an external load balancer is provisioned by Kubernetes. The LoadBalancer seemed like a more complex solution to the simple app I was trying to deploy so I decided to stick with using a NodePort.
At this point, my app should’ve been accessible from any browser. I just needed to get the assigned port number of my service and public ip address of the cluster to see my app.
kubectl describe service react-redux-api-example-service
bx cs workers <cluster_name>
This was my big moment of truth! I finally deployed my app and this was the time to see it come to life. I put in the public ip address: NodePort (22.214.171.124: 31541) into my browser and had an infinite loading screen then it erred out. 😞
I was so bummed! I was sure that I followed all the steps and it looked like my pods were running and everything was good. I tried to debug a little more but not being familiar with Kubernetes commands, I was pretty lost. I tried to rebuild and deploy my image to Kubernetes a few times but still had no luck. Luckily, one of my colleagues helped me out. At first, we still couldn’t determine the problem but after a few minutes of him doing his Kubernetes magic, he had solved the problem. Of course, it was a small, simple problem…and I had spent half an afternoon and morning trying to resolve it. 💩
I was not aware that I could get my deployment details with
kubectl get deploy -o yaml and within that yaml file, it showed what port the cluster was proxying to. It was the wrong port. It had been set to something other than 3000 (which was what I had exposed in my Dockerfile). He changed that port to 3000 and voilà it worked! 🙌🏼 A big lesson I learned from this whole process was make sure the port in your expose command is the same as your exposed port in your Dockerfile.
The Celebration Phase
I had finally deployed my first app on Kubernetes! 🎉 🎉 🎉 There are probably different ways you can deploy but I found these steps to be the easiest to follow. I hope you find this helpful and has inspired you to deploy your first app on Kubernetes. I’d be happy to answer any questions you may have. Follow me on Twitter @rizcheldayao for more tech related content 🦄