Skip to content

Commit d7bf6eb

Browse files
authored
Merge pull request #26 from heungh/main
Aurora bluegreen deployment blog
2 parents cbabc98 + 404cf13 commit d7bf6eb

9 files changed

Lines changed: 1035 additions & 0 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ AWS 기술 블로그(<https://aws.amazon.com/ko/blogs/tech/>)에서 제공하는
99
| [Amazon Bedrock을 활용한 Aurora Mysql 운영하기](https://aws.amazon.com/ko/blogs/tech/auroramysql-monitoring-with-amazonbedrock/) | [bedrock_aurora_mysql](bedrock_aurora_mysql)|
1010
| [Amazon OpenSearch Service 의 LTR플러그인을 활용한 검색 품질 개선]() | [opensearch_ltr](opensearch_ltr)|
1111
| [Amazon Bedrock Agents와 MCP(Model Context Protocol) 통합하기](https://aws-blogs-prod.amazon.com/tech/amazon-bedrock-agents-mcp-model-context-protocol/) | [bedrock-mcp-agent-cdk](bedrock-mcp-agent-cdk)|
12+
| [알아두면 쓸모 있는 Aurora MySQL Bluegreen 배포 스크립트](https://aws.amazon.com/ko/blogs/tech/auroramysql-task-automation-tip/) | [auroramysql-task-automation-tip](auroramysql-task-automation-tip)|
1213

1314
## Security
1415

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# AWS Aurora MySQL Blue/Green Deployment Automation Tool
2+
3+
이 프로젝트의 내용은 AWS 환경 전반에 걸쳐 Amazon Aurora MySQL 클러스터의 블루/그린 배포를 관리하기 위한 종합적인 자동화 툴킷을 제공합니다. 이를 통해서 Aurora mysql의 블루그린 배포 프로세스를 단순화하고, 다운타임을 줄이며, 안전한 데이터베이스 버전 업그레이드를 원활하게 진행하는데 도움이 될 것입니다.
4+
아래에서 소개하는 각각의 파이썬 스크립트는 블루그린 배포 생성, 배포과정의 상태 모니터링, 블루와 그린의 전환과정, 배포이후 old 클러스터(블루클러스트)의 정리 작업을 포함한 전체 블루/그린 배포 수명 주기의 자동화된 관리를 제공합니다. 단일 및 다중 계정 배포를 모두 지원하여 소수의 인원으로 많은 수의 데이터베이스를 업그레이드 해야 하는 기업 환경에 가장 적합합니다.
5+
6+
## Repository Structure
7+
```
8+
.
9+
├── bluegreen_create.py # 새로운 Blue/Green 배포를 생성하는데 사용하는 스크립트입니다.
10+
├── blugreen_creation_check.py # Blue/Green 배포 생성과정에서 진행상태를 체크하는데 사용하는 스크립트입니다.
11+
├── bluegreen_switchover_precheck.py # Blue/Green을 전환하기전, 사전 체크하는 스크립트입니다.
12+
├── bluegreen_switchover.py # Blue/Green을 전환할때 사용하는 스크립트 입니다.
13+
├── bluegreen_delete_old.py # Blue/Green 배포 이후 구 클러스터를 삭제할때 사용하는 스크립트입니다.
14+
└── multi-account-bluegreen-deployment.py # 여러개의 어카운트, 리전에 있는 Aurora mysql 클러스터의 Blue/Green 배포를 생성합니다.
15+
```
16+
17+
## Usage Instructions
18+
### 사전준비사항
19+
- Python 3.6 또는 그 이상
20+
- AWS 자격 증명이 적절한 권한으로 구성되었는지 확인
21+
- boto3 ,rich 라이브러리 설치 되었는지 확인
22+
23+
실행에 필요한 AWS 권한들:
24+
- rds:CreateBlueGreenDeployment
25+
- rds:DeleteBlueGreenDeployment
26+
- rds:DescribeBlueGreenDeployments
27+
- rds:SwitchoverBlueGreenDeployment
28+
- rds:DescribeDBClusters
29+
- rds:DeleteDBCluster
30+
- rds:DeleteDBInstance
31+
- sts:GetCallerIdentity
32+
33+
### 환경 배포 과정
34+
```bash
35+
# Clone the repository
36+
git clone <repository-url>
37+
cd aurora-bluegreen-deployment
38+
39+
# Install required Python packages
40+
pip install boto3 rich
41+
```
42+
43+
### Quick Start
44+
1. Create a new Blue/Green deployment:
45+
```bash
46+
python bluegreen_create.py
47+
```
48+
1. 위 스크립트를 실행할때 다음 사항들을 입력해주세요:
49+
- AWS region
50+
- DB Cluster identifier
51+
- Target engine version
52+
- Parameter group name
53+
54+
2. 배포를 실행한 다음 배포상태를 아래 스크립트로 체크합니다:
55+
```bash
56+
python blugreen_creation_check.py --region us-west-2
57+
```
58+
59+
3. 배포가 잘 되고 업그레이드가 잘 진행되었다면 사전체크를 진행하여 문제가 없다면, Blue/Green 전환을 진행합니다:
60+
```bash
61+
python bluegreen_switchover_pre_check,py us-west-2
62+
python bluegreen_switchover.py us-west-2
63+
```
64+
65+
4. 배포 이후 사용하지 않는 old cluster 삭제과정입니다:
66+
```bash
67+
# 특정리전에 있는 클러스터들을 모두 삭제할때 사용합니다.
68+
python bluegreen_delete_old.py --region us-west-2
69+
70+
# 특정 클러스터만 삭제할 때 사용합니다.
71+
python bluegreen_delete_old.py --region us-west-2 --cluster_name mydb
72+
```
73+
74+
### 멀티 어카운트, 멀티 리전에 배포하는 예제
75+
1. Multi-account, Multi-region에 Blue/Green 배포를 생성하는 과정입니다.
76+
```bash
77+
# account_id,secret_name,region 정보가 accounts.csv에 들어가 있어야 합니다.
78+
python multi-account-bluegreen-deployment.py accounts.csv
79+
```
80+
81+
82+
### Troubleshooting
83+
Common issues and solutions:
84+
85+
1. Deployment Creation Fails
86+
- Error: "InvalidParameterCombination"
87+
- 새로운 데이터베이스 버전이 현재 버전과 호환되는지 확인하세요.
88+
- 설정한 파라미터 그룹이 실제로 존재하는지, 그리고 새 데이터베이스 버전과 호환되는지 확인하세요.
89+
- 원본 데이터베이스 클러스터가 현재 사용 가능한 상태인지 확인하세요. 유지보수 중이거나 다른 작업 중이면 배포를 시작할 수 없습니다.
90+
91+
2. Switchover Issues
92+
- Error: "InvalidDBClusterState"
93+
- 새로 만든 데이터베이스 배포가 완전히 준비되어 "사용 가능(AVAILABLE)" 상태인지 확인하세요. 아직 준비 중이면 전환할 수 없습니다.
94+
- 데이터베이스에 예정된 유지보수 작업이 있는지 확인하세요. 있다면 먼저 처리해야 합니다.
95+
- 백업이나 복원 같은 다른 작업이 진행 중인지 확인하고, 완료될 때까지 기다리세요.
96+
97+
3. Deletion Failures
98+
- Error: "ResourceBusy"
99+
- 데이터베이스에서 진행 중인 작업(예: 백업, 복원, 구성 변경)이 있다면 완료될 때까지 기다리세요.
100+
- 이 데이터베이스를 사용 중인 다른 서비스나 애플리케이션이 있는지 확인하세요. 있다면 먼저 연결을 끊어야 합니다.
101+
- 이 작업을 수행할 권한이 충분한지 확인하세요. 특히 데이터베이스 삭제 권한이 있는지 체크해보세요.
102+
103+
Debug Mode:
104+
```bash
105+
# Enable debug logging
106+
export AWS_DEBUG=True
107+
export AWS_LOG_LEVEL=DEBUG
108+
```
109+
110+
## Data Flow
111+
The toolkit manages Aurora MySQL cluster upgrades through AWS Blue/Green deployment mechanism, coordinating creation, validation, and switchover processes.
112+
113+
```ascii
114+
[Source Cluster] --> [Blue/Green Creation] --> [Green Cluster]
115+
|
116+
[Validation & Monitoring] <-------------------------|
117+
|
118+
v
119+
[Switchover] --> [Cleanup Old Resources]
120+
```
121+
122+
Blue/Green 배포 전 과정을 정리하면 다음과 같습니다:
123+
1. 생성 프로세스는 소스 클러스터를 검증하고 Green 환경을 생성합니다.
124+
2. 모니터링은 배포 상태를 확인하여 배포가 제대로 되었는지 검증합니다.
125+
3. 전환과정을 통해 효과적으로 Blue/Green 전환을 진행합니다. (사전에 꼭 먼저 체크하시기 바랍니다)
126+
4. 구 클러스터(old_로 시작하는 블루 클러스터)를 충분한 시간동안 확인하고 나서, 제거합니다.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import boto3
2+
import time
3+
from rich import print
4+
from rich.prompt import Prompt, Confirm
5+
from rich.console import Console
6+
from rich.panel import Panel
7+
8+
console = Console()
9+
10+
11+
def get_current_account_info():
12+
sts_client = boto3.client("sts")
13+
try:
14+
account_info = sts_client.get_caller_identity()
15+
return {"account_id": account_info["Account"], "arn": account_info["Arn"]}
16+
except Exception as e:
17+
console.print(f"[red]AWS 계정 정보 조회 실패: {str(e)}[/red]")
18+
raise
19+
20+
21+
def get_user_inputs():
22+
console.print(Panel.fit("Aurora MySQL Bluegreen 배포 설정", style="bold blue"))
23+
24+
# 현재 계정 정보 가져오기
25+
account_info = get_current_account_info()
26+
console.print(f"[green]현재 AWS 계정: {account_info['account_id']}[/green]")
27+
28+
inputs = {
29+
"account_id": account_info["account_id"], # 자동으로 설정
30+
"region": Prompt.ask(
31+
"AWS 리전을 선택하세요",
32+
choices=["ap-northeast-2", "us-east-1", "eu-central-1", "us-west-2"],
33+
default="us-west-2",
34+
),
35+
"cluster_identifier": Prompt.ask("DB Cluster 식별자를 입력하세요"),
36+
"target_engine_version": Prompt.ask(
37+
"대상 엔진 버전을 선택하세요",
38+
choices=[
39+
"8.0.mysql_aurora.3.05.2",
40+
"8.0.mysql_aurora.3.06.1",
41+
"8.0.mysql_aurora.3.07.1",
42+
"8.0.mysql_aurora.3.08.0",
43+
],
44+
default="8.0.mysql_aurora.3.05.2",
45+
),
46+
"cluster_parameter_group": Prompt.ask(
47+
"파라미터 그룹 이름을 입력하세요", default="mysql80"
48+
),
49+
}
50+
51+
# 입력 확인
52+
console.print("\n[bold]입력하신 정보를 확인해주세요:[/bold]")
53+
for key, value in inputs.items():
54+
console.print(f"{key}: {value}")
55+
56+
if not Confirm.ask("\n입력하신 정보로 배포를 시작할까요?"):
57+
console.print("[red]배포가 취소되었습니다.[/red]")
58+
exit()
59+
60+
return inputs
61+
62+
63+
def construct_cluster_arn(inputs):
64+
return f"arn:aws:rds:{inputs['region']}:{inputs['account_id']}:cluster:{inputs['cluster_identifier']}"
65+
66+
67+
def create_blue_green_deployment(source_cluster_arn, inputs):
68+
rds_client = boto3.client("rds", region_name=inputs["region"])
69+
70+
with console.status("[bold green]Bluegreen 배포 생성 중...") as status:
71+
console.print(f"소스 DB Cluster명: {inputs['cluster_identifier']}")
72+
73+
response = rds_client.create_blue_green_deployment(
74+
BlueGreenDeploymentName="bg-" + inputs["cluster_identifier"],
75+
Source=source_cluster_arn,
76+
TargetEngineVersion=inputs["target_engine_version"],
77+
TargetDBClusterParameterGroupName=inputs["cluster_parameter_group"],
78+
)
79+
80+
deployment_id = response["BlueGreenDeployment"]["BlueGreenDeploymentIdentifier"]
81+
deployment_name = response["BlueGreenDeployment"]["BlueGreenDeploymentName"]
82+
83+
status.update("[bold green]배포 상태 확인 중...")
84+
current_status = rds_client.describe_blue_green_deployments(
85+
BlueGreenDeploymentIdentifier=deployment_id
86+
)["BlueGreenDeployments"][0]["Status"]
87+
88+
console.print(f"현재 상태: {current_status}")
89+
90+
return deployment_name
91+
92+
93+
def main():
94+
try:
95+
inputs = get_user_inputs()
96+
source_cluster_arn = construct_cluster_arn(inputs)
97+
deployment_name = create_blue_green_deployment(source_cluster_arn, inputs)
98+
console.print(
99+
f"[bold green]배포가 성공적으로 생성되었습니다. : {deployment_name}[/bold green]"
100+
)
101+
except KeyboardInterrupt:
102+
console.print("\n[yellow]프로그램이 사용자에 의해 중단되었습니다.[/yellow]")
103+
except Exception as e:
104+
console.print(
105+
f"[bold red]프로그램 실행 중 오류가 발생했습니다: {str(e)}[/bold red]"
106+
)
107+
108+
109+
if __name__ == "__main__":
110+
main()
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import boto3
2+
import sys
3+
import time
4+
import argparse
5+
6+
7+
def parse_arguments():
8+
parser = argparse.ArgumentParser(
9+
description="Aurora 블루그린 배포 및 이전 클러스터 정리 도구"
10+
)
11+
parser.add_argument(
12+
"--region", default="us-west-2", help="AWS 리전 (기본값: us-west-2)"
13+
)
14+
parser.add_argument("--cluster_name", help="삭제할 특정 클러스터 이름 (선택사항)")
15+
return parser.parse_args()
16+
17+
18+
def delete_blue_green_deployments(rds_client):
19+
"""현재 존재하는 블루그린 배포를 삭제합니다."""
20+
try:
21+
response = rds_client.describe_blue_green_deployments()
22+
deployments = response["BlueGreenDeployments"]
23+
24+
if not deployments:
25+
print("삭제할 블루그린 배포가 없습니다.")
26+
return
27+
28+
for deployment in deployments:
29+
identifier = deployment["BlueGreenDeploymentIdentifier"]
30+
cluster_name = deployment["BlueGreenDeploymentName"]
31+
32+
try:
33+
rds_client.delete_blue_green_deployment(
34+
BlueGreenDeploymentIdentifier=identifier
35+
)
36+
print(f"블루그린 배포 삭제 완료: [{identifier}] {cluster_name}")
37+
38+
# 배포 삭제 완료 대기
39+
while True:
40+
try:
41+
rds_client.describe_blue_green_deployments(
42+
BlueGreenDeploymentIdentifier=identifier
43+
)
44+
time.sleep(30)
45+
except:
46+
break
47+
48+
except Exception as e:
49+
print(f"블루그린 배포 [{identifier}] 삭제 중 에러 발생: {e}")
50+
51+
except Exception as e:
52+
print(f"Error: {e}")
53+
54+
55+
def get_old_clusters(rds_client, cluster_name=None):
56+
"""이전 클러스터(_old suffix)를 찾습니다."""
57+
try:
58+
response = rds_client.describe_db_clusters()
59+
old_clusters = []
60+
61+
for cluster in response["DBClusters"]:
62+
cluster_id = cluster["DBClusterIdentifier"]
63+
print("cluster_id", cluster_id)
64+
if cluster_id.endswith("-old1"):
65+
if cluster_name:
66+
print("if cluster_name")
67+
if cluster_name in cluster_id:
68+
old_clusters.append(cluster_id)
69+
else:
70+
print("else cluster_name")
71+
old_clusters.append(cluster_id)
72+
73+
return old_clusters
74+
except Exception as e:
75+
print(f"Error: {e}")
76+
return []
77+
78+
79+
def delete_cluster(rds_client, cluster_identifier):
80+
"""클러스터와 관련 인스턴스들을 삭제합니다."""
81+
try:
82+
# 클러스터에 속한 DB 인스턴스들을 먼저 삭제
83+
response = rds_client.describe_db_clusters(
84+
DBClusterIdentifier=cluster_identifier
85+
)
86+
87+
# DB 인스턴스 삭제
88+
for instance in response["DBClusters"][0]["DBClusterMembers"]:
89+
instance_id = instance["DBInstanceIdentifier"]
90+
print(f"DB 인스턴스 삭제 중: {instance_id}")
91+
rds_client.delete_db_instance(
92+
DBInstanceIdentifier=instance_id, SkipFinalSnapshot=True
93+
)
94+
95+
# 인스턴스 삭제 완료 대기
96+
for instance in response["DBClusters"][0]["DBClusterMembers"]:
97+
instance_id = instance["DBInstanceIdentifier"]
98+
while True:
99+
try:
100+
rds_client.describe_db_instances(DBInstanceIdentifier=instance_id)
101+
time.sleep(30)
102+
except:
103+
break
104+
105+
# 클러스터 삭제
106+
print(f"클러스터 삭제 중: {cluster_identifier}")
107+
rds_client.delete_db_cluster(
108+
DBClusterIdentifier=cluster_identifier, SkipFinalSnapshot=True
109+
)
110+
return True
111+
112+
except Exception as e:
113+
print(f"Error: {e}")
114+
return False
115+
116+
117+
def main():
118+
args = parse_arguments()
119+
120+
# AWS RDS 클라이언트 생성
121+
rds_client = boto3.client("rds", region_name=args.region)
122+
123+
# 1단계: 블루그린 배포 삭제
124+
print("=== 블루그린 배포 삭제 시작 ===")
125+
delete_blue_green_deployments(rds_client)
126+
127+
# 2단계: 이전 클러스터 삭제
128+
print("\n=== 이전 클러스터 삭제 시작 ===")
129+
old_clusters = get_old_clusters(rds_client, args.cluster_name)
130+
131+
if not old_clusters:
132+
print("삭제할 이전 클러스터가 없습니다.")
133+
return
134+
135+
print(f"삭제할 클러스터: {old_clusters}")
136+
for cluster in old_clusters:
137+
if delete_cluster(rds_client, cluster):
138+
print(f"클러스터 {cluster} 삭제 완료")
139+
else:
140+
print(f"클러스터 {cluster} 삭제 실패")
141+
142+
143+
if __name__ == "__main__":
144+
main()
31.7 KB
Loading

0 commit comments

Comments
 (0)