Speeeエンジニアの id:takanamito です。
今年7月にSpeeeが運営する不動産一括査定サイト「イエウール」のインフラ移行をしました。
今回は移行に伴って見直したInfrastructure as Codeの話について書こうと思います。
移行背景
イエウールは事業がスタートした当初よりIDCFクラウドを利用していました。
今回、以下のような背景から移行するに至りました。
- AWSは社内で採用事例の多いクラウドサービスで知見が豊富なため、イエウールもそれにのっかりたい
- 事業スタートから丸3年が経ち、不必要なサーバーがあったので一度構成を見直して整理したい
- RDSやELBなどのマネージドサービスを活用して運用コストを減らしたい
以前から移行するモチベーション自体はあったのですが、工数不足で実行できていませんでした。
私がイエウールにjoinした5月のタイミングで、少しエンジニアの工数に余裕ができたので実行に移りました。
Infrastructure as Code化
以前のイエウールは
- IDCFのWebコンソールから手動で用意したサーバー(22台)やネットワーク
- 一部Itamae化されているものの、大半が手動で環境構築
このような状態で運用されていました。
サーバーの整理後も台数が10台を超えることや、純粋に「コードレビューしたい」というモチベーションから
今回の移行では CloudFormationとItamaeを活用し、完全なInfrastructure as Code化を目指しました。※一部例外あり。後述します。
またItamaeを採用するにあたって、mitamaeも候補にあったのですが
今回は、移行スケジュール的にmitamaeを検証する余裕があまりなかったこと、また対応pluginがItamaeの方が豊富なことから
mitamaeは使わずに、Itamaeを採用することにしました。
いざInfrastructure as Code化を進める中で迷ったのがItamaeの実行に関する以下のような問題です。
- どのサーバーからItamaeを実行するのか
- Railsの
secret_key_base
やDB接続のためのパスワードなどの秘匿情報をどうやって設置するか - Itamae初回実行前にレシピをどのように実行サーバーに設置するか
それぞれ解説していきます。
どのサーバーからItamaeを実行するのか
Itamaeの実行方式には
自分自身に対してレシピを適用する $ itamae local
リモートホストに対して適用する $ itamae ssh
の2つがあります。
本番環境で利用するサーバーに対して実行することを想定すると、Itamae実行ユーザーに対してできるだけ大きな権限を渡したくないです。
例えば $ itamae ssh
を利用する場合、Itamae実行対象であるリモートホストにssh可能なユーザーに対して任意のコマンドを実行可能なroot権限を渡すことになります。
対して $ itamae local
を利用する場合、Itamae実行ユーザーに対してroot権限での $ itamae local
コマンド実行を許可するだけで済みます。
# fuga-serverのitamae-userにsudo ALL権限(絞ることも出来るが現実的でない)を付与する必要がある [hoge-server] $ itamae ssh -u itamae-user -h fuga-server recipe.rb # この場合、fuga-serverのitamae-userには`itamae local`のsudo権限だけ付与しておけば良い [hoge-server] $ ssh itamae-user@fuga-server 'sudo itamae local /path/to/recipe.rb'
また純粋に $ itamae local
の方が実行が高速。といった背景もあります。
このような理由から、今回は $ itamae local
の実行方式を採用しました。
秘匿情報の設置と初回実行までの準備
Railsの secret_key_base
やDB接続のためのパスワードなどの秘匿情報は itamae-secretsを使って管理しています。
itamae-secretsを使えば開発者が複数いる場合も、暗号化に使った秘密鍵を共有するだけで手元のマシンで秘匿情報を複合することができます。
競合としてはよく dotenvが比較されますが、公式で本番環境に利用するのを推奨していないため
今回はitamae-secretsを採用するに至りました。
また $ itamae local
を実行するためにはレシピをサーバーに設置しておく必要があります。
イエウールではCloudFormationのテンプレートやItamaeのレシピはGitHub上のリポジトリで管理しているので $ git clone
したいわけなんですが
当然CloudFormationでただ立ち上げただけのEC2インスタンスにはGitHubのDeploy keyが存在しないため、秘密鍵を設置する必要があります。
EC2ユーザーデータとパラメータストアで起動時にItamae適用
ここまででItamae実行のために
$ itamae local
で実行したい- 秘密鍵などの秘匿情報を設置したい
という動機が生まれました。
そこでイエウールチームが使った方法が EC2 ユーザーデータとパラメータストアを利用する方法です。
以下のような手順でインスタンス起動時に自動でItamaeレシピを適用しています。
- パラメータストアからGitHubのDeploy keyを取得
- Itamaeレシピを含むリポジトリをclone
- パラメータストアからitamae-secretsの複合用 秘密鍵を取得
$ itamae local
実行
実際にユーザーデータで使用しているシェルスクリプトは以下のようなものです。
#!/bin/bash -xe yum update -y yum install -y git ruby-devel gcc-c++ gem install --no-ri --no-rdoc bundler gem install --no-ri --no-rdoc io-console aws ssm get-parameters \ --names '${env}.${github_deploy_key_name}' \ --with-decryption \ --region ${AWS::Region} \ --query 'Parameters[0].Value' \ --output text \ | perl -pe 's/\\n/\n/g' > /root/.ssh/github_deploy_key chmod 600 /root/.ssh/github_deploy_key ssh-keyscan -H github.com >> /root/.ssh/known_hosts git clone git@github.com:speee/xxxx /root/xxxx aws ssm get-parameters \ --names '${env}.${itamae_secrets_key_name}' \ --with-decryption \ --region ${AWS::Region} \ --query 'Parameters[0].Value' \ --output text \ > /root/xxxx/itamae/secrets/production/keys/default cd /root/xxxx/itamae /usr/local/bin/bundle install --path vendor/bundle --without development test /usr/local/bin/bundle exec itamae local -y nodes/${env}/${role}.yml bootstrap.rb reboot
ユーザーデータに渡しているシェルスクリプトはrootユーザーが実行してくれるため
仮にインスタンス起動後に新しいレシピを適用したくなった場合も、実行ユーザーに $ sudo itamae local
する権限を付与すればいいだけなので
不用意に任意のコマンドを実行できる権限を与える心配が少なくなります。
まとめ
イエウールではこうしてインフラ群の構成管理をしています。
しかし、Route53などの設定はCloudFormation利用前から存在したリソースなため、まだコードで管理するに至っていません。
現在、roadworkerなどのツールを使ってコードで管理できるよう計画中です。
一度に全てを理想形にすることは難しいですが、ひとつずつ理想に近づけていければと思っています。