kubernetes 환경에서 elasticsearch를 설치하는 방법을 정리하였습니다. 비록 테스트 환경이지만 실제 운영환경처럼 구성하기 위해 3대의 마스터 노드와 2대의 데이터 노드로 구성하였습니다(현업에서 직접 사용할 경우 보다 더 여유 있는 구성을 추천드립니다) 3대의 마스터 노드 중 실제로 마스터의 역할을 수행하는 것은 1대이며 나머지 2대의 마스터 노드는 현재 마스터 노드의 문제가 발생할 경우 마스터로 승격할 수 있도록 Standby 형식으로 구성하였습니다.

 

1. Helm chart 다운로드

저는 kubernets 환경에서 elasticsearch를 설치하기 위해 helm을 사용하였습니다. 아래 링크의 helm으로 구성하였습니다.
https://artifacthub.io/packages/helm/elastic/elasticsearch

 

elasticsearch 8.5.1 · elastic/elastic

Official Elastic helm chart for Elasticsearch

artifacthub.io

 

helm 명령어를 통하여 repo를 추가하고 다운로드합니다.

# helm repo 추가
$ helm repo add elastic https://helm.elastic.co

# helm 파일 다운로드
$ helm pull elastic/elasticsearch
$ ls -al
drwxr-xr-x@  6 hsw  staff    192  6 25 09:45 .
drwxr-xr-x@  9 hsw  staff    288  6 25 09:44 ..
-rw-r--r--@  1 hsw  staff  28898  6 25 09:45 elasticsearch-8.5.1.tgz

# helm 압축 출기
$ tar -zxvf elasticsearch-8.5.1.tgz
$ ls -al
drwxr-xr-x@  6 hsw  staff    192  6 25 09:45 .
drwxr-xr-x@  9 hsw  staff    288  6 25 09:44 ..
drwxr-xr-x@ 12 hsw  staff    384  6 25 10:26 elasticsearch
-rw-r--r--@  1 hsw  staff  28898  6 25 09:45 elasticsearch-8.5.1.tgz

 

2. values.yaml 수정

elasticsearch 디렉토리로 이동하여 values 값을 설정합니다. 저의 경우 values.yaml 파일을 직접적으로 수정하는 것보다는 새로운 yaml을 구성하여 values.yaml 파일을 override 하여 사용하는 것을 선호하기 때문에 마스터 노드와 데이터 노드 별로 설정파일을 구성하여 작업하였습니다.

master.yaml 파일 구성

마스터 노드는 3대로 구성되어 있고 클러스터 유지를 위해 CPU는 4 코어, Memory는 16GB로 여유 있게 구성하였습니다. 그리고 Lucene의 메모리 사용을 위해 16GB의 절반인 8GB만 JVM옵션으로 설정을 하였습니다.

tolerations, nodeAffinity의 경우 사용자 환경에 맞게 설정하여 사용해 주세요 사용법은 링크를 달아놨습니다.
tolerations : https://kubernetes.io/ko/docs/concepts/scheduling-eviction/taint-and-toleration/
nodeAffinity : https://kubernetes.io/ko/docs/concepts/scheduling-eviction/assign-pod-node/

##################################
# cluster info
createCert: false
clusterName: "elasticsearch"
nodeGroup: "master"
replicas: 3
imageTag: "7.10.2"

roles:
- master

##################################
# nodeAffinity
tolerations:
- key: "develop/elasticsearch-master"
  operator: "Exists"

nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
    - matchExpressions:
      - key: develop/group
        operator: In
        values:
        - elasticsearch-master

##################################
# cluster config
# 테스트 환경이라 보안 설정을 끔
esConfig:
  elasticsearch.yml: |
    xpack.security.enabled: false

# AWS S3 스냅샷 테스트를 위한 설정
esJvmOptions:
  jvm.options: |
    -Des.allow_insecure_settings=true

##################################
# resources
esJavaOpts: "-Xmx8g -Xms8g"

resources:
  requests:
    cpu: 4
    memory: "16Gi"
  limits:
    cpu: 4
    memory: "16Gi"

volumeClaimTemplate:
  resources:
    requests:
      storage: 10Gi
  storageClassName: openebs

##################################
# service
protocol: http
service:
  type: NodePort
  nodePort: "30090"

data.yaml 파일 구성

데이터 노드의 경우 마스터 노드보다 더 많은 요청을 처리하고 데이터 관리를 수행하기 때문에 마스터 노드보다 더 여유 있는 스펙으로 리소스를 구성하였습니다. 데이터 노드 또한 마스터 노드처럼 사용자의 환경에 맞는 값으로 구성해 주세요

##################################
# cluster info
createCert: false
clusterName: "elasticsearch"
nodeGroup: "data"
replicas: 2
imageTag: "7.10.2"

roles:
- data
- ingest

##################################
# nodeAffinity
tolerations:
- key: "develop/elasticsearch-data"
  operator: "Exists"

nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
  nodeSelectorTerms:
    - matchExpressions:
      - key: develop/group
        operator: In
        values:
        - elasticsearch-data

##################################
# cluster config
# 테스트 환경이라 보안 설정을 끔
esConfig:
  elasticsearch.yml: |
    xpack.security.enabled: false

# AWS S3 스냅샷 테스트를 위한 설정
esJvmOptions:
  jvm.options: |
    -Des.allow_insecure_settings=true

##################################
# resources
esJavaOpts: "-Xmx16g -Xms16g"

resources:
  requests:
    cpu: 8
    memory: "32Gi"
  limits:
    cpu: 8
    memory: "32Gi"

volumeClaimTemplate:
  resources:
    requests:
      storage: 200Gi
  storageClassName: openebs

##################################
# service
# UI 사용을 위해 NodePort로 설정
protocol: http
service:
  type: NodePort
  nodePort: "30091"

Makefile 구성

저는 지속적으로 지웠다 설치했다는 반복 하는데 편리하기 위해 Makefile을 구성하였습니다.

PREFIX := es
TIMEOUT := 1200s
NAMESPACE := elasticsearch

install:
helm upgrade --wait --timeout=$(TIMEOUT) --install -n $(NAMESPACE) --create-namespace --values override-master.yaml $(PREFIX)-master .
helm upgrade --wait --timeout=$(TIMEOUT) --install -n $(NAMESPACE) --create-namespace --values override-data.yaml $(PREFIX)-data .

purge:
helm del $(PREFIX)-master -n $(NAMESPACE)
helm del $(PREFIX)-data -n $(NAMESPACE)

 

3. 설치 진행

Makefile을 이용하여 설치를 진행합니다.

# elasticsearch 설치
$ make install

 

설치 작업이 완료 되면 Elasticsearch는 다음과 같이 구성됩니다.

pod/elasticsearch-data-0             1/1     Running     0       1d
pod/elasticsearch-data-1             1/1     Running     0       1d
pod/elasticsearch-master-0           1/1     Running     0       1d
pod/elasticsearch-master-1           1/1     Running     0       1d
pod/elasticsearch-master-2           1/1     Running     0       1d

service/elasticsearch-data               NodePort    10.233.47.200  <none>   9200:30091/TCP,9300:31095/TCP   1d
service/elasticsearch-data-headless      ClusterIP   None           <none>   9200/TCP,9300/TCP               1d
service/elasticsearch-master             NodePort    10.233.56.28   <none>   9200:30090/TCP,9300:32277/TCP   1d
service/elasticsearch-master-headless    ClusterIP   None           <none>   9200/TCP,9300/TCP               1d

statefulset.apps/elasticsearch-data         2/2     1d
statefulset.apps/elasticsearch-master       3/3     1d

 

4. Elasticsearch 삭제

테스트가 끝나고 elasticsearch를 지우기 위해서는 다음과 같이 진행하면 됩니다.

# Elasticsearch 제거
$ make purge

 

컨테이너를 실행할 때, 여러 옵션을 사용하여 실행 중인 컨테이너에 리소스 제한을 설정할 수 있습니다. 이를 통해 컨테이너가 사용할 수 있는 CPU, 메모리, 디스크 공간 등과 같은 리소스 사용을 세밀하게 제어할 수 있습니다. Docker는 일반적으로 리소스를 자동으로 관리하지만, 경우에 따라 직접 리소스 제한을 설정하는 것이 필요할 수 있습니다. 이를 통해 컨테이너의 성능을 최적화하고, 다른 컨테이너나 호스트 시스템과의 리소스 경쟁을 관리할 수 있습니다. 예를 들어, CPU 제한을 설정하여 컨테이너가 특정 CPU 점유율을 초과하지 못하도록 할 수 있고, 메모리 제한을 설정하여 컨테이너가 지정된 메모리 양을 초과하지 않도록 할 수 있습니다. 이러한 리소스 제한 옵션을 사용하면 컨테이너의 안정성과 성능을 개선할 수 있습니다.

 

현재 동작하는 컨테이너의 resource 사용량 확인하기

docker stats 명령어를 사용하면 실행 중인 모든 컨테이너의 실시간 리소스 사용량을 확인할 수 있습니다.

docker stats 컨테이너아이디1, 컨테이너아이디2 ...

 

컨테이너 resource 사용 제한하기

--cpus 옵션을 사용하여 컨테이너에 할당할 CPU 코어의 개수를 지정할 수 있습니다.

 docker run -d --cpus=1 --name test-cpus nginx:latest

 

--cpu-shares 옵션은 Docker에서 CPU 리소스를 컨테이너 간에 분배하는 데 사용되는 상대적인 가중치를 설정하는 데 사용됩니다. 아래 설정은 기본값인 1024보다 낮은 가중치를 의미하므로, 다른 컨테이너에 비해 상대적으로 적은 CPU 리소스를 할당받을 수 있습니다.

docker run -d --cpu-shares=512 --name test-cpu-share nginx:latest

 

--memory 옵션을 사용하여 컨테이너에 할당할 메모리 양을 지정할 수 있습니다.

docker run -d --memory=1g --name test-memory nginx:latest

 

위에서 설정한 리소스 사용량 확인

 

기존 컨테이너 리소스 제한하기

앞에서는 컨테이너를 실행할 때 리소스를 제한하는 법에 대해서 알아봤지만 docker update를 이용하여 기존에 동작하고 있던 컨테이너에서도 리소스를 제한할 수 있습니다.

# 테스트용 컨테이너 생성
$ docker run -d --name test-nginx nginx:latest

# 테스트용 컨테이너 상태 확인
$ docker stats --no-stream test-nginx

 

docker update 명령어를 통해 리소스 제한 업데이트

# 리소스 설정 변경
$ docker update --memory-swap 1g --memory 1g test-nginx

# 테스트용 컨테이너 상태 확인
$ docker stats --no-stream test-nginx

'Docker' 카테고리의 다른 글

Docker Volume & BindMount  (0) 2023.07.25
컨테이너 HEALTHCHECK  (0) 2023.07.23

도커 컨테이너 파일 시스템의 생명 주기는 컨테이너의 생명 주기와 같습니다. 그렇기 때문에 컨테이너가 지워지면 컨테이너에서 기록한 데이터도 같이 지워지는데요. 이런 경우를 방지하기 위해 컨테이너의 데이터를 유지하고 관리할 수 있어야 합니다. 이런 기능을 제공하는 것이 대표적으로 2가지가 있는데 바로 volume과 bind mount입니다. 이번에는 volume과 bind mount에 대해서 정리하였습니다.

 

Docker volume

volume 생성하기

volume 명령어를 사용하여 직접적으로 volume을 생성하게 되면 /var/lib/docker/volumes/볼륨명으로 해당 볼륨의 디렉토리가 생성됩니다. 컨테이너는 해당 디렉토리를 컨테이너 경로로 마운트 하여 데이터를 읽고 쓰게 되면서 컨테이너가 기록하는 데이터를 유지할 수 있습니다.

$ docker volume create 볼륨명

volume 확인하기

생성된 volume을 `docker volume ls` 를 통하여 확인할 수 있습니다.

$ docker volume ls

volume 컨테이너에 마운트 하기

생성한 볼륨을 컨테이너에 마운트 합니다.

$ docker run 이미지명 --name 컨테이너명 -d -v <볼륨명>:<컨테이너 경로>

volume 삭제하기

`docker volume rm 볼륨명`을 통하여 볼륨을 삭제할 수 있는데 만약 해당 볼륨을 컨테이너가 사용 중이라면 해당 컨테이너부터 삭제하고 볼륨을 삭제해야 합니다.

$ docker volume rm 볼륨명

사용하지 않은 volume 전부 삭제하기

볼륨을 무작위로 생성하다 보면 사용하지 않는 볼륨들이 많이 존재하게 되는데 다음과 같은 명령어를 통하여 사용하지 않는 볼륨을 한 번에 삭제할 수 있습니다.

$ docker volume prune --all --force

dockerfile로 볼륨 생성하기

도커파일에서 생성한 볼륨은 호스트 경로의 /var/lib/docker/volumes/볼륨해쉬에 만들어집니다. Dockerfile로 생성한 volume은 매번 컨테이너를 생성할 때마다 무작위로 생성되기 때문에 사용할 때 주의해야 하며 나중에 사용 안 하는 볼륨을 지워줘야 한다.

FROM ubuntu:latest

# 볼륨을 마운트할 디렉토리 생성
RUN mkdir /app

# 볼륨 마운트 지정
VOLUME /app

# 컨테이너 내부의 파일 생성
RUN echo "Hello, Docker Volume!" > /app/data.txt

--volume-from 옵션 사용하기

Docker 컨테이너에게 다른 컨테이너에서 마운트 된 볼륨을 공유하도록 지시하는 명령어입니다. 이를 통해 볼륨을 한 컨테이너에서 생성하고 다른 컨테이너에서 사용할 수 있습니다.

$ docker volume create my-volume
$ docker run -d --name test-1 ubuntu:20.04 -v my-volume:/data
$ docker run -d --name test-2 ubuntu:20.04 --volume-from test-1

위 명령어를 이용하면 test-1에서 사용 중인 my-volume을 test-2에서도 사용할 수 있습니다.

 

Bind Mount

바인드 마운트는 호스트 시스템의 특정 경로를 컨테이너 내부 경로에 직접 연결을 하기 때문에 호스트와 컨테이너 간에 파일이 실시간으로 동기화할 수 있습니다. 저 같은 경우 개발을 한 애플리케이션의 로그를 확인하기 쉽게 하기 위해 로그 파일의 경로를 바인트 마운트 경로로 잡아서 사용하는 경우가 많습니다.

$ docker run -v 호스트경로:컨테이너경로 이미지:태그

또한 --mount 옵션을 이용하여 Bind Mount를 수행할 수 있는데 --mount를 이용하면 보다 섬세한 설정을 할 수 있습니다.

아래를 --mount 옵션을 이용하여 readonly 설정을 통해 컨테이너는 해당 파일 시스템에 readonly 권한으로 밖에 접근할 수 없도록 설정한 예시입니다.

$ docker run --mount type=bind,source=~/data,target=/data,readonly -d 이미지:태그

그밖에 옵션을 통해 여러가지 형식으로 마운트를 할 수 있습니다.

$ docker run --mount type=bind,source=호스트경로,target=컨테이너경로,옵션=값,옵션=값,...

 

'Docker' 카테고리의 다른 글

Docker resource 설정  (0) 2023.07.27
컨테이너 HEALTHCHECK  (0) 2023.07.23

Container 상태는 Run 상태인데 실제 내부적인 문제로 인해 Container가 정상적으로 동작하지 않는 경우가 있다. 이런 경우 Dockerfile을 이용하여 컨테이너 이미지를 빌드할 때 HEALTHCHECK 인스트럭션을 사용하여 컨테이너의 상태를 확인할 수 있습니다. 

 

