왜 자동화를 적용하게 되었는가?
프로젝트의 코드가 완성되면 java 빌드, docker 이미지화, ec2환경에서의 이미지 pull, docker-compose up 하는 과정이 되게 번거로웠습니다. 그래서 자동화를 해서 완성되었을 때 버튼 하나로 빌드부터 배포까지 한번에 완료됐으면 했습니다. 자동화 툴이 여러가지 있었는데 이전 회사에서 사용이라도 해봐서 친근해보이는 젠킨스를 선택했습니다.
CI/CD가 무엇인가?
CI/CD는 지속적 통합(Continuous Integration) 및 지속적 제공/배포(Continuous Delivery/Deployment)를 의미하며, 소프트웨어 개발 라이프사이클을 간소화하고 가속화하는 것을 목표로 합니다.
지속적 통합(CI)은 코드 변경 사항을 공유 소스 코드 리포지토리에 자동으로 자주 통합하는 사례를 나타냅니다. 지속적 제공 및/또는 배포(CD)는 코드 변경 사항의 통합, 테스트, 제공을 나타내는 프로세스로, 두 가지 부분으로 구성됩니다. 지속적 제공에는 자동 프로덕션 배포 기능이 없는 반면, 지속적 배포는 업데이트를 프로덕션 환경에 자동으로 릴리스합니다.
위 정의를 기반으로 CI는 Git 리포지토리에 코드 변경 사항을 자주 통합하여 코드의 안정성과 품질을 유지하는 과정이며, CD는 Docker를 활용하여 JAR 파일 이미지를 빌드한 뒤, 이를 Docker Hub에 push하고 EC2에서 pull하여 docker-compose를 통해 애플리케이션을 배포하는 과정을 자동화하는 데 적합합니다.
젠킨스 설정
우선 저는 로컬 환경에서 젠킨스를 설치하여 진행했습니다. EC2는 프리 티어를 사용 중이었기 때문에 성능이 충분할지 판단하기 어려웠습니다. 반면, 로컬 환경은 용량이 충분하고 빌드 속도도 빠를 만큼 성능이 적합하다고 판단했습니다. 그리고 젠킨스를 설정하기에 빠르게 설정할 수 있는 환경이라고 생각했습니다.
macOS의 로컬 환경에서 젠킨스 설정
https://www.jenkins.io/download/lts/macos/
Sample commands:
- Install the latest LTS version: brew install jenkins-lts
- Start the Jenkins service: brew services start jenkins-lts
- Restart the Jenkins service: brew services restart jenkins-lts
- Update the Jenkins version: brew upgrade jenkins-lts
위의 젠킨스 사이트에서 제공하는 젠킨스 다운 방법와 시작방법을 사용했습니다. homebrew가 없다면 다운해야합니다.
설치 후, 젠킨스는 기본적으로 8080 포트를 사용합니다. 그런데 제 로컬 환경에서 이미 다른 애플리케이션이 8080 포트를 사용하고 있어 충돌이 발생하기에 이를 해결하기 위해 젠킨스 포트를 9090으로 변경하여 사용했습니다. 이 과정에서 Jenkins 설정 파일 homebrew.mxcl.jenkins-lts.plist를 수정해 포트를 변경했으며, 이후 문제 없이 빌드를 진행할 수 있었습니다.
// 다운로드 위치를 확인하기 위한 명령
brew --prefix jenkins-lts
// 포트 수정을 위한 명령
vim /opt/homebrew/opt/jenkins-lts/homebrew.mxcl.jenkins-lts.plist
그 다음 초기 비밀번호를 아래의 명령어로 받아서 설정한 젠킨스 홈페이지로 접속해서 로그인해줍니다. 저의 경우 http://localhost:9090/로 접속했습니다. 초기 플러그인 설정은 젠킨스가 추천하는 것을 사용했습니다.
cat $(brew --prefix jenkins-lts)/homebrew.mxcl.jenkins-lts/secrets/initialAdminPassword
환경 변수 설정
Jenkins관리 -> System안의 Global properties에서 key, value를 추가하시면됩니다. PATH+EXTRA는 꼭 추가하는게 좋습니다. Gradle Wrapper(gradlew) 실행이나 빌드 도구 및 의존성 관리등이 잘 작동할 수 있게 해줍니다.
key: PATH+EXTRA
value: /usr/local/bin
또한 저는 git main 브랜치를 기준으로 빌드를 돌리려고 했는데, 계속 작동이 안 되던 이유중에 하나가 github에 gradle/wrapper/gradle-wrapper.jar 파일이 없으면 빌드가 제대로 돌아가지 않으므로 꼭 추가해주셔야 합니다.
플러그인 설정
다운해야되는 플러그인들의 목록입니다. 만약에 검색이 안되면 plugin 글자를 제외해서 검색해보거나 이미 다운이 되었는지 확인해보세요.
Git plugin: Jenkins와 Git 리포지토리를 연동하는 데 필요합니다.
GitHub plugin: GitHub에 파일이 올라가면 자동으로 빌드할 수 있도록 도와주는 Webhook 설정에 유용한데, Webhook을 이용하기 위해서는 외부IP가 필요해서 로컬에서는 설정하기 번거롭기에 설정하지 않았습니다.
Docker Pipeline: Jenkins에서 Docker 컨테이너를 빌드, 실행, 관리할 수 있도록 지원합니다. Docker 기반 CI/CD 작업을 설정하는 데 필수입니다.
Docker plugin: Jenkins와 Docker 데몬 간 통신을 설정하며, Docker 이미지를 빌드하거나 배포 시 필요.
Gradle Plugin: Jenkins에서 Gradle 빌드 스크립트를 직접 실행할 수 있도록 지원합니다. Gradle을 사용하는 프로젝트라면 필수입니다.
Amazon EC2 plugin: Jenkins와 AWS EC2를 통합하여, 빌드 또는 배포 시 필요한 에이전트 노드를 동적으로 생성, 관리, 할당할 수 있도록 지원합니다. 이를 통해 CI/CD 파이프라인에 필요한 컴퓨팅 리소스를 효율적으로 사용할 수 있습니다.
ssh, 아이디 인증 설정
Jenkins관리 -> Credentials에서 Stores scoped to Jenkins안의 System에서 Global credentials에서 추가하면됩니다.
추가는 username with password나 ssh username with private key로 상황에 맞게 추가하면되는데 github나 dockerhub, ec2에서의 key설정에 대해서는 다루지 않겠습니다.
저의 경우는 위와같이 설정했습니다. Name쪽의 아이디는 가렸습니다.
자동화 빌드 만들기
이제 대망의 자동화 빌드 만들기 입니다!
젠킨스에서의 설정
- 새로운 Item으로 들어가서 Pipeline을 선택하고 제목은 해당 빌드에 설명을 대표하는 제목을 해주시면됩니다.
- Pipeline의 Definition을 Pipeline script from SCM으로 설정
- SCM는 Git으로 설정하고 git 레파지토리와 이전에 설정한 git Credentials로 설정
- Branches to build는 빌드할 브랜치를 적는 것 같은데 저는 main으로 정해서 */main로 해놨습니다.
- Script Path는 github에 있는 파일을 가져오는데 'Jenkinsfile'으로 제목을 하고 루트 디렉토리(gitignore위치)에 저장해놨습니다.
- 저장!
Github에서의 설정
루트 디렉토리에서 Jenkinsfile 파일을 만들고 아래와 같이 설정해줍니다.
pipeline {
agent any
environment {
EC2_IP = "${env.EC2_IP}" // Jenkins에 설정된 EC2 IP 환경변수
}
stages {
stage('Checkout Code') {
steps {
checkout([$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'https://github.com/chanheess/calendar.git',
credentialsId: 'github']]]) //Git 레파지토리와 credentials에 등록한 github id
}
}
stage('Build Application') {
steps {
sh './gradlew clean build' //gradle의 clean 빌드를 해줍니다.
}
}
stage('Build Docker Image') {
steps {
//dockerhub-image는 등록할 이미지 이름을 넣으시면됩니다.
//특정 플랫폼을 사용하기 위해서 linux/amd64로 push했습니다.
//플랫폼이 상관없으면 docker build -t dockerhub-image . --push 로 하시면됩니다.
sh 'docker buildx build --platform linux/amd64 -t dockerhub-image . --push'
}
}
stage('Deploy to EC2') {
steps {
sshagent(['ec2']) {
sh '''
ssh -o StrictHostKeyChecking=no ${EC2_IP} " //등록한 환경 변수 ec2 ip
docker pull dockerhub-image && //등록한 도커 이미지
docker-compose down &&
docker-compose up -d
"
'''
}
}
}
}
post {
always {
echo 'Build and Deployment Process Complete!'
}
failure {
echo 'Build or Deployment Failed. Check Logs.'
}
}
}
실행
마지막으로 젠킨스 대시보드에서 만들어진 빌드로 실행 버튼을 누르면 끝입니다! 만약에 빌드 실패한다면 단계별로 나눠서 빌드를 만들어서 테스트를 해보시는것을 추천드립니다.
마무리
이번 작업은 이해하고 사용하는 데 시간이 꽤 걸렸지만, 과정을 통해 많은 것을 배울 수 있었습니다. 특히, 필요에 의해 공부하다 보니 더 잘 기억에 남고 실제로 활용하는 데 큰 도움이 되었습니다.
위 내용에서 문제가 있거나 작동하지 않는 부분이 있으면, 댓글로 질문을 남겨주세요. 최대한 빠르게 답변드리겠습니다. 감사합니다!
참고 자료
https://www.redhat.com/ko/topics/devops/what-is-ci-cd
'Backend Programming' 카테고리의 다른 글
EC2를 활용한 HTTPS 및 도메인 설정 (3) | 2024.12.12 |
---|---|
이메일 인증을 위한 Redis 설정과 문제 해결 과정 (0) | 2024.12.07 |
라우팅과 라우팅 테이블의 이해 (0) | 2024.11.28 |
JPA 영속성 컨텍스트 (3) | 2024.11.01 |
빈은 어떻게 관리되어서 응답을 하는가? (1) | 2024.10.20 |