진행 과정
- aws ec2 freetier(1GB ram)에서 docker container로 jenkins를 띄워 초기 설정 및 github과 연결하는 과정을 진행하고자 함
- ubuntu20.04 image로 ec2 instance를 생성해 연결, jenkins는 public repository의 lts version을 이용해서 pull 해서 도커 볼륨 마운트하고 켰다.
- 설치 과정에서 suggested plugin이 설치가 안됨.
- 램 부족이 유력한 원인이라고 생각하며 virtualbox의 4GB ram vm에서 테스트 진행
- 4GB ram에서는 잘 되는 것을 보니 역시 1GB ram 환경에서 컨테이너를 띄워 그 안에서 plugin 설치하는 것이 무리라는 판단.
- ec2 에서 가상 램 2GB를 확보해 다시 진행하니 성공함,
- jenkins 에서 CI(jenkins가 띄워진 컨테이너 내에서 내 github 프로젝트 빌드)과정까지 진행
- 새로운 도커 허브 repository에 5번 스냅샷을 찍어 이미지 push
위와 같이 실습했고, 정리는 아래 순서대로 진행
- ec2 가상 램 확보
- Jenkins lts image pull
- Jenkins설치 및 환경설정
- Jenkins Github 빌드 파이프라인 구축
- 내 도커 허브 repository에 push
EC2 가상 램 메모리 확보
jenkins plugin 설치 및 빌드 과정에 내 vm이 힘들어하기때문에 가상 메모리를 활성화해주자.
아래 명령어로 2G 만큼의 공간 확보 (스왑파일 생성)
**sudo fallocate -l 2G /swapfile**
스왑 파일은 루트 계정만 읽고 쓸 수 있어야 한다.
sudo chmod 600 /swapfile
스왑 영역 설정
sudo mkswap /swapfile
스왑 영역 활성화(swap on)
sudo swapon /swapfile
재부팅 시에도 스왑영역활성화가 되길 원한다면
/etc/fstab파일에 다음을 추가 해주어야 한다.
/swapfile swap swap defaults 0 0
Jenkins lts container pull하여 설치하기
아래 명령으로 public repo pull
docker pull jenkins/jenkins:lts
container 실행
sudo docker run -d -p 8081:8080 -v /jenkins:/var/jenkins_home --name jenkins -u root jenkins/jenkins:lts
옵션 설명
-d : 컨테이너를 데몬으로 실행
-p 8081:8080 : 컨테이너 외부(vm 호스트 장치)와 컨테이너 내부 포트 포워딩
-v /jenkins:/var/jenkins_home : 컨테이너 외부 vm 디렉토리와 컨테이너 내부 디렉토리 도커 볼륨 마운트
—name jenkins : 컨테이너 이름 지정
-u root : 컨테이너를 실행할 계정은 root로 지정
[선택사항] docker-compose 활용하여 실행
도커 볼륨이 잘 마운트되었는지 컨테이너를 죽였다가 띄웠다가 하다 보니 docker run 명령어 치기가 귀찮아서 docker-compose를 활용했습니다.
아래 명령어로 설치
sudo apt install docker-compose
docker run을 실행할 경로에
docker-compose.yml 이름으로 아래 내용 작성
version: "3"
services:
jenkins:
image: jenkins/jenkins:lts
user: root
volumes:
- ./jenkins:/var/jenkins_home
ports:
- 8081:8080
container_name: jenkins
docker-compose.yml이 있는 디렉토리에서 아래 명령어 실행하여 compose 파일을 활용하여 container 실행
sudo docker-compose up -d
jenkins 설치 및 환경설정
jenkins의 initialAdminPassword를 알려면 컨테이너 안으로 들어가봐야한다.
그럼 컨테이너 안으로 들어가려면?
컨테이너 내부로 들어가 bash(쉘 실행) 명령을 수행하는 명령어
sudo docker exec -it jenkins /bin/bash
Jenkins UI에 친절히 나와 있으니, 초기 비밀번호를 출력하여 초기화하자
cat /var/jenkins_home/secrets/initialAdminPassword
jenkins suggested pluggin 설치 후
아래 사진처럼 Jenkins관리 → Script Console에서 서울의 시간으로 설정
System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Seoul')
println 명령은 확인용으로 출력 해봤다.
JDK, Gradle, Git 설정
tools에서
jenkins의 jdk 설정을 위해 vm에서 JAVA_HOME 경로를 찾아 연결해주었다.
jenkins lts docker image를 pull 해서 진행한거기 때문에 경로는 똑같을듯?
git은 건들지 않았다.
gradle은 내 springboot와 버전 맞추기 위해 jenkins의 tool 자동 설치 옵션으로 해당 버전을 지정했다.
내 Springboot 프로젝트가 빌드되는 gradle 버전 확인
Gradle 같은 버전으로 자동 설치 설정
Jenkins 빌드 pipeline 구축
new Item : freestyle vs pipeline
이후 jenkins에서 빌드를 수행하기 위해서 job(item)을 생성해줘야 하는데,
보통 freestyle, pipeline 두 가지로 나뉘어 생성한다.
freestyle이 gui로 빌드 과정의 설정을 진행하고,
pipeline은 코드 기반으로 빌드 과정의 설정을 진행한다.
더 볼 것도 없이 pipeline이 재밌겠지만, pipeline이 더 좋은 이유가 두 개나 더있다.
빌드 과정을 선언하는 코드를 jenkins 내에서 작성하는 것이 아닌, Jenkinsfile 라고 불리는 파일로 관리할 수 있어, Git 등을 통한 빌드 설정의 버전관리 또한 가능.
또한 스테이지 (Stage) 라는 단위로 각 작업에 소요되는 시간, 실패 여부를 시각화하여 확인할 수 있다.
내가 처음 해보다 보니 많은 실패를 겪을 거란 예상이 되고 너무나도 필요한 설정이다.
Scripted vs Declarative
파이프라인도 문법에 따라 크게 Scripted Pipeline과 Declarative Pipeline 두가지로 나뉘는데,
Scripted 문법은 Declarative에 비해 유연성이나 확장성이 높지만, 복잡도와 유지보수 난이도가 더 높다고 한다.
나의 경우는 내 build 및 운영 호스트에 jar 파일 전달 등에 설정 해줄 것이 그렇게 많지 않을 거라는 예감으로, 유연해야할 필요는 없다고 생각, Declarative를 선택했다.
기초적인 CI과정을 진행하기 위한 pipeline 코드는 아래와 같다.
해당되는 부분들을 선언해주어 자동 빌드가 수행되도록 해보자
pipeline{
agent {어떤 jenkins agent가 수행할 수 있는지}
tools{
// 어떤 tool을 사용하여 아래 stage를 수행하는지
gradle {내가 jenkins tools에서 설정한 내 gradle 이름}
}
stages{
//stage 명은 '나 or 팀원이 식별할 수 있는 stage 이름' 으로
stage(branch 지정){
steps{
git branch: {브랜치명}, url: {빌드할 프로젝트 repository.**git**}
}
}
stage(빌드){
steps{
//jenkins container 내부에서 jar 파일이 생길 경로에서 아래 명령어를 수행하라
dir(디렉토리){
sh "gradle init"
sh "echo 'building'"
withGradle{
sh "gradle wrapper build"
}
}
}
}
}
}
아래는 내 pipeline 코드
pipeline{
agent any
tools{
gradle 'gradle-7.6.1'
}
stages{
stage('Git-Clone'){
steps{
git branch: 'dev-be', url: '<https://github.com/BYEONGRYEOL/springbootstudy.git>'
}
}
stage('Build-BE'){
steps{
dir("./backend"){
sh "gradle init"
sh "echo 'building'"
withGradle{
sh "gradle wrapper build"
}
}
}
}
}
}
이렇게 pipeline 코드까지 잘 설정해주면 jenkins에서 빌드 버튼을 눌렀을 때, 알아서 내 github repository에 있는 dev 브랜치에서 빌드를 jenkins가 구동되는 환경에 진행하여 성공하면 jar 파일이 만들어진다.
docker hub 내 repository에 여태까지 진행한 jenkins 설정이 담긴 container 이미지화한 후 push
현재까지 한 작업을 요악하면,
docker hub에 public repository jenkins/jenkins의 lts version을 사용하기 위해 tag를 lts로 지정하여
image를 pull 해왔었다.
docker pull jenkins/jenkins:lts
docker volume을 mount했기에 컨테이너를 종료하고 다시 띄워도 내 jenkins 설정은 호스트 vm에 그대로 남아있다.
이 설정들이 그대로 컨테이너 레이어에 저장되어있고, 컨테이너 종료 후 다시 실행하면 컨테이너 레이어에 저장되었던 설정들은 mount한 경로의 파일들로 불러오게 된다.
먼저 이 jenkins container image를 push할 내 docker hub repository를 생성해준다.
현재 실행중인 container의 이름은 jenkins-sbl로,
아래 명령어로 image화해줘야함
현재 컨테이너의 변경사항을 반영하기 위해 commit 하고싶다면,
먼저 컨테이너를 stop해야한다.
docker stop jenkins-sbl
docker commit jenkins-sbl byeongryeol/{내 repository 이름}:cidone
ci까지만 했다는 느낌으로 tag를 지정해주고
아래 명령어로 image가 잘 생성된 것을 확인한 후
docker images
아래 명령어로 push해줌
docker push byeongryeol/{repository 이름}:cidone
참고 및 트러블 슈팅
[참고] Virtualbox vm에서 jenkins를 docker container로 띄웠을 때 접속하는 방법
virtualbox의 vm 머신의 ip는 특별한 설정 없이는 127.0.0.1로 지정된다. 이 ip로 접속하려고해봐야 vm의 컨테이너로 연결해주지 못한다.
컨테이너를 띄운 후 아래 명령어를 통해 ip를 출력하고
ip a
내 container ip를 확인해야한다. 보통의 경우 10.0.2.*** 일것.
방금 확인한 ip로virtualbox에서 포트포워딩 규칙 지정
[참고] jenkins in container vs jenkins in local
vm 자체에 jenkins를 설치하고 Jenkins를 제대로 활용해보려고하니, Ubuntu에 java를 설치해보고 설치된 Java 경로도 잡아줘야하고 Ubuntu에 gradle을 설치한 후에 경로를 잡아주고, Ubuntu에 git 도 설치해주고…
환경설정할게 너무 많았다. 이렇게 구차하게 구축할리가 없다는 판단하에 검색해보니까
도커를 사용해서 jenkins container를 띄워 사용한다고 한다. 이 생각을 왜 못했지?
VM 로컬에 설치하고 나서 불편을 겪고 jenkins container를 띄워서 설정하는 것이 훨씬 편하다는 것을 깨달았다.
Jenkins에서 spring-boot 프로젝트 빌드 과정중 ./gradlew not found
https://stackoverflow.com/questions/75190780/jenkins-build-failed-gradlew-not-found
Jenkins build failed: gradlew: not found
I want to configure Jenkins 2.375.2 to build gradle project. But when I configure the pipe using Blue Ocean plugin and I run the pipeline I get error: + ./gradlew build /var/lib/jenkins/workspace/
stackoverflow.com
./gradlew 명령어를 사용할 때
stage('Build-BE'){
steps{
dir("./backend"){
sh "./gradlew clean build"
}
}
}
gradlew를 실행할 권한이 없어서 발생할 수 있다.
위 부분을 아래와 같이 변경함
stage('Build-BE'){
steps{
dir("./backend"){
sh "chmod +x gradlew"
sh "gradle wrapper build"
}
}
}
}
be, fe 모두 하나의 jenkins built-in node에서 수행하는 경우 설정
jenkins master, jenkins agent 를 활용하지 않고 node 설정을 하지 않은 경우에 적용
하나의 노드가 두 개의 build를 병렬적으로 수행하는 경우 정말 너무나도 비효율적으로 느릴수가 있다.