HEALTHCHECK 작성 형식

HEALTHCHECK [option] CMD 명령어
  • --interval : 헬스 체크 간격을 지정합니다. default 30s
  • --timeout : 명령어의 타임아웃을 설정합니다. default 30s
  • --start-period : 컨테이너가 실행되고 헬스 체크를 시작하기까지 대기하는 시간을 지정합니다. default 0s
  • --retries : 헬스 체크 명령어 실행 실패 시 재시도 횟수를 지정합니다. default 3

 

HEALTHCHECK 예시

FROM nginx:latest
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://localhost/ || exit 1

curl -f http://localhost/ 명령어를 통하여 10초 간격으로 3초 이내의 결과가 나타나지 않고 3번 이상 실패 시 unhealthy 하다고 판단합니다. 상태를 확인하는 방법은 "docker ps -a"를 통해 해당 컨테이너의 상태를 확인할 수 있지만 "docker inspect 컨테이너아이디"를 통하여 보다 자세한 상태를 확인할 수 있습니다. 아래는 docker inspect 명령어를 통해 확인한 결과입니다.

(생략...)
	"State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 32813,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2023-07-06T12:18:15.929466969Z",
            "FinishedAt": "0001-01-01T00:00:00Z",
            "Health": {
                "Status": "healthy",
                "FailingStreak": 0,
                "Log": [
                    {
                        "Start": "2023-07-06T12:18:45.930156552Z",
                        "End": "2023-07-06T12:18:46.006446885Z",
                        "ExitCode": 0,
                        "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r100   615  100   615    0     0   637k      0 --:--:-- --:--:-- --:--:--  600k\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\u003ctitle\u003eWelcome to nginx!\u003c/title\u003e\n\u003cstyle\u003e\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\n\u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003ch1\u003eWelcome to nginx!\u003c/h1\u003e\n\u003cp\u003eIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.\u003c/p\u003e\n\n\u003cp\u003eFor online documentation and support please refer to\n\u003ca href=\"http://nginx.org/\"\u003enginx.org\u003c/a\u003e.\u003cbr/\u003e\nCommercial support is available at\n\u003ca href=\"http://nginx.com/\"\u003enginx.com\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003e\u003cem\u003eThank you for using nginx.\u003c/em\u003e\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n"
                    }
                ]
            }
        },
(생략...)
	"Healthcheck": {
                "Test": [
                    "CMD-SHELL",
                    "curl -f http://localhost/ || exit 1"
                ],
                "Interval": 30000000000,
                "Timeout": 3000000000
            },
(생략...)

 

HEALTHCHECK의 상태 값

healthcheck의 상태 값은 3가지 종류로 나뉩니다.

  • starting : 컨테이너가 시작되고 --start-period로 정의된 대기 시간이 경과하기 전의 초기 상태입니다. 이 시간 동안 HEALTHCHECK가 수행되지 않으며, 컨테이너는 자동으로 healthy 상태로 표시됩니다.
  • healthy : HEALTHCHECK 명령이 성공적으로 실행되고 정상적인 상태를 반환한 경우입니다. 컨테이너가 정상적으로 작동하고 있다는 의미입니다.
  • unhealthy : HEALTHCHECK 명령이 실패하거나 비정상적인 상태를 반환한 경우입니다. 이는 컨테이너가 문제가 있거나 제대로 작동하지 않고 있다는 의미입니다.

healthcheck를 통하여 컨테이너의 상태를 표시할 뿐 도커는 컨테이너가 unhealthy 상태여도 컨테이너를 자동으로 재시작하는 기능을 수행하지 않습니다. 그 이유는 컨테이너를 재시작할 경우 애플레케이션이 일시적으로 드랍되면서 서비스에 문제가 발생할 수 있기 때문입니다. 도커의 입장에서 애플리케이션의 드랍으로 더 큰 문제가 발생될 수 있기 때문에 사용자에게 상태만 보고할 뿐 재시작을 수행하지 않습니다.

'Docker' 카테고리의 다른 글

Docker resource 설정  (0) 2023.07.27
Docker Volume & BindMount  (0) 2023.07.25

이 문서는 2023년 7월에 작성되었으며 Opensearch 2.8.0 버전입니다.

Opensearch의 Searchable Snapshot은 클러스터에서 삭제된 Index를 Snapshot으로 복원하여 데이터를 검색할 수 있도록 하는 기능을 수행합니다. Searchable Snapshot 기능을 사용하기 위해서는 Snapshot에 대한 사전 지식이 필요합니다. Snapshot 글을 먼저 참고하시면 진행하는데 많은 도움이 됩니다.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

클러스터 노드 구성

Opensearch에서 Searchable Snapshot 기능을 사용하기 위해서는 "search" role을 가진 노드가 필요합니다. 저는 한정된 자원으로 Master Node 1대, Data Node 2대, Search Node 1대로 클러스터를 구성하여 테스트하였습니다. (웬만하면 Master Role을 가진 노드를 3대 이상의 홀수 개수만큼 유지하는 것을 추천합니다.)

  Master Node Data Node Search Node
개수 1 2 1
Role master data
ingest
saerch

Master Node는 Opensearch의 클러스터를 관리하는 역할을 수행하며 Data Node는 실제 데이터가 저장되고 연산 기능을 수행합니다. Search Node는 Snapshot으로 부터 인덱스 데이터를 가져와 데이터를 검색할 수 있는 기능을 제공합니다.

Snapshot으로부터 데이터를 가져오는데 인덱스의 크기가 크면 매우 오래 걸릴 수 있으며 데이터를 가져오는 과정에서 네트워크 비용이 발생할 수 있습니다.

 

Snapshot 생성 시 설정

일반적인 스냅샷과 다르게 Searchable Snapshot 기능을 사용하기 위해서는 Snapshot 생성 시 "storage_type": "remote_snapshot"의 설정이 필요합니다.

PUT _snapshot/스냅샷레포지토리/스냅샷명?wait_for_completion=true
{
  "indices": "*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "partial": false,
  "storage_type": "remote_snapshot"
}

storage_type에 대한 설정은 Opensearch 공식 Document에서는 다음과 같이 설명하였습니다.

 

local indicates that all snapshot metadata and index data will be downloaded to local storage.
remote_snapshot indicates that snapshot metadata will be downloaded to the cluster, but the remote repository will remain the authoritative store of the index data. Data will be downloaded and cached as necessary to service queries. At least one node in the cluster must be configured with the search role in order to restore a snapshot using the type remote_snapshot.
Defaults to local.

 

Searchable Snapshot API

평상시에는 인덱스가 존재하지 않고 필요시 Snapshot에서 복원하여 검색할 수 있도록 합니다.

POST _snapshot/스냅샷레포지토리/스냅샷명/_restore
{
  "indices": "복원인덱스",
  "storage_type": "remote_snapshot",  
  "rename_pattern": "(.+)",
  "rename_replacement": "searchable_$1",
  "index_settings": {
      "index.number_of_replicas": 0
  }
}
  • indices : 스냅샷에 포함된 복원할 인덱스
  • storage_type : search node로 복원하기 위한 설정
  • rename_pattern : searchable snapshot으로 복원한 인덱스의 새로운 이름 규칙
  • rename_replacement : 복원한 인덱스의 이름을 재지정합니다.
  • index.number_of_replicas : 복원한 인덱스의 Replica shard 개수를 설정합니다.

인덱스를 복원하면 "saerchable_원래인덱스명" 형식으로 인덱스가 복원됩니다.

 

복원 결과 샤드 구성

일반적인 인덱스의 Shard 개수는 4개로 설정하였고 Replica Shard는 1개씩 구성하도록 설정하였습니다.

Searchable Snapshot으로 복원한 인덱스는 데이터를 넣을 수 없고 검색 기능만 가능하기 때문에 Replica shard가 불필요하다고 판단하여 저는 복원한 인덱스에는 Replica shard를 구성하지 않았습니다. Searchable Snapshot으로 복원하면 Search Node에만 샤드가 구성되며 복원 완료 시 검색이 가능합니다.

 

 

공식 Opensearch Document

https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/

 

Take and restore snapshots

Take and restore snapshots

opensearch.org

Opensearch의 Snapshot은 Opensearch Cluster의 데이터를 안정적으로 백업하고 복원하는 기능을 제공합니다. Snapshot은 인덱스 데이터, 설정, 매핑 및 기타 Opensearch 클러스터 상태 정보를 모두 포함할 수 있어 Cluster 운영에 안정성을 높일 수 있습니다.

 

Dockerfile로 이미지 생성

Opensearch에서는 AWS S3에 Snapshot 기능을 기본적으로 제공하고 있지 않습니다. 그렇기 때문에 Snapshot의 기능을 사용하기 전에 먼저 AWS S3 Plugin을 먼저 설치해야 합니다. Kubernets에서는 매번 재시작할 때마다 플러그인을 설치할 수 없으니 Dockerfile로 AWS S3 Plugin이 설치된 이미지를 생성하여 사용해야 합니다. 한글로 입력된 곳에 옳바른 값을 넣어주세요.

FROM opensearchproject/opensearch:2.8.0

ENV AWS_ACCESS_KEY_ID="에세스키"
ENV AWS_SECRET_ACCESS_KEY="시크릿키"

RUN /usr/share/opensearch/bin/opensearch-plugin install --batch repository-s3
RUN /usr/share/opensearch/bin/opensearch-keystore create

RUN echo $AWS_ACCESS_KEY_ID | /usr/share/opensearch/bin/opensearch-keystore add --stdin s3.client.default.access_key
RUN echo $AWS_SECRET_ACCESS_KEY | /usr/share/opensearch/bin/opensearch-keystore add --stdin s3.client.default.secret_key

 

저의 경우 Kubernetes환경에서 Opensearch를 사용하기 위해 Dockerfile을 이용하여 이미지를 만들어서 사용하였지만 만약 호스트 환경의 Opensearch인 경우 굳이 이미지를 만들지 않고 "bin/opensearch-plugin install --batch repository-s3" 명령어로 플러그인 설치를 하고 Opensearch를 재시작하시면 됩니다.

 

Snapshot Repository 생성

Snapshot을 생성하기 전에 먼저 Snapshot을 저장할 수 있는 Repository를 생성해야 합니다. 저는 my_repository라는 이름의 Snapshot Repository를 생성하였습니다. 한글로 입력된 곳에 옳바른 값을 넣어주세요.

PUT _snapshot/my_repository
{
  "type": "s3",
  "settings": {
    "bucket": "버킷",
    "base_path": "저장경로",
    "compress": true
  }
}

 

Snapshot Repository 확인

# Snapshot Repository의 정보
GET _snapshot/my_repository

# Snapshot Repository에 저장된 스냅샷 리스트 확인
GET _snapshot/my_repository/_all

 

Snapshot 백업

Snapshot Repository를 정상적으로 생성하였다면 해당 Repository를 이용하여 Snapshot을 생성할 수 있습니다. 저는 my_snapshot_1이라는 이름으로 스냅샷을 생성하였고 인덱스의 데이터만 스냅샷을 생성하도록 설정하였습니다. 데이터의 양이 많다면 스냅샷을 생성하는데 오랜 시간이 걸립니다. wait_for_completion=true 옵션을 설정하지 않으면 스냅샷이 생성이 완료되었는지 확인할 수 없습니다. 그래서 wait_for_completion=true 옵션을 통하여 스냅샷이 완전히 생성될 때까지 대기하여 정상적으로 생성되었는지 인지할 수 있습니다.

PUT _snapshot/my_repository/my_snapshot_1?wait_for_completion=true
{
  "indices": "*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "partial": false
}
  • indices : 스냅샷 생성할 인덱스 리스트를 입력합니다. default *
  • ignore_unavailable : 유효하지 않는 것은 무시하는지 유무를 정합니다. default false 
  • include_global_state : global 설정 값은 저장하지 않는지 유무를 정합니다. default true
  • partial : 부분 스냅샷 저장을 허용할지 선택합니다. default는 false로 false일 경우 하나의 샤드가 문제가 발생하면 스냅샷이 실패합니다.

 

Snapshot 정보 확인

Snapshot을 생성하였다면 정확하게 생성되었는지 확인합니다. Response 마지막 shards를 보면 total로 총 스냅샷을 찍은 샤드의 개수가 나오고 successful로 스냅샷 생성에 성공한 샤드의 개수가 나옵니다. 그리고 failed를 통해 스냅샷 생성에 실패한 샤드의 개수를 확인할 수 있습니다. 종종 네트워크의 문제로 인해 노드끼리의 통신 문제로 실패한 샤드가 발생할 수도 있습니다.

GET _snapshot/my_repository/my_snapshot_1

## 결과
{
    "snapshots": [
        {
            "snapshot": "my_snapshot_1",
            "uuid": "aMA2T3ZoRliO0my6557-uw",
            "version_id": 136297827,
            "version": "2.8.0",
            "indices": [
                ".opendistro-job-scheduler-lock",
                ".opendistro-ism-config",
                ".opensearch-observability",
                "my-log-2023-07-10"
            ],
            "data_streams": [],
            "include_global_state": false,
            "state": "SUCCESS",
            "start_time": "2023-07-11T12:26:32.958Z",
            "start_time_in_millis": 1689078392958,
            "end_time": "2023-07-11T12:26:35.160Z",
            "end_time_in_millis": 1689078395160,
            "duration_in_millis": 2202,
            "failures": [],
            "shards": {
                "total": 7,
                "failed": 0,
                "successful": 7
            }
        }
    ]
}

 

Snapshot으로 복구

Snapshot으로 인덱스를 복구할 때 중요한 것은 같은 이름의 인덱스가 Cluster이 이미 존재해서는 안된다는 것입니다. 만약 같은 이름의 인덱스가 존재할 경우 인덱스를 삭제하거나 인덱스를 덤프를 생성하고 인덱스를 삭제하도록 합니다. 

# 먼저 실습할 인덱스를 삭제합니다. 예시로 my-log-2023-07-10 인덱스를 삭제합니다.
DELETE my-log-2023-07-10/

# 위에서 삭제한 인덱스를 스냅샷을 통해 복구합니다. 예시로 my-log-2023-07-10 인덱스를 복구합니다.
POST _snapshot/my_repository/my_snapshot_1/_restore
{
  "indices": "my-log-*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "include_aliases": true,
  "partial": false
}

예시로 "my-log-2023-07-10" 인덱스를 삭제하고 위에서 생성한 스냅샷을 이용하여 "my-log-"로 시작하는 인덱스를 복구하였습니다.

 

마지막으로 Snapshot 및 Repository 삭제

이제 더 이상 사용하지 않는 오래된 Snapshot 및 Repository을 삭제합니다.

# 스냅샷을 삭제합니다.
DELETE _snapshot/my_repository/my_snapshot_1

# 스냅샷 Repository를 삭제합니다.
DELETE _snapshot/my_repository

 

Snapshot에 대해 더 자세한 내용은 Opensearch Document를 확인하여 주세요

https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/index/

 

Snapshots

Snapshots

opensearch.org

 

Opensearch SM(Snapshot Management)이란 Snapshot을 생성하고 일정 기간 유지시켜 주며 일정 기간이 지날 경우 자동으로 Snapshot을 삭제하여 Snapshot Repository를 관리해 주는 역할을 수행합니다. Opensearch SM을 통하여 매일 스냅샷을 생성하여 안정적인 클러스터 운영이 가능해집니다.

