Basic CICD with GitHub Action
Published: 2024-06-22 12:47:24

## Prerequisites

- Virtual Machine with Public IP
- Docker, Docker Compose installed

## Diagram

![Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mob6ht6xul8807vkv712.jpg)

When the developer pushes a commit to the repository, GitHub Action (GA) triggers the pipeline that builds the docker image and pushes it to GitHub Container Registry (ghcr.io). Then, the next job is to connect to the server by SSH, pull the latest Docker image, and restart Docker Compose.

## Walkthrough
**Step 1**: Create a docker-compose file
```yaml
services:
  web:
    image: ghcr.io/yourusername/your-web-app:latest
    ports:
      - "8080:8080"
    environment:
      - DATABASE_HOST=db
      - DATABASE_PORT=5432
    depends_on:
      - db
  db:
    image: ghcr.io/yourusername/your-database:latest
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=mydatabase
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypassword
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:
```
Create a directory that contains the docker-compose file. Save it as DIRECTORY variable.

**Step 2**: Create Repository Secrets


![GitHub Secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fpq69i4ie29dy4pvjmi.png)

Go to Settings → Secrets and variables → Repository secrets → New repository secret.


**Step 3**: Create a Github Workflow

Create `.github/workflows/deploy.yaml` file
```yaml
name: Deploy to VPS
on:
  push:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Set SHA-COMMIT
        id: vars
        run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: . 
          platforms: linux/amd64
          push: true
          tags: |
            ghcr.io/yourusername/your-web-app:latest
            ghcr.io/yourusername/your-web-app:${{ steps.vars.outputs.sha_short }
  deploy:
    needs: build
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Deploy
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          command_timeout: 30m
          script: |
            cd ${{ vars.DIRECTORY }}
            docker compose pull
            docker compose up -d
```
Note: Build and push image job contains context. If you include multiple projects in 1 repository, please specify the correct context

Follow my repository here:

https://github.com/tinhtq/devops-exercise.git

**Step 4**: Test it

Push a commit to this repository and see the result.

![Test Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpw08z66m689s7vhsch6.png)