Skip to content

Commit 76dc14a

Browse files
authored
Merge pull request #187 from YAPP-Github/fix/db-migration
[Fix] prod db 마이그레이션을 위한 테라폼 동기화 작업
2 parents dcb06e2 + 2021c09 commit 76dc14a

File tree

19 files changed

+1019
-8
lines changed

19 files changed

+1019
-8
lines changed
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
name: 'Deploy Migration Test Environment'
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
branch:
7+
description: '테스트할 브랜치 이름'
8+
required: true
9+
default: 'dev'
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: ./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+
echo "ECR_REPOSITORY=$(echo "$TF_OUTPUTS" | jq -r '.ecr_repository_name.value')" >> $GITHUB_ENV
106+
echo "PROD_ECS_CLUSTER_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_cluster_name.value')" >> $GITHUB_ENV
107+
echo "PROD_ECS_SERVICE_NAME=$(echo "$TF_OUTPUTS" | jq -r '.prod_ecs_api_service_name.value')" >> $GITHUB_ENV
108+
echo "LAMBDA_FUNCTION_NAME=$(echo "$TF_OUTPUTS" | jq -r '.migration_lambda_function_name.value')" >> $GITHUB_ENV
109+
echo "VPC_SUBNET=$(echo "$TF_OUTPUTS" | jq -r '.private_subnet_id.value')" >> $GITHUB_ENV
110+
echo "TASK_SECURITY_GROUP=$(echo "$TF_OUTPUTS" | jq -r '.task_security_group_id.value')" >> $GITHUB_ENV
111+
echo "CLONED_RDS_ADDRESS=$(echo "$TF_OUTPUTS" | jq -r '.cloned_rds_address.value')" >> $GITHUB_ENV
112+
echo "CLONED_S3_BUCKET_NAME=$(echo "$TF_OUTPUTS" | jq -r '.cloned_s3_bucket_name.value')" >> $GITHUB_ENV
113+
114+
- name: Configure AWS credentials
115+
uses: aws-actions/configure-aws-credentials@v4
116+
with:
117+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
118+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
119+
aws-region: ${{ env.AWS_REGION }}
120+
121+
- name: Set up JDK 21
122+
uses: actions/setup-java@v4
123+
with:
124+
distribution: 'temurin'
125+
java-version: 21
126+
java-package: jdk
127+
architecture: 'x64'
128+
cache: 'gradle'
129+
130+
- name: Get TEST_JWT_SECRET_KEY from SSM
131+
id: get-test-secret
132+
run: |
133+
SECRET_VALUE=$(aws ssm get-parameter --name "/common/TEST_JWT_SECRET_KEY" --with-decryption --query "Parameter.Value" --output text)
134+
echo "TEST_JWT_SECRET_KEY=$SECRET_VALUE" >> $GITHUB_ENV
135+
136+
- name: Build with Gradle
137+
run: |
138+
chmod +x gradlew
139+
./gradlew clean build -Dspring.profiles.active=migration
140+
env:
141+
TEST_JWT_SECRET_KEY: ${{ env.TEST_JWT_SECRET_KEY }}
142+
143+
- name: Login to Amazon ECR
144+
id: login-ecr
145+
uses: aws-actions/amazon-ecr-login@v2
146+
147+
- name: Build, tag, and push image to Amazon ECR
148+
id: build-image
149+
env:
150+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
151+
run: |
152+
IMAGE_TAG="migration-test-$(date +'%Y%m%d-%H%M%S')"
153+
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
154+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
155+
echo "image_uri=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
156+
157+
- name: Get Latest PROD Task Definition Info
158+
id: get-prod-info
159+
run: |
160+
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)
161+
CONTAINER_NAME=$(aws ecs describe-task-definition --task-definition "$TASK_DEF_ARN" --query "taskDefinition.containerDefinitions[0].name" --output text)
162+
echo "task_definition_arn=$TASK_DEF_ARN" >> $GITHUB_OUTPUT
163+
echo "container_name=$CONTAINER_NAME" >> $GITHUB_OUTPUT
164+
165+
- name: Create New Task Definition with Updated Image
166+
id: update-task-def
167+
run: |
168+
TASK_DEF=$(aws ecs describe-task-definition --task-definition "${{ steps.get-prod-info.outputs.task_definition_arn }}" --query "taskDefinition")
169+
170+
NEW_TASK_DEF=$(echo "$TASK_DEF" | jq --arg IMAGE "${{ steps.build-image.outputs.image_uri }}" '
171+
.containerDefinitions[0].image = $IMAGE |
172+
del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .placementConstraints, .compatibilities, .registeredAt, .registeredBy)
173+
')
174+
175+
NEW_TASK_DEF_ARN=$(aws ecs register-task-definition --cli-input-json "$NEW_TASK_DEF" --query "taskDefinition.taskDefinitionArn" --output text)
176+
echo "new_task_definition_arn=$NEW_TASK_DEF_ARN" >> $GITHUB_OUTPUT
177+
178+
- name: Invoke Pre-processing Lambda
179+
run: |
180+
META=$(aws lambda invoke \
181+
--function-name "${{ env.LAMBDA_FUNCTION_NAME }}" \
182+
--payload '{"task":"preprocess"}' \
183+
--cli-binary-format raw-in-base64-out \
184+
response.json)
185+
echo "$META" | jq .
186+
echo "$META" | jq -e '.FunctionError == null' >/dev/null || { echo "Preprocess Lambda failed"; exit 1; }
187+
188+
- name: Run DB Migration ECS Task
189+
id: ecs-run-task
190+
run: |
191+
MIGRATION_RDS_URL="jdbc:mysql://${{ env.CLONED_RDS_ADDRESS }}/eatda?useUnicode=true&characterEncoding=UTF-8"
192+
TASK_ARN=$(aws ecs run-task \
193+
--cluster ${{ env.PROD_ECS_CLUSTER_NAME }} \
194+
--task-definition ${{ steps.update-task-def.outputs.new_task_definition_arn }} \
195+
--launch-type EC2 \
196+
--network-configuration "awsvpcConfiguration={subnets=[${{ env.VPC_SUBNET }}],securityGroups=[${{ env.TASK_SECURITY_GROUP }}]}" \
197+
--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 }}" } ] } ] }' \
198+
--query "tasks[0].taskArn" --output text)
199+
echo "task_arn=$TASK_ARN" >> $GITHUB_OUTPUT
200+
if [ -z "$TASK_ARN" ] || [ "$TASK_ARN" = "None" ]; then
201+
echo "Failed to start ECS task"; exit 1;
202+
fi
203+
204+
- name: Wait for ECS Task to Complete
205+
run: |
206+
aws ecs wait tasks-stopped --cluster ${{ env.PROD_ECS_CLUSTER_NAME }} --tasks ${{ steps.ecs-run-task.outputs.task_arn }}
207+
208+
- name: Invoke Post-processing Lambda
209+
run: |
210+
aws lambda invoke --function-name ${{ env.LAMBDA_FUNCTION_NAME }} --payload '{"task": "postprocess"}' response.json
211+
212+
notify:
213+
name: Send Discord Notification (Migration)
214+
runs-on: ubuntu-latest
215+
needs: build-and-run-migration
216+
if: always()
217+
steps:
218+
- name: Prepare Notification Info
219+
id: vars
220+
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
221+
222+
- name: Discord Notify (Success)
223+
if: needs.build-and-run-migration.result == 'success'
224+
uses: tsickert/discord-webhook@v7.0.0
225+
with:
226+
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
227+
embed-title: "✅ Migration Test 배포 성공!"
228+
embed-color: 65280
229+
embed-description: |
230+
마이그레이션 테스트 환경이 성공적으로 구축되었습니다.
231+
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
232+
**실행자**: ${{ github.actor }}
233+
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
234+
235+
- name: Discord Notify (Failure)
236+
if: needs.build-and-run-migration.result != 'success'
237+
uses: tsickert/discord-webhook@v7.0.0
238+
with:
239+
webhook-url: ${{ secrets.DISCORD_WEBHOOK }}
240+
embed-title: "❌ Migration Test 배포 실패!"
241+
embed-color: 16711680
242+
embed-description: |
243+
마이그레이션 테스트 환경 구축중 오류가 발생했습니다.
244+
**커밋**: [${{ steps.vars.outputs.sha_short }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
245+
**실행자**: ${{ github.actor }}
246+
embed-url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: 'Destroy Migration Test Environment'
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
branch:
7+
description: '리소스를 생성했던 브랜치 이름'
8+
required: true
9+
default: 'dev'
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: terraform-migration-destroy
16+
cancel-in-progress: false
17+
18+
env:
19+
AWS_REGION: ap-northeast-2
20+
21+
jobs:
22+
terraform-destroy-migration:
23+
name: Terraform Destroy MIGRATION
24+
runs-on: ubuntu-latest
25+
26+
steps:
27+
- uses: actions/checkout@v5
28+
with:
29+
ref: ${{ github.event.inputs.branch }}
30+
31+
- uses: hashicorp/setup-terraform@v3
32+
33+
- name: Configure AWS credentials
34+
uses: aws-actions/configure-aws-credentials@v4
35+
with:
36+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
37+
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY_ID }}
38+
aws-region: ${{ env.AWS_REGION }}
39+
40+
- name: Terraform Init (migration)
41+
run: terraform init
42+
working-directory: ./terraform-db-migration
43+
44+
- name: Terraform Destroy MIGRATION
45+
run: terraform destroy -auto-approve
46+
working-directory: ./terraform-db-migration

