AWS IoTボタンでWebアプリの監視一時停止〜本番デプロイ〜監視再開ができるようにした

※本記事内のAWS IoTボタンの検証は米国にて行いました。

AWS IoTボタンの概要

米国など幾つかの国ではAWS IoTボタン (以下IoTボタンと略します) というものが売られています。見た目は日本で売られている特定の商品の注文をボタンひとつで行えるAmazon Dashボタンと同様ですが、IoTボタンでは、ボタンが押されたことをトリガーにLambdaを呼び出すなどして自由に処理を作りこむことができるようになっています。

活用する上で考慮すべきポイント

可能性を秘めているIoTボタンですが、何に活用しようかと考えた時に悩ましいポイントもいくつかあります。

WiFi環境が必須

WiFi環境が必須であり、常に携帯してどこでも押せるということは基本的には実現できません。例えば子どもに携帯させて、何かあったときに緊急通報的に押してもらうといったことはこのボタンだけでは実現できません。そういう意味では携帯回線を使えて、かつGPSで位置が取れたりすればもっと色々と夢が広がるなーという部分はあります。
ただ、ポケットWiFiを持ち歩けば常に携帯することも可能かもしれません。

IoTボタン自体には何の表示もできない

IoTボタンは本当にシンプルな1つのボタンだけであり、何らかの表示をしたり、通知をしたりすることはできません。

クリック種別を利用者が覚えることが難しい。

シングルクリック、ダブルクリック、長押しの3つのクリック種別を判別できるようになっているのですが、利用者側がそれぞれの種別で何が起きるかを覚えておくことが難しいということも挙げられます。

これに関してはいくつかのアプローチが考えられると思います。

シングルクリック (もしくは他のひとつのクリック種別) でしかアクションを起こさない

これであれば利用者は一つだけ覚えておけばよいので問題ないでしょう。

まずはシングルクリックして、その次のアクションを通知する

ちょっと発想を変え、まずはとりあえずシングルクリックをする。そして次の操作を何かしらの方法で利用者に通知し、それに従い操作してもらうという方法です。例えば、

  • まずは利用者はシングルクリックする。シングルクリックすると、SlackやスマートフォンへのPush通知などで次の操作の指示が飛ぶようにします。例えば以下のような通知が飛ぶようにします。

「IoTボタンがシングルクリックされました。◯◯したい場合はダブルクリック、✕✕したい場合は長押ししてください。」

  • その上で、ダブルクリックまたは長押しをすることで希望する操作ができるようにする。

これは、ボタン1つだけで何かできるというIoTボタンの特長を若干薄めている部分はあるのですが、3つの種別を有効活用したいのであれば取り得るアプローチかと思います。

活用案出し

日本では現状IoTボタンを利用することはできませんが、弊社ではせっかく国外で仕事をしているメンバーもいることから、IoTボタンを使った仕組みを作ってみようということでアイデア出しをしたところ以下のような案がでました。

イライラしたときに押して、イライラ度が見えるようにする

リモートワークだと普段の皆の表情が見えないため、感情を共有することができればコミュニケーションに役立つかもしれません。

社内Webアプリケーションの本番デプロイ

GitHubにpushすることでデプロイ処理が走るようになっているWebアプリケーションがあり、ブランチ操作に慣れていないようなデザイナーの人でも簡単にデプロイができるようになりそうです。

クラウド上にある検証用マシンの起動・停止

普段料金節約のために落としている検証用マシンをボタンで起動・停止できたら便利です。

ステージング環境の起動・停止

上記と似ていますが、本番環境とステージング環境があるWebアプリケーションにおいて、普段は料金節約のためにステージング環境を止めておきたいけれども、必要になったら立ち上げたいという場合があります。それをボタンひとつで実行できれば便利です。

監視の一時停止・再開

弊社ではAWS監視・ハイブリッドクラウド監視サービス「Datadog」を利用しいろいろなサービスを監視していますが、サービスメンテナンスやデプロイの際には一時的に監視を止めたいというシチュエーションが発生します。その際にコンソールなどからチクチクと設定を変更するのは面倒なので、ボタンで監視の停止および再開を実現できれば便利でカッコイイです。

