Elasticsearch를 사용하다 보면 가장 흔하게 마주치게 되는 에러 중 하나가 바로 NoNodeAvailableException
입니다. 이 에러는 클라이언트가 Elasticsearch 클러스터의 어떤 노드에도 연결할 수 없을 때 발생하는데, 생각보다 다양한 원인이 있어서 개발자들을 당황스럽게 만들기도 합니다. 실제로 운영 환경에서 이 에러를 만났을 때의 답답함을 저도 잘 알고 있습니다. 분명히 어제까지는 잘 작동했는데 갑자기 연결이 안 된다면? 이런 상황에서 체계적으로 문제를 해결할 수 있는 방법을 정리해보겠습니다.
1. 가장 흔한 원인: 포트 설정 오류
많은 개발자들이 놓치는 가장 기본적인 문제는 포트 번호 혼동입니다.
HTTP API vs Transport API 포트 차이점
Elasticsearch는 두 가지 다른 포트를 사용합니다:
- 9200번 포트: HTTP REST API용 (브라우저에서 접근 가능)
- 9300번 포트: Transport API용 (노드 간 통신 및 Java 클라이언트)
// ❌ 잘못된 예시 - HTTP 포트로 Transport 클라이언트 연결 시도
TransportClient client = new TransportClient()
.addTransportAddress(new InetSocketTransportAddress("localhost", 9200)); // 틀림!
// ✅ 올바른 예시 - Transport 포트 사용
TransportClient client = new TransportClient()
.addTransportAddress(new InetSocketTransportAddress("localhost", 9300)); // 맞음!
포트 확인 방법
현재 Elasticsearch가 어떤 포트를 사용하고 있는지 확인하려면:
# HTTP API로 확인
curl -X GET "localhost:9200/_nodes/_local/http?pretty"
# Transport 포트 확인
curl -X GET "localhost:9200/_nodes/_local/transport?pretty"
2. 클러스터 이름 불일치 문제
두 번째로 흔한 원인은 클러스터 이름이 맞지 않는 경우입니다.
클러스터 이름 확인하기
# 현재 클러스터 정보 확인
curl -X GET "localhost:9200/"
응답 예시:
{
"name" : "my-node-1",
"cluster_name" : "elasticsearch", // 이 값이 중요!
"cluster_uuid" : "abc123...",
"version" : {...}
}
Java 클라이언트에서 올바른 설정
// 클러스터 이름을 정확히 맞춰야 함
Settings settings = Settings.builder()
.put("cluster.name", "elasticsearch") // 위에서 확인한 이름과 일치해야 함
.build();
TransportClient client = TransportClient.builder()
.settings(settings)
.build()
.addTransportAddress(new InetSocketTransportAddress(
InetAddress.getByName("localhost"), 9300));
3. 버전 호환성 문제 해결
Elasticsearch 서버와 Java 클라이언트의 버전 불일치도 자주 발생하는 문제입니다.
버전 확인 방법
서버 버전 확인:
curl -X GET "localhost:9200/"
# 또는
./bin/elasticsearch --version
Java 클라이언트 버전 확인:
<!-- Maven pom.xml에서 확인 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.17.9</version> <!-- 이 버전이 서버와 일치해야 함 -->
</dependency>
호환성 규칙
Elasticsearch 서버 버전 | 권장 Java 클라이언트 |
---|---|
7.x | 7.x (동일 마이너 버전 권장) |
8.x | 8.x (동일 마이너 버전 권장) |
6.x | 6.x |
4. TransportClient에서 REST Client로 마이그레이션
중요한 업데이트: Elasticsearch 7.0부터 TransportClient가 deprecated되었고, 8.0에서는 완전히 제거되었습니다. 현재는 Java High Level REST Client 또는 Java API Client를 사용해야 합니다.
REST Client 사용 예시
// Elasticsearch 7.x용 High Level REST Client
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
// Elasticsearch 8.x용 Java API Client
ElasticsearchTransport transport = new RestClientTransport(
RestClient.builder(new HttpHost("localhost", 9200)).build(),
new JacksonJsonpMapper());
ElasticsearchClient client = new ElasticsearchClient(transport);
마이그레이션의 장점
- 더 안정적인 연결: HTTP 기반으로 네트워크 문제에 더 강함
- 버전 호환성: 서버와 클라이언트 버전 불일치 문제 감소
- 보안: HTTPS 지원이 더 쉬움
5. 네트워크 및 방화벽 설정
네트워크 환경에서 발생하는 문제들도 살펴보겠습니다.
방화벽 설정 확인
# Linux에서 포트 확인
sudo netstat -tlnp | grep :9300
sudo netstat -tlnp | grep :9200
# 방화벽 상태 확인 (Ubuntu/Debian)
sudo ufw status
# 포트 열기
sudo ufw allow 9200
sudo ufw allow 9300
Docker 환경에서의 주의사항
Docker로 Elasticsearch를 실행할 때:
# docker-compose.yml
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.9
ports:
- "9200:9200"
- "9300:9300" # Transport 포트도 열어야 함
environment:
- discovery.type=single-node
- network.host=0.0.0.0 # 외부 접근 허용
6. Sniffing 설정 문제
클라이언트의 sniffing 기능이 문제를 일으키는 경우가 있습니다.
Sniffing 비활성화
Settings settings = Settings.builder()
.put("cluster.name", "elasticsearch")
.put("client.transport.sniff", false) // sniffing 비활성화
.build();
Sniffing이 문제가 되는 상황
- NAT 환경에서 실행 중일 때
- 여러 네트워크 인터페이스가 있는 서버
- 클라우드 환경 (AWS, GCP 등)
7. ‘NoNodeAvailableException’ Error 발생시 단계별 확인 방법을 정리하자면…
문제가 발생했을 때 다음 순서로 확인해보세요:
1단계: 기본 연결 확인
# Elasticsearch가 실행 중인지 확인
curl -X GET "localhost:9200/"
# 응답이 없다면 Elasticsearch 재시작
sudo systemctl restart elasticsearch
2단계: 포트 및 클러스터 정보 확인
# 클러스터 이름과 포트 정보 확인
curl -X GET "localhost:9200/_nodes/_local?pretty"
3단계: Java 코드 검증
// 최소한의 연결 테스트 코드
public void testConnection() {
try {
Settings settings = Settings.builder()
.put("cluster.name", "your-cluster-name") // 실제 클러스터 이름
.put("client.transport.sniff", false)
.build();
TransportClient client = TransportClient.builder()
.settings(settings)
.build()
.addTransportAddress(new InetSocketTransportAddress(
InetAddress.getByName("localhost"), 9300));
// 간단한 클러스터 상태 확인
ClusterHealthResponse health = client.admin().cluster()
.prepareHealth().get();
System.out.println("연결 성공! 클러스터 상태: " + health.getStatus());
client.close();
} catch (Exception e) {
System.err.println("연결 실패: " + e.getMessage());
e.printStackTrace();
}
}
8. 추가적인 해결 방법 – 로그확인, 타임아웃 조정
로그 분석을 통한 진단
Elasticsearch 로그에서 연결 관련 정보 확인:
# 로그 파일 위치 확인
tail -f /var/log/elasticsearch/elasticsearch.log
# 또는 journalctl 사용
sudo journalctl -u elasticsearch -f
주목해야 할 로그 패턴:
"received message from unsupported version"
: 버전 불일치"failed to connect"
: 네트워크 연결 문제"cluster.name"
: 클러스터 이름 불일치
타임아웃 설정 조정
Settings settings = Settings.builder()
.put("cluster.name", "elasticsearch")
.put("client.transport.ping_timeout", "60s")
.put("client.transport.nodes_sampler_interval", "30s")
.build();
NoNodeAvailableException은 처음에는 복잡해 보일 수 있지만, 체계적으로 접근하면 대부분 해결할 수 있습니다. 가장 중요한 것은 포트 설정과 클러스터 이름을 정확히 맞추는 것이고, 가능하다면 최신 REST Client로 마이그레이션하는 것을 권장합니다. 실제 운영 환경에서는 이런 문제들을 미리 예방할 수 있도록 모니터링 시스템을 구축하고, 정기적인 연결 상태 확인을 하는 것이 좋습니다. 혹시 위의 방법들로도 해결이 안 된다면, Elasticsearch 버전과 환경 정보를 상세히 확인해보시기 바랍니다.