728x90
반응형

oplog 를 가져오기 위해서 어드민 권한이 있는 계정을 사용하거나

oplog read 권한과 db any access 권한을 준 계정이 필요..

 

엘라스틱 내용 정리

 

- tw-hsd monstache tomi  sample

gzip = true  //전송 압축
 
stats =true
 
mongo-url = "mongodb://계정:PW@tw-test.bi-0.naver:30001/admin"   // 연결 정보
 
elasticsearch-urls= ["http://localhost:9200"]    //엘라스틱 서치 서버 정보
 
elasticsearch-max-conns = 4    // 최대 접속
 
elasticsearch-max-seconds = 5   // 접속 타임아웃
 
elasticsearch-max-bytes = 800000   // 1회 전송 최대 용량
 
dropped-collections = false   // 컬렉션 드랍에 대한 적용 여부
 
dropped-databases = false   // database 드랍 적용 여부
 
replay = false
 
resume = false   // 재 시작
 
resume-write-unsafe = true
 
resume-name = "default"
 
index-as-update = true
 
index-oplog-time = true
 
direct-read-namespaces = ["TW_USER.MY_BBS_POST_M"]    // 시작시 전체 데이터를 읽어야 하는 컬렉션 (지정되지 않으면 데몬 실행시 부터의 oplog를 읽어옴)
change-stream-namespaces = ["TW_USER.MY_BBS_POST_M"]  // 변경 스트림에 대해서 추적할 컬렉션
 
index-files = false
 
file-highlighting = false
 
verbose = false
 
namespace-regex = '^TW_USER.MY_BBS_POST_M'    // 적용할 컬렉션 정규식

 

elasticsearch index json sample  

##curl -H "Content-Type: application/json" -XPUT "http://$HOST:9200/tw_user.my_bbs_post_m" -d @"tw_my_post_m.json"
##위와 같은 명령을 통해 맵핑할 인덱스와 형태소 분석기등을 등록해 사용...
## 인덱스를 삭제하거나 변경 시에는 재 생성 처리가 필요,
 
 
{
    "settings": {
        "analysis": {
            "analyzer": {    //형태소 분석기 등록
                "tw_hsd_smartcn": {
                    "type": "custom",
                    "tokenizer": "tw_hsd_smartcn_tokenizer",
                    "filter": [   // 각 형태소 분석기에 따른 필터 처리
                        "porter_stem",
                        "tw_hsd_stop"
 
                    ]
                }
            },
            "tokenizer": {
                "tw_hsd_smartcn_tokenizer": {
                    "type": "smartcn_tokenizer",
                    "mode": "search",
                    "user_dictionary_rules":[
                        "커스텀 단어 "
                    ],
                    "discard_punctuation": true  //구두점 제거
                }
            },
            "filter": {
                "tw_hsd_stop": {
                    "type": "stop",
                    "stopwords": [
 
                        "的"
                    ]
                }
            }
        }
    },
    "mappings": {      //인덱싱 맵핑 데이터
            "properties": {
                "bbsId": {
                    "type": "long"
                },
                "commentCount": {
                    "type": "long"
                },
                "contents": {
                    "properties": {
                        "imageUrl": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                        },
                        "invite": {
                            "properties": {
                                "charaType": {
                                    "type": "long"
                                },
                                "et": {
                                    "type": "long"
                                },
                                "hostMid": {
                                    "type": "long"
                                },
                                "partySeq": {
                                    "type": "long"
                                },
                                "st": {
                                    "type": "long"
                                }
                            }
                        },
                        "txt": {
                            "type": "text",
                           "analyzer": "tw_hsd_smartcn",
                           "fielddata": "true"
 
                        }
                    }
                },
                "ct": {
                    "type": "long"
                },
                "df": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "likeCount": {
                    "type": "long"
                },
                "mid": {
                    "type": "long"
                },
                "oplog_date": {
                    "type": "date",
                    "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
                },
                "oplog_ts": {
                    "properties": {
                        "I": {
                            "type": "long"
                        },
                        "T": {
                            "type": "long"
                        }
                    }
                },
                "seq": {
                    "type": "long"
                },
                "ut": {
                    "type": "date",
                    "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
                }
            }
        }
    }

쿼리 샘플 이미지.

  • 매핑 확인 쿼리

 

  • 일반적인 쿼리

