Build and Deploy Dockerfile in Jenkins

[Start RESTful Service With Spring Boot 2.x + Kotlin] 글에서 유일하게 다루지 못했던 주제가 있었다. 바로 Jenkins를 이용한 CI/CD 연동이다.

글을 올린지 약 1달 반 정도 지났지만, 성공적으로 빌드할 수 있게 되었고 이를 작성해보려 한다.

환경설정

Jenkins 내부에서 Docker를 빌드하려면 Docker 바이너리가 Jenkins에 포함되어 있어야 한다. Jenkins 역시 도커 이미지로 배포되기 때문에, 먼저 커스텀 Jenkins 이미지를 수정할 필요성이 있다.

Docker + Jenkins 연동은 [Run Docker + Jenkins for Android Build] 글에서 다루니 그쪽부터 보면 좋을 것 같다.

Docker를 설치하기 위해서 추가적으로 필요한 의존성은 다음과 같다.

  • ca-certificates
  • curl
  • gnupg2
  • file
  • lxc
  • apt-transport-https

이 6개를 apt install로 통해 설치하고 순서대로 명령어를 입력하면 된다.

## Install requirements
RUN dpkg --add-architecture i386
RUN rm -rf /var/lib/apt/list/* && apt-get update && apt-get install ca-certificates curl gnupg2 software-properties-common git unzip file apt-utils lxc apt-transport-https -y

그 다음, docker 서버로부터 인증서와 레포지토리 주소를 받아서 설치하면 된다.

## Install Docker-ce into Image
RUN curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey;
RUN apt-key add /tmp/dkey
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
RUN apt-get update && apt-get install docker-ce -y --no-install-recommends
RUN usermod -a -G docker jenkins

여기까지 마치면 Jenkins 이미지에 Docker-CE 바이너리가 설치된다.

아니면, 업데이트 된 [WindSekirun/Jenkins-Android-Docker] 이미지를 활용해도 문제는 없다.

JenkinsFile 작성

Spring-Boot 프로젝트를 빌드하고, 이를 Docker로 배포하는 과정에 있어서 Jenkins에 필요한 작업을 정의하는 작업이 필요하다.

Jenkins는 각 파이프라인 상에서 수행해야 될 작업을 Jenkinsfile 이라는 파일에 정의하고, 이를 빌드할 때 마다 사용할 수 있다.

필요한 작업을 정리하면 다음과 같을 것이다.

  • 환경설정
  • Gradle 빌드
  • DockerImage 빌드
  • DockerHub로 이미지 푸시
  • 빌드된 이미지 제거

이 중, gradle 까지의 작업을 반영하면 다음과 같다

pipeline {
    agent any
    stages {
        stage('Environment') {
            parallel {
                stage('chmod') {
                    steps {
                        sh 'chmod 755 ./gradlew'
                    }
                }
                stage('display') {
                    steps {
                        sh 'ls -la'
                    }
                }
            }
        }
        stage('Build Jar') {
            steps {
                sh './gradlew build'
            }
        }
     }
}

Docker 이미지 빌드

환경변수에 registry 이라는 변수를 정의한다.

environment {
        registry = "windsekirun/uploadfileboot"
}

registry 는 이미지에 대한 태그이며, https://hub.docker.com/r/windsekirun/uploadfileboot 이런 식으로 접근이 가능해진다.

그 다음 docker 빌드 명령어를 작성한다. 기본적인 빌드 명령어는 docker build -t windsekirun/uploadfileboot:latest .이므로 Jenkinsfile의 형식에 맞게 작성하면 된다.

stage('Build docker image') {
            steps {
                sh 'docker build -t $registry:latest .'
            }
}

latest 대신 $BUILD_NUMBER 나 다른 환경변수를 이용해서 사용도 가능하다.

빌드된 이미지 푸시

Jenkins 설정 > 인증정보에 정보를 추가한다

그리고 환경변수에 추가한 정보의 id를 정의하면, 준비는 완료된다.

environment {
        registry = "windsekirun/uploadfileboot"
        registryCredential = 'dockerhub'
}

마지막으로, registry에 푸시하는 명령어는 docker push windsekirun/uploadfileboot:latest 이므로, Jenkinsfile 형식에 맞춰 작성한다. 다만 crediental 정보가 필요하므로 withDockerRegistry 명령어를 통해 crediental 정보를 입력한다.

stage('Deploy docker image') {
            steps {
                withDockerRegistry([ credentialsId: registryCredential, url: "" ]) {
                    sh 'docker push $registry:latest'
                }
            }
}

빌드한 이미지 삭제

빌드된 이미지를 계속 남겨둘 필요는 없으므로, 지금까지 빌드한 이미지를 삭제한다.

stage('Clean docker image') {
            steps{
                sh "docker rmi $registry"
            }
}

전체 코드

pipeline {
    environment {
        registry = "windsekirun/uploadfileboot"
        registryCredential = 'dockerhub'
    }
    agent any
    stages {
        stage('Environment') {
            parallel {
                stage('chmod') {
                    steps {
                        sh 'chmod 755 ./gradlew'
                    }
                }
                stage('display') {
                    steps {
                        sh 'ls -la'
                    }
                }
            }
        }
        stage('Build Jar') {
            steps {
                sh './gradlew build'
            }
        }
        stage('Build docker image') {
            steps {
                sh 'docker build -t $registry:latest .'
            }
        }
        stage('Deploy docker image') {
            steps {
                withDockerRegistry([ credentialsId: registryCredential, url: "" ]) {
                    sh 'docker push $registry:latest'
                }
            }
        }
        stage('Clean docker image') {
            steps{
                sh "docker rmi $registry"
            }
        }
     }
}

이 Jenkinsfile 를 가지고 Jenkins를 설정하면 다음과 같이 나오게 된다.

마지막으로 push된 이미지는 Dockerhub 홈페이지에서 확인할 수 있다.

마무리

Jenkins 를 이용해 도커 이미지를 빌드하고 배포하는 과정까지 살펴보았다.

물론 master에 빌드한 것을 그대로 push하는 것은 매우 위험하기 때문에 개발하는 사이클마다 별도로 적용해야 되지만, 개론적인 것은 살펴본 것과 크게 다르지는 않을 것이다.

위에 살펴본 DockerHub에 올리는 것 말고도 private registry에 올리는 것 또한 가능하기에 선택하기 나름일 것 같다.