Files
opensearch/README.md
2025-09-15 13:35:51 +09:00

21 KiB

OpenSearch 3.1 클러스터 구축 가이드 (3-Node)

본 문서는 Ubuntu 24.04 환경에서 3대의 서버를 이용하여 OpenSearch 3.1 클러스터를 구축하고, 최종적으로 로드 밸런서(LB) 중심의 안정적인 프로덕션 아키텍처로 전환하는 전체 과정을 상세히 기술한다.

버전 히스토리

  • v1.0: 개별 노드 직접 접속 방식의 초기 클러스터 구축
  • v2.0: 로드 밸런서(LB)를 도입하여 고가용성 및 단일 접속점을 확보한 프로덕션 아키텍처로 전환

목차

  1. 사전 정보
  2. v1.0: 초기 클러스터 구축 (LB 미사용)
    1. 1단계: 설치
    2. 2단계: 사전 준비
    3. 3단계: OpenSearch 설정 (opensearch.yml)
    4. 4단계: JVM 및 시스템 설정
    5. 5단계: 보안 플러그인 설정
    6. 6단계: 클러스터 시작 및 적용
    7. 7단계: Dashboards 설정
    8. 8단계: Dashboards 시작 및 확인
    9. 9단계: JWT 인증 테스트
  3. v2.0: LB 중심 아키텍처로 전환
    1. 10단계: 아키텍처 목표
    2. 11단계: LB 준비 및 Nginx 프록시 구축
    3. 12단계: OpenSearch 및 Dashboards 재구성
    4. 13단계: 최종 전환 및 테스트
  4. 부록: 운영 및 관리
    1. 주요 트러블슈팅 및 교훈
    2. 추가 권장 사항 및 팁
    3. 사용자/역할 추가 (Dashboards UI)
    4. 인증서에서 정확한 DN 추출하기
    5. -nameopt RFC2253 옵션 상세 설명

1. 사전 정보

서버 사양 (3대 공통)

  • CPU: 8 vCPU
  • Memory: 65 GB
  • Disk: 2 TB SSD
  • OS: Ubuntu 24.04
  • SSH Port: 42894

노드 정보

항목 Node1 Node2 Node3
호스트네임 ds-opensearch001 ds-opensearch002 ds-opensearch003
외부 DNS (v1.0) ds-osearch001.oneunivrs.com ds-osearch002.oneunivrs.com ds-osearch003.oneunivrs.com
내부 DNS ds-osnode001.oneunivrs.com ds-osnode002.oneunivrs.com ds-osnode003.oneunivrs.com
Private IP 10.0.10.8 10.0.10.9 10.0.10.10

SSL 인증서

  • 종류: 와일드카드 *.oneunivrs.com
  • 초기 위치: Node1의 /data/cert/
  • 파일: oneunivrs.pem, root.pem, oneunivrs_key.pem
  • DN: C=KR, ST=Seoul, O="ONEUNIVERSE Co.,Ltd.", CN=*.oneunivrs.com

JWT 인증

  • 방식: 대칭키 (HS256)
  • 서명키: UGdiOTdLVjFBTWtndTRNRiZmVjdwMDdCRW1lSSUxTnA=

v1.0: 초기 클러스터 구축 (LB 미사용)

이 버전은 로드 밸런서 없이 각 노드에 직접 접속하는 방식의 기본 클러스터를 구축한다.

1단계: 설치

모든 작업은 root 계정으로 진행.

[모든 노드] APT 저장소 설정 후 OpenSearch 설치. 초기 admin 비밀번호 지정.

# APT 저장소 설정 (공식 문서 참조)

# OpenSearch 설치 (3.1.0)
env OPENSEARCH_INITIAL_ADMIN_PASSWORD='DHp5#r#GYQ9d' apt-get install opensearch=3.1.0

# Dashboards도 미리 설치
apt-get install opensearch-dashboards=3.1.0

2단계: 사전 준비

[모든 노드]

2.1. 데이터/로그 디렉토리 생성

