Help us understand the problem. What is going on with this article?

SORACOMボタンで簡単で事故らないデプロイを実現する

はじめに

デプロイ作業って面倒で大変ですよね。作業ミスが発生すると余計に大変だし。
今回はSORACOM LTE-M Buttonで簡単かつ事故が発生しにくいデプロイを目指します。

SORACOM LTE-M Buttonとは

いろいろなところで紹介されているので今更説明する必要も無いかも知れませんが、SORACOM LTE-M ButtonとはIoTプラットフォームを提供するソラコム社が販売している、LTE-M内蔵のボタンです。回線内蔵なので面倒なWiFi設定をすることなくボタンを発動することができ、クラウドと連携して様々な処理を簡単に実行させることができるIoTを手軽に始めたい人にはうってつけのアイテムです。SORACOMへの入り口がボタンだった、という人も結構いるんじゃないかと。まだ使っていない人は色々面白いのでぜひ使ってみましょう!

シングルクリック、ダブルクリック、長押しの3種類のイベントを発生させることができるので、簡単な操作で処理を分けることもできます。

ボタンには、

の3種類があり、それぞれに特長がありますが、今回使用するのはしろボタンです。一番安いから要件に一番マッチするからですね。それでは始めましょう。

デプロイの準備

ボタンでデプロイを簡単に、とは言っても、ボタンとソラコムが謎の技術で最適なデプロイ手段を自動で構築、とかできるわけではないので、デプロイ手段自体は自分で構築する必要があります。すみません。ここは頑張りましょう。今回はデプロイ方法として、AWS CodeDeployを用います。仮想マシン、コンテナ、Lambdaなどの環境に、デプロイコマンド一発でデプロイさせることができるサービスですね。始めて触ってみました。今回のデプロイターゲットとしては、こちらのWordPressをデプロイするチュートリアルを用いました。こちらは単一のマシンに対しデプロイする想定のチュートリアルですが、開発、テスト、本番の3つのマシンを用意し、デプロイメントグループを分けてそれぞれのマシンを指定してデプロイできるようにし、デプロイの開始、終了(正常/エラー)がSNSを通してメールに届くように設定しています。

このあたりが知りたければ、チュートリアルをやってみればよいでしょう。また、デプロイにこのサービスを使わないといけないと言うことはなく、おそらくすでに構築されたデプロイ方法があると思いますので、そちらを呼び出せれば問題ないです。

Lambdaの準備

デプロイするためのLambdaを準備しましょう。今回はRubyで以下のコードを書きました。

require 'json'
require 'aws-sdk'

DEPLOY_TARGETS = {
  'SINGLE' => 'Dev',
  'DOUBLE' => 'Test',
  'LONG' => 'Prod'
}.freeze

def lambda_handler(event:, context:)
    click_type = event['clickTypeName']
    target = DEPLOY_TARGETS[click_type]
    deploy(target)
    "Deploy is started!"
end

def deploy(target)
    client = Aws::CodeDeploy::Client.new
    client.create_deployment({
      application_name: "WordPress_App",
      deployment_group_name: "WordPress_DepGroup_#{target}",
      revision: {
        revision_type: "S3",
        s3_location: {
          bucket: "codedeploydemobucket-button",
          key: "WordPressApp.zip",
          bundle_type: "zip"
        }
      },
      deployment_config_name: "CodeDeployDefault.OneAtATime",
      description: "DeployTo#{target}"
    })
end

ボタンの説明資料によると、ボタンのイベントはバイナリパーサーを通して、以下の形式でlambdaのイベントに届きます。

{
  "clickType":1,
  "clickTypeName":"SINGLE",
  "batteryLevel":1,
  "binaryParserEnabled":true
}

どのクリックがされたかはclickTypeやclickTypeNameから取れますので、これをデプロイターゲットに変換して、CodeDeployのデプロイを起動すれば簡単にデプロイ先を分けられそうですね。

ボタンとの結びつけ

このLambdaとボタンを結びつけるには以下の手順が必要です。

  • Lambdaを起動する権限をもつIAMユーザーの作成
  • SORACOMで上記IAMユーザーを登録
  • ボタン用のSIMグループを作成、登録
  • SIMグループのAir設定にて、バイナリパーサーを有効にしてフォーマットに@buttonを指定
  • SIMグループのFunk設定をする

この手順を説明しようとしたのですが、よく考えると過去の記事SORACOM Funkで自回線の通信量を取得するでほとんど説明しているので、こちらをご覧いただければ良さそうです(なお、記事内にあるソラコム認証情報の作成は、上のコードでは使いませんが、この後必要になります)