このようにいろいろと案が出た中で、今回は社内Webアプリケーションの本番デプロイおよび監視の一時停止・再開の仕組みを作ってみることにしました。

IoTボタンで社内Webアプリケーションの監視一時停止〜本番デプロイ〜監視再開ができるようにした

背景

背景についてもう少し詳細にご説明します。
該当Webアプリケーションにおいては、ステージング環境での動作を確認してから、本番デプロイをするというフローになっています。デプロイ作業自体はGitHubの特定のブランチにソースコードをコマンドラインからpushするだけの操作となっているのですが、例えばコマンドラインにあまり精通していないデザイナーの人が実施するには若干危険な操作であるという事情があります。
デザインの変更だけであれば、デザイナーの人が修正してステージングで修正確認後、その人が本番までデプロイできれば便利です。ここでまさにIoTボタンの出番と考えました。

また、上記Webアプリケーションは監視されており、デプロイ時に一時的に監視でエラーが発生する場合があります。したがって、デプロイ時には一時的に監視を止めたいという状況もありました。

ボタン設計

本番デプロイだけであればダブルクリックでデプロイといった操作でもよいのですが、上記背景のように監視を止めて再開したいという事情があるため、IoTボタンを以下の流れで操作することににより本番のデプロイを可能とするフローを考えました。

  • 本番デプロイしたいとなった時点で、IoTボタンをまずはシングルクリックします。すると、Slackに以下のような通知が来ます。

本番デプロイボタンがクリックされました。本番デプロイする前に監視を無効とする必要があります。長押しをして監視を無効にしてください。

  • 指示に従い、長押しします。

本番デプロイボタンが長押しされました。現在、サイトの監視が有効になっています。少々お待ちください。

監視を無効に変更しました。
ダブルクリックして、サイトの本番デプロイしてください。
本番デプロイをしない場合は、再度長押しし、監視を有効にしてください。」

  • 指示に従い、ダブルクリックします。

本番デプロイボタンがダブルクリックされました。本番デプロイを実行します。

本番デプロイが完了しました。
現在、サイトの監視が無効になっています。

重要なのは、まずはシングルクリックすればよいとだけ覚えてよくて、残りは指示通りにボタンを押せばいいという点です。ユーザに優しいですね。

注意点:
これは経験上なのですが、ダブルクリックについては、一回ダブルクリックしただけなのにも関わらず、何度もLambda関数が呼ばれてしまうという現象がしばしば発生しています。したがって現状では、特にダブルクリックは冪等性が保たれる操作に割り当てるのがよいのではないかというのが印象です。

実装

それでは実装の説明です。
IoTボタンのセットアップについては公式ドキュメントが充実していますので省略します。
今回の場合では、処理を実際に実装するのはLambda関数となります。留意事項は以下です。

  • IoTボタンでのクリック種別はLambdaに渡されるイベントの中に文字列で入ってきます。イベントは以下のような形式です。
1
{
    "serialNumber" : "ABCDEFG12345",
    "clickType" : "SINGLE",
    "batteryVoltage" : "2000 mV"
}

http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-lambda-rule.html より。

したがって、 clickType の文字列を判定して処理を分けるようにしています。

  • 監視にはDatadogを利用しているため、Datadogの監視の無効化・有効化を実施するコードとなっています。
  • git.pyは記載していませんが、 http://qiita.com/shibataka000/items/910754486ba2585209b2 を参考にさせていただいています。
  • これはLambda自体の注意点となりますが、Paramikoがネイティブビルドを必要とするため、Linux上 (Amazon Linux推奨) でライブラリのインストールをする必要があります。

上記を踏まえ、以下のようなLambda関数を作成しました。

1
# coding:utf-8

from datadog import initialize, api
from urllib2 import Request, urlopen, URLError, HTTPError
import os
import sys
import shutil
import json
import logging
import slackweb

from dulwich.errors import (
    SendPackError,
    UpdateRefsError,
    )
from dulwich.objectspec import (
    parse_object,
    parse_reftuples,
    )

import dulwich
from dulwich import server
from dulwich import porcelain
from dulwich.repo import Repo

import git

HOOK_URL = "some_slack_hook_url"
slack = slackweb.Slack(url=HOOK_URL)

options = {
    'api_key': 'some_api_key',
    'app_key': 'some_app_key'
}

# 関連するモニターのIDを記述
monitors = [1111111, 2222222]

def single_click():
  # ret = api.Monitor.get(2304121, group_states='all')
  enabled = True
  for monitor in monitors:
    ret = api.Monitor.get(monitor, group_states='all')
    # 1つでも無効になっていたら無効と判定
    if len(str(ret['options']['silenced'])) > 2:
      enabled = False
      break
  else:
    enabled = True

  if enabled:
    slack.notify(text='現在サイトの監視が有効になっています。監視を停止する場合はボタンを長押しして下さい。本番デプロイする場合はボタンをダブルクリックしてください。')
  else:
    slack.notify(text='現在サイトの監視が無効になっています。監視を再開する場合はボタンを長押しして下さい。本番デプロイする場合はボタンをダブルクリックしてください。')

def long_click():
  enabled = True
  for monitor in monitors:
    ret = api.Monitor.get(monitor, group_states='all')
    # 1つでも無効になっていたら無効と判定
    if len(str(ret['options']['silenced'])) > 2:
      enabled = False
      break
  else:
    enabled = True

  if enabled:
    slack.notify(text='サイトの監視が有効になっています。')
    for monitor in monitors:
      api.Monitor.mute(monitor)
    slack.notify(text='サイトの監視を無効に設定しました。')
  else:
    slack.notify(text='サイトの監視が無効になっています。')
    for monitor in monitors:
      api.Monitor.unmute(monitor)
    slack.notify(text='サイトの監視を有効に設定しました。')

def double_click():
    github_repo = 'some_repository'
    release_branch = "release"
    author = 'Masatsugu SHIMOJO <shimojo.masatsugu@mmmcorp.co.jp>'
    tmp_dir = "/tmp/repo"
    dulwich.client.get_ssh_vendor = git.KeyParamikoSSHVendor
    if os.path.exists(tmp_dir):
      shutil.rmtree(tmp_dir)
    slack.notify(text='GitHubからcloneします')
    repo = porcelain.clone(
        github_repo,
        tmp_dir
    )
    dulwich.porcelain.branch_create(repo, release_branch)
    dulwich.porcelain.symbolic_ref(repo, release_branch)
    slack.notify(text='ブランチをpushします')
    dulwich.porcelain.push(repo, github_repo, ('refs/heads/' + release_branch))
    slack.notify(text='GitHubにpushしました :exclamation: デプロイ完了までしばらくお待ちください。')

def lambda_handler(event, context):
  initialize(**options)

  if event['clickType'] == "SINGLE":
    slack.notify(text="スーパーデプロイボタンがシングルクリックされました :exclamation: ")
    single_click()
  elif event['clickType'] == "DOUBLE":
    slack.notify(text='スーパーデプロイボタンがダブルクリックされました。本番デプロイします :exclamation: ')
    double_click()
  elif event['clickType'] == "LONG":
    slack.notify(text="スーパーデプロイボタンが長押しされました :exclamation: ")
    long_click()
  else:
  
  return

実際に実行してみた

実際に実行してみた結果です。

まずはともあれシングルクリック!

いったん監視を止めたいので長押しします。

デプロイするためダブルクリックします。

デプロイ完了を確認後、監視を再開するため長押しします。

このように、監視の無効・デプロイ・監視の有効の一連の作業が実現できました。

まとめ

AWS IoTボタンの紹介から活用案、そして実際に活用してみた例までご紹介いたしました。
日本で使えるようになるのが待ち遠しいですね!

このエントリーをはてなブックマークに追加