Launching your React project as a fully accessible live application doesn’t have to be a challenge. Ever wondered how you could turn your app code into a universally accessible service, all without getting tangled in server configurations? If yes, then you're in the right place.
This article is a beginner-friendly guide that unveils the entire process of containerizing and serving your React application with ease on Cloud Run, a serverless platform that takes away the overhead of managing servers.
Visualize your React frontend and your versatile backend, whether it's Python or another language, all coming together in a fault-tolerant deployment strategy that's robust, yet surprisingly simple to manage.
Dive into this comprehensive step-by-step tutorial on containerizing and deploying your React app using Google Cloud Run. We assume a common tech stack for our example: React.js for the frontend and Python for the backend.
In just a few minutes, you'll learn how to bring your application from your local machine to global availability on the internet, all through the seamless serverless solution offered by Google Cloud Run.
We'll start with some background concepts, architecture, and the approach before diving into the detailed step-by-step deployment instructions, complete with reusable code snippets and necessary commands.
Cloud Run is a Google service that streamlines the process of deploying your applications in minutes. It seamlessly runs your containerized app on Google's cloud infrastructure, handling scaling and management tasks, allowing you to focus on coding without the hassles of server administration.
Cloud Run is ideal for projects like proof-of-concepts, because you can deploy quickly and share a link with customers for immediate feedback. In essence, Cloud Run empowers developers to code more and worry less about infrastructure management.
We use Cloud Run to deploy our React app efficiently, eliminating concerns about the underlying system.
React JS is a JavaScript library for building user interfaces based on components. We selected React JS for our app's frontend for its modular structure, which simplifies updates and maintenance, and enhances the user experience. Its maintainability and flexibility also translate into time and cost savings.
Deployment in software development refers to the process of making the application available to the end users. Deployment is crucial because it enables the app to be utilized in a live environment, allowing real users to interact with it.
Dockerization is the process of packaging an application along with its required libraries, frameworks, and dependencies into a "Docker Container". This helps to eliminate the infamous "it works on my machine" syndrome and aligns with the philosophy of "build once, deploy anywhere" - thus, making the overall development process more robust.
The initial step is to create the Docker file for our backend, and build the Docker image. These packaged software enables applications to run quickly and efficiently. By pushing these Docker images into the Artifact Registry, we gain a secure and scalable storage solution for our Docker images, conveniently in the Google Cloud.
Following this, the next phase involves the creation of a new Cloud Run service. Cloud Run is serverless; it abstracts away all infrastructure management and allows us to focus on writing effective applications. After successfully deploying the backend Docker image in the service, we would be provided with a URL.
This URL replaces the original backend call in the frontend code, ensuring our frontend leverages the cloud-hosted backend services for its operations.
We repeat the Docker process for our frontend file i.e., create a Docker image on the frontend, push it to the Artifact Registry, and then finally create and deploy a Cloud Run service. Upon successful deployment, we will be provided with a URL. This returned URL will serve as the final URL of the application, signifying a successful deployment of the entire application in Cloud Run.
This systematic process ensures the efficient deployment of both frontend and backend services of the application into the cloud, enhancing its accessibility and scalability.
In this architecture, we have a bifurcated setup where the React-based front end and the corresponding backend are separately containerized and deployed as distinct services on Cloud Run - a serverless platform designed for running stateless containers. Here, the frontend delivers the user interface, providing a responsive and dynamic experience, while the backend manages business logic and databases.
Intercommunication between the frontend and the backend is governed by web security policies, including Cross-Origin Resource Sharing (CORS). CORS is a mechanism that adds HTTP headers to tell browsers to allow a web application running at one origin to access selected resources from a different origin. This is crucial for web applications where the frontend and backend are served from different origins, such as different domains, protocols, or ports. CORS authentication is vital to enable authorized cross-origin requests from the frontend to the backend, overcoming blocks by browsers' same-origin policy. This security measure facilitates data exchange between different domains and is implemented in the code where the request is being received.
When the React app (frontend) sends HTTP requests to the backend, it may include parameters that provide context or data necessary for the backend's operations, such as user data or tokens. Upon receiving a request, the backend processes the relevant logic, interacts with databases or other services as required, and then responds to the frontend with the results of the request - which could be data, confirmation messages, error codes, or other status information pivotal to the frontend's subsequent actions.
These interactions are crucial for a responsive and functional application, maintaining a lightweight frontend and a robust backend. The backend's response headers, including those related to CORS, instruct browsers to permit this essential communication, ensuring the integrity of the application.
Fig 1: Architecture
We will be deploying the frontend and backend separately, which is a fault tolerant approach with several advantages:
Overall, deploying the frontend and backend separately in Cloud Run is a recommended best practice for building scalable, resilient, and secure web applications. It promotes modularity, independent development, and efficient resource utilization.
Begin the process by creating a Dockerfile in the root directory of your React application's code. This file outlines the environment and commands necessary to run your app within a Docker container, allowing it to be deployed consistently across any Docker-supported environment.
A Docker container is a lightweight, standalone package that includes everything necessary to run a piece of software, including the code, runtime, system tools, libraries, and settings. The provided code packages your app into an image that's ready to run across any environment supported by Docker, without further configuration. Below is a reusable Dockerfile tailored for most React apps.
Create a file named `Dockerfile` without any extension in the root folder of your frontend project and add the following content:
Dockerfile:
# Use the slim version of the node 14 image as our base
FROM node:14-slim
# Create a directory for our application in the container
RUN mkdir -p /usr/src/app
# Set this new directory as our working directory for subsequent instructions
WORKDIR /usr/src/app
# Copy all files in the current directory into the container
COPY . .
# Set the PYTHONPATH environment variable, which is occasionally necessary for certain node packages
# 'PWD' is an environment variable that stores the path of the current working directory
ENV PYTHONPATH=${PYTHONPATH}:${PWD}
# Set the environment variable for the application's port
# (Be sure to replace '4200' with your application's specific port number if different)
ENV PORT 4200
# Install 'serve', a static file serving package globally in the container
RUN npm install -g serve
# Install all the node modules required by the React app
RUN npm install
# Build the React app
RUN npm run build
# Serve the 'build' directory on port 4200 using 'serve'
CMD ["serve", "-s", "-l", "4200", "./build"]
Let’s break down what each line in the Dockerfile does:
This Dockerfile provides a general template for containerizing React applications. Developers can take this as a starting point and customize environment variables, base images, or commands according to their specific application’s requirements. Remember to test locally to ensure that the Docker container behaves as expected before proceeding to deploy.
For the backend component of your application written in Python, start by placing a Dockerfile at the root directory of the backend code. This will detail the necessary setup for running your Python application within a Docker container, allowing for smooth deployment across various environments. Below is a generalized and reusable Dockerfile for a Python backend.
In the root folder of your backend project, create a file named 'Dockerfile' without any extension and include the following content:
Dockerfile:
# Select a base image that includes Python
FROM python:3.8-slim
# Set up a working directory in the container for your application
WORKDIR /app
# Copy the backend code into the container
COPY . /app
# Install any Python dependencies listed in 'requirements.txt'
RUN pip install --no-cache-dir -r requirements.txt
# Expose the port the app runs on
EXPOSE 5000
# Set the command to run your application
# (Be sure to replace './your_app_script.py' with the relative path to the Python file that starts your application)
CMD ["python", "./your_app_script.py"]
Replace `./your_app_script.py` with your actual main script path. For example, if you use Flask and your main file is `app.py`, you would change the command to `CMD ["python", "app.py"]`.
Explanation of Dockerfile directives for your Python backend:
This Dockerfile template is designed to be broadly applicable to Python backends. However, remember to test it locally before deploying to ensure that your specific application runs smoothly inside the Docker container.
Before proceeding, ensure that Docker is installed on your system. If you do not have Docker installed, you can download and install it from Docker’s official website. Once Docker is set up, you are ready to create a Docker image from your Dockerfile.
docker build -t <image_name> .
This command tells Docker to package your application into an image, which includes everything needed to run it.
Note: Make sure there's a period (.) at the end of the command - it represents the current directory and is the context Docker uses to find the Dockerfile and relevant files.
docker images
You should see `<image_name>` listed in the output. If it's there, congratulations! You've just created a Docker image of your backend application and are one step closer to deployment. Remember to keep the image name relevant and easily identifiable for convenience as you manage multiple Docker images.
The next step in deploying your React app to Cloud Run is to establish a Docker repository within the Google Cloud Artifact Registry. This service allows you to manage, store, and share container images in either a private or public setting.
To begin, navigate to the Artifact Registry section in the Google Cloud Console:
Once you've created the repository, you can push your Docker images there, making them accessible to Cloud Run for deployment. Additionally, the location of your artifact registry should be considered, as deploying your Cloud Run service in the same region can speed up the deployment process and reduce latency for users.
The next step is to upload your Docker images to the repository you've created in the Google Cloud Artifact Registry. This makes your images accessible for Cloud Run deployment.
gcloud builds
submit -t <hostname for docker artifact registry>/<project-name>/<repo-name>/<app-name> ./
Here's what each placeholder represents:
Ensure that there's a period (`./`) at the end of the command, which designates the current directory as the build context for Docker. This command triggers a Cloud Build, packages your application into a Docker image, and uploads it to the specified repository.
Lastly, to confirm successful upload, check your images listed in the artifact registry. If your image appears there, you've successfully completed Step 4, and your image is primed for deployment on Cloud Run.
To deploy your application on Cloud Run:
This will launch a new Cloud Run service using your container image. The process may take a moment. Once complete, Cloud Run will provide a unique URL where your service is accessible.
Fig: 5.1
Fig: 5.2
Once the Cloud Run service successfully deploys your application, the Google Cloud Console will display a unique URL for your service. This URL is the public entry point to your application and can be used to access it from a web browser or API client.
Copy the provided URL after deployment, and save it for later use. Test the URL in a web browser to ensure that your Cloud Run service is accessible and functioning as expected. If you encounter any issues, review the service logs in Cloud Run for troubleshooting, which you can access from the Cloud Run service details page in your Google Cloud Console.
Now that your backend is deployed on Cloud Run with a URL assigned, it's time to configure your frontend to communicate with the newly-hosted service.
Locate the file in your frontend application where API calls to the backend are made. Replace the existing backend URL with the Cloud Run service URL you obtained following the backend deployment.
Note: Remember to save your code changes to ensure the update takes effect.
This step ensures that your frontend will interact with the live version of your backend, meaning any requests made by the frontend will be processed by your application on Cloud Run.
After updating the backend URL in your frontend code, the next step is to deploy the frontend so it can interact with the newly-deployed backend service.
In case the deployment does not succeed, consult the deployment logs for error messages:
Upon successful deployment, a URL will be generated. Use this URL to access and verify the functionality of your application. You have completed the deployment process for your full-stack application on Cloud Run.
With both frontend and backend services now deployed on Cloud Run, you have functional URLs ready for user interaction and API communication.
Congratulations on successfully deploying your first React application on Cloud Run in just minutes!
This quick and efficient process is ideal for testing or demonstrating a Proof of Concept to clients. If this guide has been enlightening about Cloud Run's capabilities, please share it with others who might benefit.
Have questions? Drop a comment below, and thanks for being a part of this journey!