WSL2 で時刻がめちゃめちゃになって AWS認証情報が Signature expired になる

WSL2 Ubuntu 20.04 で serverless deploy をしようとしたところ以下のエラーに遭遇しました。

$ serverless deploy

Deploying aws-python-http-api-project to stage dev (ap-northeast-1)

✖ Stack aws-python-http-api-project-dev failed to deploy (5s)
Environment: linux, node 18.12.1, framework 3.27.0, plugin 6.2.3, SDK 4.3.2
Credentials: Local, "talkeyboid-serverless-user" profile
Docs:        docs.serverless.com
Support:     forum.serverless.com
Bugs:        github.com/serverless/serverless/issues

Error:
Signature expired: 20230205T195512Z is now earlier than 20230207T123422Z (20230207T124922Z - 15 min.)

署名の時刻がかなり怪しい。

Ubuntu 時刻と Windows 時刻を確認。

$ date
Mon Feb  6 04:56:54 JST 2023
>date
現在の日付: 2023/02/07

>time
現在の時刻: 21:51:12.03

めちゃくちゃずれてる???

ということで、NTP サーバ同期します。こんなにずれていてはしょうがないので強制一発同期(-b)です。

$ sudo apt install ntpdate
$ sudo ntpdate -b ntp.nict.jp
$ date
Tue Feb  7 21:54:01 JST 2023

うまく同期できたので再度 serverless deploy

$ serverless deploy

Deploying aws-python-http-api-project to stage dev (ap-northeast-1)

✔ Service deployed to stack aws-python-http-api-project-dev (129s)

functions:
  hello: aws-python-http-api-project-dev-hello (85 kB)

無事にデプロイすることができました。

なんでこんなにもずれるのか、WSL2 のカーネル Hyper-V 周りだそうです(あまり詳しくないので首を突っ込まない)。

qiita.com

Serverless Framework Functions プロパティ(1)

前回の記事

今回は serverless.yml の Functions プロパティについてまとめてみたいと思います。ここが Serverless Framework で一番大事なところですね(たぶん)。

基本的には以下のドキュメントを読んでまとめ、思うところを徒然に書いていきます。内容の保証はしません、というか間違ってる可能性も多いにあるので鵜呑みにせず一次情報に当たってください。

www.serverless.com

設定方法(全般)

Provider 継承

  1. provider プロパティで設定した値は全ての関数に継承される。
  2. functions プロパティで設定した値が provider と異なる場合、functions の値で上書きされる。

1の例

provider.runtime = nodejs14.x ですが、functions.hello.runtime = python2.7 となっています。

serverless.yml
provider:
  name: aws
  runtime: nodejs14.x

functions:
  hello:
    handler: handler.hello
    name: ${sls:stage}-lambdaName
    runtime: python2.7

2の例

runtime, memorySize がすべての関数に適用されます。

serverless.yml
provider:
  name: aws
  runtime: nodejs14.x
  memorySize: 512

functions:
  functionOne:
    handler: handler.functionOne

関数定義

関数定義と実コードの関係は functions.<function name>.handler = <file name>.<function(module) name> です。

関数定義

serverless.yml
functions:
  hoge:
    handler: handler.fuga

実コード:Python の場合

handler.py
def fuga(event, context):
  return ""

実コード:JavaScript の場合

handler.js
module.exports.fuga = function (event, context, callback) {};

以下の定義方法が可能です。

  1. functions プロパティには複数の関数を定義することができる。
  2. 関数は別ファイルに外出しすることができる。

1の例

serverless.yml
functions:
  functionOne:
    handler: handler.functionOne
    description: optional description for your Lambda
  functionTwo:
    handler: handler.functionTwo
  functionThree:
    handler: handler.functionThree

2の例

serverless.yml
# serverless.yml
functions:
  - ${file(./hoge-functions.yml)}
  - ${file(./fuga-functions.yml)}
