สถาบันข้อมูลขนาดใหญ่ (องค์การมหาชน)

รู้จักกับ ELK Stack เครื่องมือบริหารจัดการข้อมูลประวัติบันทึกการทำงาน (Log file) แบบรวมศูนย์

Jun 13, 2023

ELK Stack คืออะไร

เพื่อน ๆ คนไหนเคยได้ยินคำว่า “ELK Stack” กันบ้าง ครั้งแรกที่ได้ยินรู้สึกเอ๊ะกันบ้างหรือเปล่าว่ามันคืออะไร มันมาจากไหน ใช้ทำอะไร… ทำไมคนถึงพูดถึงกัน บทความนี้จะพาเพื่อน ๆ มาทำความรู้จักกับเจ้า ELK Stack เพื่ออัปเดตเทคโนโลยีให้ตามทันโลกยุคดิจิทัลกันจ้า

หากพูดถึงโลกยุคดิจิทัลที่ทุกอย่างถูกเติมเต็มด้วยเทคโนโลยีไม่ว่าจะเป็นเทคโนโลยีทางด้านการแพทย์ การเกษตร การท่องเที่ยว การสื่อสาร หรือแม้กระทั่งในชีวิตประจำวันที่เราสามารถแสกน QR โค้ดเพื่อชำระค่าสินค้า/บริการ ก็เป็นส่วนหนึ่งของเทคโนโลยี ด้วยเทคโนโลยีที่กระจายไปทุกหนแห่งทำให้เกิดการไหลเวียนของข้อมูลปริมาณมาก มีทั้งข้อมูลสำคัญ ข้อมูลที่รอการวิเคราะห์ รวมถึงการเชื่อมโยงข้อมูลเพื่อให้เกิดประโยชน์ ยกตัวอย่างเช่น ระบบเชื่อมโยงข้อมูลสุขภาพ Health Link ที่สามารถเชื่อมโยงข้อมูลประวัติการรักษาของคนไข้ระหว่างโรงพยาบาลต่าง ๆ ทั่วประเทศ หรือแม้แต่แอปพลิเคชันหรือระบบต่าง ๆ ที่มีการนำข้อมูลมาใช้ให้เกิดประโยชน์

นอกจากตัวเอกอย่างแอปพลิเคชันหรือระบบแล้ว สิ่งที่สำคัญอีกอย่างก็คือการดูแลระบบให้สามารถทำงานได้ตามปกติ สำหรับเครื่องมือที่ใช้ในการติดตาม ตรวจสอบการทำงานของระบบ (Monitoring) นั้นสามารถพบได้ทั่วไป แต่หากเป็นระบบที่มิได้ถูกติดตั้งบนระบบคลาวด์หรือเซิร์ฟเวอร์เพียงตัวเดียว การเข้าถึงเครื่องเพื่อตรวจสอบไฟล์บันทึกการทำงาน (Log file) และสถานะการทำงานของระบบคงไม่ใช่เรื่องง่าย ๆ อีกทั้งยังเสียแรงและเวลาในการเข้าถึงเครื่องต่าง ๆ ด้วยเหตุผลนี้จึงมีเครื่องมือที่จะสามารถช่วยคุณประหยัดแรงและเวลาในการเข้าถึงหลาย ๆ เครื่อง โดยการรวบรวมข้อมูลประวัติบันทึกการทำงาน (Log file) ไว้ที่ระบบกลาง เพื่อสะดวกต่อการติดตามและตรวจสอบการทำงานของระบบ เครื่องมือที่กล่าวมานั้นไม่ใช่สิ่งอื่นใด นั่นก็คือ ELK Stack นั่นเอง!!!

ELK Stack เป็นเครื่องมือในการบริหารจัดการข้อมูลประวัติบันทึกการทำงานแบบรวมศูนย์ (Centralized Logging System) ซึ่งเป็นเครื่องมือที่ได้รับความนิยมอย่างมาก ประกอบด้วย Elasticsearch, Logstash, Kibana โดย Logstash ทำหน้าที่รองรับการนำเข้าข้อมูล ประมวลผลข้อมูลและบริหารจัดการข้อมูลไฟล์ Logs จากแหล่งต่าง ๆ สามารถคัดกรอง แปลงและเรียบเรียงข้อมูล (Parse, Filter, Transform) ก่อนส่งข้อมูลจัดเก็บลงฐานข้อมูล Elasticsearch ซึ่งเป็นฐานข้อมูล NoSQL และรองรับการทำงานแบบ Full-Text Search โดยสามารถ Query ข้อมูลจากฐานข้อมูล Elasticsearch เพื่อนำไปจัดทำและแสดงผลข้อมูลในรูปแบบ Data Visualization บนเครื่องมือ Kibana ได้ สำหรับกระบวนการขนส่งข้อมูลนั้นมีหลากหลายเครื่องมือที่ใช้ในการส่งข้อมูลไปยัง Logstash หรือ Elasticsearch ยกตัวอย่างเช่น สามารถใช้ Filebeat ที่เป็นเครื่องมือในการส่งข้อมูล Log file จากหลากหลายเครื่องไปยัง Logstash เพื่อรวบรวมข้อมูลดังกล่าวไว้ที่ระบบกลาง เป็นต้น

