mixi engineer blog

*** 引っ越しました。最新の情報はこちら → https://medium.com/mixi-developers *** ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

【CREウィーク木曜日】ChatOpsによる解析基盤の運用改善とAWSクロスアカウント環境におけるChatbotの実装例

※こちらの記事は過去のブログから転載したものです。

こんにちは。XFLAG スタジオ CREチームの田村です。

 

XFLAG スタジオには、CREチームがユーザーさんからの問い合わせに基づいてサーバのログ調査を行なったり、プロダクトの様々なデータを集計したりするためのデータ解析基盤があります。今回、私はこの解析基盤の運用コストを削減するため、Slack Chatbotに運用をサポートする機能を実装しました。そこで本エントリーでは、CREチームで導入しているChatOpsの実例と、その実装の過程で行ったクロスアカウントと呼ばれる環境におけるリソース操作の方法について紹介したいと思います。

 

背景

XFLAG スタジオでは、モンスターストライクをはじめとする複数のプロダクトを抱えています。それぞれのプロダクトで利用しているAWS環境は用途別にアカウントを分離しており、チームが必要以上の権限と責任を持つことがないようIAMによってユーザーやロールを作成し、権限を管理しています。

 

CREチームもサーバのログ調査に用いる解析基盤や業務自動化ツールの運用基盤にAWSを利用しています。ログ調査には主にElasticMapReduce (以下、EMR)を使用しますが、EC2などに比べてEMRは時間あたりのコストが高くつくため、調査を始める際にEMRクラスタを立ち上げ、調査が終了するとクラスタを破棄する、という運用方法をとってきました。

 

しかし、この運用にはいくつかの問題がありました。それは次のようなものです。

  1. EMRは時間に対する従量課金制であるため、クラスタを破棄し忘れると処理を行なっていなくても時間分の料金が発生する
  2. 連続して他のメンバーが使いたい場合でも、一度クラスタを破棄してしまうとまた立ち上げなおす必要があった (EMRクラスタの起動はプロビジョニングが走るように設定されているため、調査の開始まで時間がかかってしまう)
  3. (2)を回避するため「次誰か使いますか?使わないなら落とします」というコミュニケーションが発生していた

f:id:mixi_PR:20210219125816p:plain

図1 EMR使用状況を確認するコミュニケーション


対策

これらの問題を解決するために、CREで運用しているSlack Chatbotから他のチームメンバーのEMR利用状況を確認できるようにし、一定時間使用していない場合は自動的にクラスタを終了するという方法をとることにしました。

 

機能の実装にあたって、まずは新しいEMRの運用方法を決めました。

- SlackでChatbotに対してEMRクラスタを使用することを宣言し、ロックをかける - EMRクラスタを使い終わったら、ロックを解除する

- 一定時間内に他のメンバーがロックを取得した場合にはEMRクラスタは残す

- ロックが解除された状態で一定時間経過すると自動的にEMRクラスタを破棄する

 

この運用変更によって、従来はチームメンバーが各自でとっていたコミュニケーションをChatbotが仲介します。

クロスアカウント環境による弊害

EMRクラスタの操作にはAWS APIが利用できます。そのため、私は実装開始当初はSlackで送信したメッセージに応じてAPIリクエストを飛ばす簡易な実装で済むと考えていました。

 

ところが、いざ実装という段になって環境に起因する問題が発覚しました。それは、解析基盤とChatbotの動作環境が図2のように別のAWSアカウント上に存在していたことです。

 

f:id:mixi_PR:20210219125052p:plain

図2 業務効率化基盤と解析基盤のインフラ構成

 このようにAWSアカウントをまたぐリソース同士の関係性をクロスアカウントと呼びます。当然ながらクロスアカウントではそれぞれのアカウントで作成されたIAMのユーザーやロールは共有されず、本来ならば別のアカウント内のリソースにアクセスすることはできません。

しかし、AWSにはこのような要求に応えるための仕組みが用意されています。クロスアカウント環境において別のアカウント上にあるリソースを操作するためには、はじめに対象のアカウント同士で信頼関係を確立する必要があります。この設定を行うことができるのはAWSアカウントの管理者ユーザーのみです。

 

今回の事例では、まず以下のように必要なIAMユーザーとロール、及びポリシーを準備しました。

f:id:mixi_PR:20210219124550p:plain

図3 クロスアカウントでリソースを操作するためのIAM設定

 

  • アカウントAにはChatbotで使用するためのAWSユーザー (user/cre) を作成
  • アカウントBにはEMRを操作するためのロール (role/cre) を作成

 

AssumeRoleポリシーはアカウントAのユーザーがアカウントBのAssumeRole APIに対するアクセス権限を得るために必要です。したがって、アカウントAのIAMに作成するAssumeRoleポリシーの `Statement.Resource` フィールドには、アカウントBのIAMロールを指定しなければなりません。

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::<アカウントBのAWSアカウントID>:role/cre"
  }
}

