Streamline Your Deployment Process: Deploying a React and Node App with GitHub Actions and AWS Elastic Beanstalk
In our previous blog, we delved deep into the YAML syntax, triggers, jobs, and steps that are crucial in defining powerful workflows with GitHub Actions. Building on that foundation, I am back with another exciting blog that will take your development workflows to new heights of automation and efficiency.
Today, we will embark on a journey where we connect a React
app with a Node
app, dockerize
the entire application stack, and deploy it seamlessly using the power of GitHub Actions
and AWS Beanstalk
. By the end of this blog, you will have a comprehensive understanding of how to orchestrate the deployment process effortlessly, regardless of the complexity of your application. Let's dive in and discover the magic of GitHub Actions with practical scenarios and industry-specific applications! Stay tuned and let's get started on this exciting journey. ๐๐ปโจ
Prerequisites
Basic knowledge of react and node.js.
Basic knowledge of docker.
Basic knowledge of git and github.
My previous blog on github actions. Read Here
Basic knowledge of AWS and how to setup an IAM user.(comment if you want me to write a full fledged blog on AWS IAM and Beanstalk)
Set up the React app
Make sure you have Node.js and npm installed on your machine.
Open your command line interface and navigate to the directory where you want to create your React app.
Run the following command to create a new React app:
npx create-react-app my-app
- Once the command completes, navigate into the
my-app
directory:
cd my-app
- Start the React development server by running:
npm start
- You should see your React app running locally at
http://localhost:3000
.
Set up Node.js backend
In the root of your React app (
my-app
directory), create a new directory calledserver
.Navigate into the
server
directory and initialize a new Node.js project:
npm init -y
- Install the necessary dependencies. For example, you can use Express.js as the backend framework:
npm install express
- Create a new file in the
server
directory calledindex.js
and add the following code:
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
app.get('/api', (req, res) => {
res.json({ message: 'Backend API is working!' });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
- Save the file and go back to the root of your React app.
Connect the React app to the backend
Open the
src/App.js
file in your React app.Replace the existing code with the following:
import { useState, useEffect } from 'react';
function App() {
const [message, setMessage] = useState('');
useEffect(() => {
fetch('/api')
.then(response => response.json())
.then(data => setMessage(data.message));
}, []);
return (
<div className="App">
<h1>Hello, World!</h1>
<p>{message}</p>
</div>
);
}
export default App;
- Save the file.
Dockerize the application
In the root of your React app, create a file called
Dockerfile
.Lets build a
Dockerfile
step by step:
# Base image
FROM node:14-alpine
- This specifies the base image for the Docker image. It sets the Node.js version to 14 and the Alpine Linux distribution as the base.
# Set working directory
WORKDIR /app
- This sets the working directory inside the Docker container to
/app
. It means that all subsequent commands and file operations will be performed relative to this directory.
# Copy package.json
COPY package.json ./
- This line copies the
package.json
file from the local directory (where the Dockerfile is located) to the/app
directory inside the Docker image. These files contain information about the project's dependencies.
# Install dependencies
RUN npm install
- This line runs the command
npm install
inside the Docker image. With the working directory set to/app
, it installs the dependencies defined inpackage.json
using the Node Package Manager (NPM).
COPY . .
- This line copies all the files and directories from the local directory to the
/app
directory inside the Docker image. It includes the application source code, configuration files, and any other necessary files.
You must be wondering why copied the package.json file first and then copied all the files. So what happens is this is called layer caching, so when docker builds image using a
Dockerfile
and if there is any change in code itself then docker will just copy the changes and not the things which are same like package.json. This increases speed of creating images.
# Build the React app
RUN npm run build
- This line runs the command
npm run build
inside the Docker image. With the working directory set to/app
, it executes the build script defined inpackage.json
. This typically involves compiling the React app, bundling assets, and generating the production-ready files.
CMD [ "npm", "start" ]
- This line specifies the default command to run when the Docker container is started. It uses the
CMD
instruction to execute the commandnpm start
inside the Docker image. This typically starts the application server or runs any necessary startup tasks.
So basically your Dockerfile
will look like:
# Base image
FROM node:14-alpine
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy app files
COPY . .
# Build the React app
RUN npm run build
# Start the app
CMD [ "npm", "start" ]
- Save the
Dockerfile
.
Set up GitHub repository and actions
Create a new GitHub repository for your project.
Initialize a git repository in your project folder and commit your files:
git init
git add .
git commit -m "Initial commit"
- On GitHub, go to your repository and connect it to your local repository:
git remote add origin <repository-url>
git push -u origin main
Create a new directory called
.github/workflows
in the root of your project.Inside the
.github/workflows
directory, create a new YAML file (e.g.,deploy.yml
).
name: Deploy to AWS Elastic Beanstalk
This is the name of the workflow. It could be any name you choose.
on:
push:
branches:
- main
This specifies the event that triggers the workflow. In this case, the workflow is triggered whenever there is a push event to the main branch.
jobs:
deploy:
runs-on: ubuntu-latest
This declares a job named "deploy" that runs on the latest version of Ubuntu operating system.
steps:
- name: Checkout code
uses: actions/checkout@v2
This checks out the source code from the repository.
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 16
This sets up the Node.js environment with version 16.
- name: Install dependencies
run: npm install
This installs the project dependencies using npm.
- name: Build Docker image
run: docker build -t my-app:latest .
This builds a Docker image with the name "my-app" and the "latest" tag.
- name: Log in to AWS Elastic Beanstalk
uses: aws-actions/amazon-ecr-login@v1
This logs in to AWS Elastic Beanstalk using the AWS CLI.
- name: Deploy to AWS Elastic Beanstalk
run: |
aws configure set region <YOUR_AWS_REGION>
aws configure set aws_access_key_id <YOUR_AWS_ACCESS_KEY_ID>
aws configure set aws_secret_access_key <YOUR_AWS_SECRET_ACCESS_KEY>
aws elasticbeanstalk create-application-version --application-name my-app --version-label ${{ github.sha }} --source-bundle S3Bucket=elasticbeanstalk-us-east-1-1234567890,S3Key=$GITHUB_SHA.zip
aws elasticbeanstalk update-environment --environment-name <YOUR_EB_ENVIRONMENT_NAME> --version-label ${{ github.sha }}
This deploys the application to AWS Elastic Beanstalk by configuring the AWS CLI with the AWS region, access key ID, and secret access key. It then creates a new application version using the source code from the repository and updates the environment to use the new version.
Note: The placeholders
<YOUR_AWS_REGION>
,<YOUR_AWS_ACCESS_KEY_ID>
,<YOUR_AWS_SECRET_ACCESS_KEY>
, and<YOUR_EB_ENVIRONMENT_NAME>
should be replaced with the actual values specific to your AWS environment.
You can see the deploy.yml
file will look like this:
name: Deploy to AWS Elastic Beanstalk
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm install
- name: Build Docker image
run: docker build -t my-app:latest .
- name: Log in to AWS Elastic Beanstalk
uses: aws-actions/amazon-ecr-login@v1
- name: Deploy to AWS Elastic Beanstalk
run: |
aws configure set region <YOUR_AWS_REGION>
aws configure set aws_access_key_id <YOUR_AWS_ACCESS_KEY_ID>
aws configure set aws_secret_access_key <YOUR_AWS_SECRET_ACCESS_KEY>
aws elasticbeanstalk create-application-version --application-name my-app --version-label ${{ github.sha }} --source-bundle S3Bucket=elasticbeanstalk-us-east-1-1234567890,S3Key=$GITHUB_SHA.zip
aws elasticbeanstalk update-environment --environment-name <YOUR_EB_ENVIRONMENT_NAME> --version-label ${{ github.sha }}
Note: Replace
<YOUR_AWS_REGION>
,<YOUR_AWS_ACCESS_KEY_ID>
,<YOUR_AWS_SECRET_ACCESS_KEY>
, and<YOUR_EB_ENVIRONMENT_NAME>
with your own values.
Create and configure the AWS Elastic Beanstalk environment
Go to the AWS Management Console and navigate to Elastic Beanstalk.
Create a new environment and follow the steps to configure it with the appropriate settings.
Make a note of the
<YOUR_EB_ENVIRONMENT_NAME>
you used in the previous step.
Commit and push changes
- Stage and commit the changes to your repository:
git add .
git commit -m "Added Dockerfile and GitHub Actions workflow"
- Push the changes to your GitHub repository:
git push origin main
Trigger the deployment workflow
GitHub Actions detects the push event on the main branch and starts the deployment workflow automatically.
Monitor the workflow in the "Actions" tab of your GitHub repository.
Once completed, your React app with the Node.js backend should be deployed to AWS Elastic Beanstalk.
Congratulations! You have now created a basic React app, connected it with a Node.js backend, dockerized the application, integrated GitHub Actions, and deployed it to AWS Elastic Beanstalk. ๐
Conclusion
In today's blog, we explored a practical example of deploying a React and Node.js application using the CI/CD pipeline offered by GitHub Actions and deploying the app on AWS Elastic Beanstalk. By following the step-by-step guide, you were able to leverage the power of automation and streamline your deployment process. This blog series on GitHub Actions concludes here, but if you want to delve further into the topic or have any specific requests for future content on GitHub Actions, please leave a comment and let me know. I'm always eager to bring you more valuable insights and tutorials.
Happy deploying! ๐๐