“아, 이건 기록을 안 해뒀네…” 하면서 그냥 잊으려 했는데, 메모를 정리하다가 발견해버렸다. ㅠ.ㅠ Elastic Stack으로 NetFlow와 SNMP 모니터링을 구성했던 이야기. NMS라고 하면서 SNMP와 NetFlow를 빼면 좀 섭하지… 했다가, 귀찮지만 닫았던 묶음글을 다시 열어서 마지막으로 이번 이야기, NetFlow와 SNMP 모니터링 하기를 더 넣는다. 이번엔 SNMP로 정말 끝!
이번 묶음글은 아래와 같은 순으로 진행할 예정이다. 깊이있게 다루는 것은 아니며, Elastic Stack을 시작하는 입장에서 관심있는 부분을 참고하면 될 것 같다.
- Elastic NMS Part 1: 엔진을 켜라!
- Elastic NMS Part 2: Syslog 원격로깅
- Elastic NMS Part 3: Mapping과 Template
- Elastic NMS Part 4: Kibana로 Visualize하기
- Elastic NMS Part 5: NetFlow 수신하기
- Elastic NMS Part 6: SNMP 수신하기
이 묶음글과 직접적인 관련은 없지만, 혹시 로그중앙화를 위한 간편한 솔루션을 찾는다면 이보다 앞서 검토했던 Graylog2에 대해 정리했던 글이 더욱 도움이 될 수도 있다. Graylog2는 로그중앙화와 검색, 경보발생 등이 가능한 매우 쉽고 잘 만들어진 도구다.
- Calling All Logs! Graylog2 1편: 설치하기
- Calling All Logs! Graylog2 2편: 맛보기
- Calling All Logs! Graylog2 3편: 설정
- Calling All Logs! Graylog2 4편: 기록
NetFlow와 SNMP
네트워크 모니터링을 얘기할 때, SNMP와 NetFlow는 따로 얘기할 필요가 없을 정도로 잘 알려진 관리용 프로토콜이다. 이 시험환경에서는 각 포트, 그러니까 네트워크 연결 단위의 성능측정을 위해 SNMP를 활용하여 Count 데이터를 모아 분석하고, 개별 서버나 서비스 단위의 성능측정 등을 위해 NetFlow를 활용한 데이터를 모아 분석할 수 있도록 구성해봤다.
SNMP 모니터링 구성하기
앞서 설명했던 NetFlow와 유사한 방식으로 SNMP의 기본 설정을 만들었다.
/etc/logstash/conf.d/30-snmp.conf
input {
udp {
type => snmp
tags => [ "no_default_out", "_debug" ]
port => "7450"
codec => json_lines
add_field => { "received_at" => "%{@timestamp}" }
}
}
filter {
if [type] == "snmp" {
date {
match => [ "timestamp", "ISO8601" ]
}
mutate {
add_field => { "proxy" => "%{host}" }
}
}
}
output {
if [type] == "snmp" {
elasticsearch {
hosts => ["127.0.0.1"]
index => "snmp-%{+YYYY.MM.dd}"
}
}
}
앞선 syslog나 NetFlow와는 조금 다른 접근방법을 사용한 부분은, syslog 나 NetFlow의 경우, 해당 장비에서 데이터를 전송할 기계를 설정하고 이 분석용 환경에서는 단지 그것을 수신할 포트를 열어주는 방식으로 진행했었는데, SNMP는 원래 “수동적으로 긁어가는 기계에서 자료를 주는 방식"으로 설계된 것이다 보니 중간에 긁어줄 녀석을 준비해야 했다. (이 부분은 좀 얘기가 길어지므로 이 글에서는 생략한다.)
아무튼, 간략하게 Ruby로 SNMP Poller를 하나 만들었고, 그것이 위와 같이
설정한 Logstash의 7450 포트에 json 포맷의 텍스트를 밀어넣어 주도록
구성을 했다. (이 Poller 얘기는 다음에 시간이 되면 하기로…)
Mapping 해주기
아무튼, 이 Poller는 json 형식으로 데이터를 쏴주고, 그 데이터를 다시 아래와 같은 Template을 적용한 인덱스에 저장해 주었다. (이제 Template 또는 Mapping 얘기는 여러번 나왔으니까 생략)
{
"template" : "snmp-*",
"settings": {
"index.refresh_interval": "5s"
},
"mappings" : {
"_default_" : {
"_all" : {"enabled" : false},
"properties" : {
"host": { "index": "not_analyzed", "type": "string" },
"device": { "index": "not_analyzed", "type": "string" },
"ifname": { "index": "not_analyzed", "type": "string" },
"ifindex": { "index": "not_analyzed", "type": "integer" },
"ifdescr": { "index": "not_analyzed", "type": "string" },
"iftype": { "index": "not_analyzed", "type": "integer" },
"ifmtu": { "index": "not_analyzed", "type": "integer" },
"ifphysaddress": { "index": "not_analyzed", "type": "string" },
"ifadminstatus": { "index": "not_analyzed", "type": "integer" },
"ifoperstatus": { "index": "not_analyzed", "type": "integer" },
"iflastchange": { "index": "not_analyzed", "type": "date" },
"ifinoctets": { "index": "not_analyzed", "type": "long" },
"ifinerrors": { "index": "not_analyzed", "type": "long" },
"ifoutoctets": { "index": "not_analyzed", "type": "long" },
"ifouterrors": { "index": "not_analyzed", "type": "long" },
"ifspeed": { "index": "not_analyzed", "type": "integer" },
"rx_bytes": { "index": "not_analyzed", "type": "long" },
"tx_bytes": { "index": "not_analyzed", "type": "long" },
"proxy": { "index": "not_analyzed", "type": "string" }
}
}
}
}
이걸 디버깅해보면 대충 아래와 같은 값을 얻게 된다.
{
"device" => "203.0.113.90",
"timestamp" => "2016-09-13T22:36:30+09:00",
"ifindex" => 12,
"ifdescr" => "bond0.1234",
"ifinoctets" => 893098073,
"ifoutoctets" => 4150580065,
"ifspeed" => 2000000000,
"rx_bytes" => 86415,
"tx_bytes" => 146173,
"@version" => "1",
"@timestamp" => "2016-09-13T13:36:30.000Z",
"type" => "snmp",
"received_at" => "2016-09-13T13:36:31.131Z",
"tags" => [
[0] "no_default_out",
[1] "_debug"
],
"host" => "127.0.0.1",
"proxy" => "127.0.0.1"
}
이제 Discover로 보면,

