Previously in part 1, we have already done with manual deployment our web stack to AWS EC2
, yeah I mean manual
, by that means we have to log into
EC2
then pull the code, then restart pm2
everytime we want to deploy.
There are many tools and platforms that can be used to implement automated deployment, such as Jenkins
, Travis CI
, GitHub Actions
, and AWS CodeDeploy
. These tools typically integrate with version control systems like Git and can be configured to trigger deployment workflows based on events such as code commits, pull requests, or scheduled builds. We will pick GitHub Actions
, it would be good foundation to learn about GitHub Actions
for upcoming post. To learn more about actions, we can try multiple approaches:
GitHub Actions Script Execution on Remote Server
TL;DR:
- Create an
.sh
file inAWS EC2
project folders. - Configure
GitHub Actions
tossh
to EC2 and run.sh
script file
Create .sh
file in EC2 instace
Navigate to project folder and create .sh
file for Backend
ubuntu@ip-172-31-18-43:~/apps$ cd apps/ec2-app
ubuntu@ip-172-31-18-43:~/apps/ec2-app$ sudo nano deploy-be.sh
Let's try to put something to do a test run:
#!/bin/bash
echo "Hello from the deploy script!"
Save and close the file Control + O => Control + X
, add executable permission:
ubuntu@ip-172-31-18-43:~/apps/ec2-app$ chmod +x deploy-be.sh
Run the script:
ubuntu@ip-172-31-18-43:~/apps/ec2-app$ ./deploy-be.sh
Hello from the deploy script!
Ok, we're doing well.
Configuring Github Actions
First we must set secrets for action, navigate to repository setting page, select Secrets and Variables >> Actions
![](https://res.cloudinary.com/ddceq9zjs/image/upload/v1700019896/deploy-web-stack-aws-ec2/qoovmlaqbmn8xmhht4ct.webp)
How do we get these data ? Copy SSH
connect command from EC2
instance, example:
ssh -i "ec2-exp.pem" [email protected]
SERVER_SSH_KEY
: open.pem
pair-key file when you first createEC2
instance and copy its contentSERVER_USERNAME
: left part of@
in command, should beubuntu
.SERVER_HOST
: right part of@
in command, should beec2-18-141-207-114.ap-southeast-1.compute.amazonaws.com
.
With that, we're set to go, create a yaml
file in .github/workflows
folder.
name: Deploy AWS EC2
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy to EC2 with executable SH
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Checkout code and SSH into instance and run script
env:
PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
USER_NAME: ${{ secrets.SERVER_USERNAME }}
HOSTNAME: ${{ secrets.SERVER_HOST }}
run: |
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} '
# Now we have got the access of EC2 and we will start the deploy .
cd /home/ubuntu/apps/ec2-app/
./deploy-be.sh
'
Don't forget to push yaml
to repository. Now the actions we just created will run everytime we push to ec2-sh-executable
. Noted that this is only for Backend, for Frontend we also need to create the same yaml
and set secrets
![](https://res.cloudinary.com/ddceq9zjs/image/upload/v1699952711/deploy-web-stack-aws-ec2/u8ihvbf1lv72k4i3tzzq.webp)
Our action is on at Actions
page, let's make some changes to see how it runs
![](https://res.cloudinary.com/ddceq9zjs/image/upload/v1699975065/deploy-web-stack-aws-ec2/xcgqaqz5m1zgjxvxbmpy.webp)
OK great, now we can run sh
script from inside actions, we just need to update bash script to run
redeployment process.
Update .sh
script
#!/bin/bash
echo "starting deployment process"
# pull latest source code
echo "get latest srouce code"
cd be
git pull origin main
# do a clean installation of all depepdencies
echo "instaling dependencies"
npm ci
# restart application
echo "restart application"
pm2 restart be
echo "deployment process completed"
Nothing fancy here, we just run some command lines for:
- pulling latest source code
- do a clean dependency installation
- restart
pm2
process for application
We will perform change in repository one last time to see how actions run.
![](https://res.cloudinary.com/ddceq9zjs/image/upload/v1700033724/deploy-web-stack-aws-ec2/utjqzrewh03p05hq8qc3.webp)
Let's also create one for Frontend:
nano touch deploy-fe.sh
cat deploy-be.sh >> deploy-fe.sh # copy and paste content since they're similiar
Update command accordingly for Frontend
#!/bin/bash
echo "starting deployment process"
# pull latest source code
echo "get latest srouce code"
cd fe
git pull origin main
# do a clean installation of all depepdencies
echo "instaling dependencies"
npm ci
# create build
echo "building frontend"
npm run build
# restart application
echo "restart application"
pm2 restart fe
echo "deployment process completed"
GitHub Action Runner Integration with EC2
This approach feels more methodical compared to previous one as you don't manually write command lines in actions
to run .sh
file, I feel like we should explode possible ways to do this, might come in handy later.
Set up Github Action Runners
Git Action Runner
acts as a link between our GitHub repository and the EC2 instance. This integration allows direct interaction between the two and enables automated build, test, and deployment processes.
To download and configure a Git Action Runner on our EC2 instance:
- Go to the GitHub repository and click on Settings.
- On the left-hand sidebar, click on Actions then select Runners.
- In the Runners page click on the New self-hosted runner button.
![800](https://res.cloudinary.com/ddceq9zjs/image/upload/v1700037123/deploy-web-stack-aws-ec2/ponelyp0pedbyumwjkco.png)
Note: While running the command, it may prompt some setup questions, we can simply press Enter to skip to the default options.
After running the ./run.sh command, If the agent returns a ✅ Connected to GitHub message, it indicates a successful installation.
Next, we’ll install a service to run our runner agent in the background:
sudo ./svc.sh install
sudo ./svc.sh start
The above code will start our runner service in the background, making it ready to execute workflows whenever triggered.
Create yaml
file
Create .yaml
file in .github/workflows
content with the commands below:
name: AWS EC2 CI/CD
on:
push:
branches:
- main
jobs:
build:
runs-on: self-hosted
strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: pm2 restart fe
As we can see, when workflow runs, git will connect directly to our project folder at EC2
then we run command with run
one after others.
Resource: