OpenSearch 3.1 클러스터 구축 가이드 (3-Node)
본 문서는 Ubuntu 24.04 환경에서 3대의 서버를 이용하여 OpenSearch 3.1 클러스터를 구축하고, 최종적으로 로드 밸런서(LB) 중심의 안정적인 프로덕션 아키텍처로 전환하는 전체 과정을 상세히 기술한다.
버전 히스토리
- v1.0: 개별 노드 직접 접속 방식의 초기 클러스터 구축
- v2.0: 로드 밸런서(LB)를 도입하여 고가용성 및 단일 접속점을 확보한 프로덕션 아키텍처로 전환
목차
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, Port9201, Path/, MethodGET
- 대상:
opensearch-dashboard타겟 그룹:- 대상:
10.0.10.8:5601 - 헬스 체크:
HTTPS, Port9201, Path/, MethodGET(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단계: 최종 전환 및 테스트
- LB 콘솔에서
opensearch-api와opensearch-dashboard타겟 그룹의 상태가 모두healthy로 바뀌는지 확인한다. - 모든 클라이언트의 접속 주소를
https://ds-opensearch.oneunivrs.com으로 변경한다. - API와 Dashboards 접속이 모두 정상적으로 이루어지는지 최종 확인한다.
curl -k -H "Authorization: Bearer <최종_토큰>" "https://ds-opensearch.oneunivrs.com:9200" - (선택 사항) 전환이 안정화되면, 개별 노드의 외부 IP를 제거하고 방화벽을 강화하여 보안 수준을 높인다.
부록: 운영 및 관리
주요 트러블슈팅 및 교훈
- 클러스터 형성 실패 (
cluster-manager not discovered):opensearch.yml의node.name과cluster.initial_cluster_manager_nodes목록의 이름이 불일치. 클러스터링 관련 설정의 이름은 정확히 일치해야 함. securityadmin.sh실행 실패:- 원인 1 (
InvalidKeySpecException):securityadmin.sh는 PKCS#8 형식의 개인키를 필요로 함.openssl pkcs8명령으로 변환하여 해결. - 원인 2 (포트 오류): OpenSearch 2.12 이후
securityadmin.sh는 REST 포트(9200)를 사용.
- 원인 1 (
- Dashboards 시작 실패: 2.x 버전 이후 변경된 설정 키 이름 문제 (
requestHeadersWhitelist,ssl.ca). 버전업 시 공식 문서의 Breaking Changes를 반드시 확인해야 함. - JWT 인증 실패 (
Unauthorized):- 근본 원인:
signing_key를 Base64 문자열 그대로 사용. - 해결:
signing_key를 Base64 디코딩한 바이너리 값을 실제 비밀키로 사용하여 토큰을 생성해야 함.
- 근본 원인:
- LB 헬스 체크 실패 (
503,401,405등):- 원인: 사용하는 LB가 헬스 체크 성공 기준으로
200 OK만 허용하는데, 보안이 활성화된 OpenSearch/Dashboards는 인증되지 않은 요청에200 OK를 반환하지 않음. - 해결: 모든 노드에 Nginx를 헬스 체크 전용 프록시로 설치. LB는 Nginx의
9201포트로 헬스 체크를 요청하고, Nginx는 무조건200 OK를 응답하여 문제를 우회함.
- 원인: 사용하는 LB가 헬스 체크 성공 기준으로
추가 권장 사항 및 팁
- 롤링 리스타트 시 샤드 재배치 중단:
/_cluster/settingsAPI를 통해cluster.routing.rebalance.enable을none으로 설정하면 재시작 속도를 높일 수 있음. - 인덱스 템플릿 및 ILM: 데이터가 많아지기 전에 Dashboards의
Index Management메뉴에서 인덱스 템플릿과 ILM(수명 주기 관리) 정책을 설정하여 운영을 자동화할 것. - 스냅샷 및 복구: 데이터 유실 방지를 위해 Dashboards의
Snapshots메뉴에서 외부 저장소(S3 등)로의 주기적인 백업을 반드시 설정할 것. - 방화벽 설정: 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.yml의 nodes_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 설정 시 반드시 사용하는 것이 좋다.