.NET Core에서 높은 CPU 사용량 디버그

이 문서의 적용 대상: ✔️ .NET Core 3.1 SDK 이상 버전

이 자습서에서는 과도한 CPU 사용량 시나리오를 디버그하는 방법을 알아봅니다. 제공된 예제 ASP.NET Core 웹앱 소스 코드 리포지토리를 사용하면 교착 상태를 의도적으로 초래할 수 있습니다. 엔드포인트가 응답을 중지하고 스레드가 누적됩니다. 다양한 도구를 사용하여 진단 데이터의 몇 가지 주요 부분으로 이 시나리오를 진단하는 방법을 알아봅니다.

이 자습서에서는 다음을 수행합니다.

  • 높은 CPU 사용량 조사
  • dotnet-counters를 사용하여 CPU 사용량 확인
  • 추적 생성을 위해 dotnet-trace 사용
  • PerfView에서 성능 프로파일링
  • 과도한 CPU 사용량 진단 및 해결

필수 조건

이 자습서에서는 다음을 사용하여 작업을 수행합니다.

CPU 카운터

진단 데이터를 수집하기 전에 높은 CPU 판단 기준을 관찰해야 합니다. 프로젝트 루트 디렉터리에서 다음 명령을 사용하여 샘플 애플리케이션을 실행합니다.

dotnet run

프로세스 ID를 찾으려면 다음 명령을 사용합니다.

dotnet-trace ps

명령 출력에서 프로세스 ID를 기록해 둡니다. 기존 프로세스 ID는 22884였지만 사용자의 프로세스 ID는 다를 것입니다. 현재 CPU 사용량을 확인하려면 dotnet-counters 도구 명령을 사용합니다.

dotnet-counters monitor --refresh-interval 1 -p 22884

refresh-interval은 카운터 폴링 CPU 값 사이의 시간(초)입니다. 출력은 다음과 같은 형태가 됩니다.

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                          0
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

웹앱을 실행하면 시작 직후에 CPU가 전혀 소비되지 않고 0%에 보고됩니다. 경로 매개 변수로 60000을 사용하여 api/diagscenario/highcpu 경로로 이동합니다.

https://localhost:5001/api/diagscenario/highcpu/60000

이제 dotnet-counters 명령을 다시 실행합니다. cpu-usage 카운터만 모니터링하려면 이전 명령에 '--counters System.Runtime[cpu-usage]`를 추가합니다. CPU가 소비되고 있는지 확실하지 않으므로 위와 동일한 카운터 목록을 모니터링하여 카운터 값이 애플리케이션의 예상 범위 내에 있는지 확인합니다.

dotnet-counters monitor -p 22884 --refresh-interval 1

아래와 같이 CPU 사용량이 증가하는 것을 확인할 수 있습니다(호스트 컴퓨터에 따라 CPU 사용량이 달라질 수 있음).

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                         25
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

요청 기간 동안 CPU 사용량은 증가된 비율을 중심으로 유지됩니다.

더 높은 CPU 사용량을 시각화하기 위해 여러 브라우저 탭에서 이 엔드포인트를 동시에 실행할 수 있습니다.

이 시점에서는 CPU가 예상보다 높게 실행되고 있다고 봐도 무방합니다. 문제의 영향을 식별하는 것이 원인을 찾는 데 중요합니다. 문제의 원인을 찾기 위해 진단 도구 외에 높은 CPU 사용량 효과를 활용할 예정입니다.

Profiler를 사용하여 높은 CPU 분석

CPU 사용량이 높은 앱을 분석할 때는 코드가 수행하는 작업에 대한 인사이트를 제공할 수 있는 진단 도구가 필요합니다. 일반적으로 프로파일러를 선택하며, 선택할 수 있는 다양한 프로파일러 옵션이 있습니다. dotnet-trace는 모든 운영 체제에서 사용할 수 있지만 안전 지점 바이어스 및 관리 전용 호출 스택의 제한 사항으로 인해 Linux용 'perf' 또는 Windows용 ETW와 같은 커널 인식 프로파일러에 비해 더 일반적인 정보가 제공됩니다. 성능 조사에 관리 코드만 관련된 경우 일반적으로 dotnet-trace이면 충분합니다.

perf 도구를 사용하여 .NET Core 앱 프로필을 생성할 수 있습니다. dotnet-trace를 사용할 수도 있지만 이 도구를 시연해 보겠습니다. 샘플 디버그 대상의 이전 인스턴스를 종료합니다.

.NET 앱이 /tmp 디렉터리에 map 파일을 만들도록 DOTNET_PerfMapEnabled 환경 변수를 설정합니다. 이 map 파일은 perf에서 CPU 주소를 이름별로 JIT 생성 함수에 매핑하는 데 사용됩니다. 자세한 내용은 성능 맵 및 jit 덤프 내보내기를 참조하세요.

참고 항목

.NET 6은 .NET 런타임 동작을 구성하는 환경 변수에 대해 COMPlus_ 대신 접두사 DOTNET_을 표준화합니다. 그러나 COMPlus_ 접두사도 계속 작동합니다. 이전 버전의 .NET 런타임을 사용하는 경우에도 환경 변수에 COMPlus_ 접두사를 사용해야 합니다.

동일한 터미널 세션에서 샘플 디버그 대상을 실행합니다.

export DOTNET_PerfMapEnabled=1
dotnet run

높은 CPU API 엔드포인트(https://localhost:5001/api/diagscenario/highcpu/60000)를 다시 실행합니다. 1분 요청 내에서 실행되는 동안 프로세스 ID를 사용하여 perf 명령을 실행합니다.

sudo perf record -p 2266 -g

perf 명령이 성능 수집 프로세스를 시작합니다. 약 20~30초 동안 실행한 후 Ctrl+C를 눌러 수집 프로세스를 종료합니다. 동일한 perf 명령을 사용하여 추적의 출력을 볼 수 있습니다.

sudo perf report -f

다음 명령을 사용하여 flame-graph를 생성할 수도 있습니다.

git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

이 명령은 성능 문제를 조사하기 위해 브라우저에서 볼 수 있는 flamegraph.svg를 생성합니다.

Flame graph SVG image

Visual Studio를 사용하여 높은 CPU 데이터 분석

모든 *.nettrace 파일은 Visual Studio에서 분석할 수 있습니다. Visual Studio에서 Linux *.nettrace 파일을 분석하려면 기타 필요한 문서와 함께 *.nettrace 파일을 Windows 컴퓨터로 전송한 다음 Visual Studio에서 *.nettrace 파일을 엽니다. 자세한 내용은 CPU 사용 현황 데이터 분석을 참조하세요.

참고 항목

다음 단계