Elastic Stack 구축 가이드입니다. 이 작업은 패키지 네 개를 설치하는 것으로 끝나지 않습니다. 수집 구간의 인증, Elasticsearch의 TLS와 권한, Kibana 공개 범위, 보존 정책, 스냅샷 복구까지 하나의 운영 경계로 설계해야 합니다. 이 글은 Rocky Linux 9와 Elastic Stack 9.4.2를 기준으로 재현 가능한 설치와 검증 절차를 정리합니다.

실습은 단일 호스트에서 시작하지만 운영 환경은 역할과 장애 영역을 분리합니다. 또한 모든 명령은 실행 목적과 확인 방법을 함께 설명합니다. 비밀번호와 API 키를 본문이나 셸 이력에 하드코딩하지 않고, Elastic이 기본으로 활성화하는 보안 설정도 끄지 않습니다.

Elastic Stack 구축 구조: TLS 수집 경로, 3노드 Elasticsearch, Kibana, ILM과 외부 스냅샷
TLS로 보호된 수집·검색·시각화 경로와 3노드 클러스터, 외부 스냅샷 저장소, 데이터 수명 주기를 함께 본 구조

Elastic Stack 구축 범위와 구성 요소

구성 요소 역할 운영 핵심
Elasticsearch 문서 색인·검색·집계와 샤드 관리 3노드 합의, TLS, 용량과 샤드 수
Kibana 검색·대시보드·관리 UI loopback 바인딩, HTTPS 프록시, RBAC
Logstash 파싱·변환·라우팅 back pressure, persistent queue, 비밀정보
Filebeat/Elastic Agent 로그·메트릭 수집 TLS 신뢰, 필드 규칙, 재전송
ILM rollover와 보존 기간 자동화 샤드 크기, 보존 요구, 실행 상태
Snapshot/SLM 클러스터 외부 백업과 복구 저장소 검증, 복구 훈련, 접근 분리
이 글에서 ELK는 역사적인 Elasticsearch·Logstash·Kibana 조합을 뜻하고, Elastic Stack은 Beats와 Agent, 보안·수명 주기·백업까지 포함한 운영 단위를 뜻합니다. 단순 수집이라면 Logstash를 생략하고 Agent 또는 Filebeat가 Elasticsearch로 직접 전송할 수도 있습니다.

Elastic Stack 구축 전 설계와 요구사항

Rocky Linux 9는 RHEL 호환 배포판이지만 상용 지원이 필요하다면 Elastic 지원 매트릭스에서 정확한 OS 조합을 확인해야 합니다. 또한 Elastic Stack 구성 요소는 같은 버전으로 맞춥니다. 이 글의 검증 버전은 2026년 7월 현재 공식 문서의 최신 안정판인 9.4.2입니다.

실습과 운영 토폴로지 구분

항목 단일 노드 실습 운영 권장 방향
Elasticsearch 1대, discovery.type=single-node 최소 3대의 master-eligible 노드와 장애 영역 분산
Kibana 같은 호스트의 127.0.0.1 별도 호스트 또는 다중 인스턴스와 HTTPS 프록시
Logstash 로컬 파이프라인 수집량에 맞춘 별도 노드와 persistent queue
백업 기능 확인용 별도 경로 클러스터 밖의 공유 스토리지나 객체 저장소
노출 포트 loopback만 사용 방화벽 source 제한과 TLS, 관리망 분리

호스트 자원과 시간 동기화 확인

cat /etc/rocky-release
uname -r
timedatectl status
free -h
df -hT /var/lib /var/log
sysctl vm.max_map_count
ulimit -n

Elasticsearch는 JVM heap뿐 아니라 filesystem cache를 적극 사용합니다. 따라서 같은 호스트에 무거운 Logstash 작업을 함께 넣으면 메모리 경합이 생기기 쉽습니다. 기본 자동 heap sizing을 먼저 사용하고, 부하 시험 근거가 있을 때만 Xms와 Xmx를 같은 값으로 조정합니다.

Elastic Stack 구축: 9.4.2 저장소와 패키지 설치

Elastic Stack 구축 패키지는 공식 GPG 키와 9.x RPM 저장소를 사용합니다. 저장소를 기본 비활성화하면 일상적인 dnf update에서 예상하지 못한 메이저 변경이 섞이는 일을 줄일 수 있습니다. 설치 전에는 변경 기록과 호환성을 검토하십시오.

공식 GPG 키와 저장소 등록

sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

