Automating Deployment on Virtual Machines

· 546 words · 3 minute read

I have experience where when we want to deploy our service should contact ops team. This is really takes time because sometimes those ops team very busy or need to contact ops team that takes time. So i think we should automate this to improve our SDLC. For this example i use github and github action to accomodate automation flow. Lest start.

  1. First create github token that will we used in our runner. Runner means a ‘virtual machine’ that we used for run testing and build docker image. To access our repository and github action, the runner should have github token. For simplycity we can use personal access token

  2. Put the token into repository secrets.

    secret-ci

  3. Create github action file to run unit test or integration test and build artifact.

    # This workflow will build a golang project
    # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
    
    name: Go
    
    on:
      push:
        tags:
          - "*"
    
    jobs:
      test:
        runs-on: ubuntu-latest
        services:
          dind:
            image: docker:23.0-rc-dind-rootless
            ports:
              - 2375:2375
        steps:
        - name: Checkout Code
          uses: actions/checkout@v4
    
        - name: Set up Go
          uses: actions/setup-go@v5
          with:
            go-version: '1.21'
    
        - name: Test
          run: go test -v ./...
    
      build:
        needs: test
        runs-on: ubuntu-latest
        steps:
        - name: Checkout Code
          uses: actions/checkout@v4
    
        - name: Build and Push Image
          run: |
            docker login --username mnaufala13 --password ${{ secrets.GH_SECRET }} ghcr.io
            docker build . --tag ghcr.io/mnaufala13/learn-ci:${{ github.ref_name }}
            docker push ghcr.io/mnaufala13/learn-ci:${{ github.ref_name }}        
    
  4. Create new repository to save our script and config for the service.

    repo-cd

    0.0.13
    
  5. Create script for deploy the service

    #!/bin/bash
    
    # Check if three arguments are passed
    if [ "$#" -ne 4 ]; then
        echo "Usage: $0 <image> <container_name> <image_tar_path> <config_path>"
        exit 1
    fi
    
    IMAGE=$1
    CONTAINER_NAME=$2
    IMAGE_TAR_PATH=$3
    CONFIG_PATH=$4
    
    # Step 1: Stop the container if it exists
    if docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}$"; then
        echo "Stopping existing container: $CONTAINER_NAME"
        docker stop "$CONTAINER_NAME" && docker rm "$CONTAINER_NAME"
    else
        echo "Container does not exist: $CONTAINER_NAME"
    fi
    
    # Step 2: Load the image from tar file
    echo "Loading image from: $IMAGE_TAR_PATH"
    docker load -i "$IMAGE_TAR_PATH"
    
    # Step 3: Run the container in the background
    echo "Starting container: $CONTAINER_NAME with image: $IMAGE"
    docker run -v "$CONFIG_PATH:/app/config.json" -d --name "$CONTAINER_NAME" "$IMAGE"
    
    # Step 4: Sleep for 5 seconds
    sleep 5
    
    # Step 5: Print the last 10 lines of the container's logs
    echo "Displaying last 10 lines of logs for container: $CONTAINER_NAME"
    docker logs --tail 10 "$CONTAINER_NAME"
    
  6. Insert the ssh user, host, token and github token to secret repository.

    secret-cd

  7. Create github action workflow to run our deployment script.

    # This workflow will build a golang project
    # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
    
    name: Deploy
    
    on:
      push:
        branches: ["main"]
      pull_request:
        branches: ["main"]
    
    jobs:
      Deploy:
        runs-on: ubuntu-latest
        steps:
        - name: Checkout Code
          uses: actions/checkout@v4
    
        - name: Set SSH Key and Permission
          run: |
            mkdir -p ~/.ssh
            echo "${{ secrets.SSH_PRIV_KEY }}" > ~/.ssh/id_rsa && chmod 600  ~/.ssh/id_rsa        
    
        - name: Upload Image and Config
          run: |
            VER=$( cat version.txt )
            IMG="ghcr.io/mnaufala13/learn-ci:$VER"
    
            docker login --username mnaufala13 --password ${{ secrets.GH_SECRET }} ghcr.io
            docker pull $IMG
            docker image save $IMG > image.tar
    
            scp -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa image.tar config.json deploy.sh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/tmp/        
    
        - name: Load and Run Image
          run: |
            VER=$( cat version.txt )
            IMG="ghcr.io/mnaufala13/learn-ci:$VER"
    
            ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "chmod +x /tmp/deploy.sh && /tmp/deploy.sh $IMG thor /tmp/image.tar /tmp/config.json"