jvm

3 개의 포스트

수천 개의 API/BATCH 서버를 하나의 설정 체계로 관리하기 (새 탭에서 열림)

토스페이먼츠는 수천 개의 API 서버와 배치 설정을 관리하기 위해 설정을 단순한 텍스트가 아닌 '진화하는 코드'로 정의하여 운영합니다. 복사-붙여넣기식의 중복 설정을 제거하기 위해 오버레이 아키텍처와 템플릿 패턴을 도입했으며, 이를 통해 오타나 설정 오류로 인한 대규모 정산 장애 리스크를 원천 차단합니다. 결과적으로 인프라 설정을 테스트 가능한 영역으로 끌어올려 대규모 하이브리드 클라우드 환경에서도 높은 안정성과 유연성을 확보했습니다. ### 실시간 API 서버: 오버레이와 템플릿의 결합 * **오버레이 아키텍처:** 설정을 `global`, `cluster`, `phase`, `application` 순서의 계층형 구조로 설계하여 하위 계층이 상위 계층의 기본값을 덮어쓰도록 구성했습니다. 이를 통해 공통 설정은 한 번만 정의하고 각 환경에 필요한 차이점만 관리할 수 있습니다. * **템플릿 패턴 도입:** YAML의 단순 오버레이만으로는 해결하기 어려운 긴 문자열(예: JVM 옵션) 내의 특정 값만 수정하기 위해 `{{MAX_HEAP}}`과 같은 변수 치환 방식을 사용합니다. * **동적 설정 주입:** 설정 파일 내부에 파이썬 스크립트를 삽입하여 랜덤 포트 생성이나 외부 API 호출을 통한 동적 값 할당이 가능하며, 클러스터 이름에 따른 조건부 로직을 적용해 복잡한 환경 변수 요구사항을 해결합니다. ### 배치 서버: DSL과 GitOps를 통한 단순화 * **Jenkins 기반의 단순화:** 대규모 정산 데이터를 다루는 배치 환경일수록 단순함이 강력하다는 원칙 아래, Jenkins를 활용하면서도 수동 조작의 단점을 보완하는 방향을 택했습니다. * **Groovy DSL 활용:** Jenkins의 웹 UI를 통한 수동 설정을 배제하고, Groovy 기반의 자체 DSL(Domain Specific Language)을 구축하여 수천 개의 배치 Job을 코드 형태로 관리합니다. * **GitOps 체계:** 모든 배치 설정을 코드 저장소에서 관리하고 CI/CD 파이프라인과 통합함으로써, 개발자가 직접 Jenkins에 접속하지 않고도 표준화된 환경에서 배치 작업을 배포할 수 있도록 개선했습니다. ### 인프라의 코드화와 검증 자동화 * **테스트 가능한 설정:** 설정값에 대한 오타나 논리적 오류를 방지하기 위해 설정 코드에 대한 유닛 테스트를 수행합니다. 이를 통해 수천 개의 설정 중 단 하나의 오타가 치명적인 금융 장애로 이어지는 것을 사전에 방지합니다. * **유연한 확장성:** 고정된 설정 체계에 안주하지 않고, 인프라의 변화와 개발자의 요구사항에 맞춰 설정 인프라 자체가 계속해서 진화할 수 있는 구조를 지향합니다. 단순히 설정 파일을 잘 작성하는 것에 그치지 않고, 인프라 설정을 애플리케이션 코드와 동일한 수준의 설계와 테스트를 거쳐 관리하는 것이 대규모 시스템의 안정성을 보장하는 핵심입니다. 초기에 다소 복잡해 보일 수 있는 오버레이나 DSL 도입은 장기적으로 중복을 제거하고 휴먼 에러를 막는 가장 확실한 투자입니다.

네이버 TV (새 탭에서 열림)

JVM 기반 웹 애플리케이션은 실행 초기 JIT(Just-In-Time) 컴파일러의 최적화 과정에서 발생하는 응답 지연 문제를 해결하기 위해 '웜업' 과정이 필수적입니다. 기존의 API 호출식 웜업은 데이터 오염이나 외부 시스템 부하와 같은 부작용을 초래할 수 있으나, 본 발표에서는 이를 극복하기 위해 핵심 라이브러리만을 직접 예열하는 '라이브러리 웜업' 방식을 제안합니다. 이 기술을 통해 부작용 없이 애플리케이션 배포 직후의 성능을 안정적으로 확보할 수 있습니다. **JVM 웜업의 필요성과 기존 방식의 한계** * JVM은 실행 초기에 인터프리터 방식으로 동작하다가, 반복되는 코드를 JIT 컴파일러가 네이티브 코드로 최적화하는 과정을 거치며 성능이 올라갑니다. * 이 최적화가 완료되기 전까지는 응답 시간이 길어지거나 CPU 사용량이 급증하는 현상이 발생하므로, 실제 트래픽이 들어오기 전 코드를 미리 실행하는 웜업이 필요합니다. * 기존의 API 호출 방식은 가짜 요청을 보내는 과정에서 DB 데이터 정합성을 해칠 수 있고, 외부 API 호출에 따른 불필요한 연동 부하를 발생시키는 단점이 있습니다. **라이브러리 웜업의 핵심 아이디어와 구현** * 비즈니스 로직 전체를 수행하는 대신, 애플리케이션에서 성능 비중이 크고 공통적으로 사용되는 '라이브러리 코드'만을 타겟팅하여 예열합니다. * 예를 들어 JSON 파싱, 암호화, 복잡한 수치 계산 모듈 등 JIT 컴파일 임계치(Threshold)를 넘겨야 하는 핵심 메서드들을 반복 호출하도록 설계합니다. * 애플리케이션 시작 단계(Post-Construct 등)에서 비즈니스 로직과는 독립된 웜업 코드를 실행함으로써 데이터 오염의 위험을 원천적으로 차단합니다. **성능 검증 및 실무적 이점** * 라이브러리 웜업 적용 후, 배포 초기에 발생하는 응답 속도의 '튀는 현상(Spike)'이 현저히 감소하고 전체적인 레이턴시가 안정화됨을 확인했습니다. * API 호출 방식보다 구현이 단순하고 외부 의존성이 적어 관리가 용이하며, 배포 파이프라인의 안정성을 높이는 데 기여합니다. * 다만, 모든 비즈니스 경로를 커버하지는 못하므로 성능 영향도가 높은 핵심 모듈을 선별하여 집중적으로 웜업하는 전략이 유효합니다. 빠른 스케일 아웃이 필요한 마이크로서비스 환경이나 지연 시간에 민감한 실시간 서비스라면, API 기반 웜업의 대안으로 이와 같은 라이브러리 단위의 정밀한 웜업 도입을 적극 권장합니다.

