Spring 프로젝트 Jenkins와 Github로 자동 배포

CI / CD 란?

기존의 모놀리식 소프트웨어의 개발 접근 방식이 아닌 현대화된 애플리케이션은 마이크로서비스 아키텍처(MSA)를 사용하여 소프트웨어를 구축하고 API로 이러한 서비스를 함께 연결하는 경우가 많습니다. 서비스를 작은 단위의 API로 구성하고 연결하는 방식은 신속하고 지속적인 소프트웨어 제공을 해야하며 신뢰 가능한 수준의 버전을 유지할 수 있어야 합니다. 이를 위해 CI/CD(지속적인 통합 및 배포)가 중요합니다.

CI(Continuous Integration) : 지속적인 통합 통합 프로세스.

애플리케이션에 대한 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트되어 공유 리포지토리에 통합되도록 설정할 수 있기 때문에 여러 명의 개발자가 동시에 애플리케이션 개발과 관련된 코드 작업을 할 경우 서로 충돌할 수 있는 문제를 해결할 수 있습니다.

CD(Continuous Delivery or Continuous Deployment) : 지속적인 서비스 제공 또는 지속적인 배포

지속적인 제공(Continuous Delivery)이란 개발자들이 애플리케이션에 적용한 변경 사항이 버그 테스트를 거쳐 리포지토리(예: GitHub 또는 컨테이너 레지스트리)에 자동으로 업로드되는 것을 뜻하며, 운영팀은 이 리포지토리에서 애플리케이션을 실시간 프로덕션 환경으로 배포할 수 있습니다. 이는 개발팀과 비즈니스팀 간의 가시성과 커뮤니케이션 부족 문제를 해결해 줍니다. 지속적인 제공은 최소한의 노력으로 새로운 코드를 배포하는 것을 목표로 합니다.

지속적인 배포(Continuous Deployment)란 개발자의 변경 사항을 리포지토리에서 고객이 사용 가능한 프로덕션 환경까지 자동으로 릴리스하는 것을 의미합니다. 이는 애플리케이션 제공 속도를 저해하는 수동 프로세스로 인한 운영팀의 프로세스 과부하 문제를 해결합니다. 지속적인 배포는 파이프라인의 다음 단계를 자동화함으로써 지속적인 제공이 가진 장점을 활용합니다.

image-20220209164652281

CI / CD으로 얻는 것은?

  • 개발 및 운영팀에 발생하는 통합문제를 해결
  • 개발단계에서 배포까지 자동화하므로서 주기적인 수동배포에 소요되는 시간을 절약.
  • 휴면 에러를 방지.

CI/CD 파이프라인의 마지막 단계는 지속적 배포입니다. 프로덕션 준비가 완료된 빌드를 코드 리포지토리에 자동으로 릴리스하는 지속적 제공의 확장된 형태인 지속적 배포는 애플리케이션을 프로덕션으로 릴리스하는 작업을 자동화합니다. 프로덕션 이전의 파이프라인 단계에는 수동 작업 과정이 없으므로, 지속적 배포가 제대로 이루어지려면 테스트 자동화가 제대로 설계되어 있어야 합니다. 자동화된 테스트는 CI/CD 파이프라인의 여러 테스트 및 릴리스 단계를 수행할 수 있어야 하기 때문에 많은 선행 투자가 필요합니다.

CI / CD Tools

  • Jenkins
    • 무료 오픈소스.
    • 서버 설치가 필요.
    • 사용자 및 정보가 많음.
    • 지원하는 플러그인이 많음.
    • 설치 및 사용이 간단함.
    • JIRA 등 Issue tracking과 연계가 완벽하지 않음.
  • Travis CI
    • 오픈소스용은 무료이고 Private는 유료.
    • 서버 설치가 필요 없이 Travis에서 제공해줌.
    • 설정이 간편하지만 Jenkins에 비해 플러그인이나 설정할 수 있는 범위가 적음.
    • Private을 사용해야 된다면 꽤 비싼 비용을 지불 해야 함.
  • Bamboo
    • 직관적이고 이쁜 UI.
    • Atlassian 제품군(JIRA, Confluence 등)과 완벽한 통합 제공.
    • Jenkins 에 비해 프로젝트 권한 설정이나 분산 빌드가 쉬움.
    • 유료이고 비싼 비용을 지불해야 함.

Jenkins 설치

목표

Github의 master 브랜치에 Spring 프로젝트를 push하면 Jenkins에서 Gradle로 빌드하고 운영(prod) 서버에 자동 배포.