hoge-functions.yml
getHoge:
  handler: handler.getHoge
deleteHoge:
  handler: handler.deleteHoge

権限設定

Lambda にアタッチする IAM ロールの設定は provider.iam.role.statements で設定します。

書き方は IAM ポリシーの記載と同じ書き方なのでなじみやすいですね。また、Resources は CloudFormation の組込関数が利用可能です。ただし、serverless deploy 実行時に Serverless Framework として検証されることはなく、CloudFormation 側で解釈されるので適切に設定する必要があります(実行した後にCloudFormation スタックがこけて間違いに気づくということ)。

serverless.yml
provider:
  iam:
    role:
      statements: # このプロパティに権限を設定
        - Effect: Allow
          Action:
            - dynamodb:DescribeTable
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
          Resource: 'arn:aws:dynamodb:us-east-1:*:*'

        - Effect: 'Allow'
          Action:
            - 's3:ListBucket'
          # CloudFormation の組込関数が利用可能。ただしバリデーションされることはないため注意。
          Resource: { 'Fn::Join': ['', ['arn:aws:s3:::', { 'Ref': 'ServerlessDeploymentBucket' }]] }

        - Effect: 'Allow'
          Action:
            - 's3:PutObject'
          Resource:
            Fn::Join:
              - ''
              - - 'arn:aws:s3:::'
                - 'Ref': 'ServerlessDeploymentBucket'
                - '/*'

既存の IAM ロールがある場合には、provider.iam.role にARN 指定ができます。

serverless.yml
provider:
  iam:
    role: arn:aws:iam::YourAccountNumber:role/YourIamRole

IAM 権限の詳細については別ページに記載されています。絶対大事ですが、今回はスルーします(余裕があれば別記事で書きます)。

www.serverless.com

関数 URL

これは、AWS Lambda Function URLs を利用するためのプロパティです。Lambda 単体でも HTTP エンドポイントを作成できる機能ですね。

dev.classmethod.jp

dev.classmethod.jp

利用ケースによっていくつかの定義パターンがあります。

serverless.yml
functions:
  func:
    handler: index.handler
    url: true # HTTP エンドポイントが作成される(CORS なし)
serverless.yml
functions:
  func:
    handler: index.handler
    url:
      authorizer: aws_iam # IAM 認証必須(lambda:InvokeFunctionUrl が Allow であるか)
serverless.yml
functions:
  func:
    handler: index.handler
    url:
      cors: true # CORS設定有で HTTP エンドポイントを作成
serverless.yml
functions:
  func:
    handler: index.handler
    url:
      cors: # CORS設定をカスタムしたい場合
        allowedOrigins:
          - https://url1.com
          - https://url2.com
        allowedHeaders:
          - Content-Type
          - Authorization
        allowedMethods:
          - GET
        allowCredentials: true
        exposedResponseHeaders:
          - Special-Response-Header
        maxAge: 6000 # In seconds

プロパティと CORS ヘッダの対応はドキュメント参照

Docker コンテナイメージを利用した関数

直近で利用しなさそうなので軽く目を通す程度に。。。

  • Serverless は ECR リポジトリを作成するが更新管理は行わない。リポジトリは新しいサービスや関数を初めてデプロイするときにのみ作成される。
  • scanOnPush を指定することで CVE スキャンの設定ができる。
  • イメージ URI を直で指定することが可能。
serverless.yml
provider:
  ecr:
    scanOnPush: true
    images:
      baseimage:
        path: ./path/to/context # docker build が走るディレクトリ?
        file: Dockerfile.dev # Dockerfile を明示的に指定する場合
        buildArgs: # --build-arg
          STAGE: ${opt:stage}
        cacheFrom: # --cache-from
          - my-image:latest
        platform: linux/amd64
      anotherimage:
        # イメージを直で指定
        uri: 000000000000.dkr.ecr.sa-east-1.amazonaws.com/test-lambda-docker@sha256:6bb600b4d6e1d7cf521097177dd0c4e9ea373edb91984a505333be8ac9455d38
  • function 側でイメージを直で指定できるが、handler, runtime プロパティは指定できない。

