테크믈리에의 리뷰 공간

K8S 환경에 ElasticSearch Cluster 구축하기 본문

프로그래밍|소프트웨어/ElasticSearch

K8S 환경에 ElasticSearch Cluster 구축하기

테크믈리에 2024. 10. 25. 16:52

- 서론 - 

이번 글에서는 K8S 환경에서 ElasticSearch Cluster를 구축하기 위한 방법에 관하여 알아볼 것이다.

 

실제로 현재 일하고 있는 스타트업 내의 제한적인 컴퓨팅 리소스 내에서 PostgreSQL 클러스터, Redis 클러스터와 함께 ElasticSearch 클러스터를 올려놓았으며, 이 글은 해당 과정 중 본인이 한 작업을 순서대로 정리한 것이다.

 

K8S 클러스터 내에 노드들이 더 있긴 하지만 이번에 회사에서 작업한 내용 중 간단하게 ElasticSearch Cluster와 연관된 노드들만 정리하자면 아래와 같이 정리할 수 있다.

 

우선, 이 글을 읽을 정도면 ES cluster에 관한 지식이 이미 있겠지만 간단하게 구성 요소를 설명하자면 다음과 같다.

  • Master 노드: 클러스터 상태 관리와 관련된 작업을 수행하며 저장소에는 클러스터 메타데이터, 스냅샷 메타데이터, 클러스터 설정, 노드 및 샤드 정보 등을 담고 있다. 적당한 양의 vCPU와 메모리, 50GB~100GB의 저장소가 권장된다. 고가용성을 위해서 3개를 띄우는 것을 권장한다.
  • Ingest 노드: Data 노드에 데이터가 들어가기 전에 전처리를 하기 위해 있는 노드로 임시 데이터 처리, 파이프라인 및 스크립트 캐싱, 에러 및 실패 로그 등을 담기 위하여 저장소를 활용한다. 많은 양의 vCPU와 메모리, 100GB~200GB의 빠른 속도의 저장소가 권장된다.
  • Data 노드: 실제로 데이터가 저장되는 노드로 매우 많은 양의 메모리와 빠른 속도의 저장소가 권장된다. 메모리는 32GB 이상이 적당하다. 마찬가지로 고가용성을 위해 3개 이상을 띄우는 것을 권장한다.

이러한 요구 사항에 맞출 수 있도록 각 노드의 여유 자원을 계산하여 본인은 Master, Ingest, Data 노드를 여러 노드에 위 그림과 할당하여 두었다. 

 

ElasticSearch의 경우, 클러스터 구성을 할 시 기본적으로 Data 노드들은 자체적으로 고가용성을 위하여 분산 저장을 하도록 되어 있다. 때문에 ES Cluster를 위해서는 Rook CEPH 같은 분산 저장 스토리지를 사용하는 것이 아닌 OpenEBS LocalPV와 같은 저장소를 사용하는 것이 속도, 안전성 면에서 유리하다.


- 사전 준비 - 

우선 K8S 환경에 ES Cluster를 올리고 사용하기 위해서는 다음과 같은 사전 준비가 필요하다.

  1. K8S Storage 준비 (OpenEBS LocalPV 권장)
  2. ElasticSearch Certificate 발급 (각 구성 요소간 성공적 통신 및 외부 접근을 위하여 필수)

이 중 1번의 경우, 이 블로그에도 정리되어 있으니 참고하도록하자.

 

인증서 발급의 경우, 이 글에서는 ElasticSearch에서 제공하는 CertUtil을 활용하여 인증서 발급하는 방법을 사용해볼 것이다.

# Elasticsearch 도커 컨테이너 실행
sudo docker run --name elastic-certs -it -w --rm \
       /tmp docker.elastic.co/elasticsearch/elasticsearch:8.5.1 \
       /bin/sh
       
# 도커 컨테이너 내에서 명령 실행
# --pass의 경우 본인이 원하는 것을 지정하거나 혹은 비밀번호 없이 진행하려면 ''을 입력
# --ca-pass의 경우도 마찬가지
elasticsearch-certutil ca --out /tmp/elastic-stack-ca.p12 --pass ''

elasticsearch-certutil cert --name security-master \
    --dns elasticsearch-master,elasticsearch-master.elasticsearch.svc.cluster.local,es.s-core.ai \
    --ca /tmp/elastic-stack-ca.p12 \
    --pass '' --ca-pass '' --out /tmp/elastic-certificates.p12
    
# Ctrl P+Q로 컨테이너 밖으로 나간 다음 작업
sudo docker cp elastic-certs:/tmp/elastic-certificates.p12 ./
sudo docker stop elastic-certs