バイナリパーサーだけ補足しておきますと、これはデバイスからバイナリで送られたデータを、Lambdaなどのクラウドサービスが受け取りやすいJSON形式に変換してくれる、という素敵サービスです。ボタンのような単純で電池消費を気にするデバイスの処理を可能な限り少なくすることができます。自分で使う場合はフォーマットを記載する必要がありますが、ボタンの場合はソラコム側で用意してくれているようですね。このような設定になります。

スクリーンショット 2020-02-01 8.42.19.png

ラズパイで記事を書いた時と、ボタンで記事を書く時で、ほぼ同じ設定がそのまま使える、というところにSORCOMサービスの素晴らしさを感じますね。ラズパイとボタンは全く違うデバイスなのに、SORACOMを通せばほぼ同じように使える。これすごくないですか?(LTE-M Button powered by AWSを使わなかった理由はこれ。あのボタンはAWSが提供する方法を使うため、かなり違う手順が必要になります)

ともあれこれでボタンとクラウドの処理を結びつけることができました。

実行

早速押してみましょう。

button1.jpg

緑に光りましたね。実行は成功です。そしてメールには?

{
  "account":"XXXXXXXXXXXX",
  "detailType":"CodeDeploy Deployment State-change Notification",
  "region":"ap-northeast-1",
  "source":"aws.codedeploy",
  "time":"2020-01-31T15:35:49Z",
  "notificationRuleArn":"arn:aws:codestar-notifications:ap-northeast-1:XXXXXXXXXXXX:notificationrule/0fdde84e4187bfd1a6960997e31182cd7fe886a0",
  "detail":{
    "application":"WordPress_App",
    "deploymentId":"d-RAE6Y9LY1",
    "deploymentGroup":"WordPress_DepGroup_Dev",
    "instanceGroupId":"3adb72d6-9dc6-4e61-ab1a-98142aa7856b",
    "state":"START",
    "region":"ap-northeast-1"
  },
  "resources":[
    "arn:aws:codedeploy:ap-northeast-1:731352774671:application:WordPress_App",
    "arn:aws:codedeploy:ap-northeast-1:731352774671:deploymentgroup:WordPress_App/WordPress_DepGroup_Dev"
  ],
  "additionalAttributes":{}
}

デプロイが開始されるとdetailのstateがSTARTのメール、そのあと成功だったらSUCCESS、失敗だったらFAILUREのメールが届きます。今回はSUCCESSが届いたので、デプロイは成功ですね!

次は長押ししてみましょう。結果は...

{
  "account": "XXXXXXXXXXXX",
  "detailType": "CodeDeploy Deployment State-change Notification",
  "region": "ap-northeast-1",
  "source": "aws.codedeploy",
  "time": "2020-02-01T00:11:12Z",
  "notificationRuleArn": "arn:aws:codestar-notifications:ap-northeast-1:XXXXXXXXXXXX:notificationrule/0fdde84e4187bfd1a6960997e31182cd7fe886a0",
  "detail": {
    "application": "WordPress_App",
    "deploymentId": "d-D8QN3TTY1",
    "deploymentGroup": "WordPress_DepGroup_Prod",
    "instanceGroupId": "afa0fff5-efbe-4596-8631-5122dee2006c",
    "state": "SUCCESS",
    "region": "ap-northeast-1"
  },
  "resources": [
    "arn:aws:codedeploy:ap-northeast-1:731352774671:deploymentgroup:WordPress_App/WordPress_DepGroup_Prod",
    "arn:aws:codedeploy:ap-northeast-1:XXXXXXXXXXXX:application:WordPress_App"
  ],
  "additionalAttributes": {}
}

ちゃんとProdのデプロイグループに対してデプロイが成功していますね。これで「ボタンを押すだけで簡単デプロイ」は実現できました!

いいのか?これで終わってしまって本当にいいのか?

これ簡単に実行できるのはとてもいいんですけど、まあまあ事故りそうですよね。

  • シングルクリックのつもりが長押ししちゃった!
  • 今やっちゃいけないタイミングだったのに確認せずに勝手にデプロイしちゃった!

とか起こりそうです。(シングルクリックと長押しの境目は1.2秒。そうそうミスらないでしょうが。。)

簡単であるが故に、簡単に間違いも起こってしまうんですよね。自分だけで使う開発環境は自分のタイミングでいいだろうけど、複数の人が使うテスト環境、ましてユーザーが使う本番環境はこれではまずいです。

この問題を解決するのが今回の記事の本題です。

複数人いないと発動できないようにしよう

