-
Maven, Gradle 도커에서 빌드과정 최적화하기, Gradle편Computer Science/Java 2025. 6. 1. 16:15
Docker에서 Gradle 빌드를 최적화하는 전략
Gradle은 빌드 캐싱을 잘 지원하는 빌드 도구지만, Docker 환경에서는 Maven과 같은 방식으로 의존성 설치부분만을 분리하여 캐시를 적용하기가 쉽지 않다.
Maven은 의존성 설치부분을 Dockerfile에서 분리하여 복사함으로써 의존성 설치 단계를 Docker 레이어에 고정할 수 있었지만, Gradle은 내부적으로 태스크 기반 증분 빌드와 캐시 매커니즘을 사용하기 때문에 Docker 레벨에서 명확한 의존성 설치 부분 분리가 어렵다.
그럼에도 불구하고, Docker 환경에서도 Gradle 빌드 시간을 최적화할 수 있는 방법들이 존재한다. 오늘은 그 방법들을 살펴보려고 한다.
Gradle의 캐시 전략은 의존성 설치를 분리하기가 어렵다
Gradle은 build.gradle 또는 settings.gradle 파일이 변경되거나, 의존성 트리가 바뀌면 전체 태스크의 재평가가 발생한다.
또한, Gradle은 소스 코드 외에도 build.gradle 내부 DSL 구성이나 의존성 해석 결과에 따라 태스크 그래프가 유동적으로 생성된다.결과적으로 명확한 의존성 설치하는 명령어가 존재하지 않는다.
RUN ./gradlew dependencies 명령어는 의존성 설치 트리를 보여주는 명령어이다.
의존성 캐시인 .gradle 디렉토리 하위에 저장되며, 이 디렉토리를 docker에서 Mount 시키지 않는한 일반적인 CICD 환경에서Docker 컨테이너 종료 시 사라진다
Dockerfile의 레이어 캐시는 Gradle의 내부 증분 빌드 로직을 제대로 캐싱처리하기가 어렵다
Gradle 빌드의 최적화 전략
개인적으로 내가 생각한 방법은 Gradle은 Dockerfile의 레이어 캐시보다는 Gradle 자체 캐시와 빌드 전략을 활용하는 방향이 현실적이다.
방법 1: 도커 레벨에서 .gradle 디렉토리를 캐시로 유지
# BuildKit을 사용하는 경우 RUN --mount=type=cache,target=/home/gradle/.gradle \ ./gradlew build -x test이 방식은 BuildKit 기능을 사용해, 의존성 및 태스크 결과 캐시를 로컬 디스크에 저장하고 반복 빌드에서 재활용할 수 있도록 한다.
하지만, CI/CD 환경에서 로컬에서 마운트 된 도커 캐시 볼륨을 유연하게 활용하기에는 어려움이 있을 수 있다. 따라서 gradle에서는 방법2와 같은 원격캐시를 이용하는 방법도 제공해준다
정리된 Dockerfile 예시
FROM gradle:8.7-jdk17 AS builder WORKDIR /app COPY pom.xml . COPY src ./src # BuildKit 캐시 mount 활용 RUN --mount=type=cache,target=/home/gradle/.gradle \ ./gradlew build -x test FROM eclipse-temurin:17-jdk WORKDIR /app COPY --from=builder /app/build/libs/*.jar app.jar ENTRYPOINT ["java", "-jar", "app.jar"]방법 2: Gradle 레벨에서 원격 빌드 캐시(Remote Build Cache) 활용
Gradle은 Docker나 CI 환경에서도 환경에 독립적인 캐시 활용이 가능하도록, 원격 빌드 캐시 서버를 지원한다.
.gradle 디렉토리를 공유하거나 Docker 내부에서 캐시를 보존하기 어렵다면, S3, HTTP, 또는 Gradle Enterprise와 같은 원격 캐시 서버를 통해 의존성 및 태스크 결과를 중앙 저장소에 보관하고 재사용할 수 있다.(S3는 공식 가이드는 아니고 github에서 몇몇 개발자들이 기능을 개발하였다)
설정 예시 (settings.gradle.kts 또는 gradle.properties)
buildCache { remote(HttpBuildCache) { url = "https://your-cache-server/build-cache/" push = true } }- url: 원격 캐시 서버 주소 (예: S3 + CloudFront, HTTP 서버 등)
- push: 빌드 결과를 서버에 업로드할지 여부
이 방식을 사용하면 Docker 이미지 안에서 빌드를 수행해도 캐시는 원격 서버에 저장되기 때문에 다음 빌드에서 언제 어디서든 재활용 가능하다. 이방식을 사용하면 빌드 경로인 방법1과 같은 .gradle 디렉토리를 관리해주지 않아도 된다. 이런 Gradle용 빌드 서버는 Gradle Enterprise(Develocity), HTTP 서버, AWS S3 등 다양한 형태로 구축 가능하다.
주의할 점
gradle에서도 maven과 마찬가지로 build.gradle에서 nexus 등을 통해 내부 모듈을 사용하고 있는 환경이라면, 실제 모듈의 내용이 바뀌었는데 build.gradle이 그대로인 같은 파일이라면 build.gradle이 바뀌지 않았기 때문에 캐싱이 되어 빌드된 결과물이 변경이 안된 것 처럼 보일 수 있다. maven처럼 의존성 모듈 버전을 올리는 방법을 활용할 수도 있다. 하지만 gradle은 이럴경우를 대비해 changing=true옵션을 제공해준다.
dependencies { implementation('com.example:my-library:1.0.0') { changing = true } }추가내용
이번 글에서 gradle 에서 build cache를 세팅하는 방법을 알았는데 어떤 로직에 의해 캐싱되는지 궁금할 수 있다.
gradle에서는 comilation avoidance라는 컴파일 회피 기능을 활용하여 빌드의 캐싱을 활용한다.
컴파일 회피란?
Gradle은 프로젝트의 전체 빌드를 무조건 다시 수행하지 않는다. 특히 의존성의 API가 변경되지 않은 경우, 컴파일 작업 자체를 회피할 수 있는데 이것을 컴파일 회피라고 한다. ABI(application binary interface) 호환성을 기반으로컴파일 회피는 공개 클래스, 메서드 시그니처 등을 기준으로 의존성의 변경 여부를 판단한다. 내부 구현이 바뀌었더라도, 외부에서 사용하는 API에 변화가 없다면 이전의 컴파일 결과를 재사용할 수 있도록 하는 것이다.
참조
Announcing remote cache support in Amazon ECR for BuildKit clients | Amazon Web Services
This feature will be pre-installed and supported by Docker when version 25.0 is released. This feature is already released in Buildkit versions of 0.12 or later and is available now on Finch versions 0.8 or later. Introduction Amazon Elastic Container Regi
aws.amazon.com
https://docs.gradle.org/current/userguide/gradle_optimizations.html
Incremental Builds and Build Caching Basic
An incremental build is a build that avoids running tasks whose inputs have not changed since the previous build. Re-executing such tasks is unnecessary if they would only re-produce the same output. For incremental builds to work, tasks must define their
docs.gradle.org
https://docs.gradle.org/current/dsl/org.gradle.caching.http.HttpBuildCache.html
HttpBuildCache - Gradle DSL Version 8.14.2
Configuration object for the HTTP build cache. Cache entries are loaded via GET and stored via PUT requests. A successful GET request must return a response with status 200 (cache hit) or 404 (cache miss), with cache hit responses including the cache entry
docs.gradle.org
https://blog.gradle.org/compilation-avoidance
Compilation Avoidance
Compilation Avoidance Calendar November 28, 2022 Introduction We’ve recently noticed some community chatter about speeding up Gradle compilation on the JVM by ignoring changes not affecting the ABIs of dependencies. What a great idea! In fact, Gradle has
blog.gradle.org
'Computer Science > Java' 카테고리의 다른 글
Maven, Gradle 도커에서 빌드과정 최적화하기, Maven편 (0) 2025.06.01