[Jenkins] Springboot + Gradle + Github + CodeDeploy + ELB (2)

반응형

이전글에 이어서 jenkins와 codedeploy를 사용해 ec2 server에 무중단 배포를 진행하겠습니다.

3. CodeDeploy

이번에는 codedeploy application을 생성해보겠습니다.

물론 jenkins만 사용해도 원격 server에 deploy를 할 수 있지만, codedeply를 사용하면 좀더 효율적으로 deploy를 수행할 수 있습니다.

특히 elb와 같이 aws에서 제공해주는 서비스를 사용하는 경우에는 codedeply를 사용해 deploy를 수행하는 것이 좋습니다. 만약 codedeploy를 사용하지 않는다면 복잡한 deploy 과정을 거치게 됩니다.

3-1) create application

Codedeploy service로 이동해 create applicatoin을 클릭합니다.

image.png

application name을 입력하고, compute platform은 EC2/On-premises를 선택합니다. create application 을 클릭합니다.

image.png

application 생성이 완료되었습니다. 이제 이 application이 deploy를 수행할 deployment group을 생성해야 합니다. create deployment group을 클릭합니다.

image.png

3-2) create deployment group

deployment group name을 입력하고, service role에서 앞서 생성한 codedeploy role을 반드시 선택합니다.

image.png

deployment type에서 in-plcae를 선택하고, environment configuration에서 ec2 instance를 선택합니다. key는 name을 선택하고 value에는 앞서 생성한 2개의 ec2를 선택합니다.

image.png

앞서 ec2 instance를 생성할때 tag에 name을 입력했던 것이 기억나시나요? 😂 deploy group에서 ec2 instance를 선택할때 다른 instance들과의 구분을 위해서 name tag를 등록했었습니다.

image.png

macthing instances에서 2개의 instance를 잘 찾은 것을 확인할 수 있습니다. deploy settings에는 CodeDeployDefault.OneAtTime을 선택합니다. OneAtTime는 이름에서 확인할 수 있듯이 한번에 한개의 instance에만 deploy를 진행합니다.

따라서 elb를 사용할 경우에는 deploy가 진행되지 않는 instance는 계속 service를 수행하므로 무중단 배포가 가능합니다.

enable load balncer를 선택하고, application load balancer를 선택합니다. choose a target group에는 앞서 생성했던 elb target을 선택합니다. create deployment group 버튼을 클릭합니다.

image.png

deployment group 생성이 완료되었습니다.

image.png

4. S3

다음으로 jenkins에서 build한 파일들을 upload할 s3 bucket을 생성하겠습니다.

S3 service로 이동해 create bucket을 클릭합니다.

image.png

bucket name을 입력하고 나머지 값들은 다 default 값으로 bucket을 생성합니다.

image.png

bucket이 생성되었습니다.

image.png

5. Jenkins

이제 jenkins를 설정해보도록 하겠습니다.

저는 ec2 server에 따로 jenkins server를 설치했습니다. jenkins console에 접속합니다.

image.png

5-1) download plugin

먼저 build & deploy에 사용할 plugin을 다운받아야 합니다. manage jenkins의 manage plugin으로 이동해 다음과 같은 plugin을 다운받습니다.

• AWS CodeDeploy Plugin for Jenkins
• GitHub Integration Plugin
• Gradle Plugin

image.png

5-2) create jenkins job

설치가 완료되었으면 restart 후에 new item을 클릭해 jenkins job을 생성합니다.

image.png

name을 입력하고, freesyle project를 선택합니다.

image.png

general에서 github project를 선택하고 github repository의 url을 입력합니다. 이 repository는 local에서 개발한 spring boot application을 push할 곳입니다.

image.png

저는 아래의 github repository를 사용하겠습니다.

image.png

source code management에서 git을 선택하고 repository url을 입력합니다.

image.png

repository url은 아래에서 확인할 수 있습니다.

image.png

build triggers에서는 github hook trigger를 선택합니다.

image.png

build의 첫번째는 Invoke gradle script를 선택하고 아래와 같이 입력합니다.

• gradle wrapper를 선택해 jenkins의 내장 gradle을 사용해 build를 진행합니다.
• make gradlew executable을 반드시 선택합니다.
• wrapper location은 jenkins job의 workspace를 사용하겠습니다.
• 마지막으로 task에 clean bootJar를 입력해 실행가능한 jar를 생성합니다.

image.png

다음 build는 execute shell을 선택하고 아래와 같이 입력합니다. 반드시 위의 invoke gradle script 다음의 순서에 놓아야 합니다.

아래의 command를 통해 deploy라는 directory를 만들어 앞서 Invoke gradle script로 생성된 jar와 codedeploy에서 build에 사용할 appspec.yml & deploy.sh 파일을 옮깁니다.

codedeploy application에서 deploy에 필요한 파일들만 deploy라는 directory에 따로 담아두고, 이를 s3에 올려 codedeploy가 가져가도록 할 것입니다.