{
"size" : 10,    //검색 후  결과 출력 갯수
"from" : 0,   // 페이징을 위한 검색된 것 중 시작 위치
"_source" : ["bbsId", "contents.txt"],   // 검색 한 결과에 대한 출력 필터
"query" : {        // 검색
    "match" : {   
        "contents.txt" : "手手"          
        }
},
"sort" : {    //결과에 대한 정렬
     "commentCount" : {
       "order" : "desc"
       }
   }
}
{
    "took": 85,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3703,
            "relation": "eq"
        },
        "max_score": 13.312824,
        "hits": [
            {
                "_index": "tw_user.my_bbs_post_m",
                "_type": "_doc",
                "_id": "5f2e1e7eb6f0665b11ebb7fb",
                "_score": 13.312824,
                "_source": {
                    "bbsId": 0,
                    "commentCount": 0,
                    "contents": {
                        "imageUrl": "https://party.naver.com/bbs/zh_Hant/photo_38b17763ba1893c04667d68a4699152f-1596857981236.png",
                        "invite": {
                            "charaType": 0,
                            "et": 0,
                            "hostMid": 50013008,
                            "partySeq": 178549,
                            "st": 0
                        },
                        "txt": "搓手手"
                    },
                    "ct": 1596857982325,
                    "df": "N",
                    "likeCount": 2,
                    "mid": 50013008,
                    "oplog_date": "2021/01/26 07:15:06",
                    "oplog_ts": {
                        "T": 1611645306,
                        "I": 0
                    },
                    "seq": 76440,
                    "ut": 1597047193959
                }
            },
            {
                "_index": "tw_user.my_bbs_post_m",
                "_type": "_doc",
                "_id": "5fe8a850bbaaf7115b779762",
                "_score": 13.076448,
                "_source": {
                    "bbsId": 0,
                    "commentCount": 0,
                    "contents": {
                        "invite": {
                            "charaType": 0,
                            "et": 0,
                            "hostMid": 0,
                            "partySeq": 0,
                            "st": 0
                        },
                        "txt": "手手忍不到結果花了1050給7天池\n再抽手手要沒了(啊不是"
                    },
                    "ct": 1609082960467,
                    "df": "Y",
                    "likeCount": 0,
                    "mid": 50470654,
                    "oplog_date": "2021/01/26 07:15:15",
                    "oplog_ts": {
                        "T": 1611645315,
                        "I": 0
                    },
                    "seq": 963484,
                    "ut": 1609085952553
                }
            },
        ]
    }
}

 

맵핑 예제

#!/bin/sh
 
curl -H "Content-Type: application/json" -XPUT "http://localhost:9200/party_user.my_bbs_post_m" -d @"my_post_m.json"
 
 
{
    "settings": {
        "analysis": {
            "analyzer": {
                "hsd_kuromoji": {
                    "type": "custom",
                    "tokenizer": "hsd_kuromoji_tokenizer",
                    "filter": [
                        "kuromoji_part_of_speech",
                        "kuromoji_baseform",
                        "ja_stop",
                        "kuromoji_number",
                        "kuromoji_stemmer",
                        "p2_stop"
                    ]
                }
            },
            "tokenizer": {
                "hsd_kuromoji_tokenizer": {
                    "type": "kuromoji_tokenizer",
                    "mode": "search",
                    "user_dictionary_rules":[
                        "東京スカイツリー,東京 スカイツリー,トウキョウ スカイツリー,カスタム名詞"
                    ],
                    "discard_punctuation": true
                }
            },
            "filter": {
                "p2_stop": {
                    "type": "stop",
                    "stopwords": [
                        "ん"
                    ]
                }
            }
        }
    },
    "mappings": {
            "properties": {
                "bbsId": {
                    "type": "long"
                },
                "commentCount": {
                    "type": "long"
                },
                "contents": {
                    "properties": {
                        "imageUrl": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                        },
                        "invite": {
                            "properties": {
                                "charaType": {
                                    "type": "long"
                                },
                                "et": {
                                    "type": "long"
                                },
                                "hostMid": {
                                    "type": "long"
                                },
                                "partySeq": {
                                    "type": "long"
                                },
                                "st": {
                                    "type": "long"
                                }
                            }
                        },
                        "txt": {
                            "type": "text",
                           "analyzer": "hsd_kuromoji",
                           "fielddata": "true"
 
                        }
                    }
                },
                "ct": {
                    "type": "long"
                },
                "df": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "likeCount": {
                    "type": "long"
                },
                "mid": {
                    "type": "long"
                },
                "oplog_date": {
                    "type": "date",
                    "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
                },
                "oplog_ts": {
                    "properties": {
                        "I": {
                            "type": "long"
                        },
                        "T": {
                            "type": "long"
                        }
                    }
                },
                "seq": {
                    "type": "long"
                },
                "ut": {
                    "type": "date",
                    "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
                }
            }
        }
    }
728x90
300x250
728x90
반응형