먼저 시작하기 전에 스냅샷을 생성하기 위해 Snapshot Repository가 생성되어 있어야 합니다. Snapshot Repository 생성에 대해서는 이 전 글을 확인하여 주세요.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

SM Policy 생성하기

SM Policy 생성을 통하여 매일 자동적으로 스냅샷이 생성되고 생성된지 오래된 스냅샷을 자동적으로 삭제할 수 있습니다. 

POST _plugins/_sm/policies/my-policy
{
  "description": "Daily opensearch snapshot",
  "creation": {
    "schedule": {
      "cron": {
        "expression": "0 1 * * *",
        "timezone": "UTC"
      }
    },
    "time_limit": "1h"
  },
  "deletion": {
    "schedule": {
      "cron": {
        "expression": "0 1 * * *",
        "timezone": "UTC"
      }
    },
    "condition": {
      "max_age": "7d",
      "max_count": 14,
      "min_count": 7
    },
    "time_limit": "1h"
  },
  "snapshot_config": {
    "date_format": "yyyy-MM-dd-HH:mm",
    "timezone": "UTC",
    "indices": "*",
    "repository": "my_repository",
    "ignore_unavailable": "true",
    "include_global_state": "false",
    "partial": "false",
    "storage_type": "remote_snapshot"
  }
}

 

creation에서는 스냅샷을 생성하는 정책을 정의합니다.

  • schedule.cron.expression : 매번 반복적으로 스냅샷을 생성할 시간을 설정합니다.
  • schedule.cron.timezone : 반복적으로 작업될 타임존을 설정합니다.
  • time_limit : 스냅샷을 생성하는데 최대 시간을 설정합니다. 스냅샷을 생성할 때 지정한 시간이 넘어가면 실패로 처리됩니다.

deletion에서는 스냅샷을 삭제하는 정책을 정의합니다.

  • schedule.cron.expression : 매번 반복적으로 스냅샷을 삭제할 시간을 설정합니다.
  • schedule.cron.timezone : 반복적으로 작업될 타임존을 설정합니다.
  • time_limit : 스냅샷을 삭제하는데 최대 시간을 설정합니다. 스냅샷을 삭제할 때 지정한 시간이 넘어 가면 실패로 처리됩니다.
  • condition.max_age : 생성된 스냅샷이 보존되는 시간을 설정합니다.
  • condition.max_count : 최대로 가지고 있을 수 있는 스냅샷의 개수를 설정합니다.
  • condition.min_count : 최소로 유지할 스냅샷의 개수를 설정합니다.

snapshot_config에서는 스냅샷을 생성할 때 설정 값을 정의합니다.

  • date_format : 생성된 스냅샷의 이름 format을 설정합니다. ("폴리스명-yyyy-MM-dd-HH:mm-해쉬값" 형식으로 스냅샷이 생성됩니다.)
  • timezone : date_format에서 사용되는 타임존을 설정합니다.
  • indices : 스냅샷을 생성할 인덱스 리스트를 설정합니다.
  • repository : 스냅샷을 저장할 Repository를 설정합니다.
  • ignore_unavailable : 유효하지 않은 값을 무시하는지 설정합니다.
  • include_global_state : Cluster 상태 값을 포함할 것인지 설정합니다.
  • partial : 부분 스냅샷 저장을 허용할지 선택합니다. default는 false로 false일 경우 하나의 샤드가 문제가 발생하면 스냅샷이 실패합니다.
  • storage_type : searchable snapshot을 적용할 경우 remote_snapshot를 설정하고 아닐 경우 local로 설정합니다.

 

SM Policy 확인하기

SM Policy를 정상적으로 생성되었는지 확인합니다.

GET _plugins/_sm/policies/my-policy/_explain

 

 

SM Policy Start & Stop

자동적으로 스냅샷을 관리하는 기능을 잠시 중단하는 기능을 제공하고 있습니다.

# SM Policy 중지
POST _plugins/_sm/policies/my-policy/_stop

# SM Policy 시작
POST _plugins/_sm/policies/my-policy/_start

 

SM Policy 삭제

더 이상 사용되지 않는 SM Policy가 있다면 삭제합니다.

DELETE _plugins/_sm/policies/my-policy

 

 

스냅샷을 통한 데이터 복구는 이전 글을 확인하여 주세요.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

본 문서는 2023년 7월에 작성된 문서입니다.

Artifact Hub는 Kubernetes 커뮤니티의 중앙 리소스 저장소로서, 다양한 애플리케이션 패키지, Helm 차트, OPA 정책, Falco 규칙 등을 검색, 탐색 및 공유할 수 있는 오픈 소스 플랫폼입니다. 즉 Artifact Hub는 사용자와 Helm chart repository를 중개하는 역할을 수행하고 있습니다. 오늘은 Helm package를 Artifact Hub에 등록하여 사용하는 방법에 대하여 정리하였습니다.

 

시작하기 전에

Helm chart repository는 github으로 구성하고 Artifact Hub를 이용하여 Helm chart repository와 중개하도록 구성하였습니다.

 

1. github repo 생성

자신의 github에 helm package를 등록할 수 있도록 github repository를 생성하여 줍니다. 저는 repository 명을 test-nginx라고 명시하였습니다.

 

2. 실습 디렉토리 구성

helm-nginx 디렉토리는 실제 helm chart를 작성하였고 test-nginx는 방금 생성한 github repo를 clone 한 디렉토리입니다. helm-nginx 디렉토리에서 작성된 helm chart를 test-nginx에 package 하여 github에 push를 할 예정입니다.

 

3. Helm chart 작성

helm-nginx 디렉토리로 이동하여 helm chart를 간단하게 구성도록합니다. 이번 실습에서는 불필요한 부분은 제거하고 values.yaml파일과 templates/deployment.yaml만 작업하였습니다.

values.yaml

위에 사진과 내용이 동일합니다. (실습 복붙용)

name: test-nginx

replicas: 1

imageTag: latest

resources:
  requests:
    memory: "64Mi"
    cpu: "250m"
  limits:
    memory: "128Mi"
    cpu: "500m"

deployment.yaml

위에 사진과 내용이 동일합니다. (실습 복붙용)

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: {{ .Values.name }}
  name: {{ .Values.name }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Values.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.name }}
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        resources:
{{ toYaml .Values.resources | indent 10 }}

정상적으로 작성되었는지 확인

--dry-run 옵션을 통해 실제로 생성하지 않고 helm이 잘 작성되었는지 확인할 수 있습니다.

$ helm install test --dry-run .
# 결과
NAME: test
LAST DEPLOYED: Sat Jul  1 13:51:49 2023
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: helm-nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test-nginx
  name: test-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-nginx
  template:
    metadata:
      labels:
        app: test-nginx
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        resources:
          limits:
            cpu: 500m
            memory: 128Mi
          requests:
            cpu: 250m
            memory: 64Mi

 

4. helm package 생성

상위 디렉토리인 helm 디렉토리로 이동하여 helm package 명령을 수행하여 package를 생성합니다.

# helm package 생성
$ helm package ./helm-nginx/ --destination ./test-nginx

# helm index 생성
$ helm repo index ./test-nginx

# 결과 확인
$ ls -al ./test-nginx/
total 24
drwxr-xr-x 3 hsw hsw 4096 Jul  1 14:52 .
drwxr-xr-x 4 hsw hsw 4096 Jul  1 14:27 ..
drwxr-xr-x 8 hsw hsw 4096 Jul  1 14:53 .git
-rw-r--r-- 1 hsw hsw    6 Jul  1 14:27 README.md
-rw-r--r-- 1 hsw hsw  799 Jul  1 14:27 helm-nginx-0.1.0.tgz
-rw-r--r-- 1 hsw hsw  397 Jul  1 14:52 index.yaml

 

5. github push

$ git add .
$ git commit -m "create helm package"
$ git push

 

6. github pages 설정

Settings - Pages로 이동하여 다음과 같이 설정합니다. 3번까지 수행한 후 해당 페이지를 새로고침 하면 4번처럼 주소가 나옵니다. 해당 주소를 복사하고 있다가 Artifact Hub 설정에서 사용합니다.

 

7. Artifact Hub에 등록하기

Artifact Hub에 접속하여 로그인합니다.

https://artifacthub.io/

 

Artifact Hub

Find, install and publish Kubernetes packages

artifacthub.io

 

우측 상단 User 아이콘 클릭 후 Control Panel을 선택하여 줍니다.

 

ADD REPOSITORY를 선택하게 되면 다음과 같이 화면이 나오는데 여기서 해당된 값을 입력합니다.

  • Kind : Helm charts
  • Name : 등록될 Repository 이름
  • Display name : 사용자에게 보이는 이름
  • Url : 등록할 github 주소

ADD 버튼을 클릭하여 추가하게 되면 다음과 같이 Repo가 생성됩니다.

Repo 설정이 완료되었다면 일정 시간 간격(약 30분)으로 Repo 스캔을 하게 됩니다. 스캔이 완료되면 해당 Repo를 통하여 helm을 이용해 배포할 수 있습니다.

 

'Kubernetes' 카테고리의 다른 글

kubernetes max pods 개수 수정  (0) 2024.07.31
[Helm] Helm 기본 명령어  (0) 2023.07.15
2023 CKA(Certified Kubernetes Administrator) 취득 후기  (0) 2023.07.01

본 문서는 2023년 7월에 작성된 문서입니다.

Helm은 Kubernetes 애플리케이션을 배포, 관리 및 업데이트하기 위한 패키지 관리 도구입니다. Helm은 Kubernetes 클러스터에서 애플리케이션을 쉽게 설치하고 관리하기 위한 기능을 제공합니다. Helm은 "차트"라고 하는 패키지 형식을 사용하여 Kubernetes 애플리케이션을 정의합니다. 차트는 Kubernetes 리소스 (파드, 서비스, 볼륨 등)를 정의하는 템플릿 파일과 해당 애플리케이션을 배포하고 구성하는 데 필요한 매개변수 및 값들을 포함합니다. 이러한 차트를 사용하여 애플리케이션을 쉽게 배포하고 업데이트할 수 있습니다.

helm 설치

아래 링크를 통하여 helm 설치를 진행하여 줍니다.

https://helm.sh/docs/intro/install/

 

Installing Helm

Learn how to install and get running with Helm.

helm.sh

helm repository

helm으로 설치를 진행하기 위해서는 chart를 가지고 있는 repository를 등록하여 사용해야 합니다.

# 설치하는데 필요한 chart를 가져오는 repository를 등록합니다.
helm repo add 레포지토리명 레포지토리주소

# 등록한 repository에서 설치할 수 있는 목록을 출력합니다.
helm search 레포지토리명

# 등록한 repository를 최신으로 update합니다.
helm repo update

helm install

앞에서 repo를 등록하였다면 이제는 등록된 repo를 이용하여 kubernets에 설치 작업을 진행할 수 있습니다.

helm install -n 네임스페이스 --create-namespace 앱이름 레포지토리명/설치할앱

helm upgrade

기존에 설치했던 설정값이 변경되었을 경우 helm upgrade를 통하여 변경된 내용을 적용할 수 있습니다.

helm upgrade -n 네임스페이스 앱이름 레포지토리명/설치할앱

helm delete

더 이상 배포한 앱을 사용하지 않는다면 helm delete를 통해 자원을 반납할 수 있습니다.

helm delete -n 네임스페이스 앱이름

 

[실습] helm을 이용하여 kubernetes에 nginx 설치하기

이번에 실습 예시로 helm을 이용하여 kubernets 환경에 nginx를 기본 값으로 설치하는 방법에 대해서 알아보겠습니다. helm의 repository는 ArtifactHub에서 검색할 수 있습니다. 

https://artifacthub.io/

 

Artifact Hub

Find, install and publish Kubernetes packages

artifacthub.io

 

ArtifactHub에서 nginx를 검색하면 다음과 같이 나오는데요 여기서 중요하게 봐야 되는 것 3가지가 있습니다.

먼저 어느 단체에서 만들었는지 그리고 Star를 몇개 받았는지 마지막으로 인증된 helm chart인지를 확인하여 사용자에게 가장 합리적인 것을 선택하여 줍니다.

 

페이지로 들어오면 다음과 같은 화면이 나옵니다. 이 페이지에서 helm chart에 대한 상세한 설명이 나오니 정확한 정보를 확인하기 위해서는 해당 페이지를 참고하는 것을 추천드립니다.

이제 Kubernetes 환경에 nginx를 설치하기 위해서는 INSTALL 버튼을 클릭하여 줍니다.

 

화면에 출력된 명령어를 입력하게 되면 자신의 환경에 nginx가 설치됩니다.

결과 확인

$ kubectl get po
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-6ff77558fc-qzgw5   1/1     Running   0          15s

설치 내역 삭제하기

helm delete my-nginx

 

 

 

Index Template은 인덱스를 생성할 때 사용되는 설정 템플릿입니다. 새로운 인덱스가 생성될 때 매번 정의하는 번거러움을 인덱스 템플릿 설정을 통하여 해결할 수 있습니다. 인덱스 템플릿을 선언을 통해 자동적으로 새롭게 생성되는 인덱스에 설정을 적용하여 일관된 인덱스 구조와 매핑을 유지하고 반복적인 작업을 줄이는 데 사용됩니다.

 

Index Template 생성

실습으로 my-log라는 Index Template을 생성하여 데이터 필드가 잘 적용되는지 확인하도록 하겠습니다.

 

인덱스 생성 시 설정 파라미터에 대한 정보

https://opensearch.org/docs/1.2/opensearch/rest-api/index-apis/create-index/

 

Create index

Create index Introduced 1.0

opensearch.org

PUT _index_template/my-log
{
    "index_patterns": [
        "my-log-*"
    ],
    "template": {
        "settings": {
            "number_of_shards": 4,
            "number_of_replicas": 1,
            "refresh_interval": "5s",
            "opendistro.index_state_management.policy_id": "my-ism"
        },
        "mappings": {
            "properties": {
                "cluster": {
                    "type": "keyword"
                },
                "node": {
                    "type": "keyword"
                },
                "level": {
                    "type": "keyword"
                },
                "message": {
                    "type": "text"
                },
                "timestamp": {
                    "type": "long"
                }
            },
            "dynamic_templates": [
                {
                    "default_string": {
                        "match": "default*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "keyword"
                        }
                    }
                },
                {
                    "default_number": {
                        "match": "default*",
                        "match_mapping_type": "long",
                        "mapping": {
                            "type": "integer"
                        }
                    }
                }
            ]
        }
    }
}

index_patterns을 통해 "my-log-"로 시작하는 새롭게 생성된 인덱스에 해당 Template을 적용하도록 지정합니다.

 

number_of_shards 해당 인덱스의 샤드 개수를 설정합니다. Opensearch의 경우 Cluster가 여러 Data Node로 구성되는데 하나의 Data Node에 데이터가 집중되지 않도록 number_of_shards를 설정하여 여러 Data Node에 샤드를 골고루 배치할 수 있습니다.

 

number_of_replicas는 primary shard를 복제한 replica shard의 개수를 설정합니다. 위 설정에서는 1로 설정하여 각각의 primary shard가 복제한 샤드를 한 개씩 구성할 수 있도록 설정하여 HA 구성이 되도록 설정하였습니다.

 