Jenkins 서버 준비

Jenkins를 설치 및 운영할 서버 한대를 마련하여 Rucky Linux 8.5를 설치 하였습니다.

JAVA 설치

AdoptOpenJDK 설치가이드 CentOS RPM으로 AdoptOpenJDK를 설치합니다.

(직접 설치하려면 adoptium github에서 내려 받으세요.)

# cat <<'EOF' > /etc/yum.repos.d/adoptopenjdk.repo
[AdoptOpenJDK]
name=AdoptOpenJDK
baseurl=http://adoptopenjdk.jfrog.io/adoptopenjdk/rpm/centos/$releasever/$basearch
enabled=1
gpgcheck=1
gpgkey=https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public
EOF

설치 가능 버전 확인
# yum --showduplicates list adoptopenjdk-11-hotspot
adoptopenjdk-11-hotspot.x86_64                        11.0.3+7-1                            AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.4+11-1                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.5+10-1                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.6+10-1                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.7+10-1                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.8+10-1                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.8+10-2                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.8+10-3                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.9+11-3                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.9+11.1-3                         AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.9+11.2-3                         AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.10+9-3                           AdoptOpenJDK
adoptopenjdk-11-hotspot.x86_64                        11.0.11+9-3                           AdoptOpenJDK

# yum install adoptopenjdk-11-hotspot-11.0.11+9-3.x86_64

java 원본 경로 확인
# which javac
/usr/bin/javac

# readlink -f /usr/bin/javac
/usr/lib/jvm/adoptopenjdk-11-hotspot/bin/javac

# vim /etc/profile
...
맨 아래에 JAVA_HOME은 Java설치 경로에서 bin/javac 경로를 제외하고 설정하고 path는 /bin을 포함 시켜준다.
export JAVA_HOME=/usr/lib/jvm/adoptopenjdk-11-hotspot
export PATH=$PATH:$JAVA_HOME/bin

재부팅
# shutdown -r now 

# java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)

# echo $JAVA_HOME
/usr/lib/jvm/adoptopenjdk-11-hotspot

# javac
Usage: javac <options> <source files>
where possible options include:
...

Java 17

cat <<EOF > /etc/yum.repos.d/adoptium.repo
[Adoptium]
name=Adoptium
baseurl=https://packages.adoptium.net/artifactory/rpm/centos/8/$(uname -m)
enabled=1
gpgcheck=1
gpgkey=https://packages.adoptium.net/artifactory/api/gpg/key/public
EOF

yum install temurin-17-jdk

Java 버전 선택

# alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
   1           /usr/lib/jvm/adoptopenjdk-11-hotspot/bin/java
*+ 2           /usr/lib/jvm/temurin-17-jdk/bin/java

Enter to keep the current selection[+], or type selection number:

Git 설치

# yum install git

# git --version
git version 1.8.3.1

# which git
/usr/bin/git

Gradle 설치

# yum install unzip
# wget https://services.gradle.org/distributions/gradle-7.4-bin.zip -P /opt
# unzip /opt/gradle-7.4-bin.zip
# rm -f /opt/gradle-7.4-bin.zip
# vim /etc/profile.d/gradle.sh
export GRADLE_HOME=/opt/gradle-7.4
export PATH=${GRADLE_HOME}/bin:${PATH}

# chmod +x /etc/profile.d/gradle.sh
# source /etc/profile.d/gradle.sh
# gradle -v
Welcome to Gradle 7.4!
...

8.1.1


wget https://services.gradle.org/distributions/gradle-8.1.1-bin.zip -P /opt

Jenkins 설치

jenkins 공식 홈페이지 설치 가이드를 참고합니다.

# sudo wget -O /etc/yum.repos.d/jenkins.repo \
    https://pkg.jenkins.io/redhat-stable/jenkins.repo
    
# sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key

# sudo yum upgrade

# sudo yum install jenkins
Error: Package: jenkins-2.319.3-1.1.noarch (jenkins)
           Requires: daemonize

# cat <<'EOF' > /etc/yum.repos.d/epelfordaemonize.repo
[daemonize]
baseurl=https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/
gpgcheck=no
enabled=yes
EOF

# yum install daemonize -y

# sudo yum install jenkins

# sudo systemctl daemon-reload
# sudo systemctl enable jenkins

# sudo systemctl start jenkins

방화벽 작업

개발PC에서 Jenkins 웹서버로 접근 및 Github Webhook 서버의 방화벽을 오픈합니다.

# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address=xxx.xxx.xxx.0/24 port port="8080" protocol="tcp" accept'

# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address=192.30.252.0/22 port port="8080" protocol="tcp" accept'
# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address=185.199.108.0/22 port port="8080" protocol="tcp" accept'
# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address=140.82.112.0/20 port port="8080" protocol="tcp" accept'
# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address=143.55.64.0/20 port port="8080" protocol="tcp" accept'

# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv6" source address=2a0a:a440::/29 port port="8080" protocol="tcp" accept'
# sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv6" source address=2606:50c0::/32 port port="8080" protocol="tcp" accept'

# firewall-cmd --reload
# firewall-cmd --list-all

호스트 추가

Spring test에서 메일 전송 테스트 같이 특정 host 정보를 사용한다면 /etc/hosts에 추가 해야 합니다. 그렇지 않으면 UnknownHost 오류가 발생합니다.

#vim /etc/hosts
xxx.xxx.xxx.xxx mail.test.net

개발PC에서 브라우저로 http://Jenkins서버IP:8080/ 접속 해보면 아래와 같은 화면이 나옵니다.

image-20220211111515487

안내 문구에서 빨간색 부분을 출력하면 패스워드가 나오며 출력값을 input 박스에 붙여넣고 진행하면 됩니다.

# cat /var/lib/jenkins/secrets/initialAdminPassword
63d1901qa2323743ba91267cc0a58d7de

다음으로 플로그인 설정화면이 나옵니다.

image-20220211111951483

Select plugins to install을 선택하면 기본적으로 추천하는 플러그인들이 선택되어 있으며 추가적으로 필요한 플러그인들을 선택할 수 있습니다.

플러그인 선택을 완료한 다음 아래 install 버튼을 클릭하면 플러그인이 설치 됩니다.

image-20220211132728594

플러그인 설치가 완료되면 계정 등록 화면이 나오며, 계정 등록이 끝나면 Jenkins 메인 페이지를 볼 수 있습니다.

image-20220211133508896

Jenkins 플러그인 추가

Github 관련된 추가로 플러그인을 설치하기 위해 Jenkins 관리 > 플러그인 관리에서 설치가능 탭에서 Github Integration을 검색하여 설치합니다.

image-20220214112921793

image-20220211174627224

image-20220211174737656

추가로 원격 서버에 빌드 파일을 배포하기 위해 publish over ssh 플러그인을 사용하는데, 최신 버전에서는 보안의 이유로 제외되었습니다. 수동으로 플러그인을 설치 하기 위해 publish-over-ssh.hpi를 다운로드 받고 플러그인 고급탭에서 플러그인을 설치합니다.

image-20220214134945834

설치 후 jenkins를 재시작하고 다시 플러그인 관리로 와서 설치된 플러그인 목록을 보면 Publish Over SSH가 추가된 것을 확인할 수 있습니다.

image-20220214135347025

Jenkins 설정

Github 홈페이지 설정에서 Jenkins용 토큰을 생성합니다.

image-20220211144623822

토큰을생성할 때 권한은 아래와 같이 설정합니다.

image-20220211144809033

메인 좌측 메뉴 > Jenkins 관리 > 시스템 설정에서 아래로 내려보면 Github 항목에서 Credentials Add한 다음 Manage hooks를 체크하고 Test connnection 버튼을 클릭해보세요.

image-20220211150454305

Credentials Add 팝업에서 Secret는 발급받은 토큰값을 넣으면 됩니다.

image-20220211145959508

Credentials verified for user …, rate limit: 4999 으로 나오면 성공

하단에 저장 버튼을 누르면 메인으로 돌아옵니다.

Github WebHook 설정

Payload URL에서 Jenkins서버ip:포트는 Jenkins 관리자의 메인 좌측 메뉴 > Jenkins 관리 > 시스템 설정에서 Jenkins Location에 입력된 값을 넣어야 합니다.

예) http://Jenkins서버IP:포트/github-webhook/

Just the push event 옵션은 push 일 때 이벤트가 발생한다는 의미입니다.

image-20220211171229262

Add webhook을 누르면 webhook이 추가됩니다.

image-20220211171856984

메인 좌측 메뉴 > Jenkins 관리 > Global Tool Configuration에서 JDK, Git, Gradle을 설정합니다.

image-20220211162730739

image-20220211162743567

image-20220211162808638

프로젝트에 맞는 Gradle 버전이 필요하면 install automatically 옵션으로 추가하면 간단하게 사용 가능합니다.

image-20220213234237171