Diagram ของ ELK Stack
ที่มา: dzone.com

มาถึงตรงนี้ใครอยากลองใช้งาน ELK Stack แล้วบ้าง สามารถลองทำตามขั้นตอนด้านล่างได้เลยจ้า

ติดตั้ง ELK Stack

ขั้นตอนที่ 1 เริ่มจากการติดตั้ง ELK Stack ซึ่งสามารถดาวน์โหลดไฟล์ docker-compose และ Configuration ได้จาก Repository ทั่วไป ในบทความนี้ดาวน์โหลดจาก https://github.com/deviantony/docker-elk

โครงสร้างของ Repository และไฟล์ที่ใช้งานหลักๆ ประกอบด้วย

|-- docker-compose.yml			# ไฟล์ docker-compose สำหรับ Setup Services
|--logstash				# โฟลเดอร์ Logstash
	|--config
		|--logstash.yml		# สำหรับตังค่า Logstash เพิ่มเติม
	|--pipeline
		|--logstash.conf	# สำหรับกำหนด Pipeline และการ Parser
	|--Dockerfile			# สำหรับเพิ่ม Logstash Plugins
|--elasticsearch			# โฟลเดอร์ Elasticsearch
	|--config
		|--elasticsearch.yml	# สำหรับตั้งค่า Elasticsearch เพิ่มเติม
	|--Dockerfile			# สำหรับเพิ่ม Elasticsearch plugins
|--kibana				# โฟลเดอร์ Kibana
	|--config
		|--kibana.yml		# สำหรับตั้งค่า Kibana เพิ่มเติม
	|--Dockerfile			# สำหรับเพิ่ม Kibana Plugins
|--.env					# สำหรับกำหนด Environment เช่น Password ของแต่ละ Services

ไฟล์ docker-compose.yml (based on github repository) มีการติดตั้ง Services ของ Elasticsearch, Logstash, Kibana โดยมีการเปิดใช้งาน Port ดังนี้

  • 5044: Logstash Beats input
  • 50000: Logstash TCP input
  • 9600: Logstash monitoring API
  • 9200: Elasticsearch HTTP
  • 9300: Elasticsearch TCP transport
  • 5601: Kibana
version: '3.7'

services:

  # The 'setup' service runs a one-off script which initializes the
  # 'logstash_internal' and 'kibana_system' users inside Elasticsearch with the
  # values of the passwords defined in the '.env' file.
  #
  # This task is only performed during the *initial* startup of the stack. On all
  # subsequent runs, the service simply returns immediately, without performing
  # any modification to existing users.
  setup:
    build:
      context: setup/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    init: true
    volumes:
      - setup:/state:Z
    environment:
      ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
      LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
      KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
    networks:
      - elk
    depends_on:
      - elasticsearch

  elasticsearch:
    build:
      context: elasticsearch/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,z
      - elasticsearch:/usr/share/elasticsearch/data:z
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: -Xms512m -Xmx512m
      # Bootstrap password.
      # Used to initialize the keystore during the initial startup of
      # Elasticsearch. Ignored on subsequent runs.
      ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
      # Use single node discovery in order to disable production mode and avoid bootstrap checks.
      # see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
      discovery.type: single-node
    networks:
      - elk

  logstash:
    build:
      context: logstash/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
    ports:
      - "5044:5044"
      - "50000:50000/tcp"
      - "50000:50000/udp"
      - "9600:9600"
    environment:
      LS_JAVA_OPTS: -Xms256m -Xmx256m
      LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    build:
      context: kibana/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
    ports:
      - "5601:5601"
    environment:
      KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
      # Fleet plugin
      KIBANA_FLEET_SETUP: '1'
    networks:
      - elk
    depends_on:
      - elasticsearch

networks:
  elk:
    driver: bridge

volumes:
  setup:
  elasticsearch:

สำหรับการตั้งค่า ELK Stack เพิ่มเติมสามารถทำตามขั้นตอนดังต่อไปนี้

ขั้นตอนที่ 1.1 ทำการเพิ่ม Plugin logstash-input-beats เพื่อให้ Logstash สามารถเชื่อมต่อกับ Filebeat ได้ โดยเพิ่มคำสั่ง RUN logstash-plugin install logstash-input-beats ในไฟล์ Logstash Dockerfile ตามตัวอย่างด้านล่าง

