Jenkins + CodeDeploy를 활용한 EC2에 스프링 부트 프로젝트 배포 자동화

appti·2022년 6월 3일
3

Jenkins + CodeDeploy를 활용한 EC2에 스프링 부트 프로젝트 배포 자동화

과정은 위와 같습니다.
배포용 Jenkins 서버와 실제 애플리케이션 서버를 분리하는 걸 권장하지만, 프리 티어로 사용하기 위해 하나의 인스턴스를 사용했습니다.

스프링 부트 프로젝트 생성

스프링 부트 프로젝트부터 생성하도록 하겠습니다.
단순히 배포가 잘 되었는지 확인하는 용도로만 사용할 것이기 때문에 Spring web만 의존하도록 하겠습니다.

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello World";
    }
}

애플리케이션의 health 체크 용 api를 하나 만들었습니다.


AWS IAM 역할 생성

EC2CodeDeploy에서 필요한 역할을 생성합니다.

역할 만들기를 선택합니다.

먼저 EC2부터 처리하도록 하겠습니다.
다음 권한을 추가해줍니다.

  • AWSCodeDeployFullAccess
  • AmazonS3FullAccess

이름을 구분할 수 있도록 임의로 작성했습니다.

선택한 정책을 확인한 뒤, 역할을 생성했습니다.

다음으로 CodeDeploy 관련 역할을 생성합니다.
다른 AWS 서비스의 사용 사례에서 CodeDeploy를 선택하고, EC2에서 사용할 것이기 때문에 첫 번째 항목인 CodeDeploy를 선택해줍니다.

정책이 AWSCodeDeployRole 단 하나만 존재하고, 이미 선택되어 있기 때문에 딱히 정책을 선택할 필요는 없습니다.

이름을 구분할 수 있도록 임의로 작성하고 역할을 생성했습니다.


S3

JenKins를 통해 압축된 빌드 파일을 저장할 버켓을 생성해줍니다.

이름을 구분할 수 있도록 임의로 작성했습니다.

앞서 AmazonS3FullAccess를 부여한 역할을 만들었기 때문에 버켓의 모든 퍼블릭 액세스를 차단합니다.


EC2

인스턴스를 생성해줍니다.

이름을 구분할 수 있도록 임의로 작성하고 역할을 생성했습니다.

프리 티어를 사용합니다.

키 페어를 선택해줍니다.

보안 그룹은 새롭게 생성해주도록 하겠습니다.

  • SSH : 로컬에서 접근하기 위해 내 IP로 허용했습니다.

스토리지는 프리 티어에서 사용할 수 있는 최대 범위로 설정했습니다.
이후 인스턴스를 생성합니다.

그 다음 EC2IAM 역할을 이전에 만들어둔 역할로 수정합니다.

IAM 역할을 수정했다면 반드시 인스턴스를 재부팅해줍니다.

  • 인스턴스 재부팅을 해야 변경된 IAMcodedeploy-agent가 인식할 수 있기 때문입니다.

이후 인바운드 규칙을 추가해줍니다.

  • 9000 : Jenkins 포트로 사용할 값입니다.
  • 8080 : 그냥 스프링 부트 애플리케이션 포트로 사용할 값입니다.

CodeDeploy

애플리케이션을 생성합니다.

이름을 구분할 수 있도록 임의로 작성했습니다.
컴퓨팅 플랫폼은 EC2를 사용할 것이므로 EC2/온프레미스를 선택했습니다.

애플리케이션의 배포 그룹을 생성합니다.

이름을 구분할 수 있도록 임의로 작성했습니다.

기존에 미리 생성해놨던, AWSCodeDeployRole 정책을 가진 역할을 선택합니다.

배포 유형의 경우 현재 위치를 선택합니다.
블루/그린의 경우 다중 서버를 활용해야 하다 보니 프리 티어로는 불가능합니다.

환경 구성의 경우 Amazone EC2 인스턴스를 선택하고, EC2를 구분할 수 있도록 key - value를 지정합니다.
구분할 수 있는 값이라면 어느 값이든 상관 없지만, 개인적으로는 Name키가 가장 입력하기 편했습니다.