mkdir -p /data/opensearch/{data,logs}
chown -R opensearch:opensearch /data/opensearch

2.2. 인증서 복사 및 권한 설정

[Node1]

mkdir -p /etc/opensearch/certs
cp /data/cert/*.pem /etc/opensearch/certs/

# 다른 노드로 전송
scp -P 42894 /etc/opensearch/certs/*.pem root@ds-osnode002.oneunivrs.com:/etc/opensearch/certs/
scp -P 42894 /etc/opensearch/certs/*.pem root@ds-osnode003.oneunivrs.com:/etc/opensearch/certs/

[모든 노드]

chown -R opensearch:opensearch /etc/opensearch/certs
chmod 600 /etc/opensearch/certs/oneunivrs_key.pem # 개인키 권한 축소
chmod 644 /etc/opensearch/certs/oneunivrs.pem /etc/opensearch/certs/root.pem

3단계: OpenSearch 설정 (opensearch.yml)

[모든 노드] 기존 파일 백업 후, 각 노드에 맞게 /etc/opensearch/opensearch.yml 작성.

# 클러스터 이름
cluster.name: ds-cluster

# [중요] node.name은 내부 DNS와 일치시킬 것 (클러스터링 실패 방지)
# Node1: node.name: ds-osnode001.oneunivrs.com
# Node2: node.name: ds-osnode002.oneunivrs.com
# Node3: node.name: ds-osnode003.oneunivrs.com
node.name: ds-osnode001.oneunivrs.com # 각 노드에 맞게 수정

# 역할
node.roles: [ cluster_manager, data ]

# 경로
path.data: /data/opensearch/data
path.logs: /data/opensearch/logs

# 메모리 잠금
bootstrap.memory_lock: true

# 네트워크
network.host: 0.0.0.0
http.port: 9200
transport.port: 9300

# 클러스터링
discovery.seed_hosts:
  - ds-osnode001.oneunivrs.com
  - ds-osnode002.oneunivrs.com
  - ds-osnode003.oneunivrs.com

# [중요] 최초 마스터 후보 목록. node.name과 일치해야 함.
cluster.initial_cluster_manager_nodes:
  - ds-osnode001.oneunivrs.com
  - ds-osnode002.oneunivrs.com
  - ds-osnode003.oneunivrs.com

# 보안 플러그인
plugins.security.ssl.transport.enabled: true
plugins.security.ssl.transport.pemcert_filepath: certs/oneunivrs.pem
plugins.security.ssl.transport.pemkey_filepath: certs/oneunivrs_key.pem
plugins.security.ssl.transport.pemtrustedcas_filepath: certs/root.pem
plugins.security.ssl.transport.enforce_hostname_verification: false

plugins.security.ssl.http.enabled: true
plugins.security.ssl.http.pemcert_filepath: certs/oneunivrs.pem
plugins.security.ssl.http.pemkey_filepath: certs/oneunivrs_key.pem
plugins.security.ssl.http.pemtrustedcas_filepath: certs/root.pem

# [주의] DN의 쉼표(,)는 백슬래시 두 개(\\)로 이스케이프
plugins.security.nodes_dn:
  - "CN=*.oneunivrs.com,O=ONEUNIVERSE Co.\\,Ltd.,ST=Seoul,C=KR"
plugins.security.authcz.admin_dn:
  - "CN=*.oneunivrs.com,O=ONEUNIVERSE Co.\\,Ltd.,ST=Seoul,C=KR"

plugins.security.allow_default_init_securityindex: true
plugins.security.audit.type: internal_opensearch
plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"]

4단계: JVM 및 시스템 설정

[모든 노드]

4.1. JVM 힙 메모리

/etc/opensearch/jvm.options 파일 수정. 31GB로 설정.

sed -i 's/^-Xms1g/#-Xms1g/' /etc/opensearch/jvm.options
sed -i 's/^-Xmx1g/#-Xmx1g/' /etc/opensearch/jvm.options
echo -e "\n-Xms31g\n-Xmx31g" >> /etc/opensearch/jvm.options

4.2. Systemd 오버라이드

메모리 잠금과 경로 권한 부여.

mkdir -p /etc/systemd/system/opensearch.service.d
cat <<EOF > /etc/systemd/system/opensearch.service.d/override.conf
[Service]
LimitMEMLOCK=infinity
ReadWritePaths=/data/opensearch/
EOF

systemctl daemon-reload
systemctl enable opensearch.service

5단계: 보안 플러그인 설정

[Node1에서 작업 후 다른 노드로 복사]

5.1. 인증 방식 설정 (config.yml)

/etc/opensearch/opensearch-security/config.yml 수정. JWT 우선, Basic 차선.

---
_meta:
  type: "config"
  config_version: 2
config:
  dynamic:
    http:
      anonymous_auth_enabled: false
    authc:
      # [중요] order: 0(JWT) -> order: 1(Basic)
      jwt_auth_domain:
        http_enabled: true
        order: 0
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "UGdiOTdLVjFBTWtndTRNRiZmVjdwMDdCRW1lSSUxTnA="
            jwt_header: "Authorization" # "Bearer " 접두사는 자동 처리됨
            subject_key: sub
            roles_key: roles
        authentication_backend:
          type: noop
      basic_internal_auth_domain:
        http_enabled: true
        order: 1
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: internal

5.2. 역할 매핑 (roles_mapping.yml)

/etc/opensearch/opensearch-security/roles_mapping.yml 수정.

# ... (기존 내용 유지)
all_access:
  reserved: false
  users:
    - "admin"      # 내부 사용자
  backend_roles:
    - "admin"      # JWT를 통해 온 사용자
# ... (기존 내용 유지)

5.3. 파일 복사 및 권한 설정

[Node1]

scp -P 42894 /etc/opensearch/opensearch-security/config.yml root@ds-osnode002.oneunivrs.com:/etc/opensearch/opensearch-security/
scp -P 42894 /etc/opensearch/opensearch-security/roles_mapping.yml root@ds-osnode002.oneunivrs.com:/etc/opensearch/opensearch-security/
scp -P 42894 /etc/opensearch/opensearch-security/config.yml root@ds-osnode003.oneunivrs.com:/etc/opensearch/opensearch-security/
scp -P 42894 /etc/opensearch/opensearch-security/roles_mapping.yml root@ds-osnode003.oneunivrs.com:/etc/opensearch/opensearch-security/

[모든 노드]

chown -R opensearch:opensearch /etc/opensearch
find /etc/opensearch -type d -exec chmod 750 {} \;
find /etc/opensearch -type f -exec chmod 640 {} \;
chmod 600 /etc/opensearch/certs/oneunivrs_key.pem
chmod -R 600 /etc/opensearch/opensearch-security/*

6단계: 클러스터 시작 및 적용

6.1. 클러스터 시작

마스터가 아닌 노드부터 순차적으로 시작.

systemctl start opensearch.service # Node3 -> Node2 -> Node1 순으로 실행

6.2. 개인키 변환 (PKCS#8)

securityadmin.sh는 PKCS#8 형식을 요구함. [Node1]

openssl pkcs8 -topk8 -inform PEM -outform PEM -in /etc/opensearch/certs/oneunivrs_key.pem -out /etc/opensearch/certs/oneunivrs_key.p8.pem -nocrypt
chown opensearch:opensearch /etc/opensearch/certs/oneunivrs_key.p8.pem
chmod 600 /etc/opensearch/certs/oneunivrs_key.p8.pem

6.3. 보안 설정 적용

[Node1]

# [중요] 3.1 버전은 REST 포트(9200)와 변환된 키(.p8.pem) 사용
/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \
  -cd /etc/opensearch/opensearch-security/ \
  -cacert /etc/opensearch/certs/root.pem \
  -cert /etc/opensearch/certs/oneunivrs.pem \
  -key /etc/opensearch/certs/oneunivrs_key.p8.pem \
  -h ds-osnode001.oneunivrs.com \
  -p 9200 \
  -icl \
  -nhnv

7단계: Dashboards 설정

[Node1]

7.1. 대시보드용 인증서 복사

mkdir -p /etc/opensearch-dashboards/certs
cp /etc/opensearch/certs/*.pem /etc/opensearch-dashboards/certs/
chown -R opensearch-dashboards:opensearch-dashboards /etc/opensearch-dashboards/certs

7.2. opensearch_dashboards.yml 설정

server.port: 5601
server.host: "0.0.0.0"
server.name: "oneunivrs-opensearch-dashboards"

# 고가용성을 위해 클러스터 노드 모두 기재
opensearch.hosts:
  - https://ds-osearch001.oneunivrs.com:9200
  - https://ds-osearch002.oneunivrs.com:9200
  - https://ds-osearch003.oneunivrs.com:9200

# [중요] 2.x 이후 버전의 설정 키 이름
opensearch.requestHeadersWhitelist: [ "securitytenant", "authorization" ]

# 대시보드 HTTPS
server.ssl.enabled: true
server.ssl.certificate: /etc/opensearch-dashboards/certs/oneunivrs.pem
server.ssl.key: /etc/opensearch-dashboards/certs/oneunivrs_key.pem

# [중요] 2.x 이후 버전의 설정 키 이름 (배열)
opensearch.ssl:
  verificationMode: full
  certificateAuthorities: [ "/etc/opensearch-dashboards/certs/root.pem" ]

# 서비스 계정
opensearch.username: "kibanaserver"
opensearch.password: "kibanaserver"

# 보안 연동
opensearch_security:
  multitenancy.enabled: true
  auth.anonymous_auth_enabled: false
  cookie.password: "강력하고_랜덤한_문자열_사용"

8단계: Dashboards 시작 및 확인

[Node1]

systemctl start opensearch-dashboards.service
systemctl enable opensearch-dashboards.service

브라우저에서 https://ds-osearch001.oneunivrs.com:5601 접속. admin / DHp5#r#GYQ9d 로그인 확인.

9단계: JWT 인증 테스트

9.1. 서버에서 직접 토큰 생성

외부 도구의 키 처리 방식 문제로 서버에서 직접 생성하는 것이 가장 확실. [Node1]

pip install pyjwt
vi create_token.py
# create_token.py
import jwt, time, base64
base64_secret = "UGdiOTdLVjFBTWtndTRNRiZmVjdwMDdCRW1lSSUxTnA="
# [핵심] Base64 디코딩
decoded_secret = base64.b64decode(base64_secret)
payload = { "sub": "admin", "roles": ["admin"], "exp": int(time.time()) + 3600 }
token = jwt.encode(payload, decoded_secret, algorithm="HS256")
print(token)
python3 create_token.py # 토큰 생성 후 복사

9.2. curl로 API 호출

curl -k -H "Authorization: Bearer <방금_생성한_토큰>" "https://ds-osearch001.oneunivrs.com:9200"

성공 응답 확인.


v2.0: LB 중심 아키텍처로 전환

초기 구축된 클러스터를 프로덕션 환경에 적합하도록 로드 밸런서(LB) 중심의 고가용성 아키텍처로 전환한다.

10단계: 아키텍처 목표

  • AS-IS: 클라이언트가 개별 노드(ds-osearch001 등)에 직접 접속.
  • TO-BE: 클라이언트는 LB의 단일 대표 주소(ds-opensearch.oneunivrs.com)에만 접속. 노드들은 내부망에 격리되어 보안 강화.

11단계: LB 준비 및 Nginx 프록시 구축

LB의 헬스 체크 제약(200 OK만 허용)을 우회하기 위해, 모든 OpenSearch 노드에 헬스 체크 전용 Nginx 프록시를 설치한다.

[모든 노드 (Node1, Node2, Node3)에서 실행]

# Nginx 설치
apt-get update && apt-get install nginx -y

# Nginx용 SSL 디렉토리 생성 및 인증서 복사
mkdir -p /etc/nginx/ssl
cp /etc/opensearch/certs/*.pem /etc/nginx/ssl/

# 기본 설정 비활성화
rm /etc/nginx/sites-enabled/default

# 헬스 체크용 설정 파일 생성
vi /etc/nginx/sites-available/opensearch-healthcheck

opensearch-healthcheck 파일에 아래 내용을 작성한다.

server {
    listen 9201 ssl;
    # [주의] IPv6 비활성화 환경에서는 아래 라인 주석 처리
    # listen [::]:9201 ssl;

    # 각 노드의 내부 DNS 또는 IP로 설정
    server_name ds-osnode001.oneunivrs.com; # Node2에서는 ds-osnode002...

    ssl_certificate /etc/nginx/ssl/oneunivrs.pem;
    ssl_certificate_key /etc/nginx/ssl/oneunivrs_key.pem;

    location / {
        # 헬스 체크 요청에 무조건 200 OK 응답
        return 200 'Healthy';
        add_header Content-Type text/plain;
        add_header Content-Length 7;
    }
}
# 설정 활성화 및 재시작
ln -s /etc/nginx/sites-available/opensearch-healthcheck /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx
systemctl enable nginx

12단계: OpenSearch 및 Dashboards 재구성

[로드 밸런서 설정]

  • 대표 DNS: ds-opensearch.oneunivrs.com을 생성하여 LB의 Public IP에 연결.
  • 리스너: HTTPS:9200 (API용), HTTPS:5601 (Dashboards용) 생성.
  • SSL 인증서: oneunivrs.com 인증서를 LB에 설치.
  • opensearch-api 타겟 그룹:
    • 대상: 10.0.10.8:9200, 10.0.10.9:9200, 10.0.10.10:9200
    • 헬스 체크: HTTPS, Port 9201, Path /, Method GET
  • opensearch-dashboard 타겟 그룹:
    • 대상: 10.0.10.8:5601
    • 헬스 체크: HTTPS, Port 9201, Path /, Method GET (API와 동일한 프록시 사용)

[Node1의 Dashboards 설정 변경] /etc/opensearch-dashboards/opensearch_dashboards.yml을 수정하여 LB를 바라보게 한다.

# opensearch.hosts를 새로운 LB 대표 DNS로 변경
opensearch.hosts: ["https://ds-opensearch.oneunivrs.com:9200"]

# [중요] LB <-> Dashboards 간 SNI 문제 해결을 위한 옵션 추가
opensearch.ssl:
  alwaysPresentCertificate: true
  verificationMode: full
  certificateAuthorities: [ "/etc/opensearch-dashboards/certs/root.pem" ]
# Dashboards 서비스 재시작
systemctl restart opensearch-dashboards.service

13단계: 최종 전환 및 테스트

  1. LB 콘솔에서 opensearch-apiopensearch-dashboard 타겟 그룹의 상태가 모두 healthy로 바뀌는지 확인한다.
  2. 모든 클라이언트의 접속 주소를 https://ds-opensearch.oneunivrs.com으로 변경한다.
  3. API와 Dashboards 접속이 모두 정상적으로 이루어지는지 최종 확인한다.
    curl -k -H "Authorization: Bearer <최종_토큰>" "https://ds-opensearch.oneunivrs.com:9200"
    
  4. (선택 사항) 전환이 안정화되면, 개별 노드의 외부 IP를 제거하고 방화벽을 강화하여 보안 수준을 높인다.

부록: 운영 및 관리

주요 트러블슈팅 및 교훈

  1. 클러스터 형성 실패 (cluster-manager not discovered): opensearch.ymlnode.namecluster.initial_cluster_manager_nodes 목록의 이름이 불일치. 클러스터링 관련 설정의 이름은 정확히 일치해야 함.
  2. securityadmin.sh 실행 실패:
    • 원인 1 (InvalidKeySpecException): securityadmin.sh는 PKCS#8 형식의 개인키를 필요로 함. openssl pkcs8 명령으로 변환하여 해결.
    • 원인 2 (포트 오류): OpenSearch 2.12 이후 securityadmin.sh는 REST 포트(9200)를 사용.
  3. Dashboards 시작 실패: 2.x 버전 이후 변경된 설정 키 이름 문제 (requestHeadersWhitelist, ssl.ca). 버전업 시 공식 문서의 Breaking Changes를 반드시 확인해야 함.
  4. JWT 인증 실패 (Unauthorized):
    • 근본 원인: signing_key를 Base64 문자열 그대로 사용.
    • 해결: signing_keyBase64 디코딩한 바이너리 값을 실제 비밀키로 사용하여 토큰을 생성해야 함.
  5. LB 헬스 체크 실패 (503, 401, 405 등):
    • 원인: 사용하는 LB가 헬스 체크 성공 기준으로 200 OK만 허용하는데, 보안이 활성화된 OpenSearch/Dashboards는 인증되지 않은 요청에 200 OK를 반환하지 않음.
    • 해결: 모든 노드에 Nginx를 헬스 체크 전용 프록시로 설치. LB는 Nginx의 9201 포트로 헬스 체크를 요청하고, Nginx는 무조건 200 OK를 응답하여 문제를 우회함.

추가 권장 사항 및 팁

  1. 롤링 리스타트 시 샤드 재배치 중단: /_cluster/settings API를 통해 cluster.routing.rebalance.enablenone으로 설정하면 재시작 속도를 높일 수 있음.
  2. 인덱스 템플릿 및 ILM: 데이터가 많아지기 전에 Dashboards의 Index Management 메뉴에서 인덱스 템플릿과 ILM(수명 주기 관리) 정책을 설정하여 운영을 자동화할 것.
  3. 스냅샷 및 복구: 데이터 유실 방지를 위해 Dashboards의 Snapshots 메뉴에서 외부 저장소(S3 등)로의 주기적인 백업을 반드시 설정할 것.
  4. 방화벽 설정: LB 전환 후 노드들의 외부 IP를 제거하고, OS 방화벽(UFW 등)을 사용해 신뢰할 수 있는 내부 IP 대역에서의 접속만 허용하여 보안을 강화할 것.

사용자/역할 추가 (Dashboards UI)

admin 계정으로 Dashboards에 로그인 후, Security 메뉴에서 YAML 파일 수정 없이 직관적으로 사용자, 역할, 역할 매핑을 관리할 수 있다. 일회성 작업은 UI를 사용하는 것이 편리하다.

  • 역할 생성: Security > Roles > Create role
  • 사용자 생성: Security > Internal Users > Create internal user
  • 역할 매핑: Security > Roles > (역할 선택) > Mapped users > Manage mapping

인증서에서 정확한 DN 추출하기

opensearch.ymlnodes_dn, admin_dn 설정 시, openssl 명령어로 정확한 DN을 추출하여 사용하면 실수를 방지할 수 있다.

# [권장] RFC2253 형식으로 출력
openssl x509 -in /data/cert/oneunivrs.pem -noout -subject -nameopt RFC2253

출력 예시: CN=*.oneunivrs.com,O=ONEUNIVERSE Co.\,Ltd.,ST=Seoul,C=KR opensearch.yml 적용 시: YAML 문자열 내에서 백슬래시(\)는 이스케이프해야 하므로 \\로 변경해야 한다. "CN=*.oneunivrs.com,O=ONEUNIVERSE Co.\\,Ltd.,ST=Seoul,C=KR"

-nameopt RFC2253 옵션 상세 설명

이 옵션은 기계가 파싱하기 좋은 표준 형식으로 DN을 출력한다.

  • subject= 같은 불필요한 접두사가 없다.
  • 쉼표(,)로만 구분되며 불필요한 공백이 없다.
  • 가장 중요: DN 값 자체에 포함된 특수 문자(예: Co.,Ltd.의 쉼표)를 백슬래시(\)로 자동으로 이스케이프 처리해준다.
  • OpenSearch 보안 플러그인은 이 형식을 가장 안정적으로 인식하므로, DN 설정 시 반드시 사용하는 것이 좋다.