[Spring Boot] CI/CD w/ Docker + Jenkins in GitLab

CNH·2024년 1월 28일

개발

목록 보기
7/17

작년 말에 Github Actions을 사용해 CI/CD를 구축해 본 경험에 이어, 이번에는 Jenkins를 사용하려고 한다. 다만 이번에는 싸피 환경 상 깃헙이 아닌 GitLab이다.

1. ec2에 docker 및 Jenkins 설치

Docker 안에 Jenkins를 설치하는 느낌이다. 그러기 위해서는 docker을 설치한 다음에 docker pull을 통해 jenkins가 들어있는 이미지를 설치한다.

ec2에 접속 후 sudo apt install -y docker.io등을 이용하여 Docker을 설치한 뒤, groupadd -f docker, sudo usermod -aG docker ubuntu를 통해 현재 사용자 이름(ubuntu)를 docker에다가 사용하겠다고 명시를 해준다.
mkdir jenkins 를 하고 sudo chown 1000 jenkins를 통해 이미지에서 필요한 파일들?을 넣을 폴더를 만들어 주고,
sudo docker run -d -p 9090:8080 -v /jenkins:/var/jenkins_home --name jenkins -u root -v /var/run/docker.sock:/var/run/docker.sock --privileged jenkins/jenkins를 통해 실제 jenkins가 들어있는 도커 이미지를 만들고 실행한다. 스프링 부트 기본포트가 8080이라 jenkins는 9090으로 했다.

  1. 이때, 당연하게도 ec2의 9090포트를 열어줘야 한다.
  2. sudo systemctl enable docker를 통해 docker명령어를 칠 때 sudo를 앞에 꼭 안붙여도 된다.
  3. whoami를 통해 현재 계정명을 알 수 있다. (위에서는 ubuntu)

2. Jenkins 접속 & 초기 세팅

ec2 도커에 설치한 jenkins에 접속 후 초기 세팅을 완료해준다.

ec2에서 docker ps -a를 통해 docker 컨테이너 ID를 확인해 준다. CONTAINER ID컬럼에 있는 숫자와 영문으로 된 문자열이다.
이제 ec2에서 docker logs [도커 컨테이너 ID]를 통해 initialAdminPassword를 확인하여 미리 복사해준다.
크롬 창에 http://IP주소:9090/ 를 통해 젠킨스에 접속을 해주고, 방금 복사했던 비밀번호를 입력한 뒤 권장하는 플러그인을 설치해 초기 세팅을 완료해 준다.
젠킨스에 접속한 후 관리에서 Plugins를 눌러 Git과 Docker, GitLab과 관련된 플러그인들 설치가 안 되어있다면 설치를 완료해주고, System에서 아래 내용을 작성해준다.

이 때, Credential의 경우는 처음에 없으므로 Add를 통해서 만들어 줘야 하는데, GitLab에서 Personal Access Token을 만든 뒤, Credential을 만드는 창에서 Kind를 GitLab API token으로 선택해 API token부분에 방금 전 깃랩에서 만들었던 토큰을 붙여넣어준다. 그러면 오른쪽 밑에 Test Connection버튼을 눌렀을 때 Success가 잘 뜰 것이다.

주의 Connection name은 맘대로지만, GitLab host URL에서는 레포지토리 주소를 다 쓰는게 아닌 앞에 host URL까지만 써야 한다.

3-1. 아이템 생성(Freestyle)

새로운 아이템을 만들고, 깃랩 레포지토리와 연결하여 test를 해본다. 3-1 혹은 3-2 둘 중 원하는 방법으로 선택하면 되며, 필자는 3-2를 선택하여 CI/CD 구축을 완료했으니 참고하길 바란다.

젠킨스 메인페이지에서 New Item을 누르고, 이름은 임의로 써넣은 뒤 Freestyle을 선택한다. 이후 Configure에서 정보를 채워 넣고, Credential은 아까와 다르게 ID/PW 조합을 선택한다. (깃랩 접속 아이디/비번으로 생성). 만약 브랜치를 설정하고 싶다면 */develop처럼 따로 입력해주고,

밑에 빌드 유발에서는 아래와같이 설정해준다. Build when a change is pushed to GitLab. GitLab webhook URL: 옆에 주소를 복사해주자.
또한 아래 고급을 눌러서 secret token을 생성한 뒤 복사해두자.

