Private

[ezfarm] CI/CD 서버 구축과 배포

highright96 2021. 7. 12.

해당 글의 프로젝트는 다음 URL에서 확인할 수 있다.

프로젝트 링크

 

 

CI/CD 적용 이유

서버 개발이 어느정도 완성되었고 프론트와 기능 연결을 위해 배포의 필요성을 느꼈다. 이전의 프로젝트에서는 EC2에 접속해 프로젝트를 clone 받은 후 jar 파일을 실행해 배포를 진행하였다. 하지만 이 방법은배포 후 추가적인 기능 개발, 버그 수정 등을 위해 위와 같은 과정을 반복해야해 매우 번거로웠다. 이러한 이유로Docker/Jenkins Pipeline을 사용해 CI/CD 서버를 구축하게 되었다.

 

CI/CD 설계

ezfarm 프로젝트의 CI/CD 흐름을 다음과 같이 설계했다.

  1. 깃허브의 release 브랜치에 배포할 코드를 push한다.
  2. 깃허브는 해당 레포지토리와 연결된 Jenkins에 webhook을 보낸다.(EC2 서버에 Jenkins 서버 컨테이너가 실행되고 있다.)
  3. Jenkinsfile이 실행된다. Jenkinsfile의 Pipeline은 다음과 같이 설계했다.
    1. ezfarm 레포지토리의 release 브랜치를 clone한다.
    2. 프로젝트를 빌드한다.
    3. 기존에 띄운 ezfarm 서버 컨테이너와 이미지를 삭제한다.
    4. dockerfile을 기반으로 새로운 이미지를 생성한 후 컨테이너를 실행시킨다.
  4. 배포가 완료되면 배포 완료 메일을 발송한다.
  5. 만약 빌드 또는 배포가 실패하면 실패 메일을 발송한다.

 

CI/CD 구현

1. Amazon EC2 서버 생성

  • Amazon Machine Image(AMI) : Amazon Linux 2 AMI
  • 인스턴스 유형 선택 : t2.micro
  • 인스턴스 세부 정보 구성 : 1대의 서버만 사용하므로 VPC, 서브넷 설정은 건너뛴다.
  • 스토리지 추가 : 서버의 용량은 프리티어 최대 크기인 30GB로 설정한다.
  • 보안 그룹
    • SSH/TCP/22/내 IP 추가(EC2 접속)
    • 사용자 지정 TCP/TCP/8085/내 IP 추가(Jenkins 접속)
    • 사용자 지정 TCP/TCP/8085/192.30.252.0/22, 185.199.108.0/22, 140.82.112.0/20, 143.55.64.0/20 추가(Github의 webhook)
    • TCP/TCP/8080/위치 무관 추가(ezfarm 프로젝트)
  • 고정 아이피(EIP) 할당

 

2. EC2에 Docker 설치

#도커 설치
sudo yum install docker
 
#도커 서비스 실행
sudo service docker start
 
#부팅 시 도커 자동 실행 설정
sudo chkconfig docker on

 

3. Jenkins 도커 이미지 생성 후 컨테이너 실행

#Jenkins 도커 이미지 생성
sudo docker pull jenkins/jenkins:jdk11

#Jenkins 도커 컨테이너 생성
sudo docker run -d -p 8080:8080 --name jenkins -v /home/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -u root [이미지 id]

 

4. Jenkins 컨테이너 접속 후 docker 설치

#Jenkins 도커 컨테이너 접속
sudo docker exec -it [컨테이너 id] /bin/bash

#도커 설치 후 압축 해제
curl -fsSLO https://get.docker.com/builds/Linux/x86_64/docker-17.04.0-ce.tgz
tar xzvf docker-17.04.0-ce.tgz

#bash에서 사용 가능하도록 도커 파일 이동
mv docker/docker /usr/local/bin

 

5. Github Webhook 설정

Repository -> Setting -> Webhooks -> Add Webhook

Payload URL : http://[퍼블릭 IPv4 DNS]/github-webhook/

Content type : application/json

Jenkins 연결 완료

 

6. Jenkins Pipeline 생성과 Github Repository연결

#Jenkins 서버 주소
[퍼블릭 IPv4 DNS]:[Jenkins 포트 번호]

#Jenkins 초기 비밀번호
Jenkins 컨테이너 접속 후 다음 명령어 실행
cat /var/jenkins_home/secrets/initialAdminPassword

 

Jenkins 메인 홈 -> 새로운 Item 선택 -> Pipeline 선택

Do not allow concurrent builds : 동시에 빌드가 안되도록 설정

GitHub Project : 깃허브 레포지토리 URL

Build Triggers -> Github hook trigger for GITScm polling : 깃허브 hook 트리거 설정

SCM : Git으로 설정

Repository URL : 레포지토리 URL

Branch Specifier : release 브랜치에 push되면 빌드하도록 트리거 설정

Script Path : 프로젝트 내의 Jenkinsfile이 실행되도록 설정

 

7. Dockerfile, Jenkinsfile 생성

Dockerfile

FROM adoptopenjdk/openjdk11:alpine-jre

MAINTAINER [이름] <[이메일]>

VOLUME /tmp

EXPOSE 9090

ARG JAR_FILE=build/libs/ezfarm-back-0.0.1-SNAPSHOT.jar

ADD ${JAR_FILE} ezfarm-back.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=dev", "-jar", "/ezfarm-back.jar"]

 

Jenkinsfile

pipeline {
  agent any
  environment {
    CONTAINER_NAME = 'ezfarm-con'
    IMAGE_NAME = 'ezfarm-img'
  }

  stages {
    stage('git pull'){
      steps {
        echo 'Git Pull - Start'
        sh """
        git fetch
        git pull origin master
        """
      }
      post {
        success {
          echo 'Git Pull - Success'
        }
        failure {
          error 'Git Pull - Failure'
        }
      }
    }

    stage('build'){
      steps {
        echo 'Build Start'
        sh """
        chmod 755 ./gradlew
        ./gradlew clean build
        """
      }
      post {
        success {
          echo 'Build Success'
        }
        failure {
          error 'Build Failure -> Stop'
          mail to: 'highright96@gmail.com',
               subject: "Jenkins Failure Build",
               body: "Build Failed."
        }
      }
    }

    stage('deploy'){
      steps {
        echo 'Deploy Start'
        sh """
        docker stop ${CONTAINER_NAME}
        docker rm ${CONTAINER_NAME}
        docker rmi ${IMAGE_NAME}
        docker build -t ${IMAGE_NAME} .
        docker run -d --name ${CONTAINER_NAME} -p 9090:9090 -v /home/jenkins:/var/jenkins_home ${IMAGE_NAME}
        """
      }
      post {
        success {
          mail to: 'highright96@gmail.com',
               subject: "Jenkins Success Deploy",
               body: "Deploy Success"
        }
        failure {
          error 'Deploy Failure -> Stop'
          mail to: 'highright96@gmail.com',
               subject: "Jenkins Failure Deploy",
               body: "Deploy Failed."
        }
      }
    }
  }
}

 

8. 배포 성공

성공적으로 CI/CD 서버를 구축해 배포에 낭비되는 시간을 줄일 수 있었다.

댓글