CI/CD란?시작Jenkins 인스턴스 설정Swap 메모리 설정Jenkins 설치Daemon 활성화젠킨스 시작Publish Over SSH 설치SSH 설정키 생성키 설정프로젝트 생성빌드 설정 - Git빌드 설정 - Shell빌드 후 조치Verbose output in console 설정SSH Server 설정Transfers 설정API 인스턴스 설정AWS 인바운드 규칙 설정Swap 메모리 설정JAVA 17 설치Submodule에서 설정파일 가져오기서브 모듈 레포지토리에서 배포 키 추가Credentials 추가Project에 서브 모듈 설정 추가Credentials 설정Advanced sub-modules behaviours 설정빌드의 Execute shell 수정빌드 후 조치 수정빌드Git Hub Webhook 설정하기lsof를 사용해서 포트를 사용 중인 프로세스 종료하기lsof 설치lsof 사용해서 프로세스 종료 하기
CI/CD란?
먼저 CI/CD에 대해서 알아보겠습니다.
CI란 지속적 통합입니다. 빌드/테스트의 자동화 과정인데요. 애플리케이션에 대한 새로운 코드 변경 사항이 일관되고 자동화된 프로세스로 빌드 및 테스트되고 공유 레포지토리에 병합되도록 해 코드의 품질을 지속적으로 유지될 수 있도록 하는 것입니다.
CD란 지속적 배포입니다. CI의 연장선으로 생각하면 됩니다. CI는 개발 환경에 대한 변경사항을 지속적으로 통합하는 것이었다면, CD는 자동화된 프로세스에 따라 프로덕션 레벨까지 지속적으로 배포하는 것을 의미 합니다.
이러한 반복적인 통합/배포 과정은 자동화 되지 않는다면 개발자에겐 번거롭고 지루합니다. 그렇기 때문에 코드 품질 관리에 있어서는 CI/CD가 꼭 필요 하다고 볼 수 있습니다.
오늘은 CI/CD를 실현시켜 줄 Jenkins 사용법에 대해서 알아보겠습니다.

시작
Git Hub + AWS EC2 + Jenkins 조합으로 Instakyuram 프로젝트 CI/CD를 적용해보겠습니다.
EC2 인스턴스를 두 개 생성해주겠습니다. 하나는 Jenkins서버, 다른 하나는 실제 애플리케이션을 구동시킬 서버입니다. 저는 RedHat을 선택하겠습니다.

키 페어도 지정해주겠습니다.

생성이 완료되었다면 키 파일이 있는 경로로 이동해서 jenkins 인스턴스에 접속해줍니다.
ssh -i pjh.cer ec2-user@(public IP)
Jenkins 인스턴스 설정
Swap 메모리 설정
먼저 저는 프리티어를 사용하기 때문에 램이 1GB로 굉장히 적습니다. 스왑 메모리를 설정하지 않으면 인스턴스가 자주 멈추고 버벅거리기 때문에 해당 문제를 완화하기 위해 스왑 메모리 설정을 해줍니다.
1GB일때는 2GB를 스왑메모리로 설정하는 것을 권장하고 있습니다.
sudo dd if=/dev/zero of=/swapfile bs=128M count=16 sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile sudo swapon -s sudo vi /etc/fstab #맨 밑 줄에 아래 코드를 추가 /swapfile swap swap defaults 0 0
free -m 명령어로 스왑 메모리 설정이 잘 됐는지 확인

Jenkins 설치
sudo yum install java-17-openjdk sudo yum install wget 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.key> sudo yum upgrade sudo yum install jenkins
Daemon 활성화
부팅 시 자동으로 Jenkins가 시작될 수 있도록 설정해줍니다.
sudo systemctl enable jenkins sudo systemctl start jenkins sudo systemctl status jenkins
젠킨스가 정상적으로 실행되었다면 아래와 같이 enabled, active(running)이라고 표시됩니다.

웹으로 젠킨스에 접속하기 전에 EC2에서 인바운드 규칙을 통해 포트를 열어줘야합니다. 처음에는 SSH 접속을 위한 22번 포트 밖에 열려있지 않기 때문에 8080포트를 사용하는 젠킨스를 위해 포트를 열어줍니다.
보안 그룹 클릭

인바운드 규칙 편집 클릭

규칙추가 -> 사용자 지정 TCP -> 포트범위 8080 -> 0.0.0.0/0

이제 웹 브라우저를 통해 자신의 jenkins 인스턴스의 public IP와 8080포트를 입력해 웹으로 젠킨스에 접속할 수 있습니다.
젠킨스 시작
웹으로 접속하면 아래와 같은 창이 뜹니다. 초기 비밀번호를 입력해줘야 합니다.
터미널에서 아래 명령어를 입력해서 비밀번호를 복붙 해줍니다.
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

