작년 말에
Github Actions을 사용해 CI/CD를 구축해 본 경험에 이어, 이번에는Jenkins를 사용하려고 한다. 다만 이번에는 싸피 환경 상 깃헙이 아닌GitLab이다.
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으로 했다.
- 이때, 당연하게도 ec2의
9090포트를 열어줘야 한다.sudo systemctl enable docker를 통해docker명령어를 칠 때sudo를 앞에 꼭 안붙여도 된다.whoami를 통해 현재 계정명을 알 수 있다. (위에서는ubuntu)
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까지만 써야 한다.
새로운 아이템을 만들고, 깃랩 레포지토리와 연결하여 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 events와 Merge request events에 체크한 뒤, 맨 밑에 Test > Push Test를 눌렀을 때 젠킨스에서 아래처럼 성공이 나오면 일단 연결은 된 듯 하다.

새로운 아이템을 만들고, 깃랩 레포지토리와 연결하여 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이라고 쓰여 있을텐데 확인하고 넘어가자.
docker.sock
DockerFile
deploy_be.sh
Jenkinsfile
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개의 파일을 만든다.

각각의 파일에 아래와 같은 내용을 작성한다.
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'
}
}
}
}
}
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
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에 접속하는 데 사용하는 것으로 판단된다.
우선 스프링 프로젝트에서 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를 이용한 환경변수 설정 방법으로, 필요한 사람만 보면 된다.

Jasypt는 Spring에서 쓸 수 있는 암호화 관련 툴인 것 같다. 아무튼 이 jasypt를 쓰려면 key가 필요한데, 이 key가 외부에 노출이 되면 안 되기에 젠킨스 환경변수를 이용하고자 한다. 사용하고자 하는 방법은 간단하다. 다시 9090포트로 젠킨스 페이지에 들어가서 Configure - General 에 이 필드는 매개변수가 있습니다.에 체크하고 위와 같이 key-value의 느낌으로 작성하면 된다. 그러면 Jenkinsfile 내에서 ${JASYPT_KEY}와 같이 접근할 수 있다.
위에 기술한 대로 Jenkinsfile 내에서는 ${JASYPT_KEY}로 쉽게 읽을 수 있다. 이제 실제 스프링 프로젝트 내 코드들에서 ${JASYPT_KEY}를 읽게 해주기 위해서는, Jenkinsfile과 Dockerfile을 아래처럼 수정하자.
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'
}
}
}
}
}
}
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"]
아래 내용 추가
jasypt.key=${JASYPT_KEY}
아래처럼
@를 사용해 접근
@Value("${jasypt.key}")
private String key;
참고 트러블슈팅