refresh_interval은 간단하게 설명하면 검색할 수 있는 document를 refresh 하는 것을 의미합니다. Opensearch는 데이터가 들어왔다고 바로 검색이 가능한 것이 아니고 데이터가 들어오고 해당 인덱스가 refresh가 되었을 경우 검색이 가능하게 됩니다. refresh_interval을 1s로 설정하였다면 1초마다 refresh 되어 실시간처럼 검색이 가능하게 되지만 indexing 성능에 영향을 미칠 수 있습니다. 만약 본인의 환경이 실시간을 중요하게 여기지 않고 indexing 성능을 우선으로 한다면 refresh_interval값을 크게 가져가는 것을 추천합니다. 보다 자세한 정보를 원하신다면 "Lucene"에 대해 알아보는 것을 추천합니다.

 

opendistro.index_state_management.policy_id 설정은 ISM Policy 정책을 지정하는 것입니다. ISM은 Index state Management로 인덱스의 Lifecycle을 관리해 주는 역할을 수행합니다. 아래 링크에 정리해 두었으니 자세한 내용을 링크에서 확인해 주세요

https://stdhsw.tistory.com/entry/Opensearch-ISMIndex-State-Management

 

mappings.properties 설정은 데이터의 field의 타입을 지정합니다. string 타입에는 대표적으로 keyword, text 타입이 있는데 keyword는 Aggregation과 같이 통계를 내거나 반듯이 같은 값을 검색하는 데 사용되며 text의 경우 Analyzer를 통해 데이터를 분석하는 데 사용됩니다. 그 밖에 많은 field type이 존재하는데 자세한 내용은 Opensearch document를 확인해 주세요

https://opensearch.org/docs/latest/field-types/supported-field-types/index/

 

Supported field types

Supported field types

opensearch.org

mappings.dynamic_templates 설정은 properties로 지정하지 않은 field의 데이터가 들어올 경우 해당 데이터의 필드 타입을 지정합니다. dynamic_templates 은 어떠한 데이터 필드가 들어올지 명확하지 않거나 동일한 이름 형식의 필드가 너무 많을 경우 사용합니다. 위에서 설정된 default_string의 경우 필드의 이름이 default로 시작되며 데이터 형식이 string일 경우 필드의 타입을 keyword로 지정하는 설정이며 default_number의 경우 default로 시작하여 데이터 형식이 number일 경우 필드의 타입을 integer로 설정합니다.

 

Index Template 확인하기

방금 생성한 my-log Template을 확인합니다.

GET _index_template/my-log

 

테스트 해보기

지정된 Template이 잘 적용되는지 bulk를 이용하여 실제 데이터를 넣어 확인합니다. (참고로 bulk는 HTTP Body의 가장 마지막 라인에 enter를 한번 더 넣어줘야지 Error가 발생하지 않습니다.)

POST _bulk
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992028}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992038}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992048}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992029}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992030}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992031}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992033}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992048}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "warn", "message": "[warn] json format is not valid", "timestamp": 1688992041}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992042}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992031, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992033, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992048, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "warn", "message": "[warn] json format is not valid", "timestamp": 1688992041, "default-number": 111111}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992042, "default-number": 222222}

 

정상적으로 데이터가 들어갔는지 확인해 봅니다.

POST my-log-2023-07-10/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ]
    }
  },
  "from": 0,
  "size": 20
}

 

마지막으로 저장된 데이터와 해당 데이터의 필드 타입이 어떻게 설정되었는지 확인합니다.

GET my-log-2023-07-10/

결과는 다음과 같습니다.

{
    "my-log-2023-07-10": {
        "aliases": {},
        "mappings": {
            "dynamic_templates": [
                {
                    "default_string": {
                        "match": "default*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "keyword"
                        }
                    }
                },
                {
                    "default_number": {
                        "match": "default*",
                        "match_mapping_type": "long",
                        "mapping": {
                            "type": "integer"
                        }
                    }
                }
            ],
            "properties": {
                "cluster": {
                    "type": "keyword"
                },
                "default-number": {
                    "type": "integer"
                },
                "default-text": {
                    "type": "keyword"
                },
                "level": {
                    "type": "keyword"
                },
                "message": {
                    "type": "text"
                },
                "node": {
                    "type": "keyword"
                },
                "timestamp": {
                    "type": "long"
                }
            }
        },
        "settings": {
            "index": {
                "opendistro": {
                    "index_state_management": {
                        "policy_id": "my-ism"
                    }
                },
                "refresh_interval": "5s",
                "number_of_shards": "4",
                "provided_name": "my-log-2023-07-10",
                "creation_date": "1688992258343",
                "number_of_replicas": "1",
                "uuid": "n24SvkagRl2OI0NcHb5SXw",
                "version": {
                    "created": "136297827"
                }
            }
        }
    }
}

template으로 지정한 필드의 경우 정상적으로 필드의 타입이 적용된 것을 확인할 수 있습니다. 그리고 default-number와 default-text의 경우 직접적으로 지정하지는 않았지만 dynamic_templates 설정을 통하여 자동적으로 문자열 타입은 keyword로 지정되고 숫자 타입은 integer로 설정된 것을 확인할 수 있습니다.

 

Opensearch에서 ISM은 Index State Management의 약자로 인덱스의 상태를 관리하는 Management 플러그인입니다. 인덱스의 상태란 인덱스의 생명주기를 관리하고 관리 작업을 자동화하는 기능을 제공합니다. 이를 통해 데이터 유지 정책을 정의하여 자주 사용하는 데이터를 Hot, 자주 사용되지는 않지만 종종 사용되는 데이터를 Cold, 그리고 사용되지 않는 데이터를 Delete를 하여 Opensearch의 자원을 보다 효율적으로 사용할 수 있게 할 수 있습니다.

 

ISM Policy 생성

# ISM 생성
PUT _plugins/_ism/policies/my-ism
{
    "policy": {
        "description": "my-index ism policy",
        "default_state": "hot",
        "states": [
          {
            "name" : "hot",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "cold",
                "conditions" : {
                  "min_index_age" : "3d"
                }
              }
              ]
          },
          {
            "name" : "cold",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "delete",
                "conditions" : {
                  "min_index_age" : "7d"
                }
              }
              ]
          },
          {
            "name" : "delete",
            "actions" : [
              {
                "delete" : {}
              }
            ],
            "transitions" : []
          }
        ]
    }
}

# 결과
{
    "_id": "my-ism",
    "_version": 1,
    "_primary_term": 409,
    "_seq_no": 936,
    "policy": {
        "policy": {
            "policy_id": "my-ism",
            "description": "my-index ism policy",
            "last_updated_time": 1688990475484,
            "schema_version": 1,
            "error_notification": null,
            "default_state": "hot",
            "states": [
                {
                    "name": "hot",
                    "actions": [],
                    "transitions": [
                        {
                            "state_name": "cold",
                            "conditions": {
                                "min_index_age": "3d"
                            }
                        }
                    ]
                },
                {
                    "name": "cold",
                    "actions": [],
                    "transitions": [
                        {
                            "state_name": "delete",
                            "conditions": {
                                "min_index_age": "7d"
                            }
                        }
                    ]
                },
                {
                    "name": "delete",
                    "actions": [
                        {
                            "retry": {
                                "count": 3,
                                "backoff": "exponential",
                                "delay": "1m"
                            },
                            "delete": {}
                        }
                    ],
                    "transitions": []
                }
            ],
            "ism_template": null
        }
    }
}

인덱스 policy 동작 순서

  1. default_state의 값을 hot으로 정의하여 인덱스가 생성될 때 hot 상태로 생성됩니다. 
  2. transitions.min_index_age를 통해 3일 동안 hot 데이터 형식으로 유지됩니다.
  3. 3일이 지나면 transitions.state_name으로 명시한 cold 상태로 전달됩니다.
  4. cold 상태에서 명시한 7일 동안 데이터를 유지하고 delete 상태로 전달됩니다.
  5. delete 상태에서는 action으로 정의된 delete를 통해 인덱스를  삭제합니다.

간단히 정리하면 명시한 ISM은 3일 동안 Hot 데이터로 유지하고 3일 뒤에는 Delete 되는 Policy입니다. 

 

ISM Policy 확인

ISM을 잘 생성하였다면 GET을 통해 확인합니다.

GET _plugins/_ism/policies

 

ISM 삭제

더 이상 사용되지 않는 ISM을 삭제합니다.

DELETE _plugins/_ism/policies/my-ism

 

Hot에서 바로 Delete로

개발자의 환경에 따라 Cold 데이터를 사용하지 않는 경우가 많습니다. 이런 경우 굳이 Cold 상태까지 선언하는 것보다는 Hot 다음에 바로 Delete로 보내면 됩니다. 아래 예시는 3일 동안 Hot 데이터로 유지하고 3일 뒤에 인덱스가 삭제되는 예시입니다.

PUT _plugins/_ism/policies/my-ism
{
    "policy": {
        "description": "my-index ism policy",
        "default_state": "hot",
        "states": [
          {
            "name" : "hot",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "delete",
                "conditions" : {
                  "min_index_age" : "3d"
                }
              }
              ]
          },
          {
            "name" : "delete",
            "actions" : [
              {
                "delete" : {}
              }
            ],
            "transitions" : []
          }
        ]
    }
}

 

 

자세한 내용은 아래 링크(Opensearch Document)를 확인해 주세요.

 

https://opensearch.org/docs/latest/im-plugin/ism/index/

 

Index State Management

Index State Management

opensearch.org

 

주니어 개발자가 알면 좋은 git 명령어에 대해 간단하게 정리했습니다. 이 글을 간단하게 정리하였기 때문에 보고 배운다기보다는 이 글을 통해 이러한 명령어가 있다는 것을 알고 해당 명령어로 구글링, chatGPT 또는 "git help 명령어"를 통하여 보다 더 자세한 내용을 확인하세요.

git init

로컬 저장소를 생성합니다.

# 로컬 저장소 만들기
git init

git status

현재 브런치의 변경된 상태를 출력합니다.

# 작업 디렉토리 상태 확인하기
git status

 

파일을 Stage에 추가하기

변경된 파일을 스태이지에 추가합니다.

# 파일을 스태이지에 올리기
git add

# 파일을 스태이지에 올림
git add 파일명

# 모든 변경사항을 스태이지에 올림
git add .

 

파일을 Stage에서 제거하기

파일을 Stage에서 제거하고 워킹 디렉토리에 변경 사항은 그대로 유지합니다.

# 해당 파일을 스태이지에서 제거합니다.
git reset 파일명

# git 2.23 이상 버전에서 새롭개 추가된 명령어 방식의 스태이지 제거 방법
git restore --staged 파일명

# 최소 생성으로 커밋한 내역이 없을 경우에 스태이지 제거 방법
git rm --cached 파일명

예전의 git에서는 명령어의 의미가 모호한 문제가 있었습니다. 그래서 요즘 버전에서는 restore라는 명령어가 추가되어 명령어를 명확하게 분리하도록 가이드하고 있기 때문에 스태이지에서 대상을 제거할 때는 reset보다는 restore를 사용하는 것을 권장하고 있습니다.

git commit

스태이지에 추가된 변경사항을 브런치에 커밋하여 적용합니다.

# 변경된 스태이지 브런치에 커밋하기
git commit

# 해당 메시지로 커밋하기
git commit -m "(메시지)"

# 변경 사항을 스태이지에 올리고 커밋 메시지로 커밋하기
git commit -am "(메시지)"

git branch

# 브런치 목록 확인하기
git branch

# 브런치 만들기
git branch 브런치

# 브런치 삭제
git branch  -d 삭제할브런치

# 현재 브런치를 main 브런치로 설정
git branch -M main

# 원격브런치까지 모두 출력
git branch -a

git checkout

checkout은 너무 많은 기능을 가지고 있어 switch와 restore로 나뉘었습니다. 현재는 checkout도 사용이 가능하나 앞으로 switch와 restore사용을 권장합니다.

# 해당 브런치로 체크아웃하기
git checkout 브런치

# 브런치 생성과 동시에 체크아웃하기
git checkout -b 브런치

# 베이스브런치를 지정하여 새로운 브런치 생성하고 checkout하기
git checkout -b 새로운브런치 베이스브런치

# 해당 브런치의 최신 커밋으로 체크아웃
git checkout FETCH_HEAD

# 스태이지에 기록되지 않은 모든 파일을 이전커밋으로 되돌리기
git checkout .

# 스태이지에 기록되지 않은 특정 파일을 이전커밋으로 되돌리기
git checkout -- 파일명

# 특정 커밋해쉬로 되돌리기
git checkout 커밋해쉬

git switch

checkout 명령어의 기능을 분리하여 새롭게 생성된 명령어입니다.

# 해당 브런치로 변경하기
git switch 브런치명

# 브런치를 생성하고 생성한 브런치로 변경하기
git switch -c 새로운브런치

# 현재 브런치의 커밋해쉬로 새로운 브런치 생성하기
git switch -c 새로운브런치 커밋해쉬

# 원격브런치로 switch
git switch -t origin/브런치명

git restore

기존 reset에서 자주 사용되어 새롭게 추가된 내용입니다. 파일을 특정 커밋으로 되돌리거나 스테이지에서 뺍니다.

# 지정한 파일을 최신 커밋 내역으로 되돌리기
git restore 파일명

# 지정한 파일을 특정 커밋으로 되돌리기
git restore --source 커밋해쉬 파일명

# 스태이지에 올라간 파일을 스태이지에서 빼기
git restore --staged 파일명

git log

커밋 목록 조회하며, 브런치의 commit head및 commit hash값을 알 수 있습니다.

# 커밋 목록 조회하기
git log

# 커밋 목록을 한 줄로 조회하기
git log --oneline

# 커밋별 변경 사항 목록 조회하기
git log --patch

# 커밋 목록을 그래프로 조회하기
git log --graph

# 모든 브런치 커밋 목록 조회하기
git log --branched

git tag

# 태그 추가하기 (릴리즈 버전)
git tag (태그)

# 커밋에 태그 추가하기
git tag (태그) (커밋)

# 태그 목록 조회하기
git tag --list

# 태그 삭제하기
git tag --delete

git diff (commit)

# 최근 커밋과 작업 디렉토리 비교하기
git diff

# 최근 커밋과 스태이지 비교하
git diff --staged

# 커밋과 커밋끼리 비교하기
# HEAD : 현재 브런치의 최신 커밋
# HEAD^ 또는 HEAD~1 : 현재 브런치의 최신 커밋에 바로 이전 커밋
# HEAD^^ 또는 HEAD~2 : 현재 브런치의 최신 커밋에 2개 이전 커밋
# 예제 최신 커밋과 바로 이전의 커밋 비교하기
git diff HEAD HEAD^

git diff (branch)

# 브런치끼리 비교하기
git diff 기준브런치 비교할브런치

# 원격 저장소 브런치와 비교하기
git diff main origin/main

git reset

이전 커밋으로 돌아간다.

# 되돌릴 커밋으로 되돌아가기
git reset (되돌아갈 커밋)

# 커밋만 되돌리기
git reset --soft (되돌아갈 커밋)

# 바로 이전으로 커밋만 되돌리기
git reset --soft HEAD^

# 스태이지까지 되돌리기
git reset --mixed (되돌아갈 커밋)

# 작업 디렉토리까지 되돌리기
git reset --hard (되돌아갈 커밋)

git revert

revert는 최소한 커밋을 명시한다 revert는 새로운 커밋을 생성하기 때문에 커밋 메시지를 입력해야 한다.
(이전의 커밋기록으로 새로운 커밋을 생성)

# 해당 커밋을 취소한 새로운 커밋을 추가
git revert 최소할커밋

git stash

워킹 디렉토리의 변경사항을 커밋은 하지 않고 임시 저장하는 것을 stash라고 합니다.

# 변경 사항 저장하기
$ git stash save -m "test"

# stash list 확인하기
$ git stash list

# stash 자세히 보기
$ git stash show stash@{인덱스}

