【合格しました!】AWS 認定ソリューションアーキテクト アソシエイト 受験記

AWS認定ソリューションアーキテクト アソシエイトを受験しました。
受験に向けてどんな事をしたのか、そんな勉強をしたのかをまとめます。

結果

合格しました!
得点は74%でした。
試験中に手ごたえがあまりなくドキドキでしたが、無事合格できてうれしいです!

所感

今回、認定取得を目指して学習したことで、AWSの知識が相当増えたと感じます。
具体的には

  • AWSのサービスを使用してシステムをどう構成すればよいのか
  • AWSのおのおののサービスの得意なこと不得意なこと
  • AWSにおけるベストプラクティス

というようなことが学べ、業務にいかすことができると感じています。

認定をとりたい、という方はもちろん、業務で使えるAWSの知識をもっと増やしたい、という方にもおすすめの認定です。

受験記です。

受験前のAWS歴

AWSを本格的に使っているのはここ1年ほどです。使用しているサービスは、EC2、RDS、S3などの基本的なサービスです。
構築するときは、調べながら&先輩に聞きながら、というのがほとんどでした。

勉強期間

約1カ月です。

学習前に行ったこと

1.受験要綱の確認
AWS認定ソリューションアーキテクト アソシエイトから「試験ガイドのダウンロード」ができます。ここを読みます。

2.AWS クラウドサービス活用資料集の「AWS認定試験準備に向けて」という部分を読む
AWS認定試験準備に向けて
AWS公式で、受験に向けてどのようなことをすればよいか公開されています。
ここを読んで参考にしました。
ですが、ここに書いてあることすべてを行ったわけではありません。
たとえば、「QwikLABS」は行いませんでした。中身を見たところ、いままでやったことがあるものだったのでやりませんでした。
また、「認定試験準備ワークショップの受講」も行っていません。

3.ほかの方の体験記を読みあさる
他のかたの体験記を読みあさり、何をもとに勉強すればよいかの参考にさせていただきました。

学習(1) ~対策本を読む~

※以下、学習(1)~と数字がついています。一応数字順に私は行っていきましたが、途中で(3)の途中で(1)もう一回やったりみたいなこともしました。

まずは唯一出ている対策本を読みました。

合格対策 AWS認定ソリューションアーキテクト – アソシエイト

こちらがとても勉強になりました。
ポイントがまとまっており、これを読んだだけでAWSの知識がだいぶ増えました。
これのおかげで業務でAWSを扱うときに自信を持てるようになったと思います。
この対策本は結局3回ほど読み直しました。
合わせて以下の本も読みました。

Amazon Web Services実践入門 (WEB+DB PRESS plus)

1冊目は対策本という位置づけなこともあり、各サービスがものすごく詳しく書かれているわけではありません。2冊目の本もあわせて読むことで知識を補っていきました。

学習(2) ~サンプル問題~

AWS認定ソリューションアーキテクト アソシエイトから「サンプル問題のダウンロード」ができます。
対策本を1周した時点で解いてみました。間隔をつかむためにもやっておいたほうがよいと思われます。
(あまりわからず衝撃を受けます。。。)

学習(3) ~クラウドデザインパターンを知る~

クラウドデザインパターンについて学びました。Webでも見れますし、書籍も出ています。

Amazon Web Services クラウドデザインパターン設計ガイド 改訂版

ユースケースを学ぶ、という意味でとても勉強になります。
50以上のデザインパターンがあり数は多いのですが、一つ一つのボリュームはそんなに多くないので、サクサク読めました。
知らないサービスもできてきますので、その時は調べながら進めました。

学習(4) ~各サービスについて知る~

AWS クラウドサービス活用資料集
ここに一番時間を割きました&とても勉強になりました。
各サービスの詳細な知識はここで補完しました。

EC2、ELB、Auto Scaling、
EBS、S3、RDS、DynamoDB、ElastiCache、
VPC、Route 53、
CloudWatch、CloudTrail、CloudFormation、
IAM、
SNS/SQS、
のサービスは何度も読みました。
また、上記のサービス以外については、AWS クラウドサービス活用資料集のページをざっとながめて、全然知らないサービスについては、概要とユースケースを確認するようにしました。

学習(5) ~活用事例やベストプラクティスを知る~

各サービスの知識はもちろん重要なのですが、AWSの考え方や実際の事例も重要です。

AWS クラウドサービス活用資料集の、以下の活用事例を読みました。

  • Web サービス StartUP 向け スケーラブルな構成例
  • AWS上の暗号化ソリューション
  • AWSにおけるセキュリティとコンプライアンス
  • AWS 上での DDoS 対策
  • クラウドのためのアーキテクチャ設計 -ベストプラクティス-

また、AWS Summitの資料も参考になりました。
AWS Summit Tokyo 2017 セッション資料・動画一覧
「AWS Well-Architected フレームワークによるクラウド ベスト プラクティス」など参考になります。

学習(6) ~ホワイトペーパーを読む~