イメージ内にランタイム設定・ソースコードがあるのでそこはいじれないよということでしょう。

serverless.yml
provider:
  name: aws
  ecr:
    images:
      baseimage:
        path: ./path/to/context
        # 他のプロパティの指定がないので、単に docker build が走って Dockerfile を読みに行く?

functions:
  hello:
    image: 000000000000.dkr.ecr.sa-east-1.amazonaws.com/test-lambda-docker@sha256:6bb600b4d6e1d7cf521097177dd0c4e9ea373edb91984a505333be8ac9455d38
  world:
    image: baseimage

serverless.yaml 側で workingDirectory, entryPoint, command は指定可能なようです。

serverless.yml
service: service-name
provider:
  name: aws
  ecr:
    images:
      baseimage:
        path: ./path/to/context

functions:
  hello:
    image:
      uri: 000000000000.dkr.ecr.sa-east-1.amazonaws.com/test-lambda-docker@sha256:6bb600b4d6e1d7cf521097177dd0c4e9ea373edb91984a505333be8ac9455d38
      workingDirectory: /workdir
      command:
        - executable
        - flag
      entryPoint:
        - executable
        - flag
  world:
    image:
      name: baseimage
      command:
        - command
      entryPoint:
        - executable
        - flag

ちょっと長くなってしまったので続きはまた今度。

Serverless Framework トップレベルプロパティ

前回の記事

今回は serverless.yml のトップレベルのプロパティについてまとめてみたいと思います。

基本的には以下のドキュメントを読んで要約し、思うところを徒然に書いていきます。

www.serverless.com

トップレベルプロパティ一覧

トップレベルのプロパティの分類は以下の構成になっています。

分類 設定内容(例)
Root Properties サービス名
Serverless Framework API バージョン
バリデーション設定
.env 利用有無
Deprication通知設定
Parameters 環境変数
Provider クラウドプロバイダ設定(以下 AWS 前提で記載)
CloudFormation設定(デプロイメソッド・ロールバック)
Lambda設定(ランタイム・メモリサイズ)
デプロイ先 S3 バケット設定(パブリックアクセス・バージョニング・暗号化)
API Gateway v2 設定(CORS・認証)
API Gateway v1 設定(APIキー・使用量プラン)
ALB 設定(認証)
ECR 設定(Lambdaを Docker イメージで動かす場合)
CloudFront 設定(キャッシュポリシー)
IAM 設定(Lambda ロール)
VPC 設定 (VPC Lambda 利用の場合)
Logs 設定(フォーマット・ログレベル)
Lambda Events としての S3 設定(バケット名・バージョニング)
Package デプロイする関数のディレクトリ・アーティファクトzip
Functions Lambda 関数。コンセプトで触れた "Event" もこの中で設定。
Function layers Lambda レイヤーの設定
AWS Resources その他 AWS リソース(CloudFormation テンプレート)

Provider 設定では全体的な共通設定が行えるようです。例えば Lambda のメモリサイズについては Provider でも Functions でも設定可能なので、"Functions に定義がなければ Provider 設定を反映" のような動きをするのだと思います。

API Gateway v1 (REST API)と API Gateway v2 (HTTP API)の違いは以下参照。

docs.aws.amazon.com

dev.classmethod.jp

Serverless Framework では、v1, v2 と呼び分けているので、あたかも v2 のほうが良い様に見えますが、特殊な要件がないならば v1(REST API) でしょう。

実際の構成は以下のようになります。上のテーブルの「分類」列をコメントとして記載しています。

serverless.yml
# Root Properties
service: myservice
frameworkVersion: '3'
configValidationMode: error
useDotenv: true
approached deprecation
deprecationNotificationMode: warn:summary
disabledDeprecations:
  - DEP_CODE_1 # Deprecation code to disable
  - '*' # Disable all deprecation messages

