LambdaでAWSの料金を毎日Slackに通知する(Python3)

はじめに

個人アカウントは基本的に無料枠で運用しているので、少しでも請求がある場合はいち早く気づきたいです。
先日、とあるハンズオンイベントで使ったリソースを消し忘れて、最終的に$10ぐらい請求が来てしまいました。。。

CloudWatchで請求アラートは設定していますが、閾値超えが既知の場合、当然見逃すため、最終的な請求額に驚くハメになります。

これを防ぐためにLambdaで毎日SlackにAWS料金を通知することにします。

先日LambdaがPython3に対応したので、せっかくだし勉強がてらPython3で実装したい。
ネット上にはNode.jsでの実装例が多いようで、今回はこちらを参考にPython3で実装してみます。

必要なもの

  • Slack

    • incoming-webhooks URL

    • 適当なchannel
  • lambda-uploader
    • requestsモジュールをLambda上でimportするために利用

      • カレントディレクトリにモジュールをインストールして、モジュールごとZipに固めてアップロードでもいけるはずですが、私の環境だとうまくいかなかったので
  • aws cli
    • lambda-uploaderで必要
  • AWS
    • Lambda関数用IAM Role

      • CloudWatchReadOnlyAccessポリシーをアタッチ

事前準備

lambda-uploaderをインストール

$ pip install lambda-uploader 

こちらを参考にさせていただきました。

aws cliをインストール

$ pip install awscli

credential、リージョン設定

$ aws configure

確認
$ aws configure list

コード

ディレクトリ構成

ディレクトリ名は任意です。関数名とは無関係です。

ディレクトリ構成
awscost_to_slack/
|--lambda_function.py
|--requirements.txt
|--lambda.json

lambda_function.py

超過金額に応じて色をつけるようにしています。
\$0.0なら緑、超過したら黄色、\$10超えで赤になります。

lambda_function.py
#!/usr/bin/env python
# encoding: utf-8

import json
import datetime
import requests
import boto3
import os
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Slack の設定
SLACK_POST_URL = os.environ['slackPostURL']
SLACK_CHANNEL = os.environ['slackChannel']

response = boto3.client('cloudwatch', region_name='us-east-1')

get_metric_statistics = response.get_metric_statistics(
    Namespace='AWS/Billing',
    MetricName='EstimatedCharges',
    Dimensions=[
        {
            'Name': 'Currency',
            'Value': 'USD'
        }
    ],
    StartTime=datetime.datetime.today() - datetime.timedelta(days=1),
    EndTime=datetime.datetime.today(),
    Period=86400,
    Statistics=['Maximum'])

cost = get_metric_statistics['Datapoints'][0]['Maximum']
date = get_metric_statistics['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')

def build_message(cost):
    if float(cost) >= 10.0:
        color = "#ff0000" #red
    elif float(cost) > 0.0:
        color = "warning" #yellow
    else:
        color = "good"    #green

    text = "%sまでのAWSの料金は、$%sです。" % (date, cost)

    atachements = {"text":text,"color":color}
    return atachements

def lambda_handler(event, context):
    content = build_message(cost)

    # SlackにPOSTする内容をセット
    slack_message = {
        'channel': SLACK_CHANNEL,
        "attachments": [content],
    }

    # SlackにPOST
    try:
        req = requests.post(SLACK_POST_URL, data=json.dumps(slack_message))
        logger.info("Message posted to %s", slack_message['channel'])
    except requests.exceptions.RequestException as e:
        logger.error("Request failed: %s", e)

requirements.txt

pip installしたいモジュール名を書きます。

requirements.txt
requests

lambda.json

Lambda関数名、IAM RoleのARNなどは環境に合わせてください。
スクリプト本体のファイル名とハンドラの前半を一致させないと動きません。地味にハマるので注意!

lambda.json
{
  "name": "LAMBDA_FUNCTION_NAME",
  "description": "DESCRIPTION",
  "region": "ap-northeast-1",
  "handler": "lambda_function.lambda_handler",
  "role": "arn:aws:iam::XXXXXXX:role/ROLE_NAME_FOR_LUMBDA",
  "timeout": 300,
  "memory": 128
}

デプロイ

上記ファイルを配置したディレクトリに移動して、lambda-uploaderを実行します。

$ cd awscost_to_slack
$ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin

Lambda環境変数設定

今回のLambda関数では、通知先SlackチャネルとWebhooks URLを環境変数で渡すようにしたので、設定します。

スクリーンショット 2017-06-23 14.51.30.png

lambda-uploaderのlambda.jsonに書けそうなのですが、書式が分からず、今回はマネコンで設定しました。
lambda-uploaderでLambda関数を更新すると消えてしまうので注意。

Lambda定期実行設定

CloudWatchのスケジュールイベントを定義して、lambda関数をターゲットに指定します。
時刻はUTCなので注意しましょう。
毎日UTC0:00に実行されるよう設定しました。

スクリーンショット 2017-06-23 15.01.27.png

実行イメージ

スクリーンショット 2017-06-23 14.15.54.png
こんな感じで毎朝9:00に通知がきます。

まとめ

Lambdaはほぼ無料でプログラムが動かせるので楽しいですね!
Python初心者なのでコードが見苦しい点はご容赦ください。

続きを読む

AWS Lambda + Serverless Framework + Slackで作るカスタマイズ可能なfeedリーダー その1

概要

AWS Lambda + Serverless Framework + Slackを使ってカスタマイズ出来るfeedリーダーを作ろうという記事のその1です。
(最初は一つの記事で完結させようとしていたのですがやることが多すぎました…なお続編があるかどうかは不明)

こんなの作ったよ系の記事なので、ツールとかフレームワークの説明は少なめかも。

feedの例:
slack.png

そもそもfeedリーダーを作ろうと思った動機

実はfeedの機能自体はSlackにあって、各チャンネルで

/feed subscribe https://hoge/feed 

のように打つとそのチャンネルに定期的にフィードの記事が流れてきます。ただ

  • 一つ一つの記事のdescriptionがとても長い
  • feedによっては大きいサイズの画像が表示される

などの理由から、流れてくる記事をカスタマイズしたいなーという思いから作り始めました。

Serverless Frameworkの説明

Serverless Frameworkは、AWS Lambdaなどの、関数単位で提供されているサービスの環境準備やデプロイ、パッケージング、バージョン管理、ローカルでのデバッグなどをやってくれるフレームワーク・ツールです。
3か4つぐらいのコマンドさえ覚えておけばなんとかなるので、非常に使いやすいです。
詳しくは以下の記事が分かりやすいかと思います。