sudo tee /etc/yum.repos.d/elastic-9.x.repo >/dev/null <<'EOF'
[elastic-9.x]
name=Elastic repository for 9.x packages
baseurl=https://artifacts.elastic.co/packages/9.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
type=rpm-md
EOF

sudo dnf clean metadata
sudo dnf --disablerepo='*' --enablerepo=elastic-9.x list available   elasticsearch kibana logstash filebeat

동일 버전으로 설치하고 고정 확인

STACK_VERSION='9.4.2'
sudo dnf install --enablerepo=elastic-9.x   "elasticsearch-${STACK_VERSION}"   "kibana-${STACK_VERSION}"   "logstash-${STACK_VERSION}"   "filebeat-${STACK_VERSION}"

rpm -q elasticsearch kibana logstash filebeat
sudo dnf install dnf-plugins-core
sudo dnf versionlock add elasticsearch kibana logstash filebeat
dnf versionlock 플러그인이 없다면 먼저 dnf-plugins-core를 설치하십시오. 버전 고정은 패치를 영구히 미루라는 뜻이 아닙니다. 스냅샷·호환성·롤백을 검증한 유지보수 창에서 명시적으로 고정을 풀고 업그레이드하기 위한 안전장치입니다.

Elastic Stack 구축: Elasticsearch 단일 노드 보안 구성

단일 노드 Elastic Stack 구축 실습에서는 Elasticsearch를 loopback에만 바인딩합니다. Elastic 9.x의 최초 시작은 인증과 HTTP·transport TLS를 자동 구성합니다. 이를 끄는 대신 생성된 CA를 신뢰하도록 각 클라이언트를 설정합니다.

실습용 elasticsearch.yml 최소 설정

sudo cp -a /etc/elasticsearch/elasticsearch.yml   /etc/elasticsearch/elasticsearch.yml.before-lab

sudo tee -a /etc/elasticsearch/elasticsearch.yml >/dev/null <<'EOF'

# Lab-only topology. Remove conflicting duplicate keys before restart.
cluster.name: logs-lab
node.name: es01
network.host: 127.0.0.1
http.host: 127.0.0.1
transport.host: 127.0.0.1
discovery.type: single-node
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
sudo systemctl status elasticsearch --no-pager

설정 파일에 같은 키가 이미 있으면 중복으로 추가하지 말고 기존 값을 수정합니다. 서비스 시작 실패 시 보안 기능을 비활성화하지 말고 journal과 Elasticsearch 로그에서 bootstrap check, 권한, YAML 오류를 먼저 찾습니다.

sudo journalctl -u elasticsearch --since '-10 min' --no-pager
sudo tail -n 100 /var/log/elasticsearch/logs-lab.log
sudo ss -lntp | grep -E ':(9200|9300)\b'

비밀번호 재설정과 CA 기반 HTTPS 검증

sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password   -u elastic -i

read -rsp 'elastic password: ' ELASTIC_PASSWORD
echo
export ELASTIC_PASSWORD

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   https://127.0.0.1:9200/ | jq .

unset ELASTIC_PASSWORD

인증서 검증을 우회하면 연결은 되어도 위조 서버를 구별하지 못합니다. 자동 생성 CA의 fingerprint 또는 CA 파일을 배포하고, 개인 비밀번호 대신 서비스별 최소 권한 계정이나 API 키를 사용하십시오.

Elastic Stack 구축: Kibana 등록과 HTTPS 공개

Kibana는 enrollment token으로 Elasticsearch의 CA와 인증 정보를 받아 등록할 수 있습니다. 브라우저에 직접 5601을 노출하지 않고 127.0.0.1에 바인딩한 뒤, 조직 인증서를 사용하는 reverse proxy에서 HTTPS를 종료하는 구성이 이해하기 쉽습니다.

Kibana enrollment token 생성과 등록

sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token   -s kibana

# 위 명령이 출력한 단기 토큰을 다음 명령에 입력합니다.
sudo /usr/share/kibana/bin/kibana-setup   --enrollment-token '<ONE_TIME_ENROLLMENT_TOKEN>'

sudo tee -a /etc/kibana/kibana.yml >/dev/null <<'EOF'
server.host: "127.0.0.1"
server.port: 5601
server.publicBaseUrl: "https://kibana.example.com"
EOF