# OpenSSL 필요
sudo openssl pkcs12 -nodes -passin pass:'' -in elastic-certificates.p12 -out elastic-certificate.pem
sudo openssl x509 -outform der -in elastic-certificate.pem -out elastic-certificate.crt

- ES Cluster 구축하기 -

우선은 앞서 생성한 Certificate들을 필요한 Namespace들에 등록해주도록 하자. (elasticsearch namespace는 필수)

# ES Cluster를 위한 Namespace 생성
kubectl create namespace elasticsearch

# Namespace만 바꿔가며 아래 작업을 필요한만큼 반복하자
sudo kubectl create secret generic elastic-certificates -n elasticsearch --from-file elastic-certificates.p12
sudo kubectl create secret generic elastic-certificate-pem -n elasticsearch --from-file=elastic-certificate.pem
sudo kubectl create secret generic elastic-certificate-crt -n elasticsearch --from-file=elastic-certificate.crt

 

그 다음에는 Elastic operator를 올려주도록 하자.

helm repo add elastic https://helm.elastic.co
helm repo update

helm install elastic-operator elastic/eck-operator -n elasticsearch --create-namespace

 

이제는 Master와 Ingest, Data를 올리기 위해 본인의 상황에 맞춰서 yaml파일 3개 작성해야한다.

자세한 내용은 공식 문서를 참조하도록 하자.

https://artifacthub.io/packages/helm/elastic/elasticsearch

 

elasticsearch 8.5.1 · elastic/elastic

Official Elastic helm chart for Elasticsearch

artifacthub.io

 

아래는 본인이 사용한 예시이다.

더보기
---
# 마스터 그룹 설정
clusterName: "elasticsearch"
nodeGroup: "master"

# The service that non master groups will try to connect to when joining the cluster
# This should be set to clusterName + "-" + nodeGroup for your master group
masterService: ""

createCert: false

roles:
  - master

replicas: 3
minimumMasterNodes: 1
clusterHealthCheckParams: 'wait_for_status=yellow&timeout=1s'

# Disable it to use your own elastic-credential Secret.
secret:
  enabled: true
  password: "본인이 사용할 비밀번호" # generated randomly if not defined

image: "docker.elastic.co/elasticsearch/elasticsearch"
imageTag: "8.5.1"
imagePullPolicy: "IfNotPresent"

esJavaOpts: "-Des.allow_insecure_settings=true" # example: "-Xmx1g -Xms1g"

# CORS 오류 관련 설정
esConfig:
  elasticsearch.yml: |
    xpack.security.enabled: true
    xpack.security.transport.ssl.enabled: true
    xpack.security.transport.ssl.verification_mode: certificate
    xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.enabled: true
    xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    http.cors.allow-origin: "*" # Only use unrestricted value for local development
    http.cors.enabled: true
    http.cors.allow-credentials: true
    http.cors.allow-methods: GET, POST, DELETE
    http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length

secretMounts:
  - name: elastic-certificates
    secretName: elastic-certificates
    path: /usr/share/elasticsearch/config/certs

# resources:
#   requests:
#     cpu: 
#     memory: 
#   limits:
#     cpu: 
#     memory:

volumeClaimTemplate:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "본인이 사용할 저장소 이름"
  resources:
    requests:
      storage: 50Gi

persistence:
  enabled: true
  labels:
    # Add default labels for the volumeClaimTemplate of the StatefulSet
    enabled: false
  annotations: {}

# This is the node affinity settings as defined in
# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature
# nodeAffinity: 
#   requiredDuringSchedulingIgnoredDuringExecution:
#     nodeSelectorTerms:
#       - matchExpressions:
#         - key: esmaster
#           operator: Exists

service:
  enabled: true
  labels: {}
  labelsHeadless: {}
#   type: LoadBalancer
  # Consider that all endpoints are considered "ready" even if the Pods themselves are not
  # https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#ServiceSpec
  publishNotReadyAddresses: false
#   nodePort: "31300"
#   loadBalancerIP: ""
#   annotations:
#     metallb.universe.tf/allow-shared-ip: "elasticsearch"
  httpPortName: http
  transportPortName: transport
  loadBalancerSourceRanges: []
  externalTrafficPolicy: ""

 

 

더보기
---
# Ingest 그룹 설정
clusterName: "elasticsearch"
nodeGroup: "ingest"

# The service that non master groups will try to connect to when joining the cluster
# This should be set to clusterName + "-" + nodeGroup for your master group
masterService: "elasticsearch-master"

