KitchHike Tech Blog

KitchHike Product, Design and Engineering Teams

Elasticsearchのテスト環境を構築しWerckerで実行するまでの3つのステップ

はじめに

こんにちは!KitchHikeインターンエンジニアの薬師寺です。 主にサーバーサイド、特にテストやミドルウェア周りに関わっております。

KitchHikeでは、検索にElasticsearch、CIサービスとしてWerckerを使用しています。私のミッションはWerckerでElasticsearchのテスト環境を整えることでした。

まずはWeb上でドキュメントなどを読みながらでWerckerでElasticsearchを使う方法を調査しました。しかし、Dockerfileを登録するやり方や、Travis CIなどの他のCIを使うやりかたは見つかったのですが、シンプルにWerckerとElasticsearchのみを使った環境構築は見つけられませんでした。なので、ここに記録を残しておきたいと思います。

設定手順はシンプルで、3つのステップで完了します。 以下に大まかな流れを紹介します。

  1. Elasticsearch::ExtensionsのRubygemをインストール
  2. spec/rails_helper.rbにテスト用クラスターの起動設定を記述
  3. WerckerでElasticsearchを参照できるように設定

それでは、順番に見ていきましょう。

KitchHikeでは Ruby on Rails で開発していますので、本記事はRailsでの設定例になります。ただし、Werckerでserviceごとに自動的に設定される環境変数などは、他のフレームワークでも応用できるかと思います。

1. Elasticsearch::ExtensionsのRubygemをインストール

Why? : テスト用のElasticsearchを別ポートで起動させるため

まずElasticsearchのテストを行うにあたり、ローカルマシンでテストが実施できるように環境構築を行います。 開発環境と同じElasticsearchのクラスターでテストを実行すると、開発用のデータが原因でテストに不具合が起こることがあります。よって、開発環境のクラスターとは別に、テスト環境用のクラスターを立ち上げて開発環境から隔離されたテストデータを用意する必要があります。

そのために、elasticsearch-rubyというRubygemを追加してElasticsearch::Extensions::Test::Clusterというモジュールを使用できるようにしましょう。 github.com

このモジュールの特徴は、テスト用のElasticsearchのクラスターを別のポートで立ち上げてくれる点と、用意されているstart, stop, runnning?というメソッドで簡単に起動・停止・起動中かどうかの確認が行える点です。

Gemfileに以下の記述を追加します。

group :test do
  gem "elasticsearch-extensions", git: "https://github.com/elasticsearch/elasticsearch-ruby.git", branch: "5.x"
end

2. spec/rails_helper.rbにテスト用クラスターの起動設定を記述

Why? : 1で設定した別ポートのElasticsearchを任意のテストで起動させるため

次に、任意のテストでテスト用Elasticsearchクラスターを立ち上げるように設定します。

spec/rails_helper.rbに以下の記述を追加します。

require "elasticsearch/extensions/test/cluster"

RSpec.configure do |config|
  cluster = Elasticsearch::Extensions::Test::Cluster

  config.before(:all, :elasticsearch) do
    cluster.start(nodes: 1) unless cluster.running?
  end

  config.after(:all, :elasticsearch) do
    cluster.stop if cluster.running?
  end
end

ちなみに、Elasticsearchにパスを通していない方は、Elasticsearch::Extensions::Test::Clusterの引数にElasticsearchの起動コマンドをハッシュで渡してあげると起動します。

RSpec.configure do |config|
  cluster = Elasticsearch::Extensions::Test::Cluster
  command = "/somewhere/elasticsearch-5.3.3/bin/elasticsearch"

  config.before(:all, :elasticsearch) do
    cluster.start(command: command, nodes: 1) unless cluster.running?(command: command)
  end

  config.after(:all, :elasticsearch) do
    cluster.stop(command: command) if cluster.running?(command: command)
  end
end

以上の設定により、1で設定したテスト用Elasticsearchクラスターを起動させて、任意のテストを実行できる準備ができました。Elasticsearchを使うテストには:elasticsearchを追加します。

describe "Elasticsearchを使うテスト", :elasticsearch do
  # クラスターが起動する
end

describe "Elasticsearchを使わないテスト" do
  # クラスターが起動しない
end

3. WerckerでElasticsearchを参照できるように設定

Why? : Werckerでテストが実行される際にElasticseachを使えるようにするため

続いて、WerckerでElasticsearchを使用するための設定を行っていきます。

3-1 wercker.ymlを編集

Werckerで使用するミドルウェアは、servicesで設定します。Wercker実行時にDockerイメージをダウンロードしています。

https://www.docker.elastic.co

このリンクから、使用するバージョンのElasticsearchを見つけてwercker.ymlservicesに追加しましょう。(ちなみにバージョンを指定しないと自動でlatestになります)

box: ruby:2.4.2
services:
      - mongo:3.4
      - redis
      - elasticsearch:5.3 

3-2 Werckerで立ち上がるElasticsearchのポートを確認

WerckerではElasticsearch::Extensions::Test::Clusterを使わずにテストを実行します。 ローカルマシンでは開発用データとの干渉の可能性があったので、わざわざ別クラスターで環境を分けていました。しかし、Werckerではそもそも専用のコンテナが立ち上がるのでわざわざ分ける必要はありません。 Test::Cluster を使わない方が、テストごとにクラスターの起動しなおしも発生しませんし、ビルドにかかる時間を短縮できるメリットもあります。

