22
Deploying a React app using Min.io
In previous posts I had written about how to set up a Kubernetes cluster on self-hosted hardware with the purpose of hosting applications on a Local Area Network (LAN) to use as an intranet of sorts. Today I'll cover how to deploy client-side applications onto Kubernetes cluster which was provisioned.
First, let's talk about how the deployments would be handled on a cloud platform. During the .com days, a server would have to be provisioned to host web applications (i.e. Apache/Nginx). The static website would then have to be copied into a static HTML folder (i.e. /var/www/html
) in order to be served to clients via the web server.
The process of web hosting was improved with the rise of containerization. Instead of having to set up and manage a web server directly on your server hardware, now you can pull a pre-configured image and mount your static web content onto a container which would drastically improve website deployment times.
The rise of cloud platforms furthers the improvement by abstracting away the deployment environment from the developer entirely so that more focus can be placed on the website assets themselves instead of provisioning and configuring servers.
Cloud platforms implement hosted storage using a standard called Object-Based-Storage. Object-based-storage utilizes web endpoints to control and manage assets uploaded to a server. Site content can be managed and served directly using the HTTP protocol. AWS S3 is a perfect example of how object-based-storage works.
Before we can deploy a static website to our Kubernetes cluster, we will first have to provision a Minio server.
The Minio documentation utilizes a Minio Operator and a kubectl krew plugin to provision servers. Utilizing these tools will be covered in a later production release document. For the sake of this tutorial, deploying Minio will be handled with the Minio helm chart
Utilizing Terraform, the Minio server can be deployed to Helm with the following snippet:
provider "helm" {}
resource "helm_release" "rel_minio" {
name = "files"
chart = "minio"
repository = "https://charts.bitnami.com/bitnami"
}
The helm chart can be deployed with the following commands:
terraform plan
terraform apply
Once the helm deployment has been completed, the service will be available from the Kubernetes cluster. In order to interact with the services, the credentials will have to be read in from the Kubernetes secrets which are generated by the helm chart. The following commands retrieve the secrets from Kubernetes and store them in environment variables:
export minio_access_key=$(kubectl get secret files-minio --namespace econovizer -o=jsonpath='{.data.access-key}' | base64 --decode)
export minio_secret_key=$(kubectl get secret files-minio --namespace econovizer -o=jsonpath='{.data.secret-key}' | base64 --decode)
We then have to port-forward the Kubernetes service in order to access it.
kubectl port-forward services/files-minio 9000:9000
Minio provides a CLI called mc which can be utilized to interact with the Minio server. We have to call the mc
tool with the $minio_secret_key
and the $minio_access_key
environment variables we created earlier.
mc alias set local http://127.0.0.1:9000 $minio_access_key $minio_secret_key
With the client configured we can now create a bucket for hosting our static site.
mc mb local/static
Before assets can be served from the bucket, the bucket needs to be configured for public asses.
mc policy set download local/static
With the hosting environment established, we can now create our static website. The easiest way to set up the static website is using Create React App.
npx create-react-app my-app
This command will create a React application with the name my-app
in the current folder. We need to change into the my-app
folder -- cd my-app
. Build the project with the command npm run build
. The build command creates a new folder build
.
With the build folder created, we can deploy the build to our bucket with the mc
command
mc cp -r build/* local/static/
mc ls local/static # list the files which were just uploaded to the local/static bucket
Ideally from this point, you would be able to access the static site from http://localhost:9000/static
, however Minio has a limitation which prevents it from serving up files unless they were referenced directly.
http://localhost:9000/static
will return an XML document containing a ListBucketResult
instead of index.html
. http://localhost:9000/static/index.html
will return the desired web page. Since the URL would end with index.html
, React would be looking for a web root and fail to load.
Fortunately, the issue could be fixed with a proxy application: s3www.
To simplify the deployment of s3www, I created a Terraform template which deploys the following resources to Kubernetes:
- s3www pod deployment
- s3www Kubernetes service
- Ingress which proxies the s3www service
This file can be run with the following commands:
terraform init
terraform plan -var "acces_key=$minio_access_key" -var "secret_key=$minio_secret_key" -var 'namespace=my-namespace' -out deployment.plan
terraform apply deployment.plan
Once Terraform is complete, the React application will be available from your Kubernetes cluster via Ingress. (i.e. http://host.docker.internal/
)
22