resource-management

4 개의 포스트

Amazon ECS 관리형 인스턴스를 위한 관리형 데몬 지원 발표 | Amazon Web Services (새 탭에서 열림)

Amazon ECS가 관리형 인스턴스(Managed Instances)에서 독립적인 에이전트 관리가 가능한 '관리형 데몬' 기능을 출시했습니다. 이제 플랫폼 엔지니어는 애플리케이션 팀의 배포 주기와 무관하게 모니터링, 로깅, 트레이싱 도구를 중앙에서 제어하고 업데이트할 수 있습니다. 이 기능은 데몬이 애플리케이션보다 먼저 시작되고 나중에 종료되도록 보장하여, 가시성의 공백 없는 안정적인 운영 환경을 제공합니다. ### 운영 효율성을 높이는 생명주기 분리 * 플랫폼 팀이 모니터링 및 로깅 에이전트를 애플리케이션 작업과 분리하여 독립적으로 배포, 업데이트 및 수정할 수 있습니다. * 에이전트 업데이트 시 애플리케이션 작업 정의(Task Definition)를 수정하거나 서비스를 재배포할 필요가 없어 운영팀 간의 조율 부담이 사라집니다. * '선 실행 후 종료(Start before stop)' 메커니즘을 통해 모든 인스턴스에서 애플리케이션이 실행되기 전 데몬이 먼저 활성화되도록 보장합니다. ### 유연한 자원 할당 및 배포 제어 * 특정 용량 공급자(Capacity Provider)를 대상으로 데몬을 배포할 수 있어 인프라 전반에 걸친 유연한 롤아웃이 가능합니다. * 데몬 전용 작업 정의를 통해 CPU 및 메모리 파라미터를 별도로 관리하며, 인스턴스당 단 하나의 데몬만 실행되어 시스템 자원 활용을 최적화합니다. * 배포 시 ECS가 인스턴스 교체 및 작업 마이그레이션을 자동으로 수행하며, 자동 롤백 기능을 통해 업데이트 중 발생할 수 있는 리스크를 최소화합니다. ### 심층적인 호스트 접근과 네트워킹 기술 * 새로운 `daemon_bridge` 네트워크 모드를 도입하여 애플리케이션 네트워크 구성과 격리된 상태에서 데몬과 작업 간의 통신을 지원합니다. * 권한 부여된 컨테이너(Privileged container), Linux 기능(Capabilities) 추가, 호스트 파일 시스템 경로 마운트 등 강력한 호스트 수준 접근 권한을 제공합니다. * 이러한 심층 접근 권한은 시스템 호출 모니터링이나 보안 검사 등 호스트 레벨의 세밀한 가시성이 필요한 도구 운영에 필수적입니다. 관리형 데몬 서비스는 추가 비용 없이 사용한 컴퓨팅 자원에 대해서만 요금이 부과됩니다. CloudWatch 에이전트나 타사 보안 솔루션을 운영하는 플랫폼 팀은 애플리케이션 가용성에 영향을 주지 않으면서 운영 도구를 최신 상태로 유지하기 위해 이 기능을 즉시 도입하는 것을 권장합니다.

변화하는 세상에서의 스케 (새 탭에서 열림)

