Creating an application is a lot of work! Deploying your application shouldn't be.
Every developer has said at one time or another, "it works locally, but I can't get it to work on the server!"; it can take you hours, or even days, to figure out what went wrong and to put a fix in place...
What would you say if I told you you could simply run the command docker run
after creating your application and, boom have your app deployed, allowing the world to see your awesomeness!?
To make this magic happen, all you need to do is implement the container platform Docker, in conjunction with the container orchestration platform Kubernetes, and you'll be shipping like a pro! Using Docker/Kubernetes, you can deploy your code and sleep well at night, without the fear of something breaking because of incompatible libraries, commands, etc. (especially if you add a CI/CD pipeline)!
In this article, we’ll deploy a Node.js-based API for our chat application to Kubernetes and our React-based chat application frontend.
The best part about this little project is that we are going to use the Stream Chat API to build the chat application! With Stream, you can create fault-tolerant real-time applications quickly, with minimal effort and without bothering with the technicalities behind the real-time architecture/infrastructure, or even maintaining the infrastructure. What's more, you can plug Stream into your existing application or build on top of it; it’s easy to use and has nicely written documentation.
One of the best ways to learn is to practice, so... let’s get started!
Getting Started
Sign Up with Stream
The first step on our journey is to create an account on Stream. To do this, head on over to the Stream website and click on the SIGNUP
button in the top right corner of the website:
Fill in your details and create your account, and then log in with your username and password. Alternatively, if you have a GitHub account, you can log in to Stream using the Single Sign-On option on the Sign In
page!
Don't Forget Your Keys!
Once you create your account, head on over to your Dashboard
by clicking the DASHBOARD
button at the top right corner of the page:
Next, grab your API_KEY
and API_SECRET
from the Dashboard
and stick them somewhere safe; we will be using both of them later on, in our application.
At this point, we have a Stream account and everything is looking good!
The Code!
Check out our Node.js-based API on Github; we'll use this as the basis for our app.
To prepare the API for deployment, let’s modify the generic API code to better fit our needs. We'll start by cloning the repo:
1git clone https://github.com/ezesundayeze/stream-chat-api
Note: If you don’t have git installed, install it! (or just go to the repo and download the source code...)
Once you've got the repo locally, you can run:
1cd stream-chat-api && yarn
(or "npm install
", if you prefer npm over yarn)
Next, create a .env
file and add the following:
NODE_ENV=development
PORT=8080
STREAM_API_KEY=YOUR_STREAM_API_KEY
STREAM_API_SECRET=YOUR_STREAM_API_SECRET
MONGODB_URI=YOUR_MONGODB_URI
Note: You can also reference the .env.example
file at any time to make sure you've got the correct configuration.
You may have noticed that the code we just pasted into our .env
file is missing a few values... To find your MONGODB_URI
, register on the MongoDB Atlas website and create a cluster. The URI you seek should look something like this: "mongodb+srv://user:password@cluster0-ycunq.mongodb.net/db?retryWrites=true&w=majority
"
Next, remember the YOUR_STREAM_API_KEY
and YOUR_STREAM_API_SECRET
we got from your Stream dashboard?? This is where you need it, for authentication!
Spin It Up!
At this point, you can run yarn start
to start the development server for your new and improved API!
Our React App will use this API for authorization, but you can extend the API to do anything you desire!
To deploy to Kubernetes, we need to create a Docker image, which we’ll deploy to our Kubernetes cluster.
Installing Docker and Kubernetes
Before we get ahead of ourselves, let's install Docker and Kubernetes. For our experimental app, we'll only need the Minikube or Docker Desktop installation of Kubernetes; click through to their site to set this up. Once you've installed Kubernetes, head on over to Docker website to create an account there.
Putting Them In Action
For the purpose of this tutorial, we’ll use Minikube with VirtualBox as a hypervisor.
Install Minikube and VirtualBox by running the following commands (on MacOS):
12brew install virtualbox brew install minikube
Note: If you are on a different OS, you can check this link for the appropriate installation methods.
Once you've got Minikube and VirtualBox installed, run the command below to start Minikube:
1minikube start
And run this to check that Minikube was started successfully:
1minikube status
If all has gone well, the response you receive should look like this:
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
So far, everything looks incredible; great work!
Now that our infrastructure is all set up, let’s prepare our application for deployment...
Deployment!
To kick off the deployment process, we'll need to create a "Dockerfile
" and a Kubernetes "yaml
" file. A "Dockerfile
" is a text file that contains the commands you'll typically call on the terminal to create a Docker image. Similarly, a Kubernetes "yaml
" file is a configuration file that defines all the resources that will be used in a Kubernetes cluster, like deployment
, pods
and services
.
Start off by adding a Dockerfile
to the root of the application you cloned and add the following content to your Dockerfile
:
FROM mhart/alpine-node:latest
RUN apk update && apk upgrade
RUN apk add --no-cache make gcc g++ python git
WORKDIR /usr/src/api
COPY . /usr/src/api
RUN yarn --build-from-source install bcrypt
RUN yarn add bcrypt -f --build-from-source
EXPOSE 8080
CMD ["yarn", "start"]
In this file, we are pulling the latest alpine-node
image from Docker Hub using the command "FROM mhart/alpine-node:latest
". alpine-node
can be thought of as our "base image", with all the configuration we need to run a Node.js application.
On the second line, we are updating our OS packages with the command
"RUN apk update && apk upgrade
", and, on the third line, we're installing some system-level dependencies, to allow our application to work properly, using the command "RUN apk add --no-cache make gcc g++ python git
".
Furthermore, on the next line, we create a "work directory" (where our application will live), using the command "WORKDIR /usr/src/api
".
Finally, we copy all our files to the work directory, using the command "COPY . /usr/src/api
". We use ".
" because we are in the directory where the files are (and, thus, don't need to navigate to another directory). With that said, you should be sure to insert the full, qualified file path of the location where your data is, if you are not in the directory where all your application data is.
Finally, "EXPOSE 8081
" exposes your app to the outside world on that port (8081) and "CMD ["yarn", "start"]
" is the reliable little command that starts up our application in our Docker image, in exactly the same way, each time we spin up a container from that image!
Building the Image
Let’s build the image and push it to our Docker Hub repository:
1docker build -t {duckerhub-username}/stream-chat-app
Then, log in:
docker login
and push the image to Docker Hub:
docker push {dockerhub-username}/stream-chat-app
Setting Up Kubernetes for Deployment
We'll start by creating our Kubernetes .yaml
file...
Create a directory called "kube
" and add deployment.yaml
and service.yaml
files inside of it by running the command:
1mkdir kube && touch service.yaml deployment.yaml
Next, add the following content to your service.yaml
file:
apiVersion: v1
kind: Service
metadata:
name: stream-chat-app
spec:
selector:
app: stream-chat-app
ports:
- port: 8000
targetPort: 8000
type: LoadBalancer
and in your deployment.yaml
file add the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: stream-chat-app
spec:
replicas: 1
selector:
matchLabels:
app: stream-chat-app
template:
metadata:
labels:
app: stream-chat-app
spec:
containers:
- name: app
image: {dockerhub-username}/stream-chat-app
ports:
- containerPort: 8000
imagePullPolicy: Always
Next, let’s use the kubectl
utility (command line tool for controlling Kubernetes clusters) to access the Kubernetes API and apply the configurations:
kubectl -f apply kube/
Note: Ensure you have a strong internet connection while this is running, as Kubernetes will need to pull the image from Docker Hub in the above step.
The above process will apply the configuration and trigger Kubernetes runtime to start the services and deployments, as specified; it might take a while, but you can stay on top of things by running the following to see all your services:
kubectl get services
or this command to see the status of your pods
:
kubectl get pods
Note: (Kubernetes pods
are analogus containers
in Docker)
To get even more details about what is going on, you can run:
kubectl describe deployment stream-chat-app
Finally, run:
minikube service stream-chat-app --url
to get the url
and port
for your pod
.
The response should look like this:
http://192.168.93.104:31354
This is the url you can use to access your API.
Setting Up the React App
Next, lets set up the React application that will work with our API...
Start by cloning the React application:
git clone https://github.com/ezesundayeze/stream-react-app.git && cd stream-react-app
In your React application .env
file add your API endpoint, for authentication with the Stream API REACT_APP_ENDPOINT='http://192.168.93.104:31354/v1/auth/init/'
and your API KEY
, from your Dashboard
.
That’s all you need to do to set up your React application to work with the Stream API and your backend!
You can now run yarn
and yarn start
to run your application... (Woooo!)
Wrapping Up
Viola, we are done! Using Stream to develop a real-time application is pretty simple...
For example, using the Stream API, we only needed to add the following lines of code after installing Stream Chat API to get a functional chat application with all this goodness, and more:
<React.Fragment>
<Chat client={client} theme={'messaging light'}>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
</Chat>
</React.Fragment>
For more information on all the fun you can have, check out the Stream API docs. Using these, you can try adding attachment sharing, or read receipts (among many other things):
And yes, you can check out our completed chat application, here!
Happy Coding!