ARG ELASTIC_VERSION

# https://www.docker.elastic.co/
FROM docker.elastic.co/logstash/logstash:${ELASTIC_VERSION}

# Add your logstash plugins setup here
# Example: RUN logstash-plugin install logstash-filter-json
RUN logstash-plugin install logstash-input-beats

ขั้นตอนที่ 1.2 เพิ่มเติม/แก้ไข Logstash Pipeline ในไฟล์ logstash.conf โดยที่ตัวแปร input เป็นการกำหนดค่าการรับข้อมูลจาก filebeat ที่ port 5044 และส่งมายัง Logstash Pipeline เพื่อคัดกรอง แปลงและเรียบเรียงข้อมูล โดยข้อมูลที่ได้จาก Logstash จะอยู่ในรูปแบบ JSON และ output การส่งออกจะถูกจัดเก็บลงฐานข้อมูล Elasticsearch ที่มีกำหนดค่า hosts และ port เป็น elasticsearch:9200

input {
  beats {
    port => 5044
    }
}

## Add your filters / logstash plugins configuration here

output {
  stdout { 
    codec => rubydebug 
  }
  
  elasticsearch {
		hosts => "elasticsearch:9200"
		index => "logstash-%{+YYYY.MM.dd}"
		user => "logstash_internal"
		password => " ${LOGSTASH_INTERNAL_PASSWORD} "
	} 
}

ขั้นตอนที่ 1.3 เพิ่มการตั้งค่า port 5601 สำหรับ Kibana ในไฟล์ kibana.yml

---
## Default Kibana configuration from Kibana base image.
## https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts
#
server.name: kibana
server.host: 0.0.0.0
server.port: 5601
elasticsearch.hosts: [ 'http://elasticsearch:9200' ]
monitoring.ui.container.elasticsearch.enabled: true

## X-Pack security credentials
#
elasticsearch.username: kibana_system
elasticsearch.password: changeme

## Fleet
## https://www.elastic.co/guide/en/kibana/current/fleet-settings-kb.html
#
xpack.fleet.agents.fleet_server.hosts: [ 'http://fleet-server:8220' ]
xpack.fleet.agents.elasticsearch.hosts: [ 'http://elasticsearch:9200' ]
# xpack.security.enabled: false

xpack.fleet.packages:
  - name: fleet_server
    version: latest
  - name: system
    version: latest
  - name: elastic_agent
    version: latest

xpack.fleet.agentPolicies:
  - name: Fleet Server policy
    id: fleet-server-policy
    description: Fleet Server policy
    monitoring_enabled:
      - logs
      - metrics
    package_policies:
      - name: fleet_server-1
        package:
          name: fleet_server
      - name: system-1
        package:
          name: system
      - name: elastic_agent-1
        package:
          name: elastic_agent

โดยหลังจากปรับการตั้งค่าเรียบร้อยแล้วให้รัน Container Services ขึ้นมาก็จะได้ Service ของ Elasticsearch, Logstash, Kibana ตามด้านล่าง

Container Services ของ Elasticsearch, Logstash, Kibana

สามารถเรียกดูข้อมูลใน Elasticsearch โดยใช้คำสั่ง

$curl -u username:password -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "query_string": {
      "query": "*"
    }
  }
}'

ตัวอย่างข้อมูลที่ถูกจัดเก็บลงฐานข้อมูล Elasticsearch ในรูปแบบ JSON

สามารถเข้าถึง Kibana ได้ที่ http://localhost:5601 (Server IP ที่ติดตั้ง Kibana) โดยตรวจสอบ Firewall และการเปิด port 5601 เพื่อให้สามารถเข้าถึง URL ดังกล่าว

UI ของ Kibana

ติดตั้ง Filebeat

ขั้นตอนที่ 2 การติดตั้ง Filebeat เพื่อส่งข้อมูล Log file ไปยัง Logstash สามารถทำตามขั้นตอนดังนี้

ขั้นตอนที่ 2.1 ดาวน์โหลด image ของ Filebeat โดยรันคำสั่ง

$docker pull docker.elastic.co/beats/filebeat:8.4.3