에어비앤비 (새 탭에서 열림)

에어비앤비는 4.5년에 걸쳐 수천만 라인의 Java, Kotlin, Scala 코드로 구성된 대규모 JVM 모노레포를 Gradle에서 Bazel로 성공적으로 이전했습니다. 이번 마이그레이션을 통해 빌드 속도는 3~5배, IDE 동기화 및 배포 속도는 2~3배 향상되었으며, 개발자 만족도(CSAT)가 38%에서 68%로 크게 올랐습니다. Bazel의 밀폐성(Hermeticity)과 원격 실행 기능을 활용하여 대규모 코드베이스에서도 안정적이고 확장 가능한 빌드 시스템을 구축한 것이 핵심 성과입니다. **Gradle에서 Bazel로 전환한 이유** * **빌드 속도의 혁신:** Bazel의 원격 빌드 실행(RBE)을 통해 수천 개의 작업을 병렬로 처리하며, 'Build without the Bytes' 기능을 도입하여 필요한 아티팩트만 다운로드함으로써 대역폭 소모를 줄였습니다. * **빌드 안정성 및 밀폐성:** Gradle과 달리 샌드박스 환경을 제공하여 빌드 작업이 지정된 입력 외의 파일 시스템(예: /tmp 디렉토리)에 접근하는 것을 차단하고, 환경 차이로 인한 빌드 실패를 방지했습니다. * **통일된 인프라 구축:** 에어비앤비 내의 웹, iOS, Python, Go 등 다양한 언어의 레포지토리를 Bazel로 단일화하여 원격 캐싱, 로깅, 변경된 타겟 계산 로직을 공유할 수 있게 되었습니다. **단계적 마이그레이션과 개념 증명(PoC)** * **Viaduct 플랫폼 선정:** 에어비앤비에서 가장 크고 복잡한 서비스 중 하나인 GraphQL 모놀리스 'Viaduct'를 첫 타겟으로 선정하여, 가장 까다로운 케이스에서 성능 이점을 증명했습니다. * **공존 전략:** 초기에는 개발자가 Gradle과 Bazel 중 선택해서 사용할 수 있도록 두 시스템을 병렬로 운영하여 서비스 중단 위험을 최소화했습니다. * **개발자 설득:** 단순한 성능 향상을 넘어, 초기 단계에서 발생한 버그와 통합 문제를 해결하여 개발자들이 자발적으로 Bazel을 선택하도록 유도했습니다. **자동 빌드 파일 생성 및 유지보수** * **커스텀 생성기 개발:** Bazel 빌드 파일(BUILD)을 수동으로 관리하는 불편을 줄이기 위해 소스 코드의 패키지와 임포트 구문을 분석하여 의존성 그래프를 그리는 자동 생성기를 구축했습니다. * **Gazelle의 영감:** Go 언어의 Gazelle 도구에서 아이디어를 얻었으나, JVM 언어의 특성과 성능 요구사항을 맞추기 위해 캐싱 기능을 포함한 자체 도구로 발전시켰습니다. * **CI 통합:** 모든 커밋 전에 자동 생성기를 실행하여 Gradle과 Bazel의 빌드 그래프가 항상 일치하도록 유지했습니다. **IDE 사용자 경험 개선** * **IntelliJ 동기화 최적화:** 대규모 모노레포에서 Gradle 동기화가 최대 40분까지 소요되던 문제를 Bazel의 병렬 분석과 'Query Sync(실험적 기능)' 도입을 통해 3~10분 수준으로 단축했습니다. * **IntelliJ Aspect 활용:** Bazel의 Aspect 기능을 사용하여 프로젝트 구조 정보를 추출함으로써 IDE가 소스 코드와 의존성을 더 효율적으로 이해하도록 돕습니다. **성공적인 전환을 위한 교훈** 대규모 마이그레이션에서 가장 중요한 것은 **성능에 대한 집착**과 **개발자 경험(DevEx)에 대한 투자**입니다. 빌드 속도가 빨라지면 개발자들은 자연스럽게 새로운 도구를 수용하게 되며, 특히 IntelliJ와 같은 IDE와의 매끄러운 통합이 프로젝트의 성패를 좌우합니다. 또한 빌드 파일 생성과 같은 반복적인 작업을 자동화하여 개발자가 시스템 환경 설정이 아닌 코드 작성에만 집중할 수 있는 환경을 조성하는 것이 필수적입니다.