MiniBin 엔지니어링 노트

클라우드 Mac CI 메모리 압박 진단과 동시성 제어

클라우드 Mac CI 메모리 압박 진단과 동시성 제어

동일한 CI 파이프라인이 어떤 때는 18분 만에 끝나지만, 어떤 때는 테스트 단계에서 멈춘 뒤 재실행하면 정상적으로 완료되기도 합니다. 이 현상을 곧바로 ‘일시적인 머신 성능 저하’로 단정해서는 안 됩니다. 클라우드 Mac에서는 컴파일러, 링커, 시뮬레이터, 테스트 프로세스가 통합 메모리를 동시에 사용합니다. 빌드 결과만으로는 코드 문제인지, 과도한 동시성 때문인지, 아니면 시스템이 지속적인 메모리 압축과 페이징 상태에 들어간 것인지 구분할 수 없습니다.

먼저 재현 가능한 관측 구간 정의하기

진단할 때는 커밋, 의존성 캐시 상태, 빌드 명령, 테스트 세트를 동일하게 유지해야 합니다. 문제를 안정적으로 재현하는 작업 하나를 선택하고 시작 시각, 종료 시각, 종료 코드, 실패 단계를 기록합니다. 최초 샘플링 중에는 캐시를 비우면서 동시성까지 변경하지 마세요. 여러 변수를 동시에 바꾸면 어느 변경이 효과를 냈는지 판단할 수 없습니다.

먼저 물리 메모리와 스왑 공간의 기준값을 확인합니다.

sysctl -n hw.memsize
sysctl vm.swapusage
memory_pressure -Q
vm_stat

hw.memsize는 물리 메모리의 바이트 수입니다. vm.swapusage는 현재 스왑 공간 사용량을 보여 주며, vm_stat의 페이지 크기, 압축된 페이지, 페이지 인·아웃 카운터로 추세를 파악할 수 있습니다. 특정 시점의 수치를 임계값으로 간주하기보다 작업 전후의 증감량에 주목해야 합니다.

여유 메모리가 적다고 해서 메모리가 부족한 것은 아닙니다. macOS는 파일 페이지를 캐시하는 데 메모리를 적극적으로 활용합니다. 실제로 확인해야 할 징후는 메모리 압력이 계속 높아지고 스왑 공간이 지속적으로 증가하면서, 동시에 빌드 시간이 늘어나거나 프로세스가 종료되는 현상입니다.

빌드 중 지속적으로 샘플링하기

진단 명령은 빌드 명령과 별도로 실행해야 합니다. 그래야 특정 자식 프로세스가 종료된 후에도 증거가 남습니다. 다음 스크립트는 10초마다 메모리 압력, 스왑 공간, RSS 기준 상위 15개 프로세스를 저장합니다.

#!/bin/zsh
set -eu

out="${1:-memory-samples.log}"

while true; do
  printf '
=== %s ===
' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> "$out"
  memory_pressure -Q >> "$out" 2>&1
  sysctl vm.swapusage >> "$out" 2>&1
  ps -axo pid,ppid,rss,etime,command | sort -nrk3 | head -n 16 >> "$out"
  sleep 10
done

먼저 별도의 터미널에서 zsh sample-memory.zsh를 실행한 다음 파이프라인을 시작합니다. 작업이 끝나면 샘플링을 중지합니다. RSS의 단위는 일반적으로 KB입니다. RSS가 프로세스의 전체 가상 주소 공간을 나타내는 것은 아니지만, 메모리 사용량이 계속 증가하는 컴파일러, 시뮬레이터 또는 테스트 프로세스를 찾기에는 충분합니다.

단계 표시도 함께 남기기

파이프라인에서 의존성 해석, 컴파일, 링크, 시뮬레이터 시작, 테스트 실행의 전후 시점에 UTC 시간을 출력합니다. 이렇게 하면 맥락 없는 시스템 스냅샷만 얻는 대신, 스왑 공간이 급증하기 시작한 시점을 구체적인 단계와 연결할 수 있습니다.

메모리 압력의 원인 구분하기

일반적인 패턴은 다음 표를 기준으로 우선 분류할 수 있습니다.

현상 가능성이 더 높은 원인 다음 단계
컴파일 단계에서 여러 프로세스의 RSS가 동시에 증가 컴파일 동시성이 메모리 예산을 초과함 빌드 작업 수를 줄인 후 다시 측정
시뮬레이터 시작 후 메모리 압력이 급증 병렬 테스트 대상이 너무 많음 동시에 실행되는 시뮬레이터 수 제한
단일 테스트 프로세스의 RSS가 계속 증가 테스트 또는 테스트 대상 코드의 메모리 누수 테스트를 분리하고 프로세스 샘플 수집
swap이 계속 증가하고 실행 시간 편차도 커짐 시스템에서 페이징이 빈번하게 발생함 동시성을 낮추고 상주 백그라운드 작업 축소
메모리 압력은 정상이지만 작업이 종료됨 메모리 문제가 아닐 수 있음 종료 코드와 통합 로그 확인