この問題を解決するため、複数人いないと、つまり複数個のボタンがないとデプロイできなくなるよう、以下の作戦を考えました。

  • クリックされたらSIMのタグにクリック種類と最後に発生したタイムスタンプを保存
  • 同じSIMグループに存在するSIMリストを取得する
  • SIMのタグを確認し、同じクリック種類で一定時間以内(30秒くらいが妥当?)に押されたSIMの枚数を確認する
  • 上記のSIM枚数が2枚(枚数はパラメータ化してもいい)以上あればデプロイを発動する

これであれば、自分一人ではデプロイできないので、うっかり長押ししても大丈夫ですし、誰にも確認せずにデプロイすることもできません。なかなかいいんじゃないでしょうか?

ボタングループ対応の準備

まずLambdaからSORACOM APIにアクセスするユーザーを作る必要があります。先ほどの記事でほぼ説明されていますが、実行させるAPIが違いますので、そこだけ変更しましょう。以下が必要になります。

  • SIMの情報を取得する(Subscriber:getSubscriber)
  • SIMのタグを設定する(Subscriber:putSubscriberTags)
  • SIMのタグを削除する(Subscriber:deleteSubscriberTag)
  • SIMグループ内のSIMリストを取得する(Group:listSubscribersInGroup)
{
  "statements": [
    {
      "api": [
        "Subscriber:getSubscriber",
        "Subscriber:putSubscriberTags",
        "Subscriber:deleteSubscriberTag",
        "Group:listSubscribersInGroup"
      ],
      "effect": "allow"
    }
  ]
}

続けてLambdaの環境変数を設定します、ユーザーの認証情報を作成すると発行される認証キーIDをAUTH_KEY_ID、認証キーシークレットとしてAUTH_KEYをLambdaの環境変数に設定して保存します。

LambdaからSORACOM APIの呼び出し

LambdaからいくつかのSORACOM APIを呼び出す必要があるのですが、実は先ほどの記事を読んだソラコムのoguさんがSORCOM APIを簡単に使えるSORACOM CLIをLambda Layerで用意してくれました!

こちらの記事をご覧ください
SORACOM の利用料金を定期的に Slack に通知する

これで僕たちは自分のLambdaでこのLambda Layer(arn:aws:lambda:ap-northeast-1:717257875195:layer:soracom-cli-052:1)を参照するだけでORACOM CLIを使うことができます!

スクリーンショット 2020-02-01 11.57.16.png

スクリーンショット 2020-02-01 12.00.00.png

これでLambda内からsoracom-cliが呼び出せます。とても便利!ありがたく使わせてもらいます。

これを利用してLambdaのコードをこのように変更します。

require 'json'
require 'aws-sdk'

TIME_THRESHOLD = 30
BUTTON_COUNT_THRESHOLD = {
  'SINGLE' => 1,
  'DOUBLE' => 2,
  'LONG' => 3
}.freeze

DEPLOY_TARGETS = {
  'SINGLE' => 'Dev',
  'DOUBLE' => 'Test',
  'LONG' => 'Prod'
}.freeze

def lambda_handler(event:, context:)
    click_type = event['clickTypeName']
    imsi = context.client_context['imsi']
    sim_info = JSON.parse(soracom("subscribers get --imsi #{imsi}"))

    click_type_tag = [{ "tagName" => click_type, "tagValue" => Time.now.to_i.to_s }]
    soracom("subscribers put-tags --imsi #{imsi} --body '#{JSON.generate(click_type_tag)}'")
    group_id = sim_info['groupId']
    sims_in_group = JSON.parse(soracom("groups list-subscribers --group-id #{group_id}"))
    button_count = sims_in_group.count { |sim| sim['tags'].key?(click_type) && sim['tags'][click_type].to_i > Time.now.to_i - TIME_THRESHOLD }
    if button_count >= BUTTON_COUNT_THRESHOLD[click_type]
      sims_in_group.each { |sim| soracom("subscribers delete-tag --imsi #{sim['imsi']} --tag-name #{click_type}")}
      target = DEPLOY_TARGETS[click_type]
      deploy(target)
      "Deploy is started!"
    else
      "Deploy is not started."
    end
end

def soracom(args)
    `soracom --auth-key-id #{ENV['AUTH_KEY_ID']} --auth-key #{ENV['AUTH_KEY']} #{args}`
end

