jib 소개
jib는 Google에서 제공하는 Maven/Gradle 플러그인으로, Dockerfile 없이 Java 애플리케이션을 컨테이너 화할 수 있는 도구이다.
Jib(지브)라는 의미에 단어의 의미처럼 돛이 배의 방향을 조정하고 민첩성을 높이듯이, jib 플러그인은 Java 애플리케이션 컨테이너화에서 유연성과 속도를 제공한다는 점에서 이름의 상징성과 기능이 일치한다.
주요 특징은 다음과 같다.
• Docker 환경 불필요: Docker 데몬 없이 컨테이너 이미지를 생성하고, 바로 Docker 레지스트리에 푸시할 수 있음
• 최적화된 이미지 생성: Java 애플리케이션의 디펜던시, 리소스, 코드를 별도 레이어로 관리해 빌드 효율성 극대화.
• 간단한 설정: Maven/Gradle 설정 파일에 몇 줄 추가로 빠르게 통합 가능.
• 빠른 빌드 속도: 변경된 파일만 빌드하여 시간 절약.
Docker 데몬 없이 컨테이너 화하는 원리
jib는 OCI(Open Container Initiative) 스펙을 준수하여 Docker 데몬 없이도 컨테이너 이미지를 생성
- 이미지 레이어 직접 생성: Docker 데몬 없이 Maven/Gradle 플러그인에서 애플리케이션 구성 요소를 기반으로 이미지 레이어와 메타데이터를 생성.
- HTTP API로 레지스트리와 직접 통신: Docker CLI를 사용하지 않고, 표준 레지스트리 API를 통해 이미지를 Docker Hub, GCR, ECR 등에 바로 푸시.
이로써, Docker 설치 및 설정 부담 없이 컨테이너 이미지를 빌드하고 배포 가능하다.
jib 도입 배경
jib 도입의 가장 큰 이유는 Docker Desktop의 유료화(2021년 8월 31일) 였다.
Docker Desktop 유료화로 인해 사내 개발자들이 로컬 환경에서 Docker를 사용하기 어려워졌다. 대안으로 Orbstack(Mac),
Rancher Desktop(Mac/Windows), WSL(Windows) 등이 있었지만, 다음과 같은 문제점이 있었다.
- 러닝 커브 문제: 새로운 Docker 대안 도구에 대한 학습 필요(통일성 x)
- 설정의 복잡성: 레지스트리 인증, SSL 구성, 로컬 개발 환경과의 충돌 문제.
- 비효율성: 로컬 환경과 CI/CD 파이프라인 모두에서 Docker를 관리해야 하는 번거로움.
이러한 이유로, 도커 대안 도구를 사용하는 대신 jib를 통해 개발자들이 좀 더 친숙한 빌드 도구(Maven/Gradle) 기반으로 컨테이너 빌드를 관리하는 것이 더 나은 선택이라는 결론에 도달했다.
jib 설정 및 기본사용
먼저. jib를 사용하기 위해서는 패키지 매니저에 따라 플러그인 설정을 추가해 준다. 이번 포스트에서는 Gradle 위주로 작성한다. Maven 설정을 제외하고는 사용방법이 크게 다르지 않아, maven 사용방법은 공식문서를 참고하기를 바란다.
Maven(pom.xml)
...
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
</plugin>
</plugins>
</build>
....
Gradle(build.gradle)
# build.gradle
plugins {
id 'com.google.cloud.tools.jib' version '3.4.0' // Jib 플러그인 추가
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
...
}
jib(Gradle) 명령어 형식
gradle에서 명령에 형식인 :task에 jib를 붙여주면 된다. 멀티모듈일 경우, 모듈명을 지정해 주면 되고, 모듈명을 지정하지 않을 경우에는 전체 빌드가 된다.
# build everything
./gradlew jib
# build just hello-service
./gradlew :hello-service:jib
jib는 도커파일을 사용하지 않기 때문에, 이미지 빌드 시, 사용헀던 다양한 커스텀 옵션들을 제공한다.
build.gradle과 같은 설정파일에 명세할 수도 있고, 명령어 시, 옵션으로 주입이 가능하다.(명령어의 우선순위가 더 높음)
소스의 의존하지 않고, 파이프라인에서 유동적으로 사용할 수 있는 명령어 옵션으로 사용하는 것을 추천한다.
# 1. 설정파일
# build.gradle
jib {
from {
image = '<빌드_베이스이미지>'
}
to {
image = '<배포_이미지명>'
auth {}
}
conatiner
...
}
# 2. 명령어
./gradlew :<모듈명>:jib \
-Djib.from.image=<빌드_베이스이미지> \
-Djib.to.image=<배포_이미지명>
...
jib 기본 사용
common-api라는 샘플 애플리케이션 모듈을 jib를 통해 빌드하였으며, 이미지 레지스트리로는 Harbor를 사용했다.
테스트 목적으로 SSL 인증 없이 insecure 옵션을 사용했기 때문에, 빌드 시 아래 두 옵션을 추가해야 한다.
-Djib.allowInsecureRegistries=true
: SSL 인증 없이 HTTP 프로토콜로 통신하는 insecure 레지스트리를 사용할 수 있도록 허용
-DsendCredentialsOverHttp=true
: 인증 정보를 전송할 때 HTTP를 통한 비암호화 통신을 허용
./gradlew :common-api:jib \
-Djib.to.image=<레지스트리_주소>/jib-test/common-api:0.0.1 \
-Djib.to.tags=0.0.1\,latest \
-Djib.to.auth.username=<레지스트리_유저명> \
-Djib.to.auth.password=<레지스트리_패스워드> \
-Djib.allowInsecureRegistries=true \
-DsendCredentialsOverHttp=true
빌드 결과 확인
jib는 기본적으로 classpath 기반 ENTRYPOINT를 설정해 Dockerfile 없이도 애플리케이션 실행이 가능하다. Dockerfile에서 설정하던 ENTRYPOINT, 환경 변수 등을 jib 설정으로 대체할 수 있어 높은 유연성을 제공한다. 또한, 레이어 관리로 빌드 속도를 최적화해 변경된 부분만 갱신하므로 빠르고 효율적인 빌드가 가능하다.
harbor에 접속해 보니 이미지 또한 잘 업로드가 된 것을 확인할 수 있다.
jib을 활용한 젠킨스 깃옵스 파이프라인 구성
그럼 이제 Jenkins에서 jib를 활용하여 파이프라인을 구성해 보자.
젠킨스파일 비교하기(Docker vs Jib)
jib를 도입하기 전에는 Dockerfile 작성 과정에서 개발자의 실수(휴먼 에러)가 빈번하게 발생했고, CI 서버(Jenkins)에서 Docker-in-Docker(DinD)를 사용해야 했기 때문에 권한 문제, 버전 충돌 등 다양한 의존성 문제가 있었다. 특히, Kaniko와 같은 대안 도구도 버전 호환성 문제로 자주 충돌이 발생했다.
jib를 도입하면서 Dockerfile 없이 Maven/Gradle 설정 파일로 이미지를 빌드할 수 있게 되어 휴먼 에러가 줄었고, Docker 데몬 의존성을 제거함으로써 CI/CD 파이프라인에서 권한 및 버전 문제를 해결했다. 또한, Jenkinsfile에서 이미지와 프로필 설정을 명시적으로 관리하여 가독성과 일관성이 높아지고, 파이프라인의 안정성과 유지보수성이 크게 개선되었다.
파이프라인 작성
이번 포스팅에서는 jib를 활용한 빌드 프로세스에 중점을 두고, GitOps 방식으로 Kubernetes 환경에 배포되는 파이프라인을 작성한다. 배포 환경은 ArgoCD와 Kubernetes가 이미 구축되어 있다는 전제 하에 진행된다.(배포 관련 내용은 다음 포스팅에서 작성 예정)
0.환경 변수
• MOD_NAME
: 빌드할 모듈의 이름(예: common-api).
• BUILD_STAGE
: 빌드 환경(예: dev, prod 등).
• REGISTRY_URL
: 컨테이너 이미지를 저장할 레지스트리 주소.
• REGISTRY_CREDENTIAL_ID
: 레지스트리 인증에 사용할 Jenkins 크리덴셜 ID.
• GIT_REPOSITORY_URL
: 소스 코드 저장소 URL.
• GIT_BRANCH
: 빌드할 Git 브랜치(예: develop).
1.빌드 단계
• jib를 사용해 Dockerfile 없이 Java 애플리케이션을 컨테이너 이미지로 빌드.
• Git Commit ID를 이미지 태그로 사용해 소스 코드와 배포 버전의 추적성을 확보.
• 생성된 이미지는 레지스트리에 푸시.
2.배포 단계
• GitOps 저장소에서 Helm Values 파일의 이미지 태그를 업데이트.
• 변경 사항이 있을 경우 자동으로 Git 커밋 및 푸시 수행.
• ArgoCD에 따라 Kubernetes에서 새로운 이미지로 배포.
pipeline {
agent any
environment {
MOD_NAME = "common-api"
BUILD_STAGE = "dev"
REGISTRY_URL = "이미지 레지스트리 주소"
REGISTRY_CREDENTIAL_ID = "이미지 레지스트리 크리덴셜 아이디"
GIT_REPOSITORY_URL = "깃 레포지토리 주소"
GIT_CREDENTIAL_ID = "깃 크리덴셜 아이디"
GIT_BRANCH = "develop"
GIT_COMMIT_ID = ""
}
stages {
stage("1. INIT") {
steps {
script {
currentBuild.displayName = "#${BUILD_NUMBER} - ${MOD_NAME} Application Build"
currentBuild.description = "Executed By @${NODE_NAME}"
}
}
}
stage("2. BUILD") {
steps {
script {
echo "[INFO] Starting build process"
dir("tps") {
git branch: GIT_BRANCH,
credentialsId: GIT_CREDENTIAL_ID,
url: GIT_REPOSITORY_URL
def profile = "-Djib.container.environment=SPRING_PROFILES_ACTIVE=${BUILD_STAGE}"
// Get the current commit ID
GIT_COMMIT_ID = sh(script: "git rev-parse HEAD", returnStdout: true).trim()
echo "[INFO] commit_id : ${GIT_COMMIT_ID}"
// jib 이미지 빌드 & 푸시
withCredentials([usernamePassword(credentialsId: 'REGISTRY_CREDENTIAL_ID', usernameVariable: 'REGISTRY_USERNAME', passwordVariable: 'REGISTRY_PASSWORD')]) {
sh """
./gradlew :${MOD_NAME}:jib ${profile} \
-Djib.from.image=${REGISTRY_URL}/library/openjdk:17-ea-33-jdk-buster \
-Djib.to.image=${REGISTRY_URL}/jib-test/${MOD_NAME}:${GIT_COMMIT_ID} \
-Djib.to.auth.username=${REGISTRY_USERNAME} \
-Djib.to.auth.password=${REGISTRY_PASSWORD} \
-Djib.allowInsecureRegistries=true \
-DsendCredentialsOverHttp=true \
-x test
"""
}
}
}
}
}
stage("3. DEPLOY") {
steps {
script {
echo "[INFO] Starting deploy process"
dir("tps_manifest") {
git branch: "main",
credentialsId: GIT_CREDENTIAL_ID,
url: GIT_REPOSITORY_URL_URL
withCredentials([gitUsernamePassword(credentialsId: GIT_CREDENTIAL_ID, gitToolName: 'Default')]) {
sh """
git config user.email "hackjap@devops.com" && git config user.name "hackjap"
// Helm values 파일에서 태그 업데이트
sed -i '/${MOD_NAME}:/,/tag:/s/tag: .*/tag: ${GIT_COMMIT_ID}/' helm-charts/tps-helm/values-${BUILD_STAGE}.yaml
// 변경 사항 확인
if git diff --quiet; then
echo "No changes detected, skipping commit."
else
git add helm-charts/tps-helm/values-${BUILD_STAGE}.yaml
git commit -m "🚚 [${MOD_NAME}/${BUILD_STAGE}] : change by Jenkins : tag : ${GIT_COMMIT_ID}"
git push origin main
fi
"""
}
}
}
}
}
}
post {
always {
cleanWs()
}
}
}
Jenkins 파이프라인 실행 및 확인
작성한 Jenkinsfile을 가지고 젠킨스 파이프라인을 실행한다.
ArgoCD에서도 배포한 이미지의 태그가 GIT_COMMIT_ID로 변경되었음을 확인할 수 있다. 이로써 Docker 없이 jib를 활용하여, 커밋 ID 기반 버전 관리로 GitOps 파이프라인이 잘 동작함을 확인할 수 있다.
결론 및 후기
jib와 GitOps를 활용해 Java 애플리케이션의 빌드 및 배포를 간소화였다. 그러나 jib는 Java에 특화되어 있어 Golang, Python, Node.js 등은 여전히 Docker를 사용 중이며, 빌드 과정을 통일화하는 것이 과제로 남아 있다. 앞으로 Buildpacks 같은 표준화 도구를 도입해 멀티 언어 빌드 환경의 일관성을 강화할 계획이다.
'CICD' 카테고리의 다른 글
Github Actions 기본 사용 방법 및 CI/CD 워크플로우 구성하기 (2) | 2024.12.14 |
---|---|
docker build 시, 빌드 명령어 결과 출력하기(--progress=plain) (0) | 2024.03.18 |
ArgoCD Image Updater를 활용한 Harbor RegistryOps 구축 가이드 (0) | 2023.12.12 |
ArgoCD 외부 EKS 클러스터 연동(Add Cluster) (0) | 2023.12.02 |
사설 이미지 저장소(Harbor)에 컨테이너 이미지 푸시 (0) | 2023.09.02 |