Skip to content

Commit a8b9a10

Browse files
authored
Merge pull request #188 from YAPP-Github/develop
[Feat] 2차 스프린트 프로덕션 최종 배포
2 parents 3fd45eb + c15173d commit a8b9a10

File tree

188 files changed

+5002
-1660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

188 files changed

+5002
-1660
lines changed

.github/workflows/deploy-dev.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ jobs:
102102
echo "ECS_SERVICE=$(echo "$TF_OUTPUTS" | jq -r '.ecs_api_service_name.value')" >> $GITHUB_ENV
103103
echo "CONTAINER_NAME=$(echo "$TF_OUTPUTS" | jq -r '.ecs_api_container_name.value')" >> $GITHUB_ENV
104104
echo "PRIVATE_IP=$(echo "$TF_OUTPUTS" | jq -r '.ec2_private_ip.value')" >> $GITHUB_ENV
105+
echo "CLOUDFRONT_DOMAIN_NAME=$(echo "$TF_OUTPUTS" | jq -r '.cloudfront_domain_name.value')" >> $GITHUB_ENV
105106
106107
- name: Configure AWS credentials
107108
uses: aws-actions/configure-aws-credentials@v4
@@ -110,7 +111,7 @@ jobs:
110111
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
111112
aws-region: ${{ env.AWS_REGION }}
112113

