ホエールテック株式会社 ホエールテック株式会社

  • ブログ

全文検索エンジンを使ってみよう・その1〜Elasticsearch開発環境構築編

一周回って、Laravelでの開発に日々奮闘しているsoobeです!

今回のブログのテーマは、Elasticsearch(エラスティックサーチ)です。

Elasticsearchは、検索ライブラリ「Apache Lucene」をベースにした「全文検索エンジン」です。

今回の目標です

・Dockerコンテナを使用したElasticsearchを構築できる。

・Pythonアプリケーションを使ってElasticsearchに接続して通信を確認できる。

*この記事では、Mac(Apple Silicon)+ Docker Desktop を使用しています。

1. Elasticsearch プロジェクトを作成しましょう

ターミナルを開いて、作業フォルダへcdした後、次のコマンドを入力してください。

mkdir elastic-study
cd elastic-study

mkdir -p elasticsearch python/src
touch elasticsearch/docker-compose.yml
touch elasticsearch/.env
touch python/Dockerfile
touch python/docker-compose.yml
touch python/requirements.txt
touch python/src/__init__.py
touch python/src/test_connect_elasticsearch.py

ElasticsearchとPythonを構成するディレクトリと新しい空のファイルが作成されます。

# プロジェクト名
elastic-study/
│
├── elasticsearch/
│   ├── docker-compose.yml
│   └── .env
│
└── python/
    ├── Dockerfile
    ├── docker-compose.yml
    ├── requirements.txt
    └── src/
        ├── __init__.py
        └── test_connect_elasticsearch.py

2.Elasticsearch Docker Composeを構築しましょう

elasticsearch/docker-compose.yml

以下の Elasticsearch Docker Compose をコピーして貼り付けてください。

version: "2.2"

services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    container_name: setup
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          echo -ne \
          "instances:\n"\
          "  - name: es01\n"\
          "    dns:\n"\
          "      - es01\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!";
      '
    healthcheck:
      test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
      interval: 1s
      timeout: 5s
      retries: 120
    mem_limit: ${MEM_LIMIT}
  es01:
    depends_on:
      setup:
        condition: service_healthy
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    container_name: es01
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
      - ./:/etc/elasticsearch
    ports:
      - ${ES_PORT}:9200
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01
      - discovery.seed_hosts=es01
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.http.ssl.verification_mode=certificate
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120
  kibana:
    depends_on:
      es01:
        condition: service_healthy
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    container_name: kibana
    volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  kibanadata:
    driver: local

elasticsearch/.env

公式ドキュメントElasticsearch Guideの.envを参考にしました。

STACK_VERSION = 8.6.2
ELASTIC_PASSWORD = elastic
KIBANA_PASSWORD = elastic
ES_PORT = 9200
CLUSTER_NAME = test_cluster
LICENSE = trial
MEM_LIMIT = 1073741824
KIBANA_PORT = 5601

3.Python 3.10 Docker Compose を構築しましょう

python/Dockerfile

FROM python:3.10

# ロケールとタイムゾーンの設定
RUN apt-get update && apt-get install -y locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# 必要なパッケージのインストール
RUN apt-get install -y vim less

# Pythonパッケージのインストール
COPY requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir --upgrade pip setuptools && \
    pip install --no-cache-dir -r /tmp/requirements.txt

# 作業ディレクトリの設定
WORKDIR /root/src

python/docker-compose.yml

version: '3'
services:
  python:
    restart: always
    build: .
    container_name: 'python'
    working_dir: '/root/src'
    tty: true
    volumes:
      - ./src:/root/src

python/requirements.txt

requests
elastic-transport
elasticsearch

python/src/__init__.py

# 空

python/src/test_connect_elasticsearch.py

import os
from elasticsearch import Elasticsearch

ELASTIC_SEARCH_HOST = "<--ここにプライベートIPを貼り付けてください-->"
ELASTIC_CERT_FINGERPRINT = "<--ここにELASTIC_CERT_FINGERPRINTを貼り付けてください-->"
ELASTIC_PASSWORD = "elastic"


# Elasticsearchインスタンスを作成
es = Elasticsearch(    
    f"https://{ELASTIC_SEARCH_HOST}:9200",
    ssl_assert_fingerprint=ELASTIC_CERT_FINGERPRINT,
    basic_auth=("elastic", ELASTIC_PASSWORD),
)

try:
    # Elasticsearchへの疎通確認
    es.info()
    print(es.info())
except Exception as e:
    # その他の一般的なエラーの場合
    print("An unexpected error occurred:", e)
finally:
    # 内部接続を閉じる
    es.close()

4. Elasticsearch Docker Compose を立ち上げましょう。

事前にDocker Desktopを起動してください。ターミナルを開いて、プロジェクト直下で次のコマンドを入力してください。

% (cd ./elasticsearch && docker-compose build --no-cache && docker-compose up -d)

[+] Running 6/6
 ✔ Container elasticsearch-setup-1   Recreated                                                                                                             0.3s 
 ✔ Container elasticsearch-es01-1    Recreated                                                                                                             3.3s 
 ✔ Container elasticsearch-kibana-1  Recreated                                                                                                             0.1s 
 ✔ Container setup                   Healthy                                                                                                               0.4s 
 ✔ Container es01                    Healthy                                                                                                               0.2s 
 ✔ Container kibana                  Started

実行中のDockerコンテナの一覧を表示してみましょう。

% docker ps

CONTAINER ID   IMAGE                                                 COMMAND                   CREATED              STATUS                        PORTS                              NAMES
3dfb585f1af0   docker.elastic.co/kibana/kibana:8.6.2                 "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute (healthy)   0.0.0.0:5601->5601/tcp             kibana
7cf48cacdabc   docker.elastic.co/elasticsearch/elasticsearch:8.6.2   "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute (healthy)   0.0.0.0:9200->9200/tcp, 9300/tcp   es01