AWS ホワイトペーパー
英語のものが多いのですが、日本語のものはいくつか読みました。
対策本を一度読んだあとにもいくつか読んだのですが、知らないサービスがあったり、知らない考え方があったりでなかなか読み進められませんでした。
各サービスについて知ったり、ベストプラクティスを知った後に読んだことでさくさく読めました。
ホワイトペーパーを読むことで知識を再確認したり、定着させることができたと思います。

学習(7) ~模擬試験~

模擬試験を受けることができます。
本試験と同じ操作をすることができるので、有料ですが、受験しておいたほうがよいと思います。
問題の雰囲気もつかめると思います。わからなかったところは復習しました。
アカウントを登録する必要がありますが、本試験でも必要になるアカウントです。
模擬試験の場合は、模擬試験の購入後、好きなタイミングで自分のパソコンで受験することができます。
時間、問題数ともに本試験より少ないです。

学習(番外編) ~有料セミナーやトレーニング~

私はArchitecting on AWSを受講しました。
最初に書いた「QwikLABS」も無料で受けられる範囲と有料で受けられる範囲とがあります。

前日

当日受験するテストセンターの場所と持ち物の確認をしました。

当日

最後に合格対策 AWS認定ソリューションアーキテクト – アソシエイトについている各章末問題をもう一度見直して、不安なところをググったりしていました。

学習法番外編 ~実家での勉強~

(大したことではないのですが・・・)
ちょうどお盆で実家に帰省する、した、という方も多いのではないでしょうか?
私も帰省後、東京に戻ってすぐ試験、という日程でした。
実家にいるとなかなか勉強できない!と思いましたので、

  • 友達と会う時間の2時間前に出かけて、外で勉強する
  • 家では寝る前等にさくっとこなせることをする(深く考えることは外で)
    • 問題といたり、クラウド活用集のスライドぱらぱらと見たり

最初はなかなか勉強できなさそうだし、どうしよう・・・と思っていましたが、思いのほか勉強できました。
やはり限られた時間で集中して行ったことがよかったと思います。
上記以外の時間は家族と出かけたりくっちゃべったり友達と会ったりと充実して過ごせたのではと思います。

いままで休日は「丸一日勉強するぞー」なんて意気込んで、結局集中できないことがたくさんありました(丸一日集中なんて無謀ですよね・・・)。
今後は丸一日勉強スタイルはやめようと思います。
思いもよらず、勉強のスタイルも見直すことができました。

続きを読む

aws-sam-localだって!?これは試さざるを得ない!

2017-08-11にaws-sam-localのベータ版がリリースされました。
単に「早速試したで!」と言う記事を書いても良かったのですが、少し趣向を変えてサーバレス界隈の開発環境のこれまでの推移を語った上で、aws-sam-localの使ってみた感想もお話しようかと思います。

サーバレス開発環境の今昔

サーバレス自体がかなり最近になって生まれた風潮なので昔とか言うのも問題はあるかと思いますが、とにかくサーバレスなるものの開発環境について私にわかる範囲でお話しようと思います。誤りや不正確な点については編集リクエストやコメントを頂けると幸いです。

なお、サーバレスという言葉は一般的な名詞ですが、私がAWS上でしかサーバレスに触れていないため、AzureやGCPなどには触れず、もっぱらLambdaの話になってしまうことをあらかじめご了承ください。

Lambdaのデプロイは辛かった

Lambdaの基本的なデプロイ方法はZIPで固めてアップロードです。
直接ZIPをLambdaに送るか、あらかじめS3に置いておいてLambdaにはそのURLを教えるかといった選択肢はありましたが、手動でZIPに固めてAWSに送るという手順は不可避でした。なんかもうすでに辛い。

さらに言うとLambdaに送りつけられるのはLambdaで実行するコードだけ。性質上Lambdaは単体で使われることはほとんどなく、他のサービスと連携することがほとんどなのにその辺は自分で管理するしかありませんでした。辛い。

CloudFormationで管理することは可能でしたが、CloudFormationテンプレートを書くのがかなりダルいことと、CloudFormationの更新とZIPのアップロードを別途行う必要があって手順が煩雑化しやすいため、「もうええわ」と手動管理してることが多かったと思われます。

また、ローカル環境で実行するには一工夫必要でした。

颯爽登場!Serverlessフレームワーク

そんな時に颯爽と現れたのがServerlessフレームワークでした。
ServerlessフレームワークにおいてはLambdaファンクション及び関連するリソースを独自のyamlファイルで管理します。結局は一度CloudFormationテンプレートに変換されるのですが、CloudFormationテンプレートよりも単純な形式で記述できたのが流行った一因かと思います。また、sls deployコマンドでLambdaのコードのアップロードおCloudFormationスタックの更新を一括で行ってくれたため、デプロイの手順は従来よりもはるかに簡略化されたかと思われます。

Lambdaテストしづらい問題

デプロイに関する問題はServerlessフレームワークや、ほぼ同時期に現れたSAMによって改善されましたが、開発プロセスにおいて大きな課題がありました。

テストし辛ぇ…

上記の通りLambdaは性質上他のサービスと連携することが多いため、その辺をローカル環境でどうテストするかに多くの開発者が頭を抱えました。対策として

  1. モッククラスを作って、実際のサービスのような振る舞いをさせる
  2. プロダクションとは別のリージョンに環境を再現して、そこで実行する

