AWS
リファクタリング
インフラ

10年モノのインフラを3年がかりでカイゼンした

(株)うるる に2015/06初に入社してから2018/08末で、インフラのカイゼンに一区切りついたのでまとめます。

10年モノというのは うるるで運営しているサービスが最長で10年 ほど経ってスゴイことになってました。

3年がかりなのは、インフラ大好きエンジニアが社内に少なくて、私の独り相撲で時間かかってしまいました。

カイゼンの内訳はざっくりこんな。

構成要素 2015/06初 2018/08末
CI Jenkins CircleCI
監視と通知 zabbix(ログインできない) Datadog & CloudWatchの混成
MySQLサーバー MySQL on EC2 RDS (MultiAZ)
メール送信 EC2から直接送信とSESの混成 Mailgunで固定IP
crontab EC2にSSHしてcrontab -e wheneverでGitHub管理
crontabで動かしてるシェルスクリプト EC2にSSHしてvi。SCPで配布 GitHub管理
ログファイル EC2のEBSにひたすら蓄積 CloudWatch Logs
EC2の中身 viして同ロールのEC2にSCPで配布 AnsibleでGitHub管理
Solrサーバー Master/Slaveが1セット Blue/Green構成
AWSアカウント 1垢に複数サービスが雑居 1垢1サービス & VPCでprod/stg/devに分割
ネットワーク EC2 Classic VPC

CI

いちおうJenkinsが立ってました。失敗して赤くなってるジョブが大半で、かといって誰が治すわけでもなく、よくわからないけど失敗したり成功したり、とにかく不安定でした。

CloudWatchのメトリクスで眺めて、EBSのIOPSクレジットの枯渇から激遅になって、Jenkinsジョブのタイムアウト設定で失敗になる、まで明らかにしました。その時の対処は、IOPSクレジット上限サイズの1TBのSSDのEBSを付けることと、同時並行で動けるJenkinsジョブ数に上限を設けることで、落ち着くようになりました。

とはいえ「Jenkinsおじさん」問題があるので、CIをどうにか民主化する必要があります。SaaSから検討して、TravisCIとCircleCIが最終候補になって、トラブルシュートをSSHでできるのを決め手に、CircleCIを導入しました。

8月末にCircleCI1.0が死んだときの2.0への移行作業はわりとキツかった。

監視と通知

計測、管理、改善の原則にしたがって、現状を可視化できるようにしたかったので。

zabbixが稼働して通知も来ていたものの、ログインできる人がいない。わけがわからないですね。社内に声かけて、EC2のSSH鍵とzabbixの認証に使ってたOpenLDAPのSSH鍵を入手して、OpenLDAPの管理者アカウントのパスワードを変更して、ようやくzabbixにログインできました。ついでにログインできる人が少ないので、共有アカウントを発行して、OneLoginに接続して、エンジニアなら誰でも見れるようにしました。

zabbixに入ってみると、監視項目がザル。EC2インスタンスは、エラーログの監視、ロードアベレージの監視、ストレージ空きの監視、程度。ダッシュボードも無い。

  • 障害発生をユーザーからカスタマーサポートチームへの問い合わせで気づく、
  • 営業メンバーがお客様のオフィスでデモしてる最中に白画面(HTTP 5xx)で気づく、
  • 朝、出社するとオフィスがざわついてて気づく

こんな状況だったので、zabbixを超いい感じにしても駄目な未来がみえました。もっと簡単に管理できるもの、当時はSaaS型が流行りはじめたころで、NewRelicとかMackerelとかのSaaS型から、最終的にDatadogを選びました。

MySQLサーバー

サービス本体のMySQLは入社時点でRDSになっていたのですが、社員がちゃんと関与していなかったサブシステムのMySQLがやばいことになっていました。AMIを取っているものの、mysqldumpでバックアップが失敗してる。このEC2が消滅したらどのくらい困るのかアプリのエンジニアに聞くと、いやそれは大いに困る、サービスのデータが数日抜けてお客様からクレームがくる程度に困ると。怪談です。

幸いにしてcron起動のバッチジョブのためだけのMySQLだったので、cronを止めて、mysqldumpして、予め立てておいたRDSに投入するだけでした。

メール送信

