不動産一括査定サイト「イエウール」のインフラをIDCFクラウドからAWSへ移行しました

Speeeエンジニアの id:takanamito です。
今年7月にSpeeeが運営する不動産一括査定サイト「イエウール」のインフラ移行をしました。

今回は移行に伴って見直したInfrastructure as Codeの話について書こうと思います。

f:id:takanamito:20170829172208p:plain

移行背景

イエウールは事業がスタートした当初より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などのツールを使ってコードで管理できるよう計画中です。

一度に全てを理想形にすることは難しいですが、ひとつずつ理想に近づけていければと思っています。