개요

  • ELK에서 공식적으로 지원하는 Alerting 기능은 X-PACK을 구입하여야 사용 가능한 기능입니다.
  • Yelp에서 개발한 오픈소스 라이브러리로, ElasticSearch의 데이터 패턴에 대한 알림을 설정할 수 있다. 다양한 커스터마이징을 제공함

 

설치 방법

1. 파이썬과 필요한 패키지를 설치 합니다.

 

# 파이썬 설치
yum install python3
 
# pip 설치
yum install python-pip
 
# 위에 방법으로 설치가 안될 때 진행
1. curl -O https://bootstrap.pypa.io/get-pip.py
2, python3 get-pip.py --user
 
# ciff 설치 (1.11.5 이상 필요)
yum install pyhon-ciff*
 
# 파이썬 Develop 설치 파이썬과 같은 버전으로 설치 진행
yum install python-devel3*
 
# 관련 패키지 설치
pip install "setuptools>=11.3" python setup.py install
 
# ElastAlert 설치
pip install elastalert

 

2. 설치 기본 경로로 이동

cd /usr/local/lib/python3.6/site-packages/elastalert

 

3. ElastAlert 관련 Yaml 설정

  • config.yaml 과 rule.yaml을 설정 해야 합니다.

config.yaml 주요 옵션
rules_folder : config.yaml 파일의 경로 기준으로 rule 파일들이 위치한 폴더
run_every : 알람의 주기를 설정하는 옵션, python 문법으로 timedelta 옵션에 쓸수있는 값들을 넣으면된다. days, seconds, microseconds, milliseconds, minutes, hours, weeks
buffer_time : 일부 로그소스가 실시간이 아닌 경우 결과를 버퍼링할 최근기간
es_host : elasticsearch 호스트
es_port : elasticsearch 포트

writeback_index : 메타데이터 저장에 사용하는 index, 해당 인덱스는 사용자가 직접 만들어줘야 하는듯하다.
alert_time_limit : 알람 실패시 재시도할 주기

728x90
# config.yaml 
 
# This is the folder that contains the rule yaml files
# Any .yaml file will be loaded as a rule
rules_folder: /usr/local/lib/python3.6/site-packages/elastalert/alertScript/
 
# How often ElastAlert will query Elasticsearch
# The unit can be anything from weeks to seconds
run_every:
  minutes: 5
 
# ElastAlert will buffer results from the most recent
# period of time, in case some log sources are not in real time
buffer_time:
  minutes: 15
 
# The Elasticsearch hostname for metadata writeback
# Note that every rule can have its own Elasticsearch host
es_host: syf-es-0.cocone
 
# The Elasticsearch port
es_port: 9200
 
# The AWS region to use. Set this when using AWS-managed elasticsearch
#aws_region: us-east-1
 
# The AWS profile to use. Use this if you are using an aws-cli profile.
# See http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
# for details
#profile: test
 
# Optional URL prefix for Elasticsearch
#es_url_prefix: elasticsearch
 
# Connect with TLS to Elasticsearch
#use_ssl: True
 
# Verify TLS certificates
#verify_certs: True
 
# GET request with body is the default option for Elasticsearch.
# If it fails for some reason, you can pass 'GET', 'POST' or 'source'.
# See http://elasticsearch-py.readthedocs.io/en/master/connection.html?highlight=send_get_body_as#transport
# for details
#es_send_get_body_as: GET
 
# Option basic-auth username and password for Elasticsearch
#es_username: someusername
#es_password: somepassword
 
# Use SSL authentication with client certificates client_cert must be
# a pem file containing both cert and key for client
#verify_certs: True
#ca_certs: /path/to/cacert.pem
#client_cert: /path/to/client_cert.pem
#client_key: /path/to/client_key.key
 
# The index on es_host which is used for metadata storage
# This can be a unmapped index, but it is recommended that you run
# elastalert-create-index to set a mapping
writeback_index: elastalert_status
writeback_alias: elastalert_alerts
 
# If an alert fails for some reason, ElastAlert will retry
# sending the alert until this time period has elapsed
alert_time_limit:
  days: 1
 
# Custom logging configuration
# If you want to setup your own logging configuration to log into
# files as well or to Logstash and/or modify log levels, use
# the configuration below and adjust to your needs.
# Note: if you run ElastAlert with --verbose/--debug, the log level of
# the "elastalert" logger is changed to INFO, if not already INFO/DEBUG.
#logging:
#  version: 1
#  incremental: false
#  disable_existing_loggers: false
#  formatters:
#    logline:
#      format: '%(asctime)s %(levelname)+8s %(name)+20s %(message)s'
#
#    handlers:
#      console:
#        class: logging.StreamHandler
#        formatter: logline
#        level: DEBUG
#        stream: ext://sys.stderr
#
#      file:
#        class : logging.FileHandler
#        formatter: logline
#        level: DEBUG
#        filename: elastalert.log
#
#    loggers:
#      elastalert:
#        level: WARN
#        handlers: []
#        propagate: true
#
#      elasticsearch:
#        level: WARN
#        handlers: []
#        propagate: true
#
#      elasticsearch.trace:
#        level: WARN
#        handlers: []
#        propagate: true
#
#      '':  # root logger
#        level: WARN
#          handlers:
#            - console
#            - file
#        propagate: false

 

 