# 저장되어 있는 stash 적용하기
$ git stash apply stash@{인덱스}

# stash 삭제하기
$ git stash drop stash@{인덱스}

# 인덱스 번호 0번을 apply하고 drop 두개를 한번에 수행하기
$ git stash pop

# 모든 stash list 삭제하기
$ git stash clear

git merge

타겟 브런치의 모든 커밋내역을 현재 브런치로 가져옵니다.

# 현재 브런치에 명시한 브런치 병합하기
git merge 타겟브런치

# 타겟브런치의 커밋 내역을 하나로 통합하여 머지하기
git merge --squash 타겟브런치

# conflict 발생 시 vim으로 해당 파일을 열어서 충돌을 해결하고
# git add를 진행한 뒤에
# --continue로 진행
git merge --continue

# conflict 발생 시 무시하기
git merge --abort

git rebase

rebase는 해당커밋의 기록을 타겟브런치(main 브런치)의 뒤로 재배치합니다.
마지막으로 merge를 수행해야 합니다.

# 1. 현재 test 브런치라는 가정하에 진행
# 2. test 브런치를 main으로 rebase 수행
git rebase main

# 3. main 브런치로 이동
git switch main

# 4. test 브런치와 merge
git merge test

git cherry pick

다른 브런치에 있는 커밋을 선택하여 현재 브런치에 적용하는 명령어

# 다른 브런치의 커밋해쉬를 통해 현재 브런치에 적용하기
git cherry-pick 커밋해쉬

# 다른 브런치의 커밋해쉬 여러개 가져오기
git cherry-pick 커밋해쉬1 커밋해쉬2

# 다른 브런치에 연속적으로 되어 있는 커밋해쉬 현재 브런치에 적용하기
# 총 5개의 커밋해쉬가 적용된다.
git cherry-pick 커밋해쉬1..커밋해쉬5

# conflict 발생 시 해결하고 cherry-pick 적용하기
git cherry-pick -continue

# conflict 발생 시 중단하기
git cherry-pick -abort

# 머지커밋을 cherry-pick하기
git cherry-pick -m 1 머지커밋해쉬

 


git clone

 

# 원격 저장소를 복제합니다.
git clone 원격저장소

# 원격 저장소를 해당 경로로 복제합니다.
git clone 원격저장소 로컬경로

git remote

# 원격 저장소 목록을 확인합니다.
git remote

# 원격저상소 목록과 경로를 확인합니다.
git remote -v

# 현재 로컬 저장소와 원격 저장소를 연결합니다. 일반적으로 원격저장소명을 origin으로 많이 한다.
git remote add 원격저장소명 원격저장소경로
git remote add origin 원격저장소경로

# 원격저장소 이름을 변경합니다.
git remote rename 수정전 수정후

git push

# 로컬 브런치에 있는 내용을 원격 저장소 브런치에 푸시합니다.
git push 원격저장소명 원격브런치
git push origin 원격브런치

# -u 옵션을 추가하면 이때의 원격저장소와 브랜치명을 다음부터는 git push만 입력해도 푸시가 됩니다. (pull 또한 같음)
git push -u origin 원격브런치

# 원격브런치 삭제
git push origin --delete 브런치

git fetch

# 원격저장소의 모든 내용을 가졍옵니다. 정보만 가져오고 로컬 브런치에 머지는 하지 않습니다.
git fetch

git pull

# 원격저장소에 가져와서 로컬 브런치에 머지합니다.
git pull

 

'git' 카테고리의 다른 글

git config  (0) 2023.07.11
git commit template 설정하기  (0) 2023.07.10

이번에는 git config에 대해 간단하게 정리하였습니다. 이 글은 지속적으로 업데이트하며 내용을 추가할 예정입니다.
시작하기 앞서 "git init"으로 먼저 저장소를 생성했다는 가정하에 진행합니다.

User 정보 기입

내용을 수정하거나 추가할 경우 변경한 사람이 누구인지 기록하기 위해 사용자의 정보를 입력하며 해당 사람에게 연락할 수 있도록 이메일을 입력합니다.

$ git config --global user.name 유저이름
$ git config --global user.email 유저메일

설정된 값 확인

# 명령어로 확인
$ git config -l

# 파일로 확인
$ cat ~/.gitconfig

git 기본 에디터 설정

git에는 기본 에디터로 nano 에디터를 사용하고 있습니다. 그런데 ... 이 nano 에디터는 사용하기 매우 어려우며 적응하기에도 많이 불편합니다. 그래서 대부분의 많은 개발자 분들이 자신에 맞는 에디터로 변경하고 사용합니다.

 

vim으로 설정하기

# vim으로 설정하기
$ git config --global core.editor vim
$ git config --global diff.tool vimdiff

 

vscode로 설정하기

$ git config --global core.editor "code --wait"

# git diff를 vscode로 설정하기
$ git config --global diff.tool diff-code
$ git config --global difftool.diff-code.cmd 'code --wait --diff $LOCAL $REMOTE'

기본 브런치 설정

기본 브런치가 master로 되어 있지만 요즘에 언어 순화로 master보다 main으로 설정하여 사용하는 추세입니다.

$ git config --global init.defaultBranch main
$ git branch -M main

alias 설정하기

자주 사용하거나 명령어가 너무 길다고 판단될 경우 alias 등록으로 짧고 간단하게 사용할 수 있습니다.

# git status를 st로 짧게 설정
$ git config --global alias.st status

운영체제 별 개행 처리

# window 환경
$ git config --global core.autocrlf true

# linux 환경
$ git config --global core.autocrlf input

config 설정 지우기

# --unset을 이용하여 기존의 설정을 제거할 수 있습니다.
$ git config --global --unset user.name

config 전체 수정하기

# --edit을 이용하여 global 옵션을 전체적으로 수정할 수 있습니다.
$ git config --global --edit

 

'git' 카테고리의 다른 글

주니어 개발자가 알면 좋은 git 명령어 정리  (0) 2023.07.12
git commit template 설정하기  (0) 2023.07.10

commit template을 사용하면 커밋 메시지의 구조와 형식을 일관되게 유지할 수 있습니다. 팀의 구성원들이 동일한 template을 사용하면 commit message가 일관성 있게 작성되어 코드 기록과 변경 내용을 파악하기 쉬워집니다. template을 설정하면 커밋 작성 과정이 자동화되고 개발자는 템플릿에 정의된 섹션에 적절한 내용을 작성하기만 하면 되기 때문에 git commit이 간편해집니다.

1. git commit template 파일 만들기

mkdir ~/git
vim ~/git/.gitmessage

2. ~/git/.gitmessage 파일 작성하기

생성한 파일에 커밋 템플릿을 작성합니다. 

 

# 다음과 같은 형식으로 제목을 작성합니다.
# [type]: 제목
[fea|docs|mis|fix|test|ref]: 제목

# 설명을 입력합니다.
describe:

#=========================
# fea : 새로운 기능 추가
# docs : 문서 수정
# mis : 실수로 빼먹은 내용
# fix : 버그 수정
# test : 테스트 코드
# ref : 코드 리팩토링
#=========================

3. 작성한 파일 적용하기

Git의 commit.template 설정을 업데이트합니다.

git config --global commit.template ~/git/.gitmessage

위 명령어는 ~/git/.gitmessage.txt 파일을 전역 설정으로 사용하도록 Git에 알려줍니다. --global 옵션을 사용하면 현재 사용자의 전역 Git 설정에 영향을 미치게 됩니다. 이제 git commit 명령어를 실행하면 설정한 템플릿이 기본적으로 열리며, 해당 템플릿에 따라 커밋 메시지를 작성할 수 있습니다.

 

'git' 카테고리의 다른 글

주니어 개발자가 알면 좋은 git 명령어 정리  (0) 2023.07.12
git config  (0) 2023.07.11

ubuntu 환경에서 golang version upgrade를 하는 방법에 대하여 간단하게 정리하였습니다.

0. 작업 준비

# apt update를 진행합니다.
sudo apt-get update

# wget이 설치되었는지 확인합니다.
# 다음과 같은 결과가 나오면 wget이 설치된 것 입니다.
# /usr/bin/wget
which wget

# wget이 없다면 설치하여 줍니다.
sudo apt-get install wget

1. 기존 golang 제거

sudo rm -rf /usr/local/go

2. go 공식 사이트에서 새로운 go 버전 다운로드

아래 사이트로 접속하여 새로운 go 버전의 링크를 가져옵니다.

https://go.dev/dl/

 

All releases - The Go Programming Language

 

go.dev

 

사이트에 접속하여 아래 빨간 줄에 우클릭을 통하여 "링크 주소 복사"를 합니다.

 

wget을 통하여 새로운 go 버전은 다운로드합니다.

wget https://golang.org/dl/go1.XX.X.linux-amd64.tar.gz

3. 새로운 golang 설치

다운로드한 tar 파일 압축을 해제합니다.

sudo tar -C /usr/local -xzf go1.XX.X.linux-amd64.tar.gz

 

~/.profile에 다음과 같이 PATH를 추가합니다.

export PATH=$PATH:/usr/local/go/bin

 

~/.profile을 수정한 후 적용합니다.

source ~/.profile

 

정상적으로 설치 되었는지 확인합니다.

go version

 

'Go언어' 카테고리의 다른 글

[addlicense] golang 프로젝트에 라이센스 추가하기  (0) 2024.04.29
vscode golang debug  (0) 2023.07.07
go work 사용해보기  (0) 2023.07.06
Ubuntu(Linux)에서 Go 재설치  (0) 2022.10.30
Go언어 interface reflect  (0) 2021.08.15

해당 문서는 2023년 7월에 작성된 문서입니다.

kafka.common.InconsistentClusterIdException은 Apache Kafka에서 발생하는 클러스터 ID 불일치해서 발생한 오류입니다. 이 오류는 일반적으로 Kafka 브로커에 설정된 클러스터 ID와 실제 클러스터 ID가 일치하지 않을 때 발생합니다. 클러스터 ID는 Kafka 클러스터의 고유 식별자이며, 모든 브로커가 동일한 클러스터 ID를 가지고 있어야 합니다. 여기서 대표적인 2가지 해결 방법에 대해서 정리하였습니다.

Error Message

[2023-06-28 01:42:38,833] ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)
kafka.common.InconsistentClusterIdException: The Cluster ID Ap0xMkY_TIyx9Ntyq565Pg doesn't match stored clusterId Some(LwpqAcZKQXCPnInmC0w2sw) in meta.properties. The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong

 

해결방법 1 : Cluster ID 일치 시키기

먼저 Kafka 클러스터에 속한 모든 브로커의 클러스터 ID를 확인합니다. 브로커의 설정 파일 (server.properties)을 열고 cluster.id 항목을 찾아 현재 클러스터 ID를 확인합니다. 모든 브로커가 동일한 클러스터 ID를 가져야 합니다. 각각의 브로커의 meta.properties 파일에 cluster id를 통일시킨 후 카프카를 재시작한다.

해결방법 2 : meta.properties 파일 삭제

1번 방법으로 해결이 안 될 경우 각각의 브로커에서 meta.properties 파일을 삭제하고 재시작합니다.

rm -f (log.dirs명시한 경로)/meta.properties

마지막으로

위 2가지 해결 방안은 일시적인 해결책입니다. 즉 해당 Exception이 또 발생할 가능성이 있습니다. 좀 더 카프카에 대해 배워서 해당 문제가 또 발생하지 않는 방법을 찾아야 될 것 같습니다. 보다 더 좋은 해결책을 아신다면 공유 부탁드립니다.

 

 

 

 

요즘 code editor를 vscode로 변경하면서 debug환경에 대한 설정을 진행하였습니다. 이 글의 목적은 간단하게 go언어 디버깅 환경을 구축하며, vscode의 debug 단축키 및 사용법에 대한 내용을 정리하였습니다.

Extension 설치

환경 구성은 매우 간단합니다. vscode extension에서 "go"라고 검색하여 해당 extension을 설치합니다.

launch.json 구성

vscode에서 디버깅 환경을 구성하기 위해 launch.json 파일을 생성해야 됩니다. "ctrl + shift + d" 단축키를 통해 "Run and debug" 창을 띄울 수 있습니다. 해당 창에서 "create a launch.json file"을 클릭하여 launch.json 파일을 생성합니다. 파일을 생성하면 해당 경로에 "./.vscode/launch.json" 파일이 생성되며 저는 다음과 같이 내용을 구성했습니다.

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "my project",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}"
        }
    ]
}

디버깅 하기

내가 원하는 라인에 "F9"를 통해 브레이크 포인트를 지정하여 디버깅을 진행할 수 있습니다.

위 이미지를 보면 좌측에는 변수의 값을 확인할 수 있으며, 하단에는 콘솔로 출력되는 데이터를 확인할 수 있습니다. 이상 vscode에서 go언어 디버깅환경 만드는 과정에 대해 알아봤습니다.

브레이크 포인트에 expression 부여하기

브레이크 포인트에 특정 조건일 경우에만 디버깅을 하고 싶을 경우 해당 브레이크 포인트 우클릭에 "Edit breakpoint"를 클릭하여 다음과 같이 조건을 넣을 수 있습니다.

저는 i가 5일 경우에만 브레이크 포인트에 멈추도록 설정을 하여 많은 데이터를 분석할 때 유용하게 사용할 수 있습니다.

마지막으로

마지막으로 vscode의 디버깅 관련 단축키에 대해 알아보고 마치겠습니다.

  • F5 : 디버그 진행 / 디버그 중지
  • F10 : 다음 라인 실행
  • F11 : 메서드 내부로 진입
  • Shift + F11 : 메서드 빠져나오기
  • Ctrl + Shift + F5 : 재시작
  • Shift + F5 : 디버그 종료

더 자세한 내용은 다음 링크에서 확인하세요

https://github.com/golang/vscode-go/blob/master/docs/debugging.md

 

GitHub - golang/vscode-go: Go extension for Visual Studio Code

Go extension for Visual Studio Code. Contribute to golang/vscode-go development by creating an account on GitHub.

github.com

 

'Go언어' 카테고리의 다른 글

[addlicense] golang 프로젝트에 라이센스 추가하기  (0) 2024.04.29
golang version upgrade (ubuntu)  (0) 2023.07.09
go work 사용해보기  (0) 2023.07.06
Ubuntu(Linux)에서 Go 재설치  (0) 2022.10.30
Go언어 interface reflect  (0) 2021.08.15

go work를 사용하기 위해 반듯이 go1.18 이상의 go가 설치되어 있어야 합니다. go의 workspace는 회사에서 다른 개발자들과 협업을 하게 되면 모듈의 의존성에 대한 문제가 종종 발생하게 됩니다. 이럴 때 이러한 문제를 해결할 수 있는 것이 바로 go의 workspace입니다. 간단한 예시를 들자면 example이라는 모듈을 현재 회사에서 사용하고 있는데 example이라는 모듈에 기능을 추가하거나 기능을 변경할 경우 협업하고 있는 다른 개발자들에게 영향을 미치게 해서는 안 되는 경우가 있습니다. 이런 경우 "go work"를 통하여 다른 개발자들에게 영향을 미치지 않고 example 모듈을 수정하고 수정된 모듈을 이용하여 프로젝트 개발을 진행할 수 있습니다.

해당 글은 아래 링크 go document문서를 기반으로 똑같이 따라 하며 진행하였으며 개인적인 생각과 설명이 추가된 내용입니다. go work에 대한 원본 및 자세한 내용을 원하신다면 아래 공식 문서를 확인하거나 "$ go help work"를 통하여 보다 정확한 정보를 확인할 수 있습니다.

