Jenkins Pipeline + Docker SpringBoot프로젝트 배포(DinD방식)하기

devdo·2022년 2월 24일
2

Jenkins

목록 보기
5/8
post-thumbnail

Docker in Docker 란?

한마디로, 도커 안의 도커가 있다는 것이다.

도커 바이너리를 설정하고 컨테이너 내부의 격리된 Docker 데몬을 실행하는 작업의 과정을 말한다.

CI 측면에서 접근한다면 job(task)을 수행하는 Executor(Agent)가 Docker Client와 Docker Daemon 역할까지 하게 되어 도커 명령을 수행하는데 문제가 없어진다.


Docker inside of a Docker(DinD) Container

실습할 DinD 를 그려보자면 다음과 같다.

이 구조의 아키텍쳐를 구축할려면 다음 과정을 따르자.

1) Docker 컨텐이너로 jenkins를 실행

2) 그 jenkins 컨테이너 안에 Docker를 또 다시 설치

# jenkins 컨테이너 os 확인, os 에 맞는 docker 설치 명령어를 쳐주면 된다!
cat /etc/*-release 

3) jenkins 컨테이너 안에 docker 명령어는 외부 docker 컨텐이너와 연결되어야 한다.

  • ✅ AWS EC2 안에 docker로 jenkins 실행시 -v 마운트 설정 필수!

Jenkins pipeline란?

jenkins pipeline은 그전 Freestyle로 jenkins 프로젝트를 만드는 것보다 더 관리용의성이 편하기 때문에 많이 사용된곤 합니다.

별도의 pipeline 문법으로 된 스크립트언어로 빌드 및 배포 흐름을 stage 별로 관리할 수 있습니다.

그리고 이를 하나의 문서로 관리하고 싶다면, Jenkinsfile 스크립트 파일로 관리 수도 있습니다.

이번 실습은은 하나의 인스턴스를 통해서 배포가 가능하게 할 것입니다.

원격으로 접속할 SSH서버가 따로 필요없습니다.(권장사항은 x)

젠킨스 파이프라인 첫번째 실습을 하기 위해선 DinD(Docker in Docker) 구조의 방식으로, jenkins 컨테이너 내에도 docker가 설치되어야 합니다.


Docker 이미지로 jenkins 실행

https://velog.io/@mooh2jj/AWS-EC2-Docker-설치 편에서 실행한 방법입니다.
AWS EC2 인스턴스에 Docker를 설치했으면 이제, jenkins 이미지를 run 해봅시다.

docker run \
  --name jenkins-docker \
  -p 9000:8080 -p 50000:50000 \
  -e TZ=Asia/Seoul \
  -v /home/jenkins:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /usr/bin/docker:/usr/bin/docker \
  -u root \
  -d \
  --restart unless-stopped \
  jenkins/jenkins:lts

/var/jenkins_home/workspace 위치

이 위치 꼭 기억하자!
jenkins 내 project 이름의 폴더가 생기고 또 그 안에 git clone한 레포지토리 이름의 폴더 들어간다.

docker exec -it jenkins-docker bash
cd /var/jenkins_home/workspace/{jenkins project 이름}/{git clone한 git 레포지토리 이름}

실제 배포할 프로젝트 내용이 이 안에 있는 것입니다.


Jenkins 컨테이너에 Docker && Docker-compose 설치하기

자, 이제 실행된 Jenkins 컨테이너에 다시 Docker와 Docker-compose를 깔아봅시다.

  • jenkins 컨테이너에 일단 들어갑시다.
docker exec -it jenkins-docker bash
  • docker 설치

참고)
https://velog.io/@mooh2jj/AWS-EC2-Docker-설치


OR 쉡 스크립트 파일로 정리 (권장x)

이런 방식도 있습니다.

jenkins Dockerfile

FROM jenkins/jenkins:jdk11

#도커를 실행하기 위한 root 계정으로 전환
USER root

#도커 설치
COPY docker_install.sh /docker_install.sh
RUN chmod +x /docker_install.sh
RUN /docker_install.sh

#설치 후 도커그룹의 jenkins 계정 생성 후 해당 계정으로 변경
RUN groupadd -f docker
RUN usermod -aG docker jenkins
USER jenkins

docker_install.sh

# jenkins 컨테이너 안에 docker 설치
#!/bin/sh
apt-get update && \
apt-get -y install apt-transport-https \
  ca-certificates \
  curl \
  gnupg2 \
  zip \
  unzip \
  software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce

Jenkins Pipeline 설정

자 이제 설정 완료된 Jenkins 홈페이지에 들어가서 Item 설정 > Pipeline 프로젝트를 만들어줍니다.

배포할 Git 레포지토리를 등록해붑니다. 이 레포지토리는 public으로 credentials 설정이 필요없습니다.

private라면 credentials설정을 꼭 해줍니다.

실습시, 아래와 같은 pipeline Script에 먼저 적어주시고 해주시는 게 하나씩 하나씩 파악하는 것을 추천드립니다!

자 이제 Pipeline 문서(Jenkinsfile)를 가져와야 합니다. 밑에 Script Path는 git 프로젝트 디렉토리 안에서 찾습니다. 저는 프로젝트 루트 바로 하위에 ForJenkins폴더를 하나 더 만들고 그안에 Jenkinsfile를 만들었습니다.

그래서 ForJenkins/Jenkinsfile


Jenkins pipeline

// Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any 	// 사용 가능한 에이전트에서 이 파이프라인 또는 해당 단계를 실행
    stages {
        stage('Checkout') { 
            steps {
                sh 	'echo  git source 다운로드를 수행합니다. -> git clone'
            }
        }
        stage('Build') { 
            steps {
                sh 	'echo "빌드" 단계와 관련된 몇 가지 단계를 수행합니다.'
            }
        }
        stage('Deploy') { 
            steps {
                sh  'echo "배포" 단계와 관련된 몇 가지 단계를 수행합니다.'
            }
        }
    }
}

  • 고급 문법
// Jenkinsfile(선언적 파이프라인)
pipeline { 
    agent any 
    options {
        skipStagesAfterUnstable()
    }
    stages {
        stage('Build') { 
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

실제 작성 jenkinsfile

pipeline {
    agent any 	// 사용 가능한 에이전트에서 이 파이프라인 또는 해당 단계를 실행
    
    stages {
        stage('Checkout') {
            steps {
                git branch: 'master',
                    url: 'https://github.com/mooh2jj/docker-jenkins-pipeline-test2.git'
            }
            
            post {
                success { 
                    sh 'echo "Successfully Cloned Repository"'
                }
                failure {
                    sh 'echo "Fail Cloned Repository"'
                }
            }    
        }
        
        stage('Build') { 
            steps {
            	// gralew이 있어야됨. git clone해서 project를 가져옴.
                sh 'chmod +x gradlew'
                sh  './gradlew clean build'


                sh 'ls -al ./build'
            }
            post {
                success {
                    echo 'gradle build success'
                }

                failure {
                    echo 'gradle build failed'
                }
            }
        }
        stage('Test') { 
            steps {
                echo  '테스트 단계와 관련된 몇 가지 단계를 수행합니다.'
            }
        }
        stage('Docker Rm') {
            steps {
                sh 'echo "Docker Rm Start, docker 컨테이너가 현재 돌아갈시 실행해야함"'
                sh """
                docker stop docker-jenkins-pipeline-test2
                docker rm docker-jenkins-pipeline-test2
                docker rmi -f mooh2jj/docker-jenkins-pipeline-test2
                """
            }
            
            post {
                success { 
                    sh 'echo "Docker Rm Success"'
                }
                failure {
                    sh 'echo "Docker Rm Fail"'
                }
            }
        }
        
        stage('Dockerizing'){
            steps{
                sh 'echo " Image Bulid Start"'
                sh 'docker build . -t mooh2jj/docker-jenkins-pipeline-test2'
            }
            post {
                success {
                    sh 'echo "Bulid Docker Image Success"'
                }

                failure {
                    sh 'echo "Bulid Docker Image Fail"'
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh 'docker run --name docker-jenkins-pipeline-test2 -d -p 8083:8083 mooh2jj/docker-jenkins-pipeline-test2'
            }

            post {
                success {
                    echo 'success'
                }

                failure {
                    echo 'failed'
                }
            }
        }
    }
}

** 결과


pipeline docker-compose 적용

(RDBMS가 연결되어 있으면 오류가 납니다.)
참고 : https://github.com/mooh2jj/board_vue_backend/blob/master/Jenkinsfile

jenkins 내부 컨테이너에서 docker-compose를 돌리기 때문에 docker-compose를 설치해주어야 한다.

  • jenkinsfile
pipeline {
    agent any 	// 사용 가능한 에이전트에서 이 파이프라인 또는 해당 단계를 실행
# git 프로젝트 credentials 는 access token 으로 사용
    stages {
        stage('Prepare') {
            steps {
                git branch: 'master',
                    url: 'https://{git access token}@github.com/mooh2jj/board_vue_backend.git'
            }

            post {
                success {
                    sh 'echo "Successfully Cloned Repository"'
                }
                failure {
                    sh 'echo "Fail Cloned Repository"'
                }
            }
        }

        stage('Build') {
            steps {
            	// gralew이 있어야됨. git clone해서 project를 가져옴.
                sh 'chmod +x gradlew'
                sh  './gradlew --warning-mode=all --stacktrace clean build -x test'


                sh 'ls -al ./build'
            }
            post {
                success {
                    echo 'gradle build success'
                }

                failure {
                    echo 'gradle build failed'
                }
            }
        }
        stage('Test') {
            steps {
                echo  '테스트 단계와 관련된 몇 가지 단계를 수행합니다.'
            }
        }
        stage('Prune Docker data') {
            steps {
                sh 'echo "Prune Docker data"'
                sh 'docker system prune -a --volumes -f'
            }

            post {
                success {
                    sh 'echo "Prune Docker data Success"'
                }
                failure {
                    sh 'echo "Prune Docker data Fail"'
                }
            }
        }

        stage('Docker Build'){
            steps{
                sh 'echo " Image Bulid Start"'
                sh 'docker build . -t mooh2jj/board_vue_backend'
            }
            post {
                success {
                    sh 'echo "Bulid Docker Image Success"'
                }

                failure {
                    sh 'echo "Bulid Docker Image Fail"'
                }
            }
        }

        stage('Docker Push') {
            steps {
                withCredentials([string(credentialsId: 'dockerHubPwd', variable: 'dockerHubPwd')]) {
                    sh "docker login -u mooh2jj -p ${dockerHubPwd}"
                }
                sh 'docker push mooh2jj/board_vue_backend'
            }

            post {
                success {
                    echo 'Docker Push success'
                }

                failure {
                    echo 'Docker Push failed'
                }
            }
        }
        stage('Docker Deploy'){
            steps{
                sh 'docker-compose up -d --build'
                sh 'docker-compose ps'
            }
            post {
                success {
                    echo 'docker-compose success'
                }

                failure {
                    echo 'docker-compose failed'
                }
            }
        }
    }
}

Jenkins pipeline 실행

이 상태에서 Build Now를 하고 파이프라인이 실행됩니다.

이미지상 병렬적으로 실행된다는 것을 알 수 있습니다. 파이프라인 문법에서 stage에 적힌 이름으로 각 job이 문제가 되는지 안되는지 편하게 확인할 수 있는 것이죠.

url 상 확인해보면 잘 되는 것을 확인할 수 있었습니다.



참고

profile
배운 것을 기록합니다.

0개의 댓글