작업의 편리성을 위해 jenkins를 root 사용자로 작업합니다. (보안상 계정을 사용하시는걸 추천합니다.)

# vim /etc/sysconfig/jenkins
$JENKINS_USER="root"

# systemctl restart jenkins

배포(원격) 서버에 SSH 접속을 위한 SSH Key생성을 합니다.

[Jenkins 서버] jenkins를 root 사용자로 지정하였으므로 root 홈으로 이동하여 key를 생성합니다.

# cd /root
# ssh-keygen -t ecdsa -b 521 -m PEM

# ls /root/.ssh
id_ecdsa  id_ecdsa.pub

[배포(원격) 서버]

# ssh-keygen -t ecdsa -b 521 -m PEM

#vim /root/.ssh/authorized_keys
Jenkins 서버의 id_ecdsa.pub값 추가 후 저장

[Jenkins 서버] SSH 서버 설정 및 접속 테스트

메인 좌측 메뉴 > Jenkins 관리 > 시스템 설정에서 아래로 내려보면 Publish over SSH 항목이 보이며 아래와 같이 내용을 편집하고 Test Configuration을 눌러서 Success가 나오는지 확인합니다.

Name: 임의의 이름

Hostname: /etc/hosts 파일에 명시된 호스트명 또는 ip

Username: 접속 계정명

Remote Directory: 애플리케이션 루트 경로( 접속 계정은 이 경로에 접근할 수 있는 권한이 있어야 함)

image-20230727170333535

image-20230727170414300

Rocky Linux 9버전에서 ssh 접속이 되지 않는 현상 발생

 sshd[6172]: main: sshd: ssh-rsa algorithm is disabled

https://osg.kr/archives/718

openssh 버전은 openssh-8.7p1-29.el9_2 **버전이였고, 기존에 ssh 접속 가능한 서버는 **rockylinux 8 + openssh-8.0p1-10.el8.x86_64이였다.

Jenkins 작업 생성

메인 좌측 메뉴 > 새로운 Item > Freestyle project를 선택하고 작업 이름을 입력합니다.

image-20220211142843718

✔ 연결할 Github Repository 주소를 입력합니다.

image-20220211143346726

✔ Git 설정을 합니다.

image-20220211163408621

image-20220211163535899

ssh 연동 방법: https://osg.kr/archives/700

image-20220211174941019

✔ Gradle 빌드 설정

image-20220213170155041

image-20220213234423622

Junit 테스트 report 설정

빌드 후 조치에서 Publish Junit test result report를 선택합니다.

image-20220218143915290