4. rule.yaml 설정

  • name : rule이름, 고유한 이름이므로 중복되어선 안된다.
  • type : 알람의 타입, 예제 파일 내용인 frequency 일때는 timeframe, num_events 옵션을 사용한다.
  • index : 해당 rule이 탐색할 elasticsearch의 인덱스
  • num_events : 정해진 time_frame 시간동안 일정 횟수이상 document 매치시 알람 발생.
  • timeframe : num_events 카운트를측정할 시간 단위
  • filter : 인덱스에서 매칭시킬 조건.
    • query_string : 루씬 쿼리 포맷을 사용하는 방식
    • filter: - query: query_string: query: "username: bob" - query: query_string: query: "_type: login_logs" - query: query_string: query: "field: value OR otherfield: othervalue" - query: query_string: query: "this: that AND these: those"
    • term : 지정한 필드에 매치할 값을 지정하는 방식
    • filter: - term: name_field: "bob" - term: _type: "login_logs"
    • terms : term과 같은 개념인데 매칭시킬 값을 여러개로 할수있다. (배열식)
    • filter: - terms: field: ["value1", "value2"] # value1 OR value2 - terms: fieldX: ["value1", "value2"] fieldY: ["something", "something_else"] fieldZ: ["foo", "bar", "baz"]
    • wildcard : * 문자를 사용하여 유사값을 매칭 시킬수있는 방식
    • filter: - query: wildcard: field: "foo*bar"
    • range : 숫자형식 필드에 대해 범위를 지정하는 방식
    • filter: - range: status_code: from: 500 to: 599
    • Negation, and, or : Elasticsearch 2.x 버전에서 사용되는 방식이었으나 5.x버전 이상부터는 작동되지 않고 query_string 방식을 사용한다.
# rule.yaml
 
 
# Alert when the rate of events exceeds a threshold
 
# (Optional)
# Elasticsearch host
es_host: test-es
 
# (Optional)
# Elasticsearch port
es_port: 9200
 
# (OptionaL) Connect with SSL to Elasticsearch
#use_ssl: True
 
# (Optional) basic-auth username and password for Elasticsearch
#es_username: someusername
#es_password: somepassword
 
# (Required)
# Rule name, must be unique
name: DigDag Alert Rule
 
# (Required)
# Type of alert.
# the frequency rule type alerts when num_events events occur with timeframe time
type: frequency
 
# (Required)
# Index to search, wildcard supported
index: digdag*
 
# (Required, frequency specific)
# Alert when this many documents matching the query occur within a timeframe
num_events: 1
 
# (Required, frequency specific)
# num_events must occur within this amount of time to trigger an alert
timeframe:
#  hours: 1
  seconds: 10
 
alert_text_args: ["errMsg", "path"]
alert_text: "errMsg: {0}\n\n\npath: {1}"
alert_text_type: alert_text_only

# minutes : 0분으로 하면 놓치는 얼럿 없이 다 보냄 
realert:
  minutes: 0
  
# (Required)
# A list of Elasticsearch filters used for find events
# These filters are joined with AND and nested in a filtered query
# For more info: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html
filter:
- query:
    query_string:
      query: "*"
 
# (Required)
# The alert is use when a match is found
alert:
- "slack"
slack:
slack_webhook_url: "https://hooks.slack.com/services/T02BEABAP/test/asd123"
#slack_webhook_url: "https://hooks.slack.com/services/T02BEABAP/teast/asd123"
slack_username_override: "ErrAlert-Bot"
slack_channel_override: "notification"
#slack_channel_override: "testchannel1"
slack_emoji_override: ":stest:"
#slack_msg_color: "danger"

 

5. 모든 설정이 끝났다면 커맨드 실행

nohup elastalert --verbose --start NOW --config /usr/local/elastalert/alertScript/config.yaml --rule /usr/local/elastalert/alertScript/rule.yaml &
728x90
300x250
728x90
반응형
# 아래 옵션들을 사용하겠다는 명시적 허용 ( Default : true )
cluster.routing.allocation.disk.threshold_enabled: true
 
# Default 85%, 디스크 용량이 85%이상 초과하게 되면 더이상 샤드의 할당 작업을 수행하지 않는다. primary 샤드를 생성할때는 영향을 주지 않는다.
(퍼센트는 바이트 방식으로 제한을 바꿀수 있다.)
cluster.routing.allocation.disk.watermark.low: 70%
 
