Difference between revisions of "Github Action using ssh"

From Logic Wiki
Jump to: navigation, search
Line 26: Line 26:
 
== Action ==
 
== Action ==
 
<pre>
 
<pre>
services:
+
name: Deploy:Live:IONOS
  rabbitmq:
+
on:
    image: rabbitmq:3-management
+
   push:
    container_name: rabbitmq
+
     branches:
    ports:
+
       - main
      - "5672:5672"
+
env:  
      - "15672:15672"
+
   ENVIRONMENT: prod
    environment:
+
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
+
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
+
   mongo:
+
     image: mongo
+
    restart: always
+
    environment:
+
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
+
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
+
    ports:
+
       - "27017:27017"
+
    volumes:
+
      - ./data/mongo:/data/db
+
   postgres:
+
    image: postgres:18
+
    restart: always
+
    environment:
+
      POSTGRES_USER: ${POSTGRES_USER}
+
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
+
    healthcheck:
+
      test: ["CMD-SHELL", "pg_isready"]
+
      interval: 1s
+
      timeout: 5s
+
      retries: 10
+
    ports:
+
      - "5432:5432"
+
    volumes:
+
      - ./data/postgres:/var/lib/postgresql/18/main
+
      - ./sql/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql
+
    networks:
+
      - logic_network
+
  
   redis:
+
jobs:
     container_name: redis
+
   build:
     image: redis
+
     runs-on: ubuntu-latest
    restart: always
+
     outputs:
    ports:
+
      RABBITMQ_DEFAULT_USER: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_USER }}
       - "6379:6379"
+
      RABBITMQ_DEFAULT_PASS: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_PASS }}
    volumes:
+
       MONGO_INITDB_ROOT_USERNAME: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_USERNAME }}
       - ./data/redis:/data/redis 
+
      MONGO_INITDB_ROOT_PASSWORD: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_PASSWORD }}
  keycloak:
+
       POSTGRES_USER: ${{ steps.ssm.outputs.POSTGRES_USER }}
    image: quay.io/keycloak/keycloak
+
      POSTGRES_PASSWORD: ${{ steps.ssm.outputs.POSTGRES_PASSWORD }}
     environment:
+
      KEYCLOAK_ADMIN: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN }}
       KC_DB: postgres
+
      KEYCLOAK_ADMIN_PASSWORD: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN_PASSWORD }}
      KC_DB_URL: jdbc:postgresql://postgres:5432/logic_keycloak
+
     steps:
      KC_DB_USERNAME: ${POSTGRES_USER}
+
       - name: Configure AWS credentials
       KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
+
        uses: aws-actions/configure-aws-credentials@v4
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
+
        with:
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
+
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
      KC_HEALTH_ENABLED: true
+
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      KC_LOG_LEVEL: info
+
          aws-region: ${{ secrets.AWS_REGION }}
      KC_HOSTNAME_STRICT: false
+
 
      KC_HTTP_ENABLED: true
+
       - name: Load multiple AWS parameters
     healthcheck:
+
        id: ssm
       test: ["CMD", "curl", "-f", "http://localhost:7080/health/ready"]
+
        run: |
      interval: 15s
+
          PARAMS=$(aws ssm get-parameters \
       timeout: 2s
+
            --names "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username" \
      retries: 15
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password" \
    command: ["start-dev", "--http-port", "7080", "--https-port", "7443"]
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username" \
    ports:
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password" \
    - "7080:7080"
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username" \
    - "7443:7443"
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password" \
    networks:
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username" \
       - logic_network
+
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password" \
    depends_on:
+
            --output json )
      postgres:
+
 
         condition: service_healthy
+
          RABBITMQ_DEFAULT_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username") | .Value')
networks:
+
          RABBITMQ_DEFAULT_PASS=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password") | .Value')
  logic_network:
+
          MONGO_INITDB_ROOT_USERNAME=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username") | .Value')
    name: logic_network
+
          MONGO_INITDB_ROOT_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password") | .Value')
    driver: bridge
