본문 바로가기
  • 아하하
Linux

LINUX 메모리 부족과 oom_killer 문제에 대한 이해와 대응

by 쥬쥬파파 2018. 11. 8.

출처 ㅣ http://mozi.tistory.com/28


OOM Killer 순위 설정 방법

1. 특정 프로세스의 PID 를 조회합니다.

2. /proc/PID/oom_adj 의 파일에 -17 을 입력합니다. oom_adj 는 -17 ~ 15 의 값을 가지며, 낮은 값 일수록 우선순위에서 밀려납니다.

3. /proc/PID/oom_score_adj 의 파일에 -1000 을 입력합니다. oom_score_adj 는 -1000 ~ 1000 의 값을 가지며, 낮은 값 일수록 우선순위에서 밀려납니다.

# pidof gmaster
18907

# echo -17 > /proc/18907/oom_adj
# echo -1000 > /proc/18907/oom_score_adj







1. Low memory 부족으로 인한 oom_killer 발생 관련

- cat /proc/meminfo로 메모리를 확인하면 MemFree는 여유가 있는데, OOM(Our Of Memory) 상황이 발생하는 경우가 있습니다.

- 이유는 32bit OS환경에서는 1G Memory를 기본으로 동작하며, 그 이상의 메모리를 관리하기 위해서 High Memory를 사용합니다.

 (64bit OS환경에서도 발생될 수 있으나, 64bit의 경우는 실제 장착된 메모리가 모두 고갈된 상황이기 때문에 대부분은 프로그램에 오류가 있어서 메모리 누수가 있는 경우입니다.)

- 32bit OS의 경우 MemFree는 HighFree와 LowFree, SwapFree 가 모두 합쳐진 값이기 때문에 OS동작을 위한 필수 메모리인 Low Memory가 부족하더라도 FreeMemory는 많이 남은 것으로 확인 됩니다.

 

2. oom_killer 발생 상황에서 램의 용량을 더 늘려도 Low Memory는 더 줄어드는 이유 (32bit OS의 경우)

- 메모리 부족 상황에서 가장 실수를 많이 저지르는 부분입니다.

- High Memory는 SW에서 직접적으로 접근하여 사용하는 것이 아니라 Low Memory에 할당된 메모리 맵을 통해 사용할 수 있게 되어 있습니다.

- 따라서 Low Memory 1G bytes를 기준으로 Higth Memory가 더 늘어난다면 결국 해당 High Memory를 관리하기 위해서 Low Memory는 더욱 줄어들어서 문제 상황이 더 빈번하게 만드는 상황을 만들게 됩니다.

- 보통 4G bytes 메모리의 장비에서 LowTotal은 900M 정도이지만 64G bytes 메모리에서는 아래와 같이 400M bytes도 안됩니다.

# cat /proc/meminfo 

MemTotal:     65779288 kB

MemFree:      57014984 kB

Buffers:         13020 kB

Cached:        3695264 kB

SwapCached:          0 kB

Active:        5898812 kB

Inactive:      2209372 kB

HighTotal:    65392100 kB

HighFree:     56999772 kB

LowTotal:       387188 kB

LowFree:         15212 kB

 

 

3. Low Memory 이외에도 oom_killer가 발생할 수 있는 상황

- LowMemory가 남아 있다하더라도 /proc/meminfo에서 확인되는 committed_AS가 CommitLimit를 초과하는 상황에서도 발생됩니다.

- CommitLimit은 overcomit_ratio를 통해 산정 됩니다.

 

4. vm_overcommit_memory 이해

- vm_overcommit_memory=0

 : 메모리를 알아서 할당함, 즉 메모리 할당 정책에 설정된 ratio를 기준으로 메모리를 할당하는데, 실제 메모리를 alloc()한다 하더라도 실제 사용하지 않는 경우도 있기 때문에 최대한 메모리 사용이 가능하도록 alloc() 요청시 예약만 걸어 놓는 방식임 (기본 값)

 : 실제 메모리보다 많이 할당할 수 있으나, 메모리 누수 상황이나, 대용량 프로그램이 많이 동작하는 경우에는 결국 oom 상황 발생

 

- vm_overcommit_memory=1

 : 위와 같이 설정할 경우 메모리 부족 상황에서도 alloc() 요청에 대하여 에러를 발생시키지 않습니다.

 : 메모리 부족이라는 상황에 의한 프로그램의 에러/중단을 일시적으로 회피할 수는 있으나 좋은 방법은 아닙니다.

 

- vm_overcommit_memory=2

 : 설정된 메모리를 기준으로 alloc() 요청이 들어오면 바로바로 할당하는 방식

 : 실제 메모리에 alloc() 요청으로 할당되는 메모리가 직접 반영되어 깔끔하지만 메모리 활용 효율이 떨어지는 단점이 있습니다.

 


- vm.overcommit_ratio나 vm.overcommit_memory의 조정을 통해서 oom_killer 발생 조건을 조정할 수 있습니다.

 

- 딱히 어떤 조건이 좋다고 말할 수는 없기 때문에, 동작 환경에 맞게 조정해야 합니다.

- sysctl을 통해서 oom_killer 발생 조건이나, 상황에 따른 동작 조건을 다음과 같이 설정할 수 있다.

# sysctl –w vm.overcommit_ratio=100

# sysctl –w vm.overcommit_memory=2

 --> CommitLimit(Real*Ratio(100%)+Swap) 영역을 최대 사용 가능 메모리 영역으로 

  계산하며, 메모리 할당이 시도되면 실제 해당 메모리를 사용하지 않아도 사용 용량에 합산하여 정확하게

  메모리 사용 제한을 계산하는 방식

--> overcommit_ratio를 100%에 근접시키면 oom_killer가 발생되는 시점을 잠시 늘릴 수는 있습니다.

