
안녕하세요? 지난 시간에는 슬랙 알림 메세지를 보내기 위한 토큰발급, 젠킨스 플러그인 설치 및 설정에 대하여 알아보았습니다.
만약 아직 플러그인 설정이 완료되지 않으신 분들은 Jenkins Pipeline Script로 배포 결과 슬랙 알림 메세지로 보내기(1) - 토큰발급, 플러그인 설치 및 설정 을 참고해주시기 바랍니다.
자, 그럼 이번에는 슬랙 알림 메세지를 보내기 위한 파이프라인 스크립트를 작성하고, 실제로 배포 결과를 슬랙 알림 메세지로 받아보겠습니다.
Jenkins Pipeline Script로 슬랙 메세지 보내보기
파이프라인 스크립트를 작성하기에 앞서 젠킨스 파이프라인은 Scripted와 Declarative 두가지 문법을 지원하는데, 이 글에서는 Declarative Script 문법을 이용하여 작성하겠습니다.
Declarative Script 문법을 아예 모르시거나, 두가지 문법의 차이점이 궁금하시다면 다른분들이 잘 정리해두신 아티클이 많으니 잠깐 보고오시는 것을 추천드립니다.
배포 파이프라인 스크립트를 작성하기 위하여 젠킨스에서 새로운 Item을 클릭하여 Item을 만들어줍니다.
item 이름은 원하는 이름을 넣어주시고, pipeline을 선택하여 OK를 눌러줍니다.
그러면 아래와 같이 설정 화면이 나오는데, 사진에 보이는 스크립트 부분에 파이프라인 스크립트를 작성해주면 됩니다.
간단하게, 슬랙 메세지를 보내는 스크립트를 작성해보도록 하겠습니다.
pipeline {
agent any
environment {
SLACK_CHANNEL = "{슬랙 채널 이름}"
SLACK_SUCCESS_COLOR = "#2C953C"
SLACK_FAIL_COLOR = "#FF3232"
}
stages {
stage('start') {
steps {
script {
slackSend(
channel: SLACK_CHANNEL
color: SLACK_SUCCESS_COLOR,
message: ":information_source: hello"
)
}
}
}
}
}
`pipeline 블록` : 전체 스크립트의 최상위 블록입니다. 모든 스크립트 블록은 이 안에 작성되어야 합니다.
`agent` : any, none, label, node, docker, dockerfile, kubernetes 를 파라미터로 포함할 수 있습니다. 위와 같이 any로 지정할 경우 사용 가능한 어떠한 agent로도 실행해도 된다는 것을 뜻합니다.
`environment 블록` : stage에서 사용할 변수를 정의하는 블록입니다.
`stages 블록` : 각각의 stage에 대하여 정의하는 블록입니다. 이 블록 하위에 정의한 stage는 순서대로 실행됩니다.
`stage 블록` : 스크립트 실행의 단위라고 생각하시면 편합니다. 각각의 스테이지가 모두 무사히 끝나면, 스크립트가 정상적으로 실행된 것입니다.
`script 블록` : stage에서 실행될 스크립트를 작성하는 블록입니다.
우리는 이미 앞서 슬랙 플러그인을 설치해두었기에, slackSend() 함수를 이용하여 슬랙 채널에 메세지를 보낼 수 있습니다.
위 스크립트의 경우 slackSend() 함수에 파라미터로 channel, color, message만 넣어주었습니다.
추가적인 기능이 궁금하다면 젠킨스 슬랙 알림 플러그인 공식문서 를 참고해주세요.
위 스크립트를 붙여넣기한 뒤 저장까지 하셨다면, 왼쪽 사이드바에서 `지금 빌드`를 클릭해줍니다.
빌드에 성공했다면, 지정한 채널에 아래와 같이 메세지가 온 것을 확인할 수 있습니다!
배포 알림 스크립트 작성해보기
자, 이제 slackSend() 함수를 통해 정상적으로 슬랙 메세지가 전송된다는 것을 확인하였으니, 배포 스크립트를 작성하고, stage 별로 알림을 전송해보겠습니다.
1편에서도 소개하였지만, 배포 flow는 다음과 같습니다.
1. 배포 EC2 인스턴스에 ssh 접속하여, Git Pull 하여 코드를 받아옵니다.
2. 배포 EC2 인스턴스에 ssh 접속하여, Test를 수행합니다.
3. 배포 EC2 인스턴스에 ssh 접속하여, Build를 수행합니다.
4. 배포 EC2 인스턴스에 ssh 접속하여, 도커 이미지화, 실행, 기존 도커 이미지 다운과 같은 Deploy 작업을 수행합니다.
위 배포 과정 1,2,3,4 를 Git Pull, Test, Build, Deploy라는 stage로 정의하여, 아래처럼 스크립트를 작성해줍니다.
pipeline {
agent any
options {
timestamps() // 타임스탬프 플러그인 활성화
}
environment {
REMOTE_HOST = '{배포 EC2 인스턴스 호스트}'
REMOTE_DIR = '{소스코드 디렉토리}'
DEPLOY_SCRIPT = '{배포 파이썬 스크립트}'
CREDENTIAL_ID = '{SSH KEY}' // 젠킨스 관리 > Credentials에 지정한 값
// 슬랙 메세지 전송 관련 변수
SLACK_CHANNEL = "{슬랙 메세지를 전송할 채널 이름}"
SLACK_SUCCESS_COLOR = "#2C953C"
SLACK_FAIL_COLOR = "#FF3232"
SLACK_MESSAGE_UNIT = "=================================================================="
SLACK_DURATION_TIME_MESSAGE = ""
SLACK_MESSAGE_BUILDER = ""
}
stages {
stage('Start') {
steps {
script {
SLACK_MESSAGE_BUILDER =
"${SLACK_MESSAGE_UNIT}" + "\n" +
"@here \n" +
":pencil: `${env.JOB_NAME}` 배포 파이프라인 실행 결과 리포트입니다. (<${env.BUILD_URL}|${currentBuild.displayName}>) \n"
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_SUCCESS_COLOR,
message: ":information_source: @here `${env.JOB_NAME}` 배포 파이프라인이 시작되었습니다. (<${env.BUILD_URL}|${currentBuild.displayName}>)"
)
}
}
}
stage('Git Pull') {
steps {
script {
long startTime = new Date().getTime()
sshSudoCommand("""
cd ${REMOTE_DIR} && \
git restore ${REMOTE_DIR}/gradlew && \
git restore ${REMOTE_DIR}/deploy/deploy.py
""")
sshCommand("git -C ${REMOTE_DIR} pull")
sshSudoCommand("chmod +x ${REMOTE_DIR}/gradlew")
sshSudoCommand("chmod +x ${REMOTE_DIR}/deploy/deploy.py")
long endTime = new Date().getTime()
SLACK_DURATION_TIME_MESSAGE = getStageDurationMessage(startTime, endTime)
SLACK_MESSAGE_BUILDER += ":white_check_mark: Git Pull 성공! (${SLACK_DURATION_TIME_MESSAGE}) \n"
}
}
post {
failure {
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_FAIL_COLOR,
message: "${SLACK_MESSAGE_BUILDER}" + stageFailSlackMessage("Git Pull") + "${SLACK_MESSAGE_UNIT}"
)
}
}
}
stage('Test') {
steps {
script {
long startTime = new Date().getTime()
sshSudoPythonCommand("${DEPLOY_SCRIPT} test")
long endTime = new Date().getTime()
SLACK_DURATION_TIME_MESSAGE = getStageDurationMessage(startTime, endTime)
SLACK_MESSAGE_BUILDER += ":white_check_mark: Test 성공! (${SLACK_DURATION_TIME_MESSAGE}) \n"
}
}
post {
failure {
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_FAIL_COLOR,
message: "${SLACK_MESSAGE_BUILDER}" + stageFailSlackMessage("Test") + "${SLACK_MESSAGE_UNIT}"
)
}
}
}
stage('Build') {
steps {
script{
long startTime = new Date().getTime()
sshSudoPythonCommand("${DEPLOY_SCRIPT} build")
long endTime = new Date().getTime()
SLACK_DURATION_TIME_MESSAGE = getStageDurationMessage(startTime, endTime)
SLACK_MESSAGE_BUILDER += ":white_check_mark: Build 성공! (${SLACK_DURATION_TIME_MESSAGE}) \n"
}
}
post {
failure {
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_FAIL_COLOR,
message: "${SLACK_MESSAGE_BUILDER}" + stageFailSlackMessage("Build") + "${SLACK_MESSAGE_UNIT}"
)
}
}
}
stage('Deploy') {
steps {
script{
long startTime = new Date().getTime()
sshSudoPythonCommand("${DEPLOY_SCRIPT} deploy")
long endTime = new Date().getTime()
SLACK_DURATION_TIME_MESSAGE = getStageDurationMessage(startTime, endTime)
SLACK_MESSAGE_BUILDER += ":white_check_mark: Deploy 성공 (${SLACK_DURATION_TIME_MESSAGE}) \n"
}
}
post {
failure {
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_FAIL_COLOR,
message: "${SLACK_MESSAGE_BUILDER}" + stageFailSlackMessage("Deploy") + "${SLACK_MESSAGE_UNIT}"
)
}
}
}
stage('End') {
steps {
script {
SLACK_MESSAGE_BUILDER += ":tada: `${env.JOB_NAME}` 배포 파이프라인이 성공적으로 완료되었습니다. :beer:\n" + "${env.SLACK_MESSAGE_UNIT}"
slackSend (
channel: SLACK_CHANNEL,
color: SLACK_SUCCESS_COLOR,
message: SLACK_MESSAGE_BUILDER
)
}
}
}
}
}
// ssh 접근 이후 명령어를 실행하는 함수
def sshCommand(cmd) {
sshagent(credentials: [CREDENTIAL_ID]) {
sh "ssh -o StrictHostKeyChecking=no ${REMOTE_HOST} '${cmd}'"
}
}
// ssh 접근 이후 관리자 권한으로 명령어를 실행하는 함수
def sshSudoCommand(cmd) {
sshagent(credentials: [CREDENTIAL_ID]) {
sh "ssh -o StrictHostKeyChecking=no ${REMOTE_HOST} 'sudo ${cmd}'"
}
}
// ssh 접근이후 파이썬 코드 실행 명령어를 관리자 권한으로 실행하는 함수
def sshSudoPythonCommand(cmd) {
sshagent(credentials: [CREDENTIAL_ID]) {
sh "ssh -o StrictHostKeyChecking=no ${REMOTE_HOST} 'sudo python3 ${cmd}'"
}
}
// Stage 경과 시간을 계산하여 메시지를 생성하는 함수
def getStageDurationMessage(long startTime, long endTime) {
long durationMillis = endTime - startTime
long durationSeconds = (long) (durationMillis / 1000) % 60
long durationMinutes = (long) (durationMillis / (1000 * 60)) % 60
def durationMessage = ""
if (durationMinutes > 0) {
durationMessage += "${durationMinutes}분 "
}
durationMessage += "${durationSeconds}초"
return durationMessage
}
// 슬랙 실패 메세지를 생성하는 함수
def stageFailSlackMessage(stageName) {
return ":alert: ${stageName} 단계에서 배포가 실패하였습니다. \n"
}
전역변수에 정의된 SLACK_MESSAGE_BUILDER에 스테이지가 수행될 때 마다 차곡차곡 메세지를 쌓고, `End` 스테이지에서 한번에 보내는 구조입니다.
만약 스테이지에 정의한 스크립트 수행에 실패한다면, post 블록에 정의된 스크립트가 실행됩니다.
주의할 점은, options 블록에 정의된 timestamps() 를 활성화 하려면 timestamper 플러그인이 설치되어 있어야 한다는 점입니다. 저는 각각의 스테이지마다 수행 시간을 측정하기 위하여 해당 플러그인을 설치해주었습니다.
또한, 위 스크립트의 경우 어플리케이션 내부에 파이썬으로 스크립트를 작성하여 docker 명령어, gradle 명령어를 수행하고, EC2 인스턴스 내부에 미리 git을 설치하여 remote address 설정을 해주었으며 배포될 브랜치에 체크아웃을 미리 해두었기 때문에 단순히 위 스크립트를 복사&붙여넣기 만으로 사용하실 수는 없을 것입니다.
저의 상황에 맞는 스크립트이니, 사용하실 분들은 알맞게 커스터마이징을 해서 사용하시는 것을 추천드립니다!
자, 그럼 위 스크립트를 통해 배포를 실행하면 어떤 식으로 채널에 알림 메세지가 오는지 확인해보겠습니다.
Stage View를 통해 `End` 스테이지 까지 정상적으로 수행되었음을 확인하고,
성공 알림 메세지
채널을 확인해보면 위와 같이 메세지가 왔음을 확인할 수 있습니다.
실패 알림 메세지
만약, 테스트 스테이지에서 실패를 하였다면 위처럼 어느 단계에서 배포가 실패했는지 알 수 있습니다.
마치며
이상으로 젠킨스 파이프라인 스크립트를 통하여 배포 단계별 슬랙 메세지 알림을 구현해보았습니다.
저만의 경우에서 사용할 수 있는 스크립트지만, 문법에 대하여 아시는 분들은 조금만 수정하여 본인의 상황에 맞게 커스터마이징 하실 수 있으리라 생각됩니다.
위 스크립트에서 왜 SLACK_MESSAGE_BUILDER 변수를 선언하고 스테이지 별로 메세지를 쌓아서 마지막에 전송하는 방식으로 구현했을까에 대한 의문점이 있으실 수도 있는데, 처음에는 스테이지마다 실패할 경우 메세지를 보내도록 하였더니 여러 아이템을 동시에 빌드 실행하였을 때 메세지 형식이 깨진다는 점에서 모든 단계가 끝났을 때 SLACK_MESSAGE_BUILDER에 쌓인 메세지를 보내는 형식으로 구현하였습니다.
다만, 빌드 실행 시 배포가 수행중임을 구성원들에게 알려야 한다고 생각하여, `Start` 스테이지를 맨 앞단에 두고, 배포 실행 알림 메세지는 즉시 보내도록 하였습니다.
이런 방식으로 구성하니, 여러 아이템의 빌드를 동시에 병렬로 수행하더라도 알림 메세지 형식이 깨지지 않으면서 어느 아이템의 배포가 수행중인지 알 수 있게 되었습니다.
위처럼 배포 슬랙 알림 메세지를 한 번 작성해두니 수동으로 배포 알림을 작성하는 일은 더이상 하지 않아도 되었고, 매번 OOO님, 배포중이에요? 라는 질문도 받게 되지 않았습니다. 또한 젠킨스에 직접 들어가지 않더라도 배포가 성공적으로 수행되었음을 알 수 있게 되어 굳이 젠킨스 빌드 로그를 끝까지 보고 있지 않아도 되어 좀 더 효율적으로 배포를 진행할 수 있게 되었습니다.!
'개발 > DevOps' 카테고리의 다른 글
EC2 스왑메모리 설정 방법 (0) | 2025.01.13 |
---|---|
Jenkins Pipeline Script로 슬랙에 배포 알림 메세지 보내기(1) - 토큰발급, 플러그인 설치 및 설정 (0) | 2024.08.19 |
개발을 하며 만났던 문제들과 해결 과정, 공부한 내용 등을 기록합니다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!