といった方法がありましたが、それぞれ

  1. モッククラスの実装がすこぶるダルい 下手したらロジック本体より時間かかる
  2. クラウドにデプロイしないとテストできないため、時間がかかる

といったデメリットがありました。

LocalStackとaws-sam-local

サーバレス開発者の嘆きを聞いたAtlassianがローカル環境でAWSのサービスのエンドポイントを再現するなんとも素敵なツールを作り上げました。それがLocalStackです。
再現されているサービスの数が物足りなく感じられたり、サードパーティ製であることに一抹の不安を覚えたりする人もいるかと思いますが、これ以上を求めるなら自分で作るぐらいしかないのが現状かと思います。

そしてaws-sam-local。こちらはLocalStackと少し趣が異なります。LocalStackが連携するサービスのエンドポイントを再現して提供するのに対して、aws-sam-localは実行環境の提供という意味合いが強いです。そして重要なことはAWSの公式がサポートしているということです。公式がサポートしているということです。大事なことなので(ry
「実行するのはローカルでNode.jsなりPythonなりで動かせばええやん」と思いがちですが、ランタイムのバージョンなどを本番環境と確実に揃えられるのは大きな利点です。
まだベータ版が出たばっかなので今後に期待といったところでしょう

aws-sam-local触ってみた

それでは実際に触ってみましょう。
ちなみに当方環境は

  • OS: macOS Sierra 10.12.6
  • Docker for Mac: 17.06.0-ce-mac19

です。

事前準備とインストール

公式のInstallationの項目を読み進めますが、事前にDockerを使えるようにしておく必要があります。

Macだったら普通にDocker For Macをインストールしておけば問題ありません。
一方Windowsだと

スクリーンショット 2017-08-16 14.48.18.png

まさかのDocker Toolbox推奨。 Docker For Windowsェ…
そしてaws-sam-localのインストールですが、私は-gオプション排斥論者なのでローカルインストールします

npm install aws-sam-local

実装

今回はこちらを参考にAPIゲートウェイから呼び出すLambdaを実装します。
ほぼ丸パクリですが一部アレンジしてますのでソースものっけます。

template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Local test
Resources:
  HelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs6.10
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /resource/{resourceId}
            Method: put

ランタイムをnodejs6.10に変更してます。
新しく作る場合にわざわざ古いバージョンを使う必要もありませんので。

余談ですが、WebStormのCloudFormation用のプラグインは今の所SAMには対応してないのか、Type: AWS::Serverless::Functionのところにめっちゃ赤線を引かれます。

index.js
/**
 * Created by yuhomiki on 2017/08/16.
 */

"use strict";

const os = require("os");
console.log("Loading function");


const handler = (event, context, callback) => {
  return callback(null, {
    statusCode: 200,
    headers: { "x-custom-header" : "my custom header value" },
    body: "Hello " + event.body + os.EOL
  });
};

exports.handler = handler;

完全に書き方の趣味の問題です。
内容は参考ページのものと全く同じです。

package.json
{
  "name": "sam_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "invoke-local": "sam local invoke HelloWorld -e event.json",
    "validate": "sam validate",
    "api-local": "sam local start-api"
  },
  "author": "Mic-U",
  "license": "MIT",
  "dependencies": {
    "aws-sam-local": "^0.1.0"
  }
}

aws-sam-localをローカルインストールしているので、package.jsonのscriptsに追記しています。

実行

それでは実行してみましょう

上記のpackage.jsonに記載した

  • invoke-local
  • validate
  • api-local

を実行していきます。

invoke-local

Lambdaファンクションをローカル環境で実行します。
Lambdaファンクションに渡すevent変数はワンライナーで定義することも可能ですが、あらかじめJSONファイルを作っといた方が取り回しがいいです。

json.event.json
{
  "body": "MIC"
}

実行結果はこんな感じ

スクリーンショット 2017-08-16 15.25.42.png

まず最初にdocker pullしてランタイムに応じたDockerイメージをダウンロードします。
その後はコンテナ内でLambdaファンクションを実行し、最後にcallbackに与えた引数を出力といった流れです。
ログの形式がすごくLambdaですね。あとタイムゾーンもUTCになっていますね。
メモリの使用量をローカルで確認できるのは嬉しいですね。

-dオプションをつけることでデバッグもできるようです。
公式のgithubにはご丁寧にVSCodeでデバッグしてる様子がgifで上げられてます。

validate

テンプレートファイルのチェックをします。
デフォルトではカレントディレクトリのtemplate.yamlファイルをチェックしますが、-tオプションで変更することが可能です。

失敗するとこんな感じに怒られます。

スクリーンショット 2017-08-16 15.33.47.png

成功した時は「Valid!」とだけ言ってきます。きっと必要以上に他人に関わりたくないタイプなのでしょう。

api-local

sam local start-apiコマンドはローカル環境にAPIサーバを立ち上げます。
ホットリロード機能がついてるので、立ち上げっぱなしでもソースを修正したら自動で反映されます。いい感じですね。

スクリーンショット 2017-08-16 15.40.58.png

立ち上がるとこんなメッセージが出るので、あとはCURLなりPostManなりで煮るなり焼くなり好きにしましょう。

