Chef.Yeon
Code Cook
Chef.Yeon
전체 방문자
오늘
어제
  • 분류 전체보기 (230)
    • 게임 개발 (1)
      • Unity (1)
    • Android (27)
      • Kotlin (19)
      • 우아한테크코스 5기 (4)
    • Language (11)
      • 파이썬 (3)
      • Java (7)
    • DB (2)
      • SQL (16)
    • Spring (25)
    • 코딩테스트 (56)
    • Git (1)
    • TIL (85)
    • DevOps (6)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 우아한테크코스
  • spring
  • grafana
  • java
  • Wil
  • 백준
  • elasticsearch
  • 파이썬
  • Android
  • 프로그래머스
  • kibana
  • 코틀린 인 액션
  • rsocket
  • 코딩테스트
  • webflux
  • 안드로이드
  • 코틀린
  • SQL
  • MariaDB
  • 에라토스테네스의 체
  • enum
  • til
  • 내림차순
  • 레포지토리
  • 다이나믹 프로그래밍
  • ec2
  • kotlin
  • Docker
  • 프리코스
  • 문자열

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Chef.Yeon

Code Cook

Spring

[Spring] Spring Boot3.x Docker Compose로 ElasticSearch 8.x+Kibana 구성 (Local)

2024. 8. 11. 02:53

프로젝트 루트 디렉토리에 docker-compose.yml을 생성한다.

 

1. Docker Compose 파일 작성

docker-compose.yml

version: '3.7'

services:
  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.7.1
    container_name: es
    environment:
      - node.name=es-node
      - cluster.name=search-cluster
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - xpack.security.enabled=false
      - xpack.security.http.ssl.enabled=false
      - xpack.security.transport.ssl.enabled=false
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es-data:/usr/share/elasticsearch/data
    ports:
      - 9200:9200 # https
      - 9300:9300 #tcp
    networks:
      - es-bridge

  kibana:
    image: docker.elastic.co/kibana/kibana:8.7.1
    container_name: kibana
    environment:
      SERVER_NAME: kibana
      ELASTICSEARCH_HOSTS: http://es:9200
    ports:
      - 5601:5601
    depends_on:
      - es
    networks:
      - es-bridge

volumes:
  es-data:
    driver: local

networks:
  es-bridge:
    driver: bridge

 

version: Docker Compose 파일 버전 지정

services: Docker Compose에서 정의하는 개별 서비스

volumes: 컨테이너와 호스트 간 데이터 저장/공유를 위한 볼륨 정의

networks: 서비스들이 통신할 네트워크 정의

 

- ElasticSearch 서비스 (es)

discovery.type=single-node: Elasticsearch를 단일 노드 모드로 실행

bootstrap.memory_lock=true: 메모리 스왑 방지

ES_JAVA_OPTS=Xms1g -Xmx1g: JVM 최소 힙 메모리 크기와 최대 힙 메모리 크기를 1GB로 설정

 

로컬 개발 환경에서 실행하기 위해 다음과 같이 Xpack 보안 기능과 SSL 설정을 비활성화 했다.

xpack.security.enabled=false

xpack.security.http.ssl.enabled=false

xpack.security.transport.ssl.enabled=false

 

ulimits:memlock: 메모리 락 한도 설정 (-1은 무제한)

 

- Kinaba 서비스

ELASTICSEARCH_HOSTS: 연결한 Elasticsearch 호스트 지정

  • ElasticSearch 서비스를 es라고 정의해두었으므로 http://es:9200로 연결
  • 만약 서비스 이름이 elasticsearch라면 http://elasticsearch:9200로 연결

depends on: Kibana 서비스가 es 서비스에 종속됨

  • es 서비스 시작된 후에 Kibana 시작

 

- 네트워크 정의

networks:es-bridge: 두 서비스 간의 통신을 위한 브리지 네트워크

 

터미널에서 docker-compose up 명령을 통해 실행하고, 정상적으로 실행되는지 확인해보자.

 

http://localhost:9200

 

http://localhost:5601

 

2. Nori 설치

Nori는 한글 형태소 분석기로 카카오에서 개발하여 Elasticsearch에서 공식 지원한다.

cmd를 열어 다음 명령어를 입력해주어 설치하자.

# es container 확인
$ docker ps 

# es컨테이너 접속
$ docker exec -it {containerId} /bin/bash

$ cd /usr/share/elasticsearch/bin/

$ ./elasticsearch-plugin install analysis-nori

 

 

docker compose down 후, 재실행한다.

 

3. ElasticSearch 설정 및 서비스 구현

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

 

application.yml

spring:
  elasticsearch:
    host: localhost:9200

 

ElasticSearchConfig

@Configuration
@EnableElasticsearchRepositories
public class ElasticSearchConfig extends ElasticsearchConfiguration {
    @Value("${spring.elasticsearch.host}")
    private String host;

    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo(host)
                .build();
    }
}

@EnalbeElasticsearchRepositories: elastic 레포지토리 지원 활성화

 

Post

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post extends TimeStamped {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "post_id")
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @Builder
    public Post(String username, String title, String content) {
        this.username = username;
        this.title = title;
        this.content = content;
    }
}

 

PostDocument

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Document(indexName = "posts", writeTypeHint = WriteTypeHint.FALSE)
@Setting(settingPath = "elastic/post-setting.json")
@Mapping(mappingPath = "elastic/post-mapping.json")
public class PostDocument {
    @Id
    private Long id;
    private String username;
    private String title;
    private String content;

    @Builder
    public PostDocument(Long id, String username, String title, String content) {
        this.id = id;
        this.username = username;
        this.title = title;
        this.content = content;
    }

