RailsのDockerイメージを作成する際に、環境変数やシークレットを安全に渡す方法について説明します。
皆さん、例えばconfig/initializers/omniauth.rb
のようなファイルに、以下のように書いていることはありませんか?
Rails.application.config.middleware.use OmniAuth::Builder do provider :google_oauth2, Rails.application.credentials.google[:client_id], Rails.application.credentials.google[:client_secret], { scope: "email, profile", prompt: "select_account", image_aspect_ratio: "square", image_size: 50 } end OmniAuth.config.allowed_request_methods = %i[get]
初期化ファイルであるconfig/initializers
以下のファイルでRails.application.credentials
を参照していることです。
このようなコードを書いていると本番環境用のDockerイメージを作成する際に、master.keyが設定されていないとasset:precompile
の実行時にエラーが発生してしまいます。
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
> [build 6/6] RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile: 1.133 bin/rails aborted! 1.134 NoMethodError: undefined method `[]' for nil (NoMethodError) 1.134 1.134 provider :google_oauth2, Rails.application.credentials.google[:client_id]
config/initializers
以下のファイルは、Railsの初期化時に読み込まれます。
しかしmaster.keyが設定されていないため、Rails.application.credentials
がnil
になってしまい、
[]
メソッドが呼び出せないためにエラーが発生してしまいます。
回避策としてはRails.application.credentials.google&.fetch[:client_id)
のように
nilだった場合にエラーにならないようにする方法もありますが、
あまり良い方法ではありません。
credentialsでgoogle > client_idを設定漏れがあった場合に気づかなくなってしまう恐れがあります。
そこで、Dockerイメージを作成する際に、環境変数やシークレットを安全に渡す方法を紹介します。
Docker の Build Secretsを利用する
Dockerには、Build SecretsというDocker Build時にシークレットを安全に渡すための機能があります。
https://docs.docker.com/build/building/secrets/
この仕組みを使うとmaster.keyや他の環境変数も安全に渡すことができます。
環境変数の場合
例えば環境変数にRAILS_MASTER_KEY
を設定している場合は、docker build時に次のように指定しておきます。
$ docker build -t chat_gpt_rails --secret id="RAILS_MASTER_KEY" .
このように指定することで、Dockerfile内でRUN --mount=type=secret,id=RAILS_MASTER_KEY
を使って、シークレットを安全に参照することができます。
例えばasset:precompileを実行する際に、以下のようにDockerfileを記述します。
RUN --mount=type=secret,id=RAILS_MASTER_KEY,env=RAILS_MASTER_KEY ./bin/rails assets:precompile
--mount=type=secret,id=RAILS_MASTER_KEY,env=RAILS_MASTER_KEY
とすることで、Docker Build時に指定したシークレットを環境変数RAILS_MASTER_KEY
として参照できるようになります。
シークレットファイルの場合
また環境変数ではなく、シークレットファイルを指定する場合は、以下のようにbuild時にシークレットファイルを指定します。
以下はconfig/credentials/production.key
というシークレットファイルを指定する例です。
idをmaster_key
としています。
docker build --secret id=master_key,src=./config/credentials/production.key .
そしてDockerfile内では以下のように記述します。
docker build時にidで指定したmaster_keyをtargetで指定したパスにマウントします。
こうすることで一時的に/rails/config/master.key
にホストのconfig/credentials/production.key
をマウント(作成)することができます。
RUN --mount=type=secret,id=master_key,target=/rails/config/master.key ./bin/rails assets:precompile
余談(Kamalの場合)
余談ですがRailsのデフォルトのコンテナデプロイツールである、Kamalの場合、次のように指定すると
Kamalが自動的にRAILS_MASTER_KEY
を設定してくれます。
builder: arch: amd64 secrets: - RAILS_MASTER_KEY
デフォルトで作成されるDockerfileはRAILS_MASTER_KEYを参照するようになっていないので、 前述のとおり次のように書き換える必要があります。
RUN --mount=type=secret,id=RAILS_MASTER_KEY,env=RAILS_MASTER_KEY ./bin/rails assets:precompile
まとめ
DockerのBuild Secretsを利用することで、RailsのDockerイメージ作成時に環境変数やシークレットを安全に渡すことができます。 とはいえ、build時にたくさんのシークレットを渡すのも大変なので、 Railsの場合は環境変数ではなく、なるべくcredentialsを利用して、RAILS_MASTER_KEYだけを渡すようにすると楽できそうですね。