GCP에 ElasticSearch 띄워서 검색 기능 구현하기

2024. 12. 4. 22:02·Dev
목차
  1. intro
  2. elasticsearch를 사용한 이유
  3. Tool
  4. 레시피 db에 저장하기
  5. GCP에 elasticsearch 서버 구축하기
  6. spring batch로 수집한 데이터 ELK로 전송하기
  7. elasticSearch에 저장된 데이터 조회 api 만들기

intro

앱 기능 중 음식명으로 레시피를 조회하는 기능이 있습니다. 이 검색 기능에는 ElasticSearch를 사용해서 구현했습니다. ElasticSearch를 사용하면 성능이 매우 좋다는 정보를 많이 접했기 때문에 이를 기반으로 구현해 보았습니다.

elasticsearch를 사용한 이유

엘라스틱서치를 검색 엔진으로 사용할 때 독보적인 성능을 가지고 있습니다. 그 이유는 데이터를 저장하는 방식이 rdb와는 다른 방식을 사용해서 저장을 하고 있습니다.
엘라스틱 서치에서는 색인이라고 표현하는데 rdb에서 인덱스라고 표현하는 것을 역인덱스 구조로 저장하는 방식입니다.
키워드에 인덱스를 주어지게 되어서 검색할 때 키워드를 이용해서 찾기 때문에 빠른 검색을 할 수 있습니다.

Tool

  • Spring 2.7.7
  • ElasticSearch 7.17.3
  • Google Cloud Service
  • GCP에서 무료 크레딧을 제공해주어서 GCP를 이용해서 서버를 구축해 보았습니다.*

 

원격 접속해서 docker를 이용해서 ElasticSearch 서버를 구축했습니다

ssh 키 만들어주기

$ ssh-keygen -t rsa -f ~/.ssh/[키이름] -C [gmail계정] -b 4096

 

생성된 pub 파일 내용을 메타 데이터에 ssh키 등록해주기

ssh -i [private 키파일경로] [계정]@[외부IP]

레시피 db에 저장하기

만개의 레시피 데이터를 json 파일로 db에 저장하기 쉽게 만들어 두었습니다.

  • 일단 local에서 테스트 하기 위해 데이터를 db에 저장해주었습니다.

GCP에 elasticsearch 서버 구축하기

위에서 생성한 GCP 인스턴스에 원격접속해서 docker를 이용해서 elasticsearch 컨테이너를 실행해주었습니다.

 

도커 설치

//설치
sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io

//실행
sudo systemctl enable docker
sudo service docker start

//확인
sudo service docker status

 

elasticsearch, kibana 설치

//자신에게 맞는 버전 설치
//elasticsearch
sudo docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.3
sudo docker volume create elasticsearch-volume
sudo docker run -d -p 9200:9200 -e "discovery.type=single-node" --name elasticsearch docker.elastic.co/elasticsearch/elasticsearch:7.17.3
sudo docker ps

//kibana
sudo docker pull docker.elastic.co/kibana/kibana:7.17.3
sudo docker run -d --link elasticsearch:elasticsearch -p 5601:5601 docker.elastic.co/kibana/kibana:7.17.3
sudo docker ps

 

잘 작동하는지 확인하기 위해 gcp 9200포트와 5601포트를 열어보았습니다

 

 

Nori Tokenizer 설치
한글을 형태소 단위로 분석하여 검색을 할 수 있게 지원해주는 Nori tokenizer를 설치해 주었습니다.

sudo docker exec -it {elasticsearch 컨테이너 이름} /bin/bash
./bin/elasticsearch-plugin install analysis-nori

 

보안설정

//elasticsearch 컨테이너 접속
sudo docker exec -it elasticsearch /bin/bash
vi /usr/share/elasticsearch/config/elasticsearch.yml
//xpack.security.enabled: true 
//xpack.security.transport.ssl.enabled: true 추가 후

// /usr/share/elasticsearch/bin 폴더에서 아래 코드 실행후 비밀번호 설정
elasticsearch-setup-passwords interactive

//kibana 컨테이너 접속
sudo docker exec -it {kibana 커넽이너 이름} /bin/bash
vi config/kibana.yml
//elasticsearch.username: "elastic"
//elasticsearch.password: "비밀번호" 추가하기

 

암호가 생긴 것을 확인할 수 있습니다

 

 

spring batch로 수집한 데이터 ELK로 전송하기

주기적으로 db에 있는 레시피 데이터를 elasticsearch에 저장하기 위해서 spring batch를 사용했습니다.

@Slf4j
@Configuration
@EnableBatchProcessing
@AllArgsConstructor
public class ElasticBatchConfiguration {

    private final JobBuilderFactory jobBuilderFactory; 
    private final StepBuilderFactory stepBuilderFactory; 
    private final RecipeRepository recipeRepository;
    private final RecipeElasticRepository recipeDocumentRepository;