# Default 90%, 모든 샤드 할당 작업 자체를 막는다.
(퍼센트는 바이트 방식으로 제한을 바꿀수 있다.)
cluster.routing.allocation.disk.watermark.high: 80%
 
# Default 95%, 초과시 모든 인덱스의 write 작업을 막는다. 각 인덱스 별로 index.blocks.read_only_allow_delete 옵션이 활성화 되며 read-only 만 가능한 상태로 변경된다.
cluster.routing.allocation.disk.watermark.flood_stage: 90%
 
# disk 상태 업데이트 인터벌
cluster.info.update.interval: 10s
728x90
300x250
728x90
반응형

1. ILM Policy 설정

PUT _ilm/policy/cleanup-history
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {}
      },
      "delete": {
        "min_age": "31d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

2.  템플릿 ILM 적용하기

728x90
PUT /_template/logging_policy_template?pretty
{
"index_patterns": ["access*", "app_log*", "error*", "syslog*", "auth*"], "settings": { "index.lifecycle.name": "cleanup-history" }
}

3. 기 생성된 Index에 ILM 적용하기

PUT /access*/_settings?pretty
{
  "lifecycle.name": "cleanup-history"
}

4. 적용 확인 하기

GET /access-2021.03.31*/_settings?pretty
728x90
300x250
728x90
반응형

Index Template에 ILM 정책에 대한 rollover alias 지정 (legacy index template 용이 아님)

PUT _index_template/app_log
{
  "template": {
    "settings": {
      "index": {
        "lifecycle": {
          "name": "testILM",
          "rollover_alias": "test-alias"
        }
      }
    }
  },
  "index_patterns": [
    "app_log*"
  ],
  "composed_of": []
}

geo_point 추가

POST /_template/access
{
  "mappings": {
    "properties": {
      "geoip": {
        "dynamic": true,
        "type": "object",
        "properties": {
          "location": {
            "type": "geo_point"
          },
          "latitude":{
            "type": "float"
          },
          "longitude":{
            "type": "float"
          }
        }
      }
    }
 },
  "aliases": {}

}


PUT access
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}

인덱스의 쌓인 Document 수 확인

GET /app_log/_count
{
  "query" : {
    "term": { "@timestamp" : "2021-03-09" } /* 하루 쌓이는 document 양 */
  }
}

기존 인덱스에 매핑 등록

PUT sre-www-2023.05
{
  "mappings": {
    "s.type": {
      "full_name": "s.type",
      "mapping": {
        "type": {
          "type": "keyword"
        }
      }
    }
  }
}

새로운 인덱스 생성 및 매핑 등록

PUT sre-www-2023.05-backup
{
  "mappings": {
    "properties": {
      "s.type": {
        "type": "keyword"
      }
    }
  }
}

인덱스의 매핑 필드 정보 보기

GET sre-*/_mapping/field/s.type

인덱스 삭제

DELETE sre-www-2023.05-backup

Reindex 하기

POST /_reindex?wait_for_completion=false
{
  "source": {
    "index": "sre-www-2023.05"
  },
  "dest": {
    "index": "sre-www-2023.05-backup"
  }
}

진행 Task 결과 보기

GET /_tasks

GET /_tasks/H-hNeNJFSCq0J1WPJocVeg:2335496579

POST /_tasks/H-hNeNJFSCq0J1WPJocVeg:2335882225/_cancel

매핑 정보 안들가 있는 인덱스에 넣기

PUT sre-www-2023.02/_mapping
{
  "properties": {
    "s.type": {
      "type": "keyword"
    }
  }
}

기존에 없는 Field 삽입

POST sre-www-2023.02/_update_by_query?conflicts=proceed
{
  "script": {
    "lang": "painless",
    "source": """
      if (ctx._source.s.domain == 'abc.co.kr' || ctx._source.s.domain == 'www.abc.co.kr') {
        ctx._source['s.type'] = 'abc PC';
      } else if(ctx._source.s.domain == 'm.abc.co.kr' || ctx._source.s.domain == 'brandapp-m.abc.co.kr') {
        ctx._source['s.type'] = 'abc 모바일';
      }
    """
  },
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "s.domain": "abc.co.kr"
          }
        },
        {
          "term": {
            "s.domain": "www.abc.co.kr"
          }
        },
        {
          "term": {
            "s.domain": "m.abc.co.kr"
          }
        },
        {
          "term": {
            "s.domain": "brandapp-m.abc.co.kr"
          }
        }
      ]
    }
  }
}