AWS SESは、料金の安さやセットアップの簡単さなどから、まあ普通に使うサービスです。しかし、SESからユーザーまでのログを取れない、そのくせバウンスレートでいきなり止められたりする。トラブルに弱い。

SESから受け取ってくれないお客様もいて、その方々のために、EC2にpostfix立てて送信もしてました。バッチサーバーもWebサーバもメール送信するので、SPFレコードに全台のIPアドレスが記載してあるという。SPFレコードに記載できる文字数に上限があって、サービスのスケールの上限でした。テレビ対応で増強したときは諦めてました。

このあたりの運用改善を主眼に、Mailgunに移行しました。結果、

  • SPFレコードはMailgunのみになったので、スケールの上限が外れた
  • Mailgunの過去2週間の送信ログで、カスタマーサポートチームがお客様と会話できるようになった

crontabと、crontabで動かしてるシェルスクリプト

6割くらいのシェルスクリプトはGitHub管理されていたのですが、4割くらいはEC2サーバーに転がっていました。そのEC2サーバーでオペミスこいてシェルスクリプトが消滅したら、どうリカバリするんだと。GitHub管理に改めました。

crontabの6割くらいは、リリース時にwheneverで投入していましたが、残りはcrontab -eしていたようです。オペミス消滅どうリカバリの流れで、GitHub管理に改めました。

ログファイル

EC2のEBSにひたすら蓄積していました。EBS、高いんですよね。しかもトラブルがおきるとSSHしてシェル芸で調査するという。1台だけならともかく、複数台編成のウェブサーバーだと、もう1台でサンプリングで調べるみたいな状況でした。

ログを保管してくれるSaaSはいくつか出始めたころでしたが、安いのと、とりあえず保管できればいいしで、CloudWatchLogsを選択しました。

EC2の中身

crontabの管理状況や、zabbix設定がそんなだったので、もはや何も驚きません。EC2インスタンスの中身はブラックボックス。秘伝のタレが継ぎ足され続けてて、構築手順はAMIから立てるしかない状況でした。

MySQLの追い出し、crontabのコード化、シェルスクリプトのGitHub管理、の流れで、Ansibleで管理することにしました。PuppetやChefがまあまあ辛くて、Ansibleならsshできれば動くしいいんじゃね?みたいに流行りはじめた頃でした。

  • ps, chkconfig, netstatなどで稼働してるデーモンを調べて、
  • /etcのどこかに散らかってる設定ファイルをかき集めて、
  • ansible経由でEC2インスタンスに設置できるようにして、

というリバースエンジニアリングでansible化を進めていきました。最終的に、Amazon提供のAMIから立てたEC2インスタンスを新規作成して、このansibleコードを使ってセットアップして、アプリをデプロイして、動作に差異がないことをもって、ブラックボックスが解消できました。

Solrサーバー

Solrなので、定義変更するとインデックス作り直しになるのですが、クラスタがひとつだけだと果てしない困難があります。クラスタがふたつあればいいじゃん、いわゆるblue/green deploymentだぜ、からのちょろちょろっと手順を整備して、capistranoとansibleも調整して、で実現しました。

AWSアカウント & VPC

ここまでで、EC2内の諸々がGitHub管理されて、新規作成からansible適用とアプリコードデプロイで完全動作することが保証できました。

しかしEC2が立っているのは、EC2 Classicネットワークです。残念です。これがために、ALB, WAFが使えず、EC2のt2やc4,m4などの世代が利用できません。さらに、AWSアカウントはひとつで、全サービスがごたまぜになってます。いろいろと奇跡的です。

AWSアカウント移設をしつつ、移設先はVPCにする、という引っ越しをしました。そのときの予備調査が https://qiita.com/sasasin/items/17135d5c5465fc53d385 だったりします。

一度に全要素を引っ越すのは、サービスのダウンタイムと私の体力を考えると無理だったので、

  • RDS移設
    • ElasticIPで許可して、アカウント跨ぎで接続できるようにした
  • Solr移設
    • blue/greenのスタンバイ側を引越先で稼働、blue/greenスイッチ
  • ウェブサーバー移設
  • バッチサーバー移設
  • S3移設

というように段階を踏んで実施しました。

総括

いやもう本当よくやったよ私。自画自賛。