misc.tech.notes

主に技術的な雑記的な

Alexa Skillの開発をServerless Frameworkだけで完結するための「Serverless Alexa Skills Plugin」の紹介

この記事は、Serverless Advent Calendar 13日目の記事です。

qiita.com

今回は拙作ですがオススメのServerless FrameworkのPluginを紹介したいと思います。

概要

Serverless Alexa Skills Pluginは、Serverless FrameworkでAlexa Skill用のLambda Functionを開発しながら、スキル周辺の設定も serverless.yml および serverless(sls) コマンドから管理できるようにすることで、Alexa Skillの開発をServerless Frameworkで統合管理するためのPluginです。

github.com

経緯など

Alexa Skillの開発にはLambda Functionの開発がセット

Alexaのカスタムスキルを作る場合、基本的にはLambda Functionの開発がセットになります。もちろん、Lambdaを使わずに特定のHTTP Endpointとやり取りすることで実行することもできますが、HTTPサーバを作って運用したりする手間とコストを考えるとLambdaを使う方が大抵の場合楽ですし、コストメリットも高いです。

developer.amazon.com

Lambda Functionの開発はServerless Frameworkでやりたい

最近は専らLambda Functionの開発はServerless Frameworkでやっています。以前は独自のデプロイツールを作って使っていたりもしましたが、v1.0からFrameworkの内部構造がそれ以前より段違いで良くなり、足りない機能はpull requestを送ったりpluginを作ったりして補えるようになったため、フルタイムで会社まで作ってメンテしていて、良い感じにエコシステムが確立しつつある製品に対抗してもしょうがないので乗っかる方針でやっています。

Alexa Skillの設定管理がめんどくさい

対して、Alexa Skillの設定はAlexa Skill Kitのコンソール画面から行うか、ack-cliを使う方法が公式で提唱されています。このack-cliがまた設定をJSONで書かないといけなかったり、公式ツールなのでしょうがないのですが、APIに忠実に作られているので必要なコマンド数が多かったりするわけです。

developer.amazon.com

Serverless Frameworkで全部良い感じにやりたい

そんな思いから生まれたのがServerless Alexa Skills Pluginというわけです。

使い方

インストール

インストールはnpmにリリースされてるのでこれだけです。

$ npm install -g serverless
$ serverless plugin install -n serverless-alexa-skills

認証情報の取得

利用するためにはLambda FunctionをデプロイするためのAWSの認証情報もそうですが、Alexa Skills Kit APIを実行するための認証情報も必要になります。AlexaはAWSではなくAmazon本体の管轄であり、Login with Amazon というAmazon本体アカウントを使ったOAuth2.0によるシングルサインオンで認証を受ける必要が有るため、認証情報の扱いが異なるのでそこを説明したいと思います。AWSの認証情報の取得方法についてはここでは触れません。

まず、Amazon Developer Consoleへログインします。

developer.amazon.com

APPS&SERVICES タブから Login with Amazon へ移動し、 Create a New Security Profile からセキュリティプロファイルを作成します。

f:id:FumblePerson:20171213225925p:plain

各入力項目は適当で良いです。

f:id:FumblePerson:20171213230336p:plain

作成したら、出来上がったセキュリティプロファイルの Web Settings を設定します。

f:id:FumblePerson:20171213230629p:plain

Allowed Origins は空で良いです。 Allowed Return URLshttp://localhost:3000 を入力します。このポート番号は設定(serverless.yml)で変更可能なので、変えたい場合は変えても良いです。

f:id:FumblePerson:20171213230810p:plain

ここまでできたら、できあがったセキュリティプロファイルの Client IDClient Secret を控えておきましょう。併せてもう一つ、 Vendor ID というものも必要になるので、Developer Consoleへログインした状態でコチラへアクセスして控えておきましょう。

ちょっと面倒ですが、この作業は初回だけです。同じアカウントを使う限りは今後いくつスキルを作ろうと同じ情報が使えます(漏洩などして入れ替えが必要にならなければw)

面倒な画面ポチポチはここまでです!!ここから先はみんな大好きの黒い画面をメインに進みます!!

認証情報をセット

取得した認証情報を serverless.yml へ書き込みます。直接書き込むのに抵抗がある場合は下記のように環境変数を利用すると良いでしょう。また、 Allowed Return URLs でポート番号を買えた場合は localServerPort という設定を追加してポート番号を書き込んでください。

provider:
  name: aws
  runtime: nodejs6.10

plugins:
  - serverless-alexa-skills

custom:
  alexa:
    vendorId: ${env:YOUR_AMAZON_VENDOR_ID}
    clientId: ${env:YOUR_AMAZON_CLIENT_ID}
    clientSecret: ${env:YOUR_AMAZON_CLIENT_SECRET}

