Skip to content

Deploy Migration Test Environment #15

Deploy Migration Test Environment

Deploy Migration Test Environment #15

name: 'Deploy Migration Test Environment'
on:
workflow_dispatch:
inputs:
branch:
description: '테스트할 브랜치 이름'
required: true
default: 'develop'
permissions:
contents: read
concurrency:
group: terraform-migration
cancel-in-progress: false
env:
AWS_REGION: ap-northeast-2
jobs:
build-lambda-handler:
name: Build Lambda Handler
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies and create zip file
run: |
mkdir -p build/lambda-package
pip install -r ./terraform-db-migration/lambda_src/requirements.txt -t ./build/lambda-package
cp ./terraform-db-migration/lambda_src/handler.py ./build/lambda-package/
cd ./build/lambda-package
zip -r ../migration_lambda.zip .
- name: Upload Lambda artifact
uses: actions/upload-artifact@v4
with:
name: lambda-build
path: ./build/migration_lambda.zip
terraform-apply-migration:
name: Terraform Apply MIGRATION
runs-on: ubuntu-latest
needs: build-lambda-handler
outputs:
tf_outputs_json: ${{ steps.get-outputs.outputs.data }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Download Lambda artifact
uses: actions/download-artifact@v5
with:
name: lambda-build
path: ./terraform-db-migration/build
- uses: hashicorp/setup-terraform@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Terraform Init (migration)
run: terraform init
working-directory: ./terraform-db-migration
- name: Terraform Apply MIGRATION
run: terraform apply -auto-approve
working-directory: ./terraform-db-migration
- name: Get Terraform Outputs
id: get-outputs
run: |
echo "data<<EOF" >> $GITHUB_OUTPUT
terraform output -json >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
working-directory: ./terraform-db-migration
build-and-run-migration:
name: Build Image and Run Migration Sequence
runs-on: ubuntu-latest
needs: terraform-apply-migration
steps:
- name: Checkout specific branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Parse Terraform Outputs and Set Env
id: parse-tf
run: |
TF_OUTPUTS='${{ needs.terraform-apply-migration.outputs.tf_outputs_json }}'
ECR_REPOSITORY=$(echo "$TF_OUTPUTS" | jq -r '.ecr_repository_name.value' | awk -F/ '{print $NF}')
echo "ECR_REPOSITORY=$ECR_REPOSITORY" >> $GITHUB_ENV
echo "PROD_ECS_CLUSTER_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_cluster_name.value')" >> $GITHUB_ENV
echo "PROD_ECS_SERVICE_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_api_service_name.value')" >> $GITHUB_ENV
echo "LAMBDA_FUNCTION_NAME=$(echo "$TF_OUTPUTS" | jq -r '.migration_lambda_function_name.value')" >> $GITHUB_ENV
echo "VPC_SUBNET=$(echo "$TF_OUTPUTS" | jq -r '.private_subnet_id.value')" >> $GITHUB_ENV
echo "TASK_SECURITY_GROUP=$(echo "$TF_OUTPUTS" | jq -r '.task_security_group_id.value')" >> $GITHUB_ENV
echo "CLONED_RDS_ADDRESS=$(echo "$TF_OUTPUTS" | jq -r '.cloned_rds_address.value')" >> $GITHUB_ENV
echo "CLONED_S3_BUCKET_NAME=$(echo "$TF_OUTPUTS" | jq -r '.cloned_s3_bucket_name.value')" >> $GITHUB_ENV
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
java-package: jdk
architecture: 'x64'
cache: 'gradle'
- name: Get TEST_JWT_SECRET_KEY from SSM
id: get-test-secret
run: |
SECRET_VALUE=$(aws ssm get-parameter --name "/common/TEST_JWT_SECRET_KEY" --with-decryption --query "Parameter.Value" --output text)
echo "TEST_JWT_SECRET_KEY=$SECRET_VALUE" >> $GITHUB_ENV
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew clean build -Dspring.profiles.active=migration
env:
TEST_JWT_SECRET_KEY: ${{ env.TEST_JWT_SECRET_KEY }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
IMAGE_TAG="migration-test-$(date +'%Y%m%d-%H%M%S')"
docker build -t $ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG} .
docker push $ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG}
echo "image_uri=$ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
- name: Get Latest PROD Task Definition Info
id: get-prod-info
run: |
TASK_DEF_ARN=$(aws ecs describe-services --cluster "${{ env.PROD_ECS_CLUSTER_NAME }}" --services "${{ env.PROD_ECS_SERVICE_NAME }}" --query "services[0].taskDefinition" --output text)
CONTAINER_NAME=$(aws ecs describe-task-definition --task-definition "$TASK_DEF_ARN" --query "taskDefinition.containerDefinitions[0].name" --output text)
echo "task_definition_arn=$TASK_DEF_ARN" >> $GITHUB_OUTPUT
echo "container_name=$CONTAINER_NAME" >> $GITHUB_OUTPUT
- name: Create New Task Definition with Updated Image
id: update-task-def
run: |
TASK_DEF=$(aws ecs describe-task-definition \
--task-definition "${{ steps.get-prod-info.outputs.task_definition_arn }}" \
--query "taskDefinition")
NEW_TASK_DEF=$(echo "$TASK_DEF" | jq --arg IMAGE "${{ steps.build-image.outputs.image_uri }}" '
.family = "api-migration" |
.containerDefinitions[0].image = $IMAGE |
(.containerDefinitions[0].command) |= map(
if test("prod$") then sub("prod$"; "migration") else . end
) |
del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .placementConstraints,
.compatibilities, .registeredAt, .registeredBy)
')
NEW_TASK_DEF_ARN=$(aws ecs register-task-definition \
--cli-input-json "$NEW_TASK_DEF" \
--query "taskDefinition.taskDefinitionArn" \
--output text)
echo "new_task_definition_arn=$NEW_TASK_DEF_ARN" >> $GITHUB_OUTPUT
- name: Invoke Pre-processing Lambda
run: |
META=$(aws lambda invoke \
--function-name "${{ env.LAMBDA_FUNCTION_NAME }}" \
--payload '{"task":"preprocess"}' \
--cli-binary-format raw-in-base64-out \
response.json)
echo "$META" | jq .
echo "$META" | jq -e '.FunctionError == null' >/dev/null || { echo "Preprocess Lambda failed"; exit 1; }
- name: Run DB Migration ECS Task
id: ecs-run-task
run: |
MIGRATION_RDS_URL="jdbc:mysql://${{ env.CLONED_RDS_ADDRESS }}/eatda?useUnicode=true&characterEncoding=UTF-8"
TASK_ARN=$(aws ecs run-task \
--cluster migration-cluster \
--task-definition ${{ steps.update-task-def.outputs.new_task_definition_arn }} \
--launch-type EC2 \
--overrides '{
"containerOverrides": [
{
"name": "${{ steps.get-prod-info.outputs.container_name }}",
"environment": [
{ "name": "SPRING_PROFILES_ACTIVE", "value": "migration" },
{ "name": "MIGRATION_RDS_URL", "value": "'"${MIGRATION_RDS_URL}"'" },
{ "name": "MIGRATION_S3_BUCKET", "value": "${{ env.CLONED_S3_BUCKET_NAME }}" },
{ "name": "SPRING_MAIN_WEB_APPLICATION_TYPE", "value": "NONE" }
]
}
]
}' \
--query "tasks[0].taskArn" --output text)
echo "task_arn=$TASK_ARN" >> $GITHUB_OUTPUT
if [ -z "$TASK_ARN" ] || [ "$TASK_ARN" = "None" ]; then
echo "Failed to start ECS task"; exit 1;
fi
- name: Wait for ECS Task to Complete
run: |
aws ecs wait tasks-stopped --cluster migration-cluster --tasks ${{ steps.ecs-run-task.outputs.task_arn }}
- name: Check ECS Task Exit Code
run: |
TASK_ARN=${{ steps.ecs-run-task.outputs.task_arn }}
EXIT_CODE=$(aws ecs describe-tasks \
--cluster migration-cluster \
--tasks $TASK_ARN \
--query "tasks[0].containers[0].exitCode" \
--output text)
echo "ECS Task Exit Code: $EXIT_CODE"
if [ "$EXIT_CODE" != "0" ]; then
echo "Migration task failed with exit code $EXIT_CODE"
exit 1
fi
- name: Get DB Credentials from SSM
id: get-db-creds
run: |
DB_USER=$(aws ssm get-parameter --name "/prod/MYSQL_USER_NAME" --with-decryption --query "Parameter.Value" --output text)
DB_PASS=$(aws ssm get-parameter --name "/prod/MYSQL_PASSWORD" --with-decryption --query "Parameter.Value" --output text)
echo "::add-mask::$DB_USER"
echo "::add-mask::$DB_PASS"
echo "DB_USER=$DB_USER" >> $GITHUB_ENV
echo "DB_PASS=$DB_PASS" >> $GITHUB_ENV
- name: Invoke Post-processing Lambda
run: |
PAYLOAD=$(jq -n \
--arg task "postprocess" \
--arg bucket "${{ env.CLONED_S3_BUCKET_NAME }}" \
--arg db_endpoint "${{ env.CLONED_RDS_ADDRESS }}" \
--arg db_username "${{ env.DB_USER }}" \
--arg db_password "${{ env.DB_PASS }}" \
'{
task: $task,
target_bucket_for_realignment: $bucket,
db_endpoint: $db_endpoint,
db_username: $db_username,
db_password: $db_password
}')
echo "Invoking Lambda with payload: $PAYLOAD"
aws lambda invoke \
--function-name "${{ env.LAMBDA_FUNCTION_NAME }}" \
--payload "$PAYLOAD" \
--cli-binary-format raw-in-base64-out \
response.json
notify:
name: Send Discord Notification (Migration)
runs-on: ubuntu-latest
needs: build-and-run-migration
if: always()
steps:
- name: Prepare Notification Info
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Discord Notify (Success)
if: needs.build-and-run-migration.result == 'success'
uses: tsickert/[email protected]
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
embed-title: "✅ Migration Test 배포 성공!"
embed-color: 65280
embed-description: |
마이그레이션 테스트 환경이 성공적으로 구축되었습니다.
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
**실행자**: ${{ github.actor }}
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
- name: Discord Notify (Failure)
if: needs.build-and-run-migration.result != 'success'
uses: tsickert/[email protected]
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
embed-title: "❌ Migration Test 배포 실패!"
embed-color: 16711680
embed-description: |
마이그레이션 테스트 환경 구축중 오류가 발생했습니다.
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
**실행자**: ${{ github.actor }}
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"