DevOps/DevOps

Docker 데몬없이 Jib로 Docker 이미지 빌드하기

hyomee2 2026. 1. 23. 03:43

 

백엔드 애플리케이션을 배포할 때 주로 Dockerfile를 작성하고 docker build로 이미지를 생성하는 방식이 주로 사용된다.

하지만 CI 환경에서 Dockerfile 기반 빌드는 몇 가지 구조적인 한계를 가지고 있다. 

이러한 문제를 해결하기 위해 이번 프로젝트에서는 Google Jib을 사용해 Docker 데몬 없이 이미지를 빌드하는 방식을 선택했다.

 


Dockerfile 기반 빌드의 한계

1. Docker 데몬 의존성

Dockerfile 방식은 기본적으로 Docker 데몬이 반드시 필요하다.

즉, CI 서버에도 Docker가 설치돼 있어야 하고, 내부적으로는 Docker 데몬에 접근할 수 있는 권한이 필요하다.

문제는 Docker 데몬 특성상

  • 대부분 root 권한을 요구
  • CI 환경에서 보안 리스크 증가

라는 구조적인 보안 문제가 있다는 것이다.

2. 비효율적인 레이어 캐시

Dockerfile 방식은 이미지 레이어 캐시를 개발자가 직접 설계해야 한다.

 작성하면 코드 한 줄만 바뀌어도 

  • 전체 레이어가 다시 빌드되고
  • 의존성 다운로드까지 모두 다시 실행된다.

결과적으로,

  • 불필요한 빌드가 반복되고
  • CI 빌드 시간이 길어지고
  • 캐시 전략을 잘못 짜면 성능이 급격히 나빠질 수 있다.

Jib란 무엇인가?

Jib는 Google에서 만든 JVM 전용 컨테이너 이미지 빌드 도구이다.

Jib는 Docker 데몬 없이 Gradle/Maven 빌드만으로 바로 Docker 이미지를 생성한다.

즉, Dockerfile, Docker 데몬, docker build 전부 없이 바로 레지스트리로 이미지를 push할 수 있다.

Docker 빌드 흐름
Jib 빌드 흐름

 


 

Jib를 선택한 이유

1. Docker 데몬 없이 빌드 가능

Jib는 Docker 엔진을 전혀 사용하지 않는다. 

또한 Dockerfile 방식과 다르게 Docker 설치, root 권한이 불필요하며, JVM 빌드만 필요하기 때문에,

보안적으로 훨씬 안전하며 CI 환경 구성도 단순하게 할 수 있다는 점이 이점이었다.

2. Gradle 설정만으로 이미지 생성

Jib는 Dockerfile 없이 간단한 Gradle 플러그인 설정만으로 이미지 빌드 및 레지스트리 push까지 가능하다.

3. 자동 레이어 분리 

Jib는 애플리케이션을 아래 세가지 레이어로 자동 분리한다.

  • Dependencies layer
  • Resources layer
  • Classes layer

Jib는 Dockerfile처럼 전체 이미지가 다시 빌드되는 것이 아니라 아래와 같이 변경된 레이어만 다시 빌드하므로

빌드 속도가 크게 향상되며, 특히 의존성이 많은 Spring 프로젝트에서 효과가 극대화된다.

변경 내용 다시 빌드되는 layer
Java 코드 수정 Classes layer만
리소스 수정 Resources layer만
의존성 변경 Dependencies layer만

 


Jib 설정하기

아래는 프로젝트에서 사용한 Jib 설정이다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.4'
    id 'io.spring.dependency-management' version '1.1.4'
    id 'com.google.cloud.tools.jib' version '3.4.4'
}

jib {
    from {
        image = 'eclipse-temurin:17-jre-alpine'
    }

    to {
        image = "${System.getenv('ECR_REGISTRY')}/kareer-server"

        tags = [
                System.getenv('GITHUB_SHA')?.take(7)
        ]
    }

    container {
        ports = ['8080']
        jvmFlags = [
                '-Duser.timezone=Asia/Seoul'
        ]
        creationTime = 'USE_CURRENT_TIMESTAMP'
    }
}

해당 코드에 대해 블록 단위로 살펴보자.

1. 베이스 이미지 설정

from {
    image = 'eclipse-temurin:17-jre-alpine'
}

베이스 이미지를 설정한다.

베이스 이미지는 Eclipse Temurin JRE 17(Alpine 기반)을 사용했다.

  • 17-jre: JDK가 아닌 JRE만 포함하여 이미지 크기를 감소시킨다.
  • alpine: 경량 리눅스 배포판

즉, Spring Boot 런타임에 필요한 최소 환경만 이미지에 포함했다.

 

Dockerfile을 사용할 경우

FROM openjdk:17-jre-alpine

와 동일한 역할을 Jib가 수행한다.

 

2. Push 대상 레지스트리 (ECR)

이미지를 Push할 레지스트리를 설정한다.

우리 프로젝트에서는 ECR을 사용했고,

혹시 롤백 등 특정 커밋으로 복원해야할 경우를 위해

GitHub Actions에서 제공하는 GITHUB_SHA의 앞 7자리를 이미지 태그로 붙여주었다.

    to {
        image = "${System.getenv('ECR_REGISTRY')}/${System.getenv('ECR_REPOSITORY')}"

        tags = [
                System.getenv('GITHUB_SHA')
        ]
    }

 

3. 컨테이너 런타임 설정

컨테이너 메타데이터에 애플리케이션이 사용하는 포트 정보와 타임존을 명시, 이미지 생성 시간을 기록한다.

서버 환경에 따라 타임존이 UTC로 잡히는 경우도 있어 운영 환경과 실제 시간을 일치시키기 위해 명시적으로 고정해주었다.

생성 시간은 이미지 생성 시간으로, 빌드 시점을 기준으로 기록한다.

    container {
        ports = ['8082']
        jvmFlags = [
                '-Duser.timezone=Asia/Seoul'
        ]
        creationTime = 'USE_CURRENT_TIMESTAMP'
    }

우리 프로젝트는 Blue-Green 배포로 운영 환경에서 호스트 포트가 8082이기에 8082로 지정해주었다.

 

 

[참조 자료]

1. https://devocean.sk.com/blog/techBoardDetail.do?ID=165045

 

프런트/백엔드 코드를 Docker 데몬 없이 Jib로 컨테이너 이미지 빌드하기

 

devocean.sk.com

 

'DevOps > DevOps' 카테고리의 다른 글

[DevOps] 1. DevOps  (1) 2024.11.15