프로젝트/여행지 오픈 API

[ElasticSearch] 엘라스틱서치 - Springboot 연동

블랑v 2023. 10. 30. 10:45

 

이슈

이번 프로젝트간 우리 서버는 총 두대이다. (편의상 본체 서버 A, 서브 서버 B로 부르겠다.)

ElasticSearch(특히 kibana)와 스크래퍼가 자원을 많이 먹기에, 보조 서버 B에 기능을 할당해놓은 상태이다.

그리고 주요 로직은 A 서버에 집중되어 있다.

따라서 A 서버의 springboot와 B 서버의 ElasticSearch를 연동해야 한다.

 

이 과정에서 자연스럽게 원격 접근을 위해 X-pack등의 보안 기능을 고려해야 한다는 점을 이제야 깨닫았다.

다행스럽게도 7.1~7.2 버전부터 해당 기능이 부분적으로 무료로 풀린 듯 하다.

 

7.2 version x-pack 공식 docs : https://www.elastic.co/guide/en/elasticsearch/reference/7.2/setup-xpack.html

 

Set up X-Pack | Elasticsearch Guide [7.2] | Elastic

 

www.elastic.co

 

기본 버전에서는 xpack.security.enabled가 false로 지정되어 있다.

 

yml config

sudo nano /etc/elasticsearch/elasticsearch.yml 를 통해 설정에 들어가보자.

 

대략적인 yml 내용은 다음과 같다.

Cluster 설정

  • cluster.name: my-application: 클러스터의 이름을 설정한다. 여러 노드를 하나로 묶을 때 사용

Node 설정

  • node.name: node-1: 노드의 이름을 설정한다. 클러스터 내에서 각 노드를 구분하기 위해 사용한다.
  • node.attr.rack: r1: 노드에 커스텀 속성을 추가한다. 이런 속성을 사용하여 특정 노드에 작업을 분배할 수 있다.

Paths 설정

  • path.data: /var/lib/elasticsearch: Elasticsearch 데이터가 저장될 디렉토리 경로를 설정한다.
  • path.logs: /var/log/elasticsearch: 로그 파일이 저장될 디렉토리 경로를 설정한다.

Memory 설정

  • bootstrap.memory_lock: true: 메모리를 시작 시 잠근다. 이를 통해 Elasticsearch가 swap 메모리를 사용하지 않게 한다.

Network 설정

  • network.host: 192.168.0.1: Elasticsearch가 바인딩할 네트워크 주소를 설정한다.
  • http.port: 9200: HTTP 통신을 위한 포트를 설정한다.

Discovery 설정

  • discovery.seed_hosts: ["host1", "host2"]: 클러스터 형성이나 노드 발견을 위한 초기 호스트 리스트를 설정한다.
  • cluster.initial_master_nodes: ["node-1", "node-2"]: 최초 마스터 노드 후보를 설정한다.

Gateway 설정

  • gateway.recover_after_nodes: 3: 전체 클러스터 재시작 후 몇 개의 노드가 시작되어야 복구를 시작할지 설정한다.

Various 설정

  • action.destructive_requires_name: true: 인덱스를 삭제할 때 명시적으로 이름을 지정해야 하는지 설정한다.

 

이정도 보았으니, 실제 설정을 해 보자.

우리가 건드려야 할 것은 크게 두 가지이다. 1. HTTP 설정, 2. 보안 설정

 

1. HTTP 설정

+ 수정

7.x 이상 버전부터 호스트와 노드 설정을 해야 한다고 한다.

이 부분을 추가해주자.

 

0.0.0.0은 모든 설정을 허가한다는 뜻이다. 보안을 위해 추가적으로 설정할 것. 참고로 저기 "가 빠진 것이 하나 보이는데 달아주어야 한다..

 

 

Springboot - gradle

깡통 서버 생성

 

 

https://velog.io/@eeheaven/SpringBoot-ElasticSearch-KnockKnock-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-0419Elasticsearch%EC%99%80-SpringBoot-%EC%84%9C%EB%B2%84-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0

 

[SpringBoot, ElasticSearch] KnockKnock 개발일지 - 0419(Elasticsearch와 SpringBoot 서버 연동하기)

참고한 링크 >SpringBoot + ElasticSearch 연동 및 간단 API 호출해보기 [프로그래밍/ElasticSearch elasticsearch + Spring] elasticsearch를 Java Spring에서 사용해보자 - 환경설정과 Index 만들기

velog.io

ES Date Repo docs : 버전에 따라 빠르게 바뀌는 듯 하다. 4.2 버전을 사용해야 한다.