具体的なデプロイのコマンドなどは後述のhttps://github.com/yudetamago/my_rss_to_slack_service
を参照のこと。

今回の記事で出来たもの

とりあえず

  • Serverless Frameworkを使ったデプロイ
  • デプロイした関数をデバッグ実行するとfeed(今のところRSSのみ)を取ってきてSlackのwebhookに送信する

という最低限feedの内容をSlackに引っ張ってこれるところまで出来ました。
コードではサンプルとしてAmazon Web Services ブログのfeedを取ってきています。

コードの仕組み・説明

出来たもの
https://github.com/yudetamago/my_rss_to_slack_service

全体としてはfeedをHTTP GETで取ってきて、Slack通知用に変換して送信しているだけだったりします。
(今回は最低限のところしか作っていないので、自分で関数を呼び出してfeed内容を取得してくるだけです。。)

Lambda Functionの機能自体に関連する部分

まず serverless.yml で指定した feedToSlack という関数を handler.jsmodule.exports で定義します。そして後述するfeedの処理を終えてレスポンスを返すときには、引数の callbackにレスポンス内容を入れて callback(null, response) のように呼び出すとLambdaの処理は終了します。

feedの処理

今回はfeedParserというライブラリを使っていて、リクエストをそのまま pipe でパーサーのほうに流しています。記事を読み込むと parser.on('readable') のコールバック関数が呼ばれるので、そこで読み込んだ記事を items に入れたあと、slackのattachmentsに入れています。

これからやること/やりたいこと

  • descriptionにHTMLタグが入っているときの処理

    • 現状ではタグがそのままplain textで表示されるので、どうしようかな…という気持ち。
  • RSSやAtomなどフィードの種類に依存しないようにする
    • item["rss:description"]["#"] が依存している部分だけれど、これは正直頑張るしかない。
  • 同じitemが2回以上送られないよう重複管理
    • これだけはステートフルになってしまうので、何らかの方法で楽したい…
  • 定期的にフィードを取ってくるようにする
  • 複数のフィードに対応する
    • 1つのLambda Functionで並列にHTTP GETする(そもそも出来るの?)か、フィードごとにLambda Function作るかみたいなところから検討する感じ。

まとめ

とりあえず最低限のところまでは出来たので、気力があれば続き作ります!

続きを読む

EC2 インスタンスを Chat Bot で管理する

こないだ Amazon EC2 でディープラーニングできる GPU インスタンス1を作ったのだが、趣味で使うにしてはまぁ料金が高いので常時起動させておくのはもったいなく、使わないときはインスタンスを停止させるようにしている。

しかし使い始めるときと使い終わったときにいちいち AWS 管理コンソールにログインしてインスタンスの起動/停止をするのが面倒だったので、我が家の Slack Bot から EC2 インスタンスを管理できるようにしてみた。

最終的にできたものはこういう感じ。

以下、作り方。

Chat Bot 用の AWS ユーザを作成する

既存のユーザのアクセスキーを利用してもできるが、セキュリティのために専用のユーザを作成して必要な権限のみを付与するのが好ましい。

今回は API を叩くためのユーザなので「プログラムによるアクセス」にチェックを入れる。

ユーザを作成すると、API を叩く際に必要なアクセスキー (AccessKeyId, SecretAccessKey) が生成されるので控えておく。

EC2 を操作できるポリシーを割り当てる

作成したユーザに必要な権限を付与する。

今回やりたいことは

  • EC2 インスタンスの一覧とステータスを取得する
  • EC2 インスタンスを起動させる
  • EC2 インスタンスを停止させる

の3つなので、それに合わせて以下のようなインラインポリシーを割り当てる。

AllowEC2InstanceManagement
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstanceStatus",
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "*"
        }
    ]
}

今回はすべてのインスタンスを操作できるようにするために "Resource": "*" としたが、ARN を指定することで「特定のインスタンスのみを操作可能」というような制限をすることも出来る。

Chat Bot から AWS API を叩く

我が家の Chat Bot は Node.js で動作しているので、AWS JavaScript SDK を利用して AWS API を叩く。

AWS JavaScript SDK をインストール

npm からインストールできる。

$ npm install aws-sdk

アクセスキーの設定

AWS SDK を読み込んでアクセスキーを設定する。

const AWS = require('aws-sdk');

// アクセスキーは環境変数から読み込む
AWS.config.update({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  region: 'ap-northeast-1', // 東京リージョン
});

// EC2 インスタンスを操作するためのオブジェクトを生成
const ec2 = new AWS.EC2();

アクセスキーをハードコートせずに環境変数から読み込んでいるので、Bot を動作させる環境の環境変数 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY にアクセスキーを設定しておく必要がある。

インスタンスの名前をつける

チャットから ec2 gpgpu start のようにインスタンスの名前を指定して起動/停止できるようにしたいので、インスタンス ID と名前を関連付けられるようにしておく。

// 名前から起動/停止できる EC2 インスタンスの一覧
const instances = [
  { id: "i-01315ab8f6f7015e5", name: "GPGPU" },
];

// 名前からインスタンス ID を返す
function getInstanceId(name) {
  const instance = instances.filter(i => i.name.toLowerCase() === name.toLowerCase())[0];
  return instance ? instance.id : null;
}

// インスタンス ID から名前を返す
function getInstanceName(id) {
  const instance = instances.filter(i => i.id === id)[0];
  return instance ? instance.name : null;
}

Bot のアクションを定義する

チャットから Bot にコマンドメッセージが投稿されたときに AWS API を叩くようなアクションを定義する。

以下のコードは自作の Bot フレームワーク2で動くコードだが、インタフェースを Hubot に似せて作っているので Hubot でもだいたい同じようなノリで書けると思う。