비정상 종료 직후에는 관련 시스템 기록을 검색할 수 있습니다.

log show --last 30m --style compact \
  | grep -Ei 'memory pressure|memorystatus|killed process' \
  > memory-events.log

일치하는 로그가 없다고 해서 메모리 상태가 정상이라는 뜻은 아닙니다. 샘플링 추세와 파이프라인 종료 코드를 함께 확인해야 합니다. 또한 특정 프로세스의 최대 사용량만 보고 메모리 누수로 단정해서도 안 됩니다. 컴파일과 링크 과정에서는 원래 단기적인 사용량 급증이 발생합니다. 사용량이 지속적으로 증가하고 해당 단계가 끝난 후에도 줄어들지 않을 때 메모리 누수를 더 의심할 수 있습니다.

동시성을 명확한 예산으로 관리하기

동시성 상한은 측정된 최대 사용량을 기준으로 정해야 합니다. 먼저 물리 메모리의 약 20%를 시스템, 원격 세션, 로그 프로세스용으로 남겨 두고, 나머지 공간을 컴파일러와 시뮬레이터에 할당합니다. 컴파일과 테스트가 동시에 실행되지 않는다면 예산을 각각 설정할 수 있습니다. 파이프라인에서 두 작업이 겹쳐 실행된다면 반드시 합산해서 계산해야 합니다.

Xcode 빌드는 먼저 -jobs로 보수적인 값을 시험할 수 있습니다.

set -o pipefail

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -jobs 4 \
  build

시뮬레이터 테스트에서는 병렬 대상 수를 제한합니다.

xcodebuild \
  -workspace App.xcworkspace \
  -scheme AppTests \
  -parallel-testing-enabled YES \
  -maximum-concurrent-test-simulator-destinations 2 \
  test

예시의 4와 2를 그대로 적용하지 마세요. 먼저 현재 동시성을 절반으로 줄이고 동일한 작업을 최소 세 번 연속 실행한 다음, 값을 단계적으로 높입니다. 목표는 CPU를 매초 최대한 사용하는 것이 아니라, 스왑 공간이 지속적으로 증가하지 않는 범위에서 안정적인 처리량을 확보하는 것입니다.

검증표로 튜닝 마무리하기

각 튜닝 라운드에서는 매개변수를 하나만 변경하고 다음 결과를 기록합니다.

  1. 동일한 커밋이 세 번 연속 완료되는지 여부.
  2. 최대 스왑 공간 사용량과 작업 전후의 증감량.
  3. 메모리 압력이 상승한 파이프라인 단계.
  4. 여전히 비정상 종료되는 프로세스가 있는지 여부.
  5. 총 실행 시간, 실행 시간 중앙값, 세 번의 결과 간 편차.
  6. 동시성을 낮춘 후 단위 시간당 완료된 유효 작업 수가 개선되었는지 여부.

동시성을 낮춰 개별 빌드가 조금 느려지더라도 재실행과 무작위 종료가 사라진다면 전체 처리량은 대체로 더 높아집니다. 값을 확정한 후에는 동시성 설정을 파이프라인 구성에 기록하고, 샘플링 스크립트는 상시 고빈도로 실행하지 말고 장애 진단이 필요할 때 켜는 도구로 남겨 둡니다. 구성을 변경하거나 테스트 매트릭스를 확장할 때는 다시 측정해야 합니다. 콘솔에서 현재 선택 가능한 구성을 확인한 후에도 동일한 기준 작업을 사용해 메모리 예산을 다시 수립해야 합니다.

자주 묻는 질문

클라우드 Mac의 여유 메모리만 보면 압박 상태를 알 수 있나요?

아닙니다. memory_pressure, 압축 메모리, 스왑 증가량, 프로세스 RSS를 함께 봐야 합니다. 캐시를 원활히 회수한다면 여유 메모리가 적어도 바로 이상 상태는 아닙니다.

Xcode 동시성을 낮춘 효과는 어떻게 확인하나요?

같은 커밋과 의존성 캐시, 테스트 집합으로 최소 세 번 실행해 최대 스왑, 비정상 종료, 총시간과 편차를 비교합니다. 실패와 변동이 함께 줄어야 유효한 조정입니다.

독점 물리 클라우드 Mac

재현 가능한 개발 환경을 독점 물리 노드에 배포

구성, 이용 기간과 다섯 개 노드 중 하나를 선택해 Xcode 빌드, 자동화 테스트, 원격 개발 또는 모델 추론에 사용하세요.

구성 선택 후 주문