https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#new-features

 

Spring Data Elasticsearch - Reference Documentation

The Spring Data infrastructure provides hooks for modifying an entity before and after certain methods are invoked. Those so called EntityCallback instances provide a convenient way to check and potentially modify an entity in a callback fashioned style. A

docs.spring.io

이 분의 블로그와, docs 공식 분서를 비교해가며 감을 잡았다.

공식 문서가 좋긴 하지만.. 처음부터 읽어가면서 코드를 짜기는 쉽지 않았다.

 

spring:
  elasticsearch:
    uris: http://127.0.0.1:9200
    username: "당신의 이름"
    password: "당신의 비밀번호"
    connection-timeout: 5s
    socket-timeout: 30s
    host: 127.0.0.1
    port: 9200

 

Config file

Docs 예제를 살펴보면서 구현해보자.

  1. ClientConfiguration 설정:
    • ClientConfiguration 객체를 생성하는 데 사용되는 빌더 패턴이다.
    • .connectedTo("localhost:9200"): Elasticsearch 서버의 주소를 설정한다. 
  2. RestHighLevelClient 생성:
    • RestClients.create(clientConfiguration).rest(): 위에서 설정한 clientConfiguration을 기반으로 RestHighLevelClient를 생성한다고 한다.
    • RestHighLevelClient는 Elasticsearch과 통신을 위한 고수준 클라이언트다.
  3. LowLevelClient 획득:
    • 통신용 고수준 클라이언트에서 저수준 클라이언트를 가져오는 듯 하다

고수준 클라이언트와 저수준은 다음과 같은 차이가 있다.

고수준의 경우 ElasticSearch를 쉽게 구현하는(직렬화나 메인 API를 쉽게 구현) 클라이언트이다. 주요 기능을 직관적이고 쉽게 사용할 수 있다.

저수준의 경우 반대로 조금 더 어렵지만, 유연하고 사용자 관점에서 원하는 기능을 추가할 수 있을 것이다.

 

 

연결 확인

@RestController
public class TestController {

    @Autowired
    private RestHighLevelClient client;

    @RequestMapping("test")
    public String Test() {
        try {
            MainResponse response = client.info(RequestOptions.DEFAULT);
            System.out.println("Elasticsearch 연결 성공: " + response.getVersion().toString());
        } catch (Exception e) {
            System.out.println("Elasticsearch 연결 실패: " + e.getMessage());
        }
        return "Well Done";
    }
}

내 AWS 위의 ElasticSearch와 연결됨을 확인했다.

 

일단 인덱스명으로 도큐먼트를 전부 꺼내보자

CRUD 연산 위주로 사용하고,

검색 기능 등의 고급 기능은 Kibana를 통해 직접 설정할 예정이기에 이번 프로젝트에서는 Spring Data Elasticsearch를 사용하려고 한다.

 

간단한 예제로 먼저 부딪혀보겠다.

 

 

https://tecoble.techcourse.co.kr/post/2021-10-19-elasticsearch/

 

Spring Data Elasticsearch 설정 및 검색 기능 구현

실습 Repository에서 코드를 확인할 수 있습니다. 1. Elasticsearch Elasticsearch는 Apache Lucene 기반의 Java 오픈소스 분산형 RESTful…

tecoble.techcourse.co.kr

 

Document

@Document(indexName = "accommodation") //인덱스 매핑
@Getter
public class Accommodation {
    @Id
    private String id;
    @Field(name = "accommodation_lng")
    private String accommodationLng;
    @Field(name = "accommodation_lat")
    private String accommodationLat;
    @Field(name = "accommodation_name")
    private String accommodationName; 
    @Field(name = "accommodation_type")
    private String accommodationType; 
    @Field(name = "accommodation_addr")
    private String accommodationAddr;
    @Field(name = "accommodation_pic")
    private String accommodationPic;
    @Field(name = "accommodation_score")
    private String accommodationScore;
    @Field(name = "accommodation_price")
    private String accommodationPrice;

}
service 로직

@Service
@Slf4j
public class AccommodationService {
    AccommodationRepository accommodationRepository;
    public AccommodationService(AccommodationRepository accommodationRepository) {
        this.accommodationRepository = accommodationRepository;
    }

    public Iterable<Accommodation> getAllResidences() {
        Iterable<Accommodation> accommodations = accommodationRepository.findAll();
        return accommodations;
    }

    public List<Accommodation> search(String input) {
        return accommodationRepository.findByAccommodationName(input);
    }
}

.findAll()로 요청했을 경우 결과값이 나오는 것을 확인할 수 있다.