본문 바로가기
  • 아하하
Linux

OOM Killer , 메모리 overcommit

by 쥬쥬파파 2018. 10. 10.

출처 : http://www.mimul.com/pebble/default/2013/05/10/1368171783727.html



Linux의 메모리 관리에서는 메모리 오버커밋이라는 메커니즘이 적용되어 있어 실제 메모리 이상의 공간을 확보할 수 있다. 이는 어떤 문제를 야기시킬 수 있는가 하면 Linux 가상 메모리 시스템은 프로세스가 메모리를 확보할 때는 많게 보이게 하고 실제로 프로세스가 메모리에 접근을 하게되면 실제 메모리를 할당하는데 이때 실제 메모리가 부족하게 되면 OS내부적으로 프로세스를 마음대로 kill을 해버린다. 이른바 OOM-killer(out of memory killer)라고도 한다.

OOM Killer(Out of Memory Killer)는 시스템이 실제 메모리와 가상 메모리 공간(스왑)을 다 사용해, 필요한 메모리 공간을 새로 확보 할 수 없는 경우 프로세스를 종료시켜 여유 메모리를 확보하는 Linux 커널의 메커니즘 중에 하나이다.

그래서 kill된 프로세스가 자기가 왜 죽어버리는지 알수 없는 인터럽트가 발생해서 자신이 인터럽트가 발생했을 때 예외처리를 수행하지 못하는 문제가 발생한다. 이런 현상이 데이터를 관리하는 DB 서버에서 발생한다면 Linux의 좋은 매커니즘이라도 고객의 인지 정보가 실제 데이터에는 저장되지 않는 데이터 유실을 유발시킬 수 있다. DB 자체가 메모리 부족을 인지하고 예외처리하는 로직을 테우게 OS환경을 만들어줄 필요가 있다.
사소하지만 고민해야할 부분인 것이다.

관련 커널 메개변수 정보를 설명하면..

*. vm.overcommit_memory

  • 0 : heuristic에 따라 overcommit 할 수 있고, 하지 않을 수도 있다.(디폴트) 메모리 요구가 있을 때 여유 공간이 없는 경우 실행중인 프로세스를 강제 종료 메모리를 억지로 확보함.
  • 1 : 항상 overcommit 함. 메모리를 다 사용했는데도 충분한 메모리가있는 것처럼 처리 됨. 그 외에는 0과 같음.
  • 2 : overcommit하지 않음. 메모리가 부족할 경우 메모리 확보시 에러 발생시킴. [Swap size] + ([RAM size] * vm.overcommit_ratio/100).

*. vm.overcommit_ratio

  • 사용 가능한 메모리의 백분율로 정의. overcommit_ratio의 디폴트는 50.


vm.overcommit_memory = 0 or 1 일 경우 현재 환경에서 메모리 확보 테스트해 보면

현재의 메모리 정보는 아래와 같다.

[root@mimul01 ~]# free
             total       used       free     shared    buffers     cached
Mem:       1019680     457172     562508          0      25384     159084
-/+ buffers/cache:     272704     746976
Swap:            0          0          0


Physical Memory는 1G정도 된다고 볼 수 있다. CommitLimit(상한)과 Committed_AS(사용량) 정보는 아래와 같다.

[root@mimul01 ~]# cat /proc/meminfo |grep Comm
CommitLimit:     1009480 kB
Committed_AS:     518468 kB


그런 다음 아래 소스를 가지고 malloc 테스트를 실행 해 보았다. 이 경우 오버커밋이 허용되어 있어서 malloc_test 프로그램이 구동되는 동안 계속 메모리 확보를 하고 오버커밋된 범위까지도 메모리 할당을 시도한다. 즉 malloc에서 에러 리턴을 하지 않는 구조이다.

> cat malloc_test.c
#include <stdio.h>
#include <stdlib.h>

#define KB  1024
#define MB  (1024*KB)

int main()
{
  int i = 0;
  char *ptr = NULL;
  for (i=0; ;i++) {
    ptr = (char *)malloc(MB);
    if(ptr == NULL){
      break;
    }
  }
  printf("MALLOC SIZE=%dMB\n",i);
  return(0);
}
[mimul01]/home/k2/Downloads/alloctest> gcc -o malloc_test malloc_test.c
[mimul01]/home/k2/Downloads/alloctest> ./malloc_test 
죽었음


오버커밋 처리되어 malloc_test에서 인터럽트되어 제어가 되지 않고 OOM-killer에 의해 프로세스가 죽게 된다. 이럴 경우 malloc_test 프로세스는 내부의 예외처리가 무시되어 중요한 문제에 봉착될 수 있다. 아래는 프로세스가 죽었을 때 로그이다.

May  8 11:18:50 mimul01 kernel: Out of memory: Kill process 3121 
 (malloc_test) score 884 or sacrifice child


vm.overcommit_memory = 2, vm.overcommit_ratio = 99 일 경우 테스트 사례는

CommitLimit(상한)과 Committed_AS(사용량) 정보는 아래와 같다.

[mimul01]/home/k2/Downloads/alloctest> cat /proc/meminfo  |grep Comm
CommitLimit:     1009480 kB
Committed_AS:     520128 kB


이경우에는 아래처럼 정보를 보면 알겠지만 malloc시 널로 리턴되어 거기까지 메모리를 할당하고 빠져나오는 프로그램 프로세스를 그대로 탔다. 이처럼 제어가 프로그램으로 넘어거 예외 처리를 할 수 있는 구조가 된다.

[mimul01]/home/k2/Downloads/alloctest> ./malloc_test 
MALLOC SIZE=433MB


특히나 대상 서버가 데이터 베이스 서버일 경우에는 데이터의 무결성이 중요함으로 MySQL 데몬에 의해 예외 프로세스를 타게 해주는 것이 유효해 보인다.
더 중요한건 서버의 자원 모니터링을 해서 부족하지 않도록 Scale Up을 잘 해 주는 것이 필요하다.

결론은..

데이터 베이스 서버(MySQL, PostGreSQL, Redis 등)일 경우에는 커널 정보에 아래 설정을 해 두는 것이 데이터 무결성 보호에 도움을 받을 것이다.

> cat /etc/sysctl.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 99


그리고 데이터 베이스 서버는 스왑 사용도 비용이 크므로 vm.swappiness = 0로 운영하는 것도 괜찮다.