module.exports = (bot) => {

  // 指定した EC2 インスタンスを起動する
  bot.respond(/^ec2 ([w_]+) start$/i, (msg) => {
    const id = getInstanceId(msg.match[1]);
    if (!id) return msg.send('知らないインスタンスですね・・・');
    ec2.startInstances({ InstanceIds: [id] }, (err) => {
      if (err) {
        bot.logger.error(err);
        msg.send('インスタンスの起動に失敗しました...');
      } else {
        msg.send('インスタンスを起動しました');
      }
    });
  });

  // 指定した EC2 インスタンスを停止する
  bot.respond(/^ec2 ([w_]+) stop$/i, (msg) => {
    const id = getInstanceId(msg.match[1]);
    if (!id) return msg.send('知らないインスタンスですね・・・');
    ec2.stopInstances({ InstanceIds: [id] }, (err) => {
      if (err) {
        bot.logger.error(err);
        msg.send('インスタンスの停止に失敗しました...');
      } else {
        msg.send('インスタンスを停止しました');
      }
    });
  });

  // すべての EC2 インスタンスの起動状況を取得する
  bot.respond(/^ec2 status$/i, (msg) => {
    const stateIcons = {
      running: 'large_blue_circle',
      terminated: 'red_circle',
      stopped: 'white_circle',
    };
    ec2.describeInstanceStatus({ IncludeAllInstances: true }, (err, data) => {
      if (err) {
        bot.logger.error(err);
        return msg.send('インスタンス情報の取得に失敗しました...');
      }
      const text = data.InstanceStatuses.map((instance) => {
        const id = instance.InstanceId;
        const state = instance.InstanceState.Name;
        const icon = stateIcons[state] || 'large_orange_diamond';
        const name = getInstanceName(id);
        const nameLabel = name ? ` (*${name}*)` : '';
        return `:${icon}: `${id}`${nameLabel} is ${state}`;
      });
      msg.send(text.join('n'));
    });
  });

  // インスタンスが起動中の場合は1時間に1回通知する
  bot.jobs.add('0 0 * * * *', () => {
    ec2.describeInstanceStatus((err, data) => {
      if (err) {
        bot.logger.error(err);
        return bot.send('インスタンス情報の取得に失敗しました...');
      }
      const count = data.InstanceStatuses.length;
      if (count > 0) bot.send(`${count} 台の EC2 インスタンスが起動中です`);
    });
  });

};

今回は start / stop / status の3つのコマンドに加えて、停止し忘れを防ぐために「一時間に一回通知する」機能も実装した。

ec2 オブジェクトのメソッドを使っている箇所が API を叩いている箇所なわけだが、メソッドのオプションなどの詳細は AWS JavaScript SDK のドキュメント3を参照して欲しい。

動かす

Bot をデプロイしてコマンドを投げてみると、こういう感じ。

これで AWS の管理コンソールにログインしなくても EC2 インスタンスを操作できるようになった。便利。

続きを読む

EC2上にhubotを設置してslackと連携させる

最近ChatOpsに興味が出てきたので、とりあえずEC2上にhubotが動作する環境を作り、slackと連携出来るようにしました。

前提

以下の環境が既にあること。

  • EC2
  • slack

環境構築

  • nvm〜hubotの起動までは全てEC2上での作業です。

nvmのインストール

  • curlでインストールします
sudo curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
  • .bash_profileに以下を追記します
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
  • nvmコマンドが実行できることを確認します
nvm help

nodejsのインストール

  • nvmでnodejsのv6.11.0をインストールします
  • 公式サイトを見ると includes npm 3.10.10 と書いてあるのでnpmも一緒にインストールされます
nvm install 6.11.0
  • インストールされたことを確認します
node -v
npm -v

redisのインストール

  • elasticacheは使わずAmazon Linux上にredisをインストールします
  • epelだとバージョンが古いようなのでremiを利用します
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo yum --enablerepo=remi install redis
  • redisを起動させます
sudo service redis start

hubotのインストール

  • hubotの環境を構築します
  • npmでhubot,coffescript,redis,yo,generator-hubotをインストールします
npm install -g hubot coffee-script redis yo generator-hubot
  • hubotをインストールします
mkdir -p ~/deploy-sushi
cd deploy-sushi
yo hubot
  • 対話式で色々聞かれるので自身の環境に合わせて入力してください
? ==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== Y/no


省略


? Owner (User <user@example.com>)
? Bot name (deploy-sushi)
? Description (A simple helpful robot for your Company)
? Bot adapter (campfire)
  • 今回は?4つに以下のように答えました
? Owner {MY_EMAIL}
? Bot name deploy-sushi
? Description this is deploy boy
? Bot adapter slack
  • hubotを立ち上げて動作確認をします
bin/hubot
  • いろんなメッセージが出た後にEnterを押せば deploy-sushi> という対話が始まります
deploy-sushi> deploy-sushi ping
deploy-sushi> PONG
  • hubot-slackをnpmでインストールします
npm install hubot-slack

slackと連携させる

  • slack上の操作に変わります
  • slackのサイドメニューを開き Configure Apps を開きます

スクリーンショット 2017-06-17 14.12.32.png

  • 画面上部に Hubot を入力し、検索結果をクリックします

スクリーンショット 2017-06-17 14.16.11.png

  • Installボタンをクリックします

スクリーンショット 2017-06-17 14.17.17.png

  • hubotの名前を入力して Add Hubot Integrationをクリックします

スクリーンショット 2017-06-17 14.18.21.png

  • 作成が完了したらhubotの詳細画面に遷移するので画面上部の Setup Instructions のところに記載されている HUBOT_SLACK_TOKEN={YOUR_API_TOKEN} をコピーしておきます
  • 画像を変更する・・・など何か編集を加えた場合、画面下部の保存ボタンをクリックして更新してください

  • EC2に戻り以下の環境変数をexportします

echo 'export HUBOT_SLACK_TOKEN={YOUR_API_TOKEN}' >> ~/.bash_profile
source ~/.bash_profile
  • アダプタを指定してhubotを起動します
bin/hubot --adapter slack
  • slackのどこかのチャンネルにhubotをinviteして動作確認します

スクリーンショット 2017-06-17 14.29.14.png

  • 動作確認ができたらEC2起動時に自動でhubotが立ち上がるようにします

hubotの永続実行

  • foreverをnpmでインストールします
npm install -g forever
  • bin/hubot を書き換えます
以下を追記
forever start -w -c coffee node_modules/.bin/hubot --adapter slack


以下をコメントアウト
exec node_modules/.bin/hubot --name "deploy-sushi" "$@"

  • バックグラウンドで動くことを確認します
bin/hubot
ps aux | grep hubot

EC2起動時のスクリプト作成

sudo vi /etc/init.d/start_deploy_hubot
#!/bin/bash
#chkconfig: 2345 99 10
#descpriction: start deploy hubot
DIR="/home/ec2-user/deploy-sushi"
cd $DIR
bin/hubot
  • 実行権限を付与します
sudo chmod 755 /etc/init.d/start_deploy_hubot
  • サービスに追加します
sudo chkconfig --add start_deploy_hubot
  • EC2インスタンスを再起動してhubotが立ち上がっていることを確認してください

hubotが起動しなかった・・・

  • npm,foreverのパスがec2-userにしか通っていないかったのでrootでnpm,foreverするとコマンドが見つからない。
  • トークンもec2-userの .bash_profile に記述していたので見つからない。
  • とりいそぎ bin/hubot に以下を追記しました
    • npm install よりも前に追記してください