CURLの結果はこんな感じ
スクリーンショット 2017-08-16 15.51.39.png

所感

Lambdaのローカル実行環境を公式が用意したことに大きな意義があるかと思います。
Dockerさえあればすぐに使えることと、SAMテンプレートを書かざるをえないのでInfrastructure as Codeが自然と根付いていくのも個人的には好感を持てます。

ただし、まだベータ版なこともあって機能的にもの足りない部分があるのも事実です。
具体的にはやはりDynamoDBとかもテンプレートから読み取ってDockerコンテナで用意してくれたらなーと思います。LocalStackやDynamoDB Localでできないこともないでしょうが、DynamoDB Localに関してはテンプレートからテーブル作ったりするの多分無理なのでマイグレーション用のコードを書くことになりますし、LocalStackに関しては実はあまり真面目に使ったことないのでわかりませんが環境構築に一手間かかりそう。ていうかできれば一つのツールで完結させたい。

SAMしかりaws-sam-localしかり、AWS側としてもより開発がしやすくなるような環境づくりをしていくような姿勢を見せているので、今後のアップデートに期待したいところですね。

続きを読む

serverless frameworkを使ってデプロイ

serverless frameworkってなんなん

YAMLに設定を書いておくと、CLIでAWSのデプロイ/設定が行えます。
簡単に言うと、デプロイの自動化ができます。

環境

項目 version
node 6.10.2
serverless framework 1.19.0

インストール

serverless frameworkをglobalにインストール。

npm install -g serverless

config credentials

serverless framework docs AWS – Config Credentials

プロジェクト作成

serverless frameworkのコマンドを使用してプロジェクトを作成します。

mkdir serverless-sample
serverless create -t aws-nodejs

以下内容のファイルが作成されます。試してみたら見れますがw

handler.js
'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
};
serverless.yml
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: serverless-sample

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"

provider:
  name: aws
  runtime: nodejs6.10

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iamRoleStatements:
#    - Effect: "Allow"
#      Action:
#        - "s3:ListBucket"
#      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#    - Effect: "Allow"
#      Action:
#        - "s3:PutObject"
#      Resource:
#        Fn::Join:
#          - ""
#          - - "arn:aws:s3:::"
#            - "Ref" : "ServerlessDeploymentBucket"
#            - "/*"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  include:
#    - include-me.js
#    - include-me-dir/**
#  exclude:
#    - exclude-me.js
#    - exclude-me-dir/**

functions:
  hello:
    handler: handler.hello

#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - http:
#          path: users/create
#          method: get
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill
#      - iot:
#          sql: "SELECT * FROM 'some_topic'"
#      - cloudwatchEvent:
#          event:
#            source:
#              - "aws.ec2"
#            detail-type:
#              - "EC2 Instance State-change Notification"
#            detail:
#              state:
#                - pending
#      - cloudwatchLog: '/aws/lambda/hello'
#      - cognitoUserPool:
#          pool: MyUserPool
#          trigger: PreSignUp

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"

region設定

regionの設定を追記します。

serverless.yml
--- 省略 ---
provider:
  name: aws
  runtime: nodejs6.10
  region: ap-northeast-1
--- 省略 ---

API Gateway設定

上記ロジックを呼ぶエンドポイントの設定を追記。

serverless.yml
--- 省略 ---
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: serverless-sample
          method: get
--- 省略 ---

デプロイ

serverless frameworkのコマンドを使用してデプロイします。
serverless.ymlに記載の内容でデプロイされます。

serverless deploy

参考

serverless

続きを読む

[新ツール]AWS SAMをローカル環境で実行できるSAM Localがベータリリース

コンニチハ、千葉です。 でました、その名もSAM Local !! ※2017/8/16時点でベータです SAM Localとは? AWS SAMを利用すると、サーバーレスアプリケーションをコードで定義しデプロイすること […] 続きを読む

StepFunctionsで始める分散処理Lambda

プロローグ ~こんなことをやりたかった~

業務で実装しているシステムで、定期的にこんな処理をする必要がありました。

  1. DynamoDBからデバイスのIDの一覧を取得する
  2. 各IDについて、DynamoDBにアクセスして生データを取得し、サマライズして別のテーブルに格納

こんなことをLambdaにやらせてたのですが、一つのLambdaファンクションに丸投げしてたのが災いして、デバイスが増えてきたりすると非常に辛い感じになってきました。

ですので、これを機に今まで真面目に触らなかったStepFunctionsに手を出して見ました。

ちなみにまだ実験がてらちょっといじってみたぐらいですので、記事中のソースソースコードはかなりしょぼいです。

出来上がりはこちら

Step Functionsって何?

って方のために手短に説明しますと、 Lambda同士の連携をいい感じに管理するツール と思えばほぼ間違いないです。

スクリーンショット 2017-08-11 14.55.19.png

これはデフォルトで用意されているブループリントの図ですが、視覚的にワークフローを把握しながらLambdaファンクションを組み立てることができます。

また、あるLambdaファンクションの結果に応じて次の処理を切り替えるといったことも簡単にできるようになります。

実装編

大まかな流れ