이제 깃랩 설정으로 넘어가자. 깃랩 프로젝트에서 Settings > Webhooks로 들어가서 Add new webhook을 누른다. 아까 복사한 주소를 URL에 입력하고, Secret token을 입력하고, Push eventsMerge request events에 체크한 뒤, 맨 밑에 Test > Push Test를 눌렀을 때 젠킨스에서 아래처럼 성공이 나오면 일단 연결은 된 듯 하다.

3-2. 아이템 생성(Pipeline)

새로운 아이템을 만들고, 깃랩 레포지토리와 연결하여 test를 해본다.

젠킨스 메인페이지에서 New Item을 누르고, 이름은 임의로 써넣은 뒤 Pipeline을 선택한다. 빌드 유발과 관련해서는 3-1과 똑같이 설정(URL 및 Secret Token 복사)하고, 밑에 Pipeline 탭에서는 아래와 같이 작성하자.

Pipeline script from SCM을 선택하고, 그 밑에 Git선택 후, Repository URL에는 깃랩에서의 해당 프로젝트 주소, Credentials는 ID/PW 조합(없다면 Add하여 생성), 마지막 Branches to build에는 원하는 브랜치를 선택한다. 마지막에 맨 밑에 Script Path에는 기본으로 Jenkinsfile이라고 쓰여 있을텐데 확인하고 넘어가자.


앞으로의 방향성?

  1. FreeStyle을 사용하던, PipeLine을 사용하던, CD가 가능하도록 해야함.
  2. SSH와 pem파일을 사용하여 ec2에 접속하는 과정이 필요한 것 같은데, 지금은 ec2 안에 docker 안에 jenkins가 설치되어 있어서 이 과정이 필요한지 잘 모르겠음.
  3. docker안에 jenkins를 설치했는데, 깃랩 브랜치에 새롭게 머지되면 그 이후로 다시 새로운 이미지를 생성해야 한다. 결국 jenkins 안에 docker를 실행할 수 있어야 한다는 뜻이고, 이 방법이 Docker-in-Docker, Docker-out of-Docker 2가지 방식이 있다고 하는데, 후자가 추천방법이라고 해서 후자를 따라야 할 것 같다. 참고
  4. 다음 용어나 파일들의 역할과 개념을 잘 모르겠다..
docker.sock
DockerFile
deploy_be.sh
Jenkinsfile

4. Jenkinsfile 및 Dockerfile 작성

Jenkinsfile은 pipeline, 즉 실행할 동작들이 모인 스크립트이고, Dockerfile은 스프링 프로젝트에서 .jar파일을 뽑아낸다. 전체적으로 Jenkinsfile에 쓰여 있는대로 동작하면서, docker build을 만나면 Dockerfile이 실행된다.
위에서 docker container에 jenkins image를 받았다. 여기서 우리는 이제 jenkins image에서 docker을 또 실행해야 한다. 이것을 도커 위의 도커, 즉 Docker in Docker라고 한단다. 여기서는 권장되는 Docker out of Docker의 방식을 사용해보자. 아래의 docker_install.sh파일이 그 역할이다. 이렇게 jenkins image에 도커를 설치한 다음, gitlab의 내가 설정한 브랜치에서 파일을 가져와 Dockerfile을 이용해 도커 이미지로 빌드한 다음, 이 이미지를 도커 컨테이너에 담아 실행하면 CI/CD가 완료된다.

스프링 프로젝트 최상단에 아래 스샷처럼 Jenkinsfile, Dockerfile, docker_install.sh 3개의 파일을 만든다.

각각의 파일에 아래와 같은 내용을 작성한다.

Jenkinsfile

pipeline{
    agent any

    stages{
        stage('Build'){
            steps{
                script{
                    sh 'chmod +x gradlew'
                    sh './gradlew clean build'
                    sh 'chmod +x ./docker_install.sh'
                    sh './docker_install.sh'
                }
            }
        }
        stage('Deploy'){
            steps{
                script{
                    sh 'docker build -t jenkins-test .'
                    sh 'docker rm -f jenkins-test'
                    sh 'docker run -d --name jenkins-test -p 8080:8080 jenkins-test'
                }
            }
        }
    }
}

docker_install.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

Dockerfile

FROM openjdk:17-jdk-slim

