Continuous Deployment with GitHub Actions

Continuous Deployment with GitHub Actions

I decided to take my previous learning of GitHub Actions a bit further to deploy my application on a bare metal Linux server i.e. Continuous Deployment.

Basically following is what I wanted to achieve:

  1. Build and push latest images of my application to a container repository
  2. Docker-compose up the latest images from my server

The first step I had already done in my previous post. For the second step, I wanted to do it manually and then automate that process later.

Following is the manual step I was doing to run the applications on my server:

  1. Use SCP to copy docker-compose.yml file into my remote server from local
  2. SSH login into my server
  3. Docker login to ghcr.io repository where my images were pushed
  4. Docker-compose up the copied docker-compose.yml file

Thanks to already built GitHub Actions available in the marketplace, I was able to automate the above manual process:

name: CD

on:
  push:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  REPO: my-repo
  BE: backend
  FE: frontend
  ORG: my-org
  QA_DC: docker-compose-qa.yml

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Log in to the Container registry
      uses: docker/login-action@v1
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ github.token }}

    - name: Build Backend
      uses: docker/build-push-action@v2
      with:
        context: ./${{ env.BE }}
        push: true
        tags: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.REPO }}/${{ env.BE }}:latest,${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.REPO }}/${{ env.BE }}:${{ github.run_number }}

    - name: Build Frontend
      uses: docker/build-push-action@v2
      with:
        context: ./${{ env.FE }}
        push: true
        tags: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.REPO }}/${{ env.FE }}:latest,${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.REPO }}/${{ env.FE }}:${{ github.run_number }}

    - name: Copy files to server
      uses: appleboy/scp-action@master
      with:
        host: ${{ secrets.QA_HOST }}
        username: ${{ secrets.QA_USERNAME }}
        password: ${{ secrets.QA_PASSWORD }}
        port: ${{ secrets.QA_PORT }}
        timeout: 120s
        rm: true
        source: "${{ env.QA_DC }}"
        target: "/${{ env.REPO }}"

    - name: Deploy to server
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.QA_HOST }}
        username: ${{ secrets.QA_USERNAME }}
        password: ${{ secrets.QA_PASSWORD }}
        port: ${{ secrets.QA_PORT }}
        script: |
          echo ${{ secrets.GHCR_TOKEN }} | docker login ${{ env.REGISTRY }} -u ${{ secrets.GHCR_USERNAME }} --password-stdin
          cd /${{ env.REPO }}
          docker-compose -f ${{ env.QA_DC }} pull ${{ env.FE }} ${{ env.BE }}
          docker-compose -f ${{ env.QA_DC }} up -d --build

The file is pretty much self-explanatory. Using env variables made the script a bit more reusable which I can just copy-paste into my another CD build. secrets are something you would have to set manually in GitHub on organization or repository level. GitHub gives you a complete solution with Github Actions (CI/CD), Github Container Repository (ghcr.io) and Secrets Management as well.