今回はあくまでStepFunctionsの使用感を知るための実験が主目的なので、処理を簡略化します。

まずは以下のLambdaファンクションを用意します。

  1. LambdaA: 処理対象のIDの一覧を取得(実験なのでIDは適当な文字列をハードコーディング)
  2. LambdaB: 配列で受け取ったID一つ一つについて、LambdaCを非同期で並列にinvokeする
  3. LambdaC: 受け取ったIDをコンソールに出力

LambdaBからLambdaCへの受け渡しもStepFunctionsで行いたいところですが、同一のLambdaを不特定多数並列に立ち上げるのは辛そうなので、ここはLambda内でinvokeします。

よって処理の流れは以下のようになります。

  1. LambdaAでIDの一覧を取得(実験なのでIDは適当な文字列をハードコーディング)
  2. 特に意味はないが10秒ほどスリープする
  3. LambdaBはLambdaAから受け取ったIDの一覧を使い、一つのIDに対して一つのLambdaCを非同期でinvokeする
  4. LambdaCはLambdaBから受け取ったIDをコンソールに出力。処理が並列化されていることをわかりやすくするため、2秒ほどスリープしてからLambdaBに結果を返す
  5. 全てのLambdaCの処理が終わったらLambdaBも終了する

うーん、「Lambda」がゲシュタルト崩壊しそうですね
ではこれらをServerless Frameworkで実装しようかと思います

ここで注意事項

StepFunctionsで定義した一連の処理の流れをStateMachineと言いますが、 State Machineは一度作成すると編集できません。変更したいなら都度削除して作り直さなければいけません。

幸いにして2017年2月にCloudFormationがStepFunctionsをサポートしましたので、これを利用しましょう。

AWSコンソールのGUIで毎回入力し直すよりは楽かと思います。

各種設定ファイル作成

ではServerlessで実装するための設定ファイルを書いていきましょう。

serverless.yml

まずはおなじみのserverless.ymlです

serverless.yml

service: step-test

provider:
  name: aws
  runtime: nodejs6.10

# you can overwrite defaults here
  stage: dev
  region: ap-northeast-1
  memorySize: 256
  timeOut: 30
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "lambda:InvokeFunction"
      Resource:
        - arn:aws:lambda:${self:provider.region}:${self:custom.config.accountId}:function:${self:service}-${self:custom.config.stage}-parallel

custom:
  config:
    accountId: "your Account ID" # 自分のAWSアカウントのID
    stage: ${opt:stage, self:provider.stage}

functions:
  # LambdaA
  first:
    handler: handler.first
  # LambdaB  
  second:
    handler: handler.second
    environment:
      TARGET_LAMBDA_ARN: ${self:service}-${self:custom.config.stage}-parallel
  # LambdaC
  parallel:
    handler: handler.parallel
resources: ${file(./resources/state_machine.yml)}

できるだけ使い回せるように色々と変数を使ってます。
変数の基本的な使い方はここを見ていただくとして、いくつかポイントになるところを解説します。

  • ${file(./resources/state_machine.yml)}

    • 別のファイルから読み込みます。これの中身はCloudFormationテンプレートです(後述)
  • stage: ${opt:stage, self:provider.stage}
    • コマンドラインオプションで –stageが与えられればそれを、なければprovidor.stageの値(この場合は”dev”)をデフォルトで使用。Lambdaファンクションの名前に関わります。

あと大事な点として、LambdaBはinvokeするためにLambdaCの名前を知っておく必要があります。
今回は環境変数として設定するようにしてあります。

CloudFormationテンプレート

StateMachineをCloudFormationテンプレートで作成します。
こちらを参考に

resources/state_machine.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create Step Function StateMachine"
Resources:
  InvokeLambdaRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Sid: StepFunctionsAssumeRolePolicy
              Effect: Allow
              Principal:
                Service:
                  Fn::Join: [ ".", [ states, Ref: "AWS::Region", amazonaws, com ] ]
              Action: sts:AssumeRole
        Path: /
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
  TestStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      RoleArn:
        Fn::GetAtt: [ InvokeLambdaRole, Arn ]
      DefinitionString: |-
        {
          "Comment": "An example of the Amazon States Language using wait states",
          "StartAt": "FirstState",
          "States": {
            "FirstState": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:${self:provider.region}:${self:custom.config.accountId}:function:${self:service}-${self:custom.config.stage}-first",
              "Next": "wait_using_seconds"
            },
            "wait_using_seconds": {
              "Type": "Wait",
              "Seconds": 10,
              "Next": "FinalState"
            },
            "FinalState": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:${self:provider.region}:${self:custom.config.accountId}:function:${self:service}-${self:custom.config.stage}-second",
              "End": true
            }
          }
        }
Outputs:
  StateMachineArn:
    Value:
      Ref: TestStateMachine
  StateMachineName:
    Value:
      Fn::GetAtt: [ TestStateMachine, Name ]


StateMachineの定義の部分を取り出すとこうなります