클라우드 인프라의 가용 자원이 끊임없이 변동하는 환경에서 중단 없이 실행되어야 하는 비선점형(Non-preemptive) 작업들을 효율적으로 배치하기 위한 새로운 알고리즘이 제시되었습니다. Google Research는 이번 연구를 통해 가용량이 시간에 따라 변하는 환경에서도 작업 처리량(Throughput)을 최대로 확보할 수 있는 최초의 상수 요인(Constant-factor) 근사 알고리즘을 개발했습니다. 이 알고리즘은 변동성이 큰 클라우드 환경에서 작업 손실을 최소화하고 스케줄러의 안정성을 이론적으로 보장하는 기틀을 마련했습니다. ### 동적 클라우드 환경과 스케줄링의 난제 * 현대 클라우드 환경은 하드웨어 장애, 유지보수, 고순위 작업의 자원 점유 등으로 인해 가용 자원이 실시간으로 변동하는 특성을 가집니다. * 특히 비선점형 작업은 한 번 시작하면 중간에 멈출 수 없으며, 자원 부족으로 중단될 경우 지금까지의 모든 작업 진행 내용이 소실되는 리스크가 있습니다. * 스케줄러는 각 작업의 방출 시간(Release time), 마감 기한(Deadline), 처리 시간, 가중치를 고려하여 전체 처리량의 합계(가중치 또는 작업 수)를 극대화해야 합니다. ### 오프라인 설정에서의 최적화 전략 * 미래의 작업 도착 정보와 자원 변동 추이를 미리 알고 있는 오프라인 환경에서는 단순한 그리디(Greedy) 전략이 효과적임이 입증되었습니다. * 가장 먼저 끝나는 작업을 우선 배치하는 그리디 알고리즘은 동일 가치 작업들을 스케줄링할 때 최적해의 최소 1/2 성능을 보장(1/2-approximation)합니다. * 작업마다 가치가 다른 가중치 모델의 경우, Primal-dual 프레임워크를 활용하여 최적해의 1/4 성능을 보장하는 알고리즘을 구현했습니다. ### 온라인 환경의 복잡성과 중단 모델 * 실시간으로 작업이 도착하는 온라인 환경에서는 단 하나의 잘못된 결정(긴 작업 배치)이 미래의 수많은 짧은 작업을 막을 수 있어 기존 방식의 효율성이 급격히 떨어집니다. * **재시작 허용 모델(Interruption with restarts):** 작업 중단 시 진행 데이터는 소실되지만 나중에 다시 시도할 수 있는 모델로, 오프라인과 동일하게 1/2 수준의 경쟁비(Competitive ratio)를 달성할 수 있습니다. * **재시작 불가 모델(Interruption without restarts):** 중단된 작업을 영구히 폐기해야 하는 엄격한 모델로, 일반적인 상황에서는 효율적인 스케줄링이 어렵지만 '공통 마감일'이 있는 실무적 시나리오에서는 해결책을 찾았습니다. ### 공통 마감일 시나리오를 위한 상수 경쟁 알고리즘 * 모든 작업이 동일한 마감 시한을 가지는 실제 배치 작업 환경을 위해 최초의 상수 경쟁 알고리즘(1/11 경쟁비)을 설계했습니다. * 이 알고리즘은 새로운 작업이 도착할 때마다 다음 네 가지 우선순위에 따라 잠정적 스케줄을 갱신합니다. 1. 빈 시간대에 작업 추가. 2. 기존에 예약된 미래 작업보다 현저히 작은 작업으로 교체. 3. 현재 실행 중인 작업의 남은 시간보다 도착한 작업이 더 짧을 경우 실행 중인 작업 중단 및 교체. 4. 위 조건에 해당하지 않을 경우 새 작업 폐기. 이 연구 결과는 자원 공급이 불규칙한 클라우드 시스템에서 이론적 보장을 갖춘 견고한 스케줄러를 구축할 수 있는 근거를 제공하며, 특히 저순위 배치 작업의 효율성을 극대화하는 데 실질적인 도움을 줄 수 있습니다.

코드 품질 개선 기법 20편: 이례적인 예외 과대 포장 (새 탭에서 열림)

리소스를 안전하게 해제하기 위해 사용하는 `use` 패턴이나 커스텀 예외 처리 구현 시, 발생한 여러 예외를 하나의 커스텀 예외로 감싸서(wrapping) 던지는 것은 주의해야 합니다. 이러한 '과대 포장'은 호출자가 기대하는 특정 예외 유형을 가려버려 예외 처리 로직을 무력화시키고 디버깅을 어렵게 만듭니다. 따라서 여러 예외가 동시에 발생할 때는 원인이 되는 주요 예외를 우선시하고, 부수적인 예외는 `addSuppressed`를 통해 전달하는 것이 올바른 품질 개선 방향입니다. ### 예외 과대 포장의 부작용 * 리소스 해제 과정에서 발생하는 예외까지 관리하기 위해 `DisposableException` 같은 별도의 예외 클래스로 감싸게 되면, 원래 발생한 구체적인 예외 정보(예: `IOException`)가 추상화되어 버립니다. * 이 경우 호출부에서 특정 예외를 잡기 위해 작성한 `catch(e: IOException)` 문이 작동하지 않게 되어, 의도치 않은 런타임 오류로 이어질 수 있습니다. * 특히 유틸리티 함수나 보조 함수 내부에서 이러한 포장이 일어날 경우, 호출자는 내부 구현을 상세히 알기 전까지는 예외 처리 실패의 원인을 파악하기 매우 어렵습니다. ### `addSuppressed`를 활용한 예외 우선순위 설정 * 한 코드 블록에서 비즈니스 로직과 리소스 해제(dispose) 로직 모두 예외가 발생할 수 있다면, 어떤 예외가 더 중요한지 판단하여 우선순위를 정해야 합니다. * 일반적으로 비즈니스 로직이 실행되는 `block`에서 발생한 예외가 핵심적인 정보를 담고 있으므로 이를 우선적으로 `throw`해야 합니다. * 리소스 해제 시 발생하는 보조적인 예외는 버리지 않고, 주요 예외의 `addSuppressed` 메서드에 추가함으로써 전체적인 예외 맥락을 보존하면서도 타입 시스템을 해치지 않을 수 있습니다. ### 언어별 예외 처리 시 주의사항 * **Kotlin:** `Closeable.use` 확장 함수는 이미 `addSuppressed`를 활용하여 주요 예외를 우선하는 방식으로 구현되어 있으므로, 커스텀 리소스 클래스 제작 시에도 이와 유사한 패턴을 따르는 것이 좋습니다. * **Java:** Checked Exception이 존재하는 Java에서는 예외를 다른 타입으로 감쌀 때 상속 관계를 신중히 고려해야 합니다. * 복구가 불가능한 경우가 아니라면 Checked Exception을 `RuntimeException`으로 함부로 변환하여 던지지 않아야 하며, 부모 예외 타입으로 뭉뚱그려 잡는 과정에서 예외 처리 누락이 발생하지 않도록 주의가 필요합니다. 리소스 해제와 같은 부수적인 작업에서 발생하는 예외가 본래의 실행 목적을 가진 코드의 예외를 덮어쓰지 않도록 설계해야 합니다. 항상 "어떤 예외가 개발자나 시스템에게 더 중요한 정보인가"를 고민하고, 언어에서 제공하는 예외 억제(suppression) 기능을 활용해 예외의 층위를 명확히 관리할 것을 권장합니다.

