概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Rails 5.2 Credentials and Kubernetes · Kir Shatrov
- 原文公開日: 2018/03/24
- 著者: Kir Shatrov -- Shopifyのプロダクションエンジニアです。
credential(認証情報)は英ママとしました。
Rails 5.2のcredential機能をKubernetesで使う(翻訳)
Rails 5.2で導入された新しいcredentials機能で遊んでみた結果、これが実にクールだと思えました。暗号化されたcredential(secretとも呼ばれます)をリポジトリに保存しておき、読み出す必要が生じたらマスターキーで暗号化を解除できます。
Rails 5.1で導入されたsecrets.yml
との違いを知りたい方は、「Rails Encrypted Credentials on Rails 5.2」をご覧ください。
RailsのアプローチはShopifyのejsonと非常に似通っていますが、ejsonのような非対称暗号化を採用していない点が異なります。
新しいcredential管理は、Kubernetes上で動作するコンテナ化Railsアプリでどのように機能するでしょうか。結論から言うと、驚くほどスムースでした。
ロケット科学のような難しいセットアップは不要です。Rails 5.2 credentialのデプロイがどれほど簡単であるかを本記事でご説明いたします。
$ gem install --pre rails
$ rails -v
Rails 5.2.0.rc2
$ rails new secretland --skip-javascript --skip-spring --skip-coffee --skip-turbolinks --skip-action-cable
$ bin/rails credentials:edit
# 暗号化済みcredentialをvimで開く
$ cat config/master.key
3bed2fdcb0261e6f48850de01a85fb5b
# このアプリのcredential向けマスターキーは.gitignoreにも記載されるのでgitにpushされない
お次はコンテナをビルドしましょう。まず、.dockerignore
ファイルにマスターキーを追加し、コンテナにうっかり置かれないようにします(このキーをコンテナレジストリに公開したくありません)。
$ echo config/master.key > .dockerignore
コンテナのビルドには、こちらの最小限のDockerfile
を用います。
FROM ruby:2.5
RUN mkdir -p /app
WORKDIR /app
ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true
ENV RAILS_LOG_TO_STDOUT true
COPY Gemfile /app/
COPY Gemfile.lock /app/
RUN bundle config --global frozen 1
RUN bundle install --without development test
COPY . /app
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
$ docker build -t kirshatrov/secretland:v1 .
このマスターキーを環境変数として実行します。
$ docker run -i -t -p 3000:3000 -e RAILS_MASTER_KEY=3bed2fdcb0261e6f48850de01a85fb5b kirshatrov/secretland:v1
大事な秘密情報をうっかり出力する残念なコントローラを書くと、以下のようになってしまいます。
このコンテナをKubenetesノードからダウンロードおよび実行できるよう、Dockerレジストリにpushします。
$ docker push kirshatrov/secretland:v1
Kubernetesリソースを作成する前に、そのための秘密情報の作成が必要です(私の場合Kubenetesの秘密情報は実際にはこれが初めてでしたが)。
$ kubectl create secret generic secretland-secrets --from-literal=rails-master-key=3bed2fdcb0261e6f48850de01a85fb5b
secret "secretland-secrets" created
$ kubectl describe secret secretland-secrets
Name: secretland-secrets
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
rails-master-key: 32 bytes
Deploymentのspecは次のとおりです。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: secretland
labels:
app: secretland
spec:
selector:
matchLabels:
app: secretland
template:
metadata:
labels:
app: secretland
spec:
containers:
- image: kirshatrov/secretland:v1
name: rails
ports:
- containerPort: 3000
env:
- name: RAILS_MASTER_KEY
valueFrom:
secretKeyRef:
name: secretland-secrets
key: rails-master-key
ここにトリックが潜んでいます。環境変数(RAILS_MASTER_KEY
)の設定には、事前に作成した秘密情報の値から取り出したものを使っています。こうすることで秘密情報をDeploymentから切り離し、Deploymentリソースにマスターキーが漏洩することを回避できます。このYAMLをDeployment specと一緒にアプリのリポジトリにpushすることすら可能です。
追記: 本記事コメントでVictorが指摘したように、config/master.key
にファイルとして秘密情報キーをマウントする方がよいかもしれません。これにより、Railsは環境変数ではなくこの情報を使うようになります。この場合、YAMLのspec.template
の部分は以下のようになります。
spec:
containers:
- image: kirshatrov/secretland:v1
name: rails
ports:
- containerPort: 3000
volumes:
- name: secrets
secret:
secretName: secretland-secrets
items:
- key: rails-master-key
path: /app/config/master.key
このDeploymentを適用してインターネット上に公開します。
$ kubectl apply -f deployment.yml
$ kubectl expose deployment secretland --type=LoadBalancer --port=80 --target-port=3000
見事に動きました!
本記事で用いたコードはGitHubのkirs/secretlandリポジトリでご覧いただけます。
正直、こんなに何もかもがスムースに行くとは想像もできませんでした。Rails 5.2のcredential管理はコンテナ化されたアプリでも実にうまくいきますし、Kubernetesへの秘密情報のpushもコマンド一発で完了します。
今後は、このcredential情報を編集してからbin/rails credentials:edit
とgit push
を実行するだけでproduction環境を更新できるでしょう。
本ブログの更新情報については、Twitterで@kirshatrovをフォローして下さい。
関連記事
minikubeでDockerコンテナをクラスタ構成した運用とDockerコンテナを手作業で地道に管理の運用とどっちが楽か検証しながらまとめてみた
Kubernetesでのデプロイ中に’db:migrate’や’db:seed’などのRailsタスクを管理する(翻訳)