resource-allocation

2 개의 포스트

신뢰성 향상을 위한 SLO/SLI 도입 3편 - 서비스 적용 사례 (새 탭에서 열림)

SLI(Service Level Indicator)와 SLO(Service Level Objective)의 도입은 단순히 지표를 설정하는 기술적 작업을 넘어, 사용자 중심의 관점으로 서비스를 재정의하고 조직의 협업 문화를 구축하는 과정입니다. 정량적인 지표와 오류 예산(Error Budget) 개념을 활용하면 서비스의 신뢰성과 비즈니스 혁신 사이에서 객관적인 판단 근거를 마련할 수 있습니다. 결과적으로 SLO는 사용자에게 안정적인 경험을 제공하는 동시에 엔지니어링 리소스를 효율적으로 배분하는 핵심 도구가 됩니다. **신뢰성 향상을 위한 마인드셋과 협업** * **사용자 여정 중심의 사고**: 서비스의 수많은 기능 중 사용자가 가장 많이 사용하거나 반드시 필요한 '핵심 사용자 여정(CUJ)'을 식별하는 것이 첫걸음입니다. * **다학제적 협업 체계**: 서비스 담당 부서(CUJ 정의), 인프라 조직(측정 환경 구축), SRE(도구 구현 및 모니터링) 등 이해관계자 모두가 목표를 공유하고 책임을 나누는 문화가 필수적입니다. **SLI/SLO 구현의 4단계 프로세스** * **CUJ 분석**: 가입, 메시지 송수신, 인증과 같이 비즈니스 목표와 사용자 경험에 직결되는 핵심 기능을 사용자 관점에서 선별합니다. * **SLI 정의**: 게이트웨이나 백엔드 등 적절한 측정 위치를 선정하고, 응답 시간(Latency)의 퍼센타일과 응답 성공률(Success Rate)에 대한 명확한 성공/실패 기준을 수립합니다. * **SLO 타깃 설정**: 28일 등 특정 기간 동안 달성할 현실적인 목표 수치를 정하며, 안정성 확보 비용과 사용자 경험 사이의 적절한 균형점을 찾습니다. * **시각화**: 전체 상태와 오류 예산 현황을 한눈에 파악할 수 있도록 대시보드를 구성하며, 색상(초록/주황/빨강)을 활용해 직관적인 인지성을 높입니다. **오류 예산을 활용한 의사 결정 및 운영** * **정량적 소통**: '서비스가 느리다'는 추상적 표현 대신 '응답 시간이 SLI 기준인 400ms를 초과했다'는 식의 정확한 데이터로 문제를 파악합니다. * **리소스 배분 가이드**: 오류 예산이 넉넉하면 신규 기능 출시나 공격적인 릴리스에 집중하고, 예산이 소진되어 가며 안정성 강화와 이슈 대응에 리소스를 우선 투입합니다. * **온콜(On-call) 및 예방**: 오류 예산의 상태 변화를 실시간 알림으로 받아 이슈에 신속히 대응하며, 정기적인 SLO 점검을 통해 서비스 품질을 지속적으로 관리합니다. 성공적인 SRE 문화를 정착시키기 위해서는 SLO를 단순한 규제가 아닌, 서비스의 안정성과 혁신 속도 사이에서 균형을 잡아주는 나침반으로 활용하는 것이 중요합니다. 측정 가능한 지표를 통해 막연한 불안감을 해소하고, 데이터에 기반한 의사결정을 내릴 때 비로소 지속 가능한 서비스 신뢰성을 확보할 수 있습니다.

Pinterest의 Apache Spark에서 (새 탭에서 열림)