+
          POSTGRES_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username") | .Value')
 +
          POSTGRES_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password") | .Value')
 +
          KEYCLOAK_ADMIN=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username") | .Value')
 +
          KEYCLOAK_ADMIN_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password") | .Value')
 +
          echo "::group::Hidden section"
 +
          echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> $GITHUB_OUTPUT
 +
          echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> $GITHUB_OUTPUT
 +
          echo "MONGO_INITDB_ROOT_USERNAME=$MONGO_INITDB_ROOT_USERNAME" >> $GITHUB_OUTPUT
 +
          echo "MONGO_INITDB_ROOT_PASSWORD=$MONGO_INITDB_ROOT_PASSWORD" >> $GITHUB_OUTPUT
 +
          echo "POSTGRES_USER=$POSTGRES_USER" >> $GITHUB_OUTPUT
 +
          echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> $GITHUB_OUTPUT
 +
          echo "KEYCLOAK_ADMIN=$KEYCLOAK_ADMIN" >> $GITHUB_OUTPUT
 +
          echo "KEYCLOAK_ADMIN_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD" >> $GITHUB_OUTPUT
 +
          echo "::endgroup::" 
 +
  deploy:
 +
    runs-on: ubuntu-latest
 +
     needs: build
 +
    steps:
 +
       - name: Checkout repository
 +
        uses: actions/checkout@v4
 +
     
 +
      - name: Set up SSH
 +
        run: |
 +
          mkdir -p ~/.ssh
 +
          echo "${{ secrets.IONOS_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
 +
          chmod 600 ~/.ssh/id_rsa
 +
       
 +
      - name: Test SSH connection
 +
        run: |
 +
        ssh -o StrictHostKeyChecking=accept-new \
 +
              ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }} "echo Connected successfully"
 +
 
 +
      - name: Copy files to remote server
 +
        run: |
 +
          scp -i ~/.ssh/id_rsa -r ./docker-compose-data.yml \
 +
            ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/           
 +
          scp -i ~/.ssh/id_rsa -r ./sql \
 +
            ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/sql
 +
 
 +
       - name: Persist env variables on remote host
 +
        uses: appleboy/ssh-action@v1.2.0
 +
        with:
 +
          host: ${{ secrets.IONOS_REMOTE_HOST }}
 +
          username: ${{ secrets.IONOS_REMOTE_USER }}
 +
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
 +
          script: |
 +
            echo "::group::Hidden section"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='${{ needs.build.outputs.RABBITMQ_DEFAULT_USER }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_PASS='${{ needs.build.outputs.RABBITMQ_DEFAULT_PASS }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_USERNAME='${{ needs.build.outputs.MONGO_INITDB_ROOT_USERNAME }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_PASSWORD='${{ needs.build.outputs.MONGO_INITDB_ROOT_PASSWORD }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_USER='${{ needs.build.outputs.POSTGRES_USER }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_PASSWORD='${{ needs.build.outputs.POSTGRES_PASSWORD }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN='${{ needs.build.outputs.KEYCLOAK_ADMIN }}'\" >> /etc/environment"
 +
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN_PASSWORD='${{ needs.build.outputs.KEYCLOAK_ADMIN_PASSWORD }}'\" >> /etc/environment" 
 +
            echo "::endgroup::"
 +
            echo "Environment updated."
 +
         
 +
       - name: Restart Docker containers on remote server
 +
        uses: appleboy/ssh-action@v1.2.0
 +
         with:
 +
          host: ${{ secrets.IONOS_REMOTE_HOST }}
 +
          username: ${{ secrets.IONOS_REMOTE_USER }}
 +
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
 +
          script: |         
 +
            cd /home/logicmade/bookitfor.me.uk/
 +
            docker compose -f docker-compose-data.yml down
 +
            docker compose -f docker-compose-data.yml up -d --build
 
</pre>
 
</pre>
 +
 +
=== Notes about the Yaml file ===
 +
Build job must output the variables to use them in deploy job. If steps are in the same job there is no need to do that.
 +
 +
To hide echo in the logs I used
 +
<pre>
 +
    echo "::group::Hidden section"
 +
    ......
 +
    echo "::endgroup::"
 +
</pre>
 +
 +
to use sudo with password I used this syntax
 +
echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='${{ needs.build.outputs.RABBITMQ_DEFAULT_USER }}'\" >> /etc/environment"

Revision as of 13:17, 5 December 2025


Introducing github to server

First create a ssh key in the local machine (I named it as "github-deploy" when it asked)

ssh-keygen -t ed25519 -C "github-deploy"

-t ed25519 : encryption algorithm

-C "github-deploy" : puts a comment in public key file

Copy it to server

ssh-copy-id -i ~/.ssh/github-deploy.pub bookit

Create a secret in Github repo and store private key in it.

OR

Create the key on the server and copy private key to github secrets

Server Side

  • Add public key to authorized_keys
  • install docker
  • install docker-compose
  • create folder for deployment

Action

name: Deploy:Live:IONOS
on:
  push:
    branches:
      - main
env: 
  ENVIRONMENT: prod

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      RABBITMQ_DEFAULT_USER: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_USER }}
      RABBITMQ_DEFAULT_PASS: ${{ steps.ssm.outputs.RABBITMQ_DEFAULT_PASS }}
      MONGO_INITDB_ROOT_USERNAME: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_USERNAME }}
      MONGO_INITDB_ROOT_PASSWORD: ${{ steps.ssm.outputs.MONGO_INITDB_ROOT_PASSWORD }}
      POSTGRES_USER: ${{ steps.ssm.outputs.POSTGRES_USER }}
      POSTGRES_PASSWORD: ${{ steps.ssm.outputs.POSTGRES_PASSWORD }}
      KEYCLOAK_ADMIN: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN }}
      KEYCLOAK_ADMIN_PASSWORD: ${{ steps.ssm.outputs.KEYCLOAK_ADMIN_PASSWORD }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Load multiple AWS parameters
        id: ssm
        run: |
          PARAMS=$(aws ssm get-parameters \
            --names "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username" \
                    "/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password" \
            --output json )

          RABBITMQ_DEFAULT_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/username") | .Value')
          RABBITMQ_DEFAULT_PASS=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/rabbitMQ/password") | .Value')
          MONGO_INITDB_ROOT_USERNAME=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/username") | .Value')
          MONGO_INITDB_ROOT_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/mongodb/password") | .Value')
          POSTGRES_USER=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/username") | .Value')
          POSTGRES_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/postgresql/password") | .Value')
          KEYCLOAK_ADMIN=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/username") | .Value')
          KEYCLOAK_ADMIN_PASSWORD=$(echo "$PARAMS" | jq -r '.Parameters[] | select(.Name=="/${{env.ENVIRONMENT}}/bookitfor.me.uk/keycloak/password") | .Value')
          echo "::group::Hidden section"
          echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> $GITHUB_OUTPUT
          echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> $GITHUB_OUTPUT
          echo "MONGO_INITDB_ROOT_USERNAME=$MONGO_INITDB_ROOT_USERNAME" >> $GITHUB_OUTPUT
          echo "MONGO_INITDB_ROOT_PASSWORD=$MONGO_INITDB_ROOT_PASSWORD" >> $GITHUB_OUTPUT
          echo "POSTGRES_USER=$POSTGRES_USER" >> $GITHUB_OUTPUT
          echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> $GITHUB_OUTPUT
          echo "KEYCLOAK_ADMIN=$KEYCLOAK_ADMIN" >> $GITHUB_OUTPUT
          echo "KEYCLOAK_ADMIN_PASSWORD=$KEYCLOAK_ADMIN_PASSWORD" >> $GITHUB_OUTPUT 
          echo "::endgroup::"  
  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.IONOS_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
         
      - name: Test SSH connection
        run: |
         ssh -o StrictHostKeyChecking=accept-new \
              ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }} "echo Connected successfully"

      - name: Copy files to remote server
        run: |
          scp -i ~/.ssh/id_rsa -r ./docker-compose-data.yml \
             ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/            
          scp -i ~/.ssh/id_rsa -r ./sql \
             ${{ secrets.IONOS_REMOTE_USER }}@${{ secrets.IONOS_REMOTE_HOST }}:/home/logicmade/bookitfor.me.uk/sql

      - name: Persist env variables on remote host
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.IONOS_REMOTE_HOST }}
          username: ${{ secrets.IONOS_REMOTE_USER }}
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
          script: |
            echo "::group::Hidden section"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='${{ needs.build.outputs.RABBITMQ_DEFAULT_USER }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_PASS='${{ needs.build.outputs.RABBITMQ_DEFAULT_PASS }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_USERNAME='${{ needs.build.outputs.MONGO_INITDB_ROOT_USERNAME }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export MONGO_INITDB_ROOT_PASSWORD='${{ needs.build.outputs.MONGO_INITDB_ROOT_PASSWORD }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_USER='${{ needs.build.outputs.POSTGRES_USER }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export POSTGRES_PASSWORD='${{ needs.build.outputs.POSTGRES_PASSWORD }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN='${{ needs.build.outputs.KEYCLOAK_ADMIN }}'\" >> /etc/environment"
            echo ${{ secrets.IONOS_SUDO_PASS }} | sudo -S bash -c "echo \"export KEYCLOAK_ADMIN_PASSWORD='${{ needs.build.outputs.KEYCLOAK_ADMIN_PASSWORD }}'\" >> /etc/environment"  
            echo "::endgroup::"
            echo "Environment updated."
           
      - name: Restart Docker containers on remote server
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.IONOS_REMOTE_HOST }}
          username: ${{ secrets.IONOS_REMOTE_USER }}
          key: ${{ secrets.IONOS_SSH_PRIVATE_KEY }}
          script: |          
            cd /home/logicmade/bookitfor.me.uk/
            docker compose -f docker-compose-data.yml down
            docker compose -f docker-compose-data.yml up -d --build

Notes about the Yaml file

Build job must output the variables to use them in deploy job. If steps are in the same job there is no need to do that.

To hide echo in the logs I used

     echo "::group::Hidden section"
     ......
     echo "::endgroup::"

to use sudo with password I used this syntax

echo $Template:Secrets.IONOS SUDO PASS | sudo -S bash -c "echo \"export RABBITMQ_DEFAULT_USER='$Template:Needs.build.outputs.RABBITMQ DEFAULT USER'\" >> /etc/environment"