스프링부트 + 젠킨스 + Docker + Nginx CICD 무중단 배포 구축

Elmo·2024년 5월 21일
0
post-thumbnail

팀 프로젝트를 진행하면서 젠킨스, Docker를 이용하여 CICD를 구축했다. 이 때 서버 배포 시 중단되는 현상을 방지하기 위하여 Nginx를 이용하여 블루/그린 무중단 배포를 구현했다. 구축 과정에서 다들 밤새고 착오가 굉장히 많아서 그 과정을 기록하려고 한당.

동작 과정

  1. 깃에서 main 브랜치에 push하면 WebHook을 통해 젠킨스에서 자동 빌드
  2. 깃 파이프라인이 작동하여 Gradle 빌드 -> docker 빌드 -> docker hub에 push -> 스프링부트 서버에서 docker hub로부터 가져와 배포 수행
  3. 이때 nginx가 로드 밸런싱 역할을 함. 만약 수행중인 컨테이너가 blue이면 해당 컨테이너를 삭제하고 green에 새로운 컨테이너 실행.
  4. nginx가 Reverse proxy 역할을 하여 사용자의 요청을 현재 열려있는 포트(8081 or 8082)로 연결해줌

간단하게 위와같이 동작한다.
EC2는 총 2개가 필요함. 젠킨스는 깃허브 액션과 달리 따로 서버를 구축해줘야한당.
따라서 젠킨스 서버 + 스프링부트 배포 서버 준비!

젠킨스를 선택한 이유는

  1. 보통 Docker와 같이 사용하므로 파이프라인만 잘 작성한다면 편하게 배포 가능 (근데 이 파이프라인 작성이 꽤 어려움..)
  2. 굉장히 유명하고 자료가 많아서
  3. 깃허브 액션보다 속도가 빠르다
    근데 직접 해보니 간단한 프로젝트는 그냥 깃허브 액션으로 빠르게 구축하는게 나을지도.. (초기 세팅에서 시간 투자를 많이 함)

배포 과정 정리

1. AWS 계정 생성

  • 계정은 젠킨스 서버용, 스프링부트 서버용 총 2개를 만들었다. 프리티어 특성상 하나의 계정에 인스턴스 2개를 사용하면 한계가 있다..

2. EC2 생성

  • Ubuntu 선택 - 프리티어(t2.micro) - 키페어 생성(꼭 저장하고 저장위치 기억) : RSA , ppm -
    22번, 80번, 443번 허용
  • 꼭 볼륨 프리티어 최대 30GB 할당 (안그러면 용량 부족..)
  • 탄력적 IP 할당 (IP 주소 고정)
    탄력적 IP 할당 후 작업 → 탄력적 IP 주소 연결
  • 보안 그룹 인바인드 규칙에서
    젠킨스 서버 : 8080 포트 추가
    스프링부트 서버 : 8081, 8082 포트 추가 (할당할 포트로 추가해야함)

3. 젠킨스 서버 위에 도커, 젠킨스 설치

https://hudi.blog/install-jenkins-with-docker-on-ec2/
위 블로그를 참고하여 설치한다. 근데 docker-compose를 실행시키는 과정에서 다음과 같은 에러가 계속 발생했다.

⚠️ docker.errors.DockerException: Error while fetching server API version: Connection aborted 오류 발생

이 문제는 Docker와 호환되지 않는 버전의 Python requests 패키지로 인한 것으로 보였다. Docker Python SDK가 사용하는 requests 패키지 버전과 호환되는 것을 사용해야함.


위 명령어를 통해 requests 버전을 바꿔준다.

⚠️ ModuleNotFoundError: No module named 'requests'
ERROR: Cannot uninstall urllib3 2.0.7, RECORD file not found. Hint: The package was installed by debian.

  • sudo pip3 install --ignore-installed requests==2.28.1

docker-compose

version: "3"
services:
  jenkins:
    image: jenkins/jenkins:lts
    user: root
    volumes:
      - ./jenkins:/var/jenkins_home
    ports:
      - "8080"

근데 이렇게 해도 해결될 때가 있고 잘 안될 때가 있다..건강한 멘탈을 위해 시도해봤다가 안되면 그냥 명령어를 통해 젠킨스를 실행하도록 하자

4. 젠킨스 파이프라인 구축

  • ec2 퍼블릭 ip:8080 으로 접속 → 젠킨스 홈페이지