sudo systemctl enable --now kibana
sudo journalctl -u kibana --since '-10 min' --no-pager
예시의 도메인, 인증서 경로와 허용 대역은 실제 값으로 바꿔야 합니다. enrollment token은 짧은 시간만 유효하므로 문서나 티켓에 저장하지 말고, 노출되었거나 만료되었다면 새 토큰을 발급하십시오.

Nginx HTTPS reverse proxy 예시

server {
    listen 443 ssl http2;
    server_name kibana.example.com;

    ssl_certificate     /etc/pki/tls/certs/kibana-fullchain.pem;
    ssl_certificate_key /etc/pki/tls/private/kibana.key;

    location / {
        proxy_pass http://127.0.0.1:5601;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
sudo nginx -t
sudo systemctl reload nginx

# 관리망 주소 대역으로 바꾸십시오.
sudo firewall-cmd --permanent --new-zone=kibana-admin || true
sudo firewall-cmd --permanent --zone=kibana-admin   --add-source=10.20.0.0/16
sudo firewall-cmd --permanent --zone=kibana-admin --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --zone=kibana-admin --list-all

Elastic Stack 구축: Logstash와 Filebeat 수집 파이프라인

로컬 예제는 Filebeat가 loopback의 Logstash로 보내고, Logstash가 CA로 Elasticsearch 인증서를 검증하는 흐름입니다. 원격 Beats가 5044로 접속한다면 서버·클라이언트 인증서를 별도로 배포하고 방화벽 source를 제한해야 합니다.

최소 권한 수집 계정 생성

read -rsp 'elastic password: ' ELASTIC_PASSWORD
echo
read -rsp 'logstash_writer password: ' LOGSTASH_WRITER_PASSWORD
echo

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT https://127.0.0.1:9200/_security/role/logstash_writer_role   -H 'Content-Type: application/json' -d @- <<'JSON'
{
  "cluster": ["monitor", "manage_index_templates", "manage_ilm"],
  "indices": [
    {
      "names": ["logs-*"],
      "privileges": ["auto_configure", "create_doc", "write", "manage_ilm"]
    }
  ]
}
JSON

jq -n --arg password "$LOGSTASH_WRITER_PASSWORD"   '{password:$password,roles:["logstash_writer_role"]}' | curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT https://127.0.0.1:9200/_security/user/logstash_writer   -H 'Content-Type: application/json' --data-binary @-

unset LOGSTASH_WRITER_PASSWORD

Logstash 설정 문법과 연결 검증

sudo install -d -o logstash -g logstash -m 0750 /etc/logstash/certs
sudo install -o logstash -g logstash -m 0640   /etc/elasticsearch/certs/http_ca.crt   /etc/logstash/certs/http_ca.crt

sudo /usr/share/logstash/bin/logstash-keystore   --path.settings /etc/logstash create
sudo /usr/share/logstash/bin/logstash-keystore   --path.settings /etc/logstash add ES_PASSWORD

ES_PASSWORD에는 교육용 elastic 계정이 아니라 별도로 만든 최소 권한 수집 계정의 비밀번호를 넣는 것이 원칙입니다. 아래 설정은 값 대신 keystore 변수를 참조하므로 설정 파일에 비밀정보가 남지 않습니다.

sudo tee /etc/logstash/conf.d/beats-to-elasticsearch.conf >/dev/null <<'EOF'
input {
  beats { host => "127.0.0.1" port => 5044 }
}

filter {
  if ![@metadata][pipeline] { mutate { add_tag => ["pipeline-default"] } }
}

output {
  elasticsearch {
    hosts => ["https://127.0.0.1:9200"]
    user => "logstash_writer"
    password => "${ES_PASSWORD}"
    ssl_enabled => true
    ssl_certificate_authorities => ["/etc/logstash/certs/http_ca.crt"]
    ecs_compatibility => "v8"
    data_stream => "true"
    data_stream_type => "logs"
    data_stream_dataset => "app"
    data_stream_namespace => "default"
  }
}
EOF

sudo chown logstash:logstash /etc/logstash/logstash.keystore
sudo chmod 0600 /etc/logstash/logstash.keystore

sudo -u logstash /usr/share/logstash/bin/logstash   --path.settings /etc/logstash --config.test_and_exit
sudo systemctl enable --now logstash

Filebeat filestream 입력과 출력 설정

sudo tee /etc/filebeat/filebeat.yml >/dev/null <<'EOF'
filebeat.inputs:
  - type: filestream
    id: system-messages
    enabled: true
    paths:
      - /var/log/messages

output.elasticsearch:
  enabled: false

output.logstash:
  hosts: ["127.0.0.1:5044"]

logging.level: info
EOF

sudo filebeat test config -e
sudo filebeat test output -e
sudo systemctl enable --now filebeat
sudo journalctl -u filebeat --since '-10 min' --no-pager

Filebeat가 /var/log/messages를 읽을 권한이 있는지 확인하십시오. 무조건 root로 실행하는 대신 패키지의 서비스 계정과 권한 모델을 검토하고, 민감 필드·개인정보·토큰은 수집 전에 drop 또는 redact합니다. 파싱 실패 이벤트는 별도 태그와 대시보드로 관찰해야 합니다.

Elastic Stack 구축 후 데이터 스트림과 ILM

시간 기반 로그를 하나의 인덱스에 계속 쓰면 샤드가 비대해지고 복구 시간이 길어집니다. 데이터 스트림과 ILM을 조합하면 조건에 따라 새 backing index로 rollover하고, 보존 기간이 지난 데이터를 자동 삭제할 수 있습니다. 아래 예시는 50GB 또는 1일을 기준으로 전환합니다.

30일 ILM 정책 생성

read -rsp 'elastic password: ' ELASTIC_PASSWORD
echo

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT https://127.0.0.1:9200/_ilm/policy/logs-30d   -H 'Content-Type: application/json' -d @- <<'JSON'
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {"max_primary_shard_size": "50gb", "max_age": "1d"}
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {"delete": {}}
      }
    }
  }
}
JSON