--> 또한 overcommit_memory=2를 통해서 실제 가지고 있는 메모리까지만 할당하고, 그 이후에는 malloc() 등에서 에러를 발생시킵니다.

--> 단 메모리가 모두 고갈되는 상황에 결국에 도달하게 되면 역시 oom_killer가 발생되고, 최악의 상황에서는 장비가 멈춥니다.


# sysctl -w vm.panic_on_oom=0
 --> oom_killer가 발생하더라도 OS panic 상황으로 처리하지 않음 (원인 분석 가능, dmesg 활용)

# sysctl -w vm.panic_on_oom=1
 --> oom_killer가 발생하면 OS panic 상황으로 인식하여 장비가 멈추게 함 (단 WatchDog 기능이 있는 장비에서 이런 경우 자동 부팅하여 복구됨)

5. oom_killer 회피 방법
- 64bit OS로 업그레이드 (결국 이 방법이 프로그램 수정 없이 할수 있는 최선의 방법입니다. 메모리 사용량이 매우 많다면 다른 방법은 시간을 조금 더 확보하는 수준입니다.)
 . High Memory와 Low Memory의 구분 없이 바로 Low Memory로 통합되기 때문에 Low Memory 부족으로 인한 oom_killer 상황은 거의 발생되지 않음
 . 그래도 oom_killer가 발생된다면 메모리를 증설하면 간단하게 해결됩니다.

 

 # cat /proc/meminfo

MemTotal:     32949016 kB
MemFree:      29730244 kB
..
HighTotal:           0 kB
HighFree:            0 kB
LowTotal:     32949016 kB
LowFree:      29730244 kB

 


- 전체 메모리는 많은데, Low Memory가 조금 부족하여 발생된 상황이라면
 . 램(RAM) 전체 용량은 줄이는 것 만으로도 Low Memory는 늘어 납니다. (64G, 32G -> 8G)
 . 단 시스템의 상황을 잘 분석하여 결정하셔야 합니다.
 (실제로 전체 메모리를 많이 사용하지 않는 상황에서 Low Memory만 다소 부족한 상황이 발생된다면 이 방법도 나쁘지 않습니다.)
 . 커널 패치로는 Low Memory를 확보하는 것은 불가능하며, 늘린다하더라도 특정 모듈을 덜 올려서 늘어나는 것이기 때문에 많아야 50M  bytes 정도입니다.

- overcommit_ratio 조정으로 oom_killer가 발생되는 시점을 조정할 수 있습니다.
 . overcommit_ratio에 따라 시스템이 메모리 부족으로 멈추기 전에 oom_killer가 메모리를 점유하고 있는 프로그램들을 하나씩 강제적으로 죽이는 기준 시점이 변경됩니다.
 . overcommit_ratio가 보통 50~60으로 되어 있는데, 이 수치를 95까지 올리면 기존에 발생되었던 시점보다 메모리를 더 사용할 수 있기 때문에 oom_killer는 일시적으로 해결된 것처럼 보입니다.

- overcommit_memory 조정으로도 oom_killer가 발생되지 않도록 할 수 있습니다.
 . 위에서 설명드린 것과 같이 overcommit_memory=2로 설정할 경우 실제 지원되는 메모리 만큼만 할당(단 overcommit_ratio 기준)하기 때문에 메모리가 ratio에 따른 한계치에 도달하면 더이상 alloc()를 할 수 없기 때문에 oom_killer가 발생되지 않게 됩니다.
 . 이 상황에서는 서버에서 동작하는 프로그램의 일부가 메모리 부족 상황에서 alloc()이 null로 반환되어 비정상 동작을 하거나, 에러 로그들이 발생되게 될 것입니다.
 
- 64bit로 업그레이드 후 그래도 메모리 부족 상황이 발생되고, 돈이 없어서 메모리 증설도 불가능할 경우
 . 64bit 체계에서는 Low Memory로 통합되어서 이러한 상황은 잘 발생되지 않지만 메모리 자체가 작다면 일단 overcommit_ratio를 95정도로 높게 설정해 주고, overcommit_memory를 1로 설정하여 alloc()이 요청되더라도 실제 사용하게되는 시점에 할당하여 사용할 수 있는 유연한 메모리 정책을 적용하면 어느 정도 문제를 회피할 수 있습니다.
 . 또한 Low Memory는 부족하지 않으나, 위에서 언급한 Committed_AS가 CommitLimit를 초과한 상황이 있을 수 있으므로 Swap Memory를 수동으로 늘려서 문제 상황에 도달하는 시점을 늦출 수 있습니다. (서버 동작 중에도 가능)

 

# touch /swap_file

# dd if =/dev/zero of=/swap_file  bs=1024  count=2048000

# mkswap /swap_file

# swapon /swap_file

 

 . 위와 같이 하면 Swap Memory가 추가적으로 2G bytes 늘어난 것을 확인할  수 있습니다.  (free나 cat /proc/meminfo, swapon -s 로 확인 가능)
 . 단 특정 디렉토리에 swap_file을 생성하기 전에 반드시 df로 디스크 여유 공간을 확인하셔야 하며, 부팅 후에도 적용되도록 하기 위해서는 /etc/rc.d/rc.local 이나 부팅 레벨에서 특정 명령어를 수행시킬 수 있는 곳에 swapon /swap_file 이라고 해 두시면 됩니다.


==> 무엇보다도 메모리 누수가 없는 프로그래밍을 해야 하며 업무 용량에 맞춰서 H/W 스팩을 정해야 합니다. 특히 H/W 스팩은 현재 평균 사용량을 기준하는 것이 아니라 최대로 많은 부하가 발생되는 상황의 x1.2~2배 정도의 용량을 기준으로 하는 것이 좋습니다.