근데 이 과정에서 ssh키 설정을 잘못했는지 깃허브와 연결이 되지 않았다.

  • 그냥 ssh말고 username으로 credential kind를 바꿔줌! (아이디는 내 깃허브 아이디)
  • 파이프라인에서 git url을 SSH가 아닌 https로 넣어줌

위의 블로그에서 파이프라인 중 배포 부분만 다르게 적었다.

pipeline {
    agent any

    environment {
        imagename = "elmo3356/nginx-cicd"
        registryCredential = 'docker-hub'
        dockerImage = ''
    }

    stages {
stage('Prepare') {
          steps {
            echo 'Clonning Repository'
            git url: 'https://github.com/HideOnCodec/springCICD.git',
              branch: 'main',
              credentialsId: 'HideOnCodec'
            }
            post {
             success {
               echo 'Successfully Cloned Repository'
             }
           	 failure {
               error 'This pipeline stops here...'
             }
          }
        }
stage('Unit test') {
            steps {
                sh 'chmod +x ./gradlew'
                withGradle {
                    sh '''
                        echo test start    
                        ./gradlew test
                    '''
                }
            }
				}
stage('Bulid Gradle') {
          steps {
            echo 'Bulid Gradle'
dir('.'){
								sh 'chmod +x ./gradlew'
                sh './gradlew clean build'
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }

stage('Bulid Docker') {
          steps {
            echo 'Bulid Docker'
            script {
                dockerImage = docker.build imagename
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }

stage('Push Docker') {
          steps {
            echo 'Push Docker'
            script {
                docker.withRegistry( '', registryCredential) {
                    dockerImage.push("latest")
                }
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }

stage('Docker Run') {
            steps {
                echo 'Pull Docker Image & Docker Image Run'
                sshagent (credentials: ['ssh']) {
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker pull elmo3356/nginx-cicd'"
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker ps -aqf name=elmo3356/nginx-cicd | xargs -r docker rm -f'"
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker run -d --name youthTalkTalk -p 8080:8080 elmo3356/nginx-cicd'"
                }
            }
        }
    }
    
}
    

스프링부트 서버에서 Nginx 설치 및 설정

Blue Green 무중단 배포 적용하기 (velog.io)

위 블로그를 참고했다.

  • docker 설치 후 docker-compose 설치
$ sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

$ sudo chmod +x /usr/local/bin/docker-compose

$ docker-compose --version
  • deploy.sh 작성
    위의 블로그와 거의 똑같이 작성했지만 다른 부분이 있다.
DEFAULT_CONF_PATH="/etc/nginx/conf.d"
DEFAULT_CONF="service-url.inc"

...

switch_conf() {
    cp "${DEFAULT_CONF_PATH}/${AFTER_COMPOSE_COLOR}.inc" "${DEFAULT_CONF_PATH}/${DEFAULT_CONF}"
    nginx -s reload
}
  • docker-compose.green.yml , docker-compose.blue.yml 작성
  • docker-compose.green.yml
services:
  api:
    image: 도커허브아이디/레포지토리:latest
    container_name: green
    ports:
      - 'green에 해당하는 포트번호:8080'
  • docker-compose.blue.yml
version: '3.1'

services:
  api:
    image: 도커허브아이디/레포지토리:latest
    container_name: blue
    ports:
      - 'blue에 해당하는 포트번호:8080'

nginx 프록시 역할을 잘 수행하도록 추가 설정을 해야한다.

  • sudo vim /etc/nginx/sites-enabled/default 에 두 줄 추가
include /etc/nginx/conf.d/service-url.inc;
proxy_pass $service_url;
...
server_name _;

        include /etc/nginx/conf.d/service-url.inc;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                proxy_pass $service_url;
        }
...
set $service_url http://127.0.0.1:8081;
  • /etc/nginx/conf.d/ 에 blue.inc 생성
set $service_url http://127.0.0.1:blue 포트번호;
  • /etc/nginx/conf.d/ 에 green.inc 생성
set $service_url http://127.0.0.1:green 포트번호;

스프링부트 프로젝트 생성 후 테스트

DockerFile

FROM openjdk:17-jdk

VOLUME /tmp

ARG JAR_FILE=./build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["sh", "-c", "java -Dserver.port=${PORT} -Dspring.profiles.active=${PROFILE} -jar /app.jar"]

간단히 test 할 수 있는 컨트롤러 작성

profile
엘모와 도지는 즐거워

0개의 댓글