권장 플러그인을 설치하겠습니다.

대기

설치가 완료되면 적절히 입력해줍니다.

건들일거 없습니다. 바로 Save and Finish 눌러주고 넘어갑니다.

Publish Over SSH 설치
Jenkins 관리 -> 플러그인 관리 -> 설치가능 -> ssh 검색 -> Publish Over SSH 체크 -> Install without restart

SSH 설정
젠킨스에서 API-Server로 접속해 빌드된 jar를 전달해 실행시킬 수 있어야 합니다. 관련 설정을 해보겠습니다.
키 생성
젠킨스 서버만 API-Server에 접속할 수 있도록 키를 생성해줘야합니다.
젠킨스 인스턴스의 터미널에서 아래 명령어를 입력해 키를 생성해줍니다.
ssh-keygen -t rsa -f ~/.ssh/id_rsa -m PEM
명령어를 치고난 후 ~/.ssh/ 경로에 id_rsa, id_rsa_pub와 같이 키 페어가 생깁니다.
id_rsa는 젠킨스의 ssh설정에,
id_rsa.pub는 API-Server의 Autorized_keys 설정에 복사 붙여넣기 해줄겁니다.

키 설정
- API Server 인스턴스 먼저 설정합니다. 아래 명령어를 입력 해 편집기를 켜서 맨 밑줄을 한 줄 비우고 id_rsa.pub 내용을 전부 복사해줍니다. 그리고 한 칸 띄우고 키를 구별할 수 있도록 jenkins라고 alias를 입력해줍니다.
vi ~/.ssh/authorized_keys

이후 키 설정에 접근할 수 있도록 권한 설정도 해줍니다.
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys
- Jenkins 키 설정도 해줍니다. Jenkins 관리 -> 시스템 설정 -> 맨 밑의 Publish over SSH에서 Key에 id_rsa 전부 복붙 Test Configuration 클릭후 Success가 출력되면 저장 누르고 설정 완료

SSH Servers 추가 후 아래 내용 입력



프로젝트 생성
새로운 Item 클릭 -> Item 이름 입력 -> Freestyle project -> OK

빌드 설정 - Git
Git에서 받아온 프로젝트를 빌드를 해서 API-Server 인스턴스로 넘겨줄겁니다.
관련 설정을 하겠습니다.
Jenkins 인스턴스에 git을 설치해줍니다. 터미널에 다음 명령어를 입력합니다.
sudo yum install git
그리고 젠킨스 웹 설정에서
구성 -> 소스코드 관리 -> git 선택 -> Repository URL 입력 -> 브랜치 적절히 입력 -> 빌드 유발 -> GitHub hook trigger for GITScm polling 체크
이렇게 되면 내 레포지토리에서 어떤 특정 이벤트가 발생하면 clone/pull을 받아와 빌드를 실행할 겁니다.


빌드 설정 - Shell
Git 에서 소스코드를 받아오면 빌드를 해야겠죠? 빌드를 하는 명령어를 실행할 수 있도록 설정해줍니다.
Add build step -> Excecute Shell


gradlew를 실행할 수 있도록 권한 설정을 해주고 clean 및 build를 할 수 있도록 명령어를 입력합니다.

빌드 후 조치
빌드 후에는 API-Server 인스턴스로 빌드 결과물을 넘겨주고 어떤 작업을 할지 설정해줍니다.
Verbose output in console 설정
빌드할 때 상세한 내역을 표시 해줍니다.


SSH Server 설정
아까 SSH 설정에서 설정했던 것을 사용합니다. 선택해줍니다.

Transfers 설정
젠킨스의 빌드 작업은 /var/lib/jenkins/workspace/instakyuram-prod(item 이름) 경로에서 실행됩니다.

이곳 기준으로 jar파일이 생성될 위치를 입력해주고 지울 prefix 설정도 해줍니다.
nohup을 사용하지 않으면 백그라운드에서 서버가 실행되는 것이 아니라 계속 빌드 과정이 끝나지 않는 모습을 볼 수 있습니다.
nohup을 사용해 백그라운드에서 서버가 동작할 수 있도록 해줍니다.

API 인스턴스 설정
미리 Jenkins 설정은 다 완료했습니다. 지금까지는 API 인스턴스 관련 설정이라고는 공개키 설정밖에 하지 않았습니다. 다음은 실제 애플리케이션이 구동될 인스턴스의 설정을 마무리 해보도록하겠습니다.
AWS 인바운드 규칙 설정
아까 Jenkins인스턴스에만 8080포트를 열어주고 API-Server 인스턴스에는 8080포트를 열어주지 않았습니다. 똑같이 열어줍니다.

