지난 포스팅에서 Jenkins를 K8s 환경에 설치 완료
Jenkins를 활용하여 파이프라인을 구축하기 위함
배포할 소스 정보
Java 11 (Spring boot 2.7.14)
Gradle 8.2.1
간단한 게시판 구현
본격적으로 파이프라인을 구성하기 전 먼저 어떤 순서로 진행할 지 흐름도를 그려보겠습니다.
제가 구성할 파이프라인은 크게 네 단계로 진행됩니다.
배포할 source를 가지고 있는 Gitlab에서 source를 가져오는 과정입니다.
해당 프로젝트에서 branch 전략은 별도로 적용하진 않았습니다. (main branch로 소스만 관리)
가져온 source를 가지고 source build를 진행합니다. build 과정에서 Gradle을 사용합니다.
Source Build로 생성 된 jar파일을 포함한 Image를 build합니다. Dockerfile은 같은 project 안에 위치하고 Base Image는 openjdk를 설치한 alpine linux custom image입니다.
Image Build로 생성된 Image를 K8s Cluster에 배포하는 과정입니다. 배포는 ArgoCD를 통해 배포할 계획입니다.
위에서 구성한 내용을 바탕으로 Jenkins 파이프라인을 만들어보겠습니다.
우선 Jenkins 홈에서 + 새로운 Item을 눌러 파이프라인을 생성합니다.
이름을 정하고 Pipeline을 눌러 생성합니다.
파이프라인을 생성하고 구성에 들어갑니다.
Image Build 시 적용할 Tag값을 매개변수로 미리 설정하여 설정한 값이 tag로 적용되게 구현했습니다.
만약 빌드 시간을 Tag값으로 적용하고 싶으면 매개변수 설정을 다음과 같이 합니다. 자동으로 현재 시간이 매개변수로 설정됩니다.
파이프라인을 Main과 Sub로 나눈 이유
ArgoCD를 통해 배포하기 위해서입니다.
ArgoCD는 Git과 연동하여 Git project에 있는 yaml을 가지고 클러스터에 자동으로 배포를 합니다. (CD)
Main에서는 소스코드가 들어있는 project를 가져와 소스빌드, 이미지 빌드를 진행합니다.
Sub에서는 배포를 위한 yaml이 들어있는 project를 가져와 배포를 위한 작업을 진행합니다.
먼저 Main Pipeline script를 확인해보겠습니다.
pipeline {
agent any
environment {
HARBOR_ID = credentials('harborId')
HARBOR_PW = credentials('harborPassword')
}
stages {
stage('Git Config Setting') {
steps {
sh 'git config --global http.sslVerify false'
}
}
stage('Git Clone') {
steps {
git branch: 'main', credentialsId: 'gitlab-id', url: 'https://gitlab.test.ehddhks.com/root/board.git'
}
}
stage('Gradle Build') {
steps {
sh '''
chmod 755 gradlew
./gradlew clean bootjar
mv build/libs/*.jar app.jar
ls -al
'''
}
}
stage('Build Image') {
steps {
sh '''
docker build -t harbor.test.ehddhks.com/board/board:"${VERSION}" .
docker login harbor.test.ehddhks.com -u $HARBOR_ID -p $HARBOR_PW
docker push harbor.test.ehddhks.com/board/board:"${VERSION}"
'''
}
}
stage('Invoke Argo Pipeline') {
steps {
build job: 'board-argo-deploy', parameters: [
string(name: 'VERSION', value: "${params.VERSION}")
]
}
}
}
}
script를 위에서부터 하나씩 확인해 보면
environment {
HARBOR_ID = credentials('harborId')
HARBOR_PW = credentials('harborPassword')
}
이 부분은 docker login을 위해 jenkins credential로 설정한 harbor 계정 정보를 받아오는 부분입니다.
HARBOR_ID와 HARBOR_PW로 환경변수를 선언하고 credentials에 등록한 ID를 통해 값을 불러옵니다.
사설 harbor에 image push를 하기 위해서는 harbor login이 선행되어야 하기 때문에 해당 부분이 필요합니다.
credential 생성 방법
jenkins 홈에서 jenkins 관리에 들어갑니다.
여기서 credentials 항목에 들어갑니다.
global을 클릭하고
Add Credentials를 클릭합니다.
Kind를 Secret Text로 변경하고 ID를 harborId Secret에 harbor 로그인용 id 값을 넣어줍니다.
harbrPassword도 동일하게 만들어줍니다.
stage('Git Config Setting') {
steps {
sh 'git config --global http.sslVerify false'
}
}
stage('Git Clone') {
steps {
git branch: 'main', credentialsId: 'gitlab-id', url: 'https://gitlab.test.ehddhks.com/root/board.git'
}
}
Git Config Setting stage에서는 git config 관련된 부분을 수행합니다. 여기서는 사설 Gitlab을 사용하다 보니 공인인증서가 적용되지 않아 sslVerify를 false로 설정해야 에러가 발생하지 않습니다.
이후 Git clone stage에서 source를 가져옵니다.
credentilasId 부분에서는 private project에서 source를 가져오기 위해 gitlab 로그인 정보를 넣어둔 credential을 사용합니다.
Git 로그인용 credential 생성 방법
생성 방법은 위와 동일한데 약간의 차이가 있습니다.
Kind를 Username with password로 설정하고 Username에는 Gitlab ID를 Password에는 password를 설정합니다.
stage('Gradle Build') {
steps {
sh '''
chmod 755 gradlew
./gradlew clean bootjar
mv build/libs/*.jar app.jar
ls -al
'''
}
}
source build를 진행하는 부분입니다. gradlew를 통해 bootjar 옵션으로 jar파일을 생성합니다.
stage('Build Image') {
steps {
sh '''
docker build -t harbor.test.ehddhks.com/board/board:"${VERSION}" .
docker login harbor.test.ehddhks.com -u $HARBOR_ID -p $HARBOR_PW
docker push harbor.test.ehddhks.com/board/board:"${VERSION}"
'''
}
}
Dockerfile을 사용하여 image build를 수행하는 부분입니다.
여기서 처음에 설정한 매개변수 VERSION이 build될 image의 tag 값으로 들어갑니다.
그리고 2.1 environment에서 설정한 HARBOR_ID와 HARBOR_PW도 사용됩니다.
주의할 점은 jenkins에서 docker socket파일의 위치를 설정해줘야합니다.
Docker socket 위치 설정 방법
jenkins 관리에 들어가서 System 항목에 들어갑니다.
Docker Builder 항목을 찾아 URL 부분에
unix:///var/run/docker.sock
docker.sock 위치를 설정합니다.
Test Connection 버튼을 눌러 연결을 확인합니다.
이 과정을 위해 jenkins를 설치할 때 docker를 sidecar로 올리고 해당 컨테이너의 /var/run directory를 공유 볼륨으로 잡아 mount 시켰던것입니다.
stage('Invoke Argo Pipeline') {
steps {
build job: 'board-argo-deploy', parameters: [
string(name: 'VERSION', value: "${params.VERSION}")
]
}
}
board-argo-deploy라는 파이프라인을 실행시키는 부분입니다. 이때 parameter로 매개변수로 설정한 VERSION 값을 넘겨줍니다.
여기서도 매개변수 설정을 해줍니다. 변수명은 VERSION으로 동일하게 합니다.
전체 script를 먼저 보겠습니다.
pipeline {
agent any
stages {
stage('Git Config Setting') {
steps {
sh '''
git config --global http.sslVerify false
git config --global user.name "Administrator"
git config --global user.email "admin@example.com"
git config --global credential.helper store
'''
}
}
stage('Argo Git Clone') {
steps {
git branch: 'main', credentialsId: 'gitlab-id', url: 'https://gitlab.test.ehddhks.com/root/board-deploy.git'
sh '''
rm -rf board-app-deployment.yaml
sed "s/VERSIONTAG/"${VERSION}"/g" "board-app-deployment-template.yaml" > board-app-deployment.yaml
ls -al
git add --all
git commit -m 'update image tag'
git push origin main
'''
}
}
}
}
여기서는 Git에서 소스를 가져와 수정 후 commit하고 push하는 작업을 진행합니다.
Git Config Setting에서 Main에서와 다르게
git config --global user.name "Administrator"
git config --global user.email "admin@example.com"
git config --global credential.helper store
이 config들이 추가되었습니다.
그 이유는 commit과정과 push과정에서 user의 정보와 로그인이 필요하기 때문입니다.
credential.helper store부분은 source를 가져올 당시 로그인 정보를 저장해 두는 설정입니다.
Argo Git Clone stage에서 가져오는 project의 구성을 보겠습니다.
총 네 개의 yaml로 구성되어 있습니다.
여기서 살펴 볼 yaml은 board-app-deployment-template.yaml과 board-app-deployment.yaml입니다.
## board-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: board
name: board
namespace: board-deploy
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: board
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: board
spec:
containers:
- image: harbor.test.ehddhks.com/board/board:v1.0.9
imagePullPolicy: IfNotPresent
name: board
ports:
- containerPort: 8080
protocol: TCP
resources: {}
securityContext:
runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
board-app-deployment.yaml은 현재 클러스터에 배포되어 있는 yaml입니다.
이 yaml을 먼저 지우고
## board-app-deployment-template.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: board
name: board
namespace: board-deploy
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: board
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: board
spec:
containers:
- image: hyperregistry.hypercloud.ehddhks.com/board/board:VERSIONTAG
imagePullPolicy: IfNotPresent
name: board
ports:
- containerPort: 8080
protocol: TCP
resources: {}
securityContext:
runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
template yaml에서 VERSIONTAG로 작성된 부분을 sed 명령어로 VERSION 값으로 변경합니다. 그리고 변경된 yaml을 board-app-deployment.yaml로 생성합니다.
이후 git commit과 push과정을 거쳐 해당 내용을 업데이트합니다.
ArgoCD에서 application에 Auto Sync를 설정하면 연동 된 git repository에서 source 변화가 발생했을 때 자동으로 Sync를 맞춰 Cluster에 바뀐 내용을 반영해줍니다.
전체적인 흐름은 빌드 후 바뀐 tag값으로 이미지가 harbor에 올라가고 이후 argocd와 연동 된 git repository에 변경 된 tag값을 반영해 주면 자동으로 이미지 버전이 바뀌어 클러스터에 배포되는 과정입니다.
Tekton과 비교했을 때 매우 간편하고 관리도 쉬운 장점이 있었지만 확장성에서는 Tekton에 비해 아쉬운점이 있었습니다. Jenkins를 설치할 때는 가급적이면 K8s위에 설치하기보다는 Jenkins 서버를 따로 두는 것이 더 효율적일것 같습니다.