codedeploy-agent는 직접 설치하도록 하겠습니다.

배포 구성의 경우 단순 테스트 용도이기 때문에 최소한으로 설정하도록 하겠습니다.

로드 밸런서의 경우 단일 서버만 사용하기 때문에 비활성화를 했습니다.


EC2 준비

EC2에서 필요한 작업들을 진행하도록 하겠습니다.

시간 동기화

EC2의 시간을 Chrony 방식으로 동기화합니다.

169.254.169.123Amazon Time Sync Service IP로컬 IP이며 별도 설정 없이 Private Subnet에서도 안전하게 접근할 수 있습니다.

# ec2 시간 확인
date
2022. 06. 03. () 11:31:04 UTC

# 해당 ec2가 실행될 때마다 chrony 방식으로 시간을 동기화하도록 요청
sudo chkconfig chronyd on
알림: 'systemctl enable chronyd.service'에 요청을 전송하고 있습니다.

# chrony가 169.254.169.123 IP 주소를 사용하여 시간 동기화를 하고 있는지 확인
chronyc sources -v

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current best, '+' = combined, '-' = not combined,
| /             'x' = may be in error, '~' = too variable, '?' = unusable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 169.254.169.123               3   4   377     1    +41us[  +46us] +/-  527us
^- ec2-13-209-84-50.ap-nort>     2   6    37    39  -9135us[-9131us] +/-   13ms
^- send.mx.cdnetworks.com        2   6    37    39  -1061us[-1057us] +/-   38ms
^- ec2 ip                        2   6    37    37  +9309us[+9313us] +/-  187ms
^- sshhub.dynamigs.net           2   6    37    37    -11ms[  -11ms] +/-  181ms

# 시간 동기화 지표 확인
chronyc tracking
Reference ID    : A9FEA97B (169.254.169.123)
Stratum         : 4
Ref time (UTC)  : Fri Jun 03 11:33:19 2022
System time     : 0.000015335 seconds fast of NTP time
Last offset     : +0.000015248 seconds
RMS offset      : 0.000024212 seconds
Frequency       : 20.226 ppm fast
Residual freq   : +0.023 ppm
Skew            : 0.870 ppm
Root delay      : 0.000463618 seconds
Root dispersion : 0.000334083 seconds
Update interval : 16.2 seconds
Leap status     : Normal

# 타임존 설정
sudo vim /etc/sysconfig/clock
# 다음과 같이 /etc/sysconfig/clock 값 변경
ZONE="Asia/Seoul"
KST=True

# 현재 타임존 설정 확인
cat /etc/localtime
# UTC로 설정되어 있음
TZif2UTCTZif2�UTC
UTC0

# 기존 설정 삭제
sudo rm /etc/localtime

# 대한민국 표준 시간대 정보를 심볼릭 링크로 설정
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# ec2 시간 확인
date
2022. 06. 03. () 20:35:35 KST

자바 설치

자동으로 배포된 스프링 부트 프로젝트를 실행하기 위해 자바를 설치하도록 하겠습니다.

# aws coreetto 다운로드
sudo curl -L https://corretto.aws/downloads/latest/amazon-corretto-11-x64-linux-jdk.rpm -o jdk11.rpm

# java 11 설치
sudo yum localinstall jdk11.rpm -y

# java version 선택
sudo /usr/sbin/alternatives --config java