def deploy(target)
    client = Aws::CodeDeploy::Client.new
    client.create_deployment({
      application_name: "WordPress_App",
      deployment_group_name: "WordPress_DepGroup_#{target}",
      revision: {
        revision_type: "S3",
        s3_location: {
          bucket: "codedeploydemobucket-button",
          key: "WordPressApp.zip",
          bundle_type: "zip"
        }
      },
      deployment_config_name: "CodeDeployDefault.OneAtATime",
      description: "DeployTo#{target}"
    })
end

こうすると、開発環境デプロイはボタン1個でできますが、テスト環境デプロイの時はボタン2個、本番環境デプロイの時はボタン3個必要になります。よほどのことがなければ間違えてデプロイしてしまうということは起こらないでしょう。

それではテスト環境のデプロイをやってみます。まずはボタンを1個だけダブルクリックすると、、

button2.jpg

ボタンは緑に光りましたが、デプロイは起こりませんでした。タグにはDOUBLEが残っていますね。

スクリーンショット 2020-02-01 12.14.16.png

では2つ同時にダブルクリックしましょう。同時に光ってますね。

button3.jpg

結果は?

{
  "account": "XXXXXXXXXXXX",
  "detailType": "CodeDeploy Deployment State-change Notification",
  "region": "ap-northeast-1",
  "source": "aws.codedeploy",
  "time": "2020-02-01T03:18:00Z",
  "notificationRuleArn": "arn:aws:codestar-notifications:ap-northeast-1:XXXXXXXXXXXX:notificationrule/0fdde84e4187bfd1a6960997e31182cd7fe886a0",
  "detail": {
    "application": "WordPress_App",
    "deploymentId": "d-ZQ9VSAWY1",
    "deploymentGroup": "WordPress_DepGroup_Test",
    "instanceGroupId": "552ba9d8-42a5-4917-8f43-442997f5db8f",
    "state": "SUCCESS",
    "region": "ap-northeast-1"
  },
  "resources": [
    "arn:aws:codedeploy:ap-northeast-1:731352774671:deploymentgroup:WordPress_App/WordPress_DepGroup_Test",
    "arn:aws:codedeploy:ap-northeast-1:XXXXXXXXXXXX:application:WordPress_App"
  ],
  "additionalAttributes": {}
}

テスト環境へのデプロイ起こりました。成功です!やったね!

ちょっと気になる。。

ここまでのところで、誤操作によるデプロイミスは防ぐことができそうです。テスト以上の環境にデプロイする時には自分以外のボタン所持者に声をかけて、同時にボタン押さないとデプロイできないので個人のミスで間違えてデプロイすることはないでしょう(一方で難しい、面倒な操作は要らず、ボタン押すだけというところは変わっていません)

これだけでも十分な気もしますが、これだとボタンの数がそろえば上への確認なしでデプロイできてしまいます。上司やデプロイ責任者など特定の人が持っている特定のボタンが無ければデプロイできない、とすれば、本番環境へのデプロイを上に無断で実施する、ということが防げます。(これが必要ない体制もあると思いますが)

これは上のコードをちょっと改変して、同じクリック種類で一定時間以内に押され、かつ特定のタグを持つSIMが無ければ実行しない、とすればよさそうです。

承認つきデプロイ

まず上司の持つボタンのタグにSERVICE_OWNERタグをつけます。

スクリーンショット 2020-02-01 12.33.56.png

そしてSERVICE_OWNERタグが入っていることをチェックするコードを追加します。

require 'json'
require 'aws-sdk'

TIME_THRESHOLD = 30
BUTTON_COUNT_THRESHOLD = {
  'SINGLE' => 1,
  'DOUBLE' => 2,
  'LONG' => 3
}.freeze
OWNER_COUNT_THRESHOLD = {
  'SINGLE' => 0,
  'DOUBLE' => 0,
  'LONG' => 1
}.freeze
DEPLOY_TARGETS = {
  'SINGLE' => 'Dev',
  'DOUBLE' => 'Test',
  'LONG' => 'Prod'
}.freeze

def lambda_handler(event:, context:)
    click_type = event['clickTypeName']
    imsi = context.client_context['imsi']
    sim_info = JSON.parse(soracom("subscribers get --imsi #{imsi}"))

    click_type_tag = [{ "tagName" => click_type, "tagValue" => Time.now.to_i.to_s }]
    soracom("subscribers put-tags --imsi #{imsi} --body '#{JSON.generate(click_type_tag)}'")
    group_id = sim_info['groupId']
    sims_in_group = JSON.parse(soracom("groups list-subscribers --group-id #{group_id}"))
    button_count = sims_in_group.count { |sim| sim['tags'].key?(click_type) && sim['tags'][click_type].to_i > Time.now.to_i - TIME_THRESHOLD }
    owner_count = sims_in_group.count do |sim|
      sim['tags'].key?(click_type) && sim['tags'][click_type].to_i > Time.now.to_i - TIME_THRESHOLD && sim['tags'].key?("SERVICE_OWNER")
    end
    if button_count >= BUTTON_COUNT_THRESHOLD[click_type] && owner_count >= OWNER_COUNT_THRESHOLD[click_type]
      sims_in_group.each { |sim| soracom("subscribers delete-tag --imsi #{sim['imsi']} --tag-name #{click_type}")}
      target = DEPLOY_TARGETS[click_type]
      deploy(target)
      "Deploy is started!"
    else
      "Deploy is not started."
    end