そして、以下のコマンドを実行します。

$ serverless alexa auth

このコマンドを実行するとブラウザが開かれAmazonへのログイン画面が出てきます。そこでログインに成功すると localhost:3000 へリダイレクトされ、認証に成功していれば Thank you for using Serverless Alexa Skills Plugin!! とデザイン皆無の味も素っ気もないメッセージが表示されているはずです!w

ちなみにこれによって取得した認証トークンの有効期限は1時間となっています。今後、トークンの自動リフレッシュなど実装予定ですが、現状では認証エラーが出たら再度上記コマンドの実行をお願いします。

スキルの作成

それではスキルを作ってみましょう。以下のコマンドを実行します。

$ serverless alexa create --name $YOUR_SKILL_NAME --locale $YOUR_SKILL_LOCALE --type $YOUR_SKILL_TYPE

それぞれオプションは以下の通りです。

  • name(n): スキルの名前
  • locale(l): スキルのロケール。日本語なら ja-JP, 英語なら en-US
  • type(t): スキルのタイプ。 custom or smartHome or video

スキルマニフェストの更新

スキルを作ったら、スキルの基本的な設定を表すスキルマニフェストを設定する必要があります。今作成したスキルに初期設定されているマニフェストは以下のコマンドで確認できます。

$ severless alexa manifests

Serverless: 
----------------
[Skill ID] amzn1.ask.skill.xxxxxx-xxxxxx-xxxxx
[Stage] development
[Skill Manifest]
skillManifest:
  publishingInformation:
    locales:
      ja-JP:
        name: sample
  apis:
    custom: {}
  manifestVersion: '1.0'

実行すると上記のような出力が出るので、 [Skill ID][Skill Manifest] をコピーして serverless.yml へ以下のように貼り付けます。

custom:
  alexa:
    vendorId: ${env:AMAZON_VENDOR_ID}
    clientId: ${env:AMAZON_CLIENT_ID}
    clientSecret: ${env:AMAZON_CLIENT_SECRET}
    skills:
      - id: ${env:ALEXA_SKILL_ID}
        skillManifest:
          publishingInformation:
            locales:
              ja-JP:
                name: sample
          apis:
            custom: {}
          manifestVersion: '1.0'

この serverless.yml の設定を更新後、下記のコマンドを実行するとマニフェストが更新されます。ちなみに恒例?の --dryRun/-d オプションで実際に更新は行わずに更新内容だけ確認できます。

$ serverless alexa update

マニフェストの設定構文についてはAPIに準じているので、こちらで確認できます。

developer.amazon.com

対話モデルの構築

Alexaスキルを動かすためには、対話モデルの構築が必要です。これもマニフェストとほぼ同じ流れでできますが、作成しただけのスキルは対話モデルを持っていないのでまず先に serverless.yml に対話モデルの設定を書き込む所から始まります。

custom:
  alexa:
    vendorId: ${env:AMAZON_VENDOR_ID}
    clientId: ${env:AMAZON_CLIENT_ID}
    clientSecret: ${env:AMAZON_CLIENT_SECRET}
    skills:
      - id: ${env:ALEXA_SKILL_ID}
        skillManifest:
          publishingInformation:
            locales:
              ja-JP:
                name: sample
          apis:
            custom: {}
          manifestVersion: '1.0'
        models:
          ja-JP:
            interactionModel:
              languageModel:
                invocationName: PPAP
                intents:
                  - name: PineAppleIntent
                    slots:
                    - name: Fisrt
                      type: AMAZON.Food
                    - name: Second
                      type: AMAZON.Food

このような形で models.ロケール.interactionModel という構造で下記に準じた対話モデルを設定します。

developer.amazon.com

そして、下記のコマンドで対話モデルを更新し、構築します。ここでも --dryRun が利用可能です。

$ serverless alexa build

構築した対話モデルは下記のコマンドで確認可能です。

$ serverless alexa models

Serverless: 
-------------------
[Skill ID] amzn1.ask.skill.xxxx-xxxx-xxxxx
[Locale] ja-JP
[Interaction Model]
interactionModel:
  languageModel:
    invocationName: PPAP
    intents:
      - name: PineAppleIntent
        slots:
        - name: Fisrt
          type: AMAZON.Food
        - name: Second
          type: AMAZON.Food

現段階では以上です

ここから実際にスキルを公開するまではもう少しステップがありますが、一番変更が多く試行錯誤が必要でバージョン管理しておきたいマニフェストと対話モデルが統合管理できるのは大きいのではないかと思います。今後Alexa Skills Kitを通したテストや公開まで統合できるよう開発は進めていきたいと思っています。