# Parameters
params:
  prod:
    my-parameter: foo
  dev:
    my-parameter: bar

# Provider
provider:
  name: aws
  stage: dev
  region: us-east-1
  ...(略)

# Package
package:
  patterns:
    - src/**
    - handler.js
    - '!.git/**'
    - '!.travis.yml'
  individually: true
  artifact: path/to/my-artifact.zip
  excludeDevDependencies: false

# Functions
functions:
  hello:
    handler: users.create
    image: baseimage
    runtime: nodejs14.x
    ...(略)


# Function Layers
layers:
  hello:
    path: layer-dir
    name: ${sls:stage}-layerName
    description: Description of what the lambda layer does
    compatibleRuntimes:
      - python3.8
    ...(略)


# AWS Resources
resources:
  Resources:
    usersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: usersTable
        AttributeDefinitions:
          - AttributeName: email
            AttributeType: S
        ...(略)

  extensions:
    UsersCreateLogGroup:
      Properties:
        RetentionInDays: '30'

  Outputs:
    UsersTableArn:
      Description: The ARN for the User's Table
      Value: !GetAtt usersTable.Arn
      Export:
        Name: ${self:service}:${sls:stage}:UsersTableArn

まとめ

Serverless Framework のコンセプトとマッピングすると以下のようになるでしょうか。

  • Service:serverless.yml ファイル単位。Root Properties にサービス名を service として指定。
    • Function:functions として指定。
      • Event:functions.<function name>.events に指定。
    • Resource:resources に指定。

その他にも、params, package, layers がトップレベルで指定可能なことがわかりました。

Serverless Framework のコンセプトについて

前回記事

Serverless Framework を利用するにあたり、コンセプトの理解が不可欠であると感じたのでまとめます。

基本的には以下のドキュメントを読んで要約し、思うところを徒然に書いていきます。

www.serverless.com

コンセプト

以下、公式ドキュメントでは "プロパティ" と書かれている部分を yaml ファイルの "フィールド" という意味で言い換えている場合があります。特に意図があるわけではないので適宜読み替えてください。

考え方

  • Serverless Framework は AWS Lambda 関数と Lambda 周りのインフラリソース開発のために利用できる
  • 構造化、自動化やベストプラクティスを提供するCLI(serverless のこと)を利用し、Functions と Events で構成される洗練されたサーバレスアーキテクチャの構築に集中できる

ここでは Lambda と書かれていますが、他のクラウドでも利用可能です。

www.serverless.com

特徴

  • コードとインフラの両方を管理する
  • 複数の言語をサポートする(Node.js, Python, Javaなど)

つまり、「Lambda を中心としたサーバレスアーキテクチャ開発のために利用でき、インフラとコードをまとめて管理するためのフレームワークである」ということです。

逆に言うと、Lambda(や他クラウドの FaaS) を伴わない開発の場合、特に Serverless Framework を採用する動機はないということでしょうかね。functions フィールドを指定しないとエラーになるとかそんなコントロールもあるのかしら。

Functions

  • コードは Lambda で実行される
  • 各関数はマイクロサービスのように独立した単位で実行・デプロイされる
  • 以下のような単一の処理を実行するためのコードをデプロイするために利用することが多い
    • ユーザ情報の保存
    • データベースのファイルを処理
    • スケジュールタスクの実行

「マイクロサービスのような」と書いてありますが、これは単に 「Lambda 関数は独立した単位でデプロイされる」という以上の意味はないですかね?もしくは、Serverless Framework の "サービス" は文字通りサービス毎に構築するのがベストプラクティスということでしょうか。そうした場合、現実問題としてサービスをまたぐDBも普通にあると思うのでどのサービスが主となり管理するかなど設計上の問題も出てきそうですね。

Events

  • 関数は以下のイベントによってトリガーされる
    • API Gateway URL へのリクエスト
    • S3 バケットへのアップロード
    • CloudWatch スケジュール
    • SNS トピックのメッセージ
    • CloudWatch アラート
    • etc...
  • Lambda にイベントを設定すると Serverless Framework は自動的にそのイベントに必要なリソースを作成し、それをリッスンするように設定する

例えば、Lambda 関数のみを serverless.yml に定義していたとしても、functions.<function name>.events フィールドに http アクセスの記載があった場合は API Gateway も作成されるということです。

Resources

  • Resource は以下のような AWS コンポーネントである
    • DynamoDB テーブル
    • S3 バケット
    • SNS トピック
    • CloudFormation で定義できる全てのリソースは Serverless Framework でサポートされる
  • Serverless Framework は Lambda 関数とそのイベントだけでなく、AWS リソースもデプロイできる

おっと、「CloudFormation で定義できるすべてのリソースが管理可能である」との文言が。また、 Lambda 関数とそのイベントだけでなく、AWS リソースもデプロイできる」ともありますね。

基本的には FaaS 利用に特化したほうがいい気がしますが、その他の用途でも Serverless Framework が利用できるのですね。どうなんでしょう、売り文句程度に捉えていいんですかね?他フレームワークとの使い分けや共存はここら辺が参考になりそうです(※まだ読んでないですが)。

dev.classmethod.jp

qiita.com

Services

  • Service とは Framework の管理単位
  • 一つのアプリケーションに対して複数のサービスを持つことができるが、Service は一つのプロジェクトファイルとして考えよ
  • Service は serverless.yml ファイルで構成され、関数・イベント・AWS リソースを以下のように定義する
  • 設定ファイル内のすべてが一度にデプロイされる
service: users

functions: # Your "Functions"
  usersCreate:
    events: # The "Events" that trigger this function
      - httpApi: 'POST /users/create'
  usersDelete:
    events:
      - httpApi: 'DELETE /users/delete'

resources: # The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.

serverless.yml と管理単位である Service は1対1(というよりファイルがサービスそのもの)であり、関連する関数やリソースは全て1ファイル内に書く。ファイル内に複数の関数を定義することができる。」ということですかね。

また、resources フィールドは素の CloudFormation を記述するようです。

設定形式

  • serverless.yaml 形式ではなく、serverless.json, serverless.js, serverless.ts で記載することもできる
  • JavaScript, TypeScript を使用する場合、以下のように設定をオブジェクトとしてエクスポートする必要がある
serverless.js
'use strict';

module.exports = {
  service: 'users',
  functions: {
    usersCreate: {
      events: [
        {
          httpApi: 'POST /users/create',
        },
      ],
    },
    // ...
  },
  resources: {},
};
serverless.ts
// Requiring @types/serverless in your project package.json
import type { Serverless } from 'serverless/aws';

const serverlessConfiguration: Serverless = {
  service: 'users',
  functions: {
    usersCreate: {
      events: [
        {
          httpApi: 'POST /users/create',
        },
      ],
    },
    // ...
  },
  resources: {},
};

module.exports = serverlessConfiguration;

動的に serverless.yml を作成したい場合に便利そうですね。一方でコードの管理(というか保守)が大変そうなので個人的には静的ファイルで管理したいです。コードを Git 管理するときも最終的な差分が目に見えてわかりやすいので、動的に吐くにしても静的ファイルに一旦落としてから serverless deploy したい。ここら辺は好みですかね?

プラグイン

  • プラグインを利用し機能の上書き・拡張が可能
  • plugins プロパティがあり、複数のプラグインを適用できる
serverless.yml
plugins:
  - serverless-offline
  - serverless-secrets

直近で StepFunction を作りたいので、プラグインにはお世話になりそうです。

まとめ

今回見てきた概念の関係をまとめるとこんな感じですかね?

  • Service:サービス単位、アプリケーションに関連する関数・イベント・リソースをこの中に入れる

    • Function:Lambda 関数
    • Event:Lambda トリガーのためのイベント(API Gateway や Cloudwatch、SNS など)
    • Resource:DynamoDB, S3 などの Lambda で処理するデータリソース。CloudFormation の記法で記述する。
  • yaml 形式だけでなく json, JavaScript, TypeScript でも記述可能

  • 拡張機能利用のためのプラグインが利用可能

コンセプトがあらかたわかったので次回は serverless.yml のトップレベルプロパティについて見ていこうと思います。

Serverless Framework 初めて触ってみた

Serverless Framework を利用するプロジェクトにアサインされました。

今まで全く触ったことがなかったのですが、いきなり「serverless.yaml 書いて」と言われたので急遽勉強するための環境を整えます。

インストール

www.serverless.com

node, npmバージョン確認

$ node -v
v18.12.1

$ npm -v
8.19.2

インストール

$ npm install -g serverless

serverless バージョン確認

$ serverless -v
Framework Core: 3.27.0
Plugin: 6.2.3
SDK: 4.3.2

プロジェクト作成

どうやら serverless でインタラクティブにプロジェクトが初期化できるようなのですが、選べるテンプレートが少なく、Lambda(Python) がなかったので、create コマンドで作成するようにします。

www.serverless.com

プロジェクト初期化

$ serverless create \
--template aws-python3 \ # 上記ページの "Available Templates" から選択
--path tutorial \ # コマンド実行ディレクトリに作成するプロジェクトディレクトリ名を指定
--name tutorial #  serverless.yml に記載されるプロジェクト名を指定

# GitHub等からテンプレートを持ってくる場合には `--template-url` を指定します。

AWS 権限設定

www.serverless.com

serverless でAWS APIを実行するための権限設定を行います。手順は以下の通り。

  1. IAMユーザ作成
  2. AdministratorAccessポリシーを付与・・・※補足1参照
  3. アクセスキーを作成

次に aws configure をします。serverless config credentials でラップされているようなのでこちらを使います。

$ serverless config credentials \
--provider aws \
--profile talkeyboid-serverless-user \
--key XXXXXXXXXXXXXX \
--secret XXXXXXXXXXXXXXXXXXX

プロファイル環境変数指定

$ export AWS_PROFILE=talkeyboid-serverless-user

補足1

※ドキュメントチュートリアルでは「AdministratorAccess を付与」と書いてありますが、怖すぎます。一応注意書きはある模様。

Note that the above steps grant Serverless Framework administrative access to your account. While this makes things simple when starting out, we recommend that you create and use more fine-grained permissions once you determine the scope of your serverless applications and move them into production.

権限を絞って与えるには以下が参考になりそうです。皆さん苦労されている様子。

blog.n-t.jp

qiita.com

ポリシージェネレータのリポジトリもありました。これですべてができるとは思いませんが、かなりの時短になりそうな予感。

github.com

github.com

補足2

以下のように serverless.yml を記載することで、--stage オプションに応じて、利用するプロファイルを変えることができるようです。便利。

service: new-service
provider:
  name: aws
  runtime: nodejs14.x
  profile: ${self:custom.profiles.${sls:stage}}
custom:
  profiles:
    dev: devProfile
    prod: prodProfile

また、定義した各リソースを CloudFormation 側でデプロイするときに利用するロール(サービスロール)を指定できるようでした。

provider:
  iam:
    deploymentRole: arn:aws:iam::123456789012:role/deploy-role

ここら辺は問題もある模様(カスタムリソースが勝手に作られそれにサービスロールが当たってしまう)。余裕があればここら辺もキャッチアップしたいですね。特に本番では気になる。

go-to-k.hatenablog.com

プロジェクト確認

ここで一度プロジェクトの内容を確認します。serverless create で作成したプロジェクトはこんな感じ。

$ tree
.
└── tutorial
    ├── handler.py
    └── serverless.yml

1 directory, 2 files

handler.py は Lambda デフォルトとほぼ同じコード、関数名が hello になってますが、yaml側でハンドラー定義もされてるんでしょうか。

handler.py
import json


def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

    # Use this code if you don't use the http event with the LAMBDA-PROXY
    # integration
    """
    return {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "event": event
    }
    """

serverless.yml はこんな感じ。やたらと親切にコメントでいろいろと書いてくれています。必要なところだけコメント外して書き換えるだけでいろいろとできそうですね。

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: tutorial
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '3'

provider:
  name: aws
  runtime: python3.8

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

# you can add statements to the Lambda function's IAM Role here
#  iam:
#    role:
#      statements:
#        - 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:
#  patterns:
#    - '!exclude-me.py'
#    - '!exclude-me-dir/**'
#    - include-me.py
#    - include-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:
#      - httpApi:
#          path: /users/create
#          method: get
#      - websocket: $connect
#      - 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: amzn1.ask.skill.xx-xx-xx-xx
#      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
#      - 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
#      - alb:
#          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
#          priority: 1
#          conditions:
#            host: example.com
#            path: /hello

#    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"

コメント外すとたったこれだけです。予想通りハンドラ定義ありましたね。ソースを変更するときはここを書き換えればよさそうです。

serverless.yml
service: tutorial
frameworkVersion: '3'

provider:
  name: aws
  runtime: python3.8

functions:
  hello:
    handler: handler.hello

デプロイ

デプロイ

$ serverless deploy

CloudFormationスタックが作成されたことを確認。どうやらデフォルトで dev がつくようですね。custom.profiles を指定すれば任意に設定できるのでしょうかね(知らんけど)。

Lambda, ロググループ, IAMロール, S3バケット が作成されているようです。serverless.yaml では Lambda しか定義していなかったですが良しなに作ってくれました。ただ、ここでリージョンが us-east-1 になっていることに気づきました。IAM権限設定の際に serverless config をしましたが、ここでおそらくリージョンを設定しないといけなかった気がします。もしくはテンプレート内にリージョン書けばできるかな?

とりあえず、各リソースを確認していきます。

Lambda

ソースが反映されていることがわかります。

設定はマネジメントコンソールで作成するときとかなり違いますね。デフォルトだとタイムアウトが長く、メモリも大きいです。

ロール

ポリシーは以下が当たってました。Lambdaの一番シンプルな組み込みロールと同じ感じですね。

tutorial-dev-us-east-1-lambdaRole

tutorial-dev-lambdaポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:CreateLogStream",
                "logs:CreateLogGroup"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:XXXXXXXXXXXX:log-group:/aws/lambda/tutorial-dev*:*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:XXXXXXXXXXXX:log-group:/aws/lambda/tutorial-dev*:*:*"
            ],
            "Effect": "Allow"
        }
    ]
}

S3

バケットには以下のファイルが格納されていました。

ファイル 中身
compiled-cloudformation-template.json CloudFormationに変換したテンプレート
serverless-state.json serverless.ymlで定義していない値をデフォルト値で埋めたテンプレート
tutorial.zip デプロイ資材(ここでは handler.py

これらは serverless deploy 時にコンパイルされた .serverless ディレクトリ内のファイルと "ほぼ" 同じもののようです。

$ ls .serverless/
cloudformation-template-create-stack.json  serverless-state.json
cloudformation-template-update-stack.json  tutorial.zip

どのような対応か、わかりやすく説明してくださってるページがありました。

blog.giftee.dev

どうやらこんな感じの流れなようです。

  • serverless.yml を解析
  • 埋まっていない値をデフォルト値で補完し serverless-state.json を出力
  • 定義ファイルやソースを格納するS3バケットを作成するためのテンプレートを cloudformation-template-create-stack.json として出力
  • 実際に作成するテンプレートを cloudformation-template-update-stack.json として出力。これはS3バケット上の compiled-cloudformation-template.json と同じ。※同じものがアップロードされるという意味なのかは不明

お掃除

全削除。CloudFormationスタックが削除されます。

$ serverless remove

serverless 実行用に作成したアクセスキーを無効化しておきます。

感想

全くの0からなんとなくイメージはつかめました。

serverless.yml の書き方、IAM権限の絞り込み周りを勉強していきます。

0.6 ÷ 0.2 の意味がわかりますか?「子どもの算数、なんでそうなる?」を読みました。

ゆる言語学ラジオでちらっと紹介のあったこの本を読みました。

www.iwanami.co.jp

子供の算数における様々な間違いを挙げ「なぜそのような間違いが起きるのか」「子供はどのように数を捉えているか」の考察がされています。また、そのような誤りをした子どもとの向き合い方について筆者の思いが語られています。

特に興味深かったのは第7章「かけ算の順序・かけ算の種類」。

かけ算を8つのパターンに分類しています。(分類をここに書いてしまうのはよくないので知りたい人は本を買ってください。)

そして付録の章ではそれぞれのパターンについて「等分除・包含除」を切り口にもう少し詳細な分析をしています。

「等分除・包含除」については Wikipedia の「かけ算の順序問題」でも触れられています。乗法・除法において基本的な考え方なのでしょう。

ja.wikipedia.org

ある量が「基準となる量」の「幾つ分」に除されるかを考えるとき、「基準となる量」を求めるのが等分除、「幾つ分」になるかを求めるのが包含除である。 ※上記Wikipedia「等分除と包含除」の章より

具体例を出しましょう。6個入のピノを2人で3個ずつ分けることを想像してください。上記「基準となる量」「幾つ分」と「6個のピノ」の関係はこのようになります。

基準となる量 × 幾つ分 = 6個のピノ

そして、「6個のピノを2人に3つずつ分ける」ときの考え方は問題設定により2通りあります。

問題設定 考え方 分類
一人分はいくつでしょう 基準となる量(1人当たりのピノの個数) × 2人分 = 6個 等分除
何人分でしょう 3個 × 幾つ分(何人分) = 6個 包含除

もちろん、これは "何を基準にするか" によって交換可能な概念です。「基準となる量」を2人とし、「幾つ分」をピノの個数と解釈することも可能だからです。

大人からすれば簡単すぎて当たり前のことですが、これがしっかりと腹落ちできていないと、以下の問題における 0.6 ÷ 0.2 の意味することが理解できないと思います。

(a) 0.6L の水を 0.2L ずつコップに入れていく。何杯できるか。

(b) 0.6L の水が全体の 20% であるとき、全体は何Lか。

谷口, 子どもの算数、なんでそうなる?, p.140

自分の子どものころを思い出すと、確か分数や少数の割り算を学んだときに、"計算もできるし、意味も口では説明できる。でもなんかもやもやする" と思って手元のおはじきやブロックを使って何度も確認作業をし、それでもわからなくて泣いた記憶があります。

筆者は、"誤りは必ずしも悪いことではなく、深い理解に繋がり得るもの。本人の思考をよそに誤りを訂正するのでは子どもの学びを失ってしまう恐れがある" という主旨のことを結びで語っています。学びって大事(小並感)。

MQなんもわからん同好会会長に就任しました

ふとしたことから、知人からAMQPについて聞き、AMQPってなんぞや?と思ったのでちょこっと調べてみましたが、、、なんもわからん。。。

MQTTはさわったことあるけどほぼマネージドオンリーなので中身を気にすることもなく恥をさらして生きてきた代償です。

なんとなくわかりました(なんもわからん)。たぶんものすごくわかりやすく説明してくださってるんだと思います。すみません。

一対一の高信頼性が求められるメッセージには AMQPってコト?

ちなみに、MQについての超概要は以下ページのリンクPDFが超絶わかりやすいです。

www.janog.gr.jp