{
  "Comment": "An example of the Amazon States Language using wait states",
    "StartAt": "FirstState",
    "States": {
      "FirstState": {
        "Type": "Task",
        "Resource": "arn:aws:lambda:${self:provider.region}:${self:custom.config.accountId}:function:${self:service}-${self:custom.config.stage}-first",
        "Next": "wait_using_seconds"
      },
      "wait_using_seconds": {
        "Type": "Wait",
        "Seconds": 10,
        "Next": "FinalState"
      },
      "FinalState": {
        "Type": "Task",
        "Resource": "arn:aws:lambda:${self:provider.region}:${self:custom.config.accountId}:function:${self:service}-${self:custom.config.stage}-second",
        "End": true
      }
  }
}

これに関しては用意されているWaitStateのブループリントをほぼそのまま流用しています。

Lambdaファンクション実装

さて、Lambdaファンクション本体を実装していきますが、上述の通り実験用なのでかなり簡素です。

LambdaA

こいつの役割はIDの一覧を返すことですが、実験用なのでハードコーディングしたIDの配列を返します。
実用化のさいはAPIなりDBなりにアクセスして取得するようになるでしょう

functions/first.js
"use strict";

const firstFunction = (event, context, callback) => {
  console.log("Call First Function");
  callback(null, {
    ids: ["a", "b", "c", "d", "e", "f"]
  });
};

module.exports = firstFunction;

Node.jsで実装する場合は、callbackの第二引数に入れた値がStateMachineに受け渡されます。
他の言語の場合は異なる可能性がありますので、お使いの言語に合わせて実装しましょう。

LambdaB

LambdaBはIDの一覧を分解して、それぞれに対してLambdaCをinvokeします。
LambdaAの結果をどうやって受け取るのかが気になるところでしたが、StepFunctionsで連携させた場合、LambdaAでcallbackの第二引数に入れた値がそのままevent変数として渡されます。

functions/second.js
"use strict";

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

const secondFunction = (event, context, callback) => {
  console.log("Call Second Function");
  console.log(event);
  const targetLambdaArn = process.env.TARGET_LAMBDA_ARN;

  if (!targetLambdaArn) {
    console.log("no target");
    return callback(null);
  }
  console.log(targetLambdaArn);
  const lambda = new AWS.Lambda();
  const ids = event.ids;

  Promise.all(ids.map((id) => {
    return lambda.invoke({
      FunctionName: targetLambdaArn,
      Payload: JSON.stringify({id: id})
    }).promise();
  })).then(() => {
    return callback(null);
  }).catch((err) => {
    return callback(err);
  });
};

module.exports = secondFunction;

ついでにNode.jsでのlambda.invoke()ですが、FunctionNameはARNでも関数名でもいいようです。基本的に関数名で問題ないでしょうが、より確実性を求めるならARNで指定するのもありでしょう。

LambdaC

LambdaCでは各IDに応じた処理を行います。
実際はそのIDを使ってDBにアクセスみたいな処理になると思いますが、今回は単純にコンソールに出力するだけです。
また、ちゃんと並列になってるかの確認もしたいので、現在のタイムスタンプを出力して2秒ほど待ってからcallbackします。
並列処理になっていれば各タイムスタンプはほぼ同じ時刻を示すはずです。

functions/parallel.js
"use strict";

const moment = require("moment");

const parallelFunction = (event, context, callback) => {
  console.log(event);
  console.log(`now: ${moment().format("X")}`);

  setTimeout(() => {
    callback(null);
  }, 2000)
};

module.exports = parallelFunction;

実行編

ではデプロイします。

sls deploy

うまくいっていればStepFunctionsのコンソールにStateMachineが追加されています。

スクリーンショット 2017-08-11 15.53.21.png

「New execution」をクリックして、最初のファンクションに与えるインプットを入力するとStateMachineが動き出します。

実行中はこんな感じで今どの処理をやってるか確認できます。
この図ですとFirstState(LambdaA)の処理が正常に終了し、Waiting中です。

スクリーンショット 2017-08-11 15.55.55.png

ログ確認

では懸案だったLambdaCの並列分散処理はうまくいっているのか、ログを見てみましょう。

スクリーンショット 2017-08-11 15.57.47.png

ほぼ同時にLogStreamが複数できているので、どうやら分散化はできているようです。

次にコンソールに出力したタイムスタンプを見てみます。
配列内で先頭と末尾であったaとfを比べてみましょう。

スクリーンショット 2017-08-11 16.02.08.png

スクリーンショット 2017-08-11 16.02.39.png

タイムスタンプはmoment().format("X")で出力しているので、秒単位のUNIXタイムスタンプです。
ログを見たところタイムスタンプは同じなので、1秒以内に両者は実行されていたことになります。2秒スリープを入れているのにほぼ同時に実行されているので、並列に実行されているとみなして良いかと思います。

エピローグ ~感想と今後の課題~

さて、初めてStepFunctionsを使ってみたわけですが、当初は「StepFunctionsの中にStateMachineがあって…え〜っと…よくわからん」な感じでしたが、いざ使ってみたらそんなに難しいものではありませんでした。

複雑な処理とかはできるだけLambdaを分割してStepFunctionsで連携させたいところですね。プロダクションにも取り入れるつもりです。