ただ、そのためにはWerckerで起動しているElasticsearchのポートを知らなくてはいけません。ポートを確認するにはどうすればよいのでしょうか。

実は、wercker.ymlservicesに追加したサービスは、それぞれのポート番号やバージョンなどの必要な情報が環境変数として自動で定義されます。 これはDockerが、サービスごとに一定の規則に従って環境変数を作成しているからです。

以下はWerckerの公式ドキュメントからの引用です。

Dockerlinks work by exposing the service container’s environment variables to the current environment. This happens for every service listed in the services section. For each service, Docker will expose three distinct environment variables (where the container name is the name of the service container):

<container name>_PORT_<port>_<protocol>_ADDR
<container name>_PORT_<port>_<protocol>_PORT
<container name>_PORT_<port>_<protocol>_PROTO

// any other env vars exposed from within the container 
// will be exposed in this format
<name>_ENV_<env>

devcenter.wercker.com

今回のElasticsearchを使う場合だと、具体的にはこのような環境変数が定義されています。

ELASTICSEARCH_PORT_9200_TCP_ADDR
ELASTICSEARCH_PORT_9200_TCP_PORT
ELASTICSEARCH_PORT_9200_TCP_PROTO

ELASTICSEARCH_PORT_9200_TCP_PORTから、Werckerではポート9200でElasticsearchが起動していることがわかり、RailsなどのアプリケーションからはENV['ELASTICSEARCH_PORT_9200_TCP_PORT']でポート番号 9200 を得ることができます。

ちなみに、公式ドキュメントの Environment Variables にはenvコマンドで環境変数を表示させるstepを追加する方法も書いてありますが、この方法では全ての環境変数が表示されてしまいます。Werckerプロジェクトが公開設定になっていたらセキュリティホールになってしまうので、オススメしません。

前述の通り、サービスの環境変数にはDockerが提供するコンテナ名のprefixがついているので、サービスの環境変数を知りたいときは以下のようにgrepしてあげましょう。

- script:
    code: |
        env | grep ELASTICSEARCH

これを踏まえて、以下の記述をテスト用の環境変数を記述する設定ファイルに追加します。 KitchHikeでは環境依存の変数を管理するRubygem configを利用しているので、config/settings/test.ymlに記述を追加します。

elasticsearch:
  host: <%= ENV['ELASTICSEARCH_PORT_9200_TCP_ADDR'] || 'localhost' %>
  port: <%= ENV['ELASTICSEARCH_PORT_9200_TCP_PORT'] || '9250' %> 
# 9250は`Elasticsearch::Extensions::Test::Cluster`のデフォルトのポート番号

config/initializers/elasticsearch.rbSettings.elasticsearch.host, Settings.elasticsearch.portのようにアクセスすることで、起動させるElasticsearchのホストとポートを環境ごとに変更することができます。

Elasticsearch::Model.client =
    Elasticsearch::Client.new(hosts: [
                                  { host: Settings.elasticsearch.host,
                                    port: Settings.elasticsearch.port,
                                  (省略)

3-3 Werckerでテスト用クラスターを立ち上げないように設定

また、前述の通りWerckerではElasticsearch::Extensions::Test::Clusterを起動させる必要はないため、 ローカルマシンだけでTest::Clusterを立ち上げるように設定しましょう。 Werckerでの実行時には、WERCKERという環境変数が定義され、trueを返すようになっていますので、それを利用します。 spec/rails_helper.rbに記述を追加します。

RSpec.configure do |config|
  unless ENV["WERCKER"]
    cluster = Elasticsearch::Extensions::Test::Cluster
    (以下省略)
  end
end

実現できたこと

ここまでの設定により、以下のことができるようになりました。

実行マシン 環境(environment) 起動させるElasticseach
ローカルマシン 開発環境(development) ローカルマシンにインストールしているElasticsearch
ローカルマシン テスト環境(test) Elasticsearch::Extensions::Test::Cluster
Wercker テスト環境(test) wercker.ymlのservicesで追加されたElasticsearch(Docker)

まとめ

以上で、Elasticsearchのテスト環境を整えることができました。 今回の重要なポイントをまとめると、以下の3つになります。

  • ローカルのテスト環境ではElasticsearch::Extensions::Test::Clusterを使用する
  • Werckerで動くサービスのポートは、Dockerが定義したサービス毎の環境変数を参照する
  • Dockerが定義するサービス毎の環境変数は、一定の規則に従って自動で定義される

この記事がみなさんのElasticsearchのテスト環境構築に少しでも参考になったら幸いです。

最後に、この記事を書くにあたり、Bit Journeyさんの以下の記事を参考にいたしました。

(記事の下書きはKibelaを使って書いています😊)

RSpecでElasticsearchを使ったテストを書く - Bit Journey's Tech Blog

We're Hiring!

キッチハイクでは、テストやミドルウェア、インフラ周りにも携わってくれるRailsエンジニア & React Nativeエンジニアを絶賛募集中です!

www.wantedly.com

www.wantedly.com

www.wantedly.com