es01とkibanaの2つが立ち上がったら成功です。

5. Python アプリケーションを修正しましょう。

Elasticsearch Docker コンテナが立ち上がっていること。

python/src/test_connect_elasticsearch.py

・ELASTIC_SEARCH_HOSTに、プライベートIPアドレスを貼り付けてください。

・ELASTIC_CERT_FINGERPRINTに、下記コマンドの出力結果を貼り付けてください。

% elastic_cert_fingerprint=$(openssl s_client -connect localhost:9200 -servername localhost -showcerts </dev/null 2>/dev/null | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin | awk -F '=' '{print $2}')
% echo $elastic_cert_fingerprint

0F:7A:B3:77:58:FD:3E:AD:45:69:15:45:BA:00:9F:A1:04:1C:36:AB:0D:6B:E9:4D:2F:A2:09:FF:E1:7F:2F:7E

Elasticsearchに対してSL/TLS接続を確立するための証明書のフィンガープリントが出力されました。

修正後:

python/src/test_connect_elasticsearch.py

import os
from elasticsearch import Elasticsearch

ELASTIC_SEARCH_HOST = "192.168.105.1"
ELASTIC_CERT_FINGERPRINT = "0F:7A:B3:77:58:FD:3E:AD:45:69:15:45:BA:00:9F:A1:04:1C:36:AB:0D:6B:E9:4D:2F:A2:09:FF:E1:7F:2F:7E"
ELASTIC_PASSWORD = "elastic"


# Elasticsearchインスタンスを作成
es = Elasticsearch(    
    f"https://{ELASTIC_SEARCH_HOST}:9200",
    ssl_assert_fingerprint=ELASTIC_CERT_FINGERPRINT,
    basic_auth=("elastic", ELASTIC_PASSWORD),
)

try:
    # Elasticsearchへの疎通確認
    es.info()
    print(es.info())
except Exception as e:
    # その他の一般的なエラーの場合
    print("An unexpected error occurred:", e)
finally:
    # 内部接続を閉じる
    es.close()

6. Python Docker Compose を立ち上げましょう。

% (cd ./python && docker-compose build --no-cache && docker-compose up -d)

[+] Building 23.7s (11/11) FINISHED                                                                                                        docker:desktop-linux
 => [python internal] load build definition from Dockerfile                                                                                                0.0s
 => => transferring dockerfile: 604B                                                                                                                       0.0s
 => [python internal] load .dockerignore                                                                                                                   0.0s
 => => transferring context: 2B                                                                                                                            0.0s
 => [python internal] load metadata for docker.io/library/python:3.10                                                                                      3.8s
 => CACHED [python 1/6] FROM docker.io/library/python:3.10@sha256:b54e76c629a98430ac9c92e4f6bddeb672396a895b44a85022d12ee2f7239144                         0.0s
 => [python internal] load build context                                                                                                                   0.0s
 => => transferring context: 83B                                                                                                                           0.0s
 => [python 2/6] RUN apt-get update && apt-get install -y locales &&     localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 &&     ln -sf /usr/share/zoneinfo/Asia/T  8.4s
 => [python 3/6] RUN apt-get install -y vim less                                                                                                           4.1s
 => [python 4/6] COPY requirements.txt /tmp/requirements.txt                                                                                               0.0s 
 => [python 5/6] RUN pip install --no-cache-dir --upgrade pip setuptools &&     pip install --no-cache-dir -r /tmp/requirements.txt                        7.0s 
 => [python 6/6] WORKDIR /root/src                                                                                                                         0.0s
 => [python] exporting to image                                                                                                                            0.2s
 => => exporting layers                                                                                                                                    0.2s
 => => writing image sha256:57e96a7696a8999a6c76fe798a652746026a6004d1c5e9b5a639aa06eed4b27d                                                               0.0s
 => => naming to docker.io/library/python-python                                                                                                           0.0s
[+] Building 0.0s (0/0)                                                                                                                    docker:desktop-linux
[+] Running 2/2
 ✔ Network python_default  Created                                                                                                                         0.1s 
 ✔ Container python        Started 

pythonコンテナが立ち上がりました。

7. PythonアプリケーションからElasticsearchへ接続できるか確認しましょう

% docker exec -it python bash -c  "python test_connect_elasticsearch.py"

{
'name': 'es01', 
'cluster_name': 'test_cluster', 
'cluster_uuid': 'ihlWTaJXSdyh8Z7L4qCShA', 
'version': {
    'number': '8.6.2', 
    'build_flavor': 'default', 
    'build_type': 'docker', 
    'build_hash': '2d58d0f136141f03239816a4e360a8d17b6d8f29', 
    'build_date': '2023-02-13T09:35:20.314882762Z', 
    'build_snapshot': False, 
    'lucene_version': '9.4.2',
    'minimum_wire_compatibility_version': '7.17.0',
    'minimum_index_compatibility_version': '7.0.0'
    }, 
'tagline': 'You Know, for Search'
}

Elasticsearchレスポンスデータが出力されました。

おめでとうございます。Elasticsearchへの接続に成功しました!

今回はここまでになります。次回は、登録や検索の紹介ができたらと思います。

k.k
k.k
エンジニア
Webアプリケーション開発の全般(バックエンド、フロントエンド)を担当しています。
特に、バックエンド(Laravel)が得意で、社内メンバーと熱い議論を繰り広げております。
最近は、機械学習に関心があります。
採用情報
お問い合わせ