Tutorial: Self-Hosted Node.js App Deployment on AWS EC2 or On-Premise and other Cloud Servers provider with CI/CD
II. Create and attach Elastic IPs to EC2
III. Create Github Action Workflow
IV. Managing Secrets and Variables in GitHub Actions
V. Prepare Dockerfile and docker-compose-ssh.yml
VI. Ready for deploy
VII. Setup Security Group to open port for testing
VIII Setup Portainer for monitor, manage and log on Docker Containers
XI Cloudwatch Alarm CPU High and send email (Youtube) (AWS Only)
XII Promethus + Grafana to monitor CPU, RAM, etc (AWS or Any Cloud Provider like ViettelHost , Vietnix)
9. Key pair (login). Click create new key pair. This will use for login to server and using in CI/CD
17. Click on Advanced details. To setup terminate protection, Cloudwatch Detailed Monitoring and UserData (For preinstall some of software we need)
Add this content below:
#!/bin/bash
sudo apt update -y
# Install Docker using Snap
sudo snap install docker
# Grant permission for run Docker without root user
sudo chmod 666 /var/run/docker.sock
# Install Nginx proxy manager
sudo docker run -d \
--name app \
--restart unless-stopped \
-p 80:80 \
-p 443:443 \
-p 81:81 \
-v ./data:/data \
-v ./letsencrypt:/etc/letsencrypt \
jc21/nginx-proxy-manager:latest
Attach Static IP (May be in other cloud platform in Vietnam, Static IP will be available by default)
This code is a GitHub Actions workflow that automates the deployment of a project to a server in a staging environment. Let's go through the code step by step to understand its functionality:
-
The code starts by defining the environment variables. The
APP_ENV
variable is set to the value stored in theSTAGING
secret. -
The workflow is triggered when a push event occurs on the
main
branch. -
The
build-and-deploy
job is defined, which runs on anubuntu-latest
machine. -
The steps of the job are as follows:
a. Pull code: This step uses the
actions/checkout
action to fetch the latest code from the repository.b. Extract env file multi line: This step creates an environment file (
.env
) based on theAPP_ENV
value. It removes any existing.env
file, creates a temporary file (.env_temp
), writes theAPP_ENV
value to it, converts spaces to newlines, and appends the contents to the final.env
file. Finally, it removes the temporary file.c. Docker: This step builds a Docker image for the project using the
docker build
command. It then saves the image as a tar file (your-project-name.tar
).d. Copy Docker image to ec2 use SSH Key: This step uses the
appleboy/scp-action
action to copy the Docker image tar file and thedocker-compose.yml
file to the target server. It connects to the server using SSH, specified by theSTAGE_SERVER_HOST
,STAGE_SERVER_KEY_SSH
, and other credentials stored as secrets.e. Executing remote ssh commands using SSH Key: This step uses the
appleboy/ssh-action
action to execute remote SSH commands on the target server. It connects to the server using SSH and runs the following commands:sudo docker system prune -a -f
: Cleans up any unused Docker resources on the server.cd ~/your-project-name
: Changes the directory to the project directory on the server.sudo snap install docker
: Installs Docker on the server (if not already installed).sudo docker load --inputnestjs-22.tar
: Loads the Docker image from the tar file.sudo docker-compose up -d --force-recreate
: Starts the project using Docker Compose, recreating containers if necessary.rm -rfnestjs-22.tar
: Removes the Docker image tar file from the server.
This workflow automates the process of building a Docker image, copying it to a server, and deploying the project using Docker Compose. It ensures consistency and simplifies the deployment process in a staging environment.
4. Copy workflow content from file deploy.staging.yml in folder .github/workflows and paste to input
Currently, we will set up for the following keys:
- secrets.STAGING
- secrets.STAGE_SERVER_HOST
- secrets.STAGE_SERVER_KEY_SSH
Note that all of secret key in Github Action workflow must set up on Settings for each repo before use it in Github Action.
14. Type "Your server key ssh copy from file pem. Must use vscode for copy key properly, don't use notepad or text editor"
Both file are available in this repository.Make sure your image name is the same as tar file name, which created by docker build command in Build Stage in Github Action. You can see some picture below for more detail
After complete all, we can merge pull request set up github action and ready for deploy to Server EC2.
https://docs.portainer.io/start/install-ce/server/docker/linux#deployment
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
http://localhost:9000 or http://<ip_address>:9000
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl &&\
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg &&\
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list &&\
sudo apt update -y &&\
sudo apt install caddy -y
sudo setcap CAP_NET_BIND_SERVICE=+eip $(which caddy)
caddy stop
caddy start
Using the Service If using a Caddyfile, you can edit your configuration with nano, vi, or your preferred editor:
sudo nano /etc/caddy/Caddyfile
kuma.thienlam3.line.pm {
reverse_proxy localhost:3001
}
Press CTR+O, Press Enter to save, and press CTRL + X to return back to terminal
You can place your static site files in either /var/www/html or /srv. Make sure the caddy user has permission to read the files.
To verify that the service is running:
sudo systemctl status caddy
The status command will also show the location of the currently running service file.
When running with our official service file, Caddy's output will be redirected to journalctl. To read your full logs and to avoid lines being truncated:
journalctl -u caddy --no-pager | less +G If using a config file, you can gracefully reload Caddy after making any changes:
sudo systemctl reload caddy
0 0,12 * * * sudo certbot renew --nginx --post-hook "systemctl reload nginx"
0 0,12 * * * curl -X POST -H 'Content-Type: application/json' "https://chat.googleapis.com/v1/spaces/AAAAKiLkcjo/messages?key=aaaa&token=123" -d '{"text": "'"$(sudo certbot renew --nginx)"'"}'
Save this file
caddy reload
I will still keep tutorial about Nginx, HTTPS and SSL
Create a file in folder /etc/nginx/site-enabled/your_domain_name And paste this content
server {
listen 80;
server_name test.thienlam3.line.pm;
return 301 https://test.thienlam3.line.pm$request_uri;
}
#Point test.thienlam3.line.pm to port 3002
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name test.thienlam3.line.pm http://test.thienlam3.line.pm;
location / {
proxy_pass http://localhost:3002;
client_max_body_size 5M;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}
Save this file, then using certbot to auto generated ssl for above config
sudo certbot --nginx
After choose suitable domain, then reload Nginx to take effect
sudo service nginx reload
Check nginx status again
sudo service nginx status