ARG JAR_FILE=build/libs/*.jar
# jar 파일 복제
COPY ${JAR_FILE} app.jar

# 실행 명령어
ENTRYPOINT ["java", "-jar", "app.jar"]

이렇게 작성하고, 위에서 설정한 브랜치(여기서는 deploy)에 push혹은 다른 브랜치에서 merge를 해보면 자동으로 ec2 서버에 .jar파일이 실행되어 CI/CD가 완료된다.


(추가) 아래는 application.properties 파일을 로컬용/배포용을 구분할 때 쓰는 경우이다. 보통 로컬에서는 로컬DB에서, 배포 시에는 서버DB에 접속하는 데 사용하는 것으로 판단된다.

5. Active Profile 설정

우선 스프링 프로젝트에서 application-prod.properties라는 파일을 만들자. (사실상 기존 application.properties을 복사해서 이름을 살짝 바꿔주면 된다.)

우리는 로컬에서는 application.properties를, 배포 시에는 application-prod.properties를 적용하도록 하게 하고 싶기 때문에, 스프링 프로젝트의 Dockerfile에서 java -jar하는 부분에 아래처럼 작성해보자.

ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar"]

이렇게 작성하면, java -jar을 할 때 spring.profiles.active=prod 설정을 준다는 뜻이다. 이렇게 설정을 하면 자동으로 Dockerfile을 통한 빌드 시에는 application.properties가 아닌 application-prod.properties이 적용되게 할 수 있는 것 같다.


(추가) 아래부터는 환경변수 설정에 관한 내용이다. Jenkins를 이용한 환경변수 설정 방법으로, 필요한 사람만 보면 된다.

5. Jenkins 환경변수 설정


Jasypt는 Spring에서 쓸 수 있는 암호화 관련 툴인 것 같다. 아무튼 이 jasypt를 쓰려면 key가 필요한데, 이 key가 외부에 노출이 되면 안 되기에 젠킨스 환경변수를 이용하고자 한다. 사용하고자 하는 방법은 간단하다. 다시 9090포트로 젠킨스 페이지에 들어가서 Configure - General 에 이 필드는 매개변수가 있습니다.에 체크하고 위와 같이 key-value의 느낌으로 작성하면 된다. 그러면 Jenkinsfile 내에서 ${JASYPT_KEY}와 같이 접근할 수 있다.

6. Spring Project 내 환경변수 설정

위에 기술한 대로 Jenkinsfile 내에서는 ${JASYPT_KEY}로 쉽게 읽을 수 있다. 이제 실제 스프링 프로젝트 내 코드들에서 ${JASYPT_KEY}를 읽게 해주기 위해서는, Jenkinsfile과 Dockerfile을 아래처럼 수정하자.

Jenkinsfile

sh "sed -i 's/\${JASYPT_KEY}/${JASYPT_KEY}/' ./src/main/resources/application-prod.properties"src/main/resources/application-prod.properties 파일에서 ${JASYPT_KEY}${JASYPT_KEY}로 바꾸라는 뜻인데, 첫번째 ${JASYPT_KEY}는 Jenkinsfile에서 가져온 JASYPT_KEY, 두번째 ${JASYPT_KEY}는 문자열 그대로 application-prod.properties에 있는 문자열 ${JASYPT_KEY}를 첫번째, 즉 진짜 jasypt 키로 바꾸라는 뜻이다.

pipeline{
    agent any

    stages{
        stage('Build'){
            steps{
                dir('server'){
                    script{
                        sh 'chmod +x gradlew'
                        sh "sed -i 's/\${JASYPT_KEY}/${JASYPT_KEY}/' ./src/main/resources/application-prod.properties"
                        sh './gradlew clean build'
                        sh 'chmod +x ./docker_install.sh'
                        sh './docker_install.sh'
                    }
                }
            }
        }
        stage('Deploy'){
            steps{
                dir('server'){
                    script{
                        sh 'docker build --build-arg JASYPT_KEY=${JASYPT_KEY} -t backend-jenkins .'
                        sh 'docker rm -f backend-jenkins'
                        sh 'docker run -d --name backend-jenkins -p 8080:8080 backend-jenkins'
                    }
                }
            }
        }
    }
}

Dockerfile

FROM openjdk:17-jdk-slim

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

ENV JASYPT_KEY=$JASYPT_KEY
# jar 파일 복제
COPY ${JAR_FILE} app.jar

# 실행 명령어
ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-DJASYPT_KEY=${JASYPT_KEY}", "-jar", "app.jar"]

application-prod.properties

아래 내용 추가

jasypt.key=${JASYPT_KEY}

이후 다른 jasypt 키를 쓰는 다른 파일들

아래처럼 @를 사용해 접근

@Value("${jasypt.key}")
private String key;

참고 트러블슈팅

profile
끄적끄적....

0개의 댓글