export PATH="/home/ec2-user/.nvm/versions/node/v6.11.0/bin:$PATH"
export HUBOT_SLACK_TOKEN={YOUR_API_TOKEN}
  • EC2を再起動して、hubotが立ち上がっていることを確認してください

    • プロセスの確認をします
ps aux | grep hubot
  • slackのチャンネルでもpingして返答があることを確認してください

おわり

hubotがサーバ上で動き続けるようになったので、次回はslackからgit操作を出来るようにしようと思います。

続きを読む

SlackからAWS API Gatewayを通してLambdaを起動するまで

AWS LambdaとAPI Gatewayを使いこなすためにいい練習になるかなと思って
SlackからLambdaをキックしたり、起動したLambda functionのログとかをSlackに通知するような仕組みを作ってみる。

構成はこんな感じ
スクリーンショット 2017-06-11 23.57.06.png

この構成ができれば、いわゆるサーバレスアーキテクチャが出来るってことになるのでなかなかアツい。
Lambdaが出来てからサーバレスアプリケーションが最近注目を集めてますよね。
それにbotのためにサーバー(コンテナ)を立てる必要もなくなるのでエコな構成になるんじゃないだろうか。
それではさっそくチャレンジしてみる。

AWS Lambdaについて

Lambdaを触ったことがないって人にはこの記事がおすすめ。細かく丁寧に説明されています。
http://qiita.com/s_s_k/items/b584435120e99d63975b

Lambdaを準備する

まず初めにLambdaに処理を書いていきます。
Node.jsで書く必要があります。javaでも書けるそうですがNode.jsの方がレスポンス早そうなのでこちらを採用します。
内容は簡単で、いくつかのStringメッセージを json 形式に格納して、ランダムに返却するというもの。

index.js
exports.handler = (event, context, callback) => {

    var res = {};
    res.username = "outgoing-webhook-from-lambda";
    var array = [
        'わからないことは<https://api.slack.com/custom-integrations|こちら>を参照してください。',
        'こんにちは、私は'+ res.username +'です。nAWSのLambda functionで処理されています。n',
        'API Gatewayを使ってLambda functionを起動する良い練習になりますね。',
        'outgoing-webhookを使用すればLambda functionをAPI Gateway経由でキックすることができます。'
    ];
    res.text = array[Math.floor(Math.random() * array.length)];

    callback(null, res);
};

今回はSlackに返却するのでOutgoing WebHooksが受けられるような形式にしています。以下がその形式。


{
"username": "発言するbotのユーザ名";
"text": "表示されるテキスト";
}

API Gatewayの準備

Lambda functionを起動するためにAPI GatewayからAPI(POST)してみましょう。
testというメソッドを作成しています。統合リクエストには先ほど設定したLambdaを書いておきましょう。

スクリーンショット 2017-06-13 00.03.11.png

統合リクエストの詳細をみると以下のようになっています。
スクリーンショット 2017-06-13 00.06.03.png

注意しなければいけないのが「本文マッピングテンプレート」の部分。
Slackから飛んでくるPOSTは形式が jsonではなく、form-urlencodedで送られてくるらしい。
そこで「application/x-www-form-urlencoded」の時は json 型に変換するマッピングを定義します。
以下のようにしてあげれば良いと思います。

mapping_template.txt
## convert HTML POST data or HTTP GET query string to JSON

## get the raw post data from the AWS built-in variable and give it a nicer name
#if ($context.httpMethod == "POST")
 #set($rawAPIData = $input.path('$'))
#elseif ($context.httpMethod == "GET")
 #set($rawAPIData = $input.params().querystring)
 #set($rawAPIData = $rawAPIData.toString())
 #set($rawAPIDataLength = $rawAPIData.length() - 1)
 #set($rawAPIData = $rawAPIData.substring(1, $rawAPIDataLength))
 #set($rawAPIData = $rawAPIData.replace(", ", "&"))
#else
 #set($rawAPIData = "")
#end

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())

## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
 #set($rawPostData = $rawAPIData + "&")
#end

## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))

## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])

## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end

## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
  ## finally we output the JSON for this pair and append a comma if this isn't the last pair
  #set($kvTokenised = $kvPair.split("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}

スクリーンショット 2017-06-13 00.13.00.png
この部分ですね。

ここまでできればAPIをデプロイしてやって、URLを作成しましょう。

Slack側の設定

Slack Outgoing WebHooks

integrationメニューから Outgoing WebHooks を検索してください。

image.png

流れにしたがって設定していってください。
ここに先ほどAPI Gatewayで作成したURLを貼り付けるだけでOK

image.png

動かして見ましょう。
いい感じにできました。

image.png

次回はLambdaをDBに繋ぐ or Cognito の UserPool を使って認証を簡単に作成したりしてみようと思います。

続きを読む

IAMユーザーのAWSコンソール・CLIでの操作をSlackに通知する

概要

AWSコンソール、AWS CLIでの作業はオペミスがつきものです。
IAMで実行権限を制限する、手作業を減らすなどの対策も大切ですが、不正な変更をいち早く検知する仕組みがあるとより安心です。
今回は、次のような仕組みを実現してみたいと思います。

IAMユーザーのAWSコンソール・CLIでの操作をSlackに通知する

次の記事を参考にさせてもらいました。
セキュリティーグループが変更されたらSlackに通知する -ハンズラボエンジニアブログ

CloudTrailの設定

有効化

まずは、CloudTrailを有効化します。
AWSコンソールにログインし、CloudTrailのメニューを開きます。
まだCloudTrailの設定を行っていな環境では、以下のような画面が表示されるので、「今すぐ始める」を選択します。

start_imidiatory.png

CloudTailの有効化では、任意の「追跡名」を設定します。
※今回はすべてのリージョンを対象に適用します

enable_cloudtail.png

保存先の設定

CloudTrailで取得した追跡情報はログファイルとしてS3に保存されます。次のように保存先バケットを設定を行います。
ストレージの場所で、新しいS3バケットを作成しますかを「はい」にし「S3バケット」の名称を設定します。
※今回はプレフィックスの設定は省略し、その他の項目はデフォルトのまま進めます
スクリーンショット 2017-06-10 16.17.05.png

作成

最後に「作成」を選択してCloudTrailの作成は完了です。

CloudWatch Logsの設定

前記までで作成した追跡情報をCloudWatch Logsへ配信するように設定します。

設定

CloudTrailのメニューを開き、先ほど作成した追跡情報を選択します。
スクリーンショット_2017-06-10_16_19_26.png

CloudWatch Logsの項目にある「設定」を選択します。

スクリーンショット 2017-06-10 16.36.54.png

ロググループ名を入力し「次へ」を選択します。
スクリーンショット 2017-06-10 16.36.54.png

