본 포스팅은 스스로 공부한 내용을 정리하고 기록하기 위하여 올리는 내용이며, 잘못된 내용이 있을 수도 있음을 미리 밝힙니다. 잘못된 내용이 있거나, 더 좋은 방법이 있다면 댓글로 남겨주시기 바랍니다.
MSA 아키텍쳐 서비스를 구축하는 과정에서 자동배포의 필요성을 느끼게 되었고, Jenkins를 통한 자동 배포 시스템을 구축하여 팀원들에게 제공하였습니다.
Notion 페이지를 이용하여 각자의 레포지토리를 등록하여 자동 배포 설정을 하는 방법을 공유하였습니다.
Github Actions , GitLab, 팀시티, 서클CI 등등 많은 CI/CD 툴이 존재하지만, Jenkins를 선택한 이유는 다음과 같습니다.
1. 다양한 플러그인
Jenkins는 수백 개 이상의 플러그인을 지원하며, 이를 통해 다양한 작업을 자동화할 수 있습니다. 소프트웨어 개발 프로젝트의 필요에 따라 원하는 플러그인을 선택하여 빌드 파이프라인을 유연하게 구성할 수 있습니다.
2. 커뮤니티
Jenkins는 활발한 개발자 커뮤니티를 보유하고 있어, 사용자들은 문제 해결과 지원을 받을 수 있으며, 계속해서 새로운 플러그인 및 기능이 추가되어 발전하고 있습니다.
복잡한 MSA 아키텍처 서비스를 구축하는 과정에서는 예상치 못한 복잡한 요구 사항이 계속해서 발생할 것으로 예상되어 다양성과 확장성을 제공하는 Jenkins를 선택하였습니다.
마지막에 완성된 파이프라인을 블루 오션 플러그인을 통해 확인한 사진입니다.
파이프라인의 주요 과정은 다음과 같습니다.
- github webhook
- git clone
- jenkins 에 등록한 application.properties 적용
- test
- build
- build Docker image
- Docker Hub 에 image 전송
- SSH 원격서버 접속후 , Docker pull & run
- Jenkins 를 설치하고 배포
- github app credential 추가
- github webhook 설정
- 필요한 플러그인 설치
- application.properties credentials에 추가
- docker hub credentials 추가
- SSH 배포서버 pem credentials 추가
- Groovy file 작성
- multiline pipeline default 아이템 생성
Jenkins를 설치하고 배포해야 합니다.
Docker 컨테이너를 사용하여 Jenkins 를 배포한다면, 서버가 변경되더라도 이미지를 생성하여 다른 서버에 적용하고 컨테이너를 구동할 수 있습니다.
이러한 유연성을 위해 Jenkins를 Docker 컨테이너로 실행합니다.
하지만 여기서 문제가 하나 있습니다.
Jenkins를 통하여 파이프라인에서 Docker 관련 명령을 실행하려면 도커 호스트(Daemon)에 해당 명령을 전달할수 있어야 합니다. 하지만 Docker 컨테이너로 실행한 Jenkins라면 컨테이너 내부에는 다른 환경이기 때문에 Docker가 설치되어있지 않습니다. 따라서 명령을 실행 할수 없습니다.
이러한 문제를 해결하기 위해선 DinD(Docker in Docker) 구조를 사용할 수 있도록 해야합니다.
그렇게 하기 위해선 Docker 컨테이너로 구동된 Jenkins 안에 Docker 를 설치한후, host의 docker 소켓을 컨테이너 docker 소켓과 마운트 해야합니다.
이러한 작업은 번거로우므로, docker의 옵션과 빌드된 이미지를 사용하였습니다.
docker run \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins \
getintodevops/jenkins-withdocker:lts
github credential 을 추가하는 방법에는 여러가지가 존재합니다. 저희 팀은 github organization 으로 MSA 프로젝트를 관리하기로 결정하였으므로, github app을 통한 credential 방식으로 결정하였습니다.
github app 을 생성하여 organization 에 install 한 다음, Jenkins Crediential 에 추가합니다.
참고 : https://fwani.tistory.com/23
팀원들이 각각 repository 의 webhoook을 설정하는 불편을 없애기 위해, github app 자체에서 webhook 을 관리하였습니다.
Payload URL 을 통해 webhook 을 보내줄 주소를 설정합니다.
http://{젠킨스배포url}:8080/multibranch-webhook-trigger/invoke?token={토큰(추후에사용)}
main branch 에 pull request 되는 것들을 자동배포하기 위해, pull request 를 체크해줍니다.
Jenkins 에서 필요한 플러그인들을 설치해줍니다.
Jenkins 관리 -> Plugins -> Available plugins
https://plugins.jenkins.io/multibranch-scan-webhook-trigger/
멀티브랜치 아이템이 깃헙 webhook을 받아 동작할 수 있게 해주는 도구
https://plugins.jenkins.io/pipeline-multibranch-defaults/
멀티브랜치 아이템이 리포지토리 내부 Jenkinsfile이 아닌 젠킨스 내부 스크립트로 빌드할 수 있게 해주는 도구
https://plugins.jenkins.io/multibranch-job-tear-down/
특정 브랜치가 삭제되었을 때 특정 동작을 수행할 수 있게 해주는 도구
https://plugins.jenkins.io/blueocean/
파이프라인 시각화 도구
https://plugins.jenkins.io/multibranch-build-strategy-extension/
특정 경로 이하 파일이 수정 됐을 때만 빌드 혹은 무시 설정을 위한 도구
환경 변수를 원격 서버에서 직접 설정할 수는 있지만, 이런 방식은 팀원들에게 번거로울 수 있고 중앙에서 관리하기 어려울 수 있다고 판단했습니다.
그래서 Jenkins Credentials에 환경 변수 파일을 추가하고, Git을 통해 프로젝트를 가져온 후 해당 파일을 프로젝트 안으로 복사하여 프로젝트를 빌드하는 방식을 사용했습니다.
Jenkins 관리 -> Credentials -> global -> Add credential -> Secret file -> application.properties 파일 업로드
Jenkins 관리 -> Credentials -> global -> Add credential -> Username with password -> ID 와 password 입력
Jenkins 관리 -> Credentials -> global -> Add credential -> SSH Username with private key -> pem key 입력
이상으로 등록된 credentials들은 다음과 같습니다.
설정한 credentials 들을 바탕으로 Groovy file을 작성합니다.
Jenkins 관리 -> Managed files -> Add a new config -> Groovy file
pipeline {
environment {
repository = "" //docker hub id와 repository 이름
DOCKERHUB_CREDENTIALS = credentials('dockerhub') // jenkins에 등록해 놓은 docker hub credentials 이름
dockerImage = ''
TARGET_HOST = ""
}
agent any
stages {
stage('github clone') {
steps {
echo 'github clone'
git branch: 'main', credentialsId: '', url: ''
}
}
stage('application.properties'){
steps{
withCredentials([file(credentialsId: '', variable: 'properties')]) {
script {
sh 'pwd'
sh 'ls'
sh 'cp $properties src/main/resources/application.properties'
sh 'ls'
}
}
}
}
stage('test'){
steps{
echo 'Testing...'
}
}
stage('build'){
steps{
echo 'Build...'
sh 'chmod +x ./gradlew'
sh './gradlew clean build -x test'
}
}
stage('build Docker image'){
steps{
echo 'Build Docker image...'
script{
dockerImage = docker.build repository + ":$BUILD_NUMBER"
}
}
}
stage('Login Docker'){
steps{
echo 'Login Docker...'
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin' // docker hub 로그인
}
}
stage('Deploy image to Docker Hub') {
steps {
script {
sh 'docker push $repository:$BUILD_NUMBER' //docker push
}
}
}
stage('Cleaning up') {
steps {
sh "docker rmi $repository:$BUILD_NUMBER" // docker image 제거
}
}
stage('Deploy to SSH'){
steps{
sshagent(['']) {
sh"""
ssh -o StrictHostKeyChecking=no ${TARGET_HOST} '
sudo docker stop user-service
sudo docker rm user-service
sudo pull ${repository}:${BUILD_NUMBER} //docker image pull
sudo docker run -d -p 8080:8080 --name user-service ${repository}:${BUILD_NUMBER} //docker image run
sudo docker image prune -f
'
"""
}
}
}
}
}
마지막으로 item 을 생성하여 자동배포를 설정합니다.
Item 을 multiline pipeline default 로 생성해줍니다. 이 아이템으로 생성해주어야 Jenkins 내부에서 설정된 Jenkinsfile(Groovy file) 을 통해 빌드를 수행할 수 있게 됩니다.
생성하면서 다음과 같은 설정들을 추가합니다.
Branch Source
Github : 미리 만들어두었던 github app credentials
Repository HTTPS URL : repository url 을 입력해줍니다.
Filter by name (with wildcards)
main 으로 pull request 되는 것만 배포 할 것이므로, main 만 입력하여 줍니다.
Build Strategy
Add 를 누르고 Cancel build excluded regions strategy를 눌러줍니다.
README.md를 입력하여줍니다. 앞으로 README.md 를 merge하는 과정에서는 자동 배포가 일어나지 않습니다.
Build Configuration
by default Jenkinsfile -> 등록한 groovy file 클릭, Run default Jenkinsfile within Groovy sandbox 체크
Scan Repository Triggers
Scan by webhook 체크, Trigger token 에 github repository webhook 토큰 입력
이상으로 Jenkins의 멀티 브랜치 파이프라인 자동 배포 시스템 구축을 완료하였습니다.
자동 배포에만 초점을 두었으므로, 테스트 과정은 포함하지 않았습니다.
다음은 다양한 Jenkins 플러그인을 활용하여 테스트 과정에도 주의를 기울일 예정입니다.
DinD 구현
https://blog.opendocs.co.kr/?p=704
https://kanoos-stu.tistory.com/53
jenkins dockerhub plugin
https://velog.io/@imsooyeon/Jenkins-pipeline%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%98%EC%97%AC-Docker-build-%EB%B0%8F-%EC%9D%B4%EB%AF%B8%EC%A7%80-push-%ED%95%98%EA%B8%B0
jenkins multibranch pipeline
https://creampuffy.tistory.com/202
github APP credential
https://fwani.tistory.com/23
SshAgent Docs
https://hub.docker.com/r/jenkins/ssh-agent