この記事は Speee Advent Calendar 2017 6日目の記事です。
5日目は @hatappi による Red Chainerをコードを変更せずに約2倍くらい早く処理させる でした。
数ヶ月前に、弊社の福利厚生制度でもある Speee Library のシステム全面リニューアルが行われ、アプリケーションが Rails 5.1 でリライトされました。
今回は、その運用の中で学んだ、 Rails 5.1 の Encrypted secrets と config gem を併用するためのコツを書き留めておきたいと思います。
config gem (rails_config)
YAML (config/settings.yml
) でアプリケーション設定を一元管理できる、定数管理 gem です。
環境別の設定 (config/settings/{environment}.yml
) 、各個人の開発環境毎に異なる設定 (config/settings.local.yml
) 、環境変数による設定 (use_env
オプション) などの機能を備えています。
設定値の管理を明瞭に行えるため、私は好んでこの gem を使用しています。
一方、production 環境の API キーのような、リポジトリに含めるべきでない秘匿情報はどう管理しようか? という課題もありました。
Encrypted secrets
Rails 5.1 で導入された仕組みで、API キーやインフラ設定などの秘匿情報を、config/secrets.yml.enc
で暗号化して管理できるようになります。
秘匿情報の管理については yaml_vault などの先行例もありましたが、今回のプロジェクトではせっかく Rails 5.1 を使える状況なので、Rails way な方法で管理してみることにしました。
Rails 5.2 で deprecated
Rails 5.2 では Encrypted secrets が deprecated となり、Credentials に置き換わる予定です。マイナーバージョン1つ分という、なんとも短い人生でした。
$ bin/rails secrets:edit
Encrypted secrets is deprecated in favor of credentials. Run:
bin/rails credentials --help
Encrypted secrets と異なり、Credentials は 動作環境の設定が (beta2 時点では) ないため、環境毎の設定値を扱う場合は自分で工夫する必要があります(値取得時に Rails.env
をキーにするなど)。
Encrpyted secrets + config 併用のアプローチ
自身のプロジェクトでは、全面的に config gem に乗っかっていたので、以下のようなアプローチを取りました。
- Encrypted secrets で設定した値を config gem の設定にマージする
- Rails アプリ内では config gem の値を使う (=
Settings
を参照する) - config gem で Encrypted secrets の値を透過的に参照する
基本的には config gem を使い、production 環境の設定値は Encrypted secrets で管理するといった具合です。
イニシャライザで設定値をマージする
config gem は #add_source!
で、任意のハッシュを設定値として追加できます。なので、イニシャライザに下記のようなコードを追加することで、意図した事が実現できそうです。
Settings.add_source!(Rails.application.secrets.deep_stringify_keys)
Settings.reload!
Settings
は内部で文字列キーを使用しているため、#deep_stringify_keys
で変換して正しくマージできるようにしています。
イニシャライザのファイル名
この処理を config/initializers/config.rb
(rails g config:install
を実行すると作られる)に書きたくなるのですが、そうすると NameError
が返ってきます。
/Users/yukihattori/rails51/config/initializers/config.rb:1:
in `<top (required)>': uninitialized constant Settings (NameError)
Did you mean? String
このファイルは Config
自体の設定 と定義されていて、ここではまだ設定値 Settings
は使用できません。そのため、異なるファイル名のイニシャライザを使用する必要があります(例:config_with_encrypted_secrets.rb
)。
Settings.add_source!(Rails.application.secrets.deep_stringify_keys)
Settings.reload!
動作確認
production:
hoge: fuga
$ rails runner -e production 'puts Settings.hoge'
fuga
Encrypted secrets の値を透過的に扱えてますね!
データベース設定を Encrypted secrets で管理する時の注意
例えば、staging 環境のデータベース認証情報をリポジトリ管理したいとします。
default: &default
adapter: mysql2
database: application
host: localhost
staging:
<<: *default
username: staging
password: <%= Settings.database.password %>
staging:
database:
password: pass
ところが、db:create
を実行するとエラーになってしまいます。
$ RAILS_ENV=staging bundle exec rake db:create
rake aborted!
NoMethodError: Cannot load `Rails.application.database_configuration`:
undefined method `database' for nil:NilClass
db:create
は、Rails の環境を事前に読み込んでいないため、Encrypted secrets の有効化設定 (config.read_encrypted_secrets = true
) を認識していないのが原因です。
この問題は db:load_config
タスクの依存関係に environment
を設定することで解消できます。
namespace :db do
task load_config: :environment
end
Rails 5.2 では...
Rails 5.2 では、下記 PR にて environment
が依存関係に追加されているので、この対策は必要ありません。
load_config
taskのdependency にenvironment
taskを追加しています。元々一部taskでは
environment
taskが実行されていなかった(environmentファイルがロードされてなかった)のですが、それだと、例えばdatabase.yml
にencrypted secretsを使用している場合に問題になる(environmentファイルがロードされない、という事はread_encrypted_secrets
がtrueになる事が無い)為、一通りのtaskでenvironmentファイルのロード処理が行われるようにする為に、load_config
taskのdependency にenvironment
taskを追加しています。
おわりに
config gem での秘匿情報の管理が Encrypted secrets で少し楽になりました。やはりリポジトリで管理できるのは嬉しいですね。
Encrypted secrets 自体は Rails 5.2 での deprecated により賞味期限が迫ってしまったものの、Credentials になってもこの原理自体は変わらないので、同様のアプローチで透過的に秘匿情報を扱えると思われます。
ボツアプローチ
実は、以前から他のアプローチもいろいろと試しておりました。例えば...
-
settings.yml
内で ERB を使用し、<%= Rails.application.secrets.hoge %>
で読み込む -
config/boot.rb
にrequire 'config'
を追加して config gem の Rails インテグレーションを読み込まないようにした上で、自前でConfig.load_and_set_settings
で設定を読み込む
などなど。
実際のプロジェクトでは、ここに至るまで諸々のトラブルがありました。Config
の読み込みタイミングに起因する問題や、Sidekiq との併用など…
さまざまなトラップを踏んだ末、ここに挙げた後者の方法で長らく運用していて、今回もその話をしようかと思っておりました。
が、この投稿を書くために改めて調査した結果、『一周回ってシンプルに修正できるじゃん!』と気づいた次第です。何事も書き留める事は大切ですね...
次回は弊社広報の @mogmog214 より、『自社テックブログの更新頻度を2年で6倍にした話』です!
自社テックブログの更新頻度を2年で6倍にした話 - mogmog2の日記