Playアプリのconf設定をシュッと上書きする方法

はじめに

こんにちは、meviyの切削Webチームの小平です。 最近、Play Frameworkアプリの設定を切り替える方法について考える機会がありました。今回はその際に使ったちょっとした工夫をご紹介します。

背景

ある機能をサーバーAから、新設したサーバーBに切り出す構成変更を行いました。 サーバーAは多数の処理を抱えており、スペックや台数の調整が難しくなっていたため、特定機能をサーバーBに移すことでシステム構成を整理しました。

ロジック自体に変更はありませんでしたが、システム構成の切り替えをする際は万が一に備えて「切り戻し」の手段を用意しておくと安心です。

「問題があればすぐにコードを修正してデプロイ対応!」という選択肢もありますが、アプリのコードを触らずに切り戻せる仕組みがあると、より安心ですよね。 その中でも鉄板の方法が、処理を旧サーバーに戻すような「切り戻し」機能です。

構成のざっくり概要

仕組みとしては、キューに投入されたリクエストをワーカーサーバーが処理しています。 どのサーバーが処理するかは、投入先のキューによって決まります。 たとえば、下の設定のように、構成変更後は「サーバーBのキュー」が指定されています。

external.queue {
  taskQueue = "task-queue-B"
}

このキュー名を "task-queue-A" に変更できえれば、従来のようにサーバーAが処理するようになり、「切り戻し」が可能になります。この設定をどう切り替えるかがポイントになります。 投入先キューの変更による処理サーバーの切り替え

方針の検討:アプリ側で切り替える

まず考えたのは、アプリ側で環境変数や設定テーブルの値を参照して、キュー名を切り替えられるようにする方法です。

class TaskQueueClient @Inject()(config: Configuration) {
  private val taskQueue = {
    sys.env.getOrElse("FALLBACK_TASK_QUEUE", config.get[String]("external.queue.taskQueue"))
  }
  ...
}

このようにしておけば、環境変数FALLBACK_TASK_QUEUE=task-queue-Aを設定するだけで、アプリを修正をせずに切り戻しが可能です。

アプリに手を入れずに切り戻したい

今回は「問題が起きる可能性は低い」と考えていたため、アプリに切り戻し用の仕組みを作るのは少々手間に感じました。 できればもっと手軽に切り戻す方法がないか?という気持ちもありました。

Play アプリのconf設定をシュッと上書きする方法

そんなときに便利なのが、システムプロパティ(-D)による設定上書きです。
Play公式ドキュメントのConfigurationのページにも記載されています(Configuration file syntax and features):

It’s also possible to set configuration using system properties. System properties override application.conf settings.
(訳:設定はシステムプロパティでも指定できます。システムプロパティは application.conf の設定を上書きします。)

たとえば、次のようにシステムプロパティを指定してアプリを起動するとexternal.queue.taskQueueの設定をtask-queue-Aに上書きできます。

sbt -Dexternal.queue.taskQueue=task-queue-A run

このように起動オプションの変更だけで、アプリのコードを変更せずにconf設定の上書きが可能になります。

confの上書きについて補足

この設定上書きの仕組みは、実際にはPlay Frameworkが内部で利用している「Typesafe Config」ライブラリの機能によるものです(lightbend/config: configuration library for JVM)。 Typesafe Configは、JVM系アプリケーション向けの設定管理ライブラリで、システムプロパティによる上書きや複数ファイルのマージなどに対応しています。

conf設定の応用的な上書き方法

このconfの上書きについて、基本的なキーの上書きだけでなく、配列や環境変数を使った上書きといった少し応用的なケースについても触れます。

配列の上書き

設定値が配列で定義されている場合も上書きは可能です。
lightbend/config: Set array values outside configuration files

例えばこのように、allowedExtensionsという配列が設定されているとします。

# application.conf
allowedExtensions = ["json", "xml", "yaml"]

これに対して、下のようにインデックスを指定することで設定した配列を上書きできます。

sbt -DallowedExtensions.0=txt -DallowedExtensions.1=md run

指定した要素だけを変更するのではなく、元の配列全体が置き換えられる点に注意が必要です。 上の例では、最終的にallowedExtensionsの値は次のようになります。

allowedExtensions = ["txt", "md"]

環境変数による上書き

また、システムプロパティだけではなく環境変数でも上書きが可能です。

環境変数を使って設定を上書きする際は、下のルールに従う必要があります。

  • CONFIG_FORCE_ を環境変数名の先頭に付ける
  • 環境変数名はキー名のドット (.) をアンダースコア (_) に置き換える
    • 例:external.queue.taskQueueCONFIG_FORCE_external_queue_taskQueue
  • アプリ起動時に-Dconfig.override_with_env_vars=trueを指定する

lightbend/config: Optional system or env variable overrides

export CONFIG_FORCE_external_queue_taskQueue=task-queue-A
sbt -Dconfig.override_with_env_vars=true run
優先順位について

conf設定の上書きの優先順位は下の通りです。

環境変数 > システムプロパティ(-D)> application.conf

たとえば、環境変数システムプロパティが同時に指定された場合、環境変数が優先されます。 あまり機会はないと思いますが、下のように両方指定された場合、最終的に使われる値はtask-queue-A(環境変数の値)になります。

export CONFIG_FORCE_external_queue_taskQueue=task-queue-A
sbt -Dconfig.override_with_env_vars=true -Dexternal.queue.taskQueue=task-queue-B run

まとめ

Playアプリの設定値をシュッと上書きする方法をご紹介しました。 今回はサーバーを切り戻す方法として利用しましたが、 開発環境のテスト時など、confの設定を一時的に変更したい場合にも活用できるテクニックだと思います、ぜひ検討してみてください!

DTダイナミクスでは一緒に働く仲間を募集しています!少しでも興味を持っていただけましたら、ぜひカジュアル面談に応募いただけたらと思います、お待ちしております!

www.wantedly.com