また、ここでは触れなかったServerless FrameworkでAlexa Skill用のLambda Functionを実装する方法についてはこちらをご確認ください。

serverless.com

良かったら今後Alexaスキルを作る際にご利用いただき、フィードバックやStarをいただけると励みになりますので、よろしくお願いします。

github.com

2017年もServerlessConf Tokyoは最高だった #serverlessconf

去年に引き続き今年もスピーカーとして参加させてもらいました。

marcy.hatenablog.com

会社はスポンサーもやってるんですが、セッションとLTの両方にプロポーザルが通ってしまって余裕がないので同僚に基本任せっぱなしでした(ゴメンナサイ)

会場の雰囲気など

アンダーグラウンドな雰囲気がカッコよかった去年に比べて、今年は立派なカンファレンスになった感じでした。ですが、ベンダーフリーで自由な主張が許される雰囲気は変わっておらず、正当な進化を遂げたという印象でした。去年は食事と最後のソーシャルタイム?のお酒が凄い充実していたけど、代わりに今年はノベルティのTシャツが超カッコ良いので満足しています。

ソーシャルタイムは他の登壇者や普段会えない人達と話せる良い時間だったのですが、それが無くなったのはちょっと残念です。けど、規模的にも時間的にもまあ難しいだろうなという所なので仕方ないですね。個人的にはアツいメンバーと終了後に飲みに行けて良い話を一杯できたので救われたんですけど、もしそれが無かったとしたらちょっと物足りなかったかもしれないなと。

セッション

slideship.com

こちらが今回のメインです。「2年目なので今年はツールとかサービスの外側の話は一切封印して、より具体的な中身の設計とか実装の話をしたい」というのだけはもう初めから決めていて、事例をベースにしながら話すパターンと、大きなテーマだけ掲げてあとはとにかく設計と実装の話をし倒すパターンの2種類のプロポーザルを出した結果、後者を採用していただけました。そして、念のためと思って出したLTも一緒に通ってしまったというw

セッションの冒頭でも話したのですが、去年超有名エンジニアの方にパネルでボコボコにされたのが悔しくて、その時答えられなかったあらゆる質問の答えを持ってきたつもりです。Serverlessでも信頼性の求められるシステムは作れる。(もちろん、その方のことは全く恨んでいません。むしろ去年の自分が如何に甘かったか痛感させてくれてとても感謝してます)

LT

slideship.com

煽り芸に初挑戦してみました。会場でもその後の反応も良くて嬉しかったです。ただ一点ミスがあって「リレーション」は正しくは「リレーションシップ」ですね。普段から理解はしているものの略語みたいな感覚で誤用してしまっているので、名前は正しく表現すべきエンジニアにあるまじきミスなので、気をつけたいと思います。。。

他の方々のセッション

いくつか印象的だったものを(大体時系列順のつもり)

サーバーレスについて語るときに僕の語ること

speakerdeck.com

一応スポンサー企業所属なので、ブースはできなくてもランチスポンサーセッションを放っていくのはどうかってことで、現場では聞けなかったのが残念でした。ですが、基本的には全面的にAgreeです。イベントドリブンこそ本質で、そこの議論をもっと深めて行きたいというのはずっと思っていて「よくぞ言ってくれた!!」という気持ちです。

marcy.hatenablog.com

ただ、Serverlessの良いところの一つが文脈が広く寛容的である所だとも思っていて、入り口としての意味も込めて今までの文脈も残しておいても良いのかなと。隔絶しちゃわない程度に上手く棲み分けしていきたいですね。

アンケートにも書いたのですが、私にとってServerlessとは定義としてはイベントドリブンな文脈が正しいと思っているものの、Serverlessという言葉自体は「ベンダーの垣根を越えて真のクラウドネイティブアーキテクチャを議論するためのネームスペース」って思ったりしてます。そういうコミュニティに成長していってほしいと思うし、微力ながらお手伝いしたいです。

真のサーバレスアーキテクトとサーバレス時代のゲーム開発・運用

speakerdeck.com

おそらく日本のサービス(プラットフォーム)開発者の中で一番本気でServerlessをやっているであろう丹羽さん。さすがの圧倒的知見ですね。立場は違えど、要所要所で同じ答えに辿り着いている感じがして勝手に答え合わせしてもらった気持ちです。現場で聞きたかった。裏番組だったのが本当に悔やまれますw

Open source application and Ecosystem on Serverless Framework

speakerdeck.com

福岡のサーバレスおじさん、さすがの実装力です。いつもawspecのコンセプトデザイナーだと私のことを立ててくれるんですが、私はたまたま先に同じことを思いついて途中で投げ出しただけ(ちょうどaws-sdkのメジャーバージョンアップと重なって一旦凍結したという言い訳はあるけど、その後再開しなかったのでホントただの言い訳)なのです。今回も圧倒的実装力で楽しい世界を見せてくれました。自分ももっとコード書かねばと思わせてくれました。

