Deploy a React application to Kubernetes in 5 easy steps
Tags: react, kubernetes, docker, tutorial
Published: 2019-07-09 09:55:50

Kubernetes is currently the de-facto standard for deploying applications in the cloud. Every major cloud provider offers a dedicated Kubernetes service (eg. Google Cloud with GKE, AWS with EKS, etc.) to deploy applications in a Kubernetes cluster. 

There are many reasons for choosing Kubernetes for deploying your React application:

* unified and standardized deployment model across the cloud providers
* robustness against downtime as several containers are deployed (horizontal scaling)
* handling peak traffic with auto-scaling
* zero-downtime deployments, canary deployments, etc.
* simple A/B testing

With this post, I'll demonstrate how to deploy a React application to a Kubernetes cluster with five easy steps. To follow the article you just need basic Docker knowledge. 

As we all want quick feedback while learning a new topic I'll use a local Kubernetes cluster for this example. You can spin up this local Kubernetes cluster in minutes if you have a Docker engine with Kubernetes support installed on your machine. This is available since version **18.06.0** for both Docker Desktop for [Windows](https://docs.docker.com/docker-for-windows/kubernetes/)/[Mac](https://docs.docker.com/docker-for-mac/kubernetes/).

Let's get started...


### Step 1: Create the React application