src/main/java/eatda/service/cheer/CheerService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
@RequiredArgsConstructor
3636
public class CheerService {
3737

38-
private static final int MAX_CHEER_SIZE = 3;
38+
private static final int MAX_CHEER_SIZE = 10_000;
3939

4040
private final MemberRepository memberRepository;
4141
private final StoreRepository storeRepository;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
logging:
2+
level:
3+
org.hibernate.SQL: INFO
4+
5+
spring:
6+
cloud:
7+
aws:
8+
region:
9+
static: ap-northeast-2
10+
s3:
11+
bucket: ${MIGRATION_S3_BUCKET}
12+
13+
servlet:
14+
multipart:
15+
max-file-size: 5MB
16+
max-request-size: 10MB
17+
18+
config:
19+
import: "aws-parameterstore:/prod/"
20+
21+
datasource:
22+
url: ${MIGRATION_RDS_URL}
23+
username: ${MYSQL_USER_NAME}
24+
password: ${MYSQL_PASSWORD}
25+
driver-class-name: com.mysql.cj.jdbc.Driver
26+
27+
jpa:
28+
hibernate:
29+
ddl-auto: validate
30+
31+
flyway:
32+
enabled: true
33+
baseline-on-migrate: false
34+
locations:
35+
- classpath:db/migration
36+
- classpath:db/seed/prod
37+
38+
jwt:
39+
access-token-expiration: 1h
40+
refresh-token-expiration: 14d
41+
secret-key: ${JWT_SECRET_KEY}
42+
43+
cors:
44+
origin:
45+
- "https://www.eatda.net"
46+
- "https://eatda.net"
47+
48+
oauth:
49+
client-id: ${OAUTH_CLIENT_ID}
50+
redirect-path: /login/callback
51+
allowed-origins:
52+
- "https://www.eatda.net"
53+
- "https://eatda.net"
54+
55+
kakao:
56+
api-key: ${KAKAO_API_KEY}

src/test/java/eatda/service/cheer/CheerServiceTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.LocalDateTime;
2626
import java.util.Comparator;
2727
import java.util.List;
28+
import org.junit.jupiter.api.Disabled;
2829
import org.junit.jupiter.api.Nested;
2930
import org.junit.jupiter.api.Test;
3031
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,6 +39,7 @@ class CheerServiceTest extends BaseServiceTest {
3839
class RegisterCheer {
3940

4041
@Test
42+
@Disabled("현재 한시적으로 MAX_CHEER_SIZE=10000 으로 운영 중")
4143
void 응원_개수가_최대_개수를_초과하면_예외가_발생한다() {
4244
Member member = memberGenerator.generate("123");
4345
Store store1 = storeGenerator.generate("124", "서울시 강남구 역삼동 123-45");

terraform-bootstrap/locals.tf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
locals {
2+
default_ecr_lifecycle_policy = jsonencode({
3+
rules = [
4+
{
5+
rulePriority = 1,
6+
description = "7일이 지난 마이그레이션 테스트용 임시 이미지 자동 삭제",
7+
selection = {
8+
tagStatus = "tagged",
9+
tagPrefixList = ["migration-test-"],
10+
countType = "sinceImagePushed",
11+
countUnit = "days",
12+
countNumber = 7
13+
},
14+
action = {
15+
type = "expire"
16+
}
17+
}
18+
]
19+
})
20+
}

0 commit comments

Comments
 (0)