Docker와 Jenkins를 통한 CI/CD 구축(GIt Branch 전략 수정 후)

박세건·2024년 9월 11일
0

기술 실습

목록 보기
7/18
post-thumbnail

깃 브랜치 전략을 수정하게된 이유 작성


내가 담당한 notification 브랜치를 통해서 CI/CD를 구현하는 방식으로 진행
브랜치를 나눠서 작업하기에 해당 브랜치의 코드를 나눴기에 push가 발견되면 바로 진행할 수 있어짐

CI/CD 과정

마이크로서비스를 담당하는 각 브랜치들 마다 각각의 Item을 만들어주고 Pipeline Script를 작성해서 적용시킬 계획

Webhook 연결

  1. Item 생성(Pipeline)
  2. Build Trigger 설정
  3. Pipeline 작성(우선 클론만 작성해서 테스트)
  4. Gitlap Repository에서 Webhook 설정
  5. PUSH 테스트

    정상적으로 Success 확인

Docker를 통한 CI/CD 구축

이전에 클론에 대한 내용만 작성한 pipeline을 Docker를 적용시켜 이미지화하고 컨테이너화 시켜서 배포까지 진행

새로운 Credentials 생성

  • Kine : SSH Username with private key
  1. Build 과정을 통해서 jar 파일 생성
    2 dockerfile을 통해서 jar파일을 Docker 이미지화
  2. 이전에 사용되고있던 Docker 이미지를 제거하고 최신화
  3. Docker Image를 Run 시켜서 컨테이너로 구동

Dockerfile 작성

Springboot 파일을 Build 시킨 jar 파일을 Docker Image로 만들 Docker file 작성

FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY build/libs/notification-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8083
ENTRYPOINT ["java", "-jar", "app.jar"]
VOLUME ["/data"]

Docker Image를 만들어줄 Script 작성

pipeline {
    agent any



    stages {
        stage('Repository clone') {
            steps {
                sh 'pwd'
                git branch: 'notification', credentialsId: 'qkrtprjs', url: 'https://lab.ssafy.com/s11-fintech-finance-sub1/S11P21A604.git'
            }
            post {
                failure {
                  echo 'Repository clone failure !'
                }
                success {
                  echo 'Repository clone success !'
                }
            }
        }
         stage('Build image') {
            steps {
                // dir('[디렉토리명]) {
                    
                    sh 'chmod +x ./gradlew'
                    sh './gradlew build'
                    sh 'pwd'
                    sh 'docker build -t qkrtprjs/notification .'
                // }
                echo 'Build image...'
            }
            post {
                failure {
                  echo 'Build image failure !'
                }
                success {
                  echo 'Build image success !'
                }
            }
        }

        // stage('Remove Previous image') {
        //     steps {
        //         script {
        //             try {
        //                 h 'docker stop [docker 이미지 이름]'
        //                 sh 'docker rm [docker 이미지 이름]'
        //             } catch (e) {
        //                 echo 'fail to stop and remove container'
        //             }
        //         }
        //     }
        //     post {
        //         failure {
        //           echo 'Remove Previous image failure !'
        //         }
        //         success {
        //           echo 'Remove Previous image success !'
        //         }
        //     }
        // }
        // stage('Run New image') {
        //     steps {
        //         sh 'docker run --name [docker hub 계정]/[docker 이미지 이름] -d -p [포트번호]:[포트번호] [docker hub 계정]/[docker 이미지 이름]'
        //         echo 'Run New member image'
        //     }
        //     post {
        //         failure {
        //           echo 'Run New image failure !'
        //         }
        //         success {
        //           echo 'Run New image success !'
        //         }
        //     }
        // }
    }
}

해당 코드를 통해서 이미지가 생성되는지를 확인

트러블 슈팅

+ docker build -t qkrtprjs/notification .
/var/jenkins_home/workspace/notification@tmp/durable-6398b670/script.sh.copy: 1: docker: not found