OpenSource with Serverless World

speakerdeck.com

日本人初どころか、Serverless Inc.社外では初のメンテナの座を日々の努力で勝ち取ったアツい気持ちは本当に脱帽します。

Serverless Frameworkについては1.0が出たタイミングでそれ以前から比べると圧倒的に洗練された内部構造になったことに関心していて、その後もLamveryにあってServerless Frameworkに無い機能の中でどうしても欲しいものはPRを出したりプラグインで解決できるようになったので、日本人コミッター(しかも知り合い)が居る安心感も合わせてもうデプロイの所はServerless Frameworkに任せたい気持ちです。

その他、現状資料未公開(と思われる)もの

A Cloud Guruの人とAWSのServerless系サービスのProduct Managerが話していたServerless Architecture Patternは超実戦的で必見な感じでした。資料公開に期待したい。Googleの浦底さんもLT準備でチラチラ覗くことしかできなかったので、趣味でGCPをやっている身からは資料公開に期待したい(けど、始まる前に個人の主張であることを強調してたので難しいのかな)

あと、nekoruriさんの「アウトプットしないのは知的な便秘」は的確すぎて刺さりました。慢性的にちょっとずつ出しても便秘は解消しないですもんねw栄養の吸収も悪くなるしwほんと的確過ぎるwwwスッキリ出すの大事。

おまけの与太話

ワークショップは今年は参加できなかった。。。PyCon JPもそうだったんですが、チケットが早く売り切れてしまうと地方勢としてはちょっとツラい所があります。飛行機やホテルがあって行けることが確定できないと色々無駄になってしまう可能性がある・基本泊まりになるのでスケジュールの調整難度が高いので、理想を言うと2週間前くらいまではジワジワ追加販売とかしてもらえると嬉しいです。>カンファレンス主催者各位

個人的には行きたいカンファレンスはCFPも出す率が高くて(通れば無理を通してでも100%行くので)CFP落選後に落ちても行きたいか考えてから買えるタイミングがあると嬉しいけど、これはかなり特殊な例だと思うので配慮しなくて良いですwServerlessConfについては落ちてもほぼ間違いなく行ってただろうから買っておけば良かったんですけどね。怠慢。。。

でもまあ、圧倒的少数派に配慮して多数派の満足度を損なうのもどうかって話もあると思うんで、特に文句があるわけじゃなくただの願望です。地方でMeetupやった時に大盛り上がりとかしてれば自然と配慮してもらえるだろうし、コチラの問題ではって気もします。

(カンファレンスで数百人を沸かせることができても、地元では20人集めるのにも苦労してるので悲しくなる)

最後に

スピーカーで2年連続はクラウドベンダー勢を除くと私と丹羽さんだけのようですね。丹羽さんはさっきも言ったようにサービス開発者の中では一番だと思っているので、自分はインテグレーターの中では一番だという気持ちで、来年もあの場に立たせてもらえるように一年間精進していきたいと思います。

主催の吉田さん、スタッフの皆様、今年も最高のカンファレンスをありがとうございました!!

DynamoDBをキャッシュストアっぽく使うPyPIライブラリを作りました