IAMロールの設定を求められるので、適切なロール選択するか新規で作成します。
スクリーンショット 2017-06-10 16.37.27.png

「許可」を選択してCloudWatch Logsの設定は完了です。

Lambdaの設定

CloudWatchのログストリームを受けて、Slackに内容を通知するLambda functionを用意します。
今回は、Serverless Frameworkを使ってLambda functionのデプロイを行いました。
開発環境構築は→ serverlessでLambdaのローカル開発環境を整える

プロジェクトの作成

次のコマンドでプロジェクトを初期化します。

$ sls create --template aws-nodejs --path CloudTrail-Slack

必要パッケージもインストールしておきましょう。

$ npm install async
$ npm install request

functionの作成

プロジェクト作成の手順で、プロジェクトフォルダに作成されたhandler.jsを編集します。

handler.js
'use strict';
const zlib = require('zlib');
const async = require('async');
const request = require('request');

// set slack setting
const slack_endpoint = 'https://slack.com/api/chat.postMessage';
const slack_token = process.env.SLACK_TOKEN;
const slack_channel = process.env.SLACK_CHANNEL;
const slack_user = process.env.SLACK_USERNAME;

module.exports.notify = (event, context, callback) => {
    const payload = new Buffer(event.awslogs.data, 'base64');
    zlib.gunzip(payload, (err, res) => {
        if (err) {
            return callback(err);
        }
        const parsed = JSON.parse(res.toString('utf8'));
        console.log('Decoded payload:', JSON.stringify(parsed));

        async.map(parsed.logEvents, (data, callback) => {
          notifySlack(data, callback);
        }, (err, results) =>{
          if (!err) {
            console.log(results);
            callback(null, 'success');
          }
        });
    });
}

let notifySlack = (data, callback) => {
  let log = JSON.parse(data.message);

  let attachment = {
    "fallback": log.eventName + " by " + log.userIdentity.userName,
    "color": "#36a64f",
    "author_name": log.userIdentity.userName,
    "title": log.eventName,
    "fields": [
      {
        "title": "awsRegion",
        "value": log.awsRegion,
        "short": false
      },
      {
        "title": "eventSource",
        "value": log.eventSource,
        "short": false
      },
      {
        "title": "eventId",
        "value": log.eventID,
        "short": false
      }
    ],
    "thumb_url": "https://d0.awsstatic.com/security-center/KMS_Benefit_100x100_Compliance.png",
    "ts": new Date(log.eventTime).getTime() / 1000
  }

  let payload = {
    form: {
      token: slack_token,
      channel: slack_channel,
      username: slack_user,
      attachments: JSON.stringify([attachment])
    }
  }
  // post slack message
  request.post(slack_endpoint, payload, (error, response, body) => {
        if (error) {
          console.log(error)
        } else {
          callback(null, 'slack api call success');
        }
      }
  )
}

serverlessの設定

プロジェクト作成の手順で、プロジェクトフォルダに作成されたserverless.yamlを編集します。
Slackの投稿先に関する設定は環境変数から取得するようになっています。(AWSのコンソール上から設定変更可能)
また、必要に応じてリージョンの設定なども行います。

serverless.yml
service: cloudtail-lambda # NOTE: update this with your service name

provider:
  name: aws
  runtime: nodejs6.10

functions:
  notify:
    handler: handler.notify

#    Define function environment variables here
    environment:
      SLACK_TOKEN: <すらっくのAPIとーくん>
      SLACK_CHANNEL: cloudtrail-test
      SLACK_USERNAME: CloudTrail

デプロイ

次のコマンドでデプロイします。

$ sls deploy

AWS コンソール上からもデプロイしたLambda functionが確認できました。
スクリーンショット_2017-06-10_19_02_35.png

Slackの設定

API Tokenの取得

以下の記事を参考にさせてもらいました。
Slack APIのTokenの取得・場所

チャンネルの作成

「serverlessの設定」でSLACK_CHANNELに設定したチャンネルを事前に作成しておきます。
スクリーンショット 2017-06-10 17.24.39.png

SNSとの連携

ストリームの開始

AWSコンソールから、CloudWatchのメニューを開きます。
「ログ」のメニューを開き、ロググループ一覧から今回作成したロググループを選択し、「アクション」から「Lambdaサービスへのストリーミングの開始」を選択します。
スクリーンショット_2017-06-10_17_27_52.png

ストリーム先の選択

今回作成したLambda functionを選択した後、「次へ」を選択します。
スクリーンショット_2017-06-10_18_31_49.png

フィルタ設定

ログの形式には「AWS CloudTrail」を選択した後、サブスクリプションフィルターのパターンを設定します。
スクリーンショット_2017-06-10_18_00_32.png

{ ($.userIdentity.type = "IAMUser") && (($.eventSource = "ec2.amazonaws.com") || ($.eventSource = "s3.amazonaws.com") || ($.eventSource = "iam.amazonaws.com") || ($.eventSource = "rds.amazonaws.com") || ($.eventSource = "signin.amazonaws.com"))}

$.userIdentity.type = "IAMUser"では、通知するユーザーの範囲をIAMユーザーに絞っています。これにより、Rootユーザー、IAMロールからのAPIコールは対象外となります。

$.eventSource = "<サービス名>.amazonaws.com"では、通知するサービスの範囲を絞っています。上記フィルタ設定では

  • EC2
  • S3
  • IAM
  • RDS
  • ログイン関連

のみに絞っています。

確認

IAMユーザーでAWSコンソールにログイン。S3バケットの作成を行いました。
Slackには次のような通知が確認できました。
スクリーンショット 2017-06-10 18.59.21.png

以上です。
簡単に実現できました:baby_chick:

続きを読む

AWS LambdaにSSL証明書の有効期限を確認して通知するSlackボットを作成した

はじめに

現在、私がプライベートで持っているサーバのSSL証明書は Let's Encrypt で設定します。
Let's Encrypt は証明書の期限についてはメールでお知らせしてくれますが、メールだと他のメールと埋もれて確認漏れが起こっていたりしていました。

自動で更新したらええやん! というようなご指摘はあるかと思いますが、今回はそれは無視で

環境

ローカル

  • Python 3.6
  • macOS Sierra 10.12.4

AWS

  • Lambda

    • Python 3.6
  • CloudWatch

リポジトリ

https://github.com/nnsnodnb/slackbot_ssl_expiration

準備

Apps & integrationsBots を設定しておいてください

Bots___ひやかしプロジェクト_Slack.png

ライブラリ

requirements.txt
appdirs==1.4.3
packaging==16.8
pyparsing==2.2.0
requests==2.13.0
six==1.10.0
slacker==0.9.42

