はじめに
こんにちは!KitchHikeインターンエンジニアの薬師寺です。 主にサーバーサイド、特にテストやミドルウェア周りに関わっております。
KitchHikeでは、検索にElasticsearch、CIサービスとしてWerckerを使用しています。私のミッションはWerckerでElasticsearchのテスト環境を整えることでした。
まずはWeb上でドキュメントなどを読みながらでWerckerでElasticsearchを使う方法を調査しました。しかし、Dockerfileを登録するやり方や、Travis CIなどの他のCIを使うやりかたは見つかったのですが、シンプルにWerckerとElasticsearchのみを使った環境構築は見つけられませんでした。なので、ここに記録を残しておきたいと思います。
設定手順はシンプルで、3つのステップで完了します。 以下に大まかな流れを紹介します。
- Elasticsearch::ExtensionsのRubygemをインストール
- spec/rails_helper.rbにテスト用クラスターの起動設定を記述
- 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イメージをダウンロードしています。
このリンクから、使用するバージョンのElasticsearchを見つけてwercker.yml
のservices
に追加しましょう。(ちなみにバージョンを指定しないと自動で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.yml
のservices
に追加したサービスは、それぞれのポート番号やバージョンなどの必要な情報が環境変数として自動で定義されます。
これは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>
今回の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.rb
でSettings.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エンジニアを絶賛募集中です!