    @Bean
    public ItemReader<Recipe> reader() {
        LocalDateTime lastProcessedDate = getLastProcessedDate(); // 마지막 처리 시점 가져오기
        return new RepositoryItemReaderBuilder<Recipe>()
                .repository(recipeRepository)
                .methodName("findUpdatedAfter")
                .arguments(lastProcessedDate)
                .pageSize(200)
                .sorts(Collections.singletonMap("id", Sort.Direction.DESC))
                .name("recipeItemReader")
                .build();
    }

    @Bean
    public ItemProcessor<Recipe, RecipeDocument> processor() {

        return recipe -> RecipeDocument.builder()
                    .id(recipe.getId().toString())
                    .name(recipe.getName())
                    .info(recipe.getInfo())
                    .description(recipe.getDescription())
                    .ingredient(recipe.getIngredient())
                    .recommendCount(recipe.getRecommendCount())
                    .bookmark(recipe.getBookmark())
                    .build();

    }

    @Bean
    public ItemWriter<RecipeDocument> writer() {
        return items -> {
            log.info("Writing {} items to ElasticSearch", items.size());
            recipeDocumentRepository.saveAll(items);
        };
    }

    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor("batch_task_executor");
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .<Recipe, RecipeDocument>chunk(200)
                .reader(reader())
                .processor(processor())
                .writer(writer())
                .taskExecutor(taskExecutor()) // 병렬 처리
                .throttleLimit(4) 
                .build();
    }



    @Bean
    public Job importRecipeJob(JobRepository jobRepository) {
        return jobBuilderFactory.get("importRecipeJob")
                .incrementer(new RunIdIncrementer())
                .preventRestart()
                .flow(step1())
                .end()
                .build();
    }
}

 

elasticSearch에 데이터가 잘 저장된 것을 확인할 수 있습니다.

elasticSearch에 저장된 데이터 조회 api 만들기

 

elasticSearch 연결을 위한 config 추가

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.sm.project.elasticsearch.repository")
public class ElasticConfig {

    @Value("${spring.elastic.url}")
    private String elasticUrl;

    @Value("${spring.elasticsearch.username}")
    private String username;

    @Value("${spring.elasticsearch.password}")
    private String password;

    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo(elasticUrl)
                .withBasicAuth(username,password)
                .build();

        return RestClients.create(clientConfiguration).rest();
    }

}

RecipeDocument를 조회할 수 있는 Elasticsearch Repository를 작성합니다.

@Repository("elasticSearchRepository")
public interface RecipeElasticRepository extends ElasticsearchRepository<RecipeDocument, Long> {
    List<RecipeDocument> findByName(String name);
}

재료를 이용해서 검색했을때 추천수 기준으로 정렬해서 5개의 레시피를 보여주도록 로직을 작성합니다.

@Service
@AllArgsConstructor
@Transactional
public class RecipeService {

    private final RecipeElasticRepository recipeDocumentRepository;


    // 추천수 높은 순으로 페이징된 레시피를 조회하는 메소드
    public Page<RecipeDocument> findTopRecipes(int lastIndex, int limit) {
        Pageable pageable = PageRequest.of(lastIndex / limit, limit, Sort.by(Sort.Direction.DESC, "recommendCount"));
        return recipeDocumentRepository.findAllByOrderByRecommendCountDesc(pageable);
    }
    //검색한 재료로 레시피를 조회하는 메소드
    public Page<RecipeDocument> searchByIngredient(String ingredient, int lastIndex, int limit) {
        Pageable pageable = PageRequest.of(lastIndex / limit, limit, Sort.by(Sort.Direction.DESC, "recommendCount"));
        return recipeDocumentRepository.findByIngredientContainingOrderByRecommendCountDesc(ingredient, pageable);
    }
}

 

떡으로 검색한 결과가 잘 나온 것을 확인할 수 있습니다.

 

 

'Dev' 카테고리의 다른 글

상품 주문하기 동시성 문제 해결하기  (0) 2024.12.04
과도한 트래픽에 대한 방어하기  (1) 2024.12.04
위치 기반으로 글 조회 기능 구현  (1) 2024.12.04
fcm 이용해서 앱 푸쉬 구현  (0) 2024.12.04
소켓 통신(채팅방 구현)  (0) 2024.12.04
  1. intro
  2. elasticsearch를 사용한 이유
  3. Tool
  4. 레시피 db에 저장하기
  5. GCP에 elasticsearch 서버 구축하기
  6. spring batch로 수집한 데이터 ELK로 전송하기
  7. elasticSearch에 저장된 데이터 조회 api 만들기
'Dev' 카테고리의 다른 글
  • 상품 주문하기 동시성 문제 해결하기
  • 과도한 트래픽에 대한 방어하기
  • 위치 기반으로 글 조회 기능 구현
  • fcm 이용해서 앱 푸쉬 구현
khjoon
khjoon
기록기록khjoon 님의 블로그입니다.
  • khjoon
    기록기록
    khjoon
  • 전체
    오늘
    어제
    • 분류 전체보기 (37)
      • Security (2)
      • Dev (14)
      • Infra (12)
      • Ops (9)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
khjoon
GCP에 ElasticSearch 띄워서 검색 기능 구현하기
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.