한 줄 정의
핵심 메시지
단일 자바 가상 머신에서 성능을 분석하던 기존 모델은 클라우드 네이티브 환경으로 옮겨오면서 더 이상 충분하지 않습니다. 비용 최적화, 신뢰성 최적화, 수평적 확장이라는 세 가지 새로운 축이 기존 성능 분류 체계를 보완해야 합니다.
자바 표준(자카르타 EE, 마이크로프로파일)은 분산 배포 환경의 공통 문제를 표준화하고, 클라우드 네이티브 컴퓨팅 재단(CNCF)은 벤더 중립적인 오픈 소스 생태계로 이 위에 또 한 겹의 표준을 얹습니다.
컨테이너 이미지가 클라우드 네이티브 환경의 사실상 배포 단위로 자리잡았고, 그 아래에는 가상화·네임스페이스·cgroups라는 운영체제 수준 메커니즘이 뒷받침합니다. 자바 개발자와 성능 엔지니어가 이 스택 전반을 이해해야 하는 이유는, 추상화 계층이 늘어날수록 성능 추론도 새로운 변수를 같이 다뤄야 하기 때문입니다.
쉽게 말하면
옛날에는 서버 한 대 잘 튜닝하면 됐습니다. 지금은 서버 여러 대를 자동으로 띄우고 줄이는 클라우드 환경에서, 어떤 작업을 어떤 가상 머신 위에 어떻게 띄울지 결정해야 합니다.
이를 위해 자바 진영은 두 가지 표준을 만들었습니다. 자카르타 EE는 엔터프라이즈 자바의 큰 우산이고, 마이크로프로파일은 마이크로서비스 시대에 맞는 분산 시스템용 작은 표준 묶음입니다.
자바 바깥에서는 CNCF가 쿠버네티스, 프로메테우스, 오픈텔레메트리 같은 프로젝트를 관리하면서 벤더 중립적인 클라우드 표준을 만들어 갑니다.
실제 배포 단위는 컨테이너 이미지입니다. 도커가 만든 OCI 표준이 자리잡으면서, 이미지를 빌드하고 가상 머신 위에서 실행하는 방식이 통일되었습니다.
왜 중요한가?
단일 JVM 분류 체계로는 부족하다
기존 성능 분류 체계는 “이 JVM이 얼마나 빠르게 도느냐”에 초점이 맞춰져 있었습니다. 클라우드에서는 JVM 인스턴스가 수십 개 떠 있을 때 전체적으로 성능을 어떻게 효과적으로 관리하느냐가 더 중요한 질문이 됩니다. 비용·신뢰성·수평 확장성이라는 새 축이 추가되어야 합니다.
자바 표준은 벤더 잠금을 막는다
쿼커스, 헬리돈, 오픈 리버티 같은 다양한 클라우드 네이티브 자바 제품 간의 호환성을 보장하는 것이 마이크로프로파일의 역할입니다. 표준이 없으면 개발자가 각자 솔루션을 만들거나 특정 프레임워크에 종속되기 쉽습니다. 12팩터 앱 같은 모범 사례를 라이브러리 수준에서 표준화한 점이 핵심입니다.
CNCF 랜드스케이프는 기술 선택의 지도
특정 용도에 적합한 프로젝트를 고르는 일은 클라우드 네이티브 환경에서 가장 어려운 작업 중 하나입니다. 샌드박스 → 인큐베이팅 → 졸업으로 이어지는 성숙도 분류는 “이 기술을 프로덕션에 써도 되는가”를 판단할 때 가장 빠른 1차 필터가 됩니다.
컨테이너 이미지는 배포의 새로운 원자 단위
자바의 “한 번 작성하면 어디서나 실행” 슬로건은 바이트코드 수준에서는 늘 완벽하지 않았습니다. 컨테이너 이미지가 한 단계 위에서 이 약속을 실제로 구현했습니다. 운영체제 의존성과 JVM, jar 파일까지 모두 한 이미지에 포장되니까요.
핵심 내용
8.1 클라우드 스택을 위한 자바 표준
자바 프레임워크는 JDK의 핵심 라이브러리를 확장하여 현실적인 배포 문제를 해결합니다. 최근에는 마이크로서비스 기반 아키텍처를 활용한 분산 플랫폼이 점점 더 널리 사용되고 있어, 단일 프레임워크뿐 아니라 분산 배포 방식에 적용할 수 있는 표준을 함께 고려하는 것이 중요해졌습니다.
주목할 만한 두 가지 오픈 표준
- 자카르타 EE: 엔터프라이즈 자바 표준의 집합. 6장에서 다룬 범위 그대로
- 마이크로프로파일: 클라우드 네이티브 플랫폼의 분산 시스템을 위한 표준. 6.1에서는 자카르타 EE 10도 관련 있지만 독립적인 표준화된 구성 요소로 분해된 것이 특징
마이크로프로파일은 12팩터 앱을 개발하기 위한 라이브러리의 표준화를 제공하기 때문에 자바 개발자와 아키텍트에게 매우 중요합니다.
마이크로프로파일 표준 구조
마이크로프로파일 6.1이 제공하는 표준은 마이크로서비스 기반 아키텍처 설계의 핵심 요소들을 다룹니다.
| 표준 | 역할 |
|---|---|
| 텔레메트리 | 관측성 지원 |
| OpenAPI | REST 명세와 도구 |
| REST 클라이언트 | 일관된 REST API 호출 |
| 설정 | 설정 형식이나 위치 표준화 |
| 장애 허용 | 분산 시스템에서 불가피한 장애 대응 |
| 지표 | 핵심 통계 지표 수집과 내보내기 |
| JWT 인증 | 토큰 기반 보안 일관성 유지 |
| 상태체크 | 배포 환경에서의 가용성 확인 |
이 표준들은 자카르타 EE 코어 프로파일 위에서 동작합니다. 코어 프로파일은 더 작은 런타임을 목표로 하는 EE 플랫폼이라는 점이 자카르타 EE 풀 프로파일과의 차이입니다.
이클립스 재단의 역할
이클립스 재단은 마이크로프로파일과 자카르타 EE 작업 그룹의 중심 역할을 합니다. 마이크로서비스 표준과 엔터프라이즈 자바 표준을 정의하며, OpenJDK의 어댑티움(Adoptium) 커뮤니티 빌드도 주관합니다.
표준만으로 부족한 이유
자바 표준은 유용하지만, 목표 플랫폼에서 벤더 중립성과 이식성을 확보할 수 있는 전략도 필요합니다. 오픈 소스 소프트웨어는 이러한 문제를 해결하기 위해 오픈 재단을 활용하는 오랜 전통을 가지고 있습니다.
8.2 클라우드 네이티브 컴퓨팅 재단
클라우드 네이티브 컴퓨팅 재단(CNCF)은 클라우드 네이티브 컴퓨팅을 보편적으로 확산시키기 위해 설립된 벤더 중립적 오픈 소스 소프트웨어 재단입니다.
CNCF 헌장
클라우드 네이티브 기술은 공용, 사설, 하이브리드와 같은 현대적이고 동적인 환경에서 확장 가능한 애플리케이션을 구축하고 실행할 수 있도록 조직을 지원합니다. 컨테이너, 서비스 메시, 마이크로서비스, 불변 인프라, 선언적 API 등이 대표적인 사례입니다.
컴퓨팅 플랫폼은 언어에 종속되지 않는다
벤더 중립성과 이식성은 중요한 아키텍처 문제이며, 여러 CNCF 프로젝트가 클라우드 네이티브 애플리케이션 제공에 매우 중요한 역할을 합니다.
자바 범위를 넘어선 조언
컴퓨팅 플랫폼은 특정 언어 스택에 국한되지 않습니다. 이 장의 나머지 부분에서 다룰 조언은 자바의 범위를 넘어 확장됩니다.
CNCF 랜드스케이프
수많은 프로젝트 중에서 특정 용도에 적합한 것을 선택하는 일은 쉽지 않습니다. CNCF 랜드스케이프(landscape.cncf.io)는 네이티브 생태계에 존재하는 프로젝트와 제품들을 카테고리별로 분류한 대화형 지도입니다.
주요 카테고리 다섯 가지는 다음과 같습니다.
- 애플리케이션 정의와 배포 (definition and deployment)
- 오케스트레이션과 관리 (orchestration and management)
- 런타임
- 프로비저닝 (provisioning)
- 관측성과 분석 (observability and analysis)
각 카테고리에는 개별 CNCF 프로젝트, 해당 프로젝트의 통계, 프로젝트의 소유권 정보를 확인할 수 있습니다.
OCI는 별도의 표준화 이니셔티브
컨테이너 이미지 형식과 표준은 CNCF에서 관리하지 않고, OCI(Open Container Initiative)에서 별도로 관리한다는 점에 유의해야 합니다. CNCF 랜드스케이프에 포함된 프로젝트는 벤더 중립적이지만, 일부는 영리 목적의 프로젝트도 포함되어 있습니다.
성숙도 분류
CNCF 오픈 소스 프로젝트는 성숙도에 따라 다음과 같이 분류됩니다.
| 단계 | 의미 |
|---|---|
| 샌드박스 | 초기 단계의 혁신적인 프로젝트. 아직 개발 초기이며 프로덕션에 적합하지 않을 수 있음 |
| 인큐베이팅 | 프로덕션 준비가 완료된 프로젝트. 활발히 채택되며 신뢰성을 입증받은 상태 |
| 졸업 | 여러 산업과 프로젝트에서 프로덕션 사용의 입증된 실적을 보유 |
인큐베이팅 단계는 적절한 거버넌스와 유지 관리 체계, 커뮤니티, 엔지니어링 원칙, 보안, 생태계를 갖추고 있어야 하며, 인큐베이팅 템플릿의 기준을 충족해야 합니다.
아키텍트의 활용 패턴
아키텍트들은 CNCF 랜드스케이프를 자주 사용합니다. 이는 특정 문제 영역에서 기술을 식별하고, 프로젝트의 장점을 발견하며, 개념 증명과 기능 확대에 집중하기 위한 메커니즘으로 사용됩니다.
8.2.1 쿠버네티스
쿠버네티스는 오픈 소스 컨테이너 오케스트레이션 시스템(open source container-orchestration system)입니다. 컴퓨팅 노드(호스트)로 구성된 클러스터를 활용하여 시스템 운영자와 데브옵스 팀이 분산 애플리케이션을 클러스터 전반에 걸쳐 배포, 확장, 조율할 수 있도록 합니다.
쿠버네티스는 2018년에 CNCF의 졸업 프로젝트로 지정되었습니다.
8.2.2 프로메테우스
프로메테우스는 지표 데이터를 저장하기 위한 시계열 데이터베이스와 지표 형식입니다. 이 프로젝트는 2016년 5월 CNCF의 인큐베이팅 프로젝트로 승인되었고, 2018년 8월에 졸업 프로젝트로 전환되었습니다.
8.2.3 오픈텔레메트리
오픈텔레메트리(OTel)는 애플리케이션의 관측성 데이터를 수집, 집계, 전송하여 관측성 시스템으로 전달하기 위한 표준, 형식, 라이브러리 세트입니다.
오픈텔레메트리는 CNCF 프로젝트로, 깃허브에서 진행되고 있습니다. 크로스 플랫폼 기술로 설계되었으며 특정 언어에 국한되지 않습니다. 하지만 자바는 이 표준을 구현하는 데 있어 성숙한 기술을 제공합니다.
오픈텔레메트리는 CNCF의 인큐베이팅 프로젝트에 속하지만, 폭발적으로 성장하며 이미 많은 조직에서 프로덕션 환경에 적용되고 있습니다.
8.3 가상화
가상화를 논하기에 앞서, 더 근본적인 질문인 **‘클라우드란 무엇인가?’**를 먼저 살펴보아야 합니다. ‘클라우드는 그냥 누군가의 컴퓨터일 뿐이다’라는 말을 자주 듣게 되지만, 클라우드는 그보다 더 복잡한 개념입니다.
메리 브랜즈컴의 정의
클라우드를 가장 간단히 정의하면, 동일한 하드웨어로 가득 찬 데이터 센터입니다. 처음 설치할 때와 고장 나서 폐기할 때를 제외하고는 아무도 이를 물리적으로 만지지 않으며, 모든 배포, 업데이트, 점검, 관리가 자동화한 환경입니다.
클라우드 환경에서 인프라는 상품화된 상태로 제공되며, 다양한 애플리케이션 사용 사례에 대해 즉시 실행할 준비가 되어 있습니다. 그러나 인프라와 하드웨어에 직접 접근할 수 있는 경우는 드뭅니다. 대신 고객의 런타임 환경이 인프라 또는 다른 고객과 명확히 분리되도록 관리와 제어가 이루어져야 합니다.
자바 개발자와 성능 엔지니어에게 이는 클라우드로 전환할 때 직면하게 되는 첫 번째 트레이드오프입니다.
SSH 시대의 종말
간접 관리 방식의 표준화
전통적인 시스템 관리 기법인 ‘SSH로 서버에 접속해 확인하기’(SSH into a box and have a look around)는 클라우드 환경에서 일반적으로 사용할 수 없습니다. 대신 간접적인 관리 방식을 채택하는 것이 표준이 되었습니다.
가상화의 세 가지 조건
- 가상화된 운영 체제에서 실행되는 프로그램은 베어메탈(bare metal)에서 실행될 때와 동일하게 동작해야 합니다
- 하이퍼바이저(hypervisor)라는 구성 요소가 하드웨어 리소스에 대한 모든 접근을 중지해야 합니다
- 가상화로 인한 오버헤드는 가능한 한 작아야 하며, 실행 시간에 큰 영향을 주어서는 안 됩니다
전통적인 비가상화(unvirtualized) 시스템에서는 운영 체제 커널이 특별한 권한 모드에서 실행되며(이를 위해 커널 모드로 전환이 필요), 운영 체제가 하드웨어에 직접 접근할 수 있습니다.
가상화 스택
graph TD App1[mustelid<br/>바이너리/라이브러리<br/>사용자 공간] --- HV[하이퍼바이저] App2[feline<br/>바이너리/라이브러리<br/>사용자 공간] --- HV HV --- OS[운영 체제]
가상화된 시스템에서는 게스트 운영 체제가 하드웨어에 직접 접근할 수 없습니다. 하이퍼바이저가 운영 체제와 게스트 운영 체제 사이에서 간접적인 연결 역할을 합니다. 개발자는 컨테이너를 사용해 게스트 운영 체제에 자유롭게 애플리케이션을 배포할 수 있습니다.
하이퍼바이저는 가상화의 중심에 있습니다. 과거에는 가상화의 느린 환경이 큰 문제였지만, 클라우드에서 가상 머신과 하이퍼바이저가 널리 사용되면서 하이퍼바이저 오버헤드에 대한 연구와 개선이 크게 진전되었습니다.
공용 클라우드로 이전할 때, 일반적으로 대상 플랫폼은 하이퍼바이저와 함께 제공되는 가상 머신이 됩니다. 이는 성능에 영향을 미치며, 적어도 일부는 사용자가 통제할 수 없는 영역에 속합니다.
8.3.1 적절한 가상 머신 선택하기
공용 클라우드 제공업체들은 다양한 작업 부하 프로파일에 적합하도록 설계된 여러 유형의 가상 머신을 제공합니다. 아마존 EC2는 다음과 같은 옵션을 제공합니다.
| 유형 | 특징 |
|---|---|
| 일반 목적 | 애플리케이션 시작점. M7g 시리즈는 vCPU 1개/메모리 4GB부터 m7gd.metal(CPU·메모리 256GB 베어메탈)까지 |
| 컴퓨팅 최적화 | 높은 CPU 부하를 가진 작업 부하 지원 |
| 메모리 최적화 | 대규모 데이터 세트와 메모리에서 대량의 읽기/쓰기. 데이터베이스, 인메모리 캐싱, 데이터 분석 |
| 가속 컴퓨팅 | 하드웨어 가속(acceleration) 사용. 그래픽 처리, 복잡한 계산, 생성형 AI 애플리케이션 |
| 저장소 최적화 | 로컬 저장소 읽기/쓰기 최적화. 트랜잭션 DB와 아파치 스파크 스타일 작업 |
| 고성능 컴퓨팅 | 복잡한 시뮬레이션과 딥러닝 스타일 작업 |
애저는 일반적인 컴퓨팅용 D-시리즈에서 자장소 최적화 L-시리즈까지 폭넓은 옵션을 제공합니다. 구글 클라우드는 범용적 목적, 초고용량 메모리(ultra-high memory), 컴퓨팅 집중 작업 부하, GPU가 필요한 애플리케이션 등 다양한 옵션을 제공합니다.
일반 목적부터 시작하는 원칙
클라우드에서 애플리케이션을 설계할 때는 일반 목적의 가상 머신으로 시작하는 것이 가장 좋습니다. 일반 목적 가상 머신은 다양한 작업에 적합하고, 처음 시작하기에 효율적입니다.
인스턴스 크기의 2의 제곱수 규칙
인스턴스 유형은 주로 2의 제곱수로 크기가 설정됩니다. 만일 CPU 성능이 5%만 더 필요하다면, 더 큰 가상 머신으로 업그레이드하는 것은 비용적인 부담이 될 수 있습니다. 따라서 필요에 따라 신중히 선택하는 것이 중요하며, 인스턴스 계열은 CPU, 메모리, 네트워크 처리량, 저장소의 일정한 비율을 유지하면서 선택할 수 있는 기준점을 제공합니다.
이러한 단계를 거치며 변경 사항이 성능과 비용 간의 적절한 균형을 제공하는지 측정해 나가는 것이 중요합니다.
8.3.2 가상화 고려 사항
클라우드 애플리케이션을 설계할 때는 비용, 신뢰성, 확장성을 최적화할 수 있는 가상 머신을 선택하는 것이 중요합니다. 예를 들어, CPU를 많이 사용하는 작업을 하거나 그렇게 될 가능성이 있다면, 실제로 비슷한 환경에서 성능 테스트를 실행하고 영향을 측정하는 것이 필요합니다.
임시 환경의 가치
클라우드의 큰 장점 중 하나는 테스트를 위한 임시 환경(ephemeral environment)을 쉽게 생성할 수 있다는 점입니다.
최상의 두 가지 방법 플랫폼
또 다른 옵션은 ‘최상의 두가지 방법’(best of both worlds) 플랫폼을 실행하는 것입니다. 이 모델에서는 일부 작업은 공용 클라우드에서, 다른 일부는 베어메탈에서 실행합니다. 이를 통해 버스트 스케일과 신뢰성이 필요한 특정 작업을 최적화할 수 있으며, 동시에 핵심 프로세싱에 기계적 공감과 성능을 고민할 수 있습니다.
이 접근 방식의 좋은 예로 레드햇의 오픈시프트(하이브리드 클라우드 기술)가 있습니다.
8.4 이미지와 컨테이너
1990년대 후반에 자바가 처음 등장했을 때, ‘한 번 작성하면 어디서나 실행할 수 있다’(write once, run everywhere)라는 슬로건으로 큰 주목을 받았습니다. 자바 가상 머신을 실행할 수 있는 모든 운영 체제와 컴퓨터에서 자바 코드를 실행할 수 있다는 뜻입니다. 이 목표는 매우 대담한 것이었지만, 특히 초기에는 이러한 추상화가 항상 완벽하지는 않았습니다.
벤저민 에번스
가상 머신, 동적 자기 관리(dynamic self-management), JIT 컴파일, 가비지 컬렉션과 같은 거대한 아이디어들은 이제 프로그래밍 언어의 일반적인 기술로 자리 잡았습니다.
이식성은 클라우드 네이티브 애플리케이션의 설계 목표 중 하나지만, 자바 바이트코드의 이식성으로 나타난 것이 아니라, 약간 더 높은 수준인 컨테이너 이미지에서 구현되었습니다. 컨테이너 이미지는 오케스트레이션 애플리케이션 프로세스를 생성할 수 있는 파일로, 컨테이너 관리 시스템이나 오케스트레이션 도구의 제어로 실행됩니다.
8.4.1 이미지 구조
전통적인 유닉스 시스템 환경에서 프로그램의 실행 파일은 디스크에 저장된 고정된 형태의 프로그램입니다. 이 파일을 실행하면, 프로그램이 활성화되어 애플리케이션으로 실행됩니다. 컨테이너 이미지도 마찬가지로 고정된 버전으로 생각할 수 있으며, 스케줄링과 오케스트레이션을 통해 활성화된 애플리케이션으로 변환됩니다.
구성 요소의 다양성과 복잡성이 증가하면서, 애플리케이션 스택을 관리하고 제어하기 위한 표준화가 중요해졌습니다. 도커(docker)가 2015년에 만든 오픈 컨테이너 이미지 OCI가 좋은 예입니다.
이미지는 자바 애플리케이션을 포함한 모든 애플리케이션을 패키징하는 데 선호되는 표준 방식이 되어 가고 있습니다. 이 이미지는 애플리케이션을 실행하기 위해 필요한 모든 것을 포함합니다. 여기에는 사용자 영역 구성 요소(운영 체제의 일부)와 자바 가상 머신도 포함됩니다. 심지어 베어메탈 서버에서 실행할 때도 이미지는 유용합니다.
OCI는 이미지의 형식, 이미지가 컨테이너로 실행되는 방식, 이미지를 배포하는 방식을 표준화하는 역할을 맡고 있습니다.
8.4.2 이미지 구축
소프트웨어 빌드 과정에서 이미지를 생성하는 방법으로 Dockerfile 명령어가 있습니다. Dockerfile의 한 줄 한 줄은 이미지의 새로운 레이어를 만들어냅니다. 레이어는 파일 시스템의 변경 내용을 의미하며, 컨테이너 안에서 사용되고 빌드 캐시에 저장됩니다. 빌드 캐시에 있는 기존 레이어는 새 이미지를 생성하기 위한 구성 요소로 다시 사용할 수 있습니다.
도커의 두 얼굴
도커는 기술로서의 사용과 상업적 엔티티 및 레지스트리 제공자로서의 역할이 혼동될 수 있습니다. 여기서는 도커를 오픈 소스와 다양한 도구에서 널리 사용되는 표준 형식으로 언급합니다.
기본 Dockerfile 예제
FROM이라는 명령어를 사용하여 자바 애플리케이션 animals-demo-1.0-SNAPSHOT.jar를 추가하기 위한 기반 이미지를 설정합니다. USER, RUN, COPY, WORKDIR는 app 폴더를 설정하고, 빌드된 jar 파일을 /app 폴더로 이동시킵니다. 이후 CMD라는 명령어를 통해 컨테이너가 jar 파일을 실행할 준비를 완료합니다.
FROM registry.access.redhat.com/ubi8/openjdk-17:1.13-1
USER root
RUN mkdir /app
COPY target/animals-demo-1.0-SNAPSHOT.jar /app
WORKDIR /app
CMD ["java", "-jar", "animals-demo-1.0-SNAPSHOT.jar", \
"io.opentelemetry.examples.animal.AnimalApplication"]Jib: 메이븐 통합형 이미지 빌더
Jib는 자바 애플리케이션을 빌드 할 때 사용하는 메이븐(Maven)과 같은 빌드 시스템의 일부로 실행됩니다. 따라서 자바 애플리케이션의 구조와 의존성에 대한 더 많은 정보를 가질 수 있다는 장점이 있습니다.
Jib는 이미지를 의존성, 리소스, 클래스와 같은 개별 레이어로 구성합니다. 변경된 레이어만 수정합니다. 기본적인 이론은 자바 라이브러리 의존성과 프로젝트의 리소스는 애플리케이션 코드의 클래스에 비해 변경 빈도가 낮다는 것입니다.
Jib를 사용하면 자바 라이브러리와 하위 이미지 레이어에서 애플리케이션을 빌드 할 때마다 모든 의존성을 최신 상태로 유지할 수 있다는 장점이 있습니다. Dockerfile을 사용할 경우, 기본 이미지가 패치되거나 새 버전이 출시될 때마다 수동으로 업데이트해야 합니다. Jib는 이런 업데이트 과정을 더 쉽게 해주며, 보안과 성능을 개선할 수 있습니다.
멀티스테이지 빌드
멀티스테이지 빌드(multistage build)를 사용하면 도커에서도 효율적인 빌드가 가능합니다. 이를 통해 jar 파일을 빌드한 다음, 두번째 단계에서 jar파일을 사용해 최종 이미지를 만듭니다. 첫번째 단계에서 사용된 레이어는 삭제되고, 최종 이미지에는 필요한 jar 파일만 포함됩니다. 이렇게 하면 빌드 환경이 더 표준화되고 단순화됩니다.
# 1단계: 빌더
FROM maven:3.9-openjdk-17 AS builder
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml package
# 2단계: 최종 이미지
FROM registry.access.redhat.com/ubi8/openjdk-17:1.13-1
USER root
RUN mkdir /app
# Copy the jar created by the first stage
COPY --from=builder /usr/src/app/target/animals-demo-1.0-SNAPSHOT.jar /app
WORKDIR /app
CMD ["java", "-jar", "animals-demo-1.0-SNAPSHOT.jar"]이미지 크기 고려
자바 애플리케이션의 일반적인 이미지 스택에는 운영 체제 의존성과 jar 파일이 포함됩니다. 최소한의 베이스 이미지는 레드햇의 UBI(Universal Base Image) 미니멀로 압축 크기가 37.1 MB입니다. AMD64 아키텍처에서 OpenJDK를 계층화하면 베이스 이미지의 압축 크기는 147.8 MB가 됩니다. 최종 레이어 크기는 애플리케이션 빌드 크기에 따라 달라집니다.
이미지는 상당히 커질 수 있습니다. 예를 들어, 윈도우 AMD64용 이클립스 테뮤린(Eclipse temurin) 이미지는 2.27 GB입니다.
8.4.3 컨테이너 실행
컨테이너는 애플리케이션을 독립된 환경에서 실행하기 위한 메커니즘을 제공합니다. 이는 두 가지 리눅스 커널의 네임스페이스(namespaces)와 cgroups(control groups) 두 가지 기능을 사용해 제어됩니다.
| 메커니즘 | 역할 |
|---|---|
| 네임스페이스 | 호스트 머신의 리소스에 대한 접근과 가시성 제어 |
| cgroups | 머신 리소스(특히 CPU 사용률과 메모리)에 대한 제한 적용 |
컨테이너 추상화 목표 중 하나는 서로 다른 컨테이너 간의 프로세스 격리를 제공하는 것입니다. 이는 가상 머신과 비슷하지만, 중요한 차이점이 있습니다. 컨테이너는 가상 머신처럼 하이퍼바이저를 사용하지 않는다는 점입니다. 대신, 컨테이너 안의 애플리케이션은 호스트 운영 체제에서 직접 실행되고, 하이퍼바이저의 추가적인 간접 계층 없이 호스트의 커널에 접근합니다.
이제 컨테이너는 가볍고 빠르게 시작할 수 있으며, 이는 오케스트레이션의 기반을 형성합니다.
8.5 네트워킹
컨테이너와 오케스트레이션 시스템은 임시 컴퓨팅(ephemeral computing)을 사용합니다. 이는 작업이 항상 같은 ‘장소’(물리적 서버나 가상 서버)에서 실행되지 않을 수도 있다는 뜻입니다. 임시 컴퓨팅의 장점은 비용을 절약할 수 있다는 점입니다. 필요에 따라 작업량이 늘어나면 서버를 더 추가하고, 줄이면서 서버를 줄일 수 있습니다.
스팟 컴퓨팅
확장은 주어진 환경 내에서 이루어질 수도 있고, 필요에 따라 환경에 동적으로 추가되기도 합니다(클러스터에 더 많은 노드를 추가). 이를 통해 스팟 컴퓨팅(spot computing)의 이점을 활용할 수도 있습니다. 스팟 컴퓨팅은 미사용 자원을 낮은 비용으로 사용할 수 있는 기능을 말합니다. 하지만 스팟 인스턴스는 예약되거나 보장되지 않으며, 해당 자원에 대한 수요가 발생하면 사전 통보 없이 종료됩니다.
트래픽 추상화의 필요성
네트워크 관점에서 보면, 고정된 온프레미스 데이터 센터처럼 안정적이지 않을 수 있으며, 구성 요소들이 항상 고정된 IP 주소나 지속적으로 사용 가능한 컴퓨팅 자원을 가지지 않을 수 있습니다. 따라서 네트워킹과 애플리케이션 통신을 생각할 때는 트래픽 추상화를 고려하는 것이 최선의 방법입니다.
트래픽을 일반적으로 두 가지 범주로 나눌 수 있습니다.
북남 트래픽 (외부 → 내부)
북에서 남 트래픽은 시스템의 일부가 아닌 트래픽으로, 클라우드 내 다른 위치나 인터넷에서 발생할 수 있습니다. 북에서 남 트래픽은 고정된 IP 주소를 가져야 하며, 종종 인그레스(ingress)라고 불립니다.
인그레스를 설정하는 한 가지 방법은 고가용성 로드 밸런서(highly available load balancer)를 사용하는 것입니다. 로드 밸런서는 클라우드 제공업체에서 제공하는 도구로, 외부에서 들어오는 트래픽을 내부 애플리케이션으로 전달합니다. 이를 통해 외부에서 고정된 IP 주소를 사용할 수 있으면서도, 내부적으로는 서버를 추가하거나 작업 부하를 조정하여 더 많은 요청을 처리할 수 있습니다.
동서 트래픽 (서비스 간)
동에서 서 트래픽은 오케스트레이션 계정 내에서 서비스 간 호출을 의미하며, 여기서는 오케스트레이션 플랫폼에서 제공하는 내부 서비스 디스커버리(internal service discovery)를 사용하여 확장하거나 축소할 수 있습니다. 쿠버네티스와 같은 오케스트레이션 시스템에서는 클러스터 내 다른 서비스에서 사용할 수 있는 로컬 DNS 항목을 생성하기 위해 서비스를 사용할 수 있습니다.
8.6 Fighting Animals 예제 소개
이 책에서는 ‘Hello World’ 수준을 넘어서지만, 여전히 이해하기 쉽고 실제 시스템을 시작하는 데 사용할 수 있는 완전한 예제를 제공합니다. 그 이유 중 하나는 클라우드 기술에 입문한 사람들이 기본적인 샘플 프로젝트에서 더 발전하기 어렵기 때문입니다.
관측성이라는 개념을 예제에 추가하면 상황은 더 복잡해집니다. 지나치게 단순한 예제는 실제로 운영 환경에서 사용할 수 있는 관찰 시스템으로 확장하기 어렵기 때문입니다.
Fighting Animals 아키텍처
graph LR Animal[animal<br/>서비스] --> Mammal[mammal<br/>서비스] Animal --> Fish[fish<br/>서비스] Mammal --> Feline[feline<br/>서비스] Mammal --> Mustelid[mustelid<br/>서비스]
Fighting Animals는 깃허브에서 제공되는 마이크로서비스 아키텍처를 가진 간단한 자바 애플리케이션입니다. 주요 버전은 스프링부트로 작성되었지만, 쿼커스를 기반으로 한 대안 버전도 존재합니다. 이 책에서는 스프링부트 버전을 기준으로 설명합니다.
이 애플리케이션은 도커 컨테이너로 실행되며(도커 컴포즈 사용), 포트 8080에서 REST 엔드포인트를 제공합니다. 이 엔드포인트에 요청을 보내면 여러 Fighting Animal을 JSON 형식으로 반환합니다.
{
"good": "<animal1>",
"evil": "<animal1>"
}예제 변형 시나리오
이 책에서는 다음과 같은 변형들을 다룹니다.
main: 관측 불가능micrometer_only: 마이크로미터(Micrometer) 지표만 사용Micrometer_with_prom: 프로메테우스를 활용한 마이크로미터 지표Manual_tracing: 수동 스팬을 이용한 오픈텔레메트리 추적(trace)Auto_otel: 오픈텔레메트리 자바 에이전트를 사용하여 자동으로 추적K8s-with-argo: 쿠버네티스를 활용한 예제와 배포 전략을 사용한 변경 롤아웃Logging_only: SLF4J 로깅을 오픈텔레메트리로 내보내기Micrometer_with_otel: 마이크로미터와 오픈텔레메트리 지표 사용Otel_metrics_raw_api: 오픈텔레메트리 지표를 Raw API를 사용해 구현Distributed_systems: 카프카를 사용해 Fighting Animals 시스템 확장With_infinispan: 카프카와 인피니스팬을 사용해 Fighting Animals 시스템 확장
버전 번호에 대한 안내
Fighting Animals 예제들에서는 의존성과 컨테이너의 고정된 버전(pinned versions)을 사용하고 있습니다. 이는 예제들이 재현 가능하며 시간이 지나도 변하지 않도록 보장하기 위함입니다. Latest와 같은 유동적인 버전(floating versions)을 사용하는 시스템에서는 시간이 지나면서 버전이 변경됩니다. 이는 어떤 변화가 있었고 왜 발생하는지를 이해하기 매우 어렵게 만듭니다. 그리고 시스템이 복잡해질수록 이 문제는 더욱 심각해집니다.
8.7 정리
마이크로프로파일은 클라우드 네이티브와 마이크로서비스 기반 아키텍처를 위한 애플리케이션을 구축하는 데 탁월한 표준입니다. 자바 표준만을 고려하는 것 이상으로, 클라우드 네이티브 컴퓨팅 재단은 클라우드 네이티브 아키텍처 접근 방식을 위한 고려해야 할 플랫폼 요소와 프로젝트에 대한 지침을 제공합니다.
가상화는 클라우드 네이티브 스택에서 중요한 측면이며, 컴퓨팅의 주요 구성 요소입니다. 가상화에는 저수준의 세부 사항이 있지만, 더 큰 이점은 작업에 적합한 가상 머신 구성을 선택함으로써 얻을 수 있습니다. 클러스터 배포에는 베어메탈과 가상화를 혼합하는 것도 가능합니다.
이미지와 컨테이너는 클라우드 네이티브 환경에서 사실상의 배포 단위입니다. 자바를 위한 이미지를 생성하고 계층화하는 다양한 접근 방식과 함께 주의해야 할 일반적인 함정이 있습니다. 네트워킹은 클라우드 네이티브 스택의 중요한 요소이며, 이에 따라 서비스 검색과 트래픽 라우팅에 대한 고려가 필요합니다.
비교 / 트레이드오프
Dockerfile vs Jib vs 멀티스테이지 빌드
| 항목 | Dockerfile (단일) | Jib | 멀티스테이지 Dockerfile |
|---|---|---|---|
| 빌드 환경 | 도커 데몬 필요 | 메이븐/그래들 플러그인 | 도커 데몬 필요 |
| 자바 구조 인식 | 없음 | 있음 (의존성/리소스/클래스 분리) | 없음 |
| 베이스 이미지 업데이트 | 수동 | 자동화 가능 | 수동 |
| 빌더 도구 포함 여부 | 최종 이미지에 포함 가능 | 미포함 | 빌더 단계 폐기 |
| 이미지 크기 | 보통 | 작음 | 작음 |
| CI/CD 통합 | 별도 단계 | 메이븐 단계에 통합 | 별도 단계 |
컨테이너 vs 가상 머신
| 항목 | 컨테이너 | 가상 머신 |
|---|---|---|
| 격리 메커니즘 | 네임스페이스 + cgroups | 하이퍼바이저 |
| 커널 공유 | 호스트 커널 공유 | 게스트 OS 자체 커널 |
| 시작 시간 | 초 단위 | 분 단위 |
| 리소스 오버헤드 | 작음 | 큼 (게스트 OS 전체) |
| 격리 강도 | 보통 (커널 공유) | 강함 |
| 이미지 크기 | MB 단위 | GB 단위 |
북남 트래픽 vs 동서 트래픽
| 항목 | 북남 (외부 ↔ 내부) | 동서 (서비스 ↔ 서비스) |
|---|---|---|
| 진입 지점 | 인그레스 (고정 IP) | 내부 서비스 디스커버리 |
| 핵심 도구 | 로드 밸런서 | 쿠버네티스 서비스 / DNS |
| 보안 경계 | 외부 경계 (WAF, TLS) | 내부 경계 (mTLS, 서비스 메시) |
| 트래픽 패턴 | 비교적 예측 가능 | 폭발적, 패턴 다양 |
CNCF 성숙도 단계
| 단계 | 프로덕션 적합성 | 대표 예시 |
|---|---|---|
| 샌드박스 | 실험적 | 초기 단계 신규 프로젝트 |
| 인큐베이팅 | 채택 시작 가능 | 오픈텔레메트리(시점 기준) |
| 졸업 | 표준에 가까움 | 쿠버네티스, 프로메테우스 |
EC2 인스턴스 유형 선택 지침
| 워크로드 특성 | 권장 유형 | 이유 |
|---|---|---|
| 시작 단계, 워크로드 미정 | 일반 목적 | 균형 잡힌 비율 |
| 인메모리 캐싱, DB | 메모리 최적화 | 메모리 대역폭 우선 |
| 영상 인코딩, ML 학습 | 가속 컴퓨팅 | GPU 활용 |
| 카프카, 스파크 | 저장소 최적화 | 로컬 SSD I/O |
| HPC 시뮬레이션 | 고성능 컴퓨팅 | 코어 수와 대역폭 |
내 생각
마이크로프로파일은 자바의 생존 전략
자바 진영이 클라우드 네이티브 시대에 살아남기 위한 답이 마이크로프로파일입니다. Go가 도커·쿠버네티스 같은 인프라 도구로 인프라 언어가 되는 동안, 자바는 JWT 인증·상태체크·장애 허용처럼 마이크로서비스에 정말 필요한 기능들을 표준화함으로써 엔터프라이즈에서의 지위를 지켰습니다. 스프링부트가 사실상의 표준이지만, 마이크로프로파일은 쿼커스·헬리돈 같은 GraalVM 친화적 대안의 공통 토대 역할을 합니다.
CNCF 랜드스케이프는 기술 부채 회피의 첫 단계
새 프로젝트를 시작할 때 기술을 고르는 가장 빠른 방법이 CNCF 랜드스케이프를 보고 졸업/인큐베이팅 프로젝트 안에서 고르는 것입니다. 졸업 상태가 아닌 도구를 채택하면 6개월 뒤 메인테이너가 사라지거나 API가 깨질 위험을 감수해야 합니다. 이는 단순한 보수성이 아니라 운영 비용에 대한 합리적 헷지입니다.
Jib는 자바 빌드 파이프라인의 합리적 디폴트
도커 데몬 없이도 메이븐 한 줄로 이미지를 만들 수 있고, 변경된 레이어만 푸시하기 때문에 CI/CD가 극적으로 빨라집니다. 베이스 이미지 패치 추적도 자동화되니, “왜 우리 팀은 아직 Dockerfile을 직접 쓰고 있지?”라는 질문을 한 번쯤 던져볼 만합니다. 단점은 빌드 이외 단계(예: 정적 분석)에서 별도 도커 빌드가 필요한 경우 일관성이 깨질 수 있다는 점.
컨테이너의 격리는 보안이 아니라 패키징
컨테이너가 가상 머신과 동급의 격리를 제공한다고 오해하기 쉽지만, 호스트 커널을 공유하기 때문에 커널 익스플로잇 한 번으로 격리가 무너질 수 있습니다. 멀티테넌트 보안이 필요하면 Kata Containers, gVisor 같은 추가 레이어가 필요합니다. 컨테이너는 재현 가능한 배포 단위라는 측면이 진짜 가치이고, 격리는 부산물 정도로 보는 게 정확합니다.
임시 컴퓨팅 = 상태 외부화의 강제
스팟 인스턴스가 사전 통보 없이 종료되는 환경에서는 모든 상태를 외부 저장소로 빼놓는 설계가 강제됩니다. 12팩터 앱의 “프로세스는 stateless”라는 원칙이 클라우드의 비용 구조에서 자연스럽게 도출되는 것입니다. 로컬 디스크에 캐시를 두는 옛 패턴은 사라지고, Redis/S3 같은 외부 의존이 디폴트가 된 배경입니다.
이미지 크기는 콜드 스타트와 직결
UBI 미니멀이 37 MB, OpenJDK까지 합치면 147 MB. 오토스케일링이 잦은 환경이라면 이미지 풀(pull) 시간이 콜드 스타트 지연의 큰 부분을 차지합니다. GraalVM 네이티브 이미지로 가면 jar+JVM 대신 단일 실행 파일이 들어가니 50 MB 이하도 가능합니다. 람다 같은 FaaS에서 자바가 그동안 불리했던 이유와 GraalVM이 답이 되는 이유가 여기에 있습니다.
더 알아볼 것
- 마이크로프로파일 6.1 명세를 읽고 각 표준이 스프링부트 어노테이션과 어떻게 매핑되는지 정리
- CNCF 랜드스케이프(landscape.cncf.io)에서 관측성 카테고리 졸업 프로젝트 비교
- Jib 메이븐 플러그인을 기존 스프링부트 프로젝트에 적용하고 이미지 크기/빌드 시간 측정
- 동일 애플리케이션을 단일 Dockerfile / 멀티스테이지 / Jib 세 방식으로 빌드해 레이어 구조
docker history로 비교 - EC2의 일반 목적(t/m), 컴퓨팅 최적화(c), 메모리 최적화(r) 인스턴스에서 동일 워크로드의 비용/성능 비교
- 쿠버네티스 클러스터에서 인그레스(north-south)와 서비스(east-west)의 응답 시간 차이 측정
- OCI 스펙(opencontainers.org) 정독: 이미지 매니페스트와 런타임 스펙
- Fighting Animals 예제를 로컬에서 도커 컴포즈로 실행하고
Auto_otel변형의 트레이스 관찰
관련 개념
- Ch07 하드웨어와 운영 시스템 — 단일 머신 성능 분석의 한계, 이 챕터가 다음 단계
- 마이크로프로파일
- 쿠버네티스
- 오픈텔레메트리
- OCI 컨테이너 이미지
- 12팩터 앱
- 하이퍼바이저
- 서비스 디스커버리
출처
자바 최적화 2판 (오라일리), Ben Evans 외 저, Chapter 8: 클라우드 스택의 구성 요소