Pinterest는 대규모 Spark 환경에서 빈번하게 발생하는 OOM(Out-of-Memory) 오류를 해결하기 위해 'Auto Memory Retries' 기능을 도입했습니다. 이 시스템은 태스크 수준에서 리소스 요구량을 동적으로 판단하고, 실패 시 더 큰 메모리 프로필을 가진 실행기(Executor)에서 태스크를 재시도하도록 자동화합니다. 이를 통해 수동 튜닝의 번거로움을 줄이고 자원 효율성을 높여 전체적인 작업 실패율과 운영 비용을 획기적으로 낮추는 성과를 거두었습니다. ### 기존 Spark 리소스 관리의 한계와 문제점 * Pinterest의 Spark 클러스터는 하드웨어 대비 높은 메모리 요구량으로 인해 OOM 오류가 잦았으며, 전체 작업 실패 원인의 약 4.6%가 메모리 부족에서 기인했습니다. * 사용자가 모든 스테이지와 태스크의 메모리 요구량을 정확히 예측하여 수동으로 설정하는 것은 매우 어렵고 시간이 많이 소요되는 작업입니다. * 데이터 스큐(Skew) 현상으로 인해 같은 스테이지 내에서도 특정 태스크만 과도한 메모리를 사용하는 경우가 많아, 모든 태스크를 최대치에 맞춰 설정하면 심각한 자원 낭비가 발생합니다. * 제품 팀의 우선순위 문제로 인해 비용 절감을 위한 수동 최적화가 지속적으로 이루어지기 어려운 구조적 한계가 있었습니다. ### Auto Memory Retries의 단계별 대응 전략 * **CPU 할당량 증설을 통한 메모리 확보 (1단계):** 실행기에 1개 이상의 코어가 있는 경우, OOM 발생 시 첫 번째 재시도에서 태스크당 CPU 할당량(`spark.task.cpus`)을 두 배로 늘립니다. 이를 통해 실행기 내 동시 실행 태스크 수를 줄여 개별 태스크가 사용할 수 있는 공유 메모리 공간을 즉각적으로 확보합니다. * **물리적으로 큰 실행기 투입 (2단계):** CPU 조절만으로 해결되지 않거나 단일 태스크가 이미 실행기 전체 메모리를 사용 중인 경우, 물리적으로 더 큰 메모리를 가진 새로운 실행기를 동적으로 런칭합니다. * **하이브리드 확장 프로필 적용:** 기본 설정의 2배, 3배, 4배 크기의 리소스 프로필을 미리 등록하고 단계별로 순차 적용합니다. Apache Gluten을 사용하는 워크로드의 경우 Off-heap 메모리도 함께 증설하여 가속화된 연산을 지원합니다. ### 시스템 구현 및 Spark 엔진 확장 * **태스크 수준의 리소스 프로필:** 기존 Spark의 고정된 리소스 할당 방식에서 벗어나, `Task` 객체에 개별 리소스 프로필 ID(`taskRpId`)를 저장할 수 있도록 확장하여 동일한 TaskSet 내에서도 태스크마다 사양을 다르게 가질 수 있게 구현했습니다. * **스케줄링 로직 최적화:** `TaskSetManager`는 OOM 감지 시 즉시 상위 프로필을 할당하며, `TaskSchedulerImpl`은 증설된 CPU 속성을 가진 태스크를 기존 실행기에서 우선 실행할 수 있게 하여 리소스 재사용 속도를 높였습니다. * **동적 리소스 할당:** `ExecutorAllocationManager`가 상위 프로필을 필요로 하는 대기 태스크를 실시간으로 추적하고, 물리적으로 큰 실행기가 필요한 시점에 맞춰 Kubernetes 등에 자원을 요청합니다. * **사용자 경험 개선:** 사용자가 어떤 태스크가 더 많은 자원을 사용했는지 쉽게 파악할 수 있도록 Spark UI의 태스크 목록에 리소스 프로필 ID를 표시하는 기능을 추가했습니다. 효율적인 Spark 운영을 위해서는 모든 작업을 최대 메모리 요구량에 맞추기보다, 상위 90%(P90) 수준의 일반적인 설정으로 실행하고 예외적인 태스크만 'Auto Memory Retries'로 구제하는 탄력적 전략이 권장됩니다. 이는 데이터 스큐가 심한 대규모 파이프라인에서 운영 안정성을 확보함과 동시에 인프라 비용을 최적화할 수 있는 강력한 해법이 될 것입니다.