데이터 스트림용 인덱스 템플릿

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT https://127.0.0.1:9200/_index_template/logs-app   -H 'Content-Type: application/json' -d @- <<'JSON'
{
  "index_patterns": ["logs-app-*"],
  "priority": 500,
  "data_stream": {},
  "template": {
    "settings": {
      "index.lifecycle.name": "logs-30d",
      "number_of_shards": 1,
      "number_of_replicas": 1
    }
  }
}
JSON
단일 노드 실습에서 replicas=1이면 replica shard는 할당될 노드가 없어 cluster health가 yellow입니다. 검증용으로만 replicas=0을 사용하거나, 운영에서는 최소 두 데이터 노드 이상을 구성하십시오. 보존 기간과 rollover 임계치는 실제 일일 수집량과 복구 목표를 기준으로 계산해야 합니다.

ILM 적용 상태 점검

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/_ilm/status?pretty'

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/.ds-logs-app-*/_ilm/explain?pretty'

Elastic Stack 구축: 운영 3노드 클러스터 전환

운영 Elastic Stack 구축에서는 세 master-eligible 노드를 서로 다른 장애 영역에 둡니다. 두 노드만으로는 한 노드 장애 시 과반 합의를 유지할 수 없습니다. 새 노드는 enrollment token으로 가입시키고, 모든 노드가 형성된 뒤 discovery 설정을 정리합니다.

노드 enrollment와 클러스터 확인

# 기존 클러스터 노드에서 실행
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token   -s node

# 새 노드에서 패키지 설치 직후 실행
sudo /usr/share/elasticsearch/bin/elasticsearch-reconfigure-node   --enrollment-token '<ONE_TIME_NODE_TOKEN>'
sudo systemctl enable --now elasticsearch

# 기존 클러스터에서 노드와 역할 확인
curl --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/_cat/nodes?v&h=name,ip,node.role,master,heap.percent,disk.avail'

cluster.initial_master_nodes는 새 클러스터의 첫 선출에만 사용합니다. 클러스터가 형성된 뒤 각 노드에서 제거하고 다시 설정하지 마십시오. 재시작할 때 남겨두면 실수로 별도 클러스터를 부트스트랩할 위험이 있습니다. discovery.seed_hosts에는 재시작 후에도 찾을 수 있는 master-eligible 노드를 둡니다.

Elastic Stack 구축: 스냅샷과 복구 훈련

replica는 노드 장애를 견디기 위한 사본이지 백업이 아닙니다. 실수로 삭제한 문서는 replica에도 즉시 반영됩니다. Elastic Stack 구축의 완료 조건에는 클러스터 밖의 snapshot repository, 자동 SLM 정책, 실제 복구 시험이 포함되어야 합니다.

공유 파일시스템 저장소 등록 예시

# 모든 master/data 노드의 elasticsearch.yml에 같은 경로를 추가합니다.
path.repo: ["/mnt/elastic-backup"]

