installation EFK
개요
EFK 조합을 구성하여 작동 방식을 알아보자.
- E : Elastic search
- F : Fluentd
- K : kibana
Fluentd?
Fluentd란 오픈소스 데이터 수집기이다.
Fluentd는 유연성을 위해 Ruby로 작성되었으며, 성능에 민감한 부분은 C로 작성되었다.
Fluentd 프로젝트는 Treasure Data 에서 후원한다. 참 감사하다.
라이센스는 아파치 라이선스 2.0 이다.
Fluentd를 이용한 로그 수집 아키텍쳐와 내부 구조는 아래 링크를 참조하여 이해했다.
Before Installation
시험환경
- OS : CentOS 7.5
- Hostname : jgs-fluentd
Set Up NTP
NTP(ntpd, chrony) 를 설정하여 정확한 현재 time stamp를 설정해야 하는 것을 권장한다.
ntp를 설치한다.:
[root@jgs-fluentd ~]# yum install ntp
File Descriptor
File Descriptor를 최대 수로 늘려야 한다.
아래 명령으로 현재 file descriptor를 확인한다.:
[root@jgs-fluentd ~]# ulimit -n
1024
- 1024는 충분하지 않다.
아래 파일에 다음 줄을 추가한다.:
[root@jgs-fluentd ~]# vi /etc/security/limits.conf
...
root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536
- 적용하려면 서버를 재시작 해야한다.
Optimize Network Kernel Parameters
많은 Fluentd 인스턴스로 구성된 대규모 환경의 경우 아래 매개변수를 추가한다.:
[root@jgs-fluentd ~]# cat /etc/sysctl.conf
...
net.core.somaxconn = 1024
net.core.netdev_max_backlog = 5000
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_rmem = 4096 12582912 16777216
net.ipv4.tcp_max_syn_backlog = 8096
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65535
- 이 커널 옵션은 AWS 수석 성능 아키텍트인 Brendan Gregg 의 아래 프레젠테이션에서 가져온 것이다.
- https://www.slideshare.net/brendangregg/how-netflix-tunes-ec2-instances-for-performance
Install by RPM Package
Fluentd 배포 rpm 패키지는 td-agent 이다.
이 패키지는 Treasure Data 에서 관리한다.
td-agent를 설치하자.:
[root@jgs-fluentd ~]# curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent3.sh | sh
...
========================================================================================================================================================================
Package Arch Version Repository Size
========================================================================================================================================================================
Installing:
td-agent x86_64 3.4.1-0.el7 treasuredata 48 M
Installing for dependencies:
at x86_64 3.1.13-24.el7 base 51 k
avahi-libs x86_64 0.6.31-19.el7 base 61 k
bc x86_64 1.06.95-13.el7 base 115 k
cups-client x86_64 1:1.6.3-35.el7 base 151 k
cups-libs x86_64 1:1.6.3-35.el7 base 357 k
ed x86_64 1.9-4.el7 base 72 k
m4 x86_64 1.4.16-10.el7 base 256 k
mailx x86_64 12.5-19.el7 base 245 k
patch x86_64 2.7.1-10.el7_5 base 110 k
psmisc x86_64 22.20-15.el7 base 141 k
redhat-lsb-core x86_64 4.1-27.el7.centos.1 base 38 k
redhat-lsb-submod-security x86_64 4.1-27.el7.centos.1 base 15 k
spax x86_64 1.5.2-13.el7 base 260 k
time x86_64 1.7-45.el7 base 30 k
Transaction Summary
========================================================================================================================================================================
Install 1 Package (+14 Dependent packages)
- 위 쉘 스크립트는 새로운 rpm 저장소를 등록하고 rpm 패키지를 설치한다.
아래와 같이 repo가 추가 된다.:
[root@jgs-fluentd ~]# cat /etc/yum.repos.d/td.repo
[treasuredata]
name=TreasureData
baseurl=http://packages.treasuredata.com/3/redhat/$releasever/$basearch
gpgcheck=1
gpgkey=https://packages.treasuredata.com/GPG-KEY-td-agent
fluentd를 시작하고 살펴보자.:
[root@jgs-fluentd ~]# systemctl start td-agent
[root@jgs-fluentd ~]# ss -nltp |grep fluentd
LISTEN 0 128 *:8888 *:* users:(("ruby",pid=31738,fd=15),("fluentd",pid=31733,fd=15))
[root@jgs-fluentd ~]# ls -l /opt/td-agent/
total 40
drwxrwxr-x. 2 root root 4096 May 18 21:33 bin
drwxrwxr-x. 7 root root 4096 May 21 11:13 embedded
drwxrwxr-x. 5 root root 4096 May 21 11:13 etc
-rw-rw-r--. 1 root root 3783 May 18 21:33 LICENSE
drwxrwxr-x. 2 root root 4096 May 21 11:12 LICENSES
drwxrwxr-x. 4 root root 4096 May 21 11:13 usr
-rw-rw-r--. 1 root root 8690 May 18 21:33 version-manifest.json
-rw-rw-r--. 1 root root 3761 May 18 21:33 version-manifest.txt
[root@jgs-fluentd ~]# ls -l /var/log/td-agent/
total 8
drwxr-xr-x. 3 td-agent td-agent 4096 May 21 11:21 buffer
-rw-r--r--. 1 td-agent td-agent 3111 May 21 11:21 td-agent.log
- 포트는 8888번 포트를 listen한다.
- /opt/td-agent 디렉토리에 모든 관련 파일들이 위치한다.
- 추가로 이 패키지로 인해 설치된 파일 목록은 아래 링크에서 확인 가능하다.
- https://support.treasuredata.com/hc/en-us/articles/360000687108-Overview-of-Server-Side-Agent-td-agent-
프로세스.:
[root@jgs-fluentd ~]# ps auxww |grep td-agent
td-agent 31733 0.0 0.4 241296 37716 ? Sl 11:21 0:00 /opt/td-agent/embedded/bin/ruby /opt/td-agent/embedded/bin/fluentd --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid
td-agent 31738 0.0 0.6 257592 55040 ? Sl 11:21 0:01 /opt/td-agent/embedded/bin/ruby -Eascii-8bit:ascii-8bit /opt/td-agent/embedded/bin/fluentd --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid --under-supervisor
- td-agent가 시작되면 마스터와 슬레이브 두 프로세스가 시작된다.
- 마스터 프로세스는 슬레이브 프로세스의 수명주기를 관리하고,
- 슬레이브가 실제 로그를 수집한다.
input 설정에 따라 아래 포트가 열린다.
- in_tail : notthing
- in_forward : tcp/24224, udp/24224
- in_unix : /var/run/td-agent/td-agent.sock
에이전트 설정파일은 아래 위치한다.:
/etc/td-agent/td-agent.conf
- 기본 설정은 HTTP에서 로그를 가져와서 stdout(/var/log/td-agent.log) 으로 라우트 하도록 구성되어 있다.
curl 명령을 사용하여 샘플 로그 레코드를 게시해보자.:
[root@jgs-fluentd ~]# curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test
stdout으로 설정된 /var/log/td-agent.log를 확인해보자.:
[root@jgs-fluentd ~]# tail -n1 /var/log/td-agent/td-agent.log
2019-05-21 11:29:33.743030358 +0900 debug.test: {"json":"message"}
이제 Fluentd를 사용하여 실제 로그를 수집할 준비가 되었다.
플러그인
Fluentd는 아래 7가지 플러그인 유형이 있다.
- Input
- Parser
- Filter
- Output
- Formatter
- Storage
- Buffer : 이것은 중요한 부분이라 아래 자세히 정리한다.
Buffer
Buffer 플러그인은 output 플러그인 안에서 사용된다.
예를들어, out_s3(출력 플러그인) 은 기본적으로 buf_file을 사용하여 실제 S3로 로그 데이터를 전송하기 전에 버퍼 파일에 임시로 저장한다.
- 버퍼는 file 혹은 메모리를 사용할 수 있다.
buffer 작동원리
버퍼는 chunk의 집합이다. Chunk는 단일 blob으로 연결되는 이벤트의 모음이다.
chunk를 화물상자라고 생각하면 된다. buffer plugin은 chunk를 경량 컨테이너로 사용하고, input source에서 들어오는 이벤트로 채우기 시작한다. chunk가 가득 차면 도착지로 배송한다.
내부적으로는 buffer plugin은 chunk를 저장할 두개의 분리된 장소를 사용한다.
- stage : chunk가 이벤트로 채워지는 장소
- queue : chunk가 배송전에 대기하는 대기열
chunk flow:
input event -> buffer(chunk) -> Queue(chunk) -> Output
Config File Syntax
설정파일은 Fluentd가 가질 input 또는 listeners 를 정의할 수 있고, Fluentd는 이벤트 데이터를 특정 output으로 라우팅 하기 위해 규칙을 설정한다.
설정 파일은 아래 문법으로 구성된다.
- source : 이것은 input source를 나타낸다. (로그를 어디서 수집할건지)
- match : 이것은 output 의 대상을 나타낸다. (로그를 어디에 쓸건지)
- filter : 이것은 이벤트 처리 파이프라인을 결정한다. (로그를 output에 보내기 전 사용자 정의 필터를 할 수 있음)
- system : 이것은 시스템 전체 구성을 설정한다.
- label : 이것은 output, filter를 그룹화한다.
- @include : 이것은 다른 파일을 include한다.
input과 output
아래는 in_http 와 out_stdout 플러그인을 예제로 이벤트 사이클을 살펴본다.:
<source>
@type http
port 8888
bind 0.0.0.0
</source>
- <source> 섹션은 데이터를 수집할 input 플러그인을 선택하여 구성한다.
- <source> 섹션에서 @type 에 플러그인을 지정하는데, http 플러그인은 Fluentd가 HTTP 엔드포인트로 전환하여 HTTP로 들어오는 메시지를 수집한다.
- 이제 http 포트 8888에서 들어오는 메시지를 Fluentd가 수집할 것이다.
그럼 이제 수집한 데이터를 어디로 보낼지? output을 지정하자.:
<match test.cycle>
@type stdout
</match>
- <match>는 데이터를 매칭하여 어디로 데이터를 보낼건지를 정하는데, 이 매칭할 조건이 tag기준이다.
- <match test.cycle>에서 test.cycle이 tag이다.
- @type 으로 stdout을 정의했고, 이는 그냥 표준출력인 터미널로 데이터를 보낸다(output).
그럼 이제 http 포트 8888로 데이터를 보내고 Fluentd가 이 데이터를 수집해서 output인 터미널로 출력하는지 확인해보자.:
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 17:42:04.828064560 +0900 test.cycle: {"action":"login","user":2}
- td-agent가 백그라운드 데몬으로 실행했기 때문에 stdout이 없고, stdout으로 출력되는 로그를 확인했다.
- 매칭할 태그를 선정하는 기준이 http://localhost:8888/<Tag> 와 같은 형태이다.
- http 포트 8888로 들어온 input 데이터를 output으로 잘 출력했다.
데이터는 크게 3가지 파트로 구성된다. * Time : 로그 데이터의 생성시간 * Record : 로그 데이터의 내용으로 JSON 형태로 정의된다. * Tag : 데이터의 분류이다. 각 로그 레코드는 tag를 통해서 로그의 종류가 정해진다. 이 tag에 따라서 로그에 대한 필터링, 라우팅과 같은 플러그인이 적용된다.
위 output으로 출력된 데이터를 기준으로 설명하자면 아래와 같다.
- Time : 828064560 +0900
- Tag : test.cycle:
- Record : {“action”:”login”,”user”:2}
Filter
필터는 input에서 output으로 이동 하기 전에 input에서 받은 이벤트를 output으로 통과 시키거나 거부하는 규칙을 설정할 수 있다.
필터 규칙을 설정해보자.:
<source>
@type http
port 8888
bind 0.0.0.0
</source>
<filter test.cycle>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>
</filter>
<match test.cycle>
@type stdout
</match>
- <fileter test.cycle> : 필터를 정의하고 test.cycle이라는 태그가 매칭되면 이 태그의 데이터를 검사한다.
- @type grep : 특정 문자열을 grep하기 위해 grep 플러그인을 사용했다.
- <exclude> 필터에 의해 exclude될 데이터에 대한 규칙을 정한다.
- key action : 로그 데이터의 action 키에 ^logout$ value가 포함되어 있으면 이 로그 데이터는 output에 도달하지 못하고 버려진다.
- patthern : regex로 필터할 데이터 규칙을 정한다.
아래 두 curl 명령으로 결과의 차이를 확인해보자.:
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 19:02:37.625941479 +0900 test.cycle: {"action":"login","user":2}
- 두 http 요청에 의해 하나만 output으로 출력이 되었다.
- action 키의 logout value는 필터에 의해 output으로 도달하지 못하고 필터가 된것이다.
필터의 설정은 위에서 아래 순서대로 처리되는 단계별 절차를 따른다. 그래서 많은 필터가 사용될 경우 설정파일이 복잡해진다.
그래서 이에 대한 대응으로 여러 필터를 통합할 수 있는데, 이 방법이 label이다.
labels
label 은 구성 파일을 복잡성을 해결하고 위쪽에서 아래쪽 순서를 따르지 않는다.
라벨에 키 값에 링크된 새 라우팅 섹션에서 이를 처리한다.
아래 예시를 보자.:
<source>
@type http
port 8888
bind 0.0.0.0
@label @STAGING <----레이블 추가
</source>
<filter test.cycle>
@type grep
<exclude>
key action
pattern ^login$
</exclude>
</filter>
<label @STAGING> <----이 레이블로 라우팅
<filter test.cycle>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>
</filter>
<match test.cycle>
@type stdout
</match>
</label>
- input 소스에 STAGING 라벨을 부여했고,
- <label @STAGING> 에서 직접 해당 input 데이터에 대한 필터 및 ountput 처리함.
- <source> 섹션 바로 아래에 있는 필터는 무시되고, <label> 섹션 안에서 필터처리를 한다.
결과를 보자.:
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
[root@jgs-fluentd td-agent]# tail -f /var/log/td-agent/td-agent.log
...
2019-05-23 22:50:23.228286212 +0900 test.cycle: {"action":"login","user":2}
- action : logout 과 login에 대한 HTTP 요청을 했다.
- 실제로 수집하여 output까지 전달된 데이터는 login이다
- <source> 섹션 바로 아래에 있는 필터인 login이 필터되지 않았고, <label> 섹션안에 있는 필터로 logout만 필터되었다.
이제 이 로그를 Elastic Search와 연결하여 로그를 Elastic Search 로 저장하자.
이렇게 하기 위해서는 Elasticsearch 플러그인이 필요하다.:
[root@jgs-fluentd ~]# td-agent-gem install fluent-plugin-elasticsearch
먼저 elasticsearch 서버를 구성하자.
Elastic Search?
Elasticsearch는 분산형 RESTful 검색 및 분석 엔진이다. 데이터를 중앙에 저장하여 예상 가능한 항목을 탐색하거나 예상치 못한 항목을 밝혀낼 수 있도록 지원한다.
Java로 작성되었으며 아파치 라이센스이다.
우리는 여기서 Fluentd로 부터 받은 로그 데이터를 저장하기 위해 사용할 것이고, elasticsearch 는 분산형 시스템이기 때문에 로그 데이터가 많아졌을 때 확장이 매우 수월하다.
ES Cluster
ES 클러스터는 아래 3가지 역할로 나뉘어 클러스터가 구성된다.
- Master Node
- Data Node
- Ingest Node
Mster Node
마스터 노드는 인덱스 생성, 삭제, 클러스터의 노드들 추적, 관리 및 데이터 입력시 어느 shard에 할당할지 결정하는 역할을 한다. 따라서 master node를 안정적으로 관리하는 것이 전체 cluster의 상태를 안정적으로 관리 할 수 있는 중요한 요소가 된다.
데이터 인덱싱, 검색 등에 의해 CPU, 메모리, I/O 리소스 소모가 클 수 있기 때문에 규모가 큰 클러스터 에서는 Master Node가 부하를 받지 않도록 Master와 Data 노드를 구분해서 운영하길 권장하고 있다.
Data Node
데이터 노드에는 Index를 생성한 문서 조각(shard)가 들어있다. 즉 실제 데이터를 저장 하는 저장소이다. 데이터 노드는 CRUD, 검색 및 집계와 같은 데이터 관련 작업을 처리한다. 이러한 작업은 I/O, 메모리 및 CPU를 많이 사용한다. 이러한 리소스를 모니터링하고 오버로드 된 경우 더 많은 데이터 노드를 추가하여 확장해야 한다.
Ingest Node
실제 문서 인덱싱이 발생하기 전에 Ingest Node를 사용하여 문서를 사전 처리한다. Ingest Node는 Bulk 와 인덱스의 요청을 가로 채고 변환을 적용한 다음 문서를 인덱스 또는 Bulk API로 다시 전달한다.
모든 노드는 기본적으로 Ingest 를 활성화 하므로 모든 노드가 Ingest task를 처리할 수 있다. Ingest 전용 노드를 따로 분리할 수도 있다.
인덱싱 전에 문서를 사전 처리하려면 일련의 프로세서를 지정하는 파이프라인을 정의해야 한다. 각 프로세서는 특정 방식으로 문서를 변환하는데,
예를들어 파이프라인에는 문서에서 필드를 제거하는 프로세서가 하나 있고, 필드의 이름을 바꾸는 다른 프로세서가 뒤에 오도록 구성할 수 있다.
Reference
노드의 생성 및 동작 원리
- 하나의 머신에서 Elasticsearch를 시작하게 되면, 하나의 Elasticsearch 노드가 생성된다.
- 이 노드는 동일한 네트워크 상에서 같은 클러스터명을 같는 클러스터가 존재하는 지 찾게된다.
- 만약 Join될 수 있는 클러스터가 없다면 이 노드는 스스로 클러스터를 생성하게 되고, 클러스터가 존재한다면 해당 클러스터에 Join 된다.
- 새로운 클러스터가 생성되었다면, 노드에는 아직 어떠한 인덱스도 존재하지 않는 상태이며, 새로운 인덱스를 생성할 때 인덱스를 몇개의 shard로 나누어 저장할 것인지를 정의할 수 있다.
- shard의 개수를 따로 지정하지 않는다면, Elasticsearch의 기본 shard 개수인 5개로 데이터가 나눠져 저장된다.
- 만약 노드가 기존에 존재하던 클러스터에 연결되고 해당 클러스터에 이미 인덱스가 존재한다면 해당 인덱스 shard들은 추가된 노드에 균일하게 재분산 된다.
shard and replica
- shard와 replica의 개념이 아주 중요한데, 아래 링크에 자세히 나와있으니 꼭 참조한다.
- http://guruble.com/elasticsearch-2-shard-replica/
용어
RDBMS와 ElasticSearch의 용어를 비교하면 아래와 같다.:
+------------------+--------------------+
| RDBMS | ElasticSearch |
+==================+====================+
| Database | Index |
+------------------+--------------------+
| Table | Type |
+------------------+--------------------+
| Row | Document |
+------------------+--------------------+
| Clumn | Field |
+------------------+--------------------+
| Schema | Mapping |
+------------------+--------------------+
| Index | Everything indexed |
+------------------+--------------------+
| SQL | Query DSL |
+------------------+--------------------+
설치
ElasticSearch를 설치하자.
ES 서버 정보
- Hostname : jgs-es
- IP : 172.20.1.11
- OS : CentOS7
Elasticsearch rpm 패키지 가져오기.:
[root@jgs-es ~]# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.1.1-x86_64.rpm
ES설치.:
[root@jgs-es ~]# rpm -ivh elasticsearch-7.1.1-x86_64.rpm
ES Start.:
[root@jgs-es ~]# systemctl daemon-reload
[root@jgs-es ~]# systemctl enable elasticsearch.service
[root@jgs-es ~]# systemctl start elasticsearch.service
프로세스 확인.:
elastic+ 11929 1 10 16:10 ? 00:00:40 /usr/share/elasticsearch/jdk/bin/java -Xms1g -Xmx1g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.io.tmpdir=/tmp/elasticsearch-8475739219258263933 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/lib/elasticsearch -XX:ErrorFile=/var/log/elasticsearch/hs_err_pid%p.log -Xlog:gc*,gc+age=trace,safepoint:file=/var/log/elasticsearch/gc.log:utctime,pid,tags:filecount=32,filesize=64m -Djava.locale.providers=COMPAT -Dio.netty.allocator.type=unpooled -Des.path.home=/usr/share/elasticsearch -Des.path.conf=/etc/elasticsearch -Des.distribution.flavor=default -Des.distribution.type=rpm -Des.bundled_jdk=true -cp /usr/share/elasticsearch/lib/* org.elasticsearch.bootstrap.Elasticsearch -p /var/run/elasticsearch/elasticsearch.pid --quiet
elastic+ 12013 11929 0 16:10 ? 00:00:00 /usr/share/elasticsearch/modules/x-pack-ml/platform/linux-x86_64/bin/controller
- 위 두개의 프로세스가 실행된다.
포트는 9200번과 9300번이 listen한다.
- 9200번 포트는 HTTP로 바인딩되고
- 9300번 포트는 TCP로 바인딩된다.
9200번 포트로 HTTP request를 보내보자.:
[root@jgs-es ~]# curl -X GET localhost:9200
{
"name" : "jgs-es.8.8.8.8",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "plUTCVx_QjqZmo6NokzBaw",
"version" : {
"number" : "7.1.1",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "7a013de",
"build_date" : "2019-05-23T14:04:00.380842Z",
"build_snapshot" : false,
"lucene_version" : "8.0.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
- name, cluster_name, version 등을 응답받았다.
RPM으로 ES를 설치하면 아래 경로에 구성 파일들이 배치된다.
- 기본파일 : /usr/share/elasticsearch
- 실행 파일 : bin/elasticsearch
- 플러그인 : plugins
- 설정 : /etc/elasticsearch
- elasticsearch.yml
- jvm.options
- log4j2.properties
- 데이터 (path.data) : /var/lib/elasticsearch
- 로그 (path.logs) : /var/log/elasticsearch
시스템구성
elasticsearch 를 사용하려면 필수 시스템 구성을 해야한다.
1.) ulimit
elasticsearch 유저가 최대 열수 있는 파일의 수(파일 디스크립터) 를 늘려야 한다.
sysvinit의 경우.:
# echo "elasticsearch - nofile 65535" >> /etc/security/limits.conf
난 centos7이라 systemd기반이다. systemd 기반에서는 아래와 같이 설정한다.:
[root@jgs-es ~]# vi /lib/systemd/system/elasticsearch.service
...
LimitNOFILE=65535
...
- 이미 65535로 되어있다. RPM 패키지로 설치하면 파일 디스크립터 설정이 이미 되어있네.
아래와 같이 검증한다.:
[root@jgs-es ~]# curl localhost:9200/_nodes/stats/process?pretty
...
"max_file_descriptors" : 65535,
...
2.) Disable swapping
대부분 OS는 파일시스템 캐시에 최대한 많은 메모리를 사용하려고 시도하고 사용하지 않는 응용프로그램 메모리를 swap하는데, 이로 인해 JVM 힙 부분또는 실행 가능 페이지가 디스크로 swap 될 수 있다. swapping은 노드 안전성과 성능에 매우 좋지않다. 그러므로 swap을 해제해야 한다.
쉽게 설명하자면 ES는 메모리에 많은양의 데이터를 올려놓고 searching, indexing 등의 작업을 하기 때문에 swap을 켜 놓으면 Disk로 내려서 작업이 되어 버리는 순간 성능에 악 영향을 끼칠 수 있다.
방법은 3가지이다.
- swapoff -a 로 스왑을 완전히 비활성화 하기
- 커널 파라매터인 vm.swappiness 설정하기
아래와 같이 설정한다.:
[root@jgs-es ~]# sysctl -w vm.swappiness=1
vm.swappiness = 1
- 이렇게 하면 커널의 스왑이 줄어들고 일반적으로 스왑을 하지 않는다.
- 전체 시스템이 비상 상황에서는 스왑이 사용될 수 있다.
- memory lock
위 두 설정은 root나 sudo 계정을 가지고 있을 때 가능한 설정인데, 이 설정은 JVM 자체에서 설정을 하는 방법이다.
Linux/Unix 에서 mlockall 을 사용하여 프로세스 주소 공간을 RAM에 lock하여 elasticsearch 메모리가 swap out 되지 않도록 하는 방법인데, 이 옵션은 ES가 구동될 때 자기한테 할당된 메모리를 lock 한 채로 뜨게 되며, swap으로 메모리가 빠져나가는 것을 방지 해 주는 옵션이다.
아래와 같이 설정한다.:
[root@jgs-es ~]# cat /etc/elasticsearch/elasticsearch.yml |grep memory_lock
bootstrap.memory_lock: true
- 이렇게하고 elasticsearch 를 재시작하면 되는데, 실제로 memory_lock을 하지 못한다.
- elasticsearch 유저가 memory lock할 권한이 없어서이다.
이후 root계정으로 전환하여 아래와 같이 설정한다.
sysvinit의 경우:
[root@jgs-es ~]# ulimit -l unlimited
RPM or deb 패키지로 설치한 경우.:
[root@jgs-es ~]# vi /etc/sysconfig/elasticsearch
MAX_LOCKED_MEMORY=unlimited
systemd 기반의 시스템인 경우.:
[root@jgs-es ~]# vi /lib/systemd/system/elasticsearch.service
LimitMEMLOCK=infinity
[root@jgs-es ~]# systemctl daemon-reload
[root@jgs-es ~]# systemctl restart elasticsearch
- 난 systemd 기반이라 위와 같이 설정함
검증.:
[root@jgs-es ~]# curl -X GET localhost:9200/_nodes?filter_path=**.mlockall
{"nodes":{"0CMiCjieSz-rrHoapxpu6Q":{"process":{"mlockall":true}}}}
- mlockall : true로 출력 되어야 한다.
3.) Java Heap 메모리 설정
ES의 Eeap 사이즈는 전체 서버가 가진 메모리의 반을 설정하는 것을 권장한다. (나머지 절반은 루씬 파일 캐시를 위해 남겨둬야함) 64G 이상의 메모리를 가지고 있는 서버라도 할지라도 30.5G 이상을 ES에 할당 하는 것은 권장하지 않는다.
권장 메모리 할당량에 대한 자세한 설명은 아래 링크를 참조하자.
JVM Heap 사이즈를 설정하자.:
[root@jgs-es ~]# vi /etc/elasticsearch/jvm.options
...
-Xms8g
-Xmx8g
...
- Xms는 Heap 사이즈의 최소값이고,
- Xms는 Heap 사이즈의 최대값이인데, 이 둘은 동일한 값을 사용하도록 권장한다.
4.) Cluster 설정
ES의 기본 클러스터 명은 elasticsearch 로 되어있다. ES의 노드들은 클러스터명을 기준으로 바인딩 되기 때문에 처음 설치가 끝나면 클러스터명을 유니크하게 바꿔주어야 한다.
설정하자.:
[root@jgs-es ~]# vi /etc/elasticsearch/elasticsearch.yml
...
cluster.name: jgs-es-cluster
...
node.name: jgs-es
...
- cluster.name : ES 클러스터 이름
- node.name : 현 시스템의 호스트네임 (노드를 구분하기 편하도록 노드명에 IP대신 호스트네임 입력)
확인.:
[root@jgs-es ~]# curl -X GET http://localhost:9200/
{
"name" : "jgs-es",
"cluster_name" : "jgs-es-cluster",
"cluster_uuid" : "plUTCVx_QjqZmo6NokzBaw",
"version" : {
"number" : "7.1.1",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "7a013de",
"build_date" : "2019-05-23T14:04:00.380842Z",
"build_snapshot" : false,
"lucene_version" : "8.0.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
- name과 cluster_uuid가 바뀌었다.
다중 노드의 경우 각 노드의 역할을 지정할 수 있는데, elasticsearch.yml 파일에 아래와 같이 지정하면 된다.
- node.master: true or false
- node.data: true or flase
- node.ingest: true or flase
위 3가지 노드 설정은 기본값으로 모두 true인데, 모두 flase로 설정하면 coordinating node로 작동하게 되는데 이는 부하 분산 역할을 하게 된다.
현재 나의 경우 단일 노드이므 기본값인 master,data,ingest 노드의 역할을 모두 하게된다.
5.) 네트워크 설정
ES는 기본적으로 루프백 주소(127.0.0.1)에 bind 한다. 이는 개발용 노드를 실행하기 충분하지만 다른 서버에 노드가 있는 클러스터를 구성하려면 비 루프백 주소에 bind 해야한다.
아래와 같이 0.0.0.0에 bind 하였다.:
[root@jgs-es elasticsearch]# vi elasticsearch.yml
...
network.host: 0.0.0.0
...
Fluentd 에서 ES로 로그 전송
Fluentd에서 /var/log/messages 로그를 수집하여 ES에 저장하는 설정을 하자.
Flentd 설정.:
[root@jgs-fluentd td-agent]# cat td-agent.conf
<source>
@type tail
path /var/log/messages
pos_file /var/log/td-agent/messages.log.pos
tag messages.log
format json
</source>
<match messages.log>
@type elasticsearch
host 172.20.1.11
port 9200
logstash_format true
</match>
- in_tail 플러그인으로 /var/log/messages 로그를 수집한다.
- 수집한 데이터는 in_elasticsearch 플러그인으로 172.20.1.11(ES server)로 보낸다.
Kibana
Kibana는 강력하고 화려한 그래픽을 통해 데이터를 작업할 수 있도록 해주는 오픈소스 데이터 시각화 플랫폼이다.
여기선 ES에 저장된 로그 데이터를 시각화하기 위해 Kibana를 사용한다.
Kibana를 설치한다.:
[root@jgs-es ~]# wget https://artifacts.elastic.co/downloads/kibana/kibana-5.4.0-x86_64.rpm
[root@jgs-es ~]# rpm -ivh kibana-5.4.0-x86_64.rpm
- jgs-es 서버에 Kibana를 설치하였다.
프로세스 및 포트.:
[root@jgs-es ~]# ps -ef |grep kibana
kibana 4385 1 9 12:13 ? 00:00:03 /usr/share/kibana/bin/../node/bin/node --no-warnings /usr/share/kibana/bin/../src/cli -c /etc/kibana/kibana.yml
[root@jgs-es ~]# ss -nltp
LISTEN 0 128 127.0.0.1:5601 *:* users:(("node",pid=4385,fd=11))
kibana 설정.:
[root@jgs-es ~]# vi /etc/kibana/kibana.yml
...
server.host: "0.0.0.0"
- 웹 브라우저를 열고 <http://kibanaIP:5601 포트로 접속을 확인한다.