From 0bbe271cf2f402ab897b374c9991df2aad0dc9c0 Mon Sep 17 00:00:00 2001 From: Ashutosh Gangwar Date: Wed, 25 Oct 2023 16:26:57 +0530 Subject: [PATCH] clinic: add deploy job to the github workflow --- .github/workflows/clinic.yaml | 116 +++++++++++++++++++++++++- clinic/deploy/logrotate/clinic | 9 ++ clinic/deploy/systemd/clinic@.service | 23 +++++ 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 clinic/deploy/logrotate/clinic create mode 100644 clinic/deploy/systemd/clinic@.service diff --git a/.github/workflows/clinic.yaml b/.github/workflows/clinic.yaml index b22a93d..d6c9bd6 100644 --- a/.github/workflows/clinic.yaml +++ b/.github/workflows/clinic.yaml @@ -5,8 +5,8 @@ on: - main tags: - "**" - paths-ignore: - - "**.md" + paths: + - clinic/** pull_request: branches: - main @@ -113,3 +113,115 @@ jobs: context: clinic push: true tags: ${{ steps.docker-image-tags.outputs.tags }} + + deploy: + name: Deploy + runs-on: ubuntu-latest + timeout-minutes: 10 + if: github.event_name == 'push' # skip for pull requests. + needs: + - build + # GitHub won't allow using an env var to specify environment. + environment: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'staging' }} + env: + DEPLOYMENT_ID: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'staging' }} + SCP_DEST_PATH: /tmp/${{ github.sha }} + AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.DEPLOY_BOT_ACCESS_KEY }} + defaults: + run: + working-directory: ./clinic + steps: + - name: Checkout Source + uses: actions/checkout@v4 + - name: Install prerequisites + run: | + sudo apt-get -qq update -y + sudo apt-get -qq install -y awscli + - name: Get Workflow Runner's Public IP + id: workflow-runner-ip + run: echo "ipv4=$(curl -s 'https://api.ipify.org')" >> "$GITHUB_OUTPUT" + - name: Authorize runner's SSH access to EC2 host + run: | + aws ec2 authorize-security-group-ingress \ + --group-id "$AWS_EC2_SG_ID" \ + --protocol tcp \ + --port 22 \ + --cidr ${{ steps.workflow-runner-ip.outputs.ipv4 }}/32 + env: + AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_BOT_SECRET_KEY }} + AWS_EC2_SG_ID: ${{ secrets.AWS_EC2_SG_ID }} + + - name: Upload Systemd Service and Logrotate Config + uses: appleboy/scp-action@v0.1.4 + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + port: ${{ secrets.SSH_PORT }} + key: ${{ secrets.SSH_KEY }} + source: clinic/deploy/systemd,clinic/deploy/logrotate + target: ${{ env.SCP_DEST_PATH }} + strip_components: 2 # remove `clinic/deploy/` path component at target. + rm: true # remove target directory before uploading data + + - name: Configure Deployment + uses: appleboy/ssh-action@v1.0.0 + env: + APP_ENV: ${{ secrets.APP_ENV }} + GHCR_USER: ${{ github.actor }} + GHCR_TOKEN: ${{ github.token }} + GHCR_IMAGE: ghcr.io/nilenso/ashutosh-onboarding/clinic:${{ env.DEPLOYMENT_ID == 'staging' && 'latest' || github.ref_name }} + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + port: ${{ secrets.SSH_PORT }} + key: ${{ secrets.SSH_KEY }} + # only pass sensitive env vars from here. for others, use GitHub's env + # context, which is a little less error-prone. + envs: APP_ENV,GHCR_TOKEN + script_stop: true # stop script after first failure. + script: | + # stop existing deployment + service_name="clinic@${{ env.DEPLOYMENT_ID }}.service" + sudo systemctl stop "$service_name" || true + + # pull and tag the new docker image + echo "$GHCR_TOKEN" | docker login ghcr.io -u ${{ env.GHCR_USER }} --password-stdin + docker pull ${{ env.GHCR_IMAGE }} + docker tag ${{ env.GHCR_IMAGE }} clinic:${{ env.DEPLOYMENT_ID }} + docker logout ghcr.io + + # write environment configuration + sudo mkdir -p /etc/clinic + echo "$APP_ENV" | sudo tee "/etc/clinic/${{ env.DEPLOYMENT_ID }}.env" > /dev/null + + # update systemd unit + sudo chown root:root ${{ env.SCP_DEST_PATH }}/systemd/* + sudo chmod 644 ${{ env.SCP_DEST_PATH }}/systemd/* + sudo mv ${{ env.SCP_DEST_PATH }}/systemd/* /etc/systemd/system/ + + # reload systemd daemon and restart the service + sudo systemctl daemon-reload + sudo systemctl reenable "$service_name" + sudo systemctl restart "$service_name" + + # deploy logrotate config + sudo chown root:root ${{ env.SCP_DEST_PATH }}/logrotate/* + sudo chmod 644 ${{ env.SCP_DEST_PATH }}/logrotate/* + sudo mv ${{ env.SCP_DEST_PATH }}/logrotate/* /etc/logrotate.d/ + + # clean-up + sudo docker image prune -af + sudo rm -rf ${{ env.SCP_DEST_PATH }} + + - name: Revoke runner's SSH access from EC2 host + if: ${{ always() }} + run: | + aws ec2 revoke-security-group-ingress \ + --group-id "$AWS_EC2_SG_ID" \ + --protocol tcp \ + --port 22 \ + --cidr ${{ steps.workflow-runner-ip.outputs.ipv4 }}/32 + env: + AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_BOT_SECRET_KEY }} + AWS_EC2_SG_ID: ${{ secrets.AWS_EC2_SG_ID }} diff --git a/clinic/deploy/logrotate/clinic b/clinic/deploy/logrotate/clinic new file mode 100644 index 0000000..b338910 --- /dev/null +++ b/clinic/deploy/logrotate/clinic @@ -0,0 +1,9 @@ +/var/log/clinic/*.log { + daily + rotate 30 + copytruncate + nocompress + nodelaycompress + notifempty + missingok +} diff --git a/clinic/deploy/systemd/clinic@.service b/clinic/deploy/systemd/clinic@.service new file mode 100644 index 0000000..b4cc998 --- /dev/null +++ b/clinic/deploy/systemd/clinic@.service @@ -0,0 +1,23 @@ +[Unit] +Description=Clinic %i +After=docker.service hapi-%i.service +Requires=docker.service hapi-%i.service + +[Service] +Type=simple +ExecStartPre=-docker stop %N +ExecStartPre=-docker rm %N +ExecStart=docker run --rm --name %N \ + --pull never \ + --env-file /etc/clinic/%i.env \ + --network host \ + --memory 500M \ + --cpus 0.25 \ + clinic:%i +StandardOutput=append:/var/log/clinic/%i.log +StandardError=append:/var/log/clinic/%i.log +SuccessExitStatus=130 +Restart=on-failure + +[Install] +WantedBy=default.target