기존에 field의 type이 맞지 않게 들어갔거나 field 나중에 추가된 경우 이전에 것도 Aggreation 하려면 필요한 절차들 이다

728x90
300x250
728x90
반응형
  • Kibana Dashboard 사용시 Index Pattern Id를 변경 해야 할 때,

 

Index Pattern ID 검색

GET .kibana/_search
{
  "_source": ["index-pattern.title"],
  "query": {
    "term": {
      "type": "index-pattern"
    }
  }
}

 

원하는 Visualize 에서 Inspect 한다. 그런 후

# 레퍼런스에 아래 내용 기입
# id: 는 변경 할 Index Pattern ID
[
  { "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
    "type": "index-pattern", "id": "8e2f3600-87d3-11eb-86dd-f13a798c7e8e"
    },
  { "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index",
    "type": "index-pattern", "id": "8e2f3600-87d3-11eb-86dd-f13a798c7e8e"
  }
]
728x90
300x250
728x90
반응형

workflow

 : input(로그 입력) → filter(로그 가공) → output (로그 출력)

 : Filebeat를 통해 input을 하며, Logstash에서 가공하고, Elasticsearch에서 받고 Kibana로 출력하는 흐름

 

수집로그

  : 수집하는 로그는 다음과 같다

  : Nginx(Access, Error), OS(auth, message), MongoDB (Slowquery)

 

logstash 폴더 구조

➜  logstash ls -ltr
total 526032
-rw-r--r--  1 park_hyungkyu  admin       2019  1 29 12:26 jvm.options
-rw-r--r--  1 park_hyungkyu  admin       9097  1 29 12:26 log4j2.properties
-rw-r--r--  1 park_hyungkyu  admin        342  1 29 12:26 logstash-sample.conf
-rw-r--r--  1 park_hyungkyu  admin       1696  1 29 12:26 startup.options
-rw-r--r--  1 park_hyungkyu  admin  267069440  2  1 10:25 core.alpha.tar
-rw-r--r--  1 park_hyungkyu  admin       1763  2  1 18:06 logstash.conf_fail
-rw-r--r--  1 park_hyungkyu  admin       2494  2  2 11:14 logstash.conf.org
-rw-r--r--  1 park_hyungkyu  admin       3697  2  2 11:34 pipelines.yml.org
-rw-r--r--  1 park_hyungkyu  admin      11250  2  2 12:49 logstash.yml
-rw-r--r--  1 park_hyungkyu  admin        259  2  2 12:54 pipelines.yml
-rw-------  1 root           admin      86198  2  2 12:55 nohup.out
drwxr-xr-x  5 park_hyungkyu  admin        160  2  2 15:08 conf.d

- 기본적으로 logstash 명령어 실행 시 아무 옵션 주지 않았을 때, pipelines.yml을 읽는다.

- logstash.yml 파일은 pipeline에 대한 공통된 튜닝 옵션을 제공한다. pipelines.yml에서 별도 옵션을 주지 않으면 기본적으로 logstash.yml에 등록된 옵션을 사용한다.

- conf.d 폴더는 임의로 생성하였으며, 각 로그수집의 설정값을 저장한다.

 

pipelins.yml

➜  logstash cat pipelines.yml
- pipeline.id: aplogs
  path.config: "/etc/logstash/conf.d/aplogs.conf"

 

logstash.yml

➜  logstash cat logstash.yml
path.data: /var/lib/logstash
pipeline.workers: 2
pipeline.batch.size: 125
pipeline.batch.delay: 50
pipeline.unsafe_shutdown: false
pipeline.ordered: auto
config.reload.automatic: true
config.reload.interval: 60s
http.enabled: true
http.port: 9600
dead_letter_queue.enable: false
 
dead_letter_queue.max_bytes: 1024mb
 
 
path.logs: /var/log/logstash

 

Logstash 의 config 파일

 

conf.d/nginx.conf