サンプルソース

bot.py
from slacker import Slacker
import datetime
import socket
import ssl
import slack_settings # 同ディレクトリに slack_settings.py を配置


slack = Slacker(slack_settings.SLACK_API_TOKEN)


def ssl_valid_time_remaining(hostname):
    expires = ssl_expiry_datetime(hostname)
    return expires - datetime.datetime.utcnow()


def ssl_expires_in(hostname, buffer_days=7):  # 7日前で期限分岐
    remaining = ssl_valid_time_remaining(hostname)
    if remaining < datetime.timedelta(days=0):
        raise AlreadyExpired("Cert expired %s days ago" % remaining.days)
    elif remaining < datetime.timedelta(days=buffer_days):
        return True
    else:
        return False


def ssl_expiry_datetime(hostname):
    ssl_date_fmt = r'%b %d %H:%M:%S %Y %Z'
    context = ssl.create_default_context()

    conn = context.wrap_socket(
            socket.socket(socket.AF_INET),
            server_hostname=hostname,
    )

    conn.settimeout(3.0)
    conn.connect((hostname, 443))
    ssl_info = conn.getpeercert()
    return datetime.datetime.strptime(ssl_info['notAfter'], ssl_date_fmt)  # ssl_info['notAfter'] が証明書の期限


def post_slack(hostname):
    message = '@channel https://' + hostname + ' '
    if ssl_expires_in(hostname):
        message += 'そろそろヤバイ'
    else:
        message += 'はまだ期限内' 

    # ここら辺のメソッドはslackerパッケージを使用
    slack.chat.post_message(
            '#expiration',
            message,
            as_user=True,
            link_names=True
    )


def execute(event, context):
    post_slack('<YOUR DOMAIN>')
slack_setting.py
SLACK_API_TOKEN = ''

ローカルで実行

$ python bot.py

スクリーンショット_2017-06-07_17_21_48.png

今回もまた 渡辺曜 さんにお知らせ係を担当していただきました。

Lambdaの設定

Lambda_Management_Console.png

  1. トリガーの設定
    Lambda_Management_Console.png
    各自好きなように設定。自分は cron で月〜金のAM2時に通知される設定を選択しています。
  2. 関数の名前は適当に入力
  3. ランタイムPython 3.6 を選択
  4. ソースコードアップロードは下で説明
  5. ハンドラbot.execute を入力
  6. ロール等は各自設定

外部ライブラリの反映

ローカル環境では以下コマンド等でなんとかなりますが、

ローカル環境
$ pip install -r requirements.txt

AWS Lambda上ではライブラリが認識されないので外部ライブラリごとアップロードする必要があります。

外部ライブラリをプロジェクトディレクトリに保存
$ pip install <LIBRARY_NAME> -t .

という感じにやればプロジェクトディレクトリに保存されます。
しかし、1つ1つやるのが面倒なので私は以下でしました。

$ pip freeze > requirements.txt  # requirements.txt がなければ
$ pip install -r requirements.txt -t .

あとはzipに圧縮してアップロードする

$ zip -r bot.zip *  # bot.zip は好きな名前

プロジェクトディレクトリに bot.zip ができているので .ZIPファイルをアップロード を選択して bot.zip をアップロードする

スクリーンショット 2017-06-07 17.41.57.png

うまく設定できれいれば設定した時間にSlackに通知がくるはず!
私の環境では先述通り 月〜金 AM2時 に渡辺曜さんがお知らせしてくれます!!

続きを読む

AWS Summit Tokyo 2017 参加レポート Day4 (6/2)

会社の外部研修として『AWS Summit Tokyo 2017』に6月1日(木)~2日(金)の計2日間参加して来ました。

当エントリでは6月2日(Day4)に聴講した内容をレポートします。(Day3のレポートはこちら
※注)記事には個人的なメモや感想が含まれています。予めご承知ください。


展示ブース見学1 (9:00 ~ 10:00)

基調講演開始までの待ち時間、Pavilion 展示ブースを見て回りました。

立ち寄ったブース

  • IoT.kyoto

  • Sony Global Education:KOOV

    • 子供向けIoT学習キット
    • レゴブロックのようなパーツを自由に組み合わせ
    • アプリで日本語による論理パズルのようなものを使いビジュアルプログラミング
  • tv asahi:MP360LIVE
  • レコチョク:RECOLab
  • PIXELA:パノミル
    • VRコンテンツ配信プラットフォーム
  • AUCNET:Andy ニコパス
    • 来店客などの顔認識⇒LINEやSlackなどで通知&顧客管理
    • オフィスなどの入退室管理にも
    • 感情なども判別
      • 顧客満足度のデータ化
      • 従業員のストレスチェック

基調講演(Key Note) (10:00 ~ 11:30)

スピーカー:茂木 健一郎 氏 (脳科学者)

>人工知能、”Innovation” をテーマにトーク

もはやAIと人間の脳を比較すること自体がナンセンス

 人間の意識:128bit/sec 説
  チェス等⇒チャンピオンの一生分を機械なら~1か月?
  人間=並列処理 限界ある:AI並列化し放題
 e.g. 雪道走行経験欲しい ⇒自動運転=雪国からデータを持ってくればいい

人間の脳ってみじめじゃないですか?

 ギブソン:アフォーダンス理論
  >「脳は情報処理不要」「情報は環境の中にある」

Amazon Echo早く日本で出ない?遅れてますよね

 MIT 子供の過去三年間の会話データを全て記録した話
  >BigData、機械学習で出来ること、可能性は計り知れない

人間の脳なんてもう気にしなくていい

 人工知能と人間の恋の映画
  >AI:「今2万人と同時に会話してる」「数百人と恋に落ちてる」

なんだかAIに申し訳ないなって思いました

重要なのは個性

人間の感情・パーソナリティのデータモデルはまだない
 パーソナリティ5大要素⇒人工知能にインプリメンテーションまだ無理
  ⇒ソース:由来がわからない(⇔脳の情報処理の動きは比較的判明している)

人工知能が目指すものは人間の脳とはおそらく違う

 SNSなど今のシステム⇒人間のアテンションによりすぎ

 近代平均IQは上がり続けてる
  ⇒情報処理が増えたからと言われる
   ⇒それで人間は幸せになっている?

 現代はリアルとサイバースペースの二重生活
  ⇒リアルを整理するコスト=わずらわしい(Amazon robotics Home欲しい)
   ⇒メールなど=まだ人間のアテンションが必要

 Amazonでショッピング(本、靴、ズボンなど)
 ⇒勝手に買っておいてほしい(サイズ、好みなどの把握)
   心地よさのスクリーン

 子供の脳が最高(ルーティンがない、大人がやってくれているから)
  大人はつまらない
 AIが仕事を奪う⇒歓迎、ルーティンワークは任せるべき
  ⇒遊んでいるだけの人間が一番クリエイティブ
  天才の遺伝子はない(突然変異?)
  アメリカの功績は遊ぶように働くスタイルを確立したこと

 未来の義務教育の内容
  ⇒ほぼAIがやってくれる
   ⇒小学から論文など始めるのもあり

 人間の最終目標⇒幸せになること

