Server/CI&CD

[CI/CD] GitLab + Jenkins를 활용한 SpringBoot CI/CD 구축 (3) - 完

aeeazip 2024. 2. 13. 02:31

목차


0. 포스팅 계기
1. CI/CD 개념
2. OverView : GitLab + Jenkins + Nginx + AWS EC2  
3. EC2에 Jenkins 설치 후 초기 설정
4. GitLab - Jenkins 설정
5. Jenkins - EC2 연결
6. 방화벽 설정
7. Nginx로 reverse_proxy 설정
 
 
오늘은 5번 Jenkins - EC2 연결부터 7번 Nginx로 reverse proxy 설정하는 방법에 대해서 다루려고 한다.
전반적인 OverView나 EC2에 Jenkins 설치 후 초기 설정하는 과정이 궁금하다면 이전편을 참고하기 바란다.
 
https://aeeazip.tistory.com/39

 

[CI/CD] GitLab + Jenkins를 활용한 SpringBoot CI/CD 구축 (1) - EC2에 Jenkins 설치부터 초기 설정까지

목차 0. 포스팅 계기 1. CI/CD 개념 2. OverView : GitLab + Jenkins + Nginx + AWS EC2 3. EC2에 Jenkins 설치 후 초기 설정 4. GitLab - Jenkins 설정 5. Jenkins - EC2 연결 6. 방화벽 설정 7. Nginx로 reverse_proxy 설정 8. 빌드 스크

aeeazip.tistory.com

 

 

 

 

5. Jenkins - EC2 연결


해당 파트에서는 젠킨스가 프로젝트 빌드가 끝나고 만들어진 jar 파일을 EC2로 전송한다. EC2는 jar 파일을 받아 스크립트를 실행시켜 프로젝트를 배포한다.
 
 

1. EC2 - SSH 연결

  • SSH 플러그인을 설치
  • Jenkins 관리 > Manage Credentials에서 SSH를 위한 새로운 Credential 추가
  • 이때 EC2로 Ubuntu를 사용하는 경우 Ubuntu 22.04는 버전 문제로 젠킨스 연결이 불가능 한 이슈가 발생할 수 있으므로 Ubuntu 20.04 언더 버전을 사용하는 것을 추천한다! (필자 또한 Ubuntu 20.04 사용)

 

Kind SSH Username with private key
Scope Global
ID 원하는 ID
Username EC2 사용자 이름 (Ubuntu를 사용하는 경우 ubuntu라고 작성)
Private Key Enter directly 체크 → .ppk 내용

 

 
 

  • Jenkins 관리 > System > SSH Remote hosts
  • check connection으로 테스트 가능

 

Hostname EC2 public IP
Port 22 (SSH 포트번호는 22)
Credentials 앞서 생성한 Credential

 

 

 

 

2. 빌드 설정

  • item > Configure > Build Step
  • Add bulid step > Execute shell 추가 : jar 파일을 전송하는 코드를 입력

 
jar 파일 전송 명령어는 아래와 같은 형식으로 작성해주어야 한다.

$ scp -v -o StrictHostKeyChecking=no -i [젠킨스 pem 파일 경로] [젠킨스 jar 파일 경로] [ec2 사용자]@[ec2 public ip]:[ec2 경로]

 
 
 

 
 
그런데 기본적으로 EC2에는 /var/jenkins_home 디렉토리가 없다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

그래서 💣 /var/lib/jenkins 💣로 접근해야 한다... 
경로 때문에 하루종일 삽질했다 언버블리버블~
 
 
아무튼 나는 .pem을 EC2 내부에 직접 만들어주었다.
ppk를 pem으로 변환해서 생성했고 chmod 777로 .pem 파일에 권한도 부여해주었다.
 
 

 
 

  • Build > Add build step에서 Execute shell script on remote host using ssh 추가
  • 빌드 후 jar 파일을 EC2로 보낸 다음, 실행할 스크립트를 지정할 수 있다.
  • 나는 EC2 내부에 배포 스크립트 파일을 만들어두고, EC2로 jar 파일이 전달되면 해당 스크립트가 수행될 수 있도록 설정해주었다.

 