go workspace 링크 https://go.dev/doc/tutorial/workspaces

 

테스트 환경 구성하기

테스트를 진행할 수 있도록 환경을 구성합니다.

$ mkdir -r ~/workspace
$ cd ~/workspace

 

모듈 초기화하기
(hello라는 프로젝트를 생성합니다.)

$ mkdir hello
$ cd hello
$ go mod init example.com/hello

 

example 모듈 가져오기
(회사에서 사용하는 모듈이 example이라고 가정합니다.)

$ go get golang.org/x/example/hello/reverse

 

코드 작성하기

~/workspace/hello/hello.go 파일을 생성하고 다음과 같이 코드를 작성합니다.

package main

import (
    "fmt"

    "golang.org/x/example/hello/reverse"
)

func main() {
    fmt.Println(reverse.String("Hello"))
}

 

작성된 코드를 실행시켜 모듈이 잘 적용된 것을 확인해 봅시다.

$ cd ~/workspace/hello
$ go run example.com/hello

# 결과 #
olleH

 

workspace 생성하기

go work 명령어를 이용하여 workspace를 생성합니다. (작업 디렉토리는 ~/workspace입니다.)

$ cd ~/workspace
$ go work init ./hello

 

위 명령어를 수행하게 되면 다음과 같이 go.work파일이 생성됩니다. 참고로 저는 go1.20를 사용하고 있습니다.

go 1.20

use ./hello

 

모듈 다운받아 작업 진행하기

이제는 협업하는 다른 개발자들에게 모듈을 수정해도 영향을 미치지 않도록 모듈을 다운받아 모듈을 수정하는 작업을 진행할 예정입니다.
저장소 복제하기 (작업 디렉토리는 ~/workspace입니다.)

$ cd ~/workspace
$ git clone https://go.googlesource.com/example

 

workspace에 모듈 추가하기

$ go work use ./example/hello

 

위 작업을 진행하면 go.work파일이 다음과 같이 수정됩니다.

go 1.20

use (
    ./hello
    ./example/hello
)

 

모듈 수정 작업 진행하기

~/workspace/example/hello/reverse에 int.go라는 파일을 생성하여 다음과 같이 코드를 작성합니다.

package reverse

import "strconv"

// Int returns the decimal reversal of the integer i.
func Int(i int) int {
    i, _ = strconv.Atoi(String(strconv.Itoa(i)))
    return i
}

 

~/workspace/hello/hello.go 파일에 수정된 모듈을 적용합니다.

package main

import (
    "fmt"

    "golang.org/x/example/hello/reverse"
)

func main() {
    fmt.Println(reverse.String("Hello"), reverse.Int(24601))
}

 

수정된 코드를 실행하여 수정된 모듈이 적용된 것을 확인할 수 있습니다.

$ go run ./hello
olleH 10642

지금까지 같이 협업하는 개발자들에게 모듈 수정으로 영향을 미치지 않고 모듈을 수정하여 개발을 진행하도록 하는 workspace에 대하여 알아봤습니다.

 

마지막으로

개발을 완료하고 협업하고 있는 모든 개발자들이 github에 push 하고 merge 작업을 진행할 때 반듯이 go.work 파일이 있다면 버전을 확인 후 작업을 진행한 개발자들과 패키지 버전에 대한 논의를 진행 후 merge 작업을 진행해야 build 과정에서 패키지 버전에 대한 이슈를 예방할 수 있습니다.

'Go언어' 카테고리의 다른 글

golang version upgrade (ubuntu)  (0) 2023.07.09
vscode golang debug  (0) 2023.07.07
Ubuntu(Linux)에서 Go 재설치  (0) 2022.10.30
Go언어 interface reflect  (0) 2021.08.15
Go언어 Cron  (0) 2021.06.15

Windows에서 개발을 진행하다 보면 줄 바꿈이 \r\n 이라서 종종 짜증나는 경우가 많이 있는데요. 그래서 Windows의 줄 바꿈을 Linux의 줄 바꿈과 같은 형식으로 변경하는 방법에 대하여 정리하였습니다.

Jetbrains(Goland) 줄바꿈 설정

Goland 뿐만 아니라 Jetbrains의 제품이라면 아래 방법으로 설정을 변경할 수 있습니다.
설정(Settings) -> 에디터(Editor) -> 코드 스타일(Code Style) -> 줄 구분 기호(Line separator)에서 Unix 설정인 \n으로 변경하시면 됩니다.

 

본 문서는 2023년 6월에 작성된 문서입니다.

Elasticsearch 라이센스 문제로 현재 7.10 버전을 사용하고 있습니다. 그러나 7.10 버전에서는 AWS S3에 저장하는 기능이 기본적으로 제공하고 있지 않습니다. 그래서 7.10 버전을 AWS S3에 스냅샷 저장하는 방법에 대하여 문서를 작성하였습니다. (참고로 8.0 버전 이상에서는 기본적으로 AWS S3 Snapshot을 제공하고 있습니다. 8.0 이상의 버전을 사용하시는 분들은 큰 제목 3번부터 진행하셔도 됩니다.)

1. 현재 나의 환경은?

저의 경우 Elasticsearch를 kubernetes에 설치하여 사용하고 있습니다. 그렇기 때문에 Kubernetes 환경 위주로 설명을 진행하지만 플러그인 설치하는 방법은 비슷하므로 일반적인 VM 방법과 Kubernetes 및 Docker에서 사용하는 방법에 대하여 정리하였습니다.

VM Elasticsearch

Elasticsearch는 공식적으로 제공하지 않는 부분에 있어 Elasticsearch Plugin을 통하여 확장 기능을 설치할 수 있도록 제공하고 있습니다. 이런 기능을 사용하여 Elasticsearch가 설치된 VM으로 접속하여 아래 명령어를 통하여 모든 노드에 AWS S3 Plugin을 설치하여 줍니다.

bin/elasticsearch-plugin install --batch repository-s3

모든 노드에 플러그인이 설치가 되었다면 모든 노드들을 하나씩 재시작하여 줍니다.

Kubernetes 및 Docker 환경

kubernetes, docker 환경에서는 플러그인을 설치하여 재시작을 하게 되면 해당 파드가 초기화가 되기 때문에 위 방법을 사용하지 않고 Elasticsearch 이미지를 다시 생성하여 사용해야 합니다. 

FROM docker.elastic.co/elasticsearch/elasticsearch:7.10.2
RUN bin/elasticsearch-plugin install --batch repository-s3

 

다음과 같이 Dockerfile을 생성하여 이미지를 빌드하고 빌드된 이미지를 사용합니다.

docker build -t (이미지) (도커파일경로)

2. Helm을 이용하여 Elasticsearch 설치 진행

저는 아래의 ArtifactHub를 통해 Elasticsearch 설치 작업을 진행하였습니다. 해당 문서는 AWS S3 스냅샷에 대한 내용이므로 Helm 사용법에 대해서는 추후 문서를 작성하여 링크를 붙이도록 하겠습니다. values.yaml 파일을 다음과 같이 수정하여 Elasticsearch 설치를 진행합니다. https://artifacthub.io/packages/helm/elastic/elasticsearch

image: "위에서 생성한 이미지"
imageTag: "이미지 태그"

esJvmOptions:
  jvm.options: |
    -Des.allow_insecure_settings=true

3. Elasticsearch Snapshot S3 Repo 등록

Elasticsearch가 정상적으로 설치가 되었다면 스냅샷을 저장할 Repo를 생성합니다. 한글로 작성된 부분을 사용자 환경에 맞는 값을 넣어주세요.

curl -XPUT 'localhost:9200/_snapshot/my_repository?pretty' -H 'Content-Type: application/json' -d '{
  "type": "s3",
  "settings": {
    "bucket": "버킷",
    "region": "리전",
    "base_path": "백업경로",
    "access_key": "엑세스키",
    "secret_key": "스크릿키",
    "compress": true
  }
}'

# 성공 결과
{
  "acknowledged" : true
}

4. 정상적으로 Repo가 생성되었는지 확인

curl -XGET localhost:9200/_snapshot/_all?pretty

5. Snapshot 생성

SLM(Snapshot Lifecycle Management) 를 진행하실 분은 해당 부분을 스킵하여도 됩니다.
해당 Request를 통하여 스냅샷 생성이 성공적으로 진행이 되었다면 스냅샷 정보와 스냅샷에 성공한 샤드와 실패한 샤드에 대한 개수를 알려줍니다. 종종 네트워크의 문제로 특정 노드의 샤드가 실패할 수 있는데 이런 경우 다시 시작하여 주시면 됩니다.

curl -XPUT "http://localhost:9200/_snapshot/my_repository/backup_20230613?wait_for_completion=true?pretty" -H "Content-Type: application/json" -d '
{
  "indices": "*",
  "ignore_unavailable": true,
  "include_global_state": false
}'

# 결과
생략 ...
{"total":50,"failed":0,"successful":50}

6. SLM 생성

SLM(Snapshot Lifecycle Management) 이란 Snapshot의 생명주기를 관리해 주는 기능입니다. 일정 시간에 자동으로 스냅샷을 생성하고 저장 기간을 관리하여 줍니다.

curl -X PUT 'http://localhost:9200/_slm/policy/my-snapshot' -H 'Content-Type: application/json' -d '
{
  "schedule": "0 30 1 * * ?", 
  "name": "<backup-{now/d{yyyy-MM-dd}}>",
  "repository": "my_repository", 
  "config": { 
    "indices": ["*"],
    "ignore_unavailable": true,
    "include_global_state": false
  },
  "retention": { 
    "expire_after": "30d", 
    "min_count": 5, 
    "max_count": 50 
  }
}
'

# 결과
{
  "acknowledged" : true
}
  • schedule : 스냅샷을 생성할 시간을 입력합니다.
  • name : 생성된 스냅샷을 이름 규칙을 입력합니다.
  • repository : 큰제목 3번에서 생성한 Repo를 입력합니다.
  • config
    • indices : 스냅샷 생성할 인덱스를 입력합니다. (* 이면 모든 인덱스를 스냅샷으로 저장합니다.)
    • ignore_unavailable : 유효하지 않는 값은 무시합니다.
    • include_global_state : 글로벌 설정 값을 저장할지 결정합니다.
  • retention
    • expire_after : 스냅샷 보존 기간을 설정합니다.
    • min_count : 스냅샷 최소 보존 개수를 설정합니다.
    • max_count : 스냅샷 최대 보존 개수를 설정합니다.

위 작업이 완료되었다면 매일 새벽 1시 30분마다 모든 인덱스의 스냅샷을 생성하고 30일 동안 보존되며 최대 50개 최소 5개의 스냅샷을 관리합니다.

7. SLM을 이용하여 바로 스냅샷 생성해 보기

SLM이 정상적으로 잘 작동하는지 우리는 새벽 1시 30분까지 기다릴 수 없습니다. 그렇기 때문에 다음 API를 통하여 방금 설정한 SLM을 통해 스냅샷을 지금 바로 생성하는 방법을 알아보겠습니다.

curl -X POST 'http://localhost:9200/_slm/policy/my-snapshot/_execute?pretty'

# 결과
{
  "snapshot_name" : "backup-2023-06-13-hci5ctcoq9wjfiwiuy2osg"
}

생성된 스냅샷 결과 확인

curl -XGET 'http://localhost:9200/_snapshot/my_repository/_all?pretty'

# 결과
    {
      "snapshot" : "backup-2023-06-13-hci5ctcoq9wjfiwiuy2osg",
      "uuid" : "wXXFF223FTt-rLTKIOFQk2A",
      "version_id" : 7100299,
      "version" : "7.10.2",
      "indices" : [
        "인덱스-0",
        "인덱스-1",
        "인덱스-2",
        "인덱스-3",
        "인덱스-4",
        "인덱스-5"
      ],
      (생략 ...)
      "state" : "SUCCESS",
      "start_time" : "2023-06-13T06:38:37.211Z",
      "start_time_in_millis" : 1686638317211,
      "end_time" : "2023-06-13T06:38:50.620Z",
      "end_time_in_millis" : 1686638330620,
      "duration_in_millis" : 13409,
      "failures" : [ ],
      "shards" : {
        "total" : 50,
        "failed" : 0,
        "successful" : 50
      }
    }

8. 스냅샷으로 복구하기

위에서 생성한 스냅샷으로 복구하는 방법은 다음과 같습니다. 그러나 여기서 명심해야 되는 것은 이미 같은 이름의 인덱스가 Elasticsearch에 존재하면 안 됩니다. 스냅샷은 특정 시점으로 돌아가는 것이기 때문에 같은 이름의 인덱스가 존재하면 해당 인덱스를 삭제해야 합니다.

curl -X POST "http://localhost:9200/_snapshot/my_repository/backup-2023-06-13-hci5ctcoq9wjfiwiuy2osg/_restore?pretty" -H "Content-Type: application/json" -d '
{
  "indices": "*"
}'

# 결과
{"accepted":true}
  • indices : 복구하려는 인덱스를 명시합니다.

 


Snapshot 삭제하기

curl -XDELETE 'http://localhost:9200/_snapshot/my_repository/backup-2023-06-13-hci5ctcoq9wjfiwiuy2osg?pretty'

Snapshot Repo 삭제하기

curl -XDELETE 'http://localhost:9200/_snapshot/my_repository?pretty'

 

elasticdump는 Elasticsearch 인덱스와 데이터를 내보내거나 가져오기 위한 도구입니다. elasticdump를 사용하면 Elasticsearch 클러스터의 index, document, template 등을 백업하거나 다른 클러스터로 마이그레이션하는 작업을 수행할 수 있습니다. 본 문서에서 elasticdump의 기본 적인 사용법에 대해서 정리하였습니다.

elasticdump install

$ sudo apt update
$ sudo apt install npm

$ npm install elasticdump -g

$ elasticdump --version
6.103.0
(node:16397) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023.
(생략...)

Elasticsearch Index의 데이터를 파일로 가져오기

Index의 documents데이터를 디렉토리에 파일로 가져올 수 있습니다. 현재 Index에 저장된 데이터는 다음과 같습니다.

 

이제 elasticdump를 이용하여 파일로 가져오겠습니다.

$ elasticdump --input="http://localhost:9200/my-index" --output="./my-index.txt"

 

위 명령어를 통하여 현재 디렉토리에 my-index.txt 파일로 데이터를 가져왔습니다. 가져온 데이터는 다음과 같습니다.

