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 를 확인할 수 있다.


댓글

이 블로그의 인기 게시물

About JVM Warm up

About idempotent

About Kafka Basic

About ZGC

sneak peek jitpack

Spring Boot Actuator readiness, liveness probes on k8s

About Websocket minimize data size and data transfer cost on cloud

About G1 GC

대학생 코딩 과제 대행 java, python, oracle 네 번째