Swap 메모리 설정
똑같은 프리티어이므로 똑같이 Swap Memory 설정을 해줍니다.
sudo dd if=/dev/zero of=/swapfile bs=128M count=16 sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile sudo swapon -s sudo vi /etc/fstab #맨 밑 줄에 아래 코드를 추가 /swapfile swap swap defaults 0 0
JAVA 17 설치
Java 애플리케이션이 실행될 수 있도록 자바 17을 설치해줍니다
sudo yum install java-17-openjdk
기본적인 준비가 완료 되었습니다. 다음은 필수 설정이 아닌 Submodule을 사용을 했다면 해줘야할 선택적 설정입니다.
Submodule에서 설정파일 가져오기
저희 인스타규램 프로젝트에서는 Submodule을 이용해 배포 환경에서의 프로파일을 별도로 관리했습니다. 프로젝트를 빌드하기 전에 서브모듈의 데이터도 가져오고 서브모듈에 보관 중인 설정 파일도 메인 모듈로 옮겨와야 합니다.
서브 모듈은 private repository이므로 별도의 권한이 필요합니다 깃 허브에 접속할 수 있도록 Jenkins에서 credentials을 추가해줍니다.
이 과정에서 아까 만든 젠킨스의 키 쌍을 재활용 하겠습니다.
서브 모듈 레포지토리에서 배포 키 추가
jenkins에서 처음 만들었던 공개 키를 서브모듈 레포지토리에 추가해줍니다.

Credentials 추가
Jenkins 관리 -> Manage Credentials -> 아래쪽 Jenkins 선택 Global credentials 클릭

Add Credentials 클릭 후 SSH Username with private key 선택


ID와 Username에는 적절히 식별할 수 있는 값을 넣어주고 Private Key → Enter directly를 클릭해서 Add를 누른 후 비밀 키를 복사 붙여넣기 해줍니다. 그 후 Create.

Project에 서브 모듈 설정 추가
아까 빌드 관련 설정했던 부분에서 서브 모듈 설정을 추가 합니다.
Credentials 설정
방금 추가했던 Credentials를 설정에 적용해줄겁니다.
소스코드 관리에서 Credentials 설정 해줍니다.

Advanced sub-modules behaviours 설정
추가적인 서브모듈에 관한 설정을 해줍니다.

아래 3개 옵션을 체크 해줍니다.
- 레포지토리에 있는 서브모듈 모두 업데이트
- 업데이트 할 때 서브모듈이 바라보고 있는 브랜치의 가장 최신 커밋을 가지고 오기
- 서브모듈 레포지토리의 권한을 확인할 때 서비스 코드 레포지토리 권한 계정 사용

빌드의 Execute shell 수정
cp 명령어를 추가해서 서브모듈에 있는 application-real.yml 파일을 메인 모듈로 옮겨주고 빌드할 수 있도록 수정해줍니다.

빌드 후 조치 수정
이제 서브 모듈에 있는 real 프로파일을 사용해 실행을 합니다.
#real환경의 프로파일로 실행합니다. --spring.profiles.active=real
#이 프로파일은 Jasypt로 암호화 되어있기 때문에 복호화할 수 있는 키를 입력해줍니다. --ENCRYPTOR_KEY=암호화 키

빌드
모든 설정이 마무리 되었다면 지금 빌드를 눌러줍니다.

아래쪽에선 빌드 내역을 볼 수 있습니다.

빌드 항목을 클릭하면 자세한 내용을 볼 수 있습니다.

Console output에서 출력 내용들을 볼 수 있습니다. 에러 발생 시 여기에 로그가 남겠죠?

Git Hub Webhook 설정하기
Repository에 merge가 되면 자동으로 젠킨스에게 이벤트를 전달해 빌드 및 배포를 할 수 있도록 만들 수 있습니다. 관련 설정을 해보겠습니다.
Git hub repository로 들어가 web hook을 추가하겠습니다.

다음과 같이 설정해주시고 Add webhook을 누릅니다.

코드를 수정하고 commit, push를 날려보면 자동으로 jenkins에서 빌드가 시작되는 것을 확인할 수 있습니다.

하지만 로그를 확인해보면 아래와 같이 이미 포트가 사용 중이라고 뜨는데요. 애플리케이션이 실행 중에 프로젝트가 빌드되고 다시 실행되어서 그렇습니다. 새로 배포할 때에는 기존의 애플리케이션을 종료 할 수 있도록 설정을 해보겠습니다.

lsof를 사용해서 포트를 사용 중인 프로세스 종료하기
lsof 설치
sudo yum install lsof
lsof 사용해서 프로세스 종료 하기
다음 명령어를 빌드 후 조치 항목의 Exec command에 추가해주도록 하겠습니다.
sudo kill -15 $(sudo lsof -t -i:8080)

저장 후 다시 push를 해보면 정상적으로 배포되는 것을 확인할 수 있습니다.