[젠킨스 구축] 6. 자동 빌드, push - f-lab-edu/jshop GitHub Wiki

테스트가 완료된 소스코드는 자동으로 빌드 후, 저장소(Ncloud Container Repository)로 push 된다.

이번 글에서 다룰 작업은 다음과 같다.

  1. jenkins 컨테이너 내부에서 docker build
  2. push를 위한 Ncloud Container Repository 로의 docker login

하나씩 살펴보자.

jenkins 컨테이너 내부에서 docker build

현재 젠킨스는 docker 컨테이너로 돌아간다. 이 내부에서 docker 명령을 수행해야 했다.

하지만 굳이 컨테이너 내부에 docker가 있어야 하나 싶었다. 정확하게는 docker 서버가 또 있어야 하나 싶었다.

리눅스는 모든 자원이 파일로 표현된다. docker 서버와 통신을 위한 docker 소켓 또한 파일로 표현된다.

컨테이너 내부에서 호스트의 docker 소켓에 접근할 수 있다면, 컨테이너에서는 docker 명령만 설치해 호스트의 docker 서버를 사용할 수 있을것이라 생각했다.

컨테이너 내부에서 docker 를 설치하는 Dockerfile을 만들어 사용했다.

FROM jenkins/jenkins:latest
USER root
RUN apt-get update -y
RUN apt-get install -qqy apt-transport-https ca-certificates curl gnupg2 software-properties-common -y
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
RUN apt-get update -y
RUN apt-get -y install docker-ce
RUN usermod -aG docker jenkins

docker cli 설치 과정을 포함한다.

이후 해당 이미지를 컨테이너로 만들어 실행할때 호스트의 docker 소켓을 마운트 해줬다. (/var/run/docker.sock)

docker run -d -p 8080:8080 -v /var/jenkins/:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkins

해당 과정을 거치고, 새로 생성한 jenkins 컨테이너 내부에서 docker 명령을 수행하니 큰 어려움 없이 수행할 수 있었다.

build 스크립트 작성

젠킨스 컨테이너 내부에서 docker를 실행시키기 위한 준비가 끝났으니 Dockerfile 과, Jenkinsfile을 작성해보았다.

FROM openjdk:17-oracle

RUN microdnf install findutils

WORKDIR /app

COPY . /app
RUN chmod +x ./gradlew
RUN ./gradlew build

CMD tail -f /dev/null

테스트를 위해 실행 커멘드는 tail -f /dev/null 로 설정했다.

Jenkinsfile 파이프라인 스크립트 작성

젠킨스 파이프라인 스크립트도 작성했다.

pipeline {
    agent any
        stage('Build') {
            steps {
                script {
                    dir('be') {
                        echo 'Deploying the project...'
                        sh 'docker build -t test .'
                    }                    
                }
            }
        }
    }
}

파이프라인에서 정상적으로 빌드가 된 모습이다.

Ncloud Container Repository 로 push

젠킨스에서 빌드한 이미지를 네이버 클라우드 레포지토리로 push 하는 작업이다.

NCR은 AWS의 ECR와 같이 컨테이너 이미지를 저장하는 저장소다.

docker-hub 를 사용할 수 있지만 public으로만 사용해야 하고, 보안문제때문에 개인적으로 private repo를 선호한다.

NCR도 레포지토리에 로그인을 해야 사용할 수 있다.

로그인을 위해선 API 키가 필요하지만 이 키의 경우 외부로 노출되면 안되는 값이기 때문에 jenkins의 Credentials기능을 사용하기로 했다.

Ncloud 의 API key, Secret Key를 Credentials에 text로 등록하고, pipeline에서 이 키들을 사용함으로써, 외부 노출없이 사용할 수 있다.

pipeline {
    agent any
    
    environment {
        NCLOUD_ACCESS_KEY_ID = credentials('NCLOUD_ACCESS_KEY_ID')
        NCLOUD_SECRET_KEY = credentials('NCLOUD_SECRET_KEY')
    }

    stages {        
        stage('Build') {
            steps {
                script {
                    dir('be') {
                        echo 'Deploying the project...'
                        sh 'docker build -t test .'
                    }                    
                }
            }
        }

        stage('Push') {
            steps {
                script {
                    sh 'echo $NCLOUD_SECRET_KEY | docker login jshop.kr.ncr.ntruss.com -u ${NCLOUD_ACCESS_KEY_ID} --password-stdin'
                }
            }
        }
    }
}

등록된 Credentials를 사용하기 위해선 credentials() 를 사용해 환경변수에 값을 보관해 사용한다.

환경변수로 키, 비밀키를 저장하고 로그인에 사용한다.

그리고 특정 레지스트리에 push 하기 위해선 이미지 태그 이름이 {registry}/{image}[:TAG] 의 형태여야 한다.

jshop:v1.0.0 이라는 이미지를 NCR에 push하기 위해선 이미지 이름을 jshop.kr.ncr.ntruss.com/jshop:v1.0.0 으로 만들어 줘야 한다.

파이프라인의 빌드 과정에서 이를 수정해준다. sh 'docker build -t jshop.kr.ncr.ntruss.com/jshop:v1.0.0 .'

이제 전체 파이프라인을 수행해보면 테스트 -> docker 빌드 -> NCR push 과정까지 수행되는것을 볼 수 있다.

정리

처음엔 docker 내부에서 docker 를 어케 실행해야 하는지 고민을 했었다.

그러다 docker 소켓만 마운트 시켜준다면 컨테이너 내부에서도 호스트의 docker 서버에 접속할 수 있을것이라 생각했고, 실제로 이렇게 동작했다.

내부에서 docker 빌드를 수행할 수 있으니, 파이프라인에 docker 명령을 넣어주기만 하면 되었기 때문에 이과정도 크게 어렵지 않았다.

아래는 지금까지 만든 Jenkinsfile 이다.

pipeline {
    agent any
    
    environment {        
        NCLOUD_ACCESS_KEY_ID = credentials('NCLOUD_ACCESS_KEY_ID')
        NCLOUD_SECRET_KEY = credentials('NCLOUD_SECRET_KEY')
        IMAGE = 'jshop.kr.ncr.ntruss.com/jshop:v1.0.0'
    }

    stages {
        stage('Test') {
            steps {
                script {
                    dir('be') {                
                       echo 'Test'
                        sh './gradlew clean test'
                    }
                }
            }
        }

        stage('Build') {
            steps {
                script {
                    dir('be') {
                        echo 'build'
                        sh "docker build -t ${IMAGE} ."
                    }                    
                }
            }
        }

        stage('NCR_Push') {
            steps {
                script {
                    echo 'push'
                    sh "echo ${NCLOUD_SECRET_KEY} | docker login jshop.kr.ncr.ntruss.com -u ${NCLOUD_ACCESS_KEY_ID} --password-stdin"                
                    sh "docker push ${IMAGE}"
                }
            }
        }
    }
}