仕事は一応納まったけど、リモートワークなので忘年会とかは特に無くて、奥さんも体調が良くないので年末年始に備えて寝てしまったので、一人寂しくブログ書いてます(;´Д`)

何はともあれソースなど

github.com

pypi.python.org

背景や動機

Lambdaでアプリケーションを作っていると、外部のSaaSAPIを叩いたりする時にrate limitやレイテンシが気になったり、DynamoDBに重めのクエリを投げると性能だったりキャパシティが気になって結果をキャッシュしておきたかったり、キャッシュストアが欲しくなることがままあります。

なんですが、ElastiCacheはVPC内で使うとENI生成時の遅延が気になるし、せっかく楽がしたくてServerlessなのにマネージドとはいえインスタンスを気にしないといけないのも微妙なところです。となると、Lambdaから手頃に使えるデータストアは結局DynamoDBしかないわけです。(AppEngineならMemcacheがあるから、もうAppEngineで良いんじゃないか?とか思ってしまうw)

じゃあ、もうDynamoDBをキャッシュストアの代わりに使うしかないよねってことで、それっぽいライブラリを探したんですが、見つからなかったので作りました。

インストール

Pythonおよびpipのインストールは省略すると、PyPIなので、これだけです。

pip install ddbc

余談ですが、APIが変わったらしく、PyPIのパッケージのアップロード方法がいつの間にか変わってたんですね。前からあるやつは手元のvirtualenv環境が古いままだからか気が付かなった。。。

準備

IAM権限

以下のようなPolicyを持つIAM Role/Userを使用します。

<cache-table> の所は実際に使用するキャッシュ用のDynamoDBテーブル名が入ります。
<region>:<account-id> の所はお使いの環境に合わせてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
              "dynamodb:CreateTable",
              "dynamodb:DeleteItem",
              "dynamodb:GetItem",
              "dynamodb:PutItem",
              "dynamodb:DescribeTable"
            ],
            "Resource": "arn:aws:dynamodb:<region>:<account-id>:table/<cache-table>"
        }
    ]
}

キャッシュ用のテーブルを作成

上記の権限を割り当てた環境で以下のようなコードを実行します。既に存在する場合は何もしないので、2回以上実行してしまっても無問題です。

#!/usr/bin/env python

import ddbc.utils

ddbc.utils.create_table(
    table_name='cache_table',
    region='us-east-1', # optional
    read_units=10,      # default: 5
    write_units=10      # default: 5
)

使用方法

キャッシュクライアント生成

import ddbc.cache
import time

cache = ddbc.cache.Client(
    table_name='cache_table',
    region='us-east-1', # optional
    default_ttl=100,    # default: -1 (Infinity)
    report_error=True   # default: False
)

default_ttl がキャッシュデータをセットする際にデフォルトで適用するデータのTTLです。未指定時は無限になります。
report_error はDynamoDB APIのコール時に発生した例外を報告(再スロー)するかどうかです。キャッシュストアなので、一時的なエラーやスロットリングなどの想定されるエラーは無視しても良い場面が多いので、未指定時は例外は握りつぶし、読み込み時は失敗時に None (変更可能)、書き込み系の操作なら結果を True (成功) / False (失敗) で返します。

キャッシュデータ操作

ざっとこんな感じです。pylibmc をなんとなく意識した感じで dict っぽく使えます。

cache['foo'] = 'bar'
print(cache['foo']) # => 'bar'

time.sleep(100)
print(cache['foo']) # => None

cache.set('foo', 'bar', 1000)
time.sleep(100)
print(cache['foo']) # => 'bar'

del cache['foo']
print(cache.get('foo')) # => None
print(cache.get('foo', 'buz')) # => 'buz'

補足

ちなみに、DynamoDBへのリクエストも減らすためにメモリ上にもキャッシュを持ちます。書き込みは毎回リクエストが発生しますが、読み込みはメモリ上のキャッシュが有効な間はそこからデータを取得します。なので、Lambdaもコンテナの再利用がされている場合はメモリにキャッシュ済みの有効期限内のデータを使うケースではDynamoDBへのリクエストは飛びません。

また、キー範囲が偏ると性能が出ないDynamoDBに配慮して一応キーはハッシュ化して分散するようにしています。

現状、有効期限が切れたデータは削除する際のスループット消費の方が気になるので、消されずにそのまま放置されます。

あと、1つのキーに対するデータは pickleシリアライズした文字列長でDynamoDBの制限である400KBまでしか入りません。

docs.aws.amazon.com

最後に

Lambda上でのオンライン処理で使えるデータストアが実質DynamoDBくらいしかない現状では、それなりに便利かと思うので良かったら使ってみてください。

AWSさんには、Serverlessなキャッシュストア的な新サービスと、SimpleDBの再興を切に願うばかりです。来年は是非おなしゃす。

それでは、良いお年を。

AppEngineで作るComputeEngineの定期バックアップシステム

この記事は、Google Cloud Platform Advent Calenderの21日目の記事です。遅れに遅れてしまい大変申し訳ありません。。。

エントリー時は「Cloud Functionsでなにか」と書いていたのですが、イマイチ目新しい機能も出ておらず良いネタがでなかったので、過去にAppEngineで作ったComputeEngineの定期バックアップシステムの紹介をしたいと思います。

前置きと概要

AWS方面ではわりと早い段階からAPIとタグを駆使したEC2の定期バックアップ手法は有名でした。EC2にバックアップ世代数のタグを設定し、そのタグがついているインスタンスを対象にバックアップを行うバッチを定期で起動します。

blog.suz-lab.com

最近だと、CloudWatch Eventsを利用してEC2要らずで行うことができます。

qiita.com

GCEでの例を見かけなかったのですが、基本的にはGCPスケールメリットを活かすにはステートレスなアプリケーションが前提となるので、そうするとデータを基本的にマネージドなデータストアに入れておけば良い話なので、そういったことは必要ないんだろうなと。

ただ、東京リージョンもオープンしたことですし、これからは社内システムやステートレスではない言ってしまえばレガシーなアプリケーションを移行するようなケースも増えてくるだろうと思いますし、そうなると必要になってくるんだろうなと。

ということで、AppEngineで作ったのですが、これがなかなか良い感じにできました。

EC2における手法の問題点

既知のEC2の手法には問題がいくつかありますが、これらがAppEngineを使うと良い感じに解決できます。

インスタンス数の増大による処理時間の増加

一般的にはそうそう問題とはならないでしょうが、大量のインスタンスを利用する規模の大きい企業や、GCPで代行ビジネスを行ういわゆるMSPのような立場だと、インスタンス数が増えてくると全部深夜のうちに終わらせたいのに終わらない可能性が出てきます。

APIエラー時のリトライ

もちろん確率としては低いですが、失敗することはあります。これも台数が多くなると顕著になってきます。APIのエラーハンドリングをキチンとやればという話ではあるのですが、エラー時にWaitなんかを入れだすとまた上記の話で処理時間が増えていって想定時間内に終わらない可能性が出てきます。

AppEngineでの構成

このような構成になります。公式アイコンが出たのでさっそく使ってみました。

f:id:FumblePerson:20161225004509p:plain

cloudplatform.googleblog.com

解説

ポイントを解説していきます。

定期実行

AppEngineにはタイマー機能があるので、これを使用します。バッチ処理の実行はキューなどを使っていくらでも冗長性を持たせられますが、発火元の冗長化は自力でやるととてもツラい領域なので、お任せできるのは嬉しいですね。

Scheduling Tasks With Cron for Python  |  App Engine standard environment for Python  |  Google Cloud Platform

分散処理

対象のリストアップとバックアップの実行を分けて、バックアップの実行をTaskQueueに詰めて分散で実行させます。これによって、単一のインスタンスで処理する際の処理時間の問題が解決できます。また、PushQueueを使用するとバックエンドのインスタンスをポーリングさせたり余裕を持って起動しておく必要がなくなるのがとても楽で良いですね。よくあるキューシステムではできない形なので、AppEngineのお気に入り機能の一つです。

自動再実行

TaskQueue (PushQueue)は処理に失敗すると間を置いて再実行をしてくれます。連続で失敗すると再実行間隔も調整しながら実行してくれるので、自力でExponential backoffのような実装をしなくて良いのでとても楽です。

yoshidashingo.hatenablog.com

権限管理が楽

AppEngineというかGCP自体の良さという感じではあるのですが、アカウントを跨いだ権限を与えて複数のアカウントのバックアップを一括で管理したい場合に、AppEngineのアプリケーションに自動で付与されるサービスアカウントのメールアドレスを該当のプロジェクトに招待して権限を付与してあげるだけなのが楽ですし、APIキーの共有などの煩わしさからも開放されるのでとても良い感じです。

まとめ

このような、AppEngineとTaskQueueでタスクの列挙と実行を分散で行う手法は、色々と応用が効きそうです。また、お任せできる領域が多いので、実装や管理が非常に楽になるのも大きなメリットです。AppEngineはWebアプリケーションを乗せるPaaSに留まらない便利なサービスなので、今後も積極的に使っていきたいです。

このシステムは既に私の手を離れており、手元にソースコードが無いのが残念ですが、暇を見つけてまた実装して公開したいなと思います。

AWS Lambdaのソースコードを覗いて動作原理を知る試み

この記事は、Serverless Advent Calendar 2016の14日目の記事です。

qiita.com

昨日は工藤さん(level69)による、「Serverless Meetup Sapporoで話てきた」でした。
工藤さんは元々北海道出身という縁もあることから、先日のServerless Meetup Sapporoに自腹で遠征してくれました。本当にありがとうございます。

serverless.connpass.com

本題

さて、本題ですが、最近はApache (元IBM) Openwiskを筆頭に、IronFunctionsなど、OSSなFaaS実装が増えてきていますが、ふと「Lambdaはどうなってんだろーなー。あ、ていうかLambdaもPythonなら、スクリプト言語だからある程度まではソースコード読めるんでは?」と思い立ったので、じゃあやってみましょうという試みです。

まずはFunctionの呼び出し元を知る

Functionの中でスタックトレースを出力すれば、Functionの呼び出し元が分かるはずです。Lambdaのソースコードの旅はここから始まる!ということで、出力してみましょう。

以下のようなFunctionをアップロードして実行します。

import traceback

def lambda_handler(event, context):
     traceback.print_stack()

結果を見てみましょう。

$ lamvery invoke {}
START RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e Version: $LATEST
  File "/var/runtime/awslambda/bootstrap.py", line 426, in <module>
    main()
  File "/var/runtime/awslambda/bootstrap.py", line 423, in main
    handle_event_request(request_handler, invokeid, event_body, context_objs, invoked_function_arn)
  File "/var/runtime/awslambda/bootstrap.py", line 243, in handle_event_request
    result = request_handler(json_input, context)
  File "/Users/marcy/project/lambda-source/lambda_function.py", line 4, in lambda_handler
END RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e
REPORT RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e  Duration: 0.55 ms   Billed Duration: 100 ms     Memory Size: 128 MB Max Memory Used: 15 MB  

/var/runtime/awslambda/bootstrap.py がLambda Function実行時の真の起点となるソースのようです。

起点のスクリプトをダウンロードしてみる

では、起点のスクリプトが分かったので取得してみましょう。Lambda:invokeFunctionAPIで得られる標準出力の内容は一定の長さを超えると切られてしまうので、S3に入れてみることにします。

以下のようなFunctionをアップロードして実行します。

import boto3

s3 = boto3.resource('s3')

def lambda_handler(event, context):
    s3.Object('marcy-terui', 'bootstrap.py').put(
        Body=open('/var/runtime/awslambda/bootstrap.py', 'rb'))

起点のスクリプトを読んでみる

さて、S3に入ったファイルを落として読んでみましょう。すると、まず冒頭でPythonの標準モジュールではないものをimportしています。特にimport前にsys.path.append 等を行っている記述はないので、同じディレクトリ内にあるはず。

同じディレクトリ内のファイルを全て取得する

以下のようなFunctionをアップロードして実行します。

import boto3
import os
s3 = boto3.resource('s3')

def lambda_handler(event, context):
    for f in os.listdir('/var/runtime/awslambda/'):
        s3.Object('marcy-terui', f).put(
            Body=open('/var/runtime/awslambda/{}'.format(f), 'rb'))

これで全て取得できました。

f:id:FumblePerson:20161214115851p:plain

え?ソースコードを見せろって?

はい。おっしゃる通りで、ソースコードを見ながらあーでもないこーでもないっていうのをやりたかったんですが、ソースコードに思いっきり Copyright (c) 2013 Amazon. All rights reserved. って書いてあるので、勝手に公開したら怒られかねないなと思いとどまりました。。。

気になる方は是非上記方法を試して見てください。一部 so 化されていますが、大部分は普通のPythonコードです。まだ全てをちゃんと読み込んでないですが、起動されたLambdaプロセスが呼び出しを待ち受ける方法が1つではないことが分かったりして、中々面白い発見があると思いますよ。何か最適化のヒントとなる情報などが読み取れたりするかも?(これくらいは言っても怒られないはず?)

簡単ですが、以上です

本当は、以下のようなのをある程度完成させてお披露目したかったんですが、Serverless Meetup SapporoやYAPC::Hokkaidoの諸々やら仕事がわりと立て込んでたりで進捗ダメです・・・年内には目処を付けたい。。。

とりあえず、こちらからは以上です

Serverless Meetup Sapporo会場案内とお知らせ

こんばんは。もうすぐServerless Meetup Sapporoの開催ですが、皆様いかがお過ごしでしょうか。

serverless.connpass.com

つい今しがた会場の下見に行ってきたのですが、中々IT系のイベントでは味わえない良い雰囲気でした。 ただ、入り口の場所など少し分かりにくかったので、会場の情報や当日の諸注意等をお知らせしておきたいと思います。

ちなみに会場の雰囲気はこんな感じです(レイアウトは当日代わります)

f:id:FumblePerson:20161208211233j:plain

お知らせ・諸注意

会場は言われずとも分かる方もいると思うので、まずはお知らせと諸注意の方から。

開場は12:00から、本編は13:00から

今回は開場が12:00からとなっています。12:00〜13:00までは Registration & Social ということで、入場の受付と参加者間の交流の時間となります。

飲み物持ち込み禁止、食べ物持ち込み自由

会場では Cash on Delivery (代金引換)にてお飲み物が飲めます(アルコールOK、ソフトドリンクも有り)その代わり、 お飲み物の持ち込みは不可 となっておりますのでご注意ください。

飲み物のメニューはこんな感じです。 f:id:FumblePerson:20161208213204j:plain

また、お昼時だからというわけではありませんが、とある方のご厚意により軽食が出ます。ですが、あくまで軽食でありおつまみ程度になりますので、 昼食をしっかりと食べたい場合は先に済ませていただくか、ご自分で持ち込みをお願いいたします。(少し多めに持ってきて他の方にも振る舞うと喜ばれること請け合いです!w)

是非、お早めにお越しの上、お楽しみいただければと思います。

Wifiはありません

ありません。是非、Twitter等でも盛り上がっていきたい所ですが、Pocker Wifi等のご利用をお願いいたします。

会場案内

会場はこちらです。

大通駅36番出口(地下鉄東豊線方面)からほど近く、マクドナルド向かいのダイソーさんの地下になります。ダイソーさんが目立つので建物自体は分かりやすいかと思います。

入口が少し分かりにくいのでご紹介しておきます。

建物の入口

建物の入口はダイソーさんの大きい入口から向かって左側面です。向かいにパチンコのベガスベガスがあります。

建物の入口には会場のCINEMA CAFEさんの看板はありません。目印は大戸屋さんですかね。 f:id:FumblePerson:20161208213408j:plain

ちなみに、入口脇には看板が置かれています。 f:id:FumblePerson:20161208213634j:plain

お店の入口

入るとすぐ降り階段があるので下に降ります。降りると右手にTreasureというスープカレー屋さんがあり、その向かって左脇に入り口があります。 f:id:FumblePerson:20161208214010j:plain

到着!

到着したらまず受付をお願いします。受付が終わったらMustではありませんが、お飲み物を頼んでお楽しみください。

最後に

今回はYAPC::Hokkaidoにあわせたことも相まって東京でも実現しなかったクラウドベンダー3社揃い踏み、スピーカー8名、LT4名とカンファレンス並の豪華布陣です!

しかも、クラウドベンダー以外はほとんどが道内在住の技術者、または北海道出身で今も北海道に縁のある方々です。最新の旬なテーマでも北海道でここまでやれるんだ!ということを全国に知らしめるため、是非盛り上がっていければと思います!

また会場も約50名が満員御礼で、YAPCのような大規模イベントを除けば大盛況と言えると思います。本当にスピーカーの皆様、参加者の皆様に感謝致します。ありがとうございます。

今後も継続的に開催していきたいですが、こんなに運営を頑張るのは初回だけなので是非楽しんでいただければと思います(えw

当日お会い出来るのを楽しみにしております!

Serverless FrameworkのPluginを2つリリースしました

Serverless FrameworkのPluginを2つリリースしました。

経緯など

以前からLamveryというデプロイツールを作って使い続けてきましたが、イベントを繋ぐGlue Codeを管理するには我ながら便利に使えていましたし、今も使っています。

github.com

ですが、複数のファンクションからなるシステムをServerlessで作るにはやはりFunction単位ではなくプロジェクトやサービス単位での管理をやりたくなる場面があって、Serverless Frameworkがv1.0で設定ファイルがYAML化して超シンプルになったのと、かなり作りが良くなって挙動をPluginで置き換えるのが比較的容易になったので、Serverless Frameworkの足りない機能や気に入らない部分をPluginで補うという方向でやってみることにしました。

LamveryのMulti Function対応も考えていたのですが、Function単位のデプロイ(下記参照)はあれはあれでとても満足していて崩したくなく、それを保ちながらMulti Functionにも対応するとなるとちょっと実装が厳しそうだなと感じたというのもあります。

qiita.com

Plugins

serverless-crypt

github.com

AWS KMSを使った機密情報の受け渡しをサポートするためのプラグインLamveryの同機能の移植&焼き直しです。

serverless-rollback-function

github.com

2016.11.07現在、周辺リソースや全ファンクションを対象とした deploy コマンドのrollback機能はv1.1.0での導入が決まっていますが、Function単体をデプロイする deploy function コマンドを使用した場合のロールバックのサポートはまだ未定です。片手落ち感が半端ないし、正直リソースも更新されて時間がかかる deploy コマンドよりも実運用では deploy function を多用することになるはずなので、これが巻き戻せないのはツラい。 ということで、こちらもLamveryのロールバック機能を踏襲しつつServerless Frameworkの仕様に合わせた形で実装しました。

Plugin開発で得た知見など

今週、2016.11.9(水)のServerless Meetupで発表します。

serverless.connpass.com

Lamveryはどうなるの?

Lamveryも最近は安定稼働していて特に新しい機能が欲しくなったりしてないので開発が滞り気味ですが、イベントを繋ぐGlue Codeを管理する用途では普通に使っています。そういう用途ではServerless Frameworkは重厚だし細かい所で色々気が利かないし、今後も開発は続けるつもりですので、フィードバックお待ちしてます :-)

参考までに私的な使い分けはこんな感じです。

  • イベントを繋ぐGlue CodeをFunction単位で楽に良い感じで管理したい場合はLamvery
  • Multi Functionでガッツリ開発する場合はServerless Framework(とPlugin達)

最後に

こんな感じで今後もServerless Frameworkの気に食わない部分は色々あるのでPluginを開発しつつ、Pluginが増えるとメンテが面倒なので某氏を見習って本家にねじ込めそうなものはねじ込む方向でやっていきたいと思います。よろしくお願いします(誰となく