도커를 빌드하는 과정에서 도커를 찾지 못한 에러 발생

  • 원인 : 도커를 통해서 Jenkins를 실행시켰기에 EC2에 있는 Docker에 대한 권한은 Jenkins가 갖지 못함

    • EC2 -> Docker -> Jenkins 구조이기에 Jenkins는 Docker에 대한 내용을 갖고있지 않았음

      위 문제를 해결하기 위해 도커안에서 도커를 사용할 수 있는 방식은 크게 두가지가 존재한다.

      • Docker in Docker(DinD)
        컨테이너 내부에서 도커 컨테이너를 생성하는 방법
        • 하지만 이 방법은 두 개의 Docker 데몬을 실행하게 되며, 이로 인해 성능 저하가 발생하고 호스트와 컨테이너 간의 보안 경계가 약해질 수 이어서 보안적인 측면으로 권장되지 않는다고 한다.
      • Docker out of Docker(DooD)
        컨테이너 내부에서 Docker 데몬에 접근(도커 소켓을 사용해서)하여 Docker 명령어를 실행할 수 있도록 함
        도커소켓이란, Docker 데몬과 클라이언트 간의 통신을 담당합니다. Docker 클라이언트(예: Jenkins)는 이 소켓을 통해 Docker 데몬에 명령을 전달하고, Docker 데몬은 컨테이너를 관리하는 역할을 합니다.
  • 시도 1 : DooD 선택

    • docker-compose.yml 수정

      version: '3'
      services:
      jenkins:
      image: jenkins/jenkins:lts
      ports:
        - "8080:8080"
      volumes:
        - "/var/run/docker.sock:/var/run/docker.sock"
        - "/home/ubuntu/jenkins-data:/var/jenkins_home"
      user: "root"
      tty: true
      restart: always
    • 실행시킨 후에 docker exec -it <컨테이너_ID> /bin/bash 명령어로 jenkins 컨테이너에 접속해서 docker ps로 명령어를 사용했지만 여전히 docker not found 발생

      • 도커 소켓을 추가해서 docker 데몬을 컨테이너 내부에서는 갖고있지만, docker 데몬을 실행할 수 있는 도구가 없어서 문제가 발생했다. 때문에 Docker 내부에서 데몬에게 명령을 보낼 수 있는 Docker CLI를 설치해보자
  • 시도 2 : Docker CLI 설치

# jenkins container 접속
  docker exec -it jenkins /bin/bash
  # linux 버전 확인
  cat /etc/issue
  # Docker 설치
  ## - Setup Repo
apt-get update
apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
    
    
mkdir -p /etc/apt/keyrings

#linux 버전에 맞게 설치
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

## - Install Docker Engine
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  • Jenkins 컨테이너에 접속해서 Docker CLI를 설치하는 script
  • 위 과정을 통해서 CI/CD 파이프 라인을 진행했을때에 정상적으로 진행되는 것을확인,
    하지만, 다시 Jenkins의 재부팅을 실행하면 다시 원래상태로 돌아감.
    이와 같이 매번 Jenkins를 컨테이너화 할때마다 해당 코드를 직접 실행시켜주는 것을 비효율적이라고 생각
    시도 3 : Docker CLI를 설치하는 코드를 docker-compose를 실행시킬때 같이 실행되도록 구현
    • 결과
      우선적으로 프로젝트 진행을 위해 따로 sh파일을 작성한뒤 docker-compose.yml파일을 작성할때 jenkins에 마운트를 통해서 해당파일을 실행시킬 수 있게 설정하는 것이 최선이라고 생각
  # /jenkins-docker-install.sh

apt-get update
apt-get install ca-certificates curl gnupg

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null

apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

해당 sh 파일을 작성하고

volumes:
      - ./mount/jenkins:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - ./mount/jenkins-html:/var/lib/jenkins
      - ./jenkins-docker-install.sh:/jenkins-docker-install.sh

볼륨 마운트 설정

docker-compose up -d
docker exec -it jenkins bin/bash
sh /jenkins-docker-install.sh

로 컨테이너안에서 해당 sh 문 실행해서 docker 명령어 인식가능

추가적인 자동화는 배포를 완료한 후에 따로 학습해보자

생각해볼 것

  1. 각각의 Pipreline Script가 중복되는 내용일텐데 하나의 Item으로 해결할 수 있는 방법이 있을지
  2. 현재 docker-compose를 통해서 jenkins를 띄우고 따로 Jenkins안에서 Docker를 사용하기 위해서 Docker outside of Docker 방식으로 설정해주고 있는데 매번 재실행 시킬때마다 해당 코드를 작성해줘야 하는 불편함을 해결할 수 있는 방법이 있을지

profile
멋있는 사람 - 일단 하자

0개의 댓글