SSH Site 앞서 설정한 EC2
Command /home/ubuntu/app-server/deploy.sh

 
 

 
 
이때 화면에 빨간 글씨로 SSH Site not specified 라고 뜨는데 이렇게 뜨는 것이 맞다. 

 

 

 

3. Nginx 설정

 
Nginx를 웹서버이자 프록시 서버로 사용하기 위해 EC2에 nginx를 설치해주었다.
 

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install nginx
$ sudo service nginx start
$ sudo service nginx status

 
 
먼저 설정 파일을 연다.

$ sudo vi /etc/nginx/sites-enabled/default

 
위치에 유의하며 아래의 2 줄을 추가한다.

include /etc/nginx/conf.d/service-url.inc;
proxy_pass $service_url;
+ proxy_pass 위에 try files는 주석 처리!!

 

 
 
다음으로 service-url.inc 파일을 추가해준다. 

  • memotion 서버는 9000 포트를 사용
  • proxy 서버 적용 시 ) 기본 포트(80)으로 접속 시 자동으로 9000 포트에 연결
$ sudo vi /etc/nginx/conf.d/service-url.inc

 
 
파일 내용을 작성한다.

set $service_url http://127.0.0.1:9000;

 
 
nginx 서버를 재시작한다.

$ sudo service nginx restart

 

 

 

4. 배포 스크립트 작성

 
배포 스크립트는 /home/ubuntu/app-server 디렉토리 밑에 deploy.sh 라는 이름으로 생성해준다.
필자는 /home/ubuntu/memotion_server/deploy.sh 경로로 작성했으며
 
deploy.sh 내용은 다음과 같다.
 

#!/bin/bash

REPOSITORY=/home/ubuntu/memotion_server
PROJECT_NAME=Memotion

echo ">>> 현재 구동중인 애플리케이션 pid 확인"

CURRENT_PID=$(pgrep -fl Memotion | grep java | awk '{print $1}')

echo ">> PID : " $CURRENT_PID

if [ -z "$CURRENT_PID" ]; then
  echo "구동중인 애플리케이션 없음."
else
  echo ">>> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 10
fi

echo ">>> 애플리케이션 배포"

JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)

echo ">>> JAR NAME : $JAR_NAME"

chmod +x $JAR_NAME

echo ">>> $JAR_NAME 실행"

nohup java -jar \
    -Dspring.profiles.active=prod \
    $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &

 
 
지금은 테스트 단계이므로 .jar가 없기 때문에 ~/app_server 밑에 jar 파일을 복사해서 넣어주고
~/app_server 밑에 있는 jar 파일과 deploy.sh 파일 모두 chmod 755로 권한을 부여한다.
 
그런데 build에 성공해도 자꾸 deploy.sh를 못 찾고 실행할 권한이 없다고 떴다!!!!!!!!!!!!!
경로도 문제 없고 실행 권한도 줬잖아;;
 

sudo: ./deploy.sh: command not found
chmod: changing permissions of 'deploy.sh': Operation not permitted

 
삽질 끝에 다른 사용자의 파일을 실행하려는 경우
deploy.sh의 소유자가 Jenkins 사용자가 아니라면 해당 파일을 실행할 수 없다는 것을 알았다.
 
그래서 deploy.hs 파일 소유자를 그냥 Jenkins로 바꿔주었다 ^.^
 

# 실제로 사용중인 Jenkins 사용자와 그룹을 확인
$ id jenkins

# jenkins-user:jenkins-group은 id값을 넣어줌
$ sudo chown jenkins-user:jenkins-group /home/ubuntu/memotion_server/deploy.sh

 

 
 