子供のころ無邪気に遊んでた頃が幸せじゃありませんでした?

エンジニアへ

これからのイノベーションの主役はあなた方
人間の脳、キャパシティなんて気にせず
 個性を大事に
新しいものを産み出していってほしい

スピーカー:Kris Davies 氏 (Amazon Dashサービス シニア・プロダクトマネージャー)

IoTはあらゆるレベルの変革を可能に

ショッピングのイノベーション

 (昔)店舗⇒PC⇒スマホ⇒IoT:Dashボタン(今ここ)⇒ゼロクリック(Next!)

 店でよくあること=買うものを忘れる
 ・メモを忘れる
 ・消耗品のストックを忘れる
 ・既に買ったものを忘れる

 Dash Replenishment Service(DRS)
 スマート家電
  e.g. 掃除機>ゴミパックの状態を把握⇒クラウドに
      ⇒満タンになりそう⇒自動検出して替えを発注

  • 家庭には多くのデバイス(=変革の種?)がある

    • 「もし」ではなく「いつ」が検討すべきポイント
  • カスタマーからのフィードバックが大切
  • 全てのネット接続されたデバイスが役に立つわけではない
    • 本当に役立つ「モノのインターネット」を

スピーカー:Jeff Blankenburg 氏 (Amazon Alexa Evangelist)

Alexa
 スマートホーム
 デバイスに話しかける

ALEXA エコシステム
 スキル(ASK):デベロッパーが作成可能

音声⇒テキスト⇒自然言語解析⇒意図からスキルを探す⇒処理⇒レスポンス

ボイスエクスペリエンスのデザイン⇒開発⇒テストと認証

意思の解析が難しい
 e.g. 天気予報の取得⇒言い方がいろいろある
 e.g. テレビのリモコン⇒ボタン単位でコード化を考える

スピーカー:Andy Pollock 氏 (Amazon Robotics Software Development Manager)

Amazon Robotics

 e.g. 大図書館から本を探す状況
  ⇒手をかざしたら本が降ってくる を想像/創造した

Amazon 倉庫内で起きていること

 商品入荷⇒Podに収納⇒棚ごと移動⇒保管場所へ
  注文⇒担当者のもとに棚自身が来る⇒梱包、出荷

効率化アルゴリズム

数え切れないほどの商品の種類・数万単位の同じ商品
 ⇒どれを取りに行くか?
 動かす棚、ロボットの判定
  ⇒商品自身の位置だけじゃなく、間の障害物、作業員、一度に運べる個数など
   ⇒複雑な演算が必要
    ⇒AWS使用

次のイノベーションへ

「Its stil day one」(我々はまだ”1日目”である)

クロージング

Amazon

「どうか一つでも多く、イノベーションの種をお持ち帰りください」


展示ブース見学2 (12:00 ~ 14:00)

続いてDay3では回れなかったEXPO 展示ブースを見て回りました。
ついでに認定者ラウンジに立ち寄って昼食と、前日貰い損ねた折り畳み傘をGET。

立ち寄ったブース

  • Datadog Inc.

    • サーバ運用監視・モニタリングツール
    • AWS以外も対応サービスの数がスゴイ
  • 日商エレクトロニクス株式会社:New Relic
    • サーバ運用監視・モニタリングツール
    • 上記Datadogと競合
  • ソニーネットワークコミュニケーションズ株式会社:Cloud Portal
    • AWSサービスの構成管理ツール
    • 実際の設定から構成図などのドキュメントを生成可能、PDF出力も可(←この機能欲しかった)
  • 株式会社インターナショナルシステムリサーチ:CloudGate KeyManager
    • サーバ接続するユーザとSSH鍵を一括管理するツール

      • 有効期限付きのSSH Key発行・管理
      • 鍵の受渡し⇒指紋認証・2要素認証・ワンタイムパスワードなど選択可
  • パロアルトネットワークス株式会社
  • 富士ソフト株式会社
    • ストレスの度合?をリアルタイムで測定するIoTデバイスのデモを見せて頂いた
    • モニタリングした心拍数に数学的な処理・フィルタリングを施しグラフにして可視化?
      • 詳しく説明して頂いたが正確に理解出来たか怪しい
  • クラスメソッド株式会社

    • 日本のAWSユーザでこちらの会社のブログ記事を読んだ事が無い人はいないと思う
    • AWSおみくじを引かせて頂いた(結果はELBとt2.nanoでした;)

AWS Dev Dayの会場へ移動

Session:サーバレスで王道 Web フレームワークを使う方法 (14:20 ~ 15:00)

スピーカー:塚田 朗弘 氏 (AWSジャパン 技術統括本部 ソリューションアーキテクト)

セッション概要(タイトルリンク先より引用)

サーバレスアーキテクチャでアプリケーションを開発するとき、いつも使っているフレームワークをそのまま使うことができたら嬉しいと思ったことはないでしょうか。AWS Lambda では Node.js、Python、Java、C# といったプログラミング言語をサポートしています。このセッションでは、AWS のサーバレスアーキテクチャ上で代表的な Web アプリケーションフレームワークを使う実践的な方法をご紹介します。

Express.js on serverless

aws-serverless-express使用

npm install aws-serverless-express

app.js ⇒ lambda.js に変更

Spring Framework on serverless

aws-serverless-java-container使用

コードサンプル

どちらも Code Star にプロジェクトテンプレートあり
 ⇒手軽に始められるので是非


Session:[タワーズ・クエスト]Serverless 時代のテスト戦略(仮) (15:20 ~ 16:00)

スピーカー:和田 卓人 氏 (タワーズ・クエスト株式会社 取締役社長)

セッション概要(タイトルリンク先より引用)

Serverless 時代においても、自分のコードにテストは書きたいものです。まだベストプラクティスが確立されていない Serverless 時代のテストを考えていきます。

レガシーコードのテストをかいてみる

お題:lamdbaの公式チュートリアルにテストをつける

このコードはtestableだろうか?
デプロイなし、ローカルでテストしたい!

サイズとピラミッドとループ

テスト自動化ピラミッドとアンチパターン

“Test Sizes” at Google
https://testing.googleblog.com/2010/12/test-sizes.html