ขั้นตอนที่ 2.2 เพิ่มเติม/แก้ไข การตั้งค่าในไฟล์ filebeat.yml โดยไปที่ตัวแปร filebeat.inputs เพื่อกำหนดที่อยู่ (paths) ของไฟล์ Log ที่จะถูกส่งจาก Filebeat ตัวอย่างเช่น /var/log/nginx/*.log ไปยัง Logstash โดยมีตัวแปร output.logstash ที่มีการกำหนดค่า hosts ไว้ที่ port 5044 ตัวอย่างเช่น localhost:5044  

######################## Filebeat Configuration ############################

#=========================== Filebeat prospectors =============================

filebeat.inputs:
  
- type: log
# Change to true to enable this input configuration.
  enabled: true
# Paths that should be crawled and fetched. Glob based paths.
  paths:
  - /var/log/nginx/*.log

#========================= Filebeat global options ============================

#filebeat.registry_file: /var/lib/filebeat/registry

#================================ Outputs ======================================

#----------------------------- Logstash output ---------------------------------
output.logstash:
  enabled: true
  # The Logstash hosts
  hosts: ["localhost:5044"]
 
  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: ["/etc/logstash/logstash-forwarder.crt"]

  # Certificate for TLS client authentication
  #ssl.certificate: "/etc/logstash/logstash-forwarder.crt"

  # Client Certificate Key
  #ssl.key: "/etc/logstash/logstash-forwarder.key"

  # Configure SSL verification mode. If `none` is configured, all server hosts
  # and certificates will be accepted. In this mode, SSL based connections are
  # susceptible to man-in-the-middle attacks. Use only for testing. Default is
  # `full`.
  #ssl.verification_mode: full
  #ssl.verification_mode: none

#================================ Logging ======================================
# There are three options for the log output: syslog, file, stderr.
# Under Windows systems, the log files are per default sent to the file output,
# under all other system per default to syslog.

# Sets log level. The default log level is info.
# Available log levels are: critical, error, warning, info, debug
logging.level: info

# Enable debug output for selected components. To enable all selectors use ["*"]
# Other available selectors are "beat", "publish", "service"
# Multiple selectors can be chained.
#logging.selectors: [ ]

# Send all logging output to syslog. The default is false.
logging.to_syslog: false

# If enabled, filebeat periodically logs its internal metrics that have changed
# in the last period. For each metric that changed, the delta from the value at
# the beginning of the period is logged. Also, the total values for
# all non-zero internal metrics are logged on shutdown. The default is true.
#logging.metrics.enabled: true

# The period after which to log the internal metrics. The default is 30s.
#logging.metrics.period: 30s

# Logging to rotating files files. Set logging.to_files to false to disable logging to
# files.
logging.to_files: true
logging.files:
  # Configure the path where the logs are written. The default is the logs directory
  # under the home path (the binary location).
  #path: /var/log/filebeat
  path: /var/log/filebeat

  # The name of the files where the logs are written to.
  #name: filebeat
  name: filebeat.log

  # Configure log file size limit. If limit is reached, log file will be
  # automatically rotated  
  rotateeverybytes: 10485760 # = 10MB

  # Number of rotated log files to keep. Oldest files will be deleted first.  
  keepfiles: 7

  # The permissions mask to apply when rotating log files. The default value is 0600.
  # Must be a valid Unix-style file permissions mask expressed in octal notation.
  #permissions: 0600

# Set to true to log messages in json format.
#logging.json: false

ขั้นตอนที่ 2.3 รัน Filebeat Container โดยใช้คำสั่งด้านล่าง

$docker run -d \
  --name=filebeat \
  --user=root \
  --volume="./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro" \
  --volume="/var/lib/docker/containers:/var/lib/docker/containers:ro" \
  --volume="/var/run/docker.sock:/var/run/docker.sock:ro" \
  docker.elastic.co/beats/filebeat:8.4.3 filebeat -e --strict.perms=false

จากนั้นจะได้ Container Service ของ Filebeat

สามารถทดสอบการไหลของข้อมูล Log file ที่ถูกส่งด้วย Filebeat ไปยัง Logstash เพื่อทำการ Parser และจัดเก็บลงฐานข้อมูล Elasticsearch โดยเข้าไปดูข้อมูลใน Elasticsearch ดังที่กล่าวไปก่อนหน้า และทดสอบใช้งาน Kibana โดยเชื่อมต่อกับข้อมูลดังกล่าวเป็นอันเสร็จสิ้น

 สำหรับเพื่อนๆ คนไหนที่สนใจเกี่ยวกับการบริหารจัดการข้อมูล Log file แบบรวมศูนย์สามารถหาอ่านเพิ่มเติมกันได้ที่

หวังว่าบทความนี้จะช่วยให้เพื่อน ๆ ได้รู้จักกับ ELK Stack และเข้าใจกระบวนการทำงานของมันเผื่อมีโอกาสได้ใช้งานในอนาคตกันนะคะ

เนื้อหาโดย ฐิติรัตน์ บุญช่วยชู 
ตรวจทานและปรับปรุงโดย นนทวิทย์  ชีวเรืองโรจน์

Titirat Boonchuaychu

Data Engineer Government Big Data Institute (GBDi)

Nontawit Cheewaruangroj, PhD

Project Manager and Senior Data Scientist at Big Data Institute (Public Organization), BDI