Host React app on Nginx in Docker and Kubernetes

Introduction

Today we’ll learn how to host your React Docker app on NGINX inside Docker and Kubernetes We’ll take a sample React app and host it on NGINX web server inside Docker. Then we’ll export the Docker image and import it into Kubernetes. Finally we’ll host this imported docker image in Kubernetes on Ubuntu using microk8s. We’ll also learn how to scale and load balance your app.

As usual, we’ll focus only on Ubuntu Server based terminal commands. No GUI commands. We’ll be using microk8s toolset of Ubuntu. Microk8s is not a subset but a full upstream version of Kubernetes. Microk8s makes it easy for a developer to install Kubernetes on Ubuntu with great default options. It also makes hosting and managing images easier.

Focus

  1. Create sample React app.
  2. Host the React app on NGINX web server inside Docker.
  3. Export the app image from Docker into a file.
  4. Import the docker image file in Kubernetes.
  5. Host the docker app in Kubernetes.

Steps

  1. Install nodejs, npm and default React app.
  2. Create a production build of the React app.
  3. Install docker.
  4. Create a Dockerfile file to create an image of a production build of your React app with NGINX.
  5. Export the docker image into a file.
  6. Install microk8s (Kubernetes).
  7. Enable required services for Kubernetes.
  8. Import the docker image.
  9. Host the app image in Kubernetes.
  10. Scale and load balance your app.

Here we go!

Note: To avoid typing sudo everytime, use sudo -s, and then enter password. But for the sake of completeness, I’ll be prefixing the commands with sudo where ever required.

1. Install nodejs, npm and default React app

sudo apt update
sudo apt upgrade

sudo apt-get install nodejs
sudo apt-get install npm
Install the default React app
npm install -g create-react-app
create-react-app --version
Create the hello world app and check if it is running fine
create-react-app first-react
cd ./first-react
npm start

If the output looks fine, then the first App is running. Press Ctrl-C to exit.

2. Create a production build of the React app

sudo npm run build

This will create a build folder and ill contain a production ready site.

3. Install Docker

sudo apt-get install docker.io
sudo systemctl enable docker
sudo systemctl start docker

4. Create a Dockerfile file to create an image of a production build of your React app with NGINX

Type nano Dockerfile and paste the following contents:

#Based on Node.js, to build and compile the frontend
FROM node as build

WORKDIR /app

COPY package*.json /app/

RUN npm install

COPY ./ /app/

RUN npm run build

# Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx
FROM nginx

COPY --from=build /app/build /usr/share/nginx/html

Press Ctrl-S and Ctrl-X to exit.

Build your Docker image
docker build -t first-react-app .

You can verify the contents of the image. You can bash into the image and check the contents of the file system.

docker run -it first-react-app bash
Notes about the Dockerfile
  1. You have two FROM statements. The first FROM downloads a node image, builds you React application
  2. The second FROM statement, copies the contents from first image, discards the first image, and builds a new image with NGINX inside it.
  3. The line -from=build /app/build allows you to copy contents from your first image into the second image. The build word after -from= comes from the first line in Dockerfile – as build
  4. You are chaining image building. This is officially called a multi-stage build.
  5. Important note: This new image only has the production ready build of your App that site hosted in NGINX. You don’t need nodejs or npm installed or running inside the image.

5. Export the docker image into a file

docker save first-react-app > first-react-app.tar

This creates a .tar file around 137 MB in size for this scenario.

6. Install microk8s (Kubernetes)

snap install microk8s --classic

This will install microk8s (Kubernetes) in a single command.

Important note: For Kubernetes here you have to prefix all your commands with microk8s before typing kubectl. This can be tedious. To enable you to skip typing microk8s all the time, type:

snap alias microk8s.kubectl kubectl

To revert back, type: snap unalias kubectl

Check which services are enabled/disabled

microk8s status

7. Enable required services for Kubernetes

microk8s enable dns dashboard

8. Import the docker image

microk8s ctr image import first-react-app.tar

Note that this imports the image docker.io namespace – docker.io/library/first-react-app:latest

Check the images installed in current Kubetnetes
microk8s ctr images ls
To check whether our image is imported successfully
microk8s ctr images ls | grep "first-react-app"

9. Host the app image in Kubernetes

Create a .yaml file to create a Kubernetes deployment.

nano deployment.yaml

Paste the following contents inside

