1. CI/CD 정의
CI(Continuous Integration)는 VCS(GIT 등)에서 PUSH가 되면 자동으로 빌드가 실행돼서 안정적인 배포 파일을 만드는 과정이다. 단위 테스트, 컴파일, 테스트, 패키징 작업 등이 포함됐다. 그리고 이 빌드 결과를 배포까지 진행하는 과정을 CD(Continuous Deployment)라고 한다. CI에 의해서 패키징 된 결과물들을 다시 개발서버, 테스트 서버, 운영 서버에 배포하여 개발 생산성을 높인다.
예전에는 각자가 개발한 코드를 합칠 때마다 큰 일이었다. 그래서 매주 코드 Merge날을 정해서 각자가 개발한 코드를 합치는 일만 진행했었다. 이런 수작업 때문에 생산성이 좋지 않았으며, 개발자들은 지속해서 코드가 통합되는 환경을 수동으로 구축하게 되었다. 그러나 원격 저장소가 푸시가 될 때마다 코드를 병합, 테스트 코드와 빌드를 수행하면서 더 이상 수동으로 코드를 통합할 필요가 없어지게 됐고, 개발자들은 개발에만 집중할 수 있게 됐다.
CI에서 중요한 점은 테스팅 자동화이다. 지속적으로 통합하기 위해서는 완전한 프로그램임을 보장하기 위해 테스트 코드가 구현되어야 한다.
2. Jenkins란?
Jenkins는 지속적인 통합과 배포를 도와주는 Java로 제작된 오픈 소스 툴이다. 다양한 plugin과 연동돼서 다양한 기능들을 제공해 준다. 서버에 직접적으로 설치를 안 해도(Node.js 등) Jenkins에서 plugin 설치가 됐다면 그 프로그램을 실행시켜 준다.
Jenkins는 온프레미스 방식과 클라우드 방식 둘 다 제공하며, 여기서 온프레미스란 자체적으로 보유하고 있는 서버에 직접 설치하고 운영하는 방식을 뜻합니다.
위의 그림과 같이 VCS에서 특정 branch에 commit과 push가 되면 jenkins에서는 commit, build, test, stage, deploy 단계를 거쳐서 특정 서버에 배포가 되게끔 도와줍니다.
3. EC2 서버에 Jenkins 설치
sudo apt-get update && sudo apt-get upgrade
Jenkins는 Java로 제작된 오픈 소스이기에 java 11 버전을 설치해 줍니다.
sudo apt-get install openjdk-11-jdk
java -version 명령어로 자바가 정상적으로 설치됐는지 확인해 줍니다.
sudo apt install -y git 명령어로 git을 설치해 줍니다.
git --version 명령어로 위의 사진처럼 나온다면 정상적으로 설치가 된 것입니다.
wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -
명령어로 key를 등록합니다.
echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list
Key 등록
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys FCEF32E745F2C3D5
sudo apt-get install jenkins 명령어로 jenkins를 설치해 줍니다.
참고로 Jenkins는 8080 포트에 실행됩니다. 만약 Jenkins 서버 포트 번호를 변경하고 싶다면 구글링을 하시길 추천드립니다.
서비스 재가동
sudo service jenkins restart
sudo systemctl status jenkins 명령어로 상태를 확인해 보면
위의 표시가 뜬다면 정상적으로 Jenkins 설치가 완료됐습니다.
초기 비밀번호 확인
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
비밀번호가 나오면 복사하고
URL에 8080 포트 쪽으로 들어가서 비밀번호를 붙여 넣기 합니다.
Install suggested plugins을 선택합니다.
로그인할 아이디와 암호를 입력해 줍니다.
위의 그림처럼 나온다면 Jenkins 설치가 완료된 것입니다.
4. Item 생성
저는 gradle로 프로젝트를 생성했으니 gradle로 빌드해 보겠습니다.
Jenkins 관리에 들어갑니다.
플러그인 관리를 선택.
Gradle plugin이 설치된 지 확인합니다. 만약 설치가 안 됐다면 Available plugins에서 Gradle plugin을 설치해 줍니다.
이제 본격적으로 배포 파일을 만들어보겠습니다.
새로운 Item 클릭.
이름을 정해주시고 Freestyle project를 선택해 줍니다.
설명은 알아서 입력해 주시고
소스 코드 관리에 git을 선택해 줍니다.
Repository URL에 자신의 github URL을 입력해 줍니다.
Branch Specifier는 master 브랜치의 소스 코드들을 Jenkins에서 git pull 한다는 의미입니다.
Build Steps 단계에서 Invoke Gradle script를 선택합니다.
Use Gradle Wrapper를 선택, Make gradlew executable을 선택합니다.
Tasks의 clean build는 이전의 build 파일을 삭제하고 build를 진행하는 것을 뜻합니다.
여기까지 저장을 해보고 빌드를 해봅시다. 초기하면에 들어가고
버튼을 눌러봤더니.... 안 됐습니다. 계속 기다려보기로 하겠습니다.
...
네. 안 됩니다. 계속 Compile Querydsl에서 무한 로딩이 걸리더군요.
도저히 원인도 모르겠고, 계속 구글링을 해봤더니...
제가 EC2 프리티어 사용자였던 게 문제였습니다. AWS EC2 프리티어의 경우 보통 t2.micro를 사용합니다. RAM이 1GB밖에 안 돼서, 빌드 규모가 조금만 커도 멈췄던 것입니다. 참고로 제 프로젝트는 QueryDsl을 사용했고, 프로젝트의 규모가 크다 보니 1GB RAM으로는 도저히 빌드가 안 됐습니다.
해결책은 두 가지입니다.
1) 메모리 스왑하기
https://progdev.tistory.com/26
위의 블로그에 해결책이 나왔으니 참고하시길 바랍니다. 다만, 메모리 스왑을 해도 속도가 느려진다는 단점이 있습니다.
2) EC2 사양 업그레이드
저는 micro로 되도록 꿀을 빨고 싶었으나, 어쩔 수 없이 T2 small로 사양을 업그레이드시켰습니다.
https://ndb796.tistory.com/259
블로그에 사양을 업그레이드하는 방법이 나왔으니 참고하시길 바랍니다.
이 사건으로 2가지 교훈을 얻었습니다. CI/CD 처리 과정을 빠르게 하려면 장비 업그레이드는 필수였다는 점이고, 두 번째는 compile QueryDSL이 생각보다 메모리를 많이 사용한다는 점입니다.
T2.small로 업그레이드를 해봤더니...
정상적으로 완료됐습니다. (이 맛에 현질을 합니다 ^^)
콘솔 결과의 경로로 ec2에 들어가 보면
github의 소스코드들이 정상적으로 git pull 된 것을 확인할 수 있습니다.
build 경로로 들어가 보면 정상적으로 빌드된 걸 확인할 수 있습니다.
빌드가 정상적으로 됐으면 실행을 해봐야겠죠?
Jenkins 관리 클릭.
플러그인 관리 클릭.
post build를 검색하고 다음 plugin을 설치해 줍니다.
이제 기존의 item의 구성을 클릭.
빌드 후 조치에서
post build task를 선택합니다.
script에서 hospital jar 파일을 배포해 봅니다. 그러면.
예 안 됩니다. 왜 안 되는 걸까요?
제 기존의 application.yml 파일은 이렇게 설정했습니다. application-db.yml, application-aws.yml, application-jwt.yml 파일은 보안 때문에 git ignore를 했습니다. application-jwt.yml 파일을 불러올 수 없기에 위의 에러 로그가 뜬 것입니다.
application-db.yml, application-aws.yml, application-jwt.yml 파일들을 ec2 서버에 복사하도록 했습니다.
Dspring.config.location을 이용해서 흩어진 설정 yml 파일들을 한꺼번에 빌드하도록 했습니다.
그럼에도 안 되더라고요...
이후 엔터 표시가 아닌 application.yml,/home/ubuntu/spring-config/application-jwt.yml, 이런 식으로 붙여 써야 된다는 사실을 깨달았지만, 계속 바꿨음에도 안 됐습니다.
저는 최후의 방법으로
기존 application-jwt.yml 등 3개의 파일들을 기존의 application-jwt 위치로 copy 해서 옮기고, 그다음에 빌드를 하기로 했습니다. 이제는 정말로 된 줄 알았더니...
그럼에도 문제가 발생했습니다... Permission denied가 뜨더라고요. Jenkins에서
udo cat /etc/passwd 명령어를 통해 jenkins의 권한을 살펴보면
제약이 있었습니다.
sudo visudo 명령어로
jenkins ALL=(ALL) NOPASSWD: ALL 을 입력 후 ctrl+O로 저장, ctrl+X로 종료해 줍시다. 이제 jenkins 계정에 대해서는 비밀번호를 묻지 않으므로, sudo 명령어가 정상 작동하게 됩니다.
이제 정말 마지막이라는 생각으로 실행을 해봤습니다.
정상적으로 빌드가 됐고
정상적으로 실행이 됐음을 확인할 수 있습니다.
다음 2편에서는 github에 push가 될 때마다 Jenkins에서 자동으로 실행되게 설정하는 방법.
기존 ec2(Jenkins 서버)에서 다른 ec2(배포 서버)로 파일을 어떻게 옮기는지에 관해서 올리겠습니다.
'DevOps > CI&CD' 카테고리의 다른 글
EC2에 SonarQube 설치 & Jenkins로 코드 분석하기 (2) | 2023.04.27 |
---|---|
Jenkins를 활용한 CI/CD 구축(4/4) - Pipeline 구축 (0) | 2023.04.27 |
Jenkins를 활용한 CI/CD 구축(3/4) - Docker를 활용한 무중단 배포 (0) | 2023.04.25 |
Jenkins를 활용한 CI/CD 구축(2/4) - publish over ssh (0) | 2023.02.15 |