end

def soracom(args)
    `soracom --auth-key-id #{ENV['AUTH_KEY_ID']} --auth-key #{ENV['AUTH_KEY']} #{args}`
end

def deploy(target)
    client = Aws::CodeDeploy::Client.new
    client.create_deployment({
      application_name: "WordPress_App",
      deployment_group_name: "WordPress_DepGroup_#{target}",
      revision: {
        revision_type: "S3",
        s3_location: {
          bucket: "codedeploydemobucket-button",
          key: "WordPressApp.zip",
          bundle_type: "zip"
        }
      },
      deployment_config_name: "CodeDeployDefault.OneAtATime",
      description: "DeployTo#{target}"
    })
end

OWNER_COUNT_THRESHOLDが本番環境(LONG)だけ1になっているので、開発環境やテスト環境は承認なしで実行でき、本番環境は承認必要ということですね。

それでは実行してみましょう。SERVICE_OWNERのタグがついていないボタンを3個用意して長押ししました。

button4.jpg

全部光り、かつデプロイは発生しませんでした。いいですね。ではSERVICE_OWNERタグのついているボタンを用意して、長押してみましょう。

button5.jpg

なんで1個ひげボタンなのかって?上司なんだからひげくらい生えててもいいんじゃないですかね(僕の実際の上司はひげ生やしてませんが)これがSERVICE_OWNERタグをつけた上司ボタンです。そしてしばらくすると、、

{
  "account": "XXXXXXXXXXXX",
  "detailType": "CodeDeploy Deployment State-change Notification",
  "region": "ap-northeast-1",
  "source": "aws.codedeploy",
  "time": "2020-02-01T03:51:04Z",
  "notificationRuleArn": "arn:aws:codestar-notifications:ap-northeast-1:XXXXXXXXXXXX:notificationrule/0fdde84e4187bfd1a6960997e31182cd7fe886a0",
  "detail": {
    "application": "WordPress_App",
    "deploymentId": "d-OLKNAPWY1",
    "deploymentGroup": "WordPress_DepGroup_Prod",
    "instanceGroupId": "afa0fff5-efbe-4596-8631-5122dee2006c",
    "state": "SUCCESS",
    "region": "ap-northeast-1"
  },
  "resources": [
    "arn:aws:codedeploy:ap-northeast-1:731352774671:application:WordPress_App",
    "arn:aws:codedeploy:ap-northeast-1:XXXXXXXXXXXX:deploymentgroup:WordPress_App/WordPress_DepGroup_Prod"
  ],
  "additionalAttributes": {}
}

本番環境へのデプロイが発動&成功しました!これで特定の人の承認が無いと本番デプロイできない状態にもなりましたね。仮に上司がITに疎い人であったとしても、ボタン長押しできない人はいないと思いますのでまず大丈夫です。また、判断材料はSORACOMのタグなので、権限ある人は複数いて、そのうちの1人が押せば良い、ということも簡単に対応できますね。フレキシブルに対応できるのが、SORACOMのボタンとクラウドの組み合わせのいいところです。

おわりに

この記事はデプロイを簡単にするのがテーマでしたが、裏のテーマは「ボタンをグループで使ったら面白いのでは?」でした。

ボタン単体でもいろいろな使われ方をされていますが、今回みたいにグループにしてその中でN個そろったら発動、といった使い方もできます。4個そろえてミナデインとか、位置も取得できるから五芒星の頂点に立って同時に押して結界を張るとかもできますよね

あとは○×の投票とかも、ボタン配ってシングル(○)かロング(×)か押してもらって押したらそのタグがついて、グループ内のタグを数えて集計、とかするとリアルタイム投票システムが1時間くらいで作れそう。ボタン100個くらい持っていればですが。他にも考えればあれこれできそうな可能性を感じます。

まだまだボタン面白そうなので、色々考えてみます。以上です!

1stship
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした