티스토리 뷰

자사 서버 이관하면서 elasticsearch ver 7.x → ver 8.x 로 업그레이드 할 수 있는 기회가 생겼다.
지금부터 elasticsearch 를 업그레이드 하면서 겪었던 시행착오와 알게 된 정보들을 써보려 한다.

 

개요

시작하기 전, 용어/개념 정리부터 먼저 해야 되겠다. (ES는 공식 명칭이랑 객체명 동일하게 안 가져가는 건 좀 반성할 필요가 있음)

Elasticsearch 공식 명칭 객체명/동의어 비고
High Level Rest Client RestHighLevelClient, HLRC 기존에 많이 사용하던 고수준 추상화 client으로, RestClient 를 사용함
Java Low Level Rest Client RestClient, Low Level Client 저수준 추상화 client
Java API Client ElasticsearchClient es 7.15 버전부터 생겨난 새로운 고수준 추상화 client으로, RestClient 를 사용함

 

기존에 Java 환경과 es 통신을 쉽게 해주던 client 라이브러리인 RestHighLevelClient 가 7.15 버전부터 deprecated 됐다.

deprecated 된 이유는 유연성과 호환성이라고 하는데, 호환성이 메인 이유인 것 같다.
(elasticsearch 신규 버전이 나오면 기존의 client 를 신규 버전에 맞춰서 유지보수에 비용이 많이 드는데 7.10 이전버전들은 무료이다보니 과감히 이전 버전은 지원하지 않고 신규 버전만 지원하는 새로운 client 를 만든 것이 아닐까 싶다)

 

 

 

자사 서비스는 모든 es 통신을 rest high level client 로 하고 있어 전체 코드를 변경하기에는 너무 큰 공수가 들기 때문에 아래와 같이 진행하기로 했다.

1) 기존의 rest high level client는 es 8버전대와 호환가능하게끔 설정하고

2) 추후 더이상 rest high level client가 호환되지 않는 es로 버전업할 훗날을 대비해 새로운 java api client를 만들었다.

(작업은 Migrating from the High Level Rest Client document를 보면서 진행했다)

 


기존의 RestHighLevelClient 와 ES8 버전 호환

기존의 rest high level client 를 es 8버전대와 호환 가능하게끔 설정하는 방법은 아래와 같다.

1) rest high level client 7.17 버전부터 ES8 버전과 호환가능하다고 하니 rest high level client 버전을 7.17 버전으로 업그레이드

2) 공식문서에 나온 것 같이 apiCompatibilityMode = true 로 설정해주면 끝.

RestHighLevelClient esClient = new RestHighLevelClientBuilder(restClient)
    .setApiCompatibilityMode(true)
    .build()

 


새로운 Java Api Client 설정

새로운 Java Api Client 를 설정하는 방법은 아래와 같다.

1) Elasticsearch client 를 설정해줬던 config 내에 새로운 Java Api Client 를 만들어준다.

2) 기존의 RestHighLevelClient에서 사용하는 RestClient 를 Java Api Client에도 설정해주면 끝.

// Create the low-level client
RestClient httpClient = RestClient.builder(
    new HttpHost("localhost", 9200)
).build();

// Create the HLRC
RestHighLevelClient hlrc = new RestHighLevelClientBuilder(httpClient)
    .setApiCompatibilityMode(true) 
    .build();

// Create the Java API Client with the same low level client
ElasticsearchTransport transport = new RestClientTransport(
    httpClient,
    new JacksonJsonpMapper()
);

ElasticsearchClient esClient = new ElasticsearchClient(transport);

// hlrc and esClient share the same httpClient

 


Security 설정

es 8버전부터 security 설정이 기본적으로 활성화됐다. (이전에는 security default 설정 = false)

security 설정을 비활성화 할 수도 있을 거 같은데 security 설정을 하도록 지침이 내려왔기 때문에 설정을 해줬다.

 

elasticsearch.yml 내 security 설정을 확인해보자.

xpack.security.http.ssl.enabled : true (HTTP 계층 / 클라이언드 - es 통신)

xpack.security.transport.ssl.enabled : true (Transport 계층 / es 노드 간 통신)

여기에 추가로 default 값이 true인 아래 설정을 잊지 말아야 한다.

(나는 ssl 설정만 해주고 테스트 해봤는데 connection fail 하길래 document 로 찾아보고 나서 알았음)

xpack.security.authc.api_key.enabled : true

결론 : 서비스(클라이언트)에는 API key 설정과 SSL 설정을 해줘야 한다. 


📍 API key 설정

es REST API 통신하기 위해 필요한 API key를 설정해보자.

kibana에서 설정하는 방법api로 설정하는 방법이 있는데 링크를 걸어뒀으니 확인해보삼.

 

기본적으로 이 document 에서 말하는 방법으로 API key를 생성하는 것으로
아래 형식의 req body로 요청을 보내면 해당 security 설정으로 API key 가 생성된다.

{
  "name": "my-api-key", // api key 이름 (수정불가)
  "expiration": "1d",   // 만료기한
  "role_descriptors": { // 여기가 이제 api key에 대한 설정
    "role-a": {	// role 이름
      "cluster": ["all"],	// 적용할 클러스터
      "indices": [	// 적용할 index
        {
          "names": ["index-a*"],	// index name
          "privileges": ["read"]	// index 에 대한 권한
        }
      ]
    },
    "role-b": {
      "cluster": ["all"],
      "indices": [
        {
          "names": ["index-b*"],
          "privileges": ["all"]
        }
      ]
    }
  },
  "metadata": {	// 메타 데이터
    "application": "my-application",
    "environment": {
      "level": 1,
      "trusted": true,
      "tags": ["dev", "staging"]
    }
  }
}

 

 

위 형식으로 req body 로 요청을 하고 정상적으로 API key 가 등록됐다면 아래와 같은 resp body를 확인할 수 있다.

{
  "id": "VuaCfGcBCdbkQm-e5aOx",        
  "name": "my-api-key",
  "expiration": 1544068612110,         
  "api_key": "ui2lp2axTNmsyakw9tvNnw", 
  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="  
}

 

생성한 API key의 encoded 값을 사용하는 es client 의 default header 값으로 설정해주면 끝.

RestClient restClient = RestClient
            .builder(new HttpHost(host, port, "https"))
            .setDefaultHeaders(new Header[]{
                    new BasicHeader("Authorization", "ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==") // elasticsearch 공식 문서에 있는 샘플 api key 임. 내꺼 아님.
            })
            .build();

 


📍 SSL 설정

여기 블로그를 보면 인증서를 가져올 필요 없이 finger print 값을 구해서 적용하면 된다고 하는데
아래 상황일 때 fingerprint 적용 가능한 것 같다. 

  • es 인증서로 http_ca.crt 등록
  • 'openssl x509 -fingerprint -sha256 -in config/certs/http_ca.crt' 명령어로 확인

나는 상황이 달라서 finger print 로는 적용하지 못했고, 직접 es 인증서를 가져와 client 에 ssl 설정을 해줬다.

아래는 그 방법을 기재했다.

 

1) xpack.security.http.ssl.keystore.path 로 지정된 파일을 로컬로 가져온다.

elasticsearch.yml 설정을 다시 한번 보자.

xpack.security.http.ssl.enabled : true

xpack.security.http.ssl.keystore.path : (인증서 파일이 있는 path)

해당 path로 가서 지정된 인증서를 로컬로 복사해온다.

 

2) es HttpHost 에 "https" 설정을 해준다.

HttpHost host = new Host('elastic host', 9200, "https");

 

3) restClient 내 ssl 설정을 해준다.

 

// ssl 설정만 한 코드 (ssl 설정 또한 샘플코드로 대체함)

@Bean
public RestClient restClient() {
	RestClient restClient = RestClient
		.builder(new HttpHost(host, port, "https"))
		.setDefaultHeaders(new Header[]{
			new BasicHeader("Authorization", "ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==") // elasticsearch 공식 문서에 있는 샘플 api key 임. 내꺼 아님.
		})
		.setHttpClientConfigCallback(
			httpClientBuilder -> httpClientBuilder
				.setSSLContext(sslContext())
				.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)	// 사설인증서를 사용하면 hostname verify 옵션을 이 코드로 off
		)
		.build();

	return restClient;
}

@Bean
public SSLContext sslContext(){
	// properties 내 설정한 classpath 를 인식하도록 resolver 사용
    // jar 내 path를 확인할 수 있도록 path를 InputStream로 설정
	PathMatchingResourcePatternResolver resolver = resolver.getResource();
	InputStream certInputStream = resolver.getResource('properties 내 classpath: 로 지정된 인증서 path (설정하셈)').getInputStream();
	KeyStore truststore = KeyStore.getInstance("pkcs12");
	truststore.load(certInputStream, 'es password (설정하셈)'.toCharArray());
	return SSLContexts.custom()
			.loadTrustMaterial(truststore, null)
			.build();
}

 

 

참고

https://jjeong.tistory.com/1487

https://mntdev.tistory.com/123

 

 

'정리노트 > 기타' 카테고리의 다른 글

Springboot 마이그레이션 수행기  (0) 2025.04.12
[Kafka] Kafka 기본 개념  (0) 2023.06.21
네트워크 기초  (0) 2023.05.31
ETL 프로세스 / 하둡 / 스파크  (0) 2022.09.13
Elastic Search (+ ELK 스택)  (1) 2022.09.13
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함