[DevOps] Jenkins, Docker로 CI/CD 파이프라인 구축하기 (2)

이창윤·2024년 1월 9일
0

기초 설정 및 설치 작업이 끝났으니 Pipeline을 작성할 수 있다.

Pipeline 생성

Jenkins 대시보드 -> 새로운 Item -> Pipeline 선택

Generic Webhook Trigger 플러그인 설치

Jenkins 관리 -> Plugins -> Available plugins -> Generic Webhook Trigger 검색 -> 설치 & 재시작

Pipeline Github 설정

생성한 Pipeline -> 구성 -> Github project 활성화Project url에 프로젝트 Repository의 Clone 링크 (Code -> HTTPS 클릭 시 나오는 .git으로 끝나는 링크)를 입력한다.

조금 내려서 Build Triggers의 Generic Webhook Trigger를 활성화 시켜준다.
그리고 Post content parameter에 변수를 두개 추가한다.

특정 브랜치에 PR이 Merge된 경우에만 Pipeline을 실행시키고 싶을 때!!
그냥 Github integration을 사용하면 webhook에서 브랜치나 상황을 필터링 할 수 없다

  1. Webhook에서 PR이 Merge된 경우를 판별하기 위한 변수
    • Name of variable: IF_MERGED
    • Expression: $.pull_request.merged
    • JSONPath 선택
  2. Webhook에서 브랜치 이름을 구별하기 위한 변수
    • Name of variable: BRANCH
    • Expression: $.pull_request.base.ref
    • JSONPath 선택

쭉 내려서 Optional filter에 다음과 같이 입력해준다.

  • Expression: (?=.*true)(?=.develop).
  • Text: $IF_MERGED $BRANCH

Webhook 추가하기

프로젝트 Repository -> Settings -> Webhooks -> Add webhook

  • URL: Jenkins 서버 URL:8080/generic-webhook-trigger/invoke
  • Content type: application/json
  • Which events~~: Let me select 선택
    • 쭉 내려서 Push 체크 해제하고 Pull requests 선택하기

환경변수 설정

Jenkins 관리 -> System -> Global properties
Pipeline Script에서 사용하기 위한 환경변수를 추가해놓고 ${env.환경변수명} 으로 가져다 사용할 수 있다.

Pipeline Script 작성

개발자가 코드를 올리면 서버에 자동으로 반영하는 Pipeline의 전체 과정을 한번에 정리하자면 총 5 stages로 나눌 수 있다.
1. Clone
2. Gradle 빌드
3. Docker 빌드
4. Docker Push
5. Docker Pull & 실행

Script의 구조는 아래와 같다.

pipeline { } 안에 모든 명령어를 작성한다.

  • agent: 작업을 수행하는 시스템
    • any: stage 상관 없이 Pipeline 전체에서 사용 가능한 agent를 아무거나 쓰겠다
  • environment에 필요한 환경변수 선언
    • dockerImage: 빈 변수에 docker.build 결과값인 이미지 이름과 버전을 재사용하기 위함