問題なのはStateMachineを実行する手段がコンソールから手動実行かAPIをコールするぐらいで、イベントをトリガーにできないこと。
イベントドリブンで色々やろうとしたら、まずはイベントをトリガーにLambdaを起動し、その中でStateMachineを動かすという手段を取らなければいけません。正直言ってこれはあまりイケてない。

今後の機能追加に期待ですね。

以上、これからStepFunctionsを使おうとしている方の参考になれば幸いです。

参考

続きを読む

CodeStarでAPIを公開する

AWS CodeStarを用いて簡単にAPIを公開する方法を記載します。
CodeStarを利用すると、1から設定するのは結構大変な以下サービスなどをテンプレートより一気に作成できます。

  • AWS CodeCommit(レポジトリ)
  • AWS CodeBuild(自動ビルド)
  • AWS CodePipeline(継続的デリバリ)
  • AWS CodeDeploy(自動デプロイ)
  • AWS CloudFormation(インフラ構成管理)

前提

  • CodeCommitの認証セットアップ

http://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up-ssh-unixes.html

手順

CodeStar選択

2017/8/10時点では、東京リージョンでは利用できないので、CodeStar選択の後、任意のリージョンへ。

スクリーンショット 2017-07-27 時刻 19.39.17.png

プロジェクト作成

スクリーンショット 2017-07-27 時刻 19.50.29.png

テンプレート選択

AWS Lambdaをチェックして、任意の言語を選択する
※以下はnode+Expressを選択した例

スクリーンショット 2017-07-27 時刻 19.55.03.png

プロジェクト名設定

スクリーンショット 2017-07-27 時刻 19.55.55.png

IDEの設定

必要ないので、Command line tools選択

スクリーンショット 2017-07-27 時刻 19.58.40.png

レポジトリとの接続方法を教えてくれます。
確認の後、Skipします。

スクリーンショット 2017-07-27 時刻 19.58.53.png

自動ビルド

以上で設定が完了です。
→テンプレートモジュールの自動ビルドが走ります。

スクリーンショット 2017-07-27 時刻 20.02.08.png

API更新

自動ビルドが完了すると、管理コンソール右上部の「Application Endpoint」をクリックすると、APIのEndpointへ接続できます。
プログラムを修正後、CodeCommitへpushすると、更新バージョンが自動でデプロイされます。
※CodeCommitへは管理コンソール左上部より遷移できます。

スクリーンショット 2017-08-10 時刻 17.03.13.png

スクリーンショット 2017-07-27 時刻 20.22.19.png

注意

AWS 無料利用枠を超えると、APIコール回数(Lambda実行時間)で課金されるので、サービス公開前は念のため、使用量プランで呼び出し回数を制限しておきましょう。

https://aws.amazon.com/jp/lambda/pricing/
http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-api-usage-plans.html

続きを読む

JavaによるLambdaをCodepipelineで自動リリースする最小構成サンプル

TL;DR

以下の文書では、node.js でのLambda関数のリリースを自動化する例が出ています。
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/automating-deployment.html

本文書では、これとほぼ同じようなアプリをJavaで書き、そのリリースを自動化します。それぞれの要素の意味や役割の説明は最低限に留めます。 (筆者もよくわかってません。えっへん。) 最小の動作サンプルの例示を行います。

本文書ではマネージメントコンソール上で色々手作業で設定を入れさせています。今後のUIの変更で、手順が変わっているかもしれません。ご容赦を。

前提

  • githubにコードが配置してあること。
  • 更新を検出するブランチが決めてあること。
  • Gradleでビルドを行います。
  • 中間ファイルを置くためのS3バケットがあること。例として、auto-release-sampleとします。

準備

ロールの作成

IAM マネージメントコンソールを開き、以下のようにロールを作成します。

項目 備考
名前 cloudformation-lambda-execution-role
ロールタイプ AWS CloudFormation 作成時は「AWS サービスロール」から選択する。作成後のロールの画面では、信頼関係 タブに表示される
アタッチされたポリシー AWSLambdaExecute ロールの画面では、アクセス許可 タブ=>管理ポリシー に表示される

一度ロールを作った後、作成したロールをマネージメントコンソールで開き、アクセス許可 タブ ->インラインポリシー にて、作成するには、ここをクリックしてください。をクリックする。  カスタムポリシー -> 選択 と進んだ後下記内容を記載する。ポリシー名は適宜設定する。

{
    "Statement": [
        {
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketVersioning"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::codepipeline*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "lambda:*"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "iam:GetRole",
                "iam:CreateRole",
                "iam:DeleteRole"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "iam:AttachRolePolicy",
                "iam:DetachRolePolicy"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "cloudformation:CreateChangeSet"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        }
    ],
    "Version": "2012-10-17"
}

注:必要最低限の権限になってはいないかもです。今後の本記事の更新でなるべく削って行きたいです。

githubにファイルを配置する

アプリケーションのソースコードをgithubに保存します。

具体的なファイルの中身はサンプルリポジトリを参照してください。
https://github.com/kazurof/minimum-java-lambda-with-codepipeline
ファイルのディレクトリ構成を記載しておきます。

│  build.gradle
│  buildspec.yml
│  gradlew
│  gradlew.bat
│  minimum-lambda-java-model.yaml
│
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    └─main
        └─java
            └─codepipelinesample
                    Main.java

