본문 바로가기
개발/SpringBoot

SpringBoot3 - GraalVM Native Image Support

by 궁즉변 변즉통 통즉구 2023. 2. 1.
반응형

ㅁ이번에 SpringBoot3 릴리즈 관련 내용을 보면서 Native Image Support, GraalVM, AOT 관련된 내용이 있어서 한번 찾아보고 간단히 테스트 해봤다. 간단히는 Java기반의 SpringBoot 어플리케이션 실행 시 Java에 대한 종속성 없이(빌드 결과물이 jar가 아님) 독립 실행 가능하도록 해주는 것으로 얘기할 수 있다. java도 go처럼 빌드하고 실행할 수 있다는 것을 처음 알게되었는데 몇 년전부터 spring-native라는 프로젝트로 진행이 되고 있었다가 이번에 정식 Support로 포함이 된 것이라 한다.

 

GraalVM Native Image

  • 컴파일된 Java 애플리케이션을 미리 처리(Ahead Of Time)하여 생성할 수 있는 독립 실행형 실행 파일입니다(JVM 없이 실행이 가능, Runtime의 JVM에 대한 의존성 제거)
  • 일반적으로 메모리 사용량이 더 적고 JVM보다 빠르게 시작가능
  • 활용:
    - 컨테이너 이미지를 사용하여 배포되는 애플리케이션에 매우 적합(메모리 사용량, 이미지 사이즈, 실행시간 등 장점)
    - "FaaS(Function as a Service)" 플랫폼과 결합할 때 좋을 듯

 

일반 JVM Deployment와 차이점

  • 정적 분석은 빌드 시 main entry point 에서 수행
  • 네이티브 이미지가 생성될 때 도달할 수 없는 코드는 제거되며 실행 파일의 일부가 아님
  • GraalVM은 코드의 동적 요소를 직접 인식하지 않으며 리플렉션, 리소스, 직렬화 및 동적 프록시에 대해 알려줘야 함
  • 어플리케이션 classPath는 빌드 시 고정되어 변경 불가
  • 클래스 Lazy Loading 없으며 실행 파일에 포함된 모든 항목이 시작 시 메모리에 로드 됨
  • 애플리케이션에 정의된 빈은 런타임에 변경 불가
    • Spring @Profile 주석 및 프로필별 구성은 지원 안됨
    • Bean이 한번 생성되면 변경되는 속성은 지원되지 않습니다(예: @ConditionalOnProperty 및 .enable 속성).
  • GraalVM Native Image is a complete, platform-specific executable(플랫폼에 종속적인 executable 파일)
  • Java applications 전체를 지원하지는 못한다

 

Spring AOT로 생성되는 것들

  • Java source code
  • Bytecode (for dynamic proxies etc)
  • GraalVM JSON hint files:
    • Resource hints (resource-config.json)
    • Reflection hints (reflect-config.json)
    • Serialization hints (serialization-config.json)
    • Java Proxy Hints (proxy-config.json)
    • JNI Hints (jni-config.json)

 

SpringBoot Native Image 어플리케이션 빌드 및 실행 방법

방법은 아래 2가지 방법이 있다고 한다.

1. Cloud Native Buildpacks에 대한 Spring Boot 지원을 사용하여 네이티브 실행 파일이 포함된 경량 컨테이너를 생성

2. GraalVM 네이티브 빌드 도구를 사용하여 네이티브 실행 파일 생성

 

1번 방법을 사용해서 간단히 테스트 해본다. 내부에서 실행되는 프로세스나 흐름까지는 모르겠지만 테스트는 엄청 간단한다.

먼저 샘플 어플리케이션이 필요한데 https://start.spring.io/ 에서 다운로드 받아서 사용한다. 사이트에 접속해서 의존성 설정에 "GraalVM Native Support"를 추가한다.

 

소스에는 pom.xml에 아래 설정을 확인 및 추가한다.

...
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
...

      <build>
		<plugins>
			<plugin>
				<groupId>org.graalvm.buildtools</groupId>
				<artifactId>native-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<image>
                        <!-- 기본은 tiny라고 하는데 컨테이너 내부 접속해서 확인을 좀하고 싶어서 full버전으로 설정해본다-->
					    <builder>paketobuildpacks/builder:full</builder> 
						<env>
							<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
						</env>
					</image>
				</configuration>
			</plugin>
		</plugins>
	</build>

 

그리고 메인 클래스는 아래와 같이 수정해준다.

@RestController
@SpringBootApplication
public class DemoApplication {

	@RequestMapping("/")
	String home() {
		return "Hello World!";
	}

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

 

다음 명령으로 Native-Image를 빌드한다. 로컬에 Docker가 실행 중이여야 한다.

# native-image 이미지 빌드
mvn -Pnative spring-boot:build-image

 

빌드된 이미지를 실행해본다

docker run --rm -p 8080:8080 docker.io/library/demo:0.0.1-SNAPSHOT

컨테이너가 잘 실행되고 어플리케이션도 정상적으로 동작한다.

 

이제 컨테이너 내부에는 어떻게 구성되어 있는지 접속해서 확인해본다. 먼저 빌드된 결과물이 jar가 아니다. 그리고 컨테이너 내부에 java는 물론 java process가 없다. com.example.demo.DemoAppication이라는 독립 실행가능한 파일이 OS에 위에서 그냥 실행이 되는 구조인것 같다. 이는 빌드 결과물이 java는 아니지만 OS에 종속적이다는 얘기가 된다(빌드된 OS와 실행하는 OS가 동일한 환경)

 

참고:
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html

 

반응형

댓글