small:ネットワーク、データベース、ファイル、全てのI/Oなしの独立したテスト
medium:ネットワークはlocalhostのみ、外部サービスは非推奨
large:外部サービスも含め、本番と同じ条件のテスト

テスト数:small > medium > large

small:例外系テスト

medium:ローカルテストするには ⇒FakeObjectが鍵

  • Test Double(テストの為の”代用品”)

    • Test Stub
    • Test Spy
    • Mock Object
    • Fake Object
    • Dummy Object

 https://github.com/atlassian/localstack を活用
  ⇒各種AWSサービスのローカルテスト用Fake実装

レガシーコードのジレンマ
 多くの場合、テストを描くにはコードを変更する必要がある

教訓:テストしてないコードは動かない

large:運用監視とは継続的なテスト
 品質の良いシステム
 ・バグが少ない
 ・すぐになおる

本番環境の不具合をテストに写し取る
不具合が出たら再現テストコードを書いて、落ちることを確認してから修正コードを書く

サービス間(E2E)テスト

= XLarge Test (largeの上)

マイクロサービス間テストの課題
 >モックのリクエストやレスポンスが本番と異なる為、テストは通るが本番で落ちてしまう可能性。
 ⇒Consumer-Driven Contracts testing (CDCテスト)

pactを使ったCDCテストをCIで回す
 >Consumer側をMock/Stub感覚で書く


メイン会場へ移動

Session:[ソニーモバイルコミュニケーションズ] スマートホームシステムの開発 〜AWS を活用した新規サービスの立ち上げ〜 (16:20 ~ 17:00)

スピーカー:井宮 大輔 氏 (ソニーモバイルコミュニケーションズ株式会社)

セッション概要(タイトルリンク先より引用)

新規サービスは受容性が分からないため契約者数の予測が難しく、初期開発投資をなるべく抑え素早く市場に投入することが要求されます。上記を実現すべく、スマートホームシステム開発では AWS を最大限に活用しました。本セッションでは、システム開発において直面した課題と、それを AWS IoT などのサービスを用いていかに解決したかについてご紹介します。

スマートホームシステム

コンセプト
 家庭内デバイスをネット接続
  ⇒データ収集
   ⇒適切な制御

“スマートホームHub”を通してデバイスを制御

最大の課題=セキュリティ
 ・家族(ユーザ)の認証
 ・デバイス認証

 アカウント管理・認証
  ・ユーザ管理
  ・トークンアクセス
  ・パス、Email変更

1.ユーザ認証
 ⇒Cognito User Pool使用
  ・自前のユーザアカウント可
  ・SMS、MFAが実現可能
  ・カスタム属性で独自のデータ追加可能

2.デバイス認証
 ⇒AWS IoT使用
  デバイス数万台分のThingを作成
  ⇒工場でクライアント証明書・暗号鍵埋め込み
   ⇒一台ずつポリシーを作成(他のデバイスの機能にアクセス不可)

3.メッセージ 双方向通信
 ⇒IoT Topic・ルール使用

4.データ同期
 IoT Thing Shadow
  課題:最大8KB
   ⇒一つのデバイスに複数のThingを対応させて解決


Session:AWS マネージドサービスで実現する CI/CD パイプライン (17:20 ~ 18:00)

スピーカー:千葉 悠貴 氏 (AWSジャパン株式会社 技術統括本部 ソリューションアーキテクト)

セッション概要(タイトルリンク先より引用)

CI/CD ツールは DevOps の実践に不可欠なものですが、ツール自体の構築・運用は不可価値を生まない重労働です。AWS のマネージドサービスを使うことで、開発者は自社ビジネスに価値を与える開発に集中できます。本セッションでは、AWS CodePipeline や AWS CodeBuild といった DevOps サービスの概要と、その他の AWS マネージドサービスを組み合わせた CI/CD の実践方法をご紹介します。

CI/CDを実現する”ツールとしての”AWSサービス

CI:継続的インテグレーション

フロー
1.Source>2.Build>3.Test>4.Production

必要なもの

・ソースコードのVer管理:CodeCommit
・ビルド自動化:CodeBuild
・デプロイ自動化:CodeDeploy/Beanstalk/Formation
・ワークフロー管理:CodePipeline

全て東京リージョンで使用可

⇒4つを統合したサービス:CodeStar(現在USのみ)
 ほぼ2ステップで完了
 ・Step1.プロジェクトテンプレート選択
 ・Step2.開発ツールセットアップ
  ⇒パイプライン作成
  ⇒ダッシュボード作成(JIRA統合メニュー)

CD:継続的デリバリー

安全に本番環境にデプロイ
 リリース判断/タイミング

本番環境の継続的監視・デプロイ判断

1.シンセティックトラフィックでの常時監視
  CloudWatch+Lambdaで定期実行

2.デプロイメントヘルスチェック・ロールバック
  CodeDeployの機能を使う
   ・ValidateServiceフック
   ・Minimum Healthy Hosts
   ⇒失敗と判断⇒自動でロールバック

3.デプロイ単位のセグメンテーション
  カナリアデプロイ:部分的に適用⇒運用してみてから範囲を広げていく手法
  CodeDeploy
   ・デプロイメントグループ(セグメント分割)
  CodePipeline
   ・Approvalアクション:管理者にSNSで通知する機能(Lambdaに渡せば完全自動化可能)

4.プロダクション昇格の無効化
  デプロイ先ホストの健全性を確認してからデプロイする
   ⇒本番環境の一部に問題があればリソースを無効化
  CodePipeline
   ・トランジションの無効化

5.BlackDayゲート
  ビジネス的にセンシティブな時期/時間を考慮
   パイプラインにデプロイ停止日のカレンダーを導入
    ⇒e.g. LambdaでDynamoDB上のカレンダーを参照など

「CodePipeline をカスタマイズすることでビジネスに最適な CI/CD パイプラインが作れる」


所感・備忘録

  • 基調講演待ってる間に周れるとこは周るべし
  • 基調講演は情報・刺激の宝庫
  • 講演者についてもっと事前に調べておく
  • AWSの方のセッションはそろそろ上級向けに絞ってもいいかもしれない
    • 初級~中級はググればすぐ出てくる内容がそこそこ
  • Dev Dayはコアな内容(コードとか)まで突っ込んでくれるので実入り多し
  • 夜イベントは早めに行く(公演優先して後から行ったら料理が既になかった;)
  • Twitter情報は重要、なるべくチェック
  • 会場のWifiは時々繋がらなくなる(キャパのせい?)
  • やはりAWSサミットは楽しい
    • 来年も、出来れば全日参加したい

続きを読む