input {
  beats {
    port => 5044
    host => "0.0.0.0"
    include_codec_tag => false
  }
}
filter {
    if "access" in [tags] {
      grok {
        match => { "message" => ["%{IPORHOST:[nginx][access][remote_addr]}, %{IPORHOST:[nginx][access][lb_addr]} - %{USERNAME:[nginx][access][remote_user]} \[%{HTTPDATE:[nginx][access][time_local]} T:%{DATA:[nginx][access][request_time]}\] \"%{WORD:[nginx][access][request]} %{DATA:[nginx][access][url]} HTTP/%{NUMBER:[nginx][access][http_version]}\" %{INT:[nginx][access][status]} %{NUMBER:[nginx][access][body_bytes_sent]} \"%{DATA:[nginx][access][http_referer]}\" \"%{DATA:[nginx][access][http_accept_encoding]}\" \"%{DATA:[nginx][access][sent_http_content_encoding]}\" \"%{DATA:[nginx][access][http_user_agent]}\" \"%{DATA:[nginx][access][sent_http_content_type]}\" \"%{DATA:[nginx][access][http_ca_http_header]}\""] }
        remove_field => "message"
      }
      mutate {
        add_field => { "read_timestamp" => "%{@timestamp}" }
        convert => {
         "[nginx][access][http][version]" => "float"
         "[nginx][access][user_agent][magor]" => "integer"
         "[nginx][access][body_bytes_sent]" => "integer"
         "[nginx][access][user_agent][minor]" => "integer"
         "[nginx][access][request_time]" => "float"
        }
      }
      date {
        match => [ "[nginx][access][time]", "dd/MMM/YYYY:H:m:s Z" ]
        remove_field => "[nginx][access][time]"
      }
      useragent {
        source => "[nginx][access][http_user_agent]"
        target => "[nginx][access][user_agent]"
        remove_field => "[nginx][access][http_user_agent]"
      }
      geoip {
        source => "[nginx][access][remote_addr]"
        #target => "[nginx][access][geoip]"
      }
      mutate {
        remove_field => "[geoip][timezone]"
        remove_field => "[geoip][contry_code2]"
        remove_field => "[geoip][contry_code3]"
        remove_field => "[geoip][contry_name]"
        remove_field => "[geoip][continent_code]"
      }
      if "_grokparsefailure" in [tags] {
           drop { }
       }
    }
    else if "error" in [tags] {
      grok {
        #match => { "message" => ["%{DATA:[nginx][error][time]} \[%{DATA:[nginx][error][level]}\] %{NUMBER:[nginx][error][pid]}#%{NUMBER:[nginx][error][tid]}: (\*%{NUMBER:[nginx][error][connection_id]} )?%{GREEDYDATA:[nginx][error][message]}"] }
        match => { "message" => ["(?<[nginx][error][time_local]>%{YEAR}[./-]%{MONTHNUM}[./-]%{MONTHDAY}[- ]%{TIME}) \[%{LOGLEVEL:[nginx][error][severity]}\] %{POSINT:[nginx][error][pid]}#%{NUMBER:[nginx][error][tid]}: (\*%{NUMBER:[nginx][error][connection_id]} )%{GREEDYDATA:[nginx][error][message]}(?:, client: (?<[nginx][error][clientip]>%{IP}|%{HOSTNAME}))(?:, server: %{IPORHOST:[nginx][error][server]}?)(?:, request: %{QS:[nginx][error][request]})?(?:, upstream: (?<[nginx][error][upstream]>\"%{URI}\"|%{QS}))?(?:, host: %{QS:[nginx][error][request_host]})?(?:, referrer: \"%{URI:[nginx][error][referrer]}\")?"] }
        remove_field => "message"
      }
      mutate {
        rename => { "@timestamp" => "read_timestamp" }
        convert => {
          "[nginx][error][pid]" => "integer"
          "[nginx][error][tid]" => "integer"
          "[nginx][error][connection_id]" => "integer"
 
        }
      }
      #date {
      #  match => [ "[nginx][error][time]", "YYYY/MM/dd H:m:s" ]
      #  remove_field => "[nginx][error][time]"
      #}
    }
    else if "app_log" in [tags] {
        mutate {
          gsub => [ "message", "\\n\\s\\s\\s\\s\\s\\s\\s\\s|\\n\\s\\s", "\\s" ]
       }
        grok {
          match => { "message" => ["(?<timestamp>^.{23})\s(?<class>[^ ]*)\s(?<loglevel>.[^.]*)\s(?<location>.[^ ]*)\s-\s(?<description>(.|\n^.{23})+)"] }
          remove_field => "message"
       }
        if "_grokparsefailure" in [tags] {
          drop { }
       }
    }
    else if "auth" in [tags] {
      grok {
        match => { "message" => ["%{SYSLOGTIMESTAMP:system.auth.timestamp.} %{SYSLOGHOST:[system][auth][hostname]} sshd(?:\[%{POSINT:[system][auth][pid]}\])?: %{DATA:[system][auth][ssh][event]} %{DATA:[system][auth][ssh][method]} for (invalid user )?%{DATA:[system][auth][user]} from %{IPORHOST:[system][auth][ssh][ip]} port %{NUMBER:[system][auth][ssh][port]} ssh2(: %{GREEDYDATA:[system][auth][ssh][signature]})?"] }
        pattern_definitions => {
          "GREEDYMULTILINE"=> "(.|\n)*"
        }
      }
      date {
        match => [ "time_local", "UNIX" ]
      }
      geoip {
        source => "[system][auth][ssh][ip]"
        #target => "[system][auth][ssh][geoip]"
      }
    }
    else if "syslog" in [tags] {
      grok {
        match => { "message" => ["%{SYSLOGTIMESTAMP:[system][syslog][timestamp]} %{SYSLOGHOST:[system][syslog][hostname]} %{DATA:[system][syslog][program]}(?:\[%{POSINT:[system][syslog][pid]}\])?: %{GREEDYMULTILINE:[system][syslog][message]}"] }
        pattern_definitions => { "GREEDYMULTILINE" => "(.|\n)*" }
        remove_field => "message"
      }
      date {
        match => [ "time_local", "UNIX" ]
      }
    }
    else if "mongo" in [tags] {
      grok {
        match => { "message" => ["(?<timestamp>^.{23})Z\s(?<serverity>[^ ]*)\s(?<component>[^ ]+)\s\s(?<context>[^ ]+)\s(?<process>[^ ]+)\s(?<DB>[^ ]+)\scommand:\s(?<query>[^ ]+)\s(?<total>[^ ].+)\s(?<planSummary>planSummary.+)protocol:op_msg\s(?<op_msg>.+)ms"] }
        remove_field => "message"
        }
      if "_grokparsefailure" in [tags] {
           drop { }
       }
      mutate {
        #remove_field => "total"
        convert => {
         "op_msg" => "integer"
        }
      }
    }
    else if "digdag_log" in [tags] {
      grok {
        match => { "message" => ["(?<path>[^ ]+):(?<errMsg>[^*]+)"] }
        remove_field => "message"
        }
      if "_grokparsefailure" in [tags] {
           drop { }
       }
    }
}
 