{"_index":"my-index","_type":"_doc","_id":"RO_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"1","message":"aaaa","type":"APP","timestamp":1688038455}}
{"_index":"my-index","_type":"_doc","_id":"Ru_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"3","message":"cccc","type":"DB","timestamp":1688038457}}
{"_index":"my-index","_type":"_doc","_id":"Re_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"2","message":"bbbb","type":"APP","timestamp":1688038456}}
{"_index":"my-index","_type":"_doc","_id":"R-_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"4","message":"dddd","type":"APP","timestamp":1688038459}}
{"_index":"my-index","_type":"_doc","_id":"SO_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"5","message":"eeee","type":"DB","timestamp":1688038459}}
{"_index":"my-index","_type":"_doc","_id":"Se_7BokBQKytg6noxeXW","_score":1,"_source":{"uid":"6","message":"ffff","type":"DB","timestamp":1688038459}}

파일로 가져온 데이터 Elasticsearch로 보내기

앞에서는 elasticsearch에서 데이터를 가져오는 방법을 알아봤습니다. 파일에서 elasticsearch로 전송하는 방법은 input과 output를 반대로 설정해 주면 파일에서 elasticsearch로 데이터를 전송합니다.

elasticdump --input="./my-index.txt" --output="http://localhost:9200/my-index"

A Cluster에서 B Cluster로 데이터 전송하기

이번에는 파일로 가져오지 않고 다른 클러스터로 바로 데이터를 전송하겠습니다.

A Cluster가 "localhost:9200"이고 B Cluster는 "localhost:9400" 일 경우 A Cluster에서 B Cluster로 데이터를 전송해보겠습니다.

elasticdump --input="http://localhost:9200/my-index" --output="http://localhost:9400/my-index"

그밖에 elasticdump 옵션

  • --type=analyzer: settings를 내보낸다.
  • --type=mapping: mapping을 내보낸다.
  • --type=data: documents를 내보낸다 (Default).
  • --overwirte: export 파일이 이미 존재한다면 덮어쓰기 한다.
  • --limit: 지정한 limit 개수만큼씩 끊어 가져온다. (Default: 100)
  • --output-index=${index명}: 백업해 놓은 파일로부터 가져오기 할 때 원래 인덱스가 아닌 원하는 인덱스로 지정해 줄 수 있다.

마지막으로

elasticdump는 적은 데이터를 옮길 때는 편하게 사용할 수 있지만 속도가 매우 느리기 때문에 큰 인덱스의 데이터를 가져오는 것은 추천하지 않습니다. 큰 데이터를 마이그레이션 할 때는 snapshot을 이용하여 특정 인덱스만 가져오는 것을 추천드립니다.

 

더 자세한 사용방법은 elasticdump github에서 확인할 수 있습니다.

https://github.com/elasticsearch-dump/elasticsearch-dump

 

GitHub - elasticsearch-dump/elasticsearch-dump: Import and export tools for elasticsearch

Import and export tools for elasticsearch. Contribute to elasticsearch-dump/elasticsearch-dump development by creating an account on GitHub.

github.com

 

해당 문서는 2023년 7월에 작성된 문서입니다.
윈도우에서 리눅스를 사용하기 위해 또는 Docker Desktop을 윈도우에 설치하기 위해서 가장 일반적으로 WSL을 사용하고 있습니다. 이 글에서는 WSL이 설치되었다는 가정하에 WSL의 기본 사용법에 대해 알아보겠습니다.

 

WSL 설치

https://learn.microsoft.com/ko-kr/windows/wsl/install
wsl 설치에 대해서는 위 링크를 참고하여 진행해 주시기 바랍니다.

 

WSL에 설치할 수 있는 배포 확인

먼저 wsl에서 지원하는 설치할 수 있는 배포에 대하여 알아봐야 합니다. 아래 명령어를 통하여 다음과 같이 지원하는 배포를 출력합니다.

> wsl -l -o

# 결과 #
다음은 설치할 수 있는 유효한 배포 목록입니다.
'wsl --install -d <배포>'를 사용하여 설치하세요.

NAME               FRIENDLY NAME
Ubuntu             Ubuntu
Debian             Debian GNU/Linux
kali-linux         Kali Linux Rolling
SLES-12            SUSE Linux Enterprise Server v12
SLES-15            SUSE Linux Enterprise Server v15
Ubuntu-18.04       Ubuntu 18.04 LTS
Ubuntu-20.04       Ubuntu 20.04 LTS
Ubuntu-22.04       Ubuntu 22.04 LTS
OracleLinux_8_5    Oracle Linux 8.5
OracleLinux_7_9    Oracle Linux 7.9

 

새로운 배포 설치

설치 가능한 배로를 확인하고 원하는 배포를 설치하면 됩니다. 저는 예시로 "Ubuntu-22.04"를 아래 명령어를 통하여 설치를 진행했습니다. 명령어를 수행하고 나면 user, password를 입력하여 설치를 완료합니다.

> wsl --install -d Ubuntu-22.04

# 결과 #
Installing, this may take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: 
New password:
Retype new password:

 

배포 확인

맨 앞 "*" 표시가 있는 배포는 디폴트로 설정된 배포입니다.

> wsl -l -v

# 결과 #
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2
  Ubuntu-22.04           Running         2

 

배포 시작

만약 디폴트로 설정된 배포 실행하는 방법은 간단합니다. 그냥 아무런 옵션 없이 wsl만 입력하면 디폴트로 설정된 배포가 시작됩니다.

> wsl

# 원하는 경로에서 시작하기
> wsl 절대경로

이번에는 디폴트로 설정되지 않은 배포를 시작하는 방법에 대하여 알아보겠습니다. 예제로는 앞서 설치한 Ubuntu-22.04로 진행하였습니다.

> wsl -d Ubuntu-22.04

# 원하는 경로에서 시작하기
> wsl -d Ubuntu-22.04 절대경로

 

디폴트 배포 설정하기

자주 사용하는 배포를 디폴트로 설정하여 편리하게 사용할 수 있습니다.

# 디폴트 배포 변경하기
> wsl -s Ubuntu-22.04

# 확인
> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu-22.04           Running         2
  docker-desktop         Running         2
  Ubuntu                 Running         2
  docker-desktop-data    Running         2

 

배포 삭제하기

사용하지 않는 배포를 삭제하여 깔끔하게 정리합니다.

> wsl --unregister Ubuntu-22.04

지금까지 WSL에 대한 기본 사용법에 대해서 알아봤습니다. 보다 자세한 내용은 아래 링크 또는 "wsl --help"를 통하여 확인할 수 있습니다.
 https://learn.microsoft.com/ko-kr/windows/wsl/basic-commands?source=recommendations

 

기타 등등

# wsl 업데이트
wsl --update

# wsl 종료
wsl --shutdown

 

본 문서는 2023년 6월에 작성된 문서입니다. 시간이 지나면 현재 시험 방식과 많이 다를 수 있습니다.


저는 회사에서 2년 넘게 Kubernetes를 사용을 하여 Kubernetes에 익숙한 상태였고 이왕 Kubernetes를 사용할 줄 알게 된 거 이 기회에 CKA를 취득하는 게 좋을 것 같아 시험을 보게 되었습니다. CKA 준비 기간은 2 ~ 3주 정도 걸렸으며 모든 부분을 공부한 것이 아닌 부족한 부분만 골라 보면서 공부를 하였습니다.

결제는 어떻게?

https://training.linuxfoundation.org/certification/certified-kubernetes-administrator-cka/

 

Certified Kubernetes Administrator (CKA) Exam | Linux Foundation

Training in skills, knowledge, and competency to perform the responsibilities of Kubernetes administrators.

training.linuxfoundation.org

결제 진행 방식은 위 링크를 통해 Linux Foundation에 로그인 후 결제를 진행하면 됩니다. 여기서 약간의 팁은 CKA는 자주 할인을 진행하는데 적게는 15%에서 많게는 40%까지 할인을 받을 수 있습니다. 저 같은 경우 40% 해택을 받아 321,256원으로 결제할 수 있었습니다. 여기서 환율이 적용되기 때문에 환율이 좋을 때 구매를 진행하시면 같은 40% 할인이어도 실제 구매한 가격이 다를 수 있습니다.
할인 코드 확인 방법은 https://training.linuxfoundation.org/blog/ Linux Foudation 블로그에 게시물로 업로드가 되는데 그때 코드를 받아 결제를 진행하시면 됩니다.

CKA 공부 방법

저는 회사에서 Kubernetes를 2년 이상 사용하였습니다. 회사에서 사용하였을 때는 "마사야 야오야마"가 지은 "쿠버네티스 완벽 가이드"라는 책으로 공부를 하였고 CKA 준비를 하기 위해서는 Udemy에 "Mumshad Mannambeth"의 "Certified Kubernetes Administrator (CKA) with Practice Tests" 강의를 들었습니다. 저는 어느 정도 Kubernetes를 사용하여 부족한 부분만 골라서 강의를 들었습니다. 여기서 제공하는 실습문제가 실제로 많이 도움이 되었기 때문에 실습문제는 반드시 풀어보는 게 좋습니다. 이 강의 또한 할인을 자주 하기 때문에 관심등록을 통해 할인한다는 알림을 받게 되면 그때 구입하는 것을 추천드립니다. 원래 가격은 99,000원이지만 할인을 받게 되면 15,000에서 20,000원에 결제할 수 있습니다.

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/

Killer.sh 후기

CKA 시험을 결제하게 되면 Killer.sh라는 실제 시험 환경과 아주 비슷한 환경을 제공해 줍니다. 세션을 시작하면 36시간 동안 지속적으로 비슷한 시험 환경에서 예상문제를 풀 수 있는 세션을 2개 줍니다. 1개의 세션은 36시간 동안 접속할 수 있기 때문에 주말에 문제를 여러 번 풀어보면 많은 도움이 됩니다. 세션은 2개 주지만 2개의 세션 모두 시험문제가 동일하니 굳이 2개의 세션을 사용하지 않으셔도 됩니다. 참고로 Killer.sh는 실제 CKA 시험 문제보다 어려운 문제가 나오니 여기서 좋은 못한 점수를 받았다고 낙심하지 마세요. 그리고 Killer.sh는 문제와 풀이 과정까지 전부 PDF로 제공하여 부족한 부분을 공부하는데 좋습니다.

CKA 당일!!!

PSI 브라우저 설치

시험 날짜를 정하고 나면 이메일로 PSI 브라우저를 설치할 수 있는 링크를 보내줍니다. PSI 브라우저는 시험 보기 30분 전에 설치할 수 있으며 설치가 완료되고 PSI를 구동하면 PSI와 동시에 동작할 수 없는 프로세서를 죽이고 시험에 입장을 하게 됩니다. 이때 PSI와 같이 동작할 수 없는 프로그램은 대표적으로 크롬입니다. 참고로 PSI에서 버튼을 누르면 PSI가 알아서 프로그램을 죽이니 미리 프로그램을 종료하지 않으셔도 됩니다. 

준비물

폐쇄된 공간(집보다는 주말에 회사에 출근하여 폐쇄된 회의실에서 시험을 보는 것을 추천), 여권, 웹캠(노트북 가능), 최소 15인치 이상의 화면(듀얼모니터 불가능)

감독관과의 소통

PSI 브라우저 설치가 모두 정상적으로 끝나면 감독관과 채팅을 하게 되는데.. 영어를 잘 모르는 저의 입장에서는 모든 시험 통틀어서 가장 힘든 순간이었습니다. 먼저 여권으로 신원을 확인하고 노트북에 달린 웹캠으로 시험 보는 공간을 감독관에게 확인시켜주어야 합니다. 그리고 감독관이 주변에 어떠한 물건을 치우라고 하면 바로바로 치워주면 시험을 시작하게 됩니다. 저는 감독관과 채팅을 시작하자마자 "I am not good english"라고 알려주었더니 제가 영어를 못 알아듣는 거 같으니 감독관 단어 하나씩 채팅에 쳐서 알려주는 친절함을 보여주었습니다. 그러니 영어를 잘 모르더라도 너무 두려워하지 않으셔도 됩니다.

실제 시험 환경

시험장에 들어가면 아래 이미지와 같은 환경에서 시험을 보게 됩니다. 우측 상단에는 응시자(본인)의 얼굴이 나오는데 화면에 반드시 얼굴이 벗어나면 안 됩니다. 얼굴이 화면에 벗어나게 되면 채팅창으로 감독관이 카메라에 얼굴이 나와야 된다는 메시지를 보냅니다. 그리고 가장 중요한 팁은 바로 복사 붙여 넣기인데요 저는 윈도우 노트북으로 시험을 진행하였는데 터미널과 파이어폭스, 메모장의 복붙 단축키가 다릅니다.
터미널 : ctrl + shift + c(복사), ctrl + shift + v(붙여 넣기)
파이어폭스, 메모장 : ctrl + c(복사), ctrl + v(붙여 넣기)
파이어폭스 Kubernets document에서 yaml을 복사할 때는 ctrl + c로 복사하여 터미널에서는 ctrl + shift + v로 붙여 넣어야 합니다.

참고로 문제의 언어를 선택할 수 있는데 영어, 중국어, 일본어만 제공하고 있습니다.
그리고 정말 인터넷이 아주 느리니 무조건 유선으로 시험 보는 것을 추천드리고 생각보다 PSI 브라우저가 매우 무겁습니다. 그렇기 때문에 어느 정도 성능이 좋은 컴퓨터로 시험을 보는 것을 추천드립니다.

CKA 문제 유형

문제는 15 ~ 20개 정도 나오고 2시간 동안 풀 수 있습니다. 저의 경우 17문제 나왔고 문제의 유형은 아래 리스트처럼 나왔습니다.

  • pod 생성
  • 멀티 컨테이너 pod 생성
  • 사이드카 컨테이너 pod 생성
  • deploy, statefulset, daemonset 등 생성
  • service expose
  • pv, pvc 생성 후 마운트
  • ingress, networkpolicy
  • rbac
  • etcd 백업
  • version upgrade
  • Troubleshooting

CKA Tip

  1. kubectl 자동완성은 기본적으로 적용되어 있습니다.
  2. alias k="kubectl"은 기본적으로 적용되어 있습니다.
  3. etcdctl 이 이미 설치되어 있습니다.
  4. export do="--dry-run=client -o yaml"를 미리 정의해 두면 dry-run을 수행할 때 조금은 더 빠르게 작업할 수 있습니다.
  5. kubectl create --help를 통해 create로 생성할 수 있는 리소스는 create로 생성하고 create로 생성할 수 없는 리소스는 kubernetes document에서 복사하는 게 빠릅니다. (예시로 pv, pvc는 빨리 document에서 복사해 오자)
  6. PSI 브라우저 바탕화면에서 우클릭하여 메모장을 생성 후 사용할 수 있습니다.
  7. 파이어폭스에서 ctrl + f로 검색을 하고 아래에 search highlight 체크박스 선택을 통해 검색을 강조하여 볼 수 있습니다.
  8. 파이어폭스에서 실수로 ctrl + shift + c를 누르면.. 개발자 모드가 나오는데 인터넷이 매우 느려 개발자 모드를 끄는데도 5 ~ 10초 정도 소요됩니다.
  9. 내 얼굴이 나오는 상단 메뉴를 접어 화면을 조금 더 크게 사용할 수 있다.

외우면 좋은 거

  1. etcd 백업 방법
  2. manifest위치 (/etc/kubernetes/manifests)
  3. PKI 위치 (/etc/kubernetes/pki/)
  4. 파드 DNS 확인 (kubectl exec -it 파드명 -- cat /etc/resolv.conf)
  5. kubelet 로그 확인 (journalctl -u kubelet)

CKA 시험 결과

2023년 6월 6일 시험을 봤고 6월 7일에 시험 결과가 나왔고 결과는 한 번에 합격할 수 있었습니다.!!!
CKA에 도전하는 분들 모두 응원합니다.

 

Elasticsearch의 성능을 위해 데이터를 한 건 한 건 처리하는 것이 아닌 bulk를 이용하여 한 번에 처리하는 경우가 많습니다.
bulk를 사용하여 많은 양의 데이터를 Elasticsearch에 저장할 경우 분명 HTTP 요청은 정상적으로 끝이 났는데 데이터가 저장이 안 되는 경우가 발생할 수 있습니다. 여기서 중요한 것은 Elasticsearch로 보낸 HTTP의 Response는 200이라는 정상적인 값을 리턴을 하기 때문에 문제를 찾기가 어려운데요. 이럴 때는 Response의 state code를 보는 것이 아라 Response의 body의 내용을 볼 필요가 있습니다.

문제 해결방법

해당 문제를 해결하는 방법은 2가지 방법이 있습니다.

  1. 한번에 요청하는 bulk의 사이즈를 줄인다 : 만약 자원의 부족으로 thread 설정이 불가능한 경우 한번에 처리하는 bulk의 개수를 줄여서 해당 문제를 해결할 수 있습니다.
  2. thread_pool의 wrtie queue size를 늘린다 : http request connect를 통한 부하를 줄이는 것이 더 중요한 경우 1번 방법을 사용할 수 없습니다. 그런 경우 thread가 처리할 수 있는 자원이 있을 때 2번 방법을 통하여 한번에 처리할 수 있는 bulk의 양을 늘려서 문제를 해결할 수 있습니다.

모든 상황에 만족하는 문제 해결은 없습니다. 본인의 환경에 맞는 문제 해결 방법을 찾고 해당 방법을 적용하는 것을 추천드립니다.

아래는 2번 방법을 통해 한번에 처리할 수 있는 bulk의 양을 늘리는 방법에 대해 정리하였습니다.

설정 변경하기

Elasticsearch 디렉토리 위치에 config/elasticsearch.yml파일에 다음과 같은 설정 값을 추가하고 Elasticsearch를 재시작합니다.

thread_pool.write.queue_size: 2000
thread_pool.write.size: 16

설정을 진행하자가 문제가 발생한 경우

만약 다음과 같은 메시지가 발생한 다면 thread_pool.write.size의 값을 13보다 작은 값으로 설정하세요.

java.lang.IllegalArgumentException: Failed to parse value [16] for setting [thread_pool.write.size] must be <= 13

설정값의 의미

  • thread_pool.write.size : write를 수행하는 스레드 풀의 스레드 수
  • thread_pool.write.queue_size : write를 할 때 사용되는 스레드 풀의 큐 크기
  • thread_pool.search.size : search를 수행하는 스레드 풀의 스레드 수
  • thread_pool.search.queue_size : earch를 할 때 사용되는 스레드 풀의 큐 크기

마지막으로

thread에 대한 설정은 노드의 리소스 스펙에 한계가 있기 때문에 어느 정도 설정 값 튜닝을 했음에도 불구하고 더 이상의 성능 향상이 없을 경우 노드의 Scale up을 고려하는 것을 추천드립니다.

 

Go언어 처음 설치라면 2번부터 진행하시면 됩니다.

1. Go 제거하기

# go언어 제거 하기
sudo apt-get purge golang*
sudo rm -rf /usr/local/go
sudo rm -rf $(echo $GOPATH)

# ~/bashrc 또는 ~/.profile에서 go 관련된 항목 제거(기존에 설정한 파일에서 작업)
source ~/.profile
source ~/.bashrc

# 제거 확인
go version

 

2. apt-get 업데이트 하기

sudo apt-get update
sudo apt-get -y upgrade

 

3. wget 설치 확인 및 wget 설치

# wget 설치 확인 & wget 없을 경우 wget 설치
which wget
sudo apt-get install wget

 

4. Go 최신 버전 설치하기

https://go.dev/dl/

 

Downloads - The Go Programming Language

Downloads After downloading a binary release suitable for your system, please follow the installation instructions. If you are building from source, follow the source installation instructions. See the release history for more information about Go releases

go.dev

 

위 링크에서 최신 버전(현재 최신 버전은 1.19.2)의 linux-amd64.tar.gz에 우클릭하여 링크 주소를 복사하고 wget으로 다운로드합니다.

# go 다운로드
wget https://golang.org/dl/go1.19.2.linux-amd64.tar.gz

# 압축 풀기
sudo tar -C /usr/local -xvf go1.19.2.linux-amd64.tar.gz

# 환경변수 설정
mkdir ~/go
echo "export GOROOT=/usr/local/go" >> ~/.profile
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.profile
echo "export GOPATH=$HOME/go" >> ~/.profile
source ~/.profile
echo $GOPATH

# go version 확인하기
go version

 

 

 

 

 

'Go언어' 카테고리의 다른 글

vscode golang debug  (0) 2023.07.07
go work 사용해보기  (0) 2023.07.06
Go언어 interface reflect  (0) 2021.08.15
Go언어 Cron  (0) 2021.06.15
Prometheus Go언어 Metric label  (0) 2021.06.14

Aggregations을 수행할 때 사용할 데이터

 

Sub Aggregations

aggregation을 통해 집계를 내고 그 결과에서 세부적인 집계를 위해 Sub Aggregation을 지원하고 있습니다.

 

level 필드별 duration의 평균 시간 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "avgDuration": {
                    "avg": {
                        "field": "duration"
                    }   
                }
            }
        }
    }
}

결과 보기

level의 "info"는 9개가 있으며 9개의 duration 평균은 167.777777입니다.

level의 "warn"는 6개가 있으며 6개의 duration 평균은 333.33333입니다.

level의 "debug"는 5개가 있으며 5개의 duration 평균은 258입니다.

 

level별 action 종류 및 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "byAction": {
                    "terms": {
                        "field": "action"
                    }   
                }
            }
        }
    }
}

