Spring Boot Application Blue/Green Deploy to AWS Elastic Beanstalk Using Jenkins
바로 이전 글 스프링 부트 앱 EB 배포 에서 Spring Boot App 을 AWS console 을 이용해서 Elastic Beanstalk 에 단일 인스턴스 환경으로 배포했다.
하지만, 이전에 올렸던 소스는 local mariaDB 를 바라보고 있다. 따라서 회원가입 및 로그인 기능이 전혀 동작하지 않을 것이다.
이번 글에서 정리할 내용은 local MariaDB 를 AWS RDS MariaDB 를 바라보도록 변경하고, Blue/Green Deploy 를 Jenkins 를 이용해서 진행해볼 예정이다.
아래 포스트들에서 EC2 에 Jenkins 를 설치하고, Gradle 과 Git 을 이용하여 jar 로 Build 하고 S3 versioning 을 통해서 jar 를 관리하는 방법을 정리했었다.
또한 Blue/Green Deploy 를 하기 위해서 기존에 Blue Environment 에 대한 설정을 그대로 따르기 위해, option-settings.json 란 파일을 생성하며 새로운 Environment 생성 시 동일한 구성을 사용할 수 있도록 하겠다.
마지막으로 Jenkins 가 설치된 EC2 에 Elastic Beanstalk Full Access Role 을 부여한다.
기존에 배포된 EB 환경에서는 DB connection 이 이루어지지 않아서 기능이 전혀 동작하고 있지 않은것을 확인할 수 있다.
먼저, local MariaDB 를 바라보고 있는 설정을 AWS RDS MariaDB 로 변경해보겠다.
url 을 AWS RDS 로 변경했다. username 과 password 는 보안상의 이유로 적지 않았다.
그럼 이제 변경사항을 push 하고 Jenkins 를 이용하여 Build 하겠다.
젠킨스로 Build 하고 S3 로 jar 관리하기 글에서 작성한 방법으로 Build 를 했다.
다음은 기존 Environment 와 동일한 Environment 를 만들 수 있도록 구성에 대한 정보를 가져와서 json 파일로 관리하겠다.
아래의 링크를 참고하면 된다.
aws elasticbeanstalk describe-configuration-settings --environment-name {eb_name} --application-name {app_name}
중요한 point 는 SERVER_PORT 5000 뿐이다. 왜냐면 단일 인스턴스 구성이니까.
[
{
"OptionName": "EnvironmentType",
"Namespace": "aws:elasticbeanstalk:environment",
"Value": "SingleInstance"
},
{
"Namespace":"aws:autoscaling:launchconfiguration",
"OptionName":"IamInstanceProfile",
"Value":"aws-elasticbeanstalk-ec2-role"
},
{
"OptionName": "VPCId",
"ResourceName": "AWSEBSecurityGroup",
"Namespace": "aws:ec2:vpc"
},
{
"OptionName": "EnvironmentVariables",
"Namespace": "aws:cloudformation:template:parameter",
"Value": "SERVER_PORT=5000,M2=/usr/local/apache-maven/bin,M2_HOME=/usr/local/apache-maven,JAVA_HOME=/usr/lib/jvm/java,GRADLE_HOME=/usr/local/gradle"
}
]
script 를 작성하기 전에, 현재 Jenkins 가 설치된 EC2 는 S3FullAccess 역할만 가지고 있다. 따라서 EB 생성에 대한 권한이 없기 때문에, 추가로 ElasticBeanstalkFullAccess 역할도 추가 해준다.
그냥 새로 만들고, 새로 할당해준다.
그렇다면 이제 Jenkins 로 들어가서 script 를 작성해보겠다.
Jenkins 에서 마찬가지로 FreeStyle 프로젝트로 새로운 아이템을 생성하고, 스크립트를 작성하면 된다.
스크립트 내용은 간단하다.
이미 구성되어 있는 environment name 을 변수에 할당한다.
새롭게 배포할 environment name 을 뒤에 배포 시점을 변수에 붙여서 할당한다.
S3 에 올라가있는 jar 를 가지고 application-version 을 만든다.
그리고 S3 에 올라가있는 options.json 을 가져온다.
그후에 새로운 environment 를 만든다.
약 5초간 sleep 하고 새로 생성하는 environment Health 가 Green 이 될 때 까지 while 문을 돌면서, 중간중간 2초씩 sleep 한다.
중간에 sleep 하는 이유는 api rate limiting 이 있기 때문에 너무 자주 찌르지 않는다.
그리고 Green 이 되면 이제 Status 가 Ready 가 될 때 까지 기다린다.
Ready 가 되면, 기존이 있는 EB(Blue) 와 새로 생성된 EB(Green) 의 Cname 을 swap 한다.
마지막의 old eb terminate 는 주석으로 막아놨다. swap 을 정상적으로 하고 바로 old eb 를 부술 필요는 없다.
만약에 새로운 eb 환경에 문제가 있을 경우 Cname 을 다시 예전 eb 로 돌릴 수 있도록 보험(?)을 위해서이다.
새로 배포될 환경이 이상이 없다는 확신이 있다면, CNAME SWAP 이후에 바로 old eb 를 부숴도 문제는 없을 것 같다.
#
# create application version using S3 jar
# create EB environment
# swap cnames
# terminate Old environment
# by Dong-Gil
#
# set region
aws configure set region ap-northeast-2
# current Green EB environment name
old_eb_name=$(aws elasticbeanstalk describe-environments --application-name spring-boot-security-app --query 'Environments[?Health==`Green`].EnvironmentName' --output text)
# new EB environment
new_eb_name="spring-boot-security-app"-$(($(date +%s%N)/1000000))
# create application version
aws elasticbeanstalk create-application-version --application-name "spring-boot-security-app" --version-label $new_eb_name --source-bundle "S3Bucket=spring-boot-app-jars,S3Key=security/spring-boot-app.jar"
# get EB-options.json
aws s3api get-object --bucket spring-boot-app-jars --key security/eb-options.json ~/eb-options.json
# create environment
aws elasticbeanstalk create-environment --application-name spring-boot-security-app --environment-name $new_eb_name --solution-stack-name "64bit Amazon Linux 2 v3.0.3 running Corretto 11" --version-label $new_eb_name --option-settings file://~/eb-options.json
# Wait.. 5 seconds
sleep 5s
# Wait.. for new eb to be green
new_eb_health_status=$(aws elasticbeanstalk describe-environments --application-name spring-boot-security-app --query 'Environments[*].[EnvironmentName, Health]' --output text | grep $new_eb_name | awk '{print $2}')
while [ "$new_eb_health_status" != "Green" ]
do
sleep 2s # Wait.. for 2 second For rate limit
echo "new eb health status is : " $new_eb_health_status
new_eb_health_status=$(aws elasticbeanstalk describe-environments --application-name spring-boot-security-app --query 'Environments[*].[EnvironmentName, Health]' --output text | grep $new_eb_name | awk '{print $2}')
done
echo $new_eb_health_status
echo "new eb health status is Green"
# Wait.. for new eb to be ready
new_eb_status=$(aws elasticbeanstalk describe-environments --application-name spring-boot-security-app --query 'Environments[*].[EnvironmentName, Status]' --output text | grep $new_eb_name | awk '{print $2}')
while [ "$new_eb_status" != "Ready" ]
do
sleep 2s # Wait.. for 2 second For rate limit
echo "new eb status is : " $new_eb_status
new_eb_status=$(aws elasticbeanstalk describe-environments --application-name spring-boot-security-app --query 'Environments[*].[EnvironmentName, Status]' --output text | grep $new_eb_name | awk '{print $2}')
done
echo $new_eb_status
echo "new eb health status is Ready"
# Swap CNAME Blue/Green
aws elasticbeanstalk swap-environment-cnames --source-environment-name $old_eb_name --destination-environment-name $new_eb_name
# Terminate Old EB
#aws elasticbeanstalk terminate-environment --environment-name $old_eb_name
아래는 실제 Jenkins 의 로그 이다.
정상적으로 DB connection 이 맺어졌고, 로그인 기능이 정상적으로 작동하는것이 확인이 되었다.
총 2개의 EB environment 를 확인할 수 있다.
댓글
댓글 쓰기