補足

buildspec.yml には、利用するS3リポジトリ名が記載されます。(先に、auto-release-sampleとしたもの。)実際に動かしてみる場合は適宜あなたが用意したリポジトリ名に変更してください。

CodePipeline の作成

AWS CodePipeline のマネージメントコンソールを開き、パイプラインを以下のように作成する。

パイプライン名

パイプライン名はわかればなんでも良い。

ソース

項目 備考
ソースプロバイダ GitHub GitHubを選択すると、GitHubリポジトリのURLとブランチを指定し、OAuth認証を行い、CodePipelineから指定したGitHubのリポジトリにアクセスできるようにする。
アクションカテゴリー ソース 作成時は入力を求められない。自動でつけられるものでOK
アクション名 Source 作成時は入力を求められない。自動でつけられる名前でOK
出力アーティファクト名 MyApp 作成時は入力を求められない。自動でつけられる名前でOK

ビルド

項目 備考
ビルドプロバイダ AWS CodeBuild
アクションカテゴリー ビルド 作成時は入力を求められない。
アクション名 CodeBuild 作成時は入力を求められない。

CodeBuildの新しいプロジェクトを作るを選択する。プロジェクト名はわかればなんでも良い。

ビルドプロジェクトの作成

項目
ビルド環境 Ubuntu Java8
ビルド仕様 ソースコードのルートディレクトリの buildspec.yml を使用

注:
– この時、AWS CodeBuild のサービスロールがユーザーに代わり自動的に作成されます。code-build-<ビルドプロジェクト名>-service-role という形になります。
– このビルドのアクションにおいて、以下項目が以下のように自動設定されます。

項目
入力アーティファクト MyApp
出力アーティファクト MyAppBuild

「ビルドプロジェクトの保存」を押下、「次のステップ」を押下してください。

デプロイ

項目 備考
デプロイプロバイダ AWS CloudFormation
アクションモード 変更セットの作成または置換
スタックの名前 MyBetaStack
変更セット名 MyChangeSet
テンプレートファイル packaged-minimum-lambda-java-model.yaml
Capabilities(特徴) CAPABILITY_IAM
ロール名 cloudformation-lambda-execution-role この作業手順の先頭で作ったロールを指定する。
アクションカテゴリー デプロイ 自動で設定される

AWS サービスロールの作成

ロールを新たに作ります。

当該画面の文言を転載:
「AWS CodePipeline がアカウントでリソースを使用するアクセス許可を付与するため、IAM にサービスロールを作成します。」

  • 「ロールの作成」を押下。
  • 「許可」を押下。
  • 「次のステップ」を押下。

ロール名は、「AWS-CodePipeline-Service」になるはずです。(すでにある場合はインラインポリシーに追加される挙動の様子。未確認です。)

パイプラインの確認

今まで入力した内容の確認画面が表示される。「パイプラインの作成」を押下します。

サービスロールを修正

Codebuild 向けに作成されたサービスロールが、用意したS3バケット(auto-release-sample)にアクセスできるように修正する。

今までの手順で、code-build-<ビルドプロジェクト名>-service-roleなるロールが生成されているのでそれを修正する。

  • IAM マネジメントコンソールから、ロール を選択する。
  • code-build-<ビルドプロジェクト名>-service-role を選択する。
  • アクセス許可=> インラインポリシー=> 「表示するインラインポリシーはありません。作成するには、ここをクリックしてください。」 をクリックする。
  • 「Policy Generator」 を選択して 「Select」 を選択する。
  • 「AWS Service」 で、「Amazon S3」 を選択する。
  • 「Actions」 で、「PutObject」 を選択する。
  • 「Amazon Resource Name (ARN)」 に arn:aws:s3:::auto-release-sample* と入力する。(末尾のアスタリスク必要!)
  • 「ステートメントを追加」 を選択して 「Next Step」 を選択する。
  • 「ポリシーの適用」 を選択する。

CodePipelineにLambdaを更新するステップを追加する

  • AWS CodePipeline マネジメントコンソールに移動 =>作成したpipelineを選択
  • 編集ボタンをクリック
  • Stagingの鉛筆アイコンをクリック
  • 既存のアクションの最後にある [+ Action] アイコンを選択する。
  • 以下のように入力
項目
アクションカテゴリー デプロイ
アクション名 execute_cs (わかればなんでも良い)
デプロイプロバイダ AWS CloudFormation
アクションモード 変更セットの実行
スタックの名前 MyBetaStack
変更セット名 MyChangeSet
  • 「更新」を押して保存
  • 「パイプラインの変更を保存」を押してパイプライン全体を保存

自動リリースとLambdaの実行

以上でCodepipeline 作成は終了です。gitリポジトリのmasterブランチに何か修正を入れてみてください。Codepipelineのマネジメントコンソールで、パイプラインが動作するところを確認できます。

正しく終了したら、Lambdaのマネージメントコンソールを開いてください。JavaによるLambda関数が作成されているはずです。テスト実行ができます。

感想

関係する要素技術多いせいか、手順長いですね。。。:sweat_smile:

続きを読む