113-
- name: Update MYSQL_URL in Parameter Store
114+
- name: Update MYSQL_URL, CLOUDFRONT_DOMAIN_NAME in Parameter Store
114115
run: |
115116
MYSQL_URL="jdbc:mysql://${PRIVATE_IP}:3306/eatda?useUnicode=true&characterEncoding=UTF-8"
116117
@@ -119,6 +120,12 @@ jobs:
119120
--type "SecureString" \
120121
--value "$MYSQL_URL" \
121122
--overwrite
123+
124+
aws ssm put-parameter \
125+
--name "/dev/CLOUDFRONT_DOMAIN_NAME" \
126+
--type "SecureString" \
127+
--value "$CLOUDFRONT_DOMAIN_NAME" \
128+
--overwrite
122129
123130
- name: Set up Node.js
124131
uses: actions/setup-node@v4
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
name: 'Deploy Migration Test Environment'
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
branch:
7+
description: '테스트할 브랜치 이름'
8+
required: true
9+
default: 'develop'
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: terraform-migration
16+
cancel-in-progress: false
17+
18+
env:
19+
AWS_REGION: ap-northeast-2
20+
21+
jobs:
22+
build-lambda-handler:
23+
name: Build Lambda Handler
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v4
27+
with:
28+
ref: ${{ github.event.inputs.branch }}
29+
30+
- name: Set up Python
31+
uses: actions/setup-python@v4
32+
with:
33+
python-version: '3.12'
34+
35+
- name: Install dependencies and create zip file
36+
run: |
37+
mkdir -p build/lambda-package
38+
pip install -r ./terraform-db-migration/lambda_src/requirements.txt -t ./build/lambda-package
39+
cp ./terraform-db-migration/lambda_src/handler.py ./build/lambda-package/
40+
cd ./build/lambda-package
41+
zip -r ../migration_lambda.zip .
42+
43+
- name: Upload Lambda artifact
44+
uses: actions/upload-artifact@v4
45+
with:
46+
name: lambda-build
47+
path: ./build/migration_lambda.zip
48+
49+
terraform-apply-migration:
50+
name: Terraform Apply MIGRATION
51+
runs-on: ubuntu-latest
52+
needs: build-lambda-handler
53+
outputs:
54+
tf_outputs_json: ${{ steps.get-outputs.outputs.data }}
55+
steps:
56+
- uses: actions/checkout@v4
57+
with:
58+
ref: ${{ github.event.inputs.branch }}
59+
60+
- name: Download Lambda artifact
61+
uses: actions/download-artifact@v5
62+
with:
63+
name: lambda-build
64+
path: ./terraform-db-migration/build
65+
66+
- uses: hashicorp/setup-terraform@v3
67+
68+
- name: Configure AWS credentials
69+
uses: aws-actions/configure-aws-credentials@v4
70+
with:
71+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
72+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
73+
aws-region: ${{ env.AWS_REGION }}
74+
75+
- name: Terraform Init (migration)
76+
run: terraform init
77+
working-directory: ./terraform-db-migration
78+
79+
- name: Terraform Apply MIGRATION
80+
run: terraform apply -auto-approve
81+
working-directory: ./terraform-db-migration
82+
83+
- name: Get Terraform Outputs
84+
id: get-outputs
85+
run: |
86+
echo "data<<EOF" >> $GITHUB_OUTPUT
87+
terraform output -json >> $GITHUB_OUTPUT
88+
echo "EOF" >> $GITHUB_OUTPUT
89+
working-directory: ./terraform-db-migration
90+
91+
build-and-run-migration:
92+
name: Build Image and Run Migration Sequence
93+
runs-on: ubuntu-latest
94+
needs: terraform-apply-migration
95+
steps:
96+
- name: Checkout specific branch
97+
uses: actions/checkout@v4
98+
with:
99+
ref: ${{ github.event.inputs.branch }}
100+
101+
- name: Parse Terraform Outputs and Set Env
102+
id: parse-tf
103+
run: |
104+
TF_OUTPUTS='${{ needs.terraform-apply-migration.outputs.tf_outputs_json }}'
105+
ECR_REPOSITORY=$(echo "$TF_OUTPUTS" | jq -r '.ecr_repository_name.value' | awk -F/ '{print $NF}')
106+
echo "ECR_REPOSITORY=$ECR_REPOSITORY" >> $GITHUB_ENV
107+
echo "PROD_ECS_CLUSTER_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_cluster_name.value')" >> $GITHUB_ENV
108+
echo "PROD_ECS_SERVICE_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_api_service_name.value')" >> $GITHUB_ENV
109+
echo "LAMBDA_FUNCTION_NAME=$(echo "$TF_OUTPUTS" | jq -r '.migration_lambda_function_name.value')" >> $GITHUB_ENV
110+
echo "VPC_SUBNET=$(echo "$TF_OUTPUTS" | jq -r '.private_subnet_id.value')" >> $GITHUB_ENV
111+
echo "TASK_SECURITY_GROUP=$(echo "$TF_OUTPUTS" | jq -r '.task_security_group_id.value')" >> $GITHUB_ENV
112+
echo "CLONED_RDS_ADDRESS=$(echo "$TF_OUTPUTS" | jq -r '.cloned_rds_address.value')" >> $GITHUB_ENV
113+
echo "CLONED_S3_BUCKET_NAME=$(echo "$TF_OUTPUTS" | jq -r '.cloned_s3_bucket_name.value')" >> $GITHUB_ENV
114+
115+
- name: Configure AWS credentials
116+
uses: aws-actions/configure-aws-credentials@v4
117+
with:
118+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
119+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
120+
aws-region: ${{ env.AWS_REGION }}
121+
122+
- name: Set up JDK 21
123+
uses: actions/setup-java@v4
124+
with:
125+
distribution: 'temurin'
126+
java-version: 21
127+
java-package: jdk
128+
architecture: 'x64'
129+
cache: 'gradle'
130+
131+
- name: Get TEST_JWT_SECRET_KEY from SSM
132+
id: get-test-secret
133+
run: |
134+
SECRET_VALUE=$(aws ssm get-parameter --name "/common/TEST_JWT_SECRET_KEY" --with-decryption --query "Parameter.Value" --output text)
135+
echo "TEST_JWT_SECRET_KEY=$SECRET_VALUE" >> $GITHUB_ENV
136+
137+
- name: Build with Gradle
138+
run: |
139+
chmod +x gradlew
140+
./gradlew clean build -Dspring.profiles.active=migration
141+
env:
142+
TEST_JWT_SECRET_KEY: ${{ env.TEST_JWT_SECRET_KEY }}
143+
144+
- name: Login to Amazon ECR
145+
id: login-ecr
146+
uses: aws-actions/amazon-ecr-login@v2
147+
148+
- name: Build, tag, and push image to Amazon ECR
149+
id: build-image
150+
env:
151+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
152+
run: |
153+
IMAGE_TAG="migration-test-$(date +'%Y%m%d-%H%M%S')"
154+
docker build -t $ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG} .
155+
docker push $ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG}
156+
echo "image_uri=$ECR_REGISTRY/${ECR_REPOSITORY}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
157+
158+
- name: Get Latest PROD Task Definition Info
159+
id: get-prod-info
160+
run: |
161+
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)
162+
CONTAINER_NAME=$(aws ecs describe-task-definition --task-definition "$TASK_DEF_ARN" --query "taskDefinition.containerDefinitions[0].name" --output text)
163+
echo "task_definition_arn=$TASK_DEF_ARN" >> $GITHUB_OUTPUT
164+
echo "container_name=$CONTAINER_NAME" >> $GITHUB_OUTPUT
165+
166+
- name: Create New Task Definition with Updated Image
167+
id: update-task-def
168+
run: |
169+
TASK_DEF=$(aws ecs describe-task-definition \
170+
--task-definition "${{ steps.get-prod-info.outputs.task_definition_arn }}" \
171+
--query "taskDefinition")
172+
173+
NEW_TASK_DEF=$(echo "$TASK_DEF" | jq --arg IMAGE "${{ steps.build-image.outputs.image_uri }}" '
174+
.family = "api-migration" |
175+
.containerDefinitions[0].image = $IMAGE |
176+
(.containerDefinitions[0].command) |= map(
177+
if test("prod$") then sub("prod$"; "migration") else . end
178+
) |
179+
del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .placementConstraints,
180+
.compatibilities, .registeredAt, .registeredBy)
181+
')
182+
183+
NEW_TASK_DEF_ARN=$(aws ecs register-task-definition \
184+
--cli-input-json "$NEW_TASK_DEF" \
185+
--query "taskDefinition.taskDefinitionArn" \
186+
--output text)
187+
echo "new_task_definition_arn=$NEW_TASK_DEF_ARN" >> $GITHUB_OUTPUT
188+
189+
- name: Invoke Pre-processing Lambda
190+
run: |
191+
META=$(aws lambda invoke \
192+
--function-name "${{ env.LAMBDA_FUNCTION_NAME }}" \
193+
--payload '{"task":"preprocess"}' \
194+
--cli-binary-format raw-in-base64-out \
195+
response.json)
196+
echo "$META" | jq .
197+
echo "$META" | jq -e '.FunctionError == null' >/dev/null || { echo "Preprocess Lambda failed"; exit 1; }
198+
199+
- name: Run DB Migration ECS Task
200+
id: ecs-run-task
201+
run: |
202+
MIGRATION_RDS_URL="jdbc:mysql://${{ env.CLONED_RDS_ADDRESS }}/eatda?useUnicode=true&characterEncoding=UTF-8"
203+
TASK_ARN=$(aws ecs run-task \
204+
--cluster migration-cluster \
205+
--task-definition ${{ steps.update-task-def.outputs.new_task_definition_arn }} \
206+
--launch-type EC2 \
207+
--overrides '{
208+
"containerOverrides": [
209+
{
210+
"name": "${{ steps.get-prod-info.outputs.container_name }}",
211+
"environment": [
212+
{ "name": "SPRING_PROFILES_ACTIVE", "value": "migration" },
213+
{ "name": "MIGRATION_RDS_URL", "value": "'"${MIGRATION_RDS_URL}"'" },
214+
{ "name": "MIGRATION_S3_BUCKET", "value": "${{ env.CLONED_S3_BUCKET_NAME }}" },
215+
{ "name": "SPRING_MAIN_WEB_APPLICATION_TYPE", "value": "NONE" }
216+
]
217+
}
218+
]
219+
}' \
220+
--query "tasks[0].taskArn" --output text)
221+
echo "task_arn=$TASK_ARN" >> $GITHUB_OUTPUT
222+
if [ -z "$TASK_ARN" ] || [ "$TASK_ARN" = "None" ]; then
223+
echo "Failed to start ECS task"; exit 1;
224+
fi
225+
226+
- name: Wait for ECS Task to Complete
227+
run: |
228+
aws ecs wait tasks-stopped --cluster migration-cluster --tasks ${{ steps.ecs-run-task.outputs.task_arn }}
229+
230+
- name: Check ECS Task Exit Code
231+
run: |
232+
TASK_ARN=${{ steps.ecs-run-task.outputs.task_arn }}
233+
EXIT_CODE=$(aws ecs describe-tasks \
234+
--cluster migration-cluster \
235+
--tasks $TASK_ARN \
236+
--query "tasks[0].containers[0].exitCode" \
237+
--output text)
238+
239+
echo "ECS Task Exit Code: $EXIT_CODE"
240+
241+
if [ "$EXIT_CODE" != "0" ]; then
242+
echo "Migration task failed with exit code $EXIT_CODE"
243+
exit 1
244+
fi
245+
246+
- name: Get DB Credentials from SSM
247+
id: get-db-creds
248+
run: |
249+
DB_USER=$(aws ssm get-parameter --name "/prod/MYSQL_USER_NAME" --with-decryption --query "Parameter.Value" --output text)
250+
DB_PASS=$(aws ssm get-parameter --name "/prod/MYSQL_PASSWORD" --with-decryption --query "Parameter.Value" --output text)
251+
252+
echo "::add-mask::$DB_USER"
253+
echo "::add-mask::$DB_PASS"
254+
255+
echo "DB_USER=$DB_USER" >> $GITHUB_ENV
256+
echo "DB_PASS=$DB_PASS" >> $GITHUB_ENV
257+
258+
- name: Invoke Post-processing Lambda
259+
run: |
260+
PAYLOAD=$(jq -n \
261+
--arg task "postprocess" \
262+
--arg bucket "${{ env.CLONED_S3_BUCKET_NAME }}" \
263+
--arg db_endpoint "${{ env.CLONED_RDS_ADDRESS }}" \
264+
--arg db_username "${{ env.DB_USER }}" \
265+
--arg db_password "${{ env.DB_PASS }}" \
266+
'{
267+
task: $task,
268+
target_bucket_for_realignment: $bucket,
269+
db_endpoint: $db_endpoint,
270+
db_username: $db_username,
271+
db_password: $db_password
272+
}')
273+
274+
echo "Invoking Lambda with payload: $PAYLOAD"
275+
276+
aws lambda invoke \
277+
--function-name "${{ env.LAMBDA_FUNCTION_NAME }}" \
278+
--payload "$PAYLOAD" \
279+
--cli-binary-format raw-in-base64-out \
280+
response.json
281+
282+
notify:
283+
name: Send Discord Notification (Migration)
284+
runs-on: ubuntu-latest
285+
needs: build-and-run-migration
286+
if: always()
287+
steps:
288+
- name: Prepare Notification Info
289+
id: vars
290+
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
291+
292+
- name: Discord Notify (Success)
293+
if: needs.build-and-run-migration.result == 'success'
294+
uses: tsickert/[email protected]
295+
with:
296+
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
297+
embed-title: "✅ Migration Test 배포 성공!"
298+
embed-color: 65280
299+
embed-description: |
300+
마이그레이션 테스트 환경이 성공적으로 구축되었습니다.
301+
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
302+
**실행자**: ${{ github.actor }}
303+
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
304+
305+
- name: Discord Notify (Failure)
306+
if: needs.build-and-run-migration.result != 'success'
307+
uses: tsickert/[email protected]
308+
with:
309+
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
310+
embed-title: "❌ Migration Test 배포 실패!"
311+
embed-color: 16711680
312+
embed-description: |
313+
마이그레이션 테스트 환경 구축중 오류가 발생했습니다.
314+
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
315+
**실행자**: ${{ github.actor }}
316+
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