# rolling restart 후 저장소를 등록합니다.
curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT https://127.0.0.1:9200/_snapshot/prod_backup   -H 'Content-Type: application/json' -d @- <<'JSON'
{
  "type": "fs",
  "settings": {
    "location": "/mnt/elastic-backup",
    "compress": true
  }
}
JSON

저장소 검증과 시험 스냅샷

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X POST 'https://127.0.0.1:9200/_snapshot/prod_backup/_verify?pretty'

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   -X PUT 'https://127.0.0.1:9200/_snapshot/prod_backup/smoke-test?wait_for_completion=true&pretty'

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/_snapshot/prod_backup/smoke-test?pretty'

노드의 /var/lib/elasticsearch를 rsync하거나 filesystem snapshot으로 복사하는 방식은 지원되는 백업이 아닙니다. repository 내용도 Elasticsearch 외부 프로세스가 수정하면 손상될 수 있습니다. 별도 테스트 클러스터로 정기 복구하여 인덱스, feature state와 대시보드를 확인하십시오.

Elastic Stack 구축 검증과 장애 진단

서비스·TLS·클러스터 상태 한 번에 확인

systemctl --no-pager --full status   elasticsearch kibana logstash filebeat

sudo ss -lntp | grep -E ':(9200|9300|5601|5044)\b'

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/_cluster/health?pretty'

curl --fail --silent --show-error   --cacert /etc/elasticsearch/certs/http_ca.crt   -u "elastic:${ELASTIC_PASSWORD}"   'https://127.0.0.1:9200/_cat/shards?v&s=state,index'

unset ELASTIC_PASSWORD

진단 순서

  1. systemd exit code와 최근 journal에서 최초 오류 시각을 찾습니다.
  2. 포트가 의도한 주소에만 listen하는지 확인합니다.
  3. CA·hostname·유효 기간과 클라이언트의 인증서 신뢰를 확인합니다.
  4. cluster health에서 unassigned shard 이유와 disk watermark를 확인합니다.
  5. Logstash config test, persistent queue, rejected event와 Filebeat output test를 확인합니다.
  6. ILM explain과 snapshot verify 결과로 보존·백업 자동화를 확인합니다.
  7. 같은 입력과 설정으로 격리된 환경에서 재현한 뒤 변경 사항을 기록합니다.

자주 하는 실수와 대안

실수 문제 안전한 대안
보안 기능을 false로 변경 인증·권한·TLS 경계 소실 자동 보안 설정 유지와 CA 배포
curl 인증서 검증 생략 서버 인증서 위조 탐지 불가 –cacert 또는 fingerprint 검증
elastic 계정으로 수집 과도한 superuser 권한 서비스별 role·user 또는 API key
9200·5601 전체 공개 관리 API와 UI 직접 노출 loopback·관리망·reverse proxy
무제한 단일 인덱스 샤드 비대화와 긴 복구 데이터 스트림·rollover·ILM
data 디렉터리 복사 일관성 없는 복구 불가 백업 snapshot repository와 복구 시험
2노드 master 구성 장애 시 과반 합의 상실 3개 master-eligible 노드

Elastic Stack 구축: 업그레이드와 운영 체크리스트

  • 구성 요소의 목표 버전을 동일하게 맞추고 release note와 breaking change를 검토합니다.
  • 업그레이드 전에 cluster health, unassigned shard, disk watermark와 snapshot 성공을 확인합니다.
  • 노드를 한 대씩 rolling upgrade하며 각 단계에서 클러스터 합류와 shard 회복을 기다립니다.
  • Kibana saved object, Logstash plugin, Beats module과 사용자 정의 template 호환성을 검증합니다.
  • 인증서 만료, keystore 항목, API key 회전과 최소 권한을 정기적으로 점검합니다.
  • 수집 지연, JVM pressure, rejected request, shard 크기, ILM·SLM 실패를 경보로 연결합니다.

관련 자료

Elastic Stack 구축 정리

Elastic Stack 구축의 핵심은 설치 성공 화면이 아니라 안전하게 수집하고, 예측 가능한 크기로 보존하며, 장애 뒤에 복구할 수 있는 운영 체계입니다. 같은 버전의 공식 패키지에서 시작하고 자동 TLS와 인증을 유지하십시오. 그런 다음 최소 권한, loopback·방화벽 경계, 데이터 스트림과 ILM, 클러스터 외부 스냅샷을 순서대로 검증하면 단일 노드 실습을 운영 가능한 3노드 구조로 확장할 수 있습니다.