본문으로 바로가기

[ECS] OOME (OutOfMemoryError) 최소화 해보기

category AWS/컴퓨팅 2023. 2. 9. 22:58

Contents

    개요

    ECS에서 컨테이너의 프로세스가 Task Definition에 할당된 양보다 많은 메모리를 사용할 경우 발생하는 ECS OutOfMemoryError에 대해 알아보자.

     

    ECS의 OutOfMemoryError

    ECS를 운영하다 보면 아래의 로그처럼 OutOfMemoryError가 (이하 OOME) 발생하는 경험을 겪게 된다.

    OutOfMemoryError: Container killed due to memory usage
    

    ECS OOME는 왜 발생하게 되는걸까?

     

    다음의 두 가지 시나리오가 존재한다.

     

    시나리오 1. Hard Limit을 넘어서는 메모리 사용

    ECS Task Definition에 컨테이너가 사용할 수 있는 최대 메모리를 제한시킬 수 있다.

    Soft Limit과 Hard Limit이 있는데 Hard Limit으로 설정 후 컨테이너의 프로세스가 Hard Limit보다 많은 메모리를 사용할 경우 OOME 로그를 출력하면서 컨테이너를 가차없이 종료시킨다.

    • Task Definition의 Container Definition 파라미터
      • Soft Limit : memoryReservation
      • Hard Limit : memory

     

    시나리오 2. ECS Task가 Task Definition에 설정된 양보다 더 많은 메모리를 사용

    Hard Limit 설정이 되어 있지 않아도 단순히 ECS Task가 Task Definition에 정의해놓은 메모리보다 더 많이 사용 할 경우 OOME가 발생하게 된다.

     

    해결방법

    ECS OOME에 대해 AWS에서는 다음의 4가지 항목을 해결방법으로 제시한다.

    1️⃣ Task Definition에서 사용 가능한 메모리에 여유를 준다.

    문제 해결을 위한 가장 간단한 방법이다. Task Definition이 사용할 수 있는 메모리에 여유를 준다.

     

    2️⃣ 컨테이너가 사용할 수 있는 메모리 양을 제한한다.

    Soft Limit, Hard Limit을 적절하게 설정하는 방법이다.

    Task Definition에 둘 이상의 컨테이너가 정의되어 있고 Soft Limit 혹은 Hard Limit이 없다면 Fargate는 리소스를 균등하게 분배하므로 리소스가 좀 더 필요한 컨테이너에 Soft Limit 혹은 Hard Limit을 명시하는게 좋다.

    • 단, Task Definition에 하나의 컨테이너만 정의되어 있을경우에는 해당 항목을 설정하는게 의미가 없다.

     

    3️⃣ 메모리 덤프를 따서 어떤 영역에서 메모리를 사용하는지 디버깅한다.

    java 프로세스 메모리 = heap 크기 + PermGen 크기 + (thread 개수 * thread stack 크기) + JVM 메모리

    만약 가용 메모리가 1G가 남아있는데 -Xmx1g로 설정한다면 OutOfMemoryError는 예약된 것이나 다름없다.

    → 이럴경우 Heap 크기를 줄이거나 Thread 수를 줄이거나 Thread Stack Size를 줄인다.

     

    4️⃣ Swap 메모리를 활성화 한다.

    ECS EC2에서 Swap 메모리를 활성화 시키는 방법이다. 메모리가 부족하여 OOM이 발생할 상황을 줄여준다.

    • ECS EC2의 경우에만 사용할 수 있고 Fargate의 경우에는 활성화가 불가능하다.

     

    +) JVM일 경우

    JVM 기반의 어플리케이션을 ECS로 띄울 때 도움이 될만한 내용을 소개한다.

    예를 들어서 Task Definition에 2GB의 Heap 사이즈를 갖는 JVM을 실행시키는 컨테이너에 Task Defenition에 2GB를 할당한 후 실행시키면 어떻게 될까?

    기본적으로 ECS Task에는 컨테이너를 제외하고도 메모리를 점유하고 있는 프로세스가 일부 존재하기도 하고 혹시라도 JVM 메모리 구조에서 Heap 영역을 제외한 다른 영역에서 메모리를 많이 사용할 경우 OOME가 발생하여 컨테이너가 종료될 것이다.

     

    JVM 메모리 관련 아규먼트

    메모리와 관련된 JVM 아규먼트를 적절하게 사용하여 ECS의 OOME를 최소화할 수 있다.

     

    -XX:InitialRAMPercentage : JVM의 초기 Heap 사이즈를 백분율로 구성하는 아규먼트이다.

    • -Xms 옵션과 같은 역할을 하지만 차이점은 호스트 머신이 사용하는 메모리의 백분율로 지정할 수 있다.
    • 만약, -Xms 옵션이 존재할 경우 InitialRAMPercentage 옵션은 무시된다.
    -XX:InitialRAMPercentage=50.0

     

    -XX:MaxRAMPercentage : JVM의 최대 Heap 사이즈를 백분율로 구성하는 아규먼트이다.

    • -Xmx 옵션과 같은 역할을 하지만 차이점은 호스트 머신이 사용하는 메모리의 백분율로 지정할 수 있다.
    • 만약, -Xmx 옵션이 존재할 경우 MaxRAMPercentage 옵션은 무시된다.
    • 기본값은 25%이다.
    -XX:MaxRAMPercentage=50.0

     

    -XX:MaxMetaspaceSize : JVM의 최대 Meta Space 사이즈를 지정한다.

    • 기본값은 64MB이다.
    -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m

     

    -Xss : JVM Thread의 기본 Stack 사이즈를 조정한다.

    -Xss=256k

     

    -XX:MaxDirectMemorySize : JVM의 최대 Direct Memory 사이즈를 지정한다.

    -XX:MaxDirectMemorySize=2g

     

     

    위의 아규먼트들을 참고하여 JVM 인자를 설정하게 된다면 대략 다음과 같이 설정할 수 있을 것이다.

    JAVA_OPTS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=50.0 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -XX:MaxDirectMemorySize=2g
    

     

    Reference