# java version 확인
java --version
openjdk 11.0.15 2022-04-19 LTS
OpenJDK Runtime Environment Corretto-11.0.15.9.1 (build 11.0.15+9-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.15.9.1 (build 11.0.15+9-LTS, mixed mode)

# 설치 파일 삭제
rm -rf jdk11.rpm

java version 선택의 경우 위와 같이 여러 자바 버전 중 하나를 선택할 수 있습니다.

메모리

EC2 프리티어t2.micro의 메모리는 1GB입니다.
이 정도 메모리로는 Jenkins를 사용할 수 없습니다.
이 문제는 SWAP 메모리를 통해 (임시로) 해결할 수 있습니다.
최선은 Jenkins용 배포 서버와 실제 애플리케이션 운영 서버를 분리하는 것이지만, 단순 테스트 용이기 때문에 다음과 같은 방법을 사용하도록 하겠습니다.

권장하는 SWAP 메모리의 크기는 다음과 같습니다.

# dd 명령어를 통해 swap 메모리 할당
# 시간이 1분 ~ 5분정도 걸릴 수 있음
# 크기는 2GB(128MB x 16)
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
16+0 records in
16+0 records out
2147483648 bytes (2.1 GB) copied, 15.8767 s, 135 MB/s

# swap 파일의 읽기 및 쓰기 권한 업데이트
sudo chmod 600 /swapfile

# Linux swap 영역 설정
sudo mkswap /swapfile
Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
no label, UUID=22c80ff2-8555-48cd-91ce-921d45237086

# swap 공간에 swap file을 추가해 즉시 사용할 수 있도록 설정
sudo swapon /swapfile

# 정상적으로 설정되었는지 확인
sudo swapon -s
Filename				Type		Size	Used	Priority
/swapfile                              	file    	2097148	0	-2

# fstab에 /swapfile 설정 추가
sudo vi /etc/fstab
/swapfile swap swap defaults 0 0

# 정상적으로 적용되었는지 확인
free
              total        used        free      shared  buff/cache   available
Mem:         987700      419664      242604         500      325432      425528
Swap:       2097148           0     2097148

fstab에 /swapfile 설정 추가의 경우 다음과 같이 마지막 줄에 추가하면 됩니다.

codedeploy-agent

CodeDeployEC2에서 작동할 수 있도록 codedeploy-agent를 설정해줍니다.

# aws-cli 설치 확인
aws --version
aws-cli/1.18.147 Python/2.7.18 Linux/5.10.109-104.500.amzn2.x86_64 botocore/1.18.6

# aws 설정 
sudo aws configure
AWS Access Key ID [None]: Access Key
AWS Secret Access Key [None]: Secret Access Key
Default region name [None]: ap-northeast-2
Default output format [None]: json

# codedeploy-agent 설치 파일 다운로드
# 자신의 region에 맞는 설치 파일을 다운로드 받아야 함(ap-northeast-2)
wget https://aws-codedeploy-ap-northeast-2.s3.amazonaws.com/latest/install

# codedeploy-agent를 설치하기 위한 의존성 설정
sudo yum install ruby -y

# 설치파일 권한 부여
chmod +x ./install

# codedeploy-agent 설치
sudo ./install auto

# codedeploy-agent 동작 확인
sudo service codedeploy-agent status

도커 설치

스프링 부트 애플리케이션 기본 포트와 Jenkins의 기본 포트가 중복되기도 하고, Jenkins 설치를 조금 더 간편히 처리하기 위해 도커를 통해 Jenkins를 사용하도록 하겠습니다.

# yum 업데이트
sudo yum update -y

# 도커 설치
sudo amazon-linux-extras install -y docker

# 도커 실행
sudo service docker start
Redirecting to /bin/systemctl start docker.service

# 도커에 jenkins 실행
# -p : jenkins의 기본 포트 8080을 외부 포트 9000으로 바인딩
# -e : 도커 컨테이너에 타임존 설정 
sudo docker run -d --name jenkins -p 9000:8080 -e TZ=Asia/Seoul jenkins/jenkins:jdk11

# jenkins가 정상적으로 실행되고 있는지 확인
sudo docker ps -a
CONTAINER ID   IMAGE                   COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
21bb19920e6e   jenkins/jenkins:jdk11   "/usr/bin/tini -- /u…"   7 minutes ago   Up 7 minutes   50000/tcp, 0.0.0.0:9000->8080/tcp, :::9000->8080/tcp   jenkins

jenkins

http://ec2-ip:9000으로 접속합니다.

다음과 같이 Jenkins를 사용할 수 있습니다.

# 도커에 실행중인 jenkins 컨테이너에 bash를 통해 접속
sudo docker exec -it jenkins bash

# 비밀번호 출력
cat /var/jenkins_home/secrets/initialAdminPassword
<기본 비밀번호>

비밀번호를 입력해줍니다.

Install suggested plugins를 선택합니다.

설치가 진행됩니다.

설치가 끝나면 관리자 계정을 생성할 수 있습니다.

Jenkins의 접속 URL를 추가합니다.

Dashboard -> Jankins 관리 -> 플러그인 관리에서 설치 가능으로 탭을 변경한 뒤 codedeploy를 선택해 AWS CodeDeploy 플러그인을 설치합니다.


만약 플러그인이 다음과 같은 이유로 설치되지 않았다면 하단의 주의 탭에서 해결방법을 확인해주세요.


플러그인을 설치했다면 Dashboard -> new Item -> Freestyle project를 생성합니다.
이름은 임의로 작성했습니다.

해당 프로젝트의 정보를 입력합니다.
설명은 임의로 작성했습니다.

Jenkins와 연동할 깃허브 리포지토리를 등록해야 합니다.
그 전에 깃허브 Credentials를 선택합니다.
kind로 어떤 방식으로 Credentials를 입력할지 선택할 수 있습니다.

깃허브 Repository URLCredentials를 선택해줍니다.

어떤 브랜치를 기준으로 Jenkins가 빌드를 진행할지 선택합니다.
적용하고자 하는 브랜치명을 입력해주면 됩니다.
기본값은 */master로 되어있으며, 제 경우 */main으로 변경했습니다.

빌드 유발의 경우 Github hook trigger for GITScm polling을 선택해줍니다.

Build -> Add build step에서 Execute shell을 선택해줍니다.

따로 gradle을 설치하지 않고 스프링 부트 프로젝트 내의 gradlew를 통해 빌드해주도록 하겠습니다.

빌드 후 조치 -> 빌드 후 조치 추가에서 Deploy an application to AWS CodeDeploy를 선택해줍니다.

AWS CodeDeploy Application Name의 경우 애플리케이션 이름을, AWS CodeDeploy Deployment Group의 경우 애플리케이션 배포 그룹 이름을 입력합니다.

AWS Region의 경우 서울, AP_NORTHEAST_2를 선택해줍니다.

S3 Bucket 이름을 입력합니다.

Include Files에는 CodeDeploy를 통해 S3 Bucket에 업로드 된 파일 중 EC2로 복사할 파일을 지정합니다.

**/*.jar, **/appspec.yml, **/scripts/*

배포할 애플리케이션 .jar, CodeDeploy가 참조할appspec.yml, appspec.yml을 통해 실행할 스크립트 디렉토리 scripts를 입력했습니다.

키를 입력해주고 프로젝트를 생성해줍니다.


Github Webhook

Github hook trigger for GITScm polling을 선택했으므로 관련 설정을 해주도록 하겠습니다.

Jenkins와 연동한 깃허브 리포지토리 -> Settings -> Code and automation -> Webhooks으로 이동합니다.

Add webhook을 선택합니다.

Jenkins URL/github-webhook/를 입력합니다.
반드시 마지막에 /를 추가해야하며, 추가하지 않을 시 302 Redirect가 발생해 실패합니다.

이렇게 좌측에 녹색 체크표시가 있어야 정상적으로 설정된 것입니다.


빌드 설정

# build.gradle

jar {
	enabled = false
}

스프링 부트 gradle 플러그인 2.5 버전부터 빌드 시 프로젝트 관련 의존성을 가지고 있지 않은 plain 버전과 모든 의존성을 가지고 있는 executable 버전 두 개를 빌드하게 됩니다.

여기서는 Jenkins로 배포 자동화를 진행하기 때문에 plain 버전을 필요 없으므로, 위와 같이 작성해줍니다.

이제 스프링 부트 프로젝트의 루트 디렉토리에 appspec.ymlscripts/deploy.sh를 작성하도록 하겠습니다.

# appspec.yml
version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ec2-user/cicd-test
    overwrite: yes

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

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

CodeDeploy가 의존하는 appspec.yml을 작성해줍니다.
각 섹션을 확인해보도록 하겠습니다.

version: 0.0
os: linux
  • version: 0.0
    • AppSpec의 버전을 지정합니다.
    • 0.0은 유일하게 허용된 버전입니다.
  • os: linux
    • EC2oswindow가 아니라면 linux를 입력합니다.
files:
  - source:  /
    destination: /home/ec2-user/cicd-test
    overwrite: yes
  • files
    • CodeDeploy가 작업하는 파일을 지정합니다.
  • source
    • 어떤 디렉토리를 기준으로 파일을 작업할지 선택합니다.
    • 스프링 부트 프로젝트의 루트 디렉토리를 기준으로 appspec.ymlscripts를 작성했으므로 기준을 루트 디렉토리로 설정합니다.
  • destination
    • CodeDeploy가 빌드된 파일을 복사할 때, 어느 디렉토리에 복사할지를 지정합니다.
    • /home/ec2-user/cicd-test로 설정했습니다.
  • overwrite
    • 오버라이드 유무에 대해 선택할 수 있습니다.
permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user
  • permissions
    • EC2/온프레미스에서만 작성합니다.
      • 람다나 ECS에는 resources 섹션을 작성합니다.
    • files에서 설정한 내용대로 CodeDeploy가 복사한 파일에 대한 권한을 지정합니다.
  • object
    • 필수 사항입니다.
    • 권한을 지정할 파일/디렉토리를 설정해줍니다.
  • parrtern
    • 선택 사항입니다.
    • 권한을 적용할 패턴을 지정합니다.
    • 지정하지 않거나 **를 사용해 지정하면 모든 파일에 적용됩니다.
  • owner
    • 선택 사항입니다.
    • object의 소유자 이름입니다.
  • group
    • 선택 사항입니다.
    • object의 그룹 이름입니다.
hooks:
  ApplicationStart:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ec2-user
  • 다음은 인 플레이스(in-place) 배포 기준의 생명주기입니다.

  • hooks
    • EC2/온프레미스의 경우 위 사진처럼 CodeDeploy의 생명 주기에 hook을 걸어 실행할 스크립트를 설정할 수 있습니다.
    • 사진의 흑색 바탕의 이벤트에서는 hook을 설정할 수 없습니다.
  • ApplicationStart
    • CodeDeploy가 배포 파일을 EC2에 모두 복사한 뒤 실행되는 이벤트입니다.
  • location
    • 해당 hook에서 실행할 스크립트를 지정합니다.
  • timeout
    • 제한시간을 설정합니다.
    • 초 단위입니다.
  • runas
    • 해당 스크립트를 실행할 유저입니다.
    • 기본은 codedeploy-agent 입니다.

이제 CodeDeploy 생명 주기 중 ApplicationStart에서 실행할 스크립트를 작성하도록 하겠습니다.

#!/bin/bash
BUILD_JAR=$(ls /home/ec2-user/cicd-test/build/libs/*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo "> build : $JAR_NAME" >> /home/ec2-user/deploy.log

echo "> build 파일 복사" >> /home/ec2-user/deploy.log
DEPLOY_PATH=/home/ec2-user/
cp $BUILD_JAR $DEPLOY_PATH

echo "> 실행중인 애플리케이션 pid 확인" >> /home/ec2-user/deploy.log
CURRENT_PID=$(pgrep -f $JAR_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 실행중인 애플리케이션이 없으므로 종료하지 않음" >> /home/ec2-user/deploy.log
else
  echo "> kill -15 $CURRENT_PID" >> /home/ec2-user/deploy.log
  kill -15 $CURRENT_PID
  sleep 10
fi

DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_JAR 배포"    >> /home/ec2-user/deploy.log
nohup java -jar $DEPLOY_JAR >> /home/ec2-user/deploy.log 2>/home/ec2-user/deploy_err.log &

ApplicationStart의 경우 CodeDeploy가 빌드 파일을 EC2에 복사한 이후에 실행되는 hook이기 때문에, 이를 가정하고 스크립트를 작성하면 됩니다.

BUILD_JAR=$(ls /home/ec2-user/cicd-test/build/libs/*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo "> build : $JAR_NAME" >> /home/ec2-user/deploy.log

echo "> build 파일 복사" >> /home/ec2-user/deploy.log
DEPLOY_PATH=/home/ec2-user/
cp $BUILD_JAR $DEPLOY_PATH

제 경우 /home/ec2-user/cicd-test에 빌드 파일을 복사하라고 했기 때문에 이를 경로로 잡아서 처리해주었습니다.

CURRENT_PID=$(pgrep -f $JAR_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 실행중인 애플리케이션이 없으므로 종료하지 않음" >> /home/ec2-user/deploy.log
else
  echo "> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 10
fi

$JAR_NAME으로 실행중인 애플리케이션이 있는지 확인하고 실행중인 애플리케이션이 있다면 kill -15를 통해 정상적으로 애플리케이션이 종료되도록 요청하고, sleep 10를 통해 정상적으로 종료될 때 까지 대기합니다.

DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_JAR 배포"    >> /home/ec2-user/deploy.log
nohup java -jar $DEPLOY_JAR >> /home/ec2-user/deploy.log 2>/home/ec2-user/deploy_err.log &

이후 nohup을 통해 백그라운드로 애플리케이션을 실행합니다.


배포 시도

이제 모든 설정을 끝냈기 때문에, 깃허브 리포지토리 main 브랜치에 push를 하면 Jenkinsgithub webhook을 통해 자동으로 처리해줍니다.

git push main

Jenkinsgit push를 감지했습니다.

빌드 실행 상태를 통해 빌드 진행 상황을 알 수 있습니다.

Jenkins의 역할은 CI, 코드 통합입니다.
S3에 빌드 파일을 위치시키라고 했기 때문에, 빌드 순서와 랜덤 문자열로 빌드 파일이 생성된 것을 확인할 수 있습니다.

CD, 지속적으로 배포가 가능한 상태로 유지해주는 CodeDeploy를 확인하면, 배포가 성공한 것을 확인할 수 있습니다.

nohup을 통해 백그라운드로 실행한 스프링 부트 애플리케이션이 8080 포트로 실행되고 있음을 확인할 수 있습니다.

로그용으로 만들었던 deploy.log를 확인하면 정상적으로 동작하고 있음을 확인할 수 있습니다.

ec2 ip/hello로 요청하면 정상적으로 호출되는 것을 확인할 수 있습니다.


주의

제가 경험했던 오류 및 개선사항과 관련된 주의사항을 적어보고자 합니다.

EC2 IAM 역할 변경 시

EC2 IAM 역할 변경 시 재부팅을 해 주어야 codedeploy-agent가 정상적으로 동작합니다.

EC2 보안그룹 설정

github webhook의 경우 Jenkins url로 요청을 하다 보니 보안그룹을 잘 설정해주어야 합니다.

모두 허용으로 처리하거나, 링크를 통해 위 사진처럼 github webhook ip를 확인해 등록해주면 됩니다.

Jenkins AWS CodeDeploy 설치 오류

다음과 같이 aws-java-sdk-elasticbeanstalk 관련 오류가 발생할 수 있습니다.

이 경우 Jenkins에서 aws-java-sdk-elasticbeanstalk 플러그인을 설치하면 해결할 수도 있습니다.

다만, 이 경우 다음과 같은 오류가 발생할 수도 있습니다.

Update required: Amazon Web Services SDK :: Elastic Beanstalk (aws-java-sdk-elasticbeanstalk 1.12.89-292.v2712528e879c) to be updated to 1.12.246-349.v96b_b_f7eb_a_c3c or higher

최신 버전이 아니라서 문제가 발생했다는 의미입니다.

이렇게 된 경우 기존의 플러그인을 모두 삭제한 뒤, Amazon Web Services SDK :: All 플러그인을 직접 설치하는 것을 권장합니다.

Jenkins plugin - Amazon Web Services SDK :: All으로 이동합니다.

최신 버전의 Installation optionsDownload를 통해 플러그인을 다운로드 받습니다.

Jenkins 관리 -> 플러그인 관리 -> 고급 탭으로 이동합니다.

Deploy Plugin 탭으로 가서 파일을 선택한 뒤, Deploy 버튼을 클릭합니다.
확장자가 .hpi여야 합니다.

정상적으로 설치된 것을 확인할 수 있습니다.

다시 플러그인 관리를 확인해보면 위와 같이 업데이트가 필요하다고 하는데, 이 때 업데이트 할 경우 문제가 발생합니다.

위 사진과 같이 최신 버전의 Amazon Web Services SDK :: All 플러그인을 설치했음에도 불구하고 다시 aws-java-sdk-elasticbeanstalk 버전 관련 오류가 발생합니다.

그러므로 업데이트 하지 않고 바로 설치 가능 -> AWS CodeDeploy를 설치해주면 됩니다.

AWS CodeDeploy 플러그인이 정상적으로 설치되었음을 확인할 수 있습니다.

CodeDeploy 관련 오류

CodeDeploy의 경우 발생하는 오류를 다음과 같이 크게 두 가지로 나눌 수 있습니다.

  1. codedeploy-agent 오류
  2. CodeDeploy 오류

codedeploy-agent 오류의 경우 CodeDeploy 실행 전 EC2와 접속하는 과정에서 발생하는 문제입니다.

CodeDeploy 오류는 CodeDeployEC2에서 배포하는 과정에서 발생하는 문제입니다.
주로 appspec.yml이나 hook 관련 스크립트에서 발생합니다.

CodeDeploy 오류의 경우 배포 수명 주기 이벤트 -> 이벤트 -> View events에서 확인할 수 있습니다.

codedeploy-agent 오류의 경우 다음과 같이 건너뜀 상태가 됩니다.
CodeDeploy 실행 전에 EC2와의 연결조차 되지 않았다는 의미라고 볼 수 있습니다.
그러므로 콘솔에서도 문제를 확인할 수 없습니다.

이럴 때에는 EC2에서 codedeploy-agent 관련 로그를 살펴보면 해결할 수 있습니다.

cat /var/log/aws/codedeploy-agent/codedeploy-agent.log

기본 로그 경로는 위와 같으며, codedeploy-agent 관련 설정 파일은 /etc/codedeploy-agent/conf/codedeployagent.yml에 있으므로 적절히 수정해서 사용하시면 되겠습니다.

Region이 맞지 않다거나 EC2의 권한이 부족해 S3이나 CodeDeploy에서 접근할 수 없는 등 CodeDeploy 콘솔에서 확인할 수 없는 문제들을 확인할 수 있습니다.

CodeDeploy 실패 시 후처리

CodeDeploy의 경우 배포에 실패하면 해당 배포를 계속 반복하게 됩니다.
이 상태에서 추가적으로 Jenkins를 통해 재배포를 요청한다면, JenkinsCI 요청을 성공해 S3에 배포 파일은 업로드되지만 CodeDeploy의 경우 배포하자마자 실패하게 됩니다.

이 경우 CodeDeploy에도 문제를 확인할 수 없고, 다음과 같이 codedeploy-agent에서도 요청을 무한히 반복할 뿐 에러를 확인할 수 없습니다.

제 경우 깜빡하고 EC2 정책을 변경하지 않아 정책을 변경한 뒤 인스턴스 재부팅 후 Jenkins를 통해 다시 배포를 요청한 상태였는데, 이전에 EC2 정책을 변경하지 않아 발생한 CodeDeploy 실패를 처리하지 않아 발생했었습니다.

이 경우 CodeDeploy에서 실패한 배포를 정상적으로 처리해준 뒤 다시 빌드하면 해결할 수 있습니다.

참고
Github - About webhooks
Linux 인스턴스의 시간 설정
스왑 파일을 사용하여 EC2에서 스왑 공간으로 사용할 메모리를 할당하는 방법
AppSpec 파일 구조
AppSpec 'permissions' 섹션(EC2/온프레미스 배포만 해당)
AppSpec 'hooks' 섹션
CodeDeploy 에이전트 구성 참조

profile
안녕하세요

0개의 댓글