나의 경우, Git Submodule (private repo)
로 설정 정보를 관리하고 있었다. 따라서 Jenkins Pipeline에서 private repository에 접근하기 위해 Jenkins에 Public Access Token을 먼저 등록해야 한다. (Github에서 Public Access Token을 미리 생성해야 한다)
Jenkins 관리
-> 시스템 설정
-> Github
탭으로 이동한다.
Credentials
를 추가한다.
Username with Password
를 선택하고, 다음 정보들을 입력한다.
Jenkins에서 CI/CD 파이프라인은
Item
을 통해 구성할 수 있다. Item을 여러개 생성해 여러개의 파이프라인을 생성할 수도 있으며, 각각의 파이프라인은 각각 작성된 pipeline script를 통해 다르게 동작하게 할 수 있다.
가장 흔하게 사용되는 Pipeline
을 선택해 CI/CD 파이프라인을 구축하였다.
pipeline script에는 단순하게 내가 생성한 파이프라인이 어떤 동작을 할 지를 적어주면 된다.
pipeline {
agent any
tools {
gradle 'gradle'
}
stages {
stage('Git Clone') {
steps {
git branch: 'dev', url: 'https://github.com/sungmingt/fly-away'
}
}
stage('BE-Build') {
steps {
dir("./server/fly-away") {
sh "./gradlew clean build"
}
}
}
}
}
gradle 'gradle'
: gradle이라는 tool을 사용한다
하나의 pipeline
은 각각의 stage
들로 구성되며, 각각의 stage들은 steps
로 이루어져있다.
stage('Git Clone')
: 'Git Clone' 이라는 작업을 실행한다.
다음과 같은 흐름이다.
url
의 github 주소의dev branch
로부터 Git Clone을 실행한다../backend
디렉토리에서./gradlew clean build
를 실행한다.
나는 git submodule
을 사용하고 있었기 때문에, credentials를 활용해 private 레포지토리인 서브모듈에 접근하기 위해 추가 작업이 필요했다.
만일 git submodule을 사용한다면, script의 Git Clone Stage
를 다음과 같이 작성해줘야 한다.
stage('Git Clone') {
steps {
dir('/var/jenkins_home/workspace/flyaway'){
sh '''
echo delete existing project file
'''
deleteDir()
}
checkout scmGit(
branches: [[name: '*/dev']],
extensions: [
submodule(
parentCredentials: true,
reference: '',
trackingSubmodules: true
)
],
userRemoteConfigs:[
[
credentialsId: 'credentialsId'
url: 'https://github.com/sungmingt/fly-away'
]
]
)
}
}
dir('/var/jenkins_home/workspace/flyaway')
: 이전에 clone 했던 프로젝트 파일이 존재한다면 삭제하고 clone 받는다.
checkout scmGit()
: 이 부분은 pipeline script 작성란 아래에 Pipeline Syntax
를 통해 작성하면 script를 자동으로 만들어준다.
위에서 미리 생성해둔 credentialsId를 선택하고, submodule에 대한 설정을 추가적으로 해주면 된다.
Script를 작성하고 지금 빌드
를 클릭하면, 수동으로 파이프라인을 실행할 수 있다.
Git Clone - Gradle Build
작업을 실행하던 도중 다음과 같은 에러를 만났다.
에러 내용을 봐서는 Pipeline Script에 작성한 tools 부분의 gradle을 인식하지 못하는 것 같은데, 구글링을 해도 쉽게 원인을 찾을 수 없었다.
그렇게 삽질하던 도중 Global Tool Configuration
이라는 설정을 찾을 수 있었고, 여기에 Gradle 관련 설정이 있었다.
내 프로젝트의 Gradle 버전을 알맞게 입력 후,
'gradle'
이라는 이름을 통해 인식할 수 있도록 설정하는 것 같았다.
이후 빌드에 성공할 수 있었다.
이제 Jenkins에서 build한 jar 파일을 운영 서버 EC2로 전달해야 한다. 이때 운영 서버로의 SSH 접속을 위해 많이 사용되는 Plugin이 Publish Over SSH
와 SSH Agent
이다.
이제 EC2로의 SSH 접속을 위해 설정 - Credentials
로 이동해 접속 정보를 추가한다.
Kind
: private key를 통해 SSH 접속한다.ID
: syntax에서 참조할 수 있는 식별자이다. 나중에 pipeline script
에서 사용된다.Username
: 기본값인 ubuntu를 입력한다.Private Key
: 운영 서버(EC2) 접속에 필요한 pem키 파일
내용을 입력한다. 여기서는 -----BEGIN RSA PRIVATE KEY-----
부터 -----BEGIN RSA PRIVATE KEY-----
까지 모두 입력한다.cat {key 이름}.pem
----BEGIN RSA PRIVATE KEY-----
{pem 내용}
-----BEGIN RSA PRIVATE KEY-----
Credential이 aws_key
라는 이름으로 생성된 것을 확인할 수 있다.
stage('Deploy') {
steps {
sshagent(credentials: ['aws_key']) {
sh '''
ssh -o StrictHostKeyChecking=no ubuntu@{Private 또는 Public IP}
scp /var/jenkins_home/workspace/{프로젝트명}/backend/build/libs/{프로젝트명}-0.0.1-SNAPSHOT.jar ubuntu@{Private 또는 Public IP}:/home/ubuntu/{폴더명}
ssh -tt ubuntu@{Private IP} sh ./deploy.sh
'''
}
}
}
sshagent(credentials: ['aws_key'])
: 생성한 credential를 토대로 sshagent를 통해 운영 서버에 접속한다.
ssh -o StrictHostKeyChecking=no ubuntu@{Private 또는 Public IP}
: EC2 접속 시, Host Key Checking
을 생략하도록 한다.
터미널에서 ssh 접속시 Are you sure you want to continue connecting (yes/no)? 와 같은 메시지를 띄워 접속을 확인하는 것
scp ...
: Jenkins 프로젝트 내에 저장된 빌드 파일을 운영 서버로 전송시킨다. 프로젝트명은 docker를 사용하는 경우, 컨테이너 내에 접속해 /var/jenkins_home/workspace
에서 확인할 수 있다.
또한 폴더명은 운영 서버에서 jar파일을 위치시킬 폴더명이며, 당연히 미리 생성되어 있어야 한다.
ssh -tt ...
: 운영 서버로 접속해 운영 서버의 ./deploy.sh
파일을 실행시킨다. 해당 script 파일에는 운영 서버에 기존의 spring boot 서버를 kill 시키고, 새로 전달된 jar 파일을 실행시키는 명령어가 포함되어 있다.
pid=$(pgrep -f jenkins | pgrep java)
if [ -n "${pid}" ]
then
sudo kill -9 ${pid}
echo kill process ${pid}
sleep 10
else
echo no process
fi
echo "Deployment Start..."
JAR_PATH=$(ls -t /home/ubuntu/flyaway/*.jar | head -1)
sudo chmod +x ${JAR_PATH}
sudo nohup java -jar -DSpring.profiles.active=prod ${JAR_PATH} >> /home/ubuntu/flyaway/application.log &
sleep 10
echo "Done!!"
prod 환경
(profile)으로 실행한다.pipeline {
agent any
tools {
gradle 'gradle'
}
stages {
stage('Git Clone') {
steps {
dir('/var/jenkins_home/workspace/flyaway'){
sh '''
echo delete existing project file
'''
deleteDir()
}
checkout scmGit(
branches: [[name: '*/dev']],
extensions: [
submodule(
parentCredentials: true,
reference: '',
trackingSubmodules: true
)
],
userRemoteConfigs:[
[
credentialsId: 'credentialsId',
url: 'https://github.com/sungmingt/fly-away'
]
]
)
}
}
stage('BE-Build') {
steps {
dir("./server/fly-away") {
sh "chmod 777 gradlew"
sh "./gradlew clean build"
}
}
}
stage('Deploy') {
steps {
sshagent(credentials: ['aws_key']) {
sh '''
ssh -o StrictHostKeyChecking=no ubuntu@{Private 또는 Public IP}
scp /var/jenkins_home/workspace/{프로젝트명}/backend/build/libs/{프로젝트명}-0.0.1-SNAPSHOT.jar ubuntu@{Private 또는 Public IP}:/home/ubuntu/{폴더명}
ssh -tt ubuntu@{Private IP} sh ./deploy.sh
'''
}
}
}
}
}
처음 Deploy 단계 실행 시 pipeline script에 ssh -t
옵션을 주었고, 위 에러를 만났다. 이런 경우 ssh -tt
또는 ssh -t -t
옵션을 추가로 주면 된다.
지금까지는 Jenkins GUI에서 지금 빌드
를 통해 파이프라인을 실행해왔지만(수동 배포), 이제 Github에 push를 trigger로 파이프라인이 실행(CD)되도록 하자
생성한 파이프라인 -> 구성
에서Github hook trigger for GITScm polling
을 활성화시킨다. Github으로부터 webhook이 들어오면 자동으로 파이프라인이 실행된다.
Repository -> Settings -> Webhooks
으로 이동하여 Add webhook으로 webhook을 생성한다.
Payload URL
: https://{Jenkins 접속 주소}/github-webhook/
를 입력한다.
Content type
: application/json
을 선택한다.
다음으로는 어떤 event를 통해 webhook을 실행할 것인지를 선택할 수 있으며, push를 통해 실행되도록 하였다.
이제 dev branch로의 push가 일어나면, 자동으로 파이프라인이 실행되어야 한다.
Github의 dev branch로의 push를 trigger로 파이프라인이 정상적으로 실행된다!