I start off with a fresh React application created with `create-react-app` from [Facebook](https://github.com/facebook/create-react-app):

```shell
➜ node -v
v10.16.0
➜ npm -v
6.9.0
➜ npx create-react-app react-app-kubernetes
npx: installed 91 in 5.787s

Creating a new React app in /Users/rieckpil/Desktop/react-app-kubernetes.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts...

...

Happy hacking!
```

This will create all the required configuration and files to start developing a new React application.

Make sure you are able to start and access the React application on your local machine at [http://localhost:3000](http://localhost:3000) using `npm start`. For the further sections, we need the optimized production build of the React application which is created with:

```shell
➜ npm run-script build
> react-app-kubernetes@0.1.0 build /Users/rieckpil/Desktop/junk/react-app-kubernetes
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  36.44 KB  build/static/js/2.b41502e9.chunk.js
  762 B     build/static/js/runtime~main.a8a9905a.js
  602 B     build/static/js/main.28647029.chunk.js
  517 B     build/static/css/main.2cce8147.chunk.css

...
```

If you have an existing React application you can skip this section and continue with the next one. Just make sure you have the production build of your application available at `build/`.

### Step 2: Dockerize the React application

For deploying the React application to Kubernetes, we need to package it inside a container. Kubernetes supports several container engines, but Docker is currently the most adopted one.

To create a Docker container we need a `Dockerfile` at the root level of our React application folder. This file defines what OS is used, how it is configured and what other applications are running inside this container.

For this example, I'm using [nginx](https://www.nginx.com/) to serve the content of the React application and the simplest `Dockerfile` looks like the following:

```Dockerfile
FROM nginx:1.17
COPY build/ /usr/share/nginx/html
```

Our Docker container inherits everything from the official `nginx:1.17` Docker image and just copies the React production build to the container.

To speed up the creation of the Docker container, make sure you add a `.dockerignore` to  your project to exclude the `node_modules` from being sent to the Docker context:

```
node_modules
```

Once you have your React production build in place, start your Docker engine and execute the following command to create the Docker image:

```shell
➜ docker build -t my-react-app .
Step 1/2 : FROM nginx:1.17
1.17: Pulling from library/nginx
fc7181108d40: Pull complete
d2e987ca2267: Pull complete
0b760b431b11: Pull complete
Digest: sha256:96fb261b66270b900ea5a2c17a26abbfabe95506e73c3a3c65869a6dbe83223a
Status: Downloaded newer image for nginx:1.17
 ---> f68d6e55e065
Step 2/2 : COPY build/ /usr/share/nginx/html
 ---> 35d48f28a918
Successfully built 35d48f28a918
Successfully tagged my-react-app:latest
```

For a more production-ready Docker container, make sure you customize the nginx configuration for your needs.

### Step 3: Connect to your Kubernetes cluster

To start your local Kubernetes cluster, open the Docker Desktop preferences and switch to the Kubernetes tab:

![](https://thepracticaldev.s3.amazonaws.com/i/jaof9knje650wpielfd1.png)

Here you can enable the Kubernetes support. If you enable it for the first time, the cluster creation **may take some time**, as several Docker images are downloaded in the background.

Once your local Kubernetes cluster is running, connect to it via `kubectl` (if missing, download [here](https://kubernetes.io/docs/tasks/tools/install-kubectl/)):

```shell
➜ kubectl config use-context docker-for-desktop
Switched to context "docker-for-desktop".
➜ kubectl get nodes
NAME                 STATUS    ROLES     AGE       VERSION
docker-for-desktop   Ready     master    3d        v1.10.11
➜ kubectl cluster-info
Kubernetes master is running at https://localhost:6443
KubeDNS is running at https://localhost:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

With this Kubernetes support of Docker Desktop you'll get a local single node *cluster* which is nice for evaluation and learning purposes. 

If you want to deploy your React application to a Kubernetes cluster in the cloud, start here:

* Google Kubernetes Engine (GKE) [quickstart](https://cloud.google.com/kubernetes-engine/docs/quickstart)
* Azure Kubernetes Engine (AKS) [quickstart](https://azure.microsoft.com/de-de/services/kubernetes-service/)
* AWS Kubernetes Engine (EKS) [quickstart](https://aws.amazon.com/de/eks/)
* Oracle Container Engine for Kubernetes (OKE) [quickstart](https://docs.cloud.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingclusterusingoke.htm)

### Step 4: Upload the Docker image to your container registry


To be able to pull the Docker image within the Kubernetes cluster, we need to upload the image to a **Docker registry**. For a cloud deployment, you usually get a dedicated registry from the cloud provider to upload your image. As we are deploying the application to a local cluster, we need a local Docker registry.

You can create a local Docker registry with the following statement:

```shell
➜ docker run -d -p 5000:5000 --restart=always --name registry registry:2
```

For uploading our previously created React Docker image, we have to *tag* the image with the hostname and port of the registry:

```shell
➜ docker tag my-react-app localhost:5000/my-react-app
```

And can now finally push the image to our Docker registry:

```shell
➜ docker push localhost:5000/my-react-app
The push refers to repository [localhost:5000/my-react-app]
9d9745936581: Pushed
d2f0b6dea592: Pushed
197c666de9dd: Pushed
cf5b3c6798f7: Pushed
latest: digest: sha256:66c94bdba6b06d1964a764cd14dc97d8adf202b02ab6e6fbd10b23ad4a8554a1 size: 1158
```

Now we are ready to deploy the application to the Kubernetes cluster.

### Step 5: Deploy the React application

Usually, every Kubernetes object (`service`, `pod`, `deployment`, etc.) is described in a `.yaml` file but `.json` is also possible. 

For deploying the React application to Kubernetes we need a so-called `deployment`. This Kubernetes entity makes sure our application will have as many replicas (parallel pods) as we define. In addition, we can define the Docker image we want to use, what ports are used and further metadata for our application:

```yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: my-react-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-react-app
  template:
    metadata:
      labels:
        app: my-react-app
    spec:
      containers:
        - name: my-react-app
          image: localhost:5000/my-react-app
          imagePullPolicy: Always
          ports:
            - containerPort: 80
      restartPolicy: Always
```

With just the `deployment` we wouldn't be able to access our application from outside. To expose applications, Kubernetes offers a so-called `service`. Using a `service` we can define which ports to expose to the cluster/outside. 

There are several different types of a Kuberntes `service` and I'm choosing the simplest one for your example: `NodePort`. This type will expose a defined port on every node in our Kubernetes cluster (with the local Kubernetes cluster, we just have one node) and map it to an application's port:

```yaml
kind: Service
apiVersion: v1
metadata:
  name: my-react-app
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
      nodePort: 31000
  selector:
    app: my-react-app
```

Both the `service` and `deployment` can be added to a single `.yaml` file with a `---` for separation. In our example, I've created one `deployment.yaml` file to store both:

```yaml
kind: Deployment
apiVersion: apps/v1
# more ...
---
kind: Service
apiVersion: v1
# more ...
```

You can now use this file to deploy your application to Kubernetes with:

```shell
➜ kubectl apply -f deployment.yaml
deployment.apps "my-react-app" created
service "my-react-app" created
```

And can check that everything is running using:

```shell
➜ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
my-react-app-6c8b5c4759-fgb2q   1/1       Running   0          7m
my-react-app-6c8b5c4759-jrzzj   1/1       Running   0          7m
➜ kubectl get deployment
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
my-react-app   2         2         2            2           7m
➜ kubectl get service
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP        3d
my-react-app   NodePort    10.99.224.141   <none>        80:31000/TCP   7m
```

Once everything is up and running, visit [http://localhost:31000](http://localhost:31000) on your machine and you should see your React application, now served from a Kubernetes cluster:

![](https://thepracticaldev.s3.amazonaws.com/i/p5h2kvp8m8u13lergvge.png)

To remove the React application from your Kubernetes cluster, just run 

```shell
➜ kubectl delete service,deployment my-react-app
service "my-react-app" deleted
deployment.extensions "my-react-app" deleted
```

You can find the whole codebase for this example on [GitHub](https://github.com/rieckpil/blog-tutorials/tree/master/react-app-kubernetes).

For further React related posts, visit my [blog](https://rieckpil.de):

* [Preview PDF files with React
](https://rieckpil.de/howto-pdf-preview-with-react/)
* [MicroProfile JWT Authentication with Keycloak and React
](https://rieckpil.de/howto-microprofile-jwt-authentication-with-keycloak-and-react/)
* [Up- and download files with React and Spring Boot](https://rieckpil.de/howto-up-and-download-files-with-react-and-spring-boot/)

Have fun deploying your React applications to Kubernetes,
Phil