[완] 개인서버 개발/공통 서비스 개발(완)

#2 Jenkins를 통한 CI/CD 환경 구축 with docker

북극곰은콜라 2023. 3. 17. 08:11
반응형

 


개요

Jenkins는 개발자의 CI/CD를 지원해주는 서비스이다.

Jenkins를 통해 빌드 및 배포환경을 구성할 것이다.

편리한 관리를 위해서 Jenkins는 docker로 설치한다.

 


CI / CD Flow

CI

소스는 깃헙으로 관리하며

젠킨스를 통해 빌드를 진행하고

도커허브에 빌드된 이미지를 관리한다.

(+추후 테스트 자동화도 넣을 수 있으면 좋을 듯)

CD

jenkins를 통해 배포할 타겟 설정을 하고

도커허브에서 이미지를 받아와서 deploy한다.

 


Docker 설치

// 설치
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

// 확인
sudo docker run hello-world

 


Jenkins 설치

// jenkins key 등록
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

// jenkins 설치
sudo apt-get update
sudo apt-get install jenkins

// jenkins 실행
sudo systemctl daemon-reload
sudo systemctl start jenkins
sudo systemctl status jenkins

// 포트 확인
sudo lsof -i -P -n | grep jenkins

// 방화벽 설정
sudo iptables -A INPUT -p tcp --dport [ip] -j ACCEPT
sudo netfilter-persistent save
sudo netfilter-persistent reload

전 추후에 사용 가능하도록 쉘스크립트로 작성했으며, 웹 포트도 8080에서 다른걸로 변경했습니다

관련 설정은 /lib/systemd/system/jenkins.service 에서 할 수 있다.

설정 후 데몬 리로드 하고, 서비스 재시작하면 적용된다.

 


Jenkins 초기 설정

[서버ip]:[포트] 로 접속하면 젠킨스 초기설정 화면이 나옵니다.

jenkins home 디렉토리에 secrets 폴더안에 initialAdminPassword 안의 글자를 넣으면 됩니다.

추천 plugins로 install하면 적당히 사용할정도로 설치가 가능합니다.

설정에 시간이 쫌 걸립니다.

관리자 아이디를 생성합니다.

email로 알림 같은걸 받을 시 jenkins로 접근하는 URL 설정입니다.

이 후 완료됩니다.

젠킨스는 외부에서 접근할 이유가 없기 때문에 nginx 설정 등은 패스합니다.

 


CI /CD 환경 구성

Jenkins ↔ Github 연동

github -> login -> settings -> developer settings -> personal access tokens -> generate

이름과 권한을 주고 토큰을 생성한다.

권한은 repo와 admin:repo_hook 권한이 필요하다.

젠킨스 -> jenkins 관리 -> configure System의 github 항목에 AddGitHub Server로 깃헙 연결을 추가한다.

kind에 Username with password를 선택하고

password에 방금 발급받은 github access token을 입력한다.

username에 github id를 넣는다

id와 desc는 적절한 값으로 설정한다

 


DockerHub 세팅

https://hub.docker.com/

도커 허브로가서 가입을 합니다.

가입 & 로그인 후 Account Settings로 갑니다.

github이랑 유사하게 Security에서 access token을 발급 받습니다.

username with password로 username에 아이디, password에 토큰을 넣습니다.

 


Test 서버 구현

간단하게 Spring Boot로 컨트롤러 하나만 구현합니다.

 

Dockerfile