pipeline {
	agent any
    environment {
    	registryCredential = 'dockerhub_credentials'
        dockerImage = ''
        imageName = 'Docker 이미지 이름'
    }

stages 블록 내부에 모든 stage 선언

1번 stage: Github Repository Clone하기

  • steps: stage에서 수행할 작업 단위
    • git branch: Clone할 브랜치 지정
    • credentialsId: 앞서 생성한 Github Credentials ID
    • url: < Repository URL >
  • echo: 스크립트 실행 시 진행 상황을 알 수 있게 알맞은 말을 출력
  • post: stage 이후 실행될 조건 블록
    • success: 성공 시 작업
    • failure: 실패 시 작업
    stages {
    	stage('Prepare') {
      		steps {
        		echo 'Clonning Repository'
        		git branch: 'develop', credentialsId: 'github-credentials', url: '<https://github.com/backend>'
        	}
        	post {
         		success {
           			echo 'Successfully cloned repository'
         		}
       	 		failure {
           			error 'Failed to clone repository'
         		}
        	}
    	}

2번 stage: jar파일 빌드하기

  • 루트 디렉토리에서 다음의 명령어를 수행한다.
    • shell script로 gradlew 권한 수정
    • clean & bootJar
      • build를 해도 상관없는데 나는 API 문서화에 필요한 과정을 bootJar 실행 시 수행하도록 설정해놔서 bootJar를 선택함
  • 만약 gradlew 명령어 오류가 발생하면 Jenkins 서버에 직접 접속해서 gradle을 설치해본다.
    sudo apt-get install gradle
     	stage('Bulid Gradle') {
      		steps {
        		echo 'Bulid Gradle'
            	dir('.'){
                	sh'''
                    	chmod +x gradlew
                    	./gradlew clean bootJar
        	        '''
            	}
      		}
      		post {
       			success {
         			echo 'bootJar successful'	
       			}
       			failure {
         			error 'bootJar failed'
       			}
      		}
    	}

3번 stage: Docker 이미지 빌드하기

  • dockerImage 변수에 build 결과(이름:버전) 저장
  • 개발, 운영 환경을 분리했기 때문에 build시 PROFILE 매개변수를 추가해준다.
    	stage('Bulid Docker') {
      		steps {
        		echo 'Bulid Docker'
        		script {
            		dockerImage = docker.build(imageName, "--build-arg PROFILE=dev .")
        		}
      		}
      		post {
        		success {
          			echo 'Successfully Built Image'
        		}
        		failure {
          			error 'This pipeline stops here...'
        		}
      		}
    	}

4번 stage: Docker 이미지 Dockerhub에 Push하기

  • docker.withRegistry: Dockerhub Credential를 포함
    • dockerImage.push(): dockerhub에 Build한 이미지를 Push
    	stage('Push Docker') {
      		steps {
        		echo 'Push Docker'
        		script {
            		docker.withRegistry( '', registryCredential) {
                		dockerImage.push()
            		}
        		}
      		}
      		post {
        		success {
          			echo 'Successfully Pushed Image'
        		}
        		failure {
          			error 'This pipeline stops here...'
        		}
      		}
    	}

5번 stage: Docker 이미지 실행

Spring Boot 서버에 SSH Agent로 접근해 Docker 이미지를 pull & 실행한다.

  • sshagent credentials: 앞서 생성한 SSH Agent Credential
  • sh로 Spring Boot 서버에서 Shell Script 명령어 수행
    • StrictHostKeyChecking=no 옵션: ssh로 원격 접속할 서버(Spring Boot 서버)의 .ssh/known_hosts 파일에 자동으로 새로운 호스트를 추가하지 않겠다
    1. docker pull: dockerhub에 올린 이미지를 내려받기
    2. docker ps~~: 새로운 이미지를 실행하기 전에 기존에 실행 중이던 컨테이너를 중지 & 삭제하는 작업
    3. docker run: 이미지 실행하기
      • 실행 시 필요한 환경변수는 -e옵션으로 추가할 수 있다. ( -e 환경변수명=${env.환경변수명} )
    	stage('Docker Run') {
        	steps {
            	echo 'Pull Docker Image & Docker Image Run'
            	sshagent (credentials: ['ssh_agent']) {
                	sh "ssh -o StrictHostKeyChecking=no ubuntu@Spring Boot 서버 IP주소 'docker pull 이미지 이름:버전'"
                	sh "ssh -o StrictHostKeyChecking=no ubuntu@Spring Boot 서버 IP주소 'docker ps -q --filter name=컨테이너 이름 | grep -q . && docker rm -f \\$(docker ps -aq --filter name=컨테이너 이름)'"
                	sh "ssh -o StrictHostKeyChecking=no ubuntu@Spring Boot 서버 IP주소 'docker run -d --name server -p 8080:8080 -e DB_PATH=${env.DB_PATH} 이미지 이름:버전'"
            	}
        	}
    	}
	} 

마지막으로 Pipeline의 모든 stage를 수행한 결과를 Slack Notification으로 수신한다.

  • channel : 알림을 받기로 설정한 채널명 (#붙이기)
	post {
   		success {
        	slackSend (channel: '#jenkins_notification', color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
    	}
    	failure {
        	![](https://velog.velcdn.com/images/yun8565/post/c738f70a-33ca-4cd6-8f93-b23151ceb7a4/image.png)
slackSend (channel: '#jenkins_notification', color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
    	}
	}
} // Pipeline 끝

Pipeline 빌드해보기

Jenkins 대시보드 -> Pipeline -> 지금 빌드 클릭

Stage View에서 실행 과정을 한 눈에 볼 수 있다.
좌측의 Build history를 클릭해서 Console Output으로 상황을 직접 확인할 수 있다.

총 정리

  • 필요한 플러그인: Docker, Docker Pipeline, SSH Agent, Slack Notification, Generic Webhook Trigger
  • Credentials:
  • Pipeline Stage
    1. Clone Repository
    2. Gradle build
    3. Docker build
    4. Dockerhub push
    5. (ssh) Docker pull & run


내 생각

정리를 하다 보니 Docker를 활용하는 방법은 Github Actions를 쓰는게 좋을 것 같다. Jenkins로 별도의 서버를 구축하는 수고를 덜 수 있고 Script 파일 하나만 프로젝트 Repository에 추가하면 되기 때문이다.

그래도 이렇게 정리를 해놓았으니 다음에 혹시 Jenkins를 사용해야 할 때 참고할 수 있을 것 같다!

참고

0개의 댓글