우리 작업 시스템에서 쿠버네티스 오버헤드를 최소화한 방법 (새 탭에서 열림)

Datadog은 기존 작업 시스템을 Kubernetes로 이전하는 과정에서 CPU 사용량은 증가하고 작업 처리 속도는 40-50% 저하되는 성능 퇴행 문제를 겪었습니다. 이를 해결하기 위해 VM과 Kubernetes 간의 정밀한 비교 실험을 설계하고, 지표 측정 방식과 리소스 할당(Resource Requests) 설정을 최적화하여 성능을 이전 수준으로 복구했습니다. 본 분석을 통해 Kubernetes 오버헤드의 실체를 파악하고, 고성능 워크로드를 위한 포드 배치 전략을 도출했습니다. ### 실험 환경의 통제와 정렬 * **환경 변수 통일**: 성능 차이의 원인을 정확히 규명하기 위해 VM과 Kubernetes 클러스터의 인스턴스 유형(c5.2xlarge), 커널 버전(3.13.0-141), 실행 스크립트를 동일하게 맞추어 비교 대상을 단일화했습니다. * **배치 구조 최적화**: 기존 VM 방식과 유사하게 하나의 포드 내에 하나의 부모 프로세스와 그 자식 워커들을 배치하여, 노드당 부모 프로세스 수와 포드 수가 일치하도록 구성했습니다. ### 측정 지표의 재정의: Idle CPU의 중요성 * **Load Average의 한계**: Kubernetes에서는 상태 확인 등을 위한 배경 프로세스가 빈번하게 실행되는데, 이는 실제 CPU 사용량과 무관하게 Load Average 수치를 비정상적으로 높여 시스템이 바쁜 것처럼 오해하게 만듭니다. * **Idle CPU 활용**: 프로세스 개수가 아닌 '실제 CPU가 일하지 않는 시간'을 측정하는 Idle CPU 지표를 선택함으로써, 시스템의 남은 용량을 더 정확하게 파악하고 성능 분석의 신뢰도를 높였습니다. * **처리량(Throughput) 중심**: 배치 작업의 특성에 맞춰 지연 시간(Latency)보다는 30초당 완료된 작업 수라는 처리량 지표를 핵심 성능 지표로 설정했습니다. ### 리소스 요청(Resource Requests) 및 스케줄링 튜닝 * **스케줄링 병목 해결**: 초기에 각 포드가 1 Core CPU를 요청하도록 설정했을 때, 노드당 4개의 포드만 배치되는 과소 활용 문제가 발생했습니다. 목표치인 노드당 6개 포드 배치를 위해 CPU 요청을 100m으로, 메모리 요청을 500MB로 대폭 낮췄습니다. * **단일 리소스 기준 권장**: 여러 리소스(CPU, 메모리 등)의 요청 값을 모두 엄격하게 잡으면 스케줄링이 복잡해지므로, 하나의 주된 리소스를 기준으로 배치를 유도하고 나머지는 실제 필요량에 가깝게 설정하는 것이 효율적임을 확인했습니다. * **Request와 Limit의 구분**: `request`는 스케줄링을 위한 최소 보장치이며, 실제 실행 중의 제약은 `limit`이 담당하므로 `request`를 낮추는 것이 실행 성능에 부정적인 영향을 주지 않는다는 점을 활용했습니다. ### 포드별 오버헤드의 실체 분석 * **프로세스 구조**: `pstree`를 통해 분석한 결과, 포드당 오버헤드는 주로 컨테이너 런타임인 `containerd-shim`에서 발생했습니다. * **CPU 및 메모리 비용**: 실험 결과 포드당 CPU 오버헤드는 무시할 수 있는 수준이었으며, 메모리는 포드당 약 24MB(containerd-shim 및 pause 컨테이너 포함) 수준으로 측정되었습니다. * **결론적 선택**: 오버헤드가 크지 않기 때문에, 관리 효율성을 위해 포드 하나에 여러 부모 프로세스를 억지로 집어넣기보다 '포드당 1 부모 프로세스' 구조를 유지하는 것이 더 유리하다는 결론을 내렸습니다. Kubernetes로 이전 시 발생하는 성능 저하는 플랫폼 자체의 문제라기보다 잘못된 리소스 요청 설정과 지표 해석에서 기인하는 경우가 많습니다. 노드당 포드 밀도를 최적화하기 위해 `Resource Requests`를 전략적으로 낮게 설정하고, 시스템의 부하를 판단할 때는 Load Average 대신 Idle CPU를 관찰함으로써 VM에 근접한 성능을 확보할 수 있습니다.