image.png

다음으로 post build action에서 deploy an application to aws codedeploy를 선택하고 아래와 같이 입력합니다.

• application name : codedeploy application name을 입력합니다.
• deployment group : codedeploy deployment group을 입력합니다.
• deployment config : deployment group 생성시 선택한 config를 입력합니다.
• aws region : aws region을 입력합니다. ap_northeast_2는 seoul 입니다.
• s3 bucket : s3 bucket name을 입력합니다.
• s3 prefix : s3 bucket의 upload 할 folder를 지정합니다. 저는 따로 s3 bucket에 folder를 생성하지 않았습니다. root에 upload 하겠습니다.
• subdirectory : jenkins server에서 upload 할 file이 위치하는 directory를 입력합니다. 앞서 jar와 appspec.yml & deploy.sh 파일을 옮긴 deploy directory를 입력합니다.
• include files : subdirectory에서 upload 할 file을 입력합니다. ** 를 입력해 해당 directory 내의 모든 file을 upload 합니다.

image.png

다음으로 aws 접근 방법으로 use access/secret keys를 선택합니다. 각자 소유한 AWS access key와 secret key를 입력합니다.

image.png

build & deploy job 설정을 완료했습니다 😅.

다음으로 하나의 job을 더 생성하고, 위에서 생성한 job이 수행된 후 연속적으로 실행되도록 만들겠습니다.

이 job은 앞서 생성한 deploy directory를 remove하는 작업을 수행합니다. 만약 remove 하지 않으면, 계속해서 build 후의 파일들이 deploy directory에 쌓이게되어 이전 version의 file들이 s3 bucket으로 함께 upload 되게 됩니다.

image.png

다른 값들은 모두 default로 두고, build만 execute shell을 선택해 아래와 같이 입력합니다.

아래의 command 를 통해 앞서 생성한 minholee-build-deploy job의 workspace에서 deploy directory를 제거합니다.

image.png

생성 완료후 minholee-build-deploy job의 configuration을 수정해 minholee-remove job이 연속적으로 수행되도록 설정합니다.

image.png

minholee-build-deploy job의 post build action에 build other project를 아래와 같이 추가합니다. trigger even if the build fail을 선택해 minholee-build-deploy job이 실패하더라도 deploy directory를 깔끔하게 제거하도록 하겠습니다.

image.png

jenkins의 설정이 완료되었습니다. 😊

7. Github

이제 github에서 webhook 설정을 하겠습니다.

github webhook 설정을 통해 github에 code가 push되면 해당 code가 jenkins server로 자동으로 이동하게 됩니다.

github repository로 이동해 setting으로 이동합니다.

image.png

webhooks 탭에서 add webhook을 클릭합니다.

image.png

payload url에 http://[jenkins-server]/github-webhook/ 을 입력합니다. 각자 본인의 jenkins-server 주소를 입력하면 됩니다. content type은 application/json을 선택하도록 하겠습니다.

image.png

webhook 등록이 완료되었습니다.

image.png

8. Spring Boot Application

마지막으로 github에 push할 spring boot application을 작성해보겠습니다.

각자 생성한 github repository를 clone하고 해당 path에 spring initializer를 사용해 spring boot gradle project를 생성하면 됩니다.

현재 저의 spring boot application의 구조는 다음과 같습니다.

image.png

"/" 를 호출하면 "hello this is minholee"를 return 하는 application 입니다.

demoapplication

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/")
    public String test(){
        return "hello this is minholee";
    }
}

이때 application에 꼭 포함시켜야 되는 것이 아래의 appspec.yml과 deploy.sh 파일 입니다. appspec.yml 파일은 codedeploy application이 수행해야할 deploy에 대한 설명서라고 생각하면 되겠습니다.

이 파일이 s3에 upload 되는 zip 파일의 root에 있지 않으면 codedeploy application에서 error가 발생합니다.

appspec.yml

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/build

permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 60
      runas: ec2-user

간단히 해석해보면 아래와 같습니다.

• file : s3에 upload된 파일이 ec2 instance의 destination으로 이동합니다.
• permission : hooks를 수행할 owner/group을 명시합니다.
• hooks : deploy시 수행할 script 파일을 명시합니다.

deploy.sh