加えて、アカウントBではEMRクラスタに関するポリシーを作成します。今回はChatbotからEMRクラスタの一覧取得と破棄を行うため、 `Statement.Action` フィールドに `elasticmapreduce:ListClusters` と `elasticmapreduce:TerminateJobFlows` を追加しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "elasticmapreduce:ListClusters",
                "elasticmapreduce:TerminateJobFlows"
            ],
            "Resource": "*"
        }
    ]
}

 次に、これらのIAMの設定を利用して別のAWSアカウント上のリソースを操作するまでのフローについて説明します。図4は、アカウントAのEC2インスタンスからアカウントBのEMRクラスタを終了するAPIリクエストを完了するまでの流れです。

f:id:mixi_PR:20210219124718p:plain

図4 クロスアカウント環境におけるAPIアクセスフロー

 まず、アカウントAからアカウントBのAssumeRole APIに `user/cre` ユーザーのトークンをPOSTし、`role/cre` ロールがアタッチされたトークンを取得します。ここで取得したトークンをアカウントBのEMR APIのエンドポイントにPOSTすることで、以降EMRの操作を行うことができます。

 

ここで注意するべきは、`role/cre` ロールは `user/cre` ユーザーにアタッチされるわけではなく、取得したトークンに紐づいているということです。そして、このトークンには有効期限が存在します。したがって、有効期限をすぎて失効すると再びトークンを取得し直さない限り、アカウントAからアカウントBのリソースにはアクセスできなくなります。

Slack Chatbotへの機能実装

CREチームがSlack Chatbotの開発に使用しているフレームワークは [slack-ruby/slack-ruby-bot](https://github.com/slack-ruby/slack-ruby-bot) です。ここまで説明したフローを踏まえて、Chatbotにトークン取得処理の実装を行うと次のようなフローになります。

f:id:mixi_PR:20210219124810p:plain

図5 ChatbotのEMR操作実行フロー
  • SlackからEMR操作用のメッセージを送る
  • AssumeRole APIからアカウントBの `role/cre` トークンを取得しデータベースにトークンの文字列と有効期限を保存する
  • 有効期限が切れるまでに受信したEMRコマンドには同じトークンを使い回す
  • 有効期限が切れていたら再度トークンを取得し直し、データベースを更新する

 

この流れに沿ってRubyで次のような実装を行いました。クライアントライブラリには AWS SDK for Rubyを使用しています。

class EmrClient
    def self.find_or_update_role_credentials
        # トークンの有効期限が過ぎている場合はAssumeRole APIから再度取得
        if current_credentials.expired?
            new_credentials = productivity_env_client.assume_role(
                # role/creロールのARN
                role_arn: "arn:aws:iam::123456789012:role/cre"
            ).credentials

            # 新しいトークンをデータベースに保存
            save_credentials(new_credentials)

        # 有効期限内はトークンを使い回す
        else
            current_credentials
        end
    end
end

 Slack Chatbotの機能一覧を示します。時間指定で実行する機能に関してはCRONを使用しています。

表1 Slack ChatbotのEMR操作機能一覧
Slackメッセージ 機能説明
emr status EMRの起動状態および使用中かどうかの確認
emr lock EMRクラスタの使用権を取得する
emr unlock EMRクラスタの使用権を解放する
emr terminate EMRクラスタを破棄する
- ロックされていない状態で30分たつと自動的にEMRクラスタを破棄する
- 終業時間 (19時) ぴったりにEMRクラスタを破棄する

 

この実装をChatbotに対して行い、ChatOpsによるEMRクラスタの運用を開始しました。

 

結果

従来は、EMRクラスタを使いたい場合、まずクラスタが立ち上がっていることを確認し、図1で示したように誰か他のメンバーが使用していないか確認するために全体にメンションする必要がありました。

 

それに対して、Chatbot導入後は作業中の他メンバーのアテンションを奪うようなコミュニケーションが発生することがなくなりました。相手がボットであればメンションを飛ばすことを躊躇う必要はなく、気軽にEMRクラスタの利用状況を確認することができます。  

 

こちらが実際に、Chatbotを利用してEMRクラスタの運用を行なっている様子です。

f:id:mixi_PR:20210219125908p:plain

図6 Chatbot導入後のEMRの運用


さいごに

ChatOpsは自動化によるコスト削減やオペレーションの可視化などが注目されてきました。しかし、今回導入した機能に関して言えばチームメンバーの心理的な負荷を軽減する役割の方が大きかったように思います。

「EMR使ってますか。落としていいですか」というコミュニケーションは業務の本質的な部分ではなく、定常的にそのような状況が発生することはチーム全体の集中力を削ぐことにも繋がるため望ましくありません。もし、みなさんの日常業務でも同じような場面があれば、ChatOpsの導入を検討してみてはいかがでしょうか。
 

以上、ChatOpsによる解析基盤の運用改善と、その過程でクロスアカウント環境でのAPIアクセスを実装したお話でした。

さて、CREウィーク最終日の明日は神が「CRE チームで活きたこれまでの経験」について書きます。
お楽しみに!

- 参照資料 -

docs.aws.amazon.com

aws.amazon.com