그런데 또 똑같은 오류가 발생했다~
GPT의 도움을 얻었는데 Jenkins에서 스크립트를 실행 시 절대경로를 사용하는게 좋다고 해서 절대 경로로 수정해주었다.
 
+ Jenkins 빌드는 일반적으로  Jenkins 서버 작업 공간 (/var/lib/jenkins/workspace) 에서 실행돼서 상대경로로 작성하면 찾을 수 없던 것!
 
 
다시 Jenkins 웹페이지로 돌아가
Execute shell script on remote host using ssh를 아래와 같이 절대경로로 수정해주었고
 

 
 
deploy.sh는 mv 명령어를 사용해 /home/ubuntu/memotion_server/deploy.sh에서 /var/lib/jenins/workspace/deploy.sh로 이동시켜주었고 파일 내용도 아래와 같이 수정해주었다.
 

#!/bin/bash

REPOSITORY=/var/lib/jenkins/workspace/CICD-Memotion/build/libs
PROJECT_NAME=Memotion

echo ">>> 현재 구동중인 애플리케이션 pid 확인"

CURRENT_PID=$(pgrep -fl Memotion | grep java | awk '{print $1}')

echo ">> PID : " $CURRENT_PID

if [ -z "$CURRENT_PID" ]; then
  echo "구동중인 애플리케이션 없음."
else
  echo ">>> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 10
fi

echo ">>> 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)

echo ">>> JAR NAME : $JAR_NAME"

chmod +x $JAR_NAME

echo ">>> $JAR_NAME 실행"

nohup java -jar \
    -Dspring.profiles.active=prod \
    $JAR_NAME > /var/lib/jenkins/workspace/nohup.out 2>&1 &

echo ">>> 스크립트 종료"
exit 0

 

 

 

6. 방화벽 설정


EC2의 보안설정 > 인바운드 규칙을 편집해주어도 되지만 EC2 내부에서 직접 방화벽을 설정해주어도 된다!
EC2에서 인바운드 규칙을 편집하는 쪽이 더 간편하지만, 리눅스와 친해지기 위해서... 직접 방화벽을 설정해주었다.
 
memotion 서버는 9000 포트를 사용하기 때문에 방화벽으로 80, 8080, 9000 등등의 포트를 열어주었다.
 

 
 
이제 실제 API 요청을 보냈을 때 잘 동작하는 것을 확인할 수 있다!
 
 
 
 
 

아쉬운점


처음에는 Docker와 Jenkins를 활용해 CI/CD를 구축하려고 했다.
이때, 나는 EC2 내부에 Docker를 설치해서 Jenkins가 빌드 후 생성한 jar를 EC2로 넘겨주면deploy.sh에 jar 파일을 Docker Hub에 업로드하고 이미지를 실행시키는...
 
위와 같은 로직을 생각하고 실제로 EC2에 docker, docker-compose도 설치 + DockerFile까지 작성했는데 deploy.sh 작성할 때쯤 시간이 부족하기도 하고 Docker를 사용했을 때의 장점도 살리지 못하는 것 같다는 생각에 스크립트 내용을 jar 파일을 실행하는 내용으로 변경했다.
 
Gitlab + Jenkins + Docker + EC2를 활용한 배포 조합이 흔하지 않은지(?) 리소스가 부족했는데
보통 docker 안에 jenkins를 내려받고 jenkins 이미지를 컨테이너로 실행하는 방식 = docker 활용하는 일반적 방식 인 것 같아서 다음에 기회가 된다면 요런 방식으로도 도전해보고 싶다!
 
근데 스크립트 내부에서 docker 허브에 플젝을 올려 이미지를 실행시키는게 결코 나쁜 배포 방식이라고 말할 수 없고
세상엔 정말 다양한 방식의 CI/CD 로직이 존재하니까 프로젝트 상황, 규모, 자금에 맞는 방식을 채택하는게 베스트라고 생각한다.
 
.
.
.
 
Gitlab + Jenkins + Nginx + EC2를 활용한 배포 完