apiVersion: apps/v1
kind: Deployment
metadata:
  name: first-react-deployment
  labels:
    app: first-react-app
spec:
  replicas: 5
  selector:
    matchLabels:
      app: first-react-app
  template:
    metadata:
      labels:
        app: first-react-app
    spec:
      containers:
      - name: first-react-app
        image: docker.io/library/first-react-app:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 80

Important note: If you do not specify imagePullPolicy: Never in the .yaml file, it will make your life miserable. Your deployment will not get created since there is a default policy getting applied while pulling the image for deployment. Since you are developing initially and to make things easier, we skipped applying pull policy rules.

10. Scale and load balance your app

As you might have observed, in the .yaml file we had mentioned replicas: 5. This will scale your app to 5 container instances for load balancing.

Pods

Pods in Kubernetes are smallest instance which hosts your container. Technically, a pod can have multiple containers, but for simplicity, we will assume it has only one instance of our running container.

So when you run this command – microk8s kubectl get pods, you’ll get.

NAME                                      READY   STATUS    RESTARTS   AGE
first-react-deployment-5cb789d84f-5jf47   1/1     Running   0          9m42s
first-react-deployment-5cb789d84f-kll6h   1/1     Running   0          9m42s
first-react-deployment-5cb789d84f-qjlv8   1/1     Running   0          9m42s
first-react-deployment-5cb789d84f-s7kgl   1/1     Running   0          9m42s
first-react-deployment-5cb789d84f-sztsd   1/1     Running   0          9m42s

Since we had mentioned 5 replicas, your app is running in 5 different pods. You can also run microk8s kubectl get all

NAME                                          READY   STATUS    RESTARTS   AGE
pod/first-react-deployment-5cb789d84f-5jf47   1/1     Running   0          33m
pod/first-react-deployment-5cb789d84f-kll6h   1/1     Running   0          33m
pod/first-react-deployment-5cb789d84f-qjlv8   1/1     Running   0          33m
pod/first-react-deployment-5cb789d84f-s7kgl   1/1     Running   0          33m
pod/first-react-deployment-5cb789d84f-sztsd   1/1     Running   0          33m

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.152.183.1   <none>        443/TCP   95m

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/first-react-deployment   5/5     5            5           33m

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/first-react-deployment-5cb789d84f   5         5         5       33m

As you can see, out app is still not exposed in the service namespace/section.

To finally expose our deployment, type:
microk8s kubectl expose deployment first-react-deployment --type LoadBalancer --name react-deployment-service --external-ip=192.168.26.133

You have now exposed the service to be usable externally. You can now type – microk8s kubectl get all

NAME                                          READY   STATUS    RESTARTS   AGE
pod/first-react-deployment-5cb789d84f-5jf47   1/1     Running   0          40m
pod/first-react-deployment-5cb789d84f-kll6h   1/1     Running   0          40m
pod/first-react-deployment-5cb789d84f-qjlv8   1/1     Running   0          40m
pod/first-react-deployment-5cb789d84f-s7kgl   1/1     Running   0          40m
pod/first-react-deployment-5cb789d84f-sztsd   1/1     Running   0          40m

NAME                               TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
service/kubernetes                 ClusterIP      10.152.183.1     <none>           443/TCP        101m
service/react-deployment-service   LoadBalancer   10.152.183.230   192.168.26.133   80:32751/TCP   33s

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/first-react-deployment   5/5     5            5           40m

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/first-react-deployment-5cb789d84f   5         5         5       40m

Alternatively you can also type for an exhaustive output.

microk8s kubectl get all --all-namespaces
Inspect a single pod
kubectl describe pod first-react-deployment-5cb789d84f-5jf47

Replace the pod name with your own.

Done!

Hurray! You have finished deploying and exposing your first React App and scaled it to 5 instances. Kubernetes will also load balance it for you automatically.

Check your site by typing the following since this is your Load Balancer IP address and is exposed on port 80.

curl http://10.152.183.230

Check out this line from the output – service/react-deployment-service LoadBalancer 10.152.183.230 192.168.26.133 80:32751/TCP 33s

Since you site is also exposed to external machines on port 32751 on the network, you can type this from an external machine on the network.

curl http://192.168.26.133:32751

Conclusion

Yes! we learnt how to host your React Docker app on NGINX inside Docker and Kubernetes. Hope this helps you. If you have questions you can send them to code@onezeroeight.co

Leave a Reply

Your email address will not be published. Required fields are marked *