Run CI/CD pipeline with Github action. Let’s build your own automated deployment on EC2 with Github and Docker.

TL;DR

You would know how to do the following after reading.

  • How to build docker image and push it to docker hub with Github action.

  • How to ssh to your EC2 instance with Github action.

    aws-ec2-ci-cd-structure.png


What is CI/CD

CI/CD(Continuous Integration and Continuous Deployment). It is a software engineering practice that aims to reduce the time between writing code and actually releasing it to users. This is achieved through automation of the build, test, and deployment process. In a CI/CD pipeline, code changes are automatically built, tested, and deployed to production.


Why Github Action

GitHub Actions is a CI/CD tool provided with GitHub. It allows us to automate tasks related to their projects, such as building code, running tests, and deploying code to production. Github provides the remote server for us to running the tasks. Github action workflows can be triggered by a variety of events, such as pushing code to a branch, opening a pull request, or a schedule.


SSH Connection to Remote Server

An SSH connection refers to the process of establishing a secure, encrypted connection to a remote machine using the SSH protocol. User could establish an SSH connection with a public key and private key pair to access the remote machine.


Hands-on

Now, let’s get start to make the thing happened! Hands-on source code could be found in this repo, you could clone it to your local machine and by yourself.

git clone https://github.com/yusianglin11010/github-action-ec2
  • launch an EC2 instance
  • remember to start docker service!
sudo service docker start

Generate SSH Key

  • Generate ssh key pair for connection
    • For this demo, we leave all ssh-keygen options with default value
    • All you need is press “enter” until the key generated
ssh-keygen
  • Add public key to authorized_keys
cat id_rsa.pub > authorized_keys
  • Add the following content to Github secrets, you could follow this post for setting Github action secret
    • ssh private key
    • AWS EC2 hostname
    • AWS EC2 user name
    • Docker Hub user name
    • Docker Hub user password

Add Github Action File

  • Add the following script in ./github/workflows/workflow.yaml
name: aws-ci-cd

on:
  push:
    branches:
      - 'main'

jobs:
  CI:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v3
      -
        name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      -
        name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Build and Push Image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: devinlin11010/github-action-ec2:latest
  CD:
    needs: docker
    
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2 
      - name: Deploy in EC2
        env:
            PRIVATE_KEY: ${{ secrets.AWS_PRIVATE_KEY  }}
            HOSTNAME : ${{ secrets.AWS_HOSTNAME  }}
            USER_NAME : ${{ secrets.AWS_USER  }}
            
        run: |
          echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
          ls -a
          cat 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
            docker pull devinlin11010/github-action-ec2:latest
            docker stop nginx && docker rm nginx
            docker run -p 80:80 -d --name="nginx" devinlin11010/github-action-ec2:latest
          '

Hold on…let’s break down the code

  • Firstly, we need to decide when would the workflow be triggered
    • we want the workflow be triggered when some change was push to main branch
on:
  push:
    branches:
      - 'main'
  • CI Job
    • This job section would build docker image and push it to your docker hub
    • We would use the github action published by docker community to complete this task
    • The most significant parts are:
      • context we need to identify our Dockerfile
      • push determine if you want to push image to docker hub
      • tag add your image tag for pushing
name: Build and Push Image
uses: docker/build-push-action@v4
with:
  context: .
  push: true
  tags: devinlin11010/github-action-ec2:latest
  • CD Job

    • Note that the CD job has a dependency on CI job(because we want to deploy the latest image), so we have to add needs: CI to tell the Github action that CD should be executed after CI completed
    CD:
        needs: CI
    
    • Remembered that we need to write our private key content to a private key file, which is your environment variable PRIVATAE_KEY
    echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
    ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} '
    
    • Connect to EC2 with this private key as identifier
    • Now you could run the command you want to run on EC2
      • In the EC2, we first pull the latest image the just pushed to docker hub in CI stage
      • Then we may need to delete the existed hosting image on the instance
      • Finally run your latest image
    ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} '
    
                #Now we have got the access of EC2 and we will start the deploy
                docker pull devinlin11010/github-action-ec2:latest
                docker stop nginx && docker rm nginx
                docker run -p 80:80 -d --name="nginx" devinlin11010/github-action-ec2:latest
              '
    

Conclusion

Congrats🎉🎉 Now you could automatically deploy your code to AWS EC2. With the Github action, the process deployment would be much easier. You don’t need to manually deploy the code after some feature have made or changed. If you don’t want to build docker image to docker hub, we actually could connect to instance and fetch the Github repo then build your service. I would post this method in the near feature. Hope you enjoy this article and hands-on, feel free to reach me if you encounter any problem😉.

References

https://phoenixnap.com/kb/ssh-with-key#ftoc-heading-3

https://farhan-tanvir.medium.com/ci-cd-from-github-to-aws-ec2-using-github-action-e18b621c0507