#!/bin/bash
BUILD_PATH=$(ls /home/ec2-user/build/*.jar)
JAR_NAME=$(basename $BUILD_PATH)
echo "> build 파일명: $JAR_NAME"

echo "> build 파일 복사"
DEPLOY_PATH=/home/ec2-user/
cp $BUILD_PATH $DEPLOY_PATH

echo "> springboot-deploy.jar 교체"
CP_JAR_PATH=$DEPLOY_PATH$JAR_NAME
APPLICATION_JAR_NAME=springboot-deploy.jar
APPLICATION_JAR=$DEPLOY_PATH$APPLICATION_JAR_NAME

ln -Tfs $CP_JAR_PATH $APPLICATION_JAR

echo "> 현재 실행중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -f $APPLICATION_JAR_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
  echo "> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

echo "> $APPLICATION_JAR 배포"
nohup java -jar $APPLICATION_JAR > /dev/null 2> /dev/null < /dev/null &

codedeploy application은 appspec.yml에 명시되어있는 sh 파일을 실행해 deploy를 수행합니다.

제가 작성한 appspec.yml 파일은 deploy.sh를 사용하도록 작성했으니 위의 deploy.sh 스크립트를 각 ec2 instance에서 수행합니다.

최종적으로 s3에 upload한 jar file을 각 ec2 instance로 가지고와서 java -jar 명령어를 사용해 spring boot application을 실행합니다. 이때 만약 이전에 실행되던 application이 있다면 해당 applicatoin은 kill 합니다.

build.gradle

archivesBaseName = 'springboot-deploy'

tasks.jar {
    enabled = false
}

tasks.bootJar {
    enabled = true
    mainClassName = "com.example.demo.DemoApplication"
}

추가적으로 build.gradle 파일에 위와 같은 내용을 추가합니다. archivesBaseName은 생성되는 bootJar 파일의 접두어입니다.

이제 모든 설정이 완료된 것 같습니다. 🤣

9. Test

현재 elb의 dns로 접속하면 아래와 같이 에러가 발생합니다. 아직까지 아무런 application이 ec2 instance에서 실행되고 있지 않기 때문입니다.

image.png

이제 local에서 작성한 source code를 github에 push 한뒤, elb와 연결된 2개의 ec2 서버에 deploy가 잘 되는지 확인해보도록 하겠습니다.

지금 github에 code를 push 해주세요! 😎

9-1) jenkins

이후 jenkins에 들어가보면 아래와 같이 minholee-build-deploy job이 정상적으로 수행된 것을 확인할 수 있습니다.

image.png

마찬가지로 연속으로 실행되는 minholee-remove job도 아래와 같이 정상적으로 수행되었습니다.

image.png

delivery pipeline view로 확인해보면 아래와 같습니다.

image.png

jenkins server에 접속해 workspace를 확인해보면 아래와 같이 github에 push 했던 source code들이 들어와 있는 것을 확인할 수 있습니다.

image.png

9-2) s3

S3도 확인해보면 아래와 같이 zip 파일이 하나 upload 되어있습니다. 이 zip 파일에는 codedeploy applicaiton이 deploy에 사용할 jar/appspec.yml/deploy.sh 파일이 들어있습니다.

image.png

다운로드해서 열어보면 아래와 같이 jar/appspec.yml/deploy.sh 파일을 확인할 수 있습니다.

image.png

9-3) codedeploy

CodeDeploy Applicaiton을 확인해보겠습니다.

image.png

deployment가 정상적으로 수행되었습니다.

image.png

elb에 연결된 2개의 ec2 instance 모두 deploy가 정상적으로 수행되었으며 각각의 deploy는 약 6분 30초 정도 걸린것을 확인할 수 있습니다.

또한 2개의 ec2 instance의 deploy start time이 다른것을 확인할 수 있습니다. 이전에 deploy configuration에서 OneAtATime을 선택했기 때문에, 한번에 하나의 instance에만 deploy를 수행했습니다.

따라서 하나의 instance가 deploy를 수행할때 나머지 instnace는 계속해서 service를 수행하고 있으므로 무중단으로 배포가 진행됩니다.

image.png

모든 deploy event도 정상적으로 수행되었습니다.

image.png

9-4) elb

이제 다시한번 elb의 dns로 접속해보면 아래와 같이 정상적으로 "hello this is minholee"를 출력합니다.

image.png

9-5) ec2 instnace

elb가 아닌 각각의 ec2 instance에 접속해도 동일한 결과를 출력하는 것을 확인할 수 있습니다.

instance 1

image.png

instance 2

image.png

👏👏👏 jenkins와 codedeploy를 사용한 무중단 배포를 완료하도록 하겠습니다.


참고 자료 : [ https://jojoldu.tistory.com/283?category=777282 , https://www.youtube.com/watch?v=83d5YuG-KiQ&t=1685s ]


추천서적

 

스프링 부트와 AWS로 혼자 구현하는 웹 서비스

COUPANG

www.coupang.com

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음


반응형

'Jenkins' 카테고리의 다른 글

[Jenkins] Springboot + Gradle + Github + CodeDeploy + ELB (1)  (0) 2020.08.22
[Jenkins] Gradle Build  (0) 2020.08.22
[Jenkins] Delivery Pipeline  (0) 2020.08.21
[Jenkins] Deploy  (0) 2020.08.21
[Jenkins] Compile  (0) 2020.08.21

댓글

Designed by JB FACTORY