FROM openjdk:11
ARG JAR_FILE=./build/libs/*.jar
COPY ${JAR_FILE} ./app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Spring boot를 openjdk 11로 빌드하는 dockerfile 입니다.

 

Jenkinsfile

pipeline {
  agent any
  options {
    buildDiscarder(logRotator(numToKeepStr: '3'))
  }
  environment {
    DOCKERHUB_CREDENTIALS = credentials('dockerhub')
    repository = "pbear41/simple_spring_test"
    dockerImage = ''
  }
  stages {
    stage('Gradle Build') {
      steps {
        sh './gradlew clean build'
      }
    }
    stage('Docker Build') {
      steps {
        script {
            dockerImage = docker.build repository + ":$BUILD_NUMBER"
        }
      }
    }
    stage('Login') {
      steps {
        sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
      }
    }
    stage('Push') {
      steps {
        sh 'docker push $repository:$BUILD_NUMBER'
      }
    }
  }
  post {
    always {
      sh 'docker logout'
    }
  }
}

options -> buildDiscarder: 빌드되는 산출물을 3번째까지만 유지합니다.

environment -> dockerhub credential을 지정합니다.

stage

  1. gradle 빌드
  2. docker 빌드
  3. 로그인
  4. dockerhub로 푸시

post: 로그아웃

 


CI Jenkins Job (pipeline) 생성

Multibranch Pipeline 으로 생성합니다.

아까 만든 github credential을 선택하고, github 주소를 넣고 save합니다.

repository를 스캔해서, Jenkinsfile을 찾아서 구성합니다.

SUCCESS가 뜨면 성공입니다.

아마 자동으로 빌드를 진행하고 dockerhub로 push 까지 진행될것입니다.

도커 허브에 이미지가 잘 생성된 것을 확인할 수 있습니다.

 


CD Jenkins Job 생성

Job 생성에서 pipline 생성

태그를 통해 버전을 받아서 cd할 버전을 선택할 수 있도록 argument를 받는다.

pipeline {
    agent any
    
    stages {
        stage('docker pull') {
            steps {
                sh 'docker pull pbear41/simple_spring_test:$docker_image_tag'
            }
        }
        stage('stop prev container') {
            steps {
                script {
                    try {
                        sh 'docker stop simple_spring_test'
                        sh 'docker rm simple_spring_test'
                    } catch (Exception e) {
                        echo 'no prev container'
                    }
                }
            }
        }
        stage('run container') {
            steps {
                sh 'docker run -d --name simple_spring_test -p 40000:40000 pbear41/simple_spring_test:$docker_image_tag'
            }
        }
    }
}

pipeline을 구성하고 save한다.

docker 이미지를 받고

기존 컨테이너를 종료하고

받은 이미지로 컨테이너를 띄우는 스크립트이다.

파라미터와 함께 빌드 후 성공하길 기도한다.

 

도커 컨테이너가 뜬 걸 확인했고

curl --location --request GET 'http://127.0.0.1:40000/test'
{"result":"ok"}

api도 정상동작하는걸 확인했다 ㅎㅎ

 

+TODO

  • lastest 태그 입력 시 도커허브 마지막으로 빌드된 이미지 태그를 가져와서 세팅하는 작업
  • pull 받은 이미지들 관리

 


마치며

생각보다 시행착오가 많았다.

우선 도커에 젠킨스를 설치했던것이 문제가 되었었다.

docker내에서 외부 docker 명령어를 실행 할 수 있도록 세팅까지 했는데

결국 jenkins(container) 내부에서 사용하는 ubuntu lib의 버전이 호스트보다 낮아서 정상동작하지 않았다...ㅠㅠ

결국 도커에 설치한 젠킨스를 지우고 호스트에 설치를 다시 하는 방법을 택했다.

 

실제로 프로젝트를 진행하면 이보다는 쫌 더 세련되도록 고민해야한다.

특히 서버가 여러개로 구분되고, 빌드전용서버, 아티펙토리 등 고려사항이 많아질 경우에 대해서도 고민이 필요하다.

 

이번 작업을 통해서 회사내에 CI / CD가 생각보다 주먹구구식으로 짜여진 경우가 많은 것을 느끼게 되버렸다...

 

CI / CD 구성은 이 정도에서 마무리하고 다음은 실질적인 프로젝트 구현으로 갈 듯하다.

 


REFERENCE

https://sweetcode.io/how-to-build-and-push-docker-images-to-docker-hub-using-jenkins-pipeline/

https://www.jenkins.io/doc/book/installing/

 

 

 

반응형