createCert: false

roles:
  - ingest

replicas: 2
minimumMasterNodes: 1
clusterHealthCheckParams: 'wait_for_status=yellow&timeout=1s'

# Disable it to use your own elastic-credential Secret.
secret:
  enabled: true
  password: "본인이 사용할 비밀번호" # generated randomly if not defined

image: "docker.elastic.co/elasticsearch/elasticsearch"
imageTag: "8.5.1"
imagePullPolicy: "IfNotPresent"

esJavaOpts: "-Des.allow_insecure_settings=true" # example: "-Xmx1g -Xms1g"

esConfig:
  elasticsearch.yml: |
    xpack.security.enabled: true
    xpack.security.transport.ssl.enabled: true
    xpack.security.transport.ssl.verification_mode: certificate
    xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.enabled: true
    xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    http.cors.allow-origin: "*" # Only use unrestricted value for local development
    http.cors.enabled: true
    http.cors.allow-credentials: true
    http.cors.allow-methods: GET, POST, DELETE
    http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length

secretMounts:
  - name: elastic-certificates
    secretName: elastic-certificates
    path: /usr/share/elasticsearch/config/certs

# resources:
#   requests:
#     cpu: 
#     memory: 
#   limits:
#     cpu: 
#     memory:

volumeClaimTemplate:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "본인이 사용할 저장소"
  resources:
    requests:
      storage: 100Gi

persistence:
  enabled: true
  labels:
    # Add default labels for the volumeClaimTemplate of the StatefulSet
    enabled: false
  annotations: {}

protocol: https

# This is the node affinity settings as defined in
# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature
# nodeAffinity:
#   requiredDuringSchedulingIgnoredDuringExecution:
#     nodeSelectorTerms:
#       - matchExpressions:
#         - key: esingest
#          operator: Exists

 

더보기
---
# 데이터 그룹 설정
clusterName: "elasticsearch"
nodeGroup: "data"

# The service that non master groups will try to connect to when joining the cluster
# This should be set to clusterName + "-" + nodeGroup for your master group
masterService: "elasticsearch-master"

createCert: false

roles:
  - data

replicas: 3
minimumMasterNodes: 1
clusterHealthCheckParams: 'wait_for_status=yellow&timeout=1s'

# Disable it to use your own elastic-credential Secret.
secret:
  enabled: true
  password: "본인이 사용할 비밀번호" # generated randomly if not defined

esConfig:
  elasticsearch.yml: |
    xpack.security.enabled: true
    xpack.security.transport.ssl.enabled: true
    xpack.security.transport.ssl.verification_mode: certificate
    xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.enabled: true
    xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
    http.cors.allow-origin: "*" # Only use unrestricted value for local development
    http.cors.enabled: true
    http.cors.allow-credentials: true
    http.cors.allow-methods: GET, POST, DELETE
    http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length

secretMounts:
  - name: elastic-certificates
    secretName: elastic-certificates
    path: /usr/share/elasticsearch/config/certs

image: "docker.elastic.co/elasticsearch/elasticsearch"
imageTag: "8.5.1"
imagePullPolicy: "IfNotPresent"

esJavaOpts: "-Des.allow_insecure_settings=true" # example: "-Xmx1g -Xms1g"

# resources:
#   requests:
#     cpu: 
#     memory: 
#   limits:
#     cpu: 
#     memory: 

volumeClaimTemplate:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "본인이 사용할 저장소"
  resources:
    requests:
      storage: 300Gi

persistence:
  enabled: true
  labels:
    # Add default labels for the volumeClaimTemplate of the StatefulSet
    enabled: false
  annotations: {}

protocol: https

# This is the node affinity settings as defined in
# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature
# nodeAffinity:
#   requiredDuringSchedulingIgnoredDuringExecution:
#     nodeSelectorTerms:
#       - matchExpressions:
#         - key: esdata
#           operator: Exists

 

위의 예시 파일들의 경우 원하는 노드에 띄우기 위하여 Node Affinity를 사용했었다.

 

본인만의 파일 작성이 끝났다면 다음 명령어를 순차적으로 실행하여주도록 하자.

helm install esmaster elastic/elasticsearch -f master.yaml -n elasticsearch
helm install esdata elastic/elasticsearch -f data.yaml -n elasticsearch
helm install esingest elastic/elasticsearch -f ingest.yaml -n elasticsearch

 

 

성공적으로 여기까지 진행했다면 위와 같이 모든 Pod들이 정상 실행되있는 것을 1~2분 후에 확인할 수 있을 것이다.

Comments