EFK 스택을 구축하여 GitLab의 로그를 수집하면, 로그 데이터를 시각화하여 대시보드를 생성할 수 있습니다. 이를 활용하면 운영에 필요한 다양한 정보와 인사이트를 얻어, 데이터를 기반으로 의사결정을 내리는 데 도움이 됩니다. 또한, 시스템 관리자가 터미널에서 로그 파일에 직접 액세스하지 않고도, Web UI에서 로그를 분석하여 GitLab 시스템에서 발생하는 문제를 해결하는데 활용할 수 있습니다.
이번 글에서는 EFK 스택을 설치하고 구성하여 GitLab 로그를 수집한 후, Git Activity 대시보드를 만드는 방법을 알아보겠습니다.
EFK Stack이란?
EFK 스택은 Elasticsearch, Fluentd, Kibana로 구성된 스택을 의미하는 약어입니다. 이 세 가지 도구를 결합하여 확장 가능하고 유연하며 사용하기 쉬운 로그 수집 및 분석 파이프라인을 구축할 수 있습니다.
Elasticsearch는 Apache Lucene 기반의 분산 검색 및 분석 엔진입니다. 정해진 스키마가 없는 JSON 문서로 데이터를 저장합니다.
Kibana는 데이터 시각화 및 탐색 도구입니다. 차트와 그래프를 이용해 Elasticsearch 색인(Index)에 저장된 데이터를 시각화 할 수 있습니다.
Fluentd는 로그 데이터를 수집 및 변환하여 다양한 백엔드(Elasticsearch, S3, Mongodb, HDFS 등)로 전달해주는 오픈소스 데이터 수집기입니다. 시스템 및 애플리케이션의 로그를 수집하여 전달하는 역할과 하나 혹은 여럿으로부터 전달받은 데이터를 필터링 및 처리하여 적절한 백엔드로 라우팅하는 역할로 구분한 Forwarder/Aggregator 아키텍처로 구성할 수 있습니다. 적은 리소스를 사용해야 하는 환경에서는 경량화 버전인 Fluent Bit를 사용할 수도 있습니다.
로그 수집기로 Fluentd 대신 Logstash를 사용하여 구성하면 ELK Stack이라고 합니다.
두 로그 수집기 중 어느 것이 더 좋은지 판단하기 어려우며, 서로 장단점이 있습니다. 주어진 요구사항과 환경에 따라 더 적합한 것을 선택하여 사용하거나, 때에 따라서 동시에 사용할 수도 있습니다.
Logstash와 Fluentd를 비교한 아랫글들을 참고하십시오.
로그 모니터링 시스템의 필요성
자체 관리형 GitLab의 Admin Area > Monitoring > Logs 메뉴를 통해 GitLab의 여러 로그 파일을 볼 수 있었으나, GitLab 13.0부터 다중 노드 설정에서는 로깅이 작동하지 않고 일부 정보를 표시하여 관리자에게 혼란을 줄 수 있어 Admin Area에서 이 기능이 제거되었습니다.
GitLab에는 GitLab 내의 모든 서비스와 구성 요소가 시스템 로그를 출력하는 고급 로그 시스템이 포함되어 있습니다. 이에 따라 GitLab Rails, GitLab Shell, NGINX, PostgreSQL, Puma 등의 로그가 production_json.log
, api_json.log
, application_json.log
, integrations_json.log
, sidekiq.log
, gitlab-shell.log
, puma_stdout.log
, gitlab_access.log
, gitlab_error.log
등 여러 로그 파일에 기록되며, 로그 파일이 일정 사이즈 이상이 되면 gzip
으로 압축되고 교체됩니다.
EFK 스택으로 로그 모니터링 시스템을 구축하면, 로그를 분석하기 위해 터미널에서 여러 로그 파일에 액세스하지 않고도 Kibana Discover 메뉴에서 이러한 로그 파일의 내용을 확인할 수 있습니다.
아키텍처
Fluent Bit가 GitLab의 로그를 수집하여 Fluentd로 전송합니다.
Fluentd는 Aggregator의 역할로 수집된 로그를 전달받아 Elasticsearch에 전달하여 데이터를 저장합니다.
Kibana에서는 Elasticsearch의 데이터를 조회하고 시각화합니다.
- Ubuntu 20.04 LTS에 Docker Compose을 이용하여 EFK Stack 구성
- GitLab이 설치된 서버에 Linux package로 TD-Agent Bit (Fluent Bit) 설치
EFK Stack 설치 및 구성
Docker Engine 및 Docker Compose를 설치하고, Docker Compose를 이용하여 EFK(Elasticsearch + Fluentd + Kibana) Stack을 설치합니다.
Timezone 설정
$ date
Tue Sep 28 03:59:31 UTC 2021
$ sudo timedatectl set-timezone 'Asia/Seoul'
$ date
Tue Sep 28 12:59:51 KST 2021
$ sudo ls -al /etc/localtime
lrwxrwxrwx 1 root root 32 Sep 28 12:59 /etc/localtime -> ../usr/share/zoneinfo/Asia/Seoul
Chrony 설치
시간을 동기화하는 NTP(Network Time Protocol) 데몬(daemon)인 Chrony를 설치합니다.
sudo apt-get update
sudo apt-get install chrony -y
sudo systemctl enable chronyd
sudo systemctl start chronyd
sudo systemctl status chronyd
Timezone 및 시간 동기화 상태를 확인합니다.
$ sudo timedatectl status
Local time: Tue 2021-09-28 13:30:28 KST
Universal time: Tue 2021-09-28 04:30:28 UTC
RTC time: Tue 2021-09-28 04:30:28
Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
EFK Stack 구성
Elasticsearch의 데이터를 저장할 수 있을 만큼의 여유 공간이 있는 경로에 EFK Stack 작업 디렉토리 (Working directory)를 생성합니다.
cd /data
mkdir efk-stack
cd efk-stack
docker-compose.yml
파일을 생성합니다.
vi docker-compose.yml
아래 내용으로 작성하고 저장합니다.
version: '3.9'
services:
fluentd:
build: ./fluentd
container_name: fluentd
mem_limit: 256m
volumes:
- './fluentd/conf:/fluentd/etc'
links:
- 'elasticsearch'
ports:
- '24224:24224'
- '24224:24224/udp'
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
container_name: elasticsearch
mem_limit: 12g
environment:
- 'xpack.security.enabled=false'
- 'discovery.type=single-node'
- 'bootstrap.memory_lock=true'
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
ports:
- '9200:9200'
volumes:
- './elasticsearch_data:/usr/share/elasticsearch/data'
logging:
driver: 'json-file'
options:
max-size: '200m'
max-file: '10'
kibana:
image: docker.elastic.co/kibana/kibana:7.15.0
container_name: kibana
mem_limit: 1g
links:
- 'elasticsearch'
ports:
- '5601:5601'
Elasticsearch plugin이 포함된 커스텀 Fluentd 이미지 생성을 위한 Dockerfile
파일을 생성합니다.
mkdir fluentd
cd fluentd
vi Dockerfile
아래 내용으로 작성하고 저장합니다.
FROM fluent/fluentd:v1.14.1-debian-1.0
USER root
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--no-document", "--version", "5.1.0"]
USER fluent
Fluentd 구성 파일 fluentd/conf/fluent.conf
를 생성하고, Fluent Bit으로 전달받은 로그를 Elasticsearch에 저장하도록 구성합니다.
mkdir conf
cd conf
vi fluent.conf
아래 내용으로 작성하고 저장합니다.
<system>
log_level debug
</system>
<source>
@type forward
@label @GITLAB
bind 0.0.0.0
port 24224
</source>
<label @GITLAB>
<match gitlab.**>
@type copy
<store>
@type elasticsearch
host elasticsearch
port 9200
log_es_400_reason true
logstash_format true
logstash_prefix gitlab
logstash_dateformat %Y.%m.%d
suppress_type_name true
include_tag_key true
tag_key @log_name
<buffer>
flush_interval 1s
retry_wait 5s
</buffer>
</store>
</match>
</label>
<label @ERROR>
<match **>
@type stdout
</match>
</label>
Elasticsearch의 데이터를 영속적(Persistent)으로 저장하기 위한 바인드 마운트(Bind mount)용 디렉토리를 생성합니다.
cd ../..
mkdir elasticsearch_data