그리고 자세히 보면 아래와 같이 값과 자료형을 확인할 수 있다.

최종 설정
역시 최종적인 설정은 조금 다른데, 다음과 같다.
input {
udp {
type => snmp
port => "7450"
codec => json_lines
add_field => { "received_at" => "%{@timestamp}" }
add_field => { "[@metadata][output]" => "self" }
}
}
filter {
if [type] == "snmp" {
date {
match => [ "timestamp", "ISO8601" ]
}
mutate {
add_field => { "proxy" => "%{host}" }
}
if [device] {
mutate {
add_tag => [ "snmp" ]
add_field => { "[nms][pod]" => "%{device} pod" }
add_field => { "[nms][zone]" => "%{device} if %{ifname}" }
add_field => { "[nms][account]" => "%{device} account" }
add_field => { "[nms][hostname]" => "%{device} hostname" }
}
translate {
field => "[nms][pod]"
destination => "[nms][pod]"
override => true
dictionary_path => "/opt/hyeoncheon-elastic/conf/dict.device-map.yml"
}
translate {
field => "[nms][zone]"
destination => "[nms][zone]"
override => true
dictionary_path => "/opt/hyeoncheon-elastic/conf/dict.device-map.yml"
}
translate {
field => "[nms][account]"
destination => "[nms][account]"
override => true
dictionary_path => "/opt/hyeoncheon-elastic/conf/dict.device-map.yml"
}
translate {
field => "[nms][hostname]"
destination => "[nms][hostname]"
override => true
dictionary_path => "/opt/hyeoncheon-elastic/conf/dict.device-map.yml"
}
} else {
mutate {
add_tag => [ "ping" ]
add_field => { "[nms][pod]" => "global" }
add_field => { "[nms][zone]" => "global" }
add_field => { "[nms][account]" => "global" }
add_field => { "[nms][hostname]" => "global" }
}
}
}
}
output {
if [type] == "snmp" {
elasticsearch {
hosts => ["127.0.0.1"]
index => "snmp-%{+YYYY.MM.dd}"
}
}
}
NetFlow에서 설정한 내용과 유사한 구조인데, 간략히 덧붙이면 다음과 같다.
- 중간에 끼인 기계가 있으니
proxy라는 필드에 그 값을 넣어줬다. - zone, account 등의 정보는 같은 방식으로
translate하여 넣어줬다. - 전혀 설명하지 않았던 부분이긴 한데, 앞서 설명한 SNMP Poller가 하는 기능 중 하나가 주기적으로 지정된 IP에 ping 시험을 하고 그 결과를 같은 입력으로 넣어주고 있다. (이 부분은 그냥 이 글에서는 무시하시길)
Visualize해서 Dashboard에 그려보면 대충 이런 모양을 얻을 수 있다.

아무튼, 나름 쓸만하다.
이 묶음의 다른 글들
- Elastic NMS Part 1: 엔진을 켜라!
- Elastic NMS Part 2: Syslog 원격로깅
- Elastic NMS Part 3: Mapping과 Template
- Elastic NMS Part 4: Kibana로 Visualize하기
- Elastic NMS Part 5: NetFlow 수신하기
- Elastic NMS Part 6: SNMP 수신하기
함께 읽기
- Calling All Logs! Graylog2 1편: 설치하기
- Calling All Logs! Graylog2 2편: 맛보기
- Calling All Logs! Graylog2 3편: 설정
- Calling All Logs! Graylog2 4편: 기록
- PaperTrail, Cloud에서는 Cloud 로그를!
- Cloud App에서 PaperTrail 사용하기