ServerlessなOpsを考える


はじめに

Serverless Advent Calendar 2018 の18日目です。

ServerlessOps に関連した、ちょっとしたTipsを紹介します。


No Opsな時代を目指して...

ServerlessなOpsに関する個人的な心得

私は日々のサービス開発で 意地のサーバーレス をモットーに、主にAWSのFaaSやFully Managed Servicesのみを使ってシステム開発をしているわけですが、他にもこだわりがあって、

AWSコンソールにログインしたら負け

というポリシーを持っています。 (※よく負ける)

昔は sshしたら負け とかよく言ってましたが、Serverlessをやっているとsshするケースなんてほとんど存在しないため、インスタンスへのsshログインの次はコンソールログインと闘い始めたわけであります。

いつか、 まだAWSコンソールで疲弊しているの? と言われないために...
(たぶんずっと言われない)

コンソールログインは邪悪か?

いいえ、邪悪ではありません。

ただ、管理しているAWSのRootアカウントやIAMユーザーの数が増えてくると、

  • ID / Passwordの管理が面倒
  • ログイン毎のMFAコードの入力がうざったい
  • 会社さんによってはAWSコンソールにログインできる場所や端末が限られている? (未確認)
  • IAMの権限管理が面倒

などなど、色々と課題は出てきます。
また、頻繁にコンソールにログインしているうちは No Ops とは遠い状態にいることは確かです。

とは言っても日々のサービス運用でコンソールへのログインが必要になるケースは発生します。
その際に、コンソールでの単純作業の運用手順書を作る...なんて仕事は間違ってもやりたくないですし、何回も同じ事象でコンソールログインしている場合、私は積極的にChatOps化を検討しています。

ここで言うChatOpsは、主にSlackのSlash Commandを使ったOpsだと思ってください。

ChatOpsの何が楽なのよ?

もちろんコンソールにログインしなくても作業できるので楽ができます。
Slackという 24/7 でAccessibleな、大変ブラックなツールが身近にあるのに、使わない手はないわけです。

コンソールへのログインを無くしてちょっとでも楽をして、より Less Ops な状態にし、 No Ops に向かって進化したいのです。

ChatBotに向いているOpsって?

  • 日々のOpsでAWSコンソールで行っている単純作業
    • ログ検索
    • IAMの権限管理
    • 各種リソースのパラメータ調整
  • 外部からの依頼で行う、ある程度手順が決まった作業
    • テストデータを投入してほしいんだけど、とか

などなど、色々あると思います。

従来はこういった作業を簡単に行うために、Scriptを書いてEC2インスタンスに置いて実行したりしていましたが、なんとサーバーレスな世界ではEC2インスタンスを立てられません!!

結果的にSlackのSlach Commandを使うのが最もお手軽でした。


ServerlessなChatOps

散々ポエムを書きましたが、ここからが本題 (?) です。
適当な課題をServerlessなChatOpsで解決してみます。

課題

Serverlessなマイクロサービス群でシステムを構築していて、とある HogeService でエラーが発生し、Slackに通知が飛んできました。

Alarmを読み解くと、
- DBからデータが取得できずにエラーが発生した
- どうやらLambdaで処理したrequestIdは 12345678-aaaa-bbbb-cccc-1234567890ab のようだ

このAlarmの前後で何が起こっているのかログを調べよう!ということで、

  1. AWSコンソールにログイン、
  2. CloudWatchのサービスページに移動して、
  3. CloudWatch Logsを開いて、
  4. 対象のLambda Functionのロググループを探して、
  5. 対象のログストリームからエラーが発生したrequestIdを検索して、
  6. 該当のrequestIdが付与された一連のトランザクションのログを表示

..と、これでようやくログの調査ができます。

このOpsを、SlackのSlach Commandで簡単に行えるようにしたいと思います。

開発

当然ですが、ChatOpsな機能もServerlessを使って開発します。
何かしらのPaaSのような、アイドル時に課金されてしまうサービスを使うと負けです。

構成

  1. Slackで、Slash Command (例えば /logsearch) とRequest IDを入力する
  2. SlackからのコマンドはAPI Gatewayで受ける
  3. LambdaをInvokeし、AWS SDKを使って CloudWatchLogs.filterLogEvents を使ってログを検索する
  4. 結果をLambdaのレスポンスで返す
  5. Slackに一連のログが表示される

Bot開発のフレームワークは何でもいいのですが、個人的には claudia-bot-builder が好きでよく使います。

3については、こんな感じの関数を作っておくと便利です。
(細かい説明は割愛します)

const queryLogs = (lambdaName, utcDate, requestId) => {
  const params = {
    logGroupName: `/aws/lambda/${lambdaName}`,
    filterPattern: `"${requestId}"`,
    logStreamNamePrefix: `${utcDate}`,
    limit: 20
  };

  return new Promise((resolve, reject) => {
    CloudWatchLogs.filterLogEvents(params).promise().then((data) => {
      const rawLogs = data.events;
      _.sortBy(rawLogs, 'timestamp');

      const logs = [];
      for (rawLog of rawLogs) {
        logs.push(rawLog.message);
      }

      resolve(logs);
    }).catch((err) => {
      reject(err);
    });    
  });
};

この構成には問題あり...

この構成で動くこともありますが、ログの検索に少し時間がかかるため、だいたいSlackのResponse時間制限の3秒が経過してしまってSlack側がタイムアウトします。

実運用する際は、最初のLambdaはSlackのコマンドを受け取って非同期タスクを実行し、すぐにResponseを返すべきです。

非同期タスクの実行は、通知の柔軟性を考慮するとSNSを挟むのが良いかと思います。

わざわざ構成図を描く必要はないと思いますが、あえて描くとこんな感じです。

  1. Slackで、Slash Command (例えば /logsearch) とRequest IDを入力する
  2. SlackからのコマンドはAPI Gatewayで受ける
  3. LambdaをInvokeし、SNSのTopicにデータをPublishして、すぐにSlackにResponseを返す
  4. SNSにAsyncTask用のLambdaをSubscribeさせておき、AWS SDKを使って CloudWatchLogs.filterLogEvents を使ってログを検索する
  5. 結果をSlackに送信する
  6. Slackに一連のログが表示される

結果的に、Slack上でこんな感じの結果が得られます。

Slash Commandの例

/logsearch 12345678

結果

まとめ

どうでしょう?

Before
1. AWSコンソールにログイン、
2. CloudWatchのサービスページに移動して、
3. CloudWatch Logsを開いて、
4. 対象のLambda Functionのロググループを探して、
5. 対象のログストリームからエラーが発生したrequestIdを検索して、
6. 該当のrequestIdが付与された一連のトランザクションのログを表示
After
1. /logsearch ${requestId} と、Slackにコマンドを打つだけ



コンソールへのログインを省略したおかげで、ずいぶんと楽になった気にならないでしょうか??

こんな感じで Less Ops 化を進め、 No Ops な時代に向けて少しずつ進んでいきたい次第でございます。


投稿内容は個人の意見であり、所属企業の意見を代表するものではありません。