.github/workflows/deploy-prod.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ jobs:
107107
echo "ECS_SERVICE=$(echo "$TF_OUTPUTS" | jq -r '.ecs_api_service_name.value')" >> $GITHUB_ENV
108108
echo "CONTAINER_NAME=$(echo "$TF_OUTPUTS" | jq -r '.ecs_api_container_name.value')" >> $GITHUB_ENV
109109
echo "RDS_ENDPOINT=$(echo "$TF_OUTPUTS" | jq -r '.rds_endpoint.value')" >> $GITHUB_ENV
110+
echo "CLOUDFRONT_DOMAIN_NAME=$(echo "$TF_OUTPUTS" | jq -r '.cloudfront_domain_name.value')" >> $GITHUB_ENV
110111
111112
- name: Configure AWS credentials
112113
uses: aws-actions/configure-aws-credentials@v4
@@ -115,6 +116,14 @@ jobs:
115116
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
116117
aws-region: ${{ env.AWS_REGION }}
117118

119+
- name: Update CLOUDFRONT_DOMAIN_NAME in Parameter Store
120+
run: |
121+
aws ssm put-parameter \
122+
--name "/prod/CLOUDFRONT_DOMAIN_NAME" \
123+
--type "SecureString" \
124+
--value "$CLOUDFRONT_DOMAIN_NAME" \
125+
--overwrite
126+
118127
- name: Update DB URL in Parameter Store
119128
run: |
120129
aws ssm put-parameter --name "/prod/RDS_ENDPOINT" --value "jdbc:mysql://${{ env.RDS_ENDPOINT }}/eatda?useUnicode=true&characterEncoding=UTF-8" --type SecureString --overwrite

0 commit comments

Comments
 (0)