From 4972d539efc5b75fcd6232e02dd352ef528e46b6 Mon Sep 17 00:00:00 2001 From: Vedran Devcic Date: Thu, 6 Feb 2025 10:05:22 +0100 Subject: [PATCH] Add Docker build and ECS task definition --- .aws/ecs/task-definition-app-staging.json | 84 ++++++++++ .github/workflows/build-and-push.yml | 35 +++++ .github/workflows/deploy-staging-ecs.yml | 179 ++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 .aws/ecs/task-definition-app-staging.json create mode 100644 .github/workflows/build-and-push.yml create mode 100644 .github/workflows/deploy-staging-ecs.yml diff --git a/.aws/ecs/task-definition-app-staging.json b/.aws/ecs/task-definition-app-staging.json new file mode 100644 index 0000000..5c5a43a --- /dev/null +++ b/.aws/ecs/task-definition-app-staging.json @@ -0,0 +1,84 @@ +{ + "containerDefinitions": [ + { + "name": "js-react-example", + "image": "702192518610.dkr.ecr.eu-west-1.amazonaws.com/js-react-example:latest", + "cpu": 0, + "portMappings": [ + { + "containerPort": 3000, + "hostPort": 3000, + "protocol": "tcp" + } + ], + "essential": true, + "entryPoint": [], + "command": [], + "stopTimeout": 70, + "environment": [ + { + "name": "SITE_URL", + "value": "https://react-example.byinfinum.co" + }, + { + "name": "NEXT_PUBLIC_API_ENDPOINT", + "value": "https://cekila.infinum.co/api/v1/" + }, + { + "name": "NEXT_PUBLIC_NEXT_APP_ENV", + "value": "staging" + }, + { + "name": "NEXT_PUBLIC_BUGSNAG_API_KEY", + "value": "156963fd85bf53f2811f45106f5fcb2d" + } + ], + "mountPoints": [], + "volumesFrom": [], + "secrets": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "js-react-example-staging-eu-west-1", + "awslogs-region": "eu-west-1", + "awslogs-stream-prefix": "container" + } + } + } + ], + "family": "infinum-staging-cluster-js-react-example", + "taskRoleArn": "arn:aws:iam::702192518610:role/infinum-staging-cluster-js-react-example-task-role", + "executionRoleArn": "arn:aws:iam::702192518610:role/js-react-example-staging-task-execution-role", + "networkMode": "awsvpc", + "volumes": [], + "placementConstraints": [], + "requiresCompatibilities": ["FARGATE"], + "cpu": "256", + "memory": "512", + "tags": [ + { + "key": "Project", + "value": "js-react-example" + }, + { + "key": "Environment", + "value": "staging" + }, + { + "key": "terraform", + "value": "false" + }, + { + "key": "Client", + "value": "Infinum" + }, + { + "key": "Contact", + "value": "server-admin@infinum.com" + }, + { + "key": "Name", + "value": "staging-task-definition-js-react-example" + } + ] +} \ No newline at end of file diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml new file mode 100644 index 0000000..e3765b5 --- /dev/null +++ b/.github/workflows/build-and-push.yml @@ -0,0 +1,35 @@ +name: Build and push + +on: + push: + branches: + - main + paths-ignore: + - '.github/**' + - '**.md' + workflow_dispatch: + inputs: + custom_tag: + description: 'Set custom tag for image' + required: true + type: string + +jobs: + build-and-push: + if: ${{ github.event_name == 'push' }} + name: 'Run on push' + uses: infinum/devops-pipelines/.github/workflows/docker-build-push.yml@v1.12.12 + with: + cloud: 'AWS' + environment: 'production' + secrets: inherit + + build-and-push-manual: + if: ${{ github.event_name == 'workflow_dispatch' }} + name: 'Run on workflow dispatch' + uses: infinum/devops-pipelines/.github/workflows/docker-build-push.yml@v1.12.12 + with: + cloud: 'AWS' + environment: 'production' + custom_tag: ${{ inputs.custom_tag }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/deploy-staging-ecs.yml b/.github/workflows/deploy-staging-ecs.yml new file mode 100644 index 0000000..0013b1e --- /dev/null +++ b/.github/workflows/deploy-staging-ecs.yml @@ -0,0 +1,179 @@ +name: Deploy-staging + +on: + workflow_dispatch: + inputs: + custom_tag: + description: 'Set custom tag for image' + required: true + type: string + environment: + type: choice + description: 'The environment to deploy to' + options: + - staging + - production + required: true + default: 'staging' + push: + branches: + - main + paths-ignore: + - '.github/**' + - '**.md' + +jobs: + context: + name: Setup context + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + outputs: + aws_region: ${{ steps.get.outputs.aws_region }} + aws_ecr_uri: ${{ steps.get.outputs.aws_ecr_uri }} + aws_ecr_region: ${{ steps.get.outputs.aws_ecr_region }} + aws_ecr_account_id: ${{ steps.get.outputs.aws_ecr_account_id }} + aws_ecs_cluster: ${{ steps.get.outputs.aws_ecs_cluster }} + steps: + - id: get + run: | + echo "aws_region=${{ vars.AWS_REGION }}" >> "$GITHUB_OUTPUT" + echo "aws_ecr_uri=${{ vars.AWS_ECR_URI }}" >> "$GITHUB_OUTPUT" + echo "aws_ecr_region=${{ vars.AWS_ECR_REGION }}" >> "$GITHUB_OUTPUT" + echo "aws_ecr_account_id=${{ vars.AWS_ECR_ACCOUNT_ID }}" >> "$GITHUB_OUTPUT" + echo "aws_ecs_cluster=${{ vars.AWS_ECS_CLUSTER }}" >> "$GITHUB_OUTPUT" + + build-push-image: + if: ${{ github.event_name == 'push' }} + name: Build deploy image and push to registry + uses: infinum/devops-pipelines/.github/workflows/docker-build-push.yml@v1.12.16 + needs: context + with: + environment: ${{ inputs.environment }} + cloud: AWS + tags: ${{ needs.context.outputs.aws_ecr_uri }}:${{ github.sha }} + aws_ecr_region: ${{ needs.context.outputs.aws_ecr_region }} + aws_ecr_account_id: ${{ needs.context.outputs.aws_ecr_account_id }} + target: deploy + secrets: inherit + + build-push-image-manual: + if: ${{ github.event_name == 'workflow_dispatch' }} + name: Run on workflow dispatch + uses: infinum/devops-pipelines/.github/workflows/docker-build-push.yml@v1.12.16 + needs: context + with: + environment: ${{ inputs.environment }} + cloud: AWS + tags: ${{ needs.context.outputs.aws_ecr_uri }}:${{ inputs.custom_tag }} + aws_ecr_region: ${{ needs.context.outputs.aws_ecr_region }} + aws_ecr_account_id: ${{ needs.context.outputs.aws_ecr_account_id }} + secrets: inherit + + run-migrations: + name: Run Prisma db migrations + runs-on: ubuntu-latest + needs: [context, build-push-image] + environment: ${{ inputs.environment }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Node + uses: actions/setup-node@v2 + with: + node-version: '20' + - name: Install dependencies + run: npm ci + - name: Open SSH tunnel + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ needs.context.outputs.aws_region }} + run: | + aws ssm start-session \ + --target i-0cb09814d228ec31d \ + --document-name AWS-StartPortForwardingSessionToRemoteHost \ + --parameters host="${{ secrets.JUMPHOST_HOST }}",portNumber="5432",localPortNumber="5432" & + sleep 10 + - name: Run migrations + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + run: npx prisma migrate deploy + + run-migrations-manual: + name: Run Prisma db migrations + runs-on: ubuntu-latest + needs: [context, build-push-image-manual] + environment: ${{ inputs.environment }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Node + uses: actions/setup-node@v2 + with: + node-version: '20' + - name: Install dependencies + run: npm ci + - name: Open SSH tunnel + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ needs.context.outputs.aws_region }} + run: | + aws ssm start-session \ + --target ${{ vars.JUMPHOST_ID }} \ + --document-name AWS-StartPortForwardingSessionToRemoteHost \ + --parameters host="${{ secrets.JUMPHOST_HOST }}",portNumber="${{ vars.JUMPHOST_PORT }}",localPortNumber="${{ vars.SERVER_PORT }}" & + - name: Run migrations + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + run: npx prisma migrate deploy + + deploy-image: + if: ${{ github.event_name == 'push' }} + name: Deploy backend + uses: infinum/devops-pipelines/.github/workflows/deploy-ecs-task-definition.yml@v1.12.16 + needs: [context, build-push-image, run-migrations] + with: + image_uri: ${{ needs.context.outputs.aws_ecr_uri }}:${{ github.sha }} + environment: ${{ inputs.environment }} + aws_region: ${{ needs.context.outputs.aws_region }} + ecs_cluster: ${{ needs.context.outputs.aws_ecs_cluster }} + ecs_service: js-revisor + task_def_path: .aws/ecs/task-definition-app-staging.json + container_name: js-revisor + secrets: inherit + + deploy-image-manual: + if: ${{ github.event_name == 'workflow_dispatch' }} + name: Deploy backend + uses: infinum/devops-pipelines/.github/workflows/deploy-ecs-task-definition.yml@v1.12.16 + needs: [context, build-push-image-manual, run-migrations-manual] + with: + image_uri: ${{ needs.context.outputs.aws_ecr_uri }}:${{ inputs.custom_tag }} + environment: ${{ inputs.environment }} + aws_region: ${{ needs.context.outputs.aws_region }} + ecs_cluster: ${{ needs.context.outputs.aws_ecs_cluster }} + ecs_service: js-revisor + task_def_path: .aws/ecs/task-definition-app-staging.json + container_name: js-revisor + secrets: inherit + + notify-deployment-automatic: + name: Send Slack notification + uses: infinum/devops-pipelines/.github/workflows/slack-notification.yml@v1.12.16 + needs: + [ + context, + build-push-image, + build-push-image-manual, + run-migrations, + run-migrations-manual, + deploy-image, + deploy-image-manual, + ] + with: + channel: project-js-revisor-notifications + outcome: ${{ needs.build-push-image.result == 'success' || needs.build-push-image-manual.result == 'success'}} + color: ${{ needs.build-push-image.result == 'success' || needs.build-push-image-manual.result == 'success' }} + title: "[Staging] deploy js-revisor: ${{ needs.build-push-image.result == 'success' || needs.build-push-image-manual.result == 'success'}}" + secrets: inherit \ No newline at end of file