output {
  if "access" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "access-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
   else if "error" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "error-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
    else if "app_log" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "app_log-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
   else if "auth" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        ecs_compatibility => disabled
        index => "auth-%{+yyyy.MM.dd}"
        }
    }
   else if "syslog" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "syslog-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
   else if "mongo" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "mongo-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
   else if "digdag_log" in [tags] {
    elasticsearch {
        hosts => ["test:9200"]
        manage_template => false
        index => "digdag-%{+yyyy.MM.dd}"
        ecs_compatibility => disabled
        }
    }
}

Nginx Access Log와 Error Log, System log, MongoDb SlowQuery, DigDag Log 에 대해 filter grok pattern/regex 사용하여 파싱

 

filebeat 설치

  • filebeat.yml 파일로 filebeat를 설정하고, modules.d에서 모듈 관리 각 로그에 대한 모듈를 저장/관리
-- 다운로드 및 설치
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.9.2-x86_64.rpm
rpm -ivh filebeat-7.9.2-x86_64.rpm
 
-- 설치 확인
rpm -qa | grep filebeat
filebeat-7.9.2-1.x86_64

 

filebeat 설정

# cd /etc/filebeat
 
[root@abcd filebeat]# ls
fields.yml  filebeat.reference.yml  filebeat.yml  modules.d
 
 
# cat filebeat.yml
 
filebeat.inputs:
 - type: log
   enabled: true
   paths:
     - /tmp/test.log
   tags: ["app_log"]
   symlinks: true
   exclude_lines: ["DEBUG|INFO|TRACE|WARN"]
 
 - type: log
   enabled: true
   paths:
     - /var/log/nginx/access.log
   tags: ["access"]
   exclude_lines: [".*l7check.*"]
 
 - type: log
   enabled: true
   paths:
     - /var/log/nginx/error.log
   tags: ["error"]
 
 - type: log
   enabled: true
   paths:
     - /var/log/message*
   tags: ["syslog"]
 
 - type: log
   enabled: true
   paths:
     - /var/log/secure*
   tags: ["auth"]
 
 - type: log
   enabled: true
   paths:
     - /data/mongodb/router/log/mongos.log
   tags: ["mongo"]
 
filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enable: true
 
setup.template.settings:
  index.number_of_shards: 1
 
setup.kibana:
  host: "test:5601"
 
output.logstash:
  hosts: ["test:5044"]
 
#output.elasticsearch:
#  hosts: ["test-es:9200"]
 
processors:
#  - add_host_metadata: ~
#  - add_cloud_metadata: ~
#  - add_docker_metadata: ~
#  - add_kubernetes_metadata: ~
   - drop_fields:
       fields: ["agent.ephemeral_id", "agent.hostname", "agent.id", "agent.type", "agent.version", "ecs.version", "input.type", "log.offset", "version"]
 
logging.level: info
#logging.level: debug
 
 
 
# filebeat 모듈은 선택에 따라 사용하는 것으로 함
 
# filebeat 실행
nohup filebeat -e -c /etc/filebeat/filebeat.yml &

 

728x90
300x250

+ Recent posts