추가된 Publish Junit test result report에서 Test report XMLs 값을 build/test-results/test/*.xml로 지정합니다.

이 위치는 jenkins에서 빌드가 완료되면 project home path(/var/lib/jenkins/workspace/프로젝트명)가 생성되는데 그 하위에 build/test-results/test를 보면 다음과 같은 xml 파일들이 있습니다.

image-20220218144439698

image-20220218143756835

✔빌드 후 조치에서 Send build artifacts over SSH를 선택합니다.

image-20220214145421171

image-20220214145606026

빌드 후 조치에서 상세 항목을 편집합니다.

Name: 임의의 이름

Source files: 원격 서버에 배포할 파일을 지정합니다. Jenkins가 빌드를 완료하면 /var/lib/jenkins/workspace/solution-shop/build/libs 위치에 jar파일을 생성하는데 workspace 경로를 제외하고 입력합니다.

Remove prefix: jar 파일만 전송하기 위해 앞에 build/libs 폴더를 제외 시킵니다.

Remote directory: 원격 서버에 배포할 경로를 지정합니다. (생략하면 설정에서 등록한 web-app-server1의 remote directory경로로 설정됩니다.)

Exec command: 원격 서버에 배포한 다음 실행할 명령어를 등록합니다.

image-20220215172049839

image-20220215172121269

저장을 누르면 메인화면에서 작업이 추가된 것이 보입니다.

image-20220211173531225

배포(원격) 서버에 서비스 설정을 합니다.

배포 서버에 접속하여 아래와 같은 설정을 합니다.

Java가 설치되어 있어야 합니다. (위에 Java 설치 참고)

서비스 경로 만들기

# mkdir /app

서비스 등록

# vim /etc/systemd/system/web-app-shop.service
[Unit]
Description=kdr solution shop web service
Wants=network-online.target
After=network-online.target

[Service]
User=root
ExecStart=/bin/bash -c "exec java -jar -DSpring.profiles.active=prod /app/shop/*.jar -XX:MinRAMPercentage=10 -XX:MaxRAMPercentage=15"
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

# systemctl daemon-reload 
# systemctl enable web-app-shop

Spring 프로젝트를 커밋 하기 전에 몇가지 수정 및 추가를 합니다.

application-prod.yml (운영서버 배포 설정 파일)에서 서버 포트, shutdown,

server:
  port: 9090
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
...  
file:
    path: /app/shop/logs

build.gradle에서 jar task를 skip하도록 설정을 추가합니다. 이것을 하지 않으면 빌드 후 xxx.jar, xxx-plain.jar 두개의 파일이 생성됩니다.

jar {
    enabled(false)
}

배포 script를 작성합니다. (배포 서버 OS가 Rocky Linux입니다.)

프로젝트에 shell script 파일 하나를 추가합니다.

image-20220215162912418

deploy.sh 파일에 다음과 같이 작성합니다.

#!/bin/bash
APP_NAME="solution"
SOURCE_PATH=/app
DEPLOY_PATH=/app/shop
LOG_PATH=/app/shop/logs
BACKUP_PATH=/app/shop/backup
HEALTH_CHECK_URL="http://localhost:9090/member/login"
today=`date`

if [ ! -d $LOG_PATH ] ;
then
    mkdir $LOG_PATH
    sleep 1
fi

if [ ! -d $DEPLOY_PATH ] ;
then
    mkdir $DEPLOY_PATH
    sleep 1
fi

if [ ! -d $BACKUP_PATH ] ;
then
    mkdir $BACKUP_PATH
   sleep 1
fi

echo "[$today] deploy start!!" >> $LOG_PATH/deploy.log

echo "[$today] copy backup jar" >> $LOG_PATH/deploy.log
cp -f $DEPLOY_PATH/*.jar ${BACKUP_PATH}

echo "[$today] stop systemctl stop web-app-shop" >> $LOG_PATH/deploy.log
systemctl stop web-app-shop

sleep 15

echo "[$today] source move to deploy path" >> $LOG_PATH/deploy.log
mv $SOURCE_PATH/$APP_NAME*.jar ${DEPLOY_PATH}

echo "[$today] systemctl start web-app-shop" >> $LOG_PATH/deploy.log
systemctl start web-app-shop

sleep 15

echo "[$today] start health check web-app-shop" >> $LOG_PATH/deploy.log
online=false
loopCount=1
while [ $loopCount -le 5 ]
do
    code=$(curl -sL --connect-timeout 20 --max-time 30 -w "%{http_code}\\n" "$HEALTH_CHECK_URL" -o /dev/null)
    echo "[$today] start health check web-app-shop $loopCount => code: $code" >> $LOG_PATH/deploy.log
    if [ "$code" = "200" ];
    then
        online=true
        break
    else loopCount=$((loopCount + 1))
        sleep 1
    fi
done

echo "online : $online" >> $LOG_PATH/deploy.log

if $online; then
    echo "[$today] web-app-shop is online." >> $LOG_PATH/deploy.log
    exit 0 # Success
else
    echo "[$today] web-app-shop start fail." >> $LOG_PATH/deploy.log
    echo "[$today] web-app-shop recovery." >> $LOG_PATH/deploy.log
    cp -f $BACKUP_PATH/*.jar ${DEPLOY_PATH}
    sleep 1
    systemctl restart web-app-shop
    exit 1 # Failed
fi

수정이 완료 되었다면 merge 후 master에 push를 날려봅니다.

image-20220214192953995

Jenkins 웹화면을 보면 좌측에 빌드 실행상태에 빌드가 진행중인 것이 보이고 Console Output을 클릭해보면 현재 진행상태를 알 수 있습니다.

image-20220214193307794

image-20220214193419890

좌측 메뉴에 Test Result를 눌러보면 테스트 결과도 확인할 수 있습니다.

image-20220218145354811

빌드가 완료되면 /var/lib/jenkins/workspace 경로 아래에 파일이 생성됩니다.

#  ls /var/lib/jenkins/workspace/solution-shop/build/libs
solution-1.0.1.jar

빌드 후 배포(원격) 서버의 서비스 상태를 확인 해봅니다.

# ls /app/shop
backup  deploy.sh  logs  solution-1.0.1.jar

# systemctl status web-app-shop
● web-app-shop.service - kdr solution shop web service
   Loaded: loaded (/etc/systemd/system/web-app-shop.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2022-02-14 19:33:11 KST; 3min 46s ago
 Main PID: 12050 (java)
    Tasks: 50 (limit: 49488)
   Memory: 720.4M
   CGroup: /system.slice/web-app-shop.service
           └─12050 java -jar -DSpring.profiles.active=prod /app/shop/solution-1.0.1.jar
           
# vim /app/shop/logs/application.log
22-02-14 19:10:12.621 INFO  o.s.b.w.e.tomcat.GracefulShutdown - Commencing graceful shutdown. Waiting for active requests to complete
22-02-14 19:10:12.625 INFO  o.s.b.w.e.tomcat.GracefulShutdown - Graceful shutdown complete
22-02-14 19:10:12.652 INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
22-02-14 19:10:12.654 INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown initiated...
22-02-14 19:10:12.660 INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown completed.
22-02-14 19:10:12.660 INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
22-02-14 19:10:12.661 INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
22-02-14 19:10:12.667 INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
22-02-14 19:10:14.402 INFO  o.h.validator.internal.util.Version - HV000001: Hibernate Validator 6.2.0.Final
22-02-14 19:10:14.413 INFO  n.kdrco.solution.SolutionApplication - Starting SolutionApplication using Java 11.0.11 on web-app-server1 with PID 12050 (/app/shop/solution-1.0.1.jar started by root in /)
22-02-14 19:10:14.421 INFO  n.kdrco.solution.SolutionApplication - The following profiles are active: prod

...

22-02-14 19:33:21.376 INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 9090 (http) with context path ''
22-02-14 19:33:21.996 INFO  o.s.s.a.ScheduledAnnotationBeanPostProcessor - No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
22-02-14 19:33:22.010 INFO  n.kdrco.solution.SolutionApplication - Started SolutionApplication in 10.228 seconds (JVM running for 10.878)

Slack으로 알림 받기

Slack 앱에서 Jenkins를 설치합니다.

image-20220215201701197

설치가 완료되면 안내 페이지가 나오는데 아래로 내려보면 토큰 값이 있습니다. 이것을 복사 해둡니다.

image-20220215211514619

Jenkins 웹 > Jenkins 관리 > 플러그인 관리에서 Slack Notification을 설치합니다.

image-20220215204655143

Jenkins 관리 > Manage Credential Add credentials에서 Slack 을 추가합니다.

image-20220215205159599

Secret에 복사 해둔 slack 토큰 값을 입력합니다.

image-20220215211336026

이전에 생성해둔 작업 설정에서 빌드 조치에 Slack Notifications를 추가합니다.

image-20220215211728976

성공, 실패, Unstable( 등록해둔 shell script가 실패한 경우) 등을 추가합니다.

image-20220215212935277

고급을 눌러 slack 정보를 기입합니다.

workspace: Slack의 team’s workspace name(예: kdr-company)

Credential: Manage Credential에서 추가한 slack을 선택합니다.

Channel: 알림을 받을 채널명을 적어줍니다. (예 #jenkins-build)

image-20220215214422934

Jenkins 빌드를 다시 해보면 아래와 같이 Slack으로 알림이 옵니다.

image-20220215214643137

기타 / 참고

카나리(canary) 배포 전략

새로운 버전을 모든 서버가 아닌 일부 서버에만 배포해서 긴 시간 충분히 모니터링하며 이상 여부를 판단한 후에 나머지 서버에 배포하는 방법으로, 문제 발생 여부를 빠르게 판단할 수 있다는 장점이 있습니다. 아래 그림과 같이 처음에는 서버 한 대에만 배포하고, 모니터링을 완료하면 서버의 3분의 1부터 전체에 이르기까지 여러 번에 나누어 배포를 진행합니다.

img

배포하다가 장애가 발생하면 먼저 배포 공지 스레드에 현재 상황을 공유하고 배포 도구에 남아 있는 이전 버전의 결과물로 재배포를 진행합니다. 롤백 과정을 완료하면 장애가 발생한 내용의 PR을 되돌리고(revert) 새로운 버전의 태그를 추가로 발급한 다음 장애가 발생한 버전의 태그를 Git에서 제거합니다. 이렇게 하면 나중에 잘못된 버전으로 롤백하는 것을 방지할 수 있습니다.

참고:

https://www.jenkins.io/doc/

https://www.redhat.com/ko/topics/devops/what-is-ci-cd

https://engineering.linecorp.com/ko/blog/build-a-continuous-cicd-environment-based-on-data/

https://engineering.linecorp.com/ko/blog/line-ads-devops-culture/

댓글남기기