AWS Infrastructure Operations Rules#
ํ๋ง๋๋ก (๋น๊ฐ๋ฐ์์ฉ)#
์ด ๋ฌธ์๋ ์๋น์ค๊ฐ ๋์๊ฐ๋ "ํด๋ผ์ฐ๋ ์๋ฒ์ค(AWS)"์์ ๋ฌธ์ ๊ฐ ์๊ฒผ์ ๋ ์ฐ๋ ์๊ธ์กฐ์น ๋งค๋ด์ผ์ ๋๋ค. ๋ง์น ๊ฑด๋ฌผ ๊ด๋ฆฌ์ค์ ๋ถ์ "์ ์ ์ ๋๊บผ๋น์ง ํ์ธํ์ธ์", "์๋ ์ ๊ฒผ์ ๋ ๋ฐธ๋ธ ์ฌ๋ ๋ฒ" ๊ฐ์ ์๋ด๋ฌธ๊ณผ ๋น์ทํฉ๋๋ค. ์๋ฒ๊ฐ ๋ฉ์ถ๊ฑฐ๋ ๋๋ ค์ก์ ๋, ์ด๋๋ถํฐ ์ ๊ฒํ๊ณ ์ด๋ป๊ฒ ๋์ด๋ฆฌ๋์ง๋ฅผ ์์๋๋ก ์ ๋ฆฌํด ๋ ๋น์ ๋์์ง์ด๋ผ๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
๋ฌด์์ยท์ธ์ (๋น๊ฐ๋ฐ์์ฉ)#
-
๋ฌด์์ ํด์ฃผ๋์
- ์๋ฒ๊ฐ 502/503 ๊ฐ์ ์๋ฌ๋ก "์ ๊ฒ ์ค" ํ๋ฉด์ ๋์ธ ๋ ์์ธ์ ์ฐพ์ ๋ณต๊ตฌํ๋ ์ ์ฐจ๋ฅผ ์๋ ค์ค๋๋ค.
- ๋ฐ์ดํฐ ์ ์ฅ ๊ณต๊ฐ(DB ๋์คํฌ)์ด ๊ฝ ์ฐผ์ ๋ ๊ธด๊ธ์ผ๋ก ๊ณต๊ฐ์ ๋๋ฆฌ๋ ๋ฐฉ๋ฒ์ ์๋ดํฉ๋๋ค.
- ๋น๋ฐ๋ฒํธ(ํ ํฐ) ๋ถ์ผ์น๋ก ์บ์ ๊ธฐ๋ฅ์ด ๋ฉ์ท์ ๋ ๋ค์ ๋ง์ถ๋ ๋ฐฉ๋ฒ์ ์๋ดํฉ๋๋ค.
- ๋ฐฐํฌ(์ ๋ฒ์ ์ ์ฉ)๊ฐ ์คํจํ๊ฑฐ๋, ๊ฐ๋ฐ์ PC์ ๊ฐ์ํ๊ฒฝ์ด ์ ๊ฒผ์ ๋ ํธ๋ ๋ฐฉ๋ฒ์ ์๋ดํฉ๋๋ค.
- ์ฅ์ ๊ฐ ๋ฌ์ ๋ "๋ฌด์๋ถํฐ ํ์ธํ ์ง" ์ ๊ฒ ์์๋ฅผ ์ ์ํฉ๋๋ค (๋ณดํต DB ์ ์ฅ๊ณต๊ฐ์ ๊ฐ์ฅ ๋จผ์ ๋ด ๋๋ค).
-
์ธ์ ์๋ํ๋์
- ์ด์/์คํ ์ด์ง ์๋ฒ์ ์ฅ์ ๊ฐ ๋ฐ์ํ์ ๋.
- ๋น๊ณต๊ฐ๋ง(์ธ๋ถ์์ ์ง์ ์ ์ ๋ถ๊ฐ) ์์ ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ๊ฒํด์ผ ํ ๋.
- ๋ฐฐํฌ ํ ์ ๋ฒ์ ์ด ์ค์ ๋ก ์ ๋ด๋์ง ํ์ธํ๊ณ ์ถ์ ๋.
ํต์ฌ ์ฉ์ด (๋น๊ฐ๋ฐ์์ฉ)#
| ์ฉ์ด | ์ฌ์ด ์ค๋ช |
|---|---|
| AWS | ์๋น์ค๊ฐ ์ค์ ๋ก ๋์๊ฐ๋ ์๋ง์กด์ ํด๋ผ์ฐ๋ ์๋ฒ์ค |
| RDS | ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ดํ๋ ํด๋ผ์ฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค (์ ์ฅ ์ฐฝ๊ณ ) |
| storage-full | ๋ฐ์ดํฐ ์ฐฝ๊ณ (๋์คํฌ)๊ฐ ๊ฝ ์ฐจ์ ๋ ๋ชป ์ฐ๋ ์ํ |
| EC2 / ASG | ์๋ฒ ์ปดํจํฐ ํ ๋(EC2)์, ํธ๋ํฝ์ ๋ง์ถฐ ์๋์ผ๋ก ๋์๋ฅผ ๋๋ฆฌ๊ณ ์ค์ด๋ ๋ฌถ์(ASG) |
| VPC (private) | ์ธ๋ถ์์ ์ง์ ๋ชป ๋ค์ด์ค๊ฒ ๋ง์ ๋ ๋ด๋ถ ์ ์ฉ ๋คํธ์ํฌ |
| SSM | ๊ทธ ๋ด๋ถ๋ง ์๋ฒ์ ์์ ํ๊ฒ ์๊ฒฉ ๋ช ๋ น์ ๋ณด๋ด๋ ํต๋ก |
| ElastiCache / Redis | ์์ฃผ ์ฐ๋ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ๊บผ๋ด ์ฐ๋ ์์ ์ ์ฅ์(์บ์) |
| AUTH token | ์บ์ ์ ์์ฉ ๋น๋ฐ๋ฒํธ |
| WRONGPASS | ๋น๋ฐ๋ฒํธ๊ฐ ์ ๋ง์ ์บ์ ์ ์์ด ๊ฑฐ๋ถ๋ ์๋ฌ |
| drift | ์ค๊ณ ๋ฌธ์(์ฝ๋)์ ์ค์ ์๋ฒ ์ํ๊ฐ ์ด๊ธ๋ ์ํฉ |
| Terraform | ์๋ฒ ๊ตฌ์ฑ์ ์ฝ๋๋ก ์ ์ด ๋๊ณ ๊ทธ๋๋ก ๋ง๋ค์ด ์ฃผ๋ ๋๊ตฌ (์ค๊ณ๋) |
| CodeDeploy | ์๋ก ๋ง๋ ๋ฒ์ ์ ์๋ฒ์ ์๋์ผ๋ก ๋ฐฐํฌํด ์ฃผ๋ ๋๊ตฌ |
| Blue/Green ๋ฐฐํฌ | ์ ๋ฒ์ ์ ์์ ๋ฐ๋ก ๋์ ๋๊ณ ํ ๋ฒ์ ๊ฐ์๋ผ์ฐ๋ ๋ฌด์ค๋จ ๋ฐฐํฌ ๋ฐฉ์ |
| CloudWatch ์๋ | ์๋ฒ ์ํ(CPU, ๋จ์ ๊ณต๊ฐ ๋ฑ)๋ฅผ ๊ฐ์ํ๋ค ์ํํ๋ฉด ์๋ ค ์ฃผ๋ ๊ฒฝ๋ณด |
| Colima VM | ๊ฐ๋ฐ์ PC์์ ์ปจํ ์ด๋๋ฅผ ๋๋ฆฌ๊ธฐ ์ํ ๋ก์ปฌ ๊ฐ์ ๋จธ์ |
AWS ์ธํ๋ผ ์ฅ์ ๋์ ๋ฐ ์ด์ ๊ท์น์ ๋๋ค.
SSM์ผ๋ก VPC ๋ด๋ถ RDS ์ฟผ๋ฆฌ ์คํ#
ํ๋ก๋์ /์คํ ์ด์ง RDS๋ private VPC์ ์์นํด ๋ก์ปฌ์์ ์ง์ ์ ์ ๋ถ๊ฐ. SSM Send-command + EC2์์ ์ผํ์ฑ postgres ์ปจํ ์ด๋๋ก psql ์คํํ๋ค.
์ ์ฐจ#
# 1. EC2 ์ธ์คํด์ค ID ์กฐํ (ASG ๊ต์ฒด ๋๋น โ ๋งค๋ฒ ๋ค์ ์กฐํ ํ์)
INSTANCE_ID=$(aws ec2 describe-instances \
--filters " Name=tag:Environment,Values=production " \
" Name=instance-state-name,Values=running " \
--query ' Reservations[0].Instances[0].InstanceId ' --output text)
# 2. DB ์ฐ๊ฒฐ ์ ๋ณด ํ์ธ (์ปจํ
์ด๋ ๋ด๋ถ)
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name " AWS-RunShellScript " \
--parameters ' commands=[ " docker exec kobic-server cat config/production.yaml | grep -A6 \ " ^database:\ " " ] '
# 3. postgres ์ด๋ฏธ์ง pre-pull (์ด๋ฏธ์ง ๋ค์ด๋ก๋์ ์๊ฐ ๊ฑธ๋ฆฌ๋ฉด send-command ํ์์์)
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name " AWS-RunShellScript " \
--parameters ' commands=[ " docker pull postgres:15-alpine " ] '
# 4. psql ์ฟผ๋ฆฌ ์คํ
aws ssm send-command --instance-ids $INSTANCE_ID \
--document-name " AWS-RunShellScript " \
--parameters ' commands=[ " docker run --rm --network host -e PGPASSWORD= < password > postgres:15-alpine psql -h < db-host > -U postgres -d serverpod -c \ " < SQL > \ " " ] '
๊ฒฐ๊ณผ polling ํจํด#
CMD_ID=$(...) # send-command ๋ฐํ๊ฐ
until aws ssm get-command-invocation \
--command-id $CMD_ID --instance-id $INSTANCE_ID \
--query ' Status ' --output text | grep -qE " ^(Success|Failed)$ " ; do
sleep 3
done
aws ssm get-command-invocation --command-id $CMD_ID \
--instance-id $INSTANCE_ID \
--query ' [Status,StandardOutputContent] ' --output text
์ฃผ์#
-
psql์kobic-server์ปจํ ์ด๋์ ํฌํจ๋์ง ์์ โ ๋ฐ๋์ ๋ณ๋postgres:15-alpine์ปจํ ์ด๋ ์ฌ์ฉ - ASG ๊ต์ฒด๋ก EC2 ์ธ์คํด์ค ID๋ ์์ฃผ ๋ฐ๋ โ ํ๋์ฝ๋ฉ ๊ธ์ง, ๋งค๋ฒ ์กฐํ
- DB ๋น๋ฐ๋ฒํธ๋
kobic-server์ปจํ ์ด๋ ๋ด๋ถconfig/passwords.yaml์์ runmode ์น์ ๋ณ๋ก ๋ถ๋ฆฌ๋จ
์ฐธ๊ณ : 2026-04-18 ํ๋ก๋์ drift ์ง๋จยท์๋ migration ๋๊ธฐํ ์ ํ์ฉ (Issue #5022)
ElastiCache AUTH์ SSM Parameter Store ๋๊ธฐํ ์ด์#
์ฆ์#
RedisError(WRONGPASS invalid username-password pair or user is disabled)
์๋ฒ ๋ก๊ทธ์ ๋ฐ๋ณต์ ์ผ๋ก ์ถ๋ ฅ. ์ ํ๋ฆฌ์ผ์ด์ ์ Redis ์์กด ๊ธฐ๋ฅ(์บ์ ๋ฑ) ๋์ ์คํจ.
์์ธ#
Terraform์ด ๊ด๋ฆฌํ๋ 3๊ฐ ๋ฆฌ์์ค ๊ฐ ๋๊ธฐํ๊ฐ ๊นจ์ง ์ ์์:
| ๋ฆฌ์์ค | ์ญํ |
|---|---|
random_password.redis_auth_token | ํ ํฐ ์์ฑ |
aws_ssm_parameter.redis_auth_token | SSM Parameter Store ์ ์ฅ (์๋ฒ๊ฐ ์ฐธ์กฐ) |
aws_elasticache_replication_group.xxx.auth_token | ์ค์ Redis ํด๋ฌ์คํฐ AUTH |
Terraform apply ๋ถ๋ถ ์คํจ ๋๋ ์๋ ๋ณ๊ฒฝ์ผ๋ก SSM๊ณผ ElastiCache ๊ฐ์ด ์ด๊ธ๋๋ฉด WRONGPASS.
์ง๋จ#
# SSM ํ๋ผ๋ฏธํฐ ์ต์ข
๋ณ๊ฒฝ์ผ
aws ssm describe-parameters \
--parameter-filters " Key=Name,Values=/kobic/staging/redis/auth-token " \
--query ' Parameters[0].[LastModifiedDate,Version] ' --output text
# ElastiCache AUTH ์ต์ข
๋ณ๊ฒฝ์ผ
aws elasticache describe-replication-groups \
--replication-group-id kobic-staging \
--query ' ReplicationGroups[0].AuthTokenLastModifiedDate ' --output text
๋ ๋ ์ง๊ฐ ํฌ๊ฒ ์ฐจ์ด๋๋ฉด drift ํ์ .
ํด๊ฒฐ โ ROTATE ์ ๋ต#
SSM ๊ฐ์ ๊ธฐ์ค์ผ๋ก ElastiCache๋ฅผ ๋๊ธฐํ:
# 1. SSM์์ AUTH ํ ํฐ ์กฐํ
TOKEN=$(aws ssm get-parameter \
--name /kobic/staging/redis/auth-token --with-decryption \
--query ' Parameter.Value ' --output text)
# 2. ElastiCache AUTH ROTATE
aws elasticache modify-replication-group \
--replication-group-id kobic-staging \
--auth-token " $TOKEN " \
--auth-token-update-strategy ROTATE \
--apply-immediately
# 3. ์๋ฃ ํ์ธ (PendingAuthTokenStatus: None, Status: available)
aws elasticache describe-replication-groups \
--replication-group-id kobic-staging \
--query ' ReplicationGroups[0].{Status:Status,PendingAuthTokenStatus:PendingModifiedValues.AuthTokenStatus} '
# 4. ์ปจํ
์ด๋ ์ฌ์์ โ ํ์ (๊ธฐ์กด connection ์บ์๊ฐ old token ์ ์ง)
aws ssm send-command --instance-ids < INSTANCE_ID > \
--parameters ' commands=[ " docker restart kobic-server " ] '
์ ๋ต ์ ํ#
| ์ ๋ต | ํจ๊ณผ | ์ฌ์ฉ ์์ |
|---|---|---|
ROTATE | ๊ธฐ์กด + ์ ๊ท ํ ํฐ ๋ ๋ค ํ์ฉ | ์ผ๋ฐ์ drift ๋ณต๊ตฌ (๊ถ์ฅ) |
SET | ์ ๊ท ํ ํฐ๋ง ํ์ฉ | ๊ธํ ๋ณด์ ์กฐ์น |
DELETE | AUTH ๋นํ์ฑํ | ๋๋ฌพ |
ROTATE ํ ๋ฒ ํ์๋ ๊ธฐ์กด ํ ํฐ์ด ์ฌ์ ํ ํ์ฉ ์ํ์ด๋ฏ๋ก, ์์ ์ ํ ์ ํ ๋ฒ ๋ ROTATE ํ์.
์ฐธ๊ณ : Issue #5023์์ Staging ElastiCache AUTH(2026-02-12)์ SSM(2026-04-02) ๊ฐ 2๊ฐ์ drift ๋ณต๊ตฌ.
RDS Storage-Full ์ฅ์ ๋์#
์ฆ์#
- ์๋ฒ 502/503 ์๋ฌ (์ฑ์์ "์๋ฒ ์ ๊ฒ ์ค" ํ๋ฉด)
- RDS ์ธ์คํด์ค ์ํ:
storage-full - CloudWatch
FreeStorageSpace๋ฉํธ๋ฆญ์ด 0์ ๋๋ฌ
์ง๋จ ์์#
์ธํ๋ผ ์ฅ์ ๋ฐ์ ์ RDS๋ฅผ ๊ฐ์ฅ ๋จผ์ ํ์ธํฉ๋๋ค (๊ฐ์ฅ ํํ ์์ธ).
# 1. RDS ์ธ์คํด์ค ์ํ ํ์ธ
aws rds describe-db-instances \
--db-instance-identifier kobic-staging \
--query ' DBInstances[0].{Status:DBInstanceStatus,Storage:AllocatedStorage,MaxStorage:MaxAllocatedStorage,FreeStorage:FreeStorageSpace} '
# 2. FreeStorageSpace ๋ฉํธ๋ฆญ ํ์ธ (์ต๊ทผ 1์๊ฐ)
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name FreeStorageSpace \
--dimensions Name=DBInstanceIdentifier,Value=kobic-staging \
--start-time $(date -u -v-1H +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 300 --statistics Minimum \
--output table
# 3. ์คํ ๋ฆฌ์ง ์ฌ์ฉ ์์ธ ๋ถ์ (DB ์ ์ ํ)
SELECT pg_size_pretty(pg_database_size( ' serverpod ' )) as db_size;
-- ํ
์ด๋ธ๋ณ ์ฉ๋ ํ์ธ
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname|| ' . ' ||tablename)) as total_size
FROM pg_tables
WHERE schemaname = ' public '
ORDER BY pg_total_relation_size(schemaname|| ' . ' ||tablename) DESC
LIMIT 20;
-- WAL ์ฌ์ด์ฆ ํ์ธ
SELECT pg_size_pretty(pg_wal_size()) as wal_size;
์คํ ๋ฆฌ์ง ํ์ฅ ์ ์ฐจ#
# ๊ธด๊ธ ์คํ ๋ฆฌ์ง ํ์ฅ (์ฆ์ ์ ์ฉ, ๋ค์ดํ์ ์์)
aws rds modify-db-instance \
--db-instance-identifier kobic-staging \
--allocated-storage 200 \
--max-allocated-storage 500 \
--apply-immediately
# ํ์ฅ ์งํ ์ํ ํ์ธ (storage-optimization ์ํ๊ฐ ๋จ)
aws rds describe-db-instances \
--db-instance-identifier kobic-staging \
--query ' DBInstances[0].DBInstanceStatus '
์ฃผ์์ฌํญ:
- RDS ์คํ ๋ฆฌ์ง ํ์ฅ์ ์ถ์ ๋ถ๊ฐ (ํ๋ฒ ๋๋ฆฌ๋ฉด ์ค์ผ ์ ์์)
- ํ์ฅ ํ 6์๊ฐ ์ฟจ๋ค์ด (์ถ๊ฐ ํ์ฅ ๋ถ๊ฐ)
storage-optimization์ํ์์๋ DB๋ ์ ์ ์ฌ์ฉ ๊ฐ๋ฅ
Replication Slot ์ ๋ฆฌ#
CDC(Change Data Capture)์ฉ replication slot์ด ์คํ ๋ฆฌ์ง๋ฅผ ์๋นํ๋ ์ฃผ์ ์์ธ์ ๋๋ค.
-- replication slot ์ํ ํ์ธ
SELECT slot_name, slot_type, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained_wal
FROM pg_replication_slots;
-- ๋นํ์ฑ slot ์ญ์ (ClickHouse ClickPipe ์ฐ๊ฒฐ ๋๊ธด ๊ฒฝ์ฐ)
SELECT pg_drop_replication_slot( ' slot_name_here ' );
Terraform ์ฝ๋ Drift ์ด์#
๊ธด๊ธ ํ์ฅ ํ Terraform ์ฝ๋์ ์ค์ ์ธํ๋ผ๊ฐ ๋ถ์ผ์นํฉ๋๋ค. ๋ณ๋ ์ด์๋ก ์ฝ๋๋ฅผ ์ ๋ฐ์ดํธํด์ผ ํฉ๋๋ค.
| ํ๊ฒฝ | Terraform ์ฝ๋ (ํ์ฌ) | ์ค์ ์ธํ๋ผ (ํ์ฅ ํ) | ํ์ํ ๋ณ๊ฒฝ |
|---|---|---|---|
| staging | allocated_storage=20, max=50 |
allocated_storage=200, max=500 |
์ฝ๋ ์ ๋ฐ์ดํธ ํ์ |
| production | allocated_storage=20, max=100 |
ํ์ธ ํ์ | ์ํฉ์ ๋ฐ๋ผ |
ํ์ผ ์์น: backend/kobic_server/deploy/aws/terraform/variables.tf (line 203-235,
db_configs ๋ณ์)
CodeDeploy ASG ๋์ ๋ฌธ์ #
์ฆ์#
- ๋ฐฐํฌ ์ CodeDeploy๊ฐ ์ค๋๋ (์ด๋ฏธ ์ญ์ ๋) ASG์ ๋ฐฐํฌ ์๋
- ๋ฐฐํฌ ์คํจ ๋๋ ์ง์ฐ
์์ธ#
CodeDeploy Deployment Group์ ๊ณผ๊ฑฐ Auto Scaling Group ์ด๋ฆ์ด ๋จ์ ์์. ASG๋ฅผ ์ฌ์์ฑ(terraform destroy/apply)ํ ๋ CodeDeploy๊ฐ ์๋ ์ ๋ฆฌํ์ง ์์.
์ง๋จ#
# CodeDeploy Deployment Group์ ASG ๋ชฉ๋ก ํ์ธ
aws deploy get-deployment-group \
--application-name kobic-app \
--deployment-group-name kobic-staging-dg \
--query ' deploymentGroupInfo.autoScalingGroups[].name '
# ์ค์ ์กด์ฌํ๋ ASG ํ์ธ
aws autoscaling describe-auto-scaling-groups \
--query ' AutoScalingGroups[?contains(AutoScalingGroupName, `kobic`)].AutoScalingGroupName '
ํด๊ฒฐ#
# ์ฌ๋ฐ๋ฅธ ASG๋ง ํฌํจํ๋๋ก Deployment Group ์
๋ฐ์ดํธ
aws deploy update-deployment-group \
--application-name kobic-app \
--current-deployment-group-name kobic-staging-dg \
--auto-scaling-groups " kobic-staging-asg " # ํ์ฌ ์ ํจํ ASG๋ง ์ง์
์๋ฐฉ#
- Terraform์์ ASG ์ฌ์์ฑ ์ CodeDeploy Deployment Group๋ ํจ๊ป ์ ๋ฐ์ดํธ๋๋์ง ํ์ธ
- CI/CD ํ์ดํ๋ผ์ธ์ stale ASG ์ ๋ฆฌ ์คํฌ๋ฆฝํธ ์ถ๊ฐ ๊ฒํ
Colima VM ๋์คํฌ ์ ๊น (๋ก์ปฌ ๊ฐ๋ฐ)#
์ฆ์#
colima start์คํจ- ์๋ฌ ๋ฉ์์ง:
disk is locked๋๋qemu: could not acquire disk lock
์์ธ#
Colima VM์ด ๋น์ ์ ์ข ๋ฃ๋์ด ๋์คํฌ ํ์ผ์ ๋ฝ์ด ๋จ์ ์์.
ํด๊ฒฐ#
# 1. Colima ๊ฐ์ ์ข
๋ฃ
colima stop --force
# 2. ๋ฝ ํ์ผ ์ ๊ฑฐ
rm -f ~/.colima/default/diffdisk # ๋๋ ํด๋น ํ๋กํ์ผ ๊ฒฝ๋ก
rm -f ~/.lima/colima/diffdisk
# 3. QEMU ํ๋ก์ธ์ค ์ ๋ฆฌ
pkill -f qemu-system
# 4. ์ฌ์์
colima start
๋์: ๋ฌธ์ ๊ฐ ์ง์๋๋ฉด VM ์ญ์ ํ ์ฌ์์ฑ
colima delete
colima start --cpu 4 --memory 8 --disk 60
Docker ๊ธฐ๋ฐ ๋ฐฐํฌ ์ํฌํ๋ก์ฐ#
๋ฐฐํฌ ํ๋ฆ#
GitHub Actions โ Docker Build โ ECR Push โ CodeDeploy โ ASG Instance
| ๋จ๊ณ | ๋๊ตฌ | ์ค๋ช |
|---|---|---|
| ๋น๋ | GitHub Actions | Docker ์ด๋ฏธ์ง ๋น๋ |
| ์ ์ฅ | Amazon ECR | ์ด๋ฏธ์ง ๋ ์ง์คํธ๋ฆฌ |
| ๋ฐฐํฌ | CodeDeploy | Rolling ๋ฐฐํฌ |
| ์คํ | EC2 (ASG) | Docker Compose๋ก ์คํ |
๋ฐฐํฌ ๋์๋ณ ๋ธ๋์น#
| ํ๊ฒฝ | ์ฝ๋ ๋ฐ์ ๋ธ๋์น | ๋ฐฐํฌ ๋ฐฉ๋ฒ |
|---|---|---|
| Staging | development |
GitHub Actions workflow_dispatch |
| Production | main |
main ๋ธ๋์น push ์ ์๋ ๋ฐฐํฌ |
CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ๋ช ๋ช #
| ํ๊ฒฝ | Application | Deployment Group | ๋ฐฐํฌ ์ ๋ต |
|---|---|---|---|
| Staging | kobic-app |
kobic-staging-group |
in-place (kobic-staging-group-base ASG ์ฌ์ฌ์ฉ) |
| Production | kobic-app |
kobic-production-group |
Blue/Green (์ ๊ท ASG ์์ฑ, ๊ตฌ ASG ์ด๋ฆ: CodeDeploy_kobic-production-group_d-XXX) |
์ฃผ์: ๊ณผ๊ฑฐ ๋ฌธ์์ kobic-staging-dg๊ฐ ์ธ๊ธ๋์๋๋ผ๋ ์ค์ ์ด๋ฆ์ kobic-staging-group
์
๋๋ค.
aws deploy list-deployment-groups --application-name kobic-app๋ก ํ์ฌ ์ํ ํ์ธ.
์ปจํ ์ด๋ ์ด๋ฏธ์ง ํ๊ทธ ํ์#
{env}-{shortSha}-{timestamp}
์: production-8023d1ba-20260418005915
staging-87eec5a2-20260418084056
shortSha๋ ๋จธ์ง commit์ ์ 8์๋ฆฌ. ํ๊ทธ๋ก ๋ฐฐํฌ๋ ๋ฒ์ ์ ์ญ์ถ์ ํ ์ ์์ต๋๋ค.
๋ฐฐํฌ ๋ฐ์ ๊ฒ์ฆ ๋ฃจํด#
ํ๋ก๋์ ๋ฐฐํฌ ํ ์ค์ ์ปจํ ์ด๋์ ์ ์ด๋ฏธ์ง๊ฐ ์คํ ์ค์ธ์ง ํ์ธ:
# 1. ์ต์ CodeDeploy ๋ฐฐํฌ ID ์กฐํ
aws deploy list-deployments \
--application-name kobic-app \
--deployment-group-name kobic-production-group \
--max-items 1 \
--query ' deployments[0] ' --output text
# 2. ๋ฐฐํฌ ์ํ ํ์ธ (Succeeded ์ธ์ง)
aws deploy get-deployment --deployment-id < id > \
--query ' deploymentInfo.{Status:status,CompleteTime:completeTime} '
# 3. ํ์ฌ ์๋น์ค ์ค์ธ EC2 ์ธ์คํด์ค ์กฐํ (Blue/Green์ด๋ฉด ์ ASG)
aws autoscaling describe-auto-scaling-groups \
--query ' AutoScalingGroups[?contains(AutoScalingGroupName, `production`)].{Name:AutoScalingGroupName,ID:Instances[0].InstanceId} '
# 4. ์คํ ์ค ์ปจํ
์ด๋ ์ด๋ฏธ์ง ํ์ธ (SSM send-command)
aws ssm send-command --instance-ids < id > \
--document-name " AWS-RunShellScript " \
--parameters ' commands=[ " docker ps --format \ " { { .Image } } { { .Status } } \ " | grep kobic " ] ' \
--query ' Command.CommandId ' --output text
# 5. ๊ฒฐ๊ณผ ํ์ธ (๋ช
๋ น ์คํ ํ ~6์ด ๋๊ธฐ)
aws ssm get-command-invocation \
--command-id < command-id > --instance-id < id > \
--query ' StandardOutputContent ' --output text
์๋ฒ ๋ก๊ทธ ์ถ์ถ (IAP ๋ฑ ๊ธฐ๋ฅ๋ณ ๋๋ฒ๊น ):
# ์ต๊ทผ 30๋ถ IAP ๊ด๋ จ ๋ก๊ทธ
aws ssm send-command --instance-ids < id > \
--document-name " AWS-RunShellScript " \
--parameters ' commands=[ " docker logs $(docker ps --format \ " { { .ID } } \ " | head -1) --since 30m 2 > & 1 | grep -E \ " \\[IAP\\]|App Store\ " | tail -40 " ] '
CloudWatch ์๋ ์ค์ ํํฉ#
ํ์ฌ ์ค์ ๋ ์๋#
| ์๋ | ์๊ณ๊ฐ | alarm_actions |
|---|---|---|
database-high-cpu | 80% | ๋น ๋ฐฐ์ด (์๋ฆผ ์์) |
database-high-connections | 50๊ฐ | ์ค์ ์์ |
๋ฏธ์ค์ (๊ถ์ฅ ์ถ๊ฐ)#
| ์๋ | ๊ถ์ฅ ์๊ณ๊ฐ | ์ฉ๋ |
|---|---|---|
FreeStorageSpace | < 5GB | RDS ์คํ ๋ฆฌ์ง ๋ถ์กฑ ์ฌ์ ๊ฐ์ง |
FreeableMemory | < 256MB | RDS ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ๊ฐ์ง |
SwapUsage | > 256MB | ๋ฉ๋ชจ๋ฆฌ ์ค์ ๋ฐ์ ๊ฐ์ง |
์ฐธ๊ณ : ํ์ฌ alarm_actions = []๋ก ์ค์ ๋์ด ์์ด ์๋์ด ๋ฐ์ํด๋ ์๋ฆผ์ด ์ ๋ฌ๋์ง ์์.
SNS ํ ํฝ ์ฐ๊ฒฐ์ด ํ์ํฉ๋๋ค.
์ธํ๋ผ ๋๋ฒ๊น ์์ (์ฅ์ ๋ฐ์ ์)#
์ฅ์ ๋ฐ์ ์ ์๋ ์์๋ก ํ์ธํฉ๋๋ค:
1. RDS ์ํ ํ์ธ (storage-full์ด ๊ฐ์ฅ ํํ ์์ธ)
โ
2. EC2/ASG ์ธ์คํด์ค ์ํ ํ์ธ
โ
3. Target Group ํฌ์ค์ฒดํฌ ํ์ธ
โ
4. ALB ์ ๊ทผ ๋ก๊ทธ ํ์ธ
โ
5. CodeDeploy ๋ฐฐํฌ ์ํ ํ์ธ
โ
6. CloudWatch ๋ก๊ทธ ํ์ธ
๋น ๋ฅธ ์ํ ํ์ธ ๋ช ๋ น์ด#
# RDS ์ํ
aws rds describe-db-instances \
--query ' DBInstances[*].{ID:DBInstanceIdentifier,Status:DBInstanceStatus,Storage:AllocatedStorage} ' \
--output table
# ASG ์ธ์คํด์ค ์ํ
aws autoscaling describe-auto-scaling-groups \
--query ' AutoScalingGroups[?contains(AutoScalingGroupName,`kobic`)].{Name:AutoScalingGroupName,Desired:DesiredCapacity,Instances:Instances[*].{ID:InstanceId,Health:HealthStatus } } ' \
--output json
# Target Group ํฌ์ค
aws elbv2 describe-target-health \
--target-group-arn < target-group-arn > \
--output table
# ์ต๊ทผ ๋ฐฐํฌ ์ํ
aws deploy list-deployments \
--application-name kobic-app \
--deployment-group-name kobic-staging-dg \
--max-items 3 \
--query ' deployments '
ํ์ ์์ (TODO)#
์๋ ํญ๋ชฉ๋ค์ ์ธํ๋ผ ์์ ์ฑ ํฅ์์ ์ํด ๋ณ๋ ์ด์๋ก ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค.
HIGH (๊ธด๊ธ)#
-
RDS FreeStorageSpace CloudWatch ์๋ ์ถ๊ฐ: Terraform
database.tf์ FreeStorageSpace < 5GB ์๋ ์ถ๊ฐ -
alarm_actions์ SNS ํ ํฝ ์ฐ๊ฒฐ: ํ์ฌ ๋น ๋ฐฐ์ด๋ก ์ค์ ๋ ๊ธฐ์กด ์๋์
deployment_sns_topic_arn์ฐ๊ฒฐ -
Terraform variables.tf staging db_configs ์
๋ฐ์ดํธ:
allocated_storage: 20โ200,max_allocated_storage: 50โ500 - Replication slot ์คํ ๋ฆฌ์ง ์์ธ ๋ถ์: ClickHouse ClickPipe CDC slot์ WAL retention ํ์ธ ๋ฐ ์ต์ ํ
- Redis AUTH ์ค์ ํ์ธ: Redis ์ ์ ์ ๋น๋ฐ๋ฒํธ ๊ด๋ จ ์ด์ ์ ๊ฒ
MEDIUM#
- stale ASG ์ ๋ฆฌ ์๋ํ: CodeDeploy Deployment Group์์ ์กด์ฌํ์ง ์๋ ASG ์๋ ์ ๊ฑฐ
- replication slot ๋ชจ๋ํฐ๋ง ์ถ๊ฐ: retained WAL ํฌ๊ธฐ ๊ธฐ๋ฐ ์๋ ์ค์
- ์ ๊ธฐ ์ ๋ฆฌ ํฌ๋ก ์ก: ์ค๋๋ ๋ฐฐํฌ ์ํฐํฉํธ, ๋ก๊ทธ ๋ฑ ์๋ ์ ๋ฆฌ
-
deletion_protection ํ๊ฒฝ๋ณ ๋ถ๋ฆฌ: production๋ง
true, staging/development๋false์ ์ง (ํ์ฌ ๋ชจ๋false)
LOW (์๋ํ ์คํฌ ํ๋ณด)#
ํฅํ Claude Code ์คํฌ๋ก ์๋ํ ๊ฐ๋ฅํ ํจํด๋ค:
| ์คํฌ๋ช | ์ฐ์ ์์ | ์ค๋ช |
|---|---|---|
aws/ssm-exec | HIGH | SSM์ผ๋ก EC2 ์ธ์คํด์ค์ ๋ช ๋ น ์คํ |
aws/rds-monitor | HIGH | RDS ์ํ/๋ฉํธ๋ฆญ ์ค์๊ฐ ํ์ธ |
aws/health | HIGH | ์ ์ฒด ์ธํ๋ผ ํฌ์ค์ฒดํฌ ๋์๋ณด๋ |
aws/tg-health | MEDIUM | Target Group ํฌ์ค์ฒดํฌ ์ํ ํ์ธ |
aws/asg-cleanup | MEDIUM | stale ASG/๋ฐฐํฌ ๊ทธ๋ฃน ์ ๋ฆฌ |
aws/incident-response | LOW | ์ฅ์ ๋์ ์๋ํ ๋ฐ๋ถ |
๊ด๋ จ ํ์ผ#
backend/kobic_server/deploy/aws/terraform/variables.tf- RDS/ASG ์ค์ (root)-
backend/kobic_server/deploy/aws/terraform/modules/environment/database.tf- RDS ์ธ์คํด์ค ๋ฐ CloudWatch ์๋ backend/kobic_server/deploy/aws/terraform/modules/environment/variables.tf- ํ๊ฒฝ๋ณ ๋ณ์ ์ ์backend/kobic_server/deploy/aws/terraform/modules/environment/compute.tf- ASG/EC2 ์ค์ -
backend/kobic_server/deploy/aws/terraform/modules/shared/codedeploy.tf- CodeDeploy ์ฑ ์ค์ -
backend/kobic_server/deploy/aws/terraform/modules/environment/codedeploy.tf- ํ๊ฒฝ๋ณ Deployment Group