결과 보기

{
    "took": 4,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 20,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "byLevel": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "info",
                    "doc_count": 9,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "warn",
                    "doc_count": 6,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "debug",
                    "doc_count": 5,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                }
            ]
        }
    }
}

"info"는 9개이며 그중에 "close_file"는 4개, "open_file"은 4개, "open_socket"은 1개 입니다.

"warn"는 6개이며 그중에 "close_file"는 2개, "open_file"은 2개, "open_socket"은 1개, "close_socket"은 1개입니다.

"info"는 9개이며 그중에 "close_file"는 2개, "open_file"은 2개, "close_socket"은 1개입니다.

 

 

Aggregation을 수행할 때 사용할 데이터

terms

terms는 데이터의 keyword별 종류 및 개수를 집계하는데 사용합니다.

 

모든 도큐먼트의 level의 종류와 개수 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregation을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

level 필드의 데이터 종류와 각 종류별로 도큐먼트가 몇개 있는지 확인합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            }
        }
    }
}

결과 보기

"byLevel" 내부의 buckets를 보면 "key"에는 level필드의 값이 "doc_count"에는 개수를 표현하고 있습니다.

즉 "info"는 9개가 있고, "warn"은 6개, "debug"는 5개가 있는 것을 확인할 수 있습니다.

 

range

값의 범위 별로 도큐먼트의 개수를 측정하는데 사용할 수 있습니다.

 

모든 도큐먼트의 duration의 범위별 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

duration의 범위 100이하, 100부터 200까지, 200부터 300까지, 300부터 400까지, 400이상 으로 범위를 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "range": {
                "field": "duration",
                "ranges": [
                    {
                        "to": 100
                    },
                    {
                        "from": 100,
                        "to": 200
                    },
                    {
                        "from": 200,
                        "to": 300
                    },
                    {
                        "from": 300,
                        "to": 400
                    },
                    {
                        "from": 400
                    }
                ]
            }
        }
    }
}

결과 보기

 

histogram

range의 경우 from, to를 통하여 범위를 지정했다면 histogram은 range와 다르게 interval옵션을 통하여 interval단위 별로 필드의 개수를 측정하는 Aggregations입니다.

 

모든 도큐먼트의 duration을 100 단위로 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "histogram": {
                "field": "duration",
                "interval": 100
            }
        }
    }
}

결과 보기

100 단위 별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_range

range처럼 date필드 또한 범위를 지정하여 집계를 할 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 범위 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

"start_time"필드의 값을 범위 별로 측정하도록 하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_range": {
            "date_range": {
                "field": "start_time",
                "ranges": [
                    {
                        "from": "2021-09-12 10:10:10",
                        "to": "2021-09-12 10:10:15"
                    },
                    {
                        "from": "2021-09-12 10:10:15",
                        "to": "2021-09-12 10:10:20"
                    },
                    {
                        "from": "2021-09-12 10:10:20"
                    }
                ]
            }
        }
    }
}

결과 보기

각 시간대별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_histogram

histogram과 같이 date_histogram도 interval 옵션을 넣어 각 interval별 집계를 측정할 수 있습니다.

interval옵션에 second, minute, hour, day, month, week 등을 넣을 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 1분 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_his": {
            "date_histogram": {
                "field": "start_time",
                "interval": "minute"
            }
        }
    }
}

결과 보기

 

 

 

 

Aggregation을 수행할 때 사용할 데이터

 

cardinality

cardinality는 데이터의 종류가 몇 가지 있는지 확인하는 데 사용됩니다.

 

user_id가 1인 도큐먼트에 action이 몇가지 종류가 있는지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "cardinality_by_action": {
            "cardinality": {
                "field": "action"
            }
        }
    }
}

결과 보기

"cardinality_by_action"를 통해 user_id가 1인 도큐먼트에서는 action의 종류가 4개가 있다는 것을 확인할 수 있습니다.

실제로 "open_file", "close_file", "open_socket", "close_socket" 이렇게 4개입니다.

 

percentiles

각각의 값을 백분율로 보는 Aggregations입니다. 퍼센트 옵션을 설정하지 않으면 디폴트로 [1, 5, 25, 50, 75, 95, 99]로 설정됩니다.

 

user_id가 1인 도큐먼트에 duration의 백분율 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration"
            }
        }
    }
}

결과 보기

하위 1%의 값은 100이며, 상위 99%의 값은 430인 것을 확인할 수 있습니다.

 

퍼센트를 설정하여 확인하기

이제는 디폴트 퍼센트값이 아닌 퍼센트를 사용자가 [25, 50, 75]로 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration",
                "percents": [ 25, 50, 75 ]
            }
        }
    }
}

결과 보기

 

percentile_ranks

지정한 값이 백분율로 몇 퍼센트 안에 속하는지 확인할 수 있습니다.

values라는 옵션으로 값을 지정할 수 있습니다.

 

user_id가 1인 도큐먼트에 필드 duration에 100, 200, 300의 값이 있다면 백분율 몇 퍼센트인지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "rank_by_duration": {
            "percentile_ranks": {
                "field": "duration",
                "values": [ 100, 200, 300 ]
            }
        }
    }
}

결과 보기

 

Aggregation을 수행할 때 사용할 데이터

Aggregations이란

저장된 데이터를 수치화시키는 여러 가지의 연산을 수행하는 역할을 합니다. 이러한 기능을 통하여 데이터를 분석하여 사용자가 보기 좋게 시각화를 할 수 있습니다.

 

이번 페이지에서는 min, max, sum, avg, stats에 대하여 알아보겠습니다.

min

query의 검색 결과에 특정 필드의 값이 가장 작은 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최솟값 구하기

"size"를 0으로 설정한 이유는 우리는 데이터를 가져오는 것이 목적이 아닌 user_id가 1인 도큐먼트에서 duration이 가장 작은 값만 도출하는 것이 목적이기 때문에 불필요하게 데이터를 가져오는 것을 생략하기 위함입니다.

"aggs"를 통하여 쿼리에 Aggregation을 선언하고 "minDuration"이라는 Aggregation의 이름을 사용자가 지정합니다. 그리고 "min"을 통하여 duration이라는 필드의 최솟값을 측정하도록 합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "minDuration": {
            "min": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"hits"는 검색 결과를 나타내지만 우리는 "size"를 0으로 설정하였기 때문에 결과값은 나오지 않습니다.

"aggregations"에 우리가 지정한 "minDuration"이라는 명칭으로 최소값이 100이라는 결과가 나왔습니다.

 

max

query의 검색 결과에 특정 필드의 값이 가장 큰 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최대값 구하기

"maxDuration"이라는 명칭으로 duration의 값이 가장 큰 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "maxDuration": {
            "max": {
                "field": "duration"
            }
        }
    }
}

결과 보기

maxDuration을 통하여 user_id가 1인 도큐먼트에서 duration이 가장 큰 값은 430이라는 결과를 알 수 있습니다.

 

sum

query의 검색 결과에 특정 필드의 값을 전부 더한 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"sumDuration"이라는 명칭으로 user_id가 1인 duration의 값을 전부 더한 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "sumDuration": {
            "sum": {
                "field": "duration"
            }
        }
    }
}

결과 보기

sumDuration을 통하여 user_id가 1인 도큐먼트에서 duration을 전부 더한 값이 2600이라는 것을 알 수 있습니다.

 

avg

query의 검색 결과에 특정 필드의 값의 평균 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"avgDuration"이라는 명칭으로 user_id가 1인 duration의 값의 평균을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "avgDuration": {
            "avg": {
                "field": "duration"
            }
        }
    }
}

결과 보기

avgDuration을 통하여 user_id가 1인 도큐먼트에서 duration의 평균값이 216.66666666이라는 것을 알 수 있습니다.

 

stats

stats를 통하여 위에서 본 count, min, max, sum, avg의 모든 결과를 가져올 수 있습니다.

 

user_id가 1인 도큐먼트의 duration 필드의 stats 결과 값 확인하기

"statsDuration"이라는 명칭으로 user_id가 1인 도큐먼트의 count, min, max, sum, avg의 값을 확인하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "statDuration": {
            "stats": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"statDuration"을 통해 count, min, max, avg, sum의 결과 값을 확인할 수 있습니다.

 

그렇다면 min만 필요하더라도 stats하나로 모든 결과 값을 구하면 된다고 생각할 수 있습니다. 그러나 이러한 생각은 좋지 못한 생각입니다. min만 구하기 위해서 stats를 사용하는 것은 매우 안 좋은 선택입니다. 이 이유는 Elasticsearch에게 그만큼 많은 부하를 줄 수 있기 때문입니다. 그러나 반대로 min, max, sum을 구해야 될 경우에는 min, max, sum 각각 3번의 검색을 수행하는 것보다는 stats를 한 번을 사용하여 네트워크 I/O를 줄여 주는 것이 더 현명한 선택이라고 생각됩니다.

 

삽입된 데이터는 이전 페이지를 참조해주세요

https://stdhsw.tistory.com/entry/Elasticsearch-%EA%B2%80%EC%83%89%ED%95%98%EA%B8%B01

 

match 쿼리

match는 term과 비슷해 보이지만 미묘한 차이가 있습니다. match의 경우 기본적인 검색을 수행하는데 사용을 하지만 저 같은 경우 text 필드에서 보다 상세한 검색을 수행할 경우 주로 사용됩니다.  즉 문서의 내용에서 특정 단어가 들어 갔는지 검색할때 많이 사용됩니다.

 

text 필드 검색하기

message 필드에 "aaa"라는 문자열이 포함된 모든 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "aaa"
                    }
                }
            ]
        }
    }
}

 

term 쿼리

term의 경우 match와 비슷한 모습을 취하지만 term은 완전히 같은 데이터를 검색하는데 많이 사용됩니다. 저 같은 경우 주로 keyword타입에서 특정 단어를 검색하는데 많이 사용하고 있습니다.

 

keyword 타입 검색하기

level 필드의 값이 "info"인 값을 가진 도큐먼트를 검색합니다.

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "level": "info"
                    }
                }
            ]
        }
    }
}

 

range 쿼리

range 쿼리는 값의 범위를 검색하는데 사용됩니다. 

 

range쿼리의 파라미터

파라미터명 설명
gte 크거나 같다
gt 크다
lte 작거나 같다
lt 작다

 

범위를 이용하여 검색하기

user_id가 2보다 크거나 같으면서 7보다 작은 도큐먼트 검색하기

api POST http://localhost:9200/my-log-index-2021-08-29/_search
header Content-type: application/json
body {
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "user_id": {
                            "gte": "2",
                            "lt": "7"
                        }
                    }
                }
            ]
        }
    }
}

 

 

+ Recent posts