インフラをアレしてる佐野です。トレタのコア部分はEngineyardで運用していますが、事業拡大に伴いサブシステムも増えてきました。新しいサブシステムは主にAWSで運用しています。そこで今回は事例として弊社の新規部分のインフラ運用のやり方、そこで使われている道具(Packer, Terraform, Serverspec, Ansible, Roadworker, Circle CI)、考え方などについて書きます。これらの道具はもはやよく知られたものであり、あまり真新しくはないとは思っています。しかしながら弊社に遊びに来た方や採用の応募者の方などからトレタのシステム運用に関する質問をいただくことがあり、その説明資料のかわりになるかな、という目的もあって書かせていただきます。これ以外にも道具はあるのですが、なんとなく興味をもってくれそうなワードをタイトルに羅列させていただきました。以下、目次になります。
- 図
- AMIの作成
- サーバ/ネットワーク
- サーバプロビジョニング
- DNS
- 監視
- 運用の方針と考え方
- 今後やりたいこと
1. 図
EC2インスタンスをはじめ、種々のマネージドサービスを利用しています。これらの管理に使っている主な道具は件のとおりPacker, Terraform, Ansible, Roadworkerです。図中において矢印が刺さっている箇所が管理対象です。矢印が刺さっていない、例えばRDSなどは手作業です。これらをなぜTerraformの管理下に置かないかというと、ちょっと怖くて日和りました...。また、図を見ていただければわかるように、Circle CIを中心にインフラオペレーションを行っており、特定のブランチにpushもしくはマージされたタイミングでインフラオペレーションが走るようにしてあります。以下、それぞれ道具の使い方やポイントについて書きます。
2. AMIの作成: Packer(緑の線)
PackerはOSの基本設定と、Rubyなどコンパイルに時間のかかるもののランタイムがあらかじめインストールされたAMIを用意するために使っています(Rubyなど実行環境のランタイムについてはRPMやdebにして固めてあとからyum install / apt-get installする方式でも良いかもしれない)。masterブランチにプッシュもしくはマージされたタイミングでCircle CIからpacker buildが走るようにしてあります。packer build後にServerspecが流れ、成功したらAMIが作成されるようにしています。次の図のようなイメージです。Serverspecのテストケースがパスされ、AMIが作成されていることがわかります。
なお、packer buildにはそこそこ時間がかかるというのと、Circle CIのビルドには制限時間があるので、あまりに時間がかかるようになったら別の手段にするなど工夫する必要がでてきます。
3. サーバ/ネットワーク: Terraform(青い線)
VPC(サブネット、ルーティング、セキュリティグループ..etc)とEC2のみを管理対象としています。インスタンスを増やしたり減らしたり、新しいサブネットを切ったりするにはTerraformで行います。インスタンスの種には先ほど言及したPackerで作成したAMIを使います。TerraformのapplyはCircle CIで行うのですが、ここでポイントになるのがtfstateファイルです。terraformはapplyを行うとtfstateというファイルが生成され、次のapplyではそのtfstateとの差分を適用する動きになります。各個人のローカルマシンから実行する場合はこのtfstateファイルをgithubにプッシュし、そこで一元管理をすることになると思いますが、Circle CIにapplyをさせた場合、apply後のtfstateファイルをどこかに置く必要があります。 もっとも手っ取り早いのがS3に格納する方法だと思っています。S3にtfstate格納用のバケット(たとえばfoo-tf-state-bucket)を掘っておき、Circle CIからは下記のような流れでapplyを行うようにします。S3からtfstateを取得する -> apply -> apply後のtfstateをS3にアップロードという動きをしてくれます。
terraform remote config -backend=S3 -backend-config="bucket=foo-tf-state-bucket" -backend-config="key=terraform.tfstate" terraform remote pull terraform apply terraform remote push
どうしてもローカルマシンから実行したいケース、たとえばCircle CIが死んでいるときなどは、復活を待つか、ローカルからも上記のようにS3上のtfstateを参照し、事後にS3にプッシュしておくようにします。
4. サーバプロビジョニング: Ansible(橙色の線)
Ansibleはミドルウェアやライブラリのインストールを行うために使っています。以前はPackerでミドルウェアの設定まで全部入りのAMIを作って単にそれを使うようにしていました(個人ブログによると約一年前か...)。
しかしながら、シェルスクリプト製の謎フレームワークみたいになってしまったってのと、コンフィグ類の変更が頻繁なときがあるとPacker buildの時間が妙にイラつくというのがあり、Packerからミドルウェア以上のレイヤの部分を切り離してAnsibleでプロビジョニングするように戻しました。また、先日Ansible2系が出ましたが未だ1系です。コンフィグレーションのテストは例によってServerspecを利用しています。テストコードがあればいつでも他のツールに乗り換えることができるし、以前のように全部入りのAMI方式にも戻せる。プロビジョニング後にデプロイを行えばそのサーバはサービスreadyになります。アプリケーションのデプロイにももちろんCircle CIを使っています。
ここで、Circle CIからEC2インスタンスにansible-playbookする簡単なTipsについて紹介します。おおまかな手順は以下になります。
- 秘密情報の復号
- Circle CIのコンテナのIPを取得する
- aws-cliなどで対象のEC2インスタンスのsshポート(from Circle CI)を開放する
- プロビジョニング
- sshポートを閉じる
まず秘密情報の復号です。各種ファイルをgithubに格納する際、セキュリティ上なるべく格納したくないものもあります。クレデンシャルが書かれたファイルや、秘密鍵、SSL証明書などにです。このようなファイルがない場合は気にしなくても良いですが、ある場合はansible-vaultを使って対象のファイルを暗号化してgit pushしておきます。下記のようにして暗号化できます。
ansible-vault encrypt FILE
そしてCircle CIからgit pullした際、これを次のようにして復号します。ansible_vault_password.txtは復号のパスワードが書かれたファイルです。
ansible-vault decrypt FILE --vault-password-file=ansible_vault_password.txt
Circle CIからやる場合、復号のパスワードはCircle CIの環境変数設定でANSIBLE_VAULT_PASSWORDなどとして格納しておきます。Circle CIには環境変数をマスキングして格納してくれる便利な機能があり、AWS_ACCESS_KEY_IDなども同様にして扱えます。
ここで登録されたものはcircle.yml内で$ANSIBLE_VAULT_PASSWORDなどとして使うことができます。次のようにして復号できます。
pre: - pip install -r requirements.txt - echo $ANSIBLE_VAULT_PASSWORD > ansible_vault_password.txt - ansible-vault decrypt secret_file --vault-password-file=ansible_vault_password.txt
あとの手順3, 4, 5についてはCircle CIからEC2サーバのsshポートを開放してプロビジョニングを実行し、完了後にsshポートを閉じておしましです。
5. DNS: Roadworker(ピンク(///)の線)
DNSのレコード管理にはTerraformではなくRoadworkerを使っています。入社してすぐに導入したのがRoadworkerで、わざわざ乗り換える理由もないのでこれはこのまま回しています。Route53のDNSレコードをRubyのDSLで記述できます。
hosted_zone "toreta.in." do #### Toreta corporate site #### rrset "toreta.in.", "A" do ttl 300 resource_records( "54.64.225.166" ) end end
言い尽くされていることですが、コード化することによりコメントもつけられますし、gitで変更履歴も管理できるようになります。
6. 監視
監視については自分のブログに以前書いたのですが、このときから大きく変わっていないです。
7. 運用の方針と考えていること
今回述べたシステムについての運用方針について書きます。これについては賛否あると思いますが、こちらについては私はこのように考えて運用しています。
ホスト名はつけない、ロールだけを気にする
検証時を除き、サーバにsshするケースはほとんどないです。EC2タグやMackerelにサービス名/ロール名をつけ、サービスローンチ後は"ロードバランサの配下にAppロールをもったサーバが何台かある"とだけ認識すれば良いと思っています。サービスとロールだけを気にします。 この言葉はあまり浸透していませんが、PETS or CATTLEというものがあります。オンプレで運用されているサーバは名前をつけ、故障したら直してあげる、あたかもペットのようにケアする。クラウドのサーバ群は家畜の如く雑に扱う。という考え方です。ディスポーザブルではないコンポーネントはなるべくマネージド・サービスに寄せ、EC2で運用するものはなるべく家畜として扱えるアプリケーションサーバだけにします。
異常なインスタンスは問答無用で落とす
EC2で稼働させているMySQLサーバなどがある場合は別として、ロードバランサ配下で均等に負荷分散されているサーバは、異常時は全台で同じ異常が発生するはずです。なので基本的には"appサーバが1台だけおかしい"みたいなケースでは調査自体をしないです(一日一回なぜかN台中の1台が...といった事案が頻発するのであればさすがに調査しますが)。appサーバで丁重に扱わなければならないのはログだけだと思っています。ログはfluendなどを利用してBigQueryやS3に転送しているので、そのサーバのFluentdのファイルバッファがゼロになっていることを確認してから死んでもらいます。
自動化しすぎない
インフラをコード化するのは、省力化、人的ミスの軽減あたりが主な目的だと思っています。大抵のことは自動化できると思ってはいますが、私は"自動化で疲弊するくらいならそこはもう手動で良い"という考え方です。ドキュメントと手作業でカバーするのは現実的な落とし所だと考えています。なので適度に諦めます。具体的にどこを諦めたのかというと冒頭で述べたRDS周りとか...。
今後のやりたいこと
- コア機能のAWS化
- コンテナの有効活用
- コスト削減(キャパプラが雑なので...俺の...)
- 裏側のキメラ化している部分を紐解いていく
- ちゃんとしたログ基盤も作りてぇな...
お約束
コア部分は早くもレガシー化していますし、ansibleでの実行はダイナミックインベントリを使っていなかったり、重要なクレデンシャルがコードにベタ書きの箇所もあったりと、直したい部分は多々あります。その他、もともと負債になっている箇所、私が生み出してしまった闇もあります。 一緒に整備してくれる仲間がほしいです。ログ基盤やデータ分析基盤も作りたいし、やらなきゃならないことも山のようにあります。興味を持ってくれた方、この記事を見て"貴様のやり方は既に----私が2000年前に通過した道だッッッ"と思った方、またこのネタがわかる方などお待ちしております。
おわり