[K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 - CI/CD 파이프라인 구축:2

mDev_97·2023년 10월 26일
0

Kubernetes

목록 보기
7/10
post-thumbnail

📌 프로젝트 주제에 대한 자세한 설명은 [K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 - 프로젝트 소개 게시글을 확인해주세요.

이번 게시글에서 진행할 내용을 CI/CD 파이프라인 구축하기 중에서
GitHub Webhook 구성과 Jenkins Pipeline 설정 입니다.

CI/CD PipeLine 구축

소스코드 GitHub Repository 구성

Webhook을 구성하기 위해서는 GitHub Repository를 구성해야 합니다.
저 같은 경우는 도커 이미지를 빌드하기 위해서 소스 코드가 필요하기 때문에
GitHub Repository가 이미 구성되어 있습니다.

해당 프로젝트를 함께 진행하시는 분들은 GitHub Repository를 fork하여 깃허브 리포지토리를 구성해주시면 됩니다.


Webhook 구성

깃허브 리포지토리를 구성하였다면 소스코스 Push 이벤트가 발생할 때마다
Jenkins에게 Request를 보내 알릴 수 있는 Webhook을 구성해보겠습니다.

Webhook을 구성하기 위해서 자신의 리포지토리에서
[Settings] -> [Webhooks] -> [Add webhook]로 들어가줍니다.

자신의 Github 패스워드 입력

그럼 Webhook에 대해서 설정해보겠습니다.

Payload URL에 경우 자신의 Jenkins IP:Port/github-webhook/ 이라고 지정해주고
Content Type은 application/json 타입으로
webhook 이벤트 트리거는 Just the push event를 선택하고 Webhook을 추가해주겠습니다.

그럼 이제 깃허브에서 push 이벤트가 발생할 때마다 Jenkins 서버로 Webhook Request가 전송됩니다.


Kubernetes Manifest GitHub repository 구성

현재 진행하고 있는 프로젝트의 CI/CD 파이프라인 아키텍처를 보면 Jenkins를 통해서
Kubernetes Manifest GitHub repository가 수정이 되어지는 것을 알 수 있습니다.

이를 위해서는 Kubernetes Manifest를 저장해두는 리포지토리가 필요합니다.
[K8S] CI/CD Pipeline, 모니터링, 로깅 시스템 구축 프로젝트 -GKE 환경에 배포하기 게시글에 있는 메니페스트를 직접 리포지토리를 생성해서 추가하셔도 되고, 저의 쿠버네티스 GitHub 리포지토리fork하셔서 구성하셔도 됩니다.


Jenkinsfile 작성

소스코드가 Push 되었을 때, Webhook을 통해서 Jenkins 서버로 요청이 간다면,
Jenkins에서 수행할 일련의 작업들을 Jenkinsfile에 정의해주어야 합니다.

프로젝트의 루트 디렉토리에 Jenkinsfile을 아래와 같이 작성해줍니다.

pipeline{
    agent any

    environment {
        dockerHubRegistry = '[Docker Hub Registry 명]'
        dockerHubRegistryCredential = 'docker-hub'
        githubCredential = 'k8s_manifest_git'
    }

    stages {
        stage('check out application git branch'){
            steps {
                checkout scm
            }
            post {
                failure {
                    echo 'repository checkout failure'
                }
                success {
                    echo 'repository checkout success'
                }
            }
        }

        stage('docker image build'){
            steps{
                sh "docker build -t ${dockerHubRegistry}/react:${currentBuild.number} ./frontend"
                sh "docker build -t ${dockerHubRegistry}/react:latest ./frontend"

                sh "docker build -t ${dockerHubRegistry}/nodejs:${currentBuild.number} ./backend"
                sh "docker build -t ${dockerHubRegistry}/nodejs:latest ./backend"

                sh "docker build -t ${dockerHubRegistry}/db:${currentBuild.number} ./mysql"
                sh "docker build -t ${dockerHubRegistry}/db:latest ./mysql"
            }
            post {
                    failure {
                      echo 'Docker image build failure !'
                    }
                    success {
                      echo 'Docker image build success !'
                    }
            }
        }
        stage('Docker Image Push') {
            steps {
                withDockerRegistry([ credentialsId: dockerHubRegistryCredential, url: "" ]) {
                    sh "docker push ${dockerHubRegistry}/react:${currentBuild.number}"
                    sh "docker push ${dockerHubRegistry}/react:latest"

                    sh "docker push ${dockerHubRegistry}/nodejs:${currentBuild.number}"
                    sh "docker push ${dockerHubRegistry}/nodejs:latest"

                    sh "docker push ${dockerHubRegistry}/db:${currentBuild.number}"
                    sh "docker push ${dockerHubRegistry}/db:latest"

                    sleep 10 /* Wait uploading */
                }
            }
            post {
                    failure {
                      echo 'Docker Image Push failure !'
                      sh "docker rmi ${dockerHubRegistry}/react:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/react:latest"

                      sh "docker rmi ${dockerHubRegistry}/nodejs:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/nodejs:latest"

                      sh "docker rmi ${dockerHubRegistry}/db:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/db:latest"
                    }
                    success {
                      echo 'Docker image push success !'
                      sh "docker rmi ${dockerHubRegistry}/react:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/react:latest"

                      sh "docker rmi ${dockerHubRegistry}/nodejs:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/nodejs:latest"

                      sh "docker rmi ${dockerHubRegistry}/db:${currentBuild.number}"
                      sh "docker rmi ${dockerHubRegistry}/db:latest"
                    }
            }
        }
        stage('K8S Manifest Update') {
            steps {
                sh "ls"
                sh 'mkdir -p gitOpsRepo'
                dir("gitOpsRepo")
                {
                    git branch: "main",
                    credentialsId: githubCredential,
                    url: '[GitHub Repository Https Clone Url]'
                    
                    sh "git config --global user.email '[GitHub Email]'"
                    sh "git config --global user.name '[GitHub Name]'"

                    sh "sed -i 's/react:.*\$/react:${currentBuild.number}/g' web-deployment.yaml"
                    sh "sed -i 's/nodejs:.*\$/nodejs:${currentBuild.number}/g' api-deployment.yaml"
                    sh "sed -i 's/db:.*\$/mysql:${currentBuild.number}/g' mysql-deployment.yaml"

                    sh "git add ."
                    sh "git commit -m '[UPDATE] k8s ${currentBuild.number} image versioning'"
                    withCredentials([gitUsernamePassword(credentialsId: githubCredential,
                                     gitToolName: 'git-tool')]) {
                        sh "git remote set-url origin [GitHub Repository URL]"
                        sh "git push -u origin main"
                    }
                }
            }
            post {
                    failure {
                      echo 'K8S Manifest Update failure !'
                    }
                    success {
                      echo 'K8S Manifest Update success !'
                    }
            }
        }

    }
}

Jenkins 파이프라인 구성

이제는 Jenkins에서 Webhook Request를 받아서 빌드할 수 있도록 구성하기 위해서 새로운 ITEM을 생성해줍니다.

Jenkins 서버로 들어와서 [새로운 Item] 클릭
아이템 이름을 입력하고 Pipeline을 선택

저는 GitHub에서 프로젝트를 가져올 것이기 때문에 GitHub project를 선택해주고
Project url에는 GitHub 리포지토리 주소를 넣어줍니다.

그리고 Webhook을 사용해서 GitHub에서 가져올 것이기 때문에 GitHub hook trigger for GITScm polling으로 Trigger를 선택해줍니다.

Pipeline의 경우 저희는 이미 Jenkinsfile을 작성하였습니다.
그렇기 때문에 직접 Pipeline을 작성하는 것이 아닌 Pipeline script from SCM 을 선택하여서
Git에서 가져올 것이라고 지정해줍니다.

우리는 GitHub에서 정보들을 받아올 것이기 때문에 Credentials을 추가해주어야합니다.

Credentials -> [Add] -> [Jenkins] 을 클릭

Username의 경우 본인의 GitHub 이름을 적어주시면 되고, ID는 그대로 적으셔도 무방합니다.
만약 ID를 저와 다르게 작성하신다면 Jenkinsfile에도 수정을 해주셔야 합니다.
이 때, Password의 경우 GitHub의 Developer Setting에서 토큰을 발급받아서 입력하시면 됩니다.

빌드를 진행할 브랜치는 main 브랜치로 지정해주겠습니다.
그리고 Script Path는 이름이 반드시 일치하여야 합니다.

그리고 이제 Docker Hub에 Push하기 위해서 필요한 Credentials를 추가해주도록 하겠습니다.

대시보드 화면에서 [Jenkins 관리] -> [Credentials]를 선택

Stores scoped to Jenkins 아래에 있는 System을 클릭 후 Global Credentials로 들어가줍니다.

여기서 이제 [Add Credentials]를 통해서 Docker Hub를 위한 Credentials을 추가해주겠습니다.

Username은 Docker Hub 레지스트리의 이름을 적고, 패스워드는 Docker Hub 비밀번호 또는 토큰 값을 넣어주시면 됩니다.
만약 ID를 저와 다르게 작성하신다면 Jenkinsfile에도 수정을 해주셔야 합니다.

그럼 아래와 같이 인증정보들이 잘 설정되었습니다.


결과 확인

위의 과정들을 잘 수행하셨다면 이제 Jenkins 구성과 Webhook 부분은 잘 구성된 것입니다.
이제 구성한 내용들이 잘 작동하는지 확인해보겠습니다.

Jenkins의 첫 번째 빌드같은 경우는 직접 빌드를 실행해주어야 합니다.

그러므로 빌드를 실행하기 위해서 [지금 빌드]를 클릭해줍니다.

그럼 아래와 같이 빌드가 성공한 것을 볼 수 있습니다.
저 같은 경우에는 3번 째 빌드 성공을 한 것인데요.

이제 빌드가 성공적으로 진행이 되었으면 Kubernetes 메니페스트가 저장되어 있는
GitHub 리포지토리도 잘 수정이 되었는지 확인해보겠습니다.

Kubernetes 메니페스트가 저장되어 있는 GitHub 리포지토리로 들어와서 커밋을 확인해보면
아래와 같이 잘 적용된 것을 확인할 수 있습니다.

profile
안녕하세요. 백엔드, 클라우드, 인프라에 관심과 열정이 있는 김문성입니다. 😊

0개의 댓글