[![logo](https://www.hongsnet.net/images/logo.gif)](https://www.hongsnet.net)

# Memory Monitoring ITEM Overview

일반적인 Memory 사용량 리소스를 수집할 경우 사용량/가용량을 Focus 한다. 하지만.. 리눅스의 경우 Pagecache와 같은 가용 메모리를 활용하려는 메커니즘과 다양한 구조가 있기 때문에 이를 세분화하여, 시스템의 이상징후에 대응하는 것이 맞다.

그리고 아이템은 `Zabbix 기반`하에 작성하였다.


# Memory Item 및 설명

시작하기 전에.. 아래의 리소스들은 리눅스의 `/proc/meminfo` 통계 파일을 참조하는 구조이다.


## Memory Extension

아래의 그래프를 보면, CPU/MEM/SWAP을 모두 볼 수 있기 때문에 일목요연하다고 생각할 수 있지만, 리눅스 메모리 관리구조는 아래의 그래프와 같이 그리 간단하게 판단해서는 안되며, 이를 여기서는 **as-is 그래프**라고 정의한다.

![asis_mem](./images/04p_23-02-16_custom.png)


그럼 위의 그래프가 아닌 다음의 그래프를 먼저 보자. 아래부터는 이를 **to-be 그래프**라고 정의한다.

![tobe_mem](./images/03p_mem_ext.png)

각 필드의 값은 다음과 같다.

- `cached memory` : Pagecache 사용영역에 대한 필드 값이다. 리눅스(커널)은 메모리 가용량에 대해 최대한 Pagecache를 활용하는 메커니즘이다.
- `buffers memory` : 일반적으로 inode와 같이 인덱스 정보를 담고 있는 영역이다. RHEL 6 이하의 경우만 해당한다.
- `used memory(total - free)` : Pagacache 포함 전체 메모리의 사용량이다. 크게 참조할 데이터는 아니다.
- `free memory(Allocable immediately)` : 커널이 아무런 간섭없이, 즉시 할당이 가능한 메모리 영역이다.
- `avail memory(free+buffers+cached)` : 표현이 애매하지만, 이 영역은 `사용하고 있지만, 필요 시 할당 가능이 가능한 영역으로 해석` 할 수 있다.


## Active / Inactive Memory

리눅스 메모리관리 구조는 분명히 그리 간단하게 판단해서는 안된다고 했다. 그 두 번째 이유가 바로 LRU 페이지 교체 알고리즘을 통해 `예전에 사용한 페이지는 제거될 것으로 예상` 된다는 것이다. 먼저 그래프를 보자.

![active_mem](./images/03p_mem_act_inact.png)

위 그래프의 현황은 Active(anon) 영역이 82.92GB인데, 이는 Application에서 사용하는 메모리로 봐도 무방하다. (**OS 영역에서 아예 사용하지 않는다는 뜻이 아님**)

그럼 각 필드의 값은 다음과 같다.

- `Active(anon) memory` : 최근에 사용되었고, **Swap Out**(메모리 -> 디스크)될 수 없는 메모리 영역
- `Active(file) memory` : **더 최근에 사용**되었고, Swap Out될 수 없는 메모리 영역
- `Inactive(anon) memory` : **예전에 사용**되었고, Swap Out될 수 있는 메모리 영역
- `Inactive(file) memory` : **예전에 사용**되었고, 아무런 영향없이 제거(Drop Cache)할 수 있는 메모리 영역 


## Swapping

위의 as-is 그래프를 보면, SWAP 사용량이 %로 표현되는데, 좀 더 명확히하기 위해서는 SWAP-in(디스크 -> 메모리)과 SWAP-out(메모리 -> 디스크)에 대한 현황을 표현하는 것이 맞다.

![swap_mem](./images/03p_swap.png)

그럼 각 필드의 값은 다음과 같다.

- `swap free` : 전체 스왑 영역의 가용량 이다.
- `swap in` : 디스크 -> 메모리로의 페이지 이동량 이다.
- `swap out` : 메모리 -> 디스크로의 페이지 이동량 이다.
- `swap used` : 전체 스왑 영역의 사용량 이다.


# Memory Monitoring Bash Script

Zabbix에서 위의 그래프를 출력하기 위해서는 아이템이 반드시 필요한데, 다음은 수집 스크립트의 코드 내역이다.


```bash
# cat /var/lib/zabbix/scripts/memory_ext.sh

#!/bin/bash
# Description : Zabbix Memory Resource Script
# Author : juhanida21@nate.com

if [[ $1 == "anon_pages" ]]; then
   anon_pages_size=0
   anon_pages_size=`cat /proc/meminfo |grep "AnonPages:" |awk '{print $2}'`
   anon_pages_result=`expr $anon_pages_size '*' 1024`
   echo $anon_pages_result

elif [[ $1 == "mapped" ]]; then
   mapped_size=0
   mapped_size=`cat /proc/meminfo |grep "Mapped:" |awk '{print $2}'`
   mapped_result=`expr $mapped_size '*' 1024`
   echo $mapped_result

elif [[ $1 == "shm" ]]; then
   shm_size=0
   shm_size=`cat /proc/meminfo |grep "Shmem:" |awk '{print $2}'`
   shm_result=`expr $shm_size '*' 1024`
   echo $shm_result

elif [[ $1 == "slab" ]]; then
   slab_size=0
   slab_size=`cat /proc/meminfo |grep "Slab:" |awk '{print $2}'`
   slab_result=`expr $slab_size '*' 1024`
   echo $slab_result

elif [[ $1 == "dirty" ]]; then
   dirty_size=0
   dirty_size=`cat /proc/meminfo |grep "Dirty" |awk '{print $2}'`
   dirty_result=`expr $dirty_size '*' 1024`
   echo $dirty_result

elif [[ $1 == "active" ]]; then
   active_size=0
   active_size=`cat /proc/meminfo |grep "Active:" |awk '{print $2}'`
   active_result=`expr $active_size '*' 1024`
   echo $active_result

elif [[ $1 == "active_anon" ]]; then
   active_anon_size=0
   active_anon_size=`cat /proc/meminfo |grep "Active(anon)" |awk '{print $2}'`
   active_anon_result=`expr $active_anon_size '*' 1024`
   echo $active_anon_result

elif [[ $1 == "active_file" ]]; then
   active_file_size=0
   active_file_size=`cat /proc/meminfo |grep "Active(file)" |awk '{print $2}'`
   active_file_result=`expr $active_file_size '*' 1024`
   echo $active_file_result

elif [[ $1 == "inactive" ]]; then
   inactive_size=0
   inactive_size=`cat /proc/meminfo |grep "Inactive:" |awk '{print $2}'`
   inactive_result=`expr $inactive_size '*' 1024`
   echo $inactive_result

elif [[ $1 == "inactive_anon" ]]; then
   inactive_anon_size=0
   inactive_anon_size=`cat /proc/meminfo |grep "Inactive(anon)" |awk '{print $2}'`
   inactive_anon_result=`expr $inactive_anon_size '*' 1024`
   echo $inactive_anon_result

elif [[ $1 == "inactive_file" ]]; then
   inactive_file_size=0
   inactive_file_size=`cat /proc/meminfo |grep "Inactive(file)" |awk '{print $2}'`
   inactive_file_result=`expr $inactive_file_size '*' 1024`
   echo $inactive_file_result

elif [[ $1 == "swap_cache" ]]; then
   swap_cache_size=0
   swap_cache_size=`cat /proc/meminfo |grep "SwapCached" |awk '{print $2}'`
   swap_cache_result=`expr $swap_cache_size '*' 1024`
   echo $swap_cache_result

elif [[ $1 == "writeback" ]]; then
   writeback_size=0
   writeback_size=`cat /proc/meminfo |grep "Writeback:" |awk '{print $2}'`
   writeback_result=`expr $writeback_size '*' 1024`
   echo $writeback_result

elif [[ $1 == "sreclaim" ]]; then
   sreclaim_size=0
   sreclaim_size=`cat /proc/meminfo |grep "SReclaimable:" |awk '{print $2}'`
   sreclaim_result=`expr $sreclaim_size '*' 1024`
   echo $sreclaim_result

elif [[ $1 == "sunreclaim" ]]; then
   sunreclaim_size=0
   sunreclaim_size=`cat /proc/meminfo |grep "SUnreclaim:" |awk '{print $2}'`
   sunreclaim_result=`expr $sunreclaim_size '*' 1024`
   echo $sunreclaim_result

elif [[ $1 == "kernel_stack" ]]; then
   kernel_stack_size=0
   kernel_stack_size=`cat /proc/meminfo |grep "KernelStack:" |awk '{print $2}'`
   kernel_stack_result=`expr $kernel_stack_size '*' 1024`
   echo $kernel_stack_result

elif [[ $1 == "page_tables" ]]; then
   page_tables_size=0
   page_tables_size=`cat /proc/meminfo |grep "PageTables:" |awk '{print $2}'`
   page_tables_result=`expr $page_tables_size '*' 1024`
   echo $page_tables_result

elif [[ $1 == "writeback_tmp" ]]; then
   writeback_tmp_size=0
   writeback_tmp_size=`cat /proc/meminfo |grep "WritebackTmp:" |awk '{print $2}'`
   writeback_tmp_result=`expr $writeback_tmp_size '*' 1024`
   echo $writeback_tmp_result

elif [[ $1 == "nfs_unstable" ]]; then
   nfs_unstable_size=0
   nfs_unstable_size=`cat /proc/meminfo |grep "NFS_Unstable:" |awk '{print $2}'`
   nfs_unstable_result=`expr $nfs_unstable_size '*' 1024`
   echo $nfs_unstable_result

elif [[ $1 == "committed_as" ]]; then
   committed_as_size=0
   committed_as_size=`cat /proc/meminfo |grep "Committed_AS:" |awk '{print $2}'`
   committed_as_result=`expr $committed_as_size '*' 1024`
   echo $committed_as_result

elif [[ $1 == "unevictable" ]]; then
   unevictable_size=0
   unevictable_size=`cat /proc/meminfo |grep "Unevictable:" |awk '{print $2}'`
   unevictable_result=`expr $unevictable_size '*' 1024`
   echo $unevictable_result

elif [[ $1 == "mlocked" ]]; then
   mlocked_size=0
   mlocked_size=`cat /proc/meminfo |grep "Mlocked:" |awk '{print $2}'`
   mlocked_result=`expr $mlocked_size '*' 1024`
   echo $mlocked_result

elif [[ $1 == "bounce" ]]; then
   bounce_size=0
   bounce_size=`cat /proc/meminfo |grep "Bounce:" |awk '{print $2}'`
   bounce_result=`expr $bounce_size '*' 1024`
   echo $bounce_result

elif [[ $1 == "hardwarecorrupted" ]]; then
   hardwarecorrupted_size=0
   hardwarecorrupted_size=`cat /proc/meminfo |grep "HardwareCorrupted:" |awk '{print $2}'`
   hardwarecorrupted_result=`expr $hardwarecorrupted_size '*' 1024`
   echo $hardwarecorrupted_result

fi
```

> 위의 스크립트는 간단하다. /proc/meminfo 현황파일의 필드를 잡아(grep) 이를 필드 위치(awk)에 맞게 가져온 후 이를 Zabbix의 수집단위인
Bytes에 맞게 단위를 환산(expr 명령)하면 된다.

그리고 구조를 보면, 스크립트에 인자를 호출하는 방식이기 때문에 유지보수도 쉬운 장점이 있다

```bash
$ memory_ext.sh test
```
> 위의 test 인자를 호출하였기 때문에 if ~ elif 제어조건문에 이를 처리하는 로직을 구현하면 된다.


# Zabbix 아이템 설정

설정 -> 템플릿

![swap_mem](./images/zabbix_memory_item.png)

> 상기 리소스는 모두 **30초 단위**로 수집된다.

