회사에서 스프링부트로 개발한 웹 애플리케이션의 수동배포를 Jenkins를 도입하여 자동으로 빌드와 배포가 가능하도록 변경한 내용입니다.
젠킨스를 설치하기 전에 컨테이너 관리 플랫폼인 docker를 우선적으로 설치한 후에 젠킨스 컨테이너를 설치한다.
dnf install -y dnf-utils
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf install -y docker-ce --allowerasing
docker --version
systemctl status docker
systemctl enable docker
systemctl start docker
참고링크: Rocky Linux 8에 docker 설치하기
도커 컨테이너들을 GUI환경에서 관리하기 위해 Portainer 컨테이너를 설치한다.
portainer/portainer 로 할 경우 예전 버전을 내려받게 되어 있어서 뒤에 꼭 ce를 붙여주는게 좋다.
docker pull portainer/portainer-ce
docker volume create portainer_data
docker run -d -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
브라우저에서 서버아이피(ex 10.22.33.44)에 포트 9000을 붙여 접속한다
http://10.22.33.44:9000
처음 접근시 어드민 계정 생성을 위해 ID/PW를 입력해줘야 한다.
ID/PW는 기억해주자.
접근한 Portainer 에서 image 페이지에서 jenkins/jenkins 입력후 pull the image 클릭 (특정 버전 기입이 없으면 자동으로 latest 버전으로 받아진다.)
Volumes 페이지로 이동하여 Add Volume을 누른 후 이름을 jenkins_volume으로 입력 후 Create the volume을 클릭한다
Container 페이지로 이동하여 Add Container 클릭
Name에 jenkins, Image에 jenkins/jenkins:latest, 그리고 map additional port를 눌러 host 7602 container 8080(젠킨스는 무조건 8080으로 받음.필수) 입력
컨테이너를 만들기 전 옵션 설정을 위해 화면 맨 하단 탭 중에 volumes 탭을 눌러 볼륨 매핑을 해준다. map additional volume을 눌러 매핑할 항목을 총 2개로 만들어주고, 아래 이미지처럼 작성한다.
container : /var/jenkins_home
volume : jenkins_volume
container(bind) : /var/run/docker.sock
host : /var/run/docker.sock
Env 탭으로 이동하여 타임존 환경변수 설정을 한다. add an environment variable 을 클릭하고 name TZ , value Asia/Seoul 입력
Restart Policy 탭으로 이동해서 unless stopped 선택
상단에 Deploy the container 를 클릭하여 컨테이너 생성
브라우저에서 서버아이피(ex 10.22.33.44)에 포트 7602를 붙여 접속한다
http://10.22.33.44:7602
젠킨스 생성 시 처음 발급받은 패스워드 키가 별도로 생성되므로 서버에서 하단 경로로 이동하여 vi편집기를 통해 생성된 패스워드를 확인하여 붙여넣는다.
패스워드 성공 후 어드민 계정까지 생성해 준다.
참고링크: Docker를 WebUI로 관리하기 - Jenkins 설치하기
스크립트 진행에 필요한 플러그인들을 설치한다.
Jenkins 관리 > Plugins 로 이동하여 Available plugins에서 아래 항목을 검색하여 설치 진행
우측 계정 정보에서 설정으로 이동
어플리케이션 페이지에서 신규 엑세스 토큰 발급. 발급 후 생성된 액세스토큰은 따로 기록해 놓기
젠킨스 관리 > Credentials > System > Global credentials 에서 Add Credentials 클릭
Gitea Personal Access Token 을 선택하여 액세스 토큰 기입 및 id 와 설명란 입력
젠킨스 관리 > System 으로 이동하여 Gitea Servers 항목에 환경 정보 저장
젠킨스 관리 > System 으로 이동하여 Publish over SSH 항목에 환경 정보 저장
이 때 개발/운영서버의 정보가 여러개일 경우, 여러개 다 저장해둔다. (개발계 서버, 운영계 서버 등)
스프링부트 프로젝트 gradle SDK 버전에 맞춰서 미리 빌드 설정을 잡아 준다.
Jenkins 관리 > Managed files로 이동하여 Add a new Config 선택
이름에 프로젝트 명을 넣고 임시 스크립트를 집어넣는다.
pipeline {
agent any
tools {
gradle 'Gradle-8.2.1'
}
stages {
stage('Clone Repository') {
steps {
script {
if (env.BRANCH_NAME == 'dev') {
git url: 'http://gitip:3000/appstore/appstore_bos.git',
branch: 'dev',
credentialsId: 'Jenkins Access Token'
} else if (env.BRANCH_NAME == 'main') {
git url: 'http://gitip:3000/appstore/appstore_bos.git',
branch: 'main',
credentialsId: 'Jenkins Access Token'
} else {
error("Unsupported branch: ${env.BRANCH_NAME}")
}
}
}
}
stage('Build with Gradle') {
steps {
script {
sh "gradle clean bootJar"
}
}
}
}
post {
success {
echo 'Build and completed '
}
failure {
echo 'Build failed. Please check the logs.'
}
}
}
이후 summit을 누른다
젠킨스 메인화면에서 new Item을 눌러 멀티브랜치 파이프라인을 생성해준다.
Owner에는 gitea에 있는 조직명(혹은 사용자명)을 입력하면 알아서 해당 조직에 속한 레포지터리 리스트를 콤보박스로 보여준다. 혹시 콤보박스 항목이 나오지 않는 경우, 조직명을 다시 확인해봐야 한다.
gitea에서 보면 조직명/레포지터리로 구성되어 있다.
ex) appstore/appstore_bos
Discover branches 항목을 All branches로 선택 후 나머지 항목들은 삭제 한다. 이후 add 를 눌러 Filter by name(with regular expression) 선택
Regular expression 에 파이프라인 스크립트를 수행하고 싶은 브랜치만 기입. ex) dev|main
jenkinsfile 선택란에는 아까 생성한 Pipeline Script Id를 입력해준다.
gitea에서 사용할 webhook을 등록해준다. 이때 물음표를 눌러보면 사용할 수 있는 url이 나온다.
ex) 젠킨스서버ip:7602/multibranch-webhook-trigger/invoke?token=sample-test
gitea 로 이동하여 프로젝트 > 설정 > 웹훅으로 이동 후 우측 상단 Webhook 추가 클릭
아마 첫 구동시에는 파이프라인 스크립트 때문에 실패가 떨어질 것이다.
젠킨스 관리 > In-process Script Approval로 이동
groovy script 를 Approve 해줘야 한다.
(스크립트가 변경될 때마다 계속 Approve를 해줘야한다. 따로 groovy 허용 없이 하는 방법이 있다고는 하는데 나중에 찾아봐야 함.)
dev 목록의 맨 우측의 플레이버튼 클릭
이후 해당 빌드 넘버를 클릭하여 Pipeline Overview 항목을 보면 파이프라인 스크립트가 정상 수행되었음 확인이 가능하다.
젠킨스 관리 > Manage files로 이동 후 스크립트 Edit 진행.
git url : 깃 주소
git branch : 클론받을 브랜치 정보 ex) dev, main
git credentialsId : jenkins에 정의해놓은 gitea 액세스 토큰 아이디
jarFile : gradle build 후 jar 파일 경로/정보 (보통 build/libs 폴더에 위치하고 파일명은 프로젝트 명)
sshPublisher sshPublisherDesc configName : jenkins에 정의해놓은 ssh 통신 id
sshPublisher sshTransfer remoteDirectory : sftp 로 파일을 저장할 서버 경로
sshPublisher sshTransfer execCommand : 파일 전송 완료 후 실행시킬 커맨드 (서버에 미리 shell script 구현 후 해당 쉘스크립트를 호출)
일반적으로 스프링부트 jar 파일 실행 스크립트를 구현해놓는다.
nohup java -jar 파일경로 환경변수 &
pipeline {
agent any
tools {
gradle 'Gradle-8.2.1'
}
stages {
stage('Clone Repository') {
steps {
script {
if (env.BRANCH_NAME == 'dev') {
git url: 'http://gitip:3000/appstore/appstore_bos.git',
branch: 'dev',
credentialsId: 'Jenkins Access Token'
} else if (env.BRANCH_NAME == 'main') {
git url: 'http://gitip:3000/appstore/appstore_bos.git',
branch: 'main',
credentialsId: 'Jenkins Access Token'
} else {
error("Unsupported branch: ${env.BRANCH_NAME}")
}
}
}
}
stage('Build with Gradle') {
steps {
script {
sh "gradle clean bootJar"
}
}
}
stage('Transfer JAR to Remote Server and Reboot Program') {
steps {
script {
def jarFile = sh(script: "find build/libs -name 'appstore_bos.jar'", returnStdout: true).trim()
if (jarFile) {
if (env.BRANCH_NAME == 'dev') {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'root@DEVWEBSERVER',
transfers: [
sshTransfer(
sourceFiles: jarFile,
remoteDirectory: "/usr/local/appstore",
removePrefix: "build/libs",
execCommand: """
sh /usr/local/appstore/runstore_bos.sh
"""
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
} else if (env.BRANCH_NAME == 'main') {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'root@PRODWEBSERVER',
transfers: [
sshTransfer(
sourceFiles: jarFile,
remoteDirectory: "/usr/local/appstore",
removePrefix: "build/libs",
execCommand: """
sh /usr/local/appstore/runstore_bos.sh
"""
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
} else {
error("Unsupported branch: ${env.BRANCH_NAME}")
}
} else {
error("JAR file not found")
}
}
}
}
}
post {
success {
echo 'Build and deployment completed successfully.'
}
failure {
echo 'Build failed. Please check the logs.'
}
}
}
파이프라인 스크립트는 브랜치 전략 혹은 빌드/배포 정책에 맞게 수정하여 사용한다. 위 내용은 브랜치 별로 각각의 서버에 빌드 파일 전송 후 각 서버에 저장한 shell script를 통해 애플리케이션을 배포하는 방식으로 진행하였다.
git push를 통해 최종적으로 빌드가 정상적으로 동작하는지 확인한다.