Deploying and Scaling the Official Strapi Demo App "Foodadvisor" with Kubernetes
Tags: kubernetes, strapi, app
Published: 2021-10-12 08:26:57

With the constant rise in the number of internet users, the demand for creating highly available and scalable web apps is at an all-time high. Gone are the days when businesses would run their servers on self-managed hardware.

The advent of cloud computing has shifted the gears toward the cloud-native model of software development. Cloud-native is more than just using a cloud provider to serve apps—it's an all-in-one approach that takes full advantage of the cloud computing model.

In a cloud-native deployment, the application is microservice-oriented, and each part of the application is containerized. The containers are orchestrated to ensure they’re scheduled in a way that optimizes resource utilization, which means the application is performant and scales in response to user load. This approach is different from the traditional monolith application that must be scaled and maintained manually.

[Kubernetes](https://kubernetes.io/) is a container orchestration system that is used for automated container deployment, scaling, and management. Thanks to Kubernetes, running a containerized app that scales is a breeze. Multiple worker machines are combined to form a cluster, and each worker machine hosts pods that run the different containers pertaining to the application.

If you're new to Kubernetes, the [official tutorials](https://kubernetes.io/docs/tutorials/) are an excellent starting point.

## Leveraging Kubernetes with a Headless CMS

Content management systems (CMSes) have been around for a long time. Traditionally they’ve been monolithic applications serving content, images, HTML, CSS, and JavaScript all from one place. This made it difficult to reuse content, since the content was tied to the view.

A [headless CMS](https://en.wikipedia.org/wiki/Headless_content_management_system) takes a different approach and separates content from the view. In a headless CMS, content is served through an API consumed by multiple clients responsible for displaying the content. This separation is ideal for cloud-native architecture, as the individual parts can be containerized and scaled independently of each other.

[Strapi](https://strapi.io/) is an open-source tool written with Node.js that offers unparalleled customizations and a super-fast headless CMS. To see firsthand how it performs, Strapi provides an official demo app called [FoodAdvisor](https://foodadvisor.strapi.io/) that can be previewed in Strapi’s website or deployed locally or in the cloud. The FoodAdvisor app shows a listing of different restaurants and serves as a demo of the capabilities of Strapi.

In this tutorial, you'll learn how to leverage Kubernetes to deploy the FoodAdvisor app.

### Prerequisites

To follow along, you'll need a Kubernetes cluster running. You can use a cloud provider that offers a Kubernetes cluster like [Google's GKE](https://cloud.google.com/kubernetes-engine) or [Amazon's EKS](https://aws.amazon.com/eks/), or you can use [Minikube](https://minikube.sigs.k8s.io/) to set up a cluster locally.

This tutorial will assume you're using a local cluster with Minikube. You can install Minikube by following their [guide](https://minikube.sigs.k8s.io/docs/start/).

You will also need to install [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) to control Kubernetes clusters.

If you wish to prepare your own containerized Docker images, you need to also install [Docker](https://www.docker.com/). If you do not want to create your own Docker images, you can use the ones I have prepared.

Make sure the local cluster is running by executing the following command:

```shell
minikube start
```

Minikube will automatically configure `kubectl` to communicate with this local cluster, so no manual configuration is necessary.

## Deploying the FoodAdvisor App Using Kubernetes

Once the Minikube cluster is up and running, you can proceed to deploy the FoodAdvisor app with Kubernetes.

### Preparing the Docker Images

As the first step, you need to clone the FoodAdvisor [GitHub repo](https://github.com/strapi/foodadvisor). This tutorial will be using the version `v2` of the FoodAdvisor app, which [offers some improvements over the previous version](https://strapi.io/blog/foodadvisor-v2).

```shell
git clone https://github.com/strapi/foodadvisor.git
cd foodadvisor
git checkout v2
```

In the repo, you'll see two directories: `api` and `client`. The first directory holds the backend Strapi server as well as the demo data. The `client` directory contains the frontend written in Next.js. The server uses a local SQLite file as the database, so you don't need to set up a separate database to run this app.

After cloning the repo, you will prepare the Docker images for the backend and the frontend. You will need a [Docker Hub](https://hub.docker.com/) account to push the images. If you do not wish to create an account, feel free to skip to [Deploying the Backend](#deploying-the-backend) and use the images I have already prepared.

After creating a Docker Hub account, log in with your username and password by running the `docker login` command.

In the `api` directory, create the following `Dockerfile`:

```Dockerfile
FROM node:14

WORKDIR /app
ADD ./package.json /app
ADD ./yarn.lock /app
RUN yarn install --frozen-lockfile

ADD . /app
RUN yarn seed

EXPOSE 1337

CMD ["yarn", "develop"]
```

Edit the `.env.example` file and set the `CLIENT_URL` variable to `http://foodadvisor.client`. Then, in the same directory, run the following commands:

```shell
docker build -t <your-docker-hub-username>/foodadvisor-api .
docker image push <your-docker-hub-username>/foodadvisor-api:latest
```

Don’t forget to substitute your Docker Hub username.

Now, create the following Dockerfile in the `client` directory:

```Dockerfile
FROM node:16

WORKDIR /app
ADD ./package.json /app
ADD ./yarn.lock /app
ADD ./.env.development /app
RUN yarn install --frozen-lockfile

ADD . /app

EXPOSE 3000

CMD ["yarn", "dev"]
```

Edit the `.env.development` file and set the `NEXT_PUBLIC_API_URL` to `http://foodadvisor.backend` and run the following commands, substituting your Docker Hub username of course:

```shell
docker build -t <your-docker-hub-username>/foodadvisor-client .
docker image push <your-docker-hub-username>/foodadvisor-client:latest
```

Now the Docker images are ready for Kubernetes to use.

## Deploying the Backend

With the Docker images ready, you can start deploying the backend to your Kubernetes cluster. The `kubectl` command will be used to handle communication with the cluster. `kubectl` takes a YAML file, which declares the configuration, and they’re applied to the cluster.

Save the following configuration in `deploy-backend.yaml`:

```YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: foodadvisor-backend
spec:
  selector:
    matchLabels:
      app: foodadvisor
      component: backend
  template:
    metadata:
      labels:
        app: foodadvisor
        component: backend
    spec:
      containers:
      - name: foodadvisor-api
        image: heraldofsolace/foodadvisor-api
        ports:
          - containerPort: 1337
```

If you created your own Docker images in the previous step, change `heraldofsolace/foodadvisor-api` to `<your-docker-hub-username>/foodadvisor-api`.

The YAML file sets up a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) named `foodadvisor-backend`. The deployment creates a [Pod](https://kubernetes.io/docs/concepts/workloads/pods/), which runs one container with your specified Docker image.

Now apply the changes:

```shell
kubectl apply -f deploy-backend.yaml
```

You can verify the deployment by running the following command:

```shell
kubectl get deployments
```

![Screenshot showing the deployments](https://i.imgur.com/q5R5QRF.png)

The backend is now running inside the cluster. But it is isolated from the outside world by default. To be able to send requests to the server from outside the cluster, you need to expose appropriate ports—in this case, port 1337 since this is the port Strapi listens to.

```shell
kubectl expose deployment foodadvisor-backend --port 1337
```

This will create a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) which you can verify by running the following command:

```shell
kubectl get services foodadvisor-backend
```

![Screenshot showing the backend service](https://i.imgur.com/Ix0BzCy.png)

### Deploying the Frontend

The next step is to deploy the frontend and connect it to the backend. Save the following in `deploy-frontend.yaml`:

```YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: foodadvisor-frontend
spec:
  selector:
    matchLabels:
      app: foodadvisor
      component: frontend
  template:
    metadata:
      labels:
        app: foodadvisor
        component: frontend
    spec:
      containers:
      - name: foodadvisor-client
        image: heraldofsolace/foodadvisor-client
        ports:
          - containerPort: 3000
```

Again, if you created your own Docker image, replace `heraldofsolace/foodadvisor-client` with `<your-docker-hub-username>/foodadvisor-client`.

Like the backend, this YAML creates a deployment that contains a pod that runs the specified Docker image.

Apply the changes to deploy the frontend:

```shell
kubectl apply -f deploy-frontend.yaml
```

Verify the deployment by running `kubectl get deployments`.

![Screenshot showing the deployments](https://i.imgur.com/tNTctdo.png)

Similar to the backend, you need to expose port 3000 so that you can use your browser to reach the frontend.

```shell
kubectl expose deployment foodadvisor-frontend --port 3000
```

Verify the service by running `kubectl get services foodadvisor-frontend`.

![Screenshot showing the frontend service](https://i.imgur.com/Qh5Ehzv.png)

### Creating an Ingress

Finally, you'll need to add an [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) to your cluster. The job of the ingress is to route the incoming requests to the frontend or the backend, depending on the URL you use.

The following ingress rule will route all requests made to `http://foodadvisor.backend` to the backend service you created earlier, and all requests made to `http://foodadvisor.client` to the frontend service.

```YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
spec:
  rules:
  - host: foodadvisor.backend
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: foodadvisor-backend
              port:
                number: 1337
  - host: foodadvisor.client
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: foodadvisor-frontend
              port:
                number: 3000
```

Save the YAML to `ingress.yaml`. If this is the first time you’re using ingress on Minikube, you need to enable the addon:

```shell
minikube addons enable ingress
```

Verify that the NGINX Ingress controller is running:

```shell
kubectl get pods -n ingress-nginx
```

![Screenshot showing the NGINX controller](https://i.imgur.com/HdugS2u.png)

You need to run this step only once. Finally, apply the ingress:

```shell
kubectl apply -f ingress.yaml
```

The last step is to make the hostnames `http://foodadvisor.backend` and `http://foodadvisor.client` known to your operating system so that you can open them with your browser. For that, you need to know the IP address of the minikube cluster:

```shell
minikube ip
```

![Screenshot showing the output of minikube ip](https://i.imgur.com/s78d21Q.png)

Edit `/etc/hosts` and add the following lines at the end. Substitute the appropriate IP address from the previous output:

```
192.168.99.100  foodadvisor.client
192.168.99.100  foodadvisor.backend
```

With these changes in place, requests to `http://foodadvisor.backend` and `http://foodadvisor.client` will be sent to your minikube cluster where the ingress controller will pick them up and route them to their appropriate destinations.

Visit `http://foodadvisor.backend/admin` to access your Admin panel. Since this is the first time, you’ll be prompted to create an admin user.

![Screenshot showing the user creation page](https://i.imgur.com/r07LBmH.png)

Once you’ve created an admin user, you can access the Strapi dashboard and explore the sample data that’s been prepopulated.

![Screenshot showing the admin panel](https://i.imgur.com/q6G6NHk.png)

To access the frontend, visit `http://foodadvisor.client`. You should see the homepage of the Foodadvisor app.

![Screenshot showing the FoodAdvisor homepage](https://i.imgur.com/Kn0X6oV.png)

Congratulations, you have successfully deployed Foodadvisor with Kubernetes.

## Conclusion

There’s no doubt that the cloud-native approach is going to be the de facto standard for application development in the near future. This method does away with monolithic applications and instead breaks down an application into scalable, performant microservices. The net result is an easy-to-develop and easy-to-manage application, which is difficult to achieve with a traditional deployment.

This tutorial demonstrated how easily you can get started with Kubernetes to deploy the official Strapi demo app. If this interests you, check out [Strapi's website](https://strapi.io) to learn more about the headless CMS.