    public static PostDocument from(Post post) {
        return PostDocument.builder()
                .id(post.getId())
                .username(post.getUsername())
                .title(post.getTitle())
                .content(post.getContent())
                .build();
    }
}

@Document

Elasticsearch 인덱스를 지정하고, 해당 클래스가 Elasticsearch 도큐먼트로 매핑되도록 정의

  • indexName: Elasticsearch 인덱스 이름 지정 (필수)
  • writeTypeHint: 도큐먼트 타입 힌트작성 여부 (기본값: true)
    • 기본적으로 Spring Data Elasticsearch는 도큐먼트에 '_class' 필드 생성한다.
  • createIndex: 인덱스 자동 생성 여부 (기본값: true)
    • true로 설정된 경우, Spring Data Elasticsearch가 애플리케이션 시작시 @Document에 의해 생성된 인덱스가 있는지 확인하고 없으면 생성한다.
  • shards, replicas, refreshInterval, versionType

@Mapping

Elasticsearch 인덱스의 매핑을 정의하는 JSON 파일의 경로 지정

(런타임 필드로 인덱스 매핑을 정의하면, 클래스 경로에 해당 JSON 파일의 경로를 설정해야한다.)

post-mapping.json 파일은 resources/elastic 폴더 하위에 있다.

 

@Setting

Elasticsearch 인덱스의 설정을 정의하는 JSON 파일 경로 지정

post-setting.json 파일은 resources/elastic 폴더 하위에 있다.

 

post-setting.json

인덱스의 분석기, 토크나이저와 같은 설정을 포함할 수 있다.

{
  "analysis": {
    "analyzer": {
      "korean": {
        "type": "nori"
      }
    }
  }
}

 

post-mapping.json

필드의 데이터 타입(text, keyword 등), 분석기 설정, 필드 속성 등이 정의된다.

{
  "properties": {
    "id": {
      "type": "long"
    },
    "username": {
      "type": "text",
      "analyzer": "korean"
    },
    "title": {
      "type": "text",
      "analyzer": "korean"
    },
    "content": {
      "type": "text",
      "analyzer": "korean"
    }
  }
}

 

PostSearchController

@RestController
@RequestMapping("api/v1/posts")
@RequiredArgsConstructor
public class PostSearchController {
    private final PostSearchService postSearchService;

    @GetMapping("/search")
    ResponseEntity<List<PostDocument>> search(@RequestParam String keyword) {
        return ResponseEntity.ok(postSearchService.searchByKeyword(keyword));
    }
}

 

PostService

@Service
@RequiredArgsConstructor
public class PostService {
    private final PostElasticRepository postElasticRepository;

    public Post add(PostRequestDto request) {
        Post post = Post.builder()
                .username(request.getUsername())
                .title(request.getTitle())
                .content(request.getContent())
                .build();
        postRepository.save(post);
        postElasticRepository.save(PostDocument.from(post));
        return post;
    }
}

 

수정 기능은 postElasticRepository.save를 통해 PostDocument를 다시 저장해주면 해당 id이 document가 수정된다.

삭제 기능은 postElasticRepository.deleteById 또는 delete를 사용해 삭제할 수 있다.

 

PostSearchService

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class PostSearchService {
    private final PostElasticRepository postElasticRepository;

    public List<PostDocument> searchByKeyword(String keyword) {
        return postElasticRepository.findByTitle(keyword);
    }
}

 

PostElasticRepository

@Repository
public interface PostElasticRepository extends ElasticsearchRepository<PostDocument, Long> {
    List<PostDocument> findByTitle(String title);
}

 

ElasticsearchRepository는 Spring Data Elasticsearch에서 지원한다.

기본적인 CRUD가 지원되고, 메서드 이름을 기반으로 쿼리를 자동 생성할 수 있다. (Query mehtods)

findByTitle은 파라미터가 '제목'일 때 다음과 같은 elasticsearch query가 생성된다.

{
  "query": {
    "match": {
      "title": "제목"
    }
  }
}

4. API 테스트

Postman과 Kibana를 사용하여 Elasticsearch에 저장된 데이터를 확인하고, 데이터를 검색해보자.

 

우선애플리케이션을 실행하고,  데이터 확인을 위해 Analytics(Kibana이다) > Discover로 이동해 DataView를 만들어준다.



만약 date 필드가 있으면 Timestamp field를 설정해주자.

 

Postman을 사용해 요청을 보내보자.

 

DataView에서 Refresh 버튼을 클릭하면, 저장된 데이터를 확인할 수 있다. 

 

PostMan으로 요청을 보내서 Elasticsearch 검색 기능 동작을 확인해보자.

 

 

728x90

'Spring' 카테고리의 다른 글

[Spring] Elastic Cloud + Spring Boot 3.x 연동하기  (0) 2024.08.23
[Spring] Spring Boot 프로젝트 이름 변경하기  (0) 2024.08.08
[WebFlux] Reactive Streams  (0) 2023.08.09
[Spring] 의존성 주입(DI: Dependency Injection)  (0) 2023.07.22
[Spring] Junit5 테스트 No tests found for given includes 오류 해결  (0) 2023.06.21
    'Spring' 카테고리의 다른 글
    • [Spring] Elastic Cloud + Spring Boot 3.x 연동하기
    • [Spring] Spring Boot 프로젝트 이름 변경하기
    • [WebFlux] Reactive Streams
    • [Spring] 의존성 주입(DI: Dependency Injection)
    Chef.Yeon
    Chef.Yeon
    보기 좋고 깔끔한 코드를 요리하기 위해 노력하고 있습니다.

    티스토리툴바