使う予定ないのに Ozone 10 にアップグレードした僕は完全にセールの奴隷(^q^)ついでにプラグイン棚卸

今セールしているので Ozone 9 から 10 にアップグレードしました。使う予定はまだないです。軽く沼ってる気がします。

新機能が使えるぞ!※まだ1秒も使ってない。

ついでにプラグインを棚卸しました。なんか他に買ってる気もするが一旦これだけ。

種別 メーカー ソフト
DAW Ableton Ableton Live 11 Suite
Plugin Native Instruments KOMPLETE 13 Ultimate
Plugin WAVES Diamond 12
Plugin WAVES Sibilance 14
Plugin WAVES Vocal Rider 12
Plugin FabFilter Pro Q3
Plugin iZotope Ozone 10 Advanced
Plugin Antares Autotune Access

それぞれどこにアプリケーション、オーディオファイル、VSTが配置されていて自分で外付けSSDに逃がしたのはどれかとか整理したいんですが大変なのでまたやる気のあるときに(絶対やらないやつ)。

Git Bash で スラッシュを含むパスをコマンドライン引数に指定する

業務環境では WSL2 が使えないので Git Bash で AWS CLI 操作をしていましたが、スラッシュが扱えなかったためメモ。

MSYS側で良しなに Windows パスを扱えるようにしてくれますが、CLI の引数では / はそのまま通してほしいです。

そんなときに MSYS_NO_PATHCONV=1 を指定します。

MSYS_NO_PATHCONV=1 aws logs describe-metric-filters --log-group-name /aws/lambda/hoge-function

以上!

WSL2にAnaconda入れてJupyter動かしたら起動に9分近くかかってうんこ漏れた

導入までは以下の記事ほぼそのままです。※pip 使わず conda 使ってるところだけ違う。元記事の方はうんこ漏らしてません。

kondeneenen.com

インストール

インストーラは一ディレクトリ作成

$ mkdir tmp
$ cd tmp

以下のページより Linux 向けインストーラのパスをコピー

www.anaconda.com

これ

ダウンロード

# URLに上記インストーラのパスを指定
$ wget https://repo.anaconda.com/archive/Anaconda3-2022.10-Linux-x86_64.sh

インストーラ実行

$ bash Anaconda3-2022.10-Linux-x86_64.sh 

Welcome to Anaconda3 2022.10

In order to continue the installation process, please review the license
agreement.
Please, press ENTER to continue
>>> 

##### ここにライセンス文が表示される #####

ライセンスに同意

Do you accept the license terms? [yes|no]
[no] >>> yes

ロケーション選択

Anaconda3 will now be installed into this location:
/home/talkeyboid/anaconda3

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/home/talkeyboid/anaconda3] >>>

conda init するか

Do you wish the installer to initialize Anaconda3
by running conda init? [yes|no]
[no] >>> yes

/tmp を削除

$ cd ..
$ rm -rf tmp/

パス設定

パス設定し反映

$ echo "export PATH=~/anaconda3/bin:\$PATH" >> ~/.bash_profile
$ source ~/.bash_profile

バージョン確認

$ conda -V
conda 22.9.0

conda init

元記事では conda init bash を実行していますが、インストーラの指示に従い、conda init? >>> yes としたため対応不要です。

念のため確認

$ cat ~/.bashrc
...
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/home/talkeyboid/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/home/talkeyboid/anaconda3/etc/profile.d/conda.sh" ]; then
        . "/home/talkeyboid/anaconda3/etc/profile.d/conda.sh"
    else
        export PATH="/home/talkeyboid/anaconda3/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

設定を反映

$ source ~/.bashrc

するとこんな感じでプロンプトに conda 環境名がつく

talkeyboid@talkeyboidsrfc:~$ source ~/.bashrc 
(base) talkeyboid@talkeyboidsrfc:~$ 

conda 環境構築

一旦適当に作る。

$ conda create -n test python=3.9

$ conda info -e
# conda environments:
#
base                  *  /home/talkeyboid/anaconda3
test                     /home/talkeyboid/anaconda3/envs/test

$ conda activate test

Jupyter導入

# 元サイトは pip で入れているが私の環境では conda でインストールする
$ conda install jupyterlab

$ jupyter kernelspec list
Available kernels:
  python3    /home/talkeyboid/anaconda3/envs/test/share/jupyter/kernels/python3

Jupyter起動

$ jupyter lab --NotebookApp.token=''
[W 2023-03-06 21:54:36.226 LabApp] 'token' has moved from NotebookApp to ServerApp. This config will be passed to ServerApp. Be sure to update your config before our next release.
[I 2023-03-06 21:54:36.232 ServerApp] jupyterlab | extension was successfully linked.
[I 2023-03-06 21:54:36.238 ServerApp] nbclassic | extension was successfully linked.
[I 2023-03-06 21:54:36.241 ServerApp] Writing Jupyter server cookie secret to /home/talkeyboid/.local/share/jupyter/runtime/jupyter_cookie_secret
[I 2023-03-06 21:54:36.458 ServerApp] notebook_shim | extension was successfully linked.
[W 2023-03-06 21:54:36.475 ServerApp] All authentication is disabled.  Anyone who can connect to this server will be able to run code.
[I 2023-03-06 21:54:36.478 ServerApp] notebook_shim | extension was successfully loaded.
[I 2023-03-06 21:54:36.480 LabApp] JupyterLab extension loaded from /home/talkeyboid/anaconda3/envs/test/lib/python3.9/site-packages/jupyterlab
[I 2023-03-06 21:54:36.480 LabApp] JupyterLab application directory is /home/talkeyboid/anaconda3/envs/test/share/jupyter/lab
[I 2023-03-06 21:54:36.484 ServerApp] jupyterlab | extension was successfully loaded.

  _   _          _      _
 | | | |_ __  __| |__ _| |_ ___
 | |_| | '_ \/ _` / _` |  _/ -_)
  \___/| .__/\__,_\__,_|\__\___|
       |_|
                                                                           
Read the migration plan to Notebook 7 to learn about the new features and the actions to take if you are using extensions.

https://jupyter-notebook.readthedocs.io/en/latest/migrate_to_notebook7.html

Please note that updating to Notebook 7 might break some of your extensions.

[I 2023-03-06 21:54:36.491 ServerApp] nbclassic | extension was successfully loaded.
[I 2023-03-06 21:54:36.492 ServerApp] Serving notebooks from local directory: /home/talkeyboid
[I 2023-03-06 21:54:36.492 ServerApp] Jupyter Server 1.23.4 is running at:
[I 2023-03-06 21:54:36.492 ServerApp] http://localhost:8888/lab
[I 2023-03-06 21:54:36.492 ServerApp]  or http://127.0.0.1:8888/lab
[I 2023-03-06 21:54:36.492 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

が、http://localhost:8888/lab もしくは http://localhost:8888 でつながらない。

こんなかんじで pending

設定ファイル周りで何かよくないのかといろいろとググっていたところ、いつのまにか起動してました。あまりに時間がかかってしまっていたので、ググってる途中でちょっとうんこ漏れた。

何回もアクセスしていたからか、起動ログ的には GET がめちゃくちゃ走ってる。22:04~22:13って時間かかりすぎでないか???使い物にならん。

[I 2023-03-06 22:04:20.021 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

[I 2023-03-06 22:13:09.644 ServerApp] 302 GET / (127.0.0.1) 31.37ms
[I 2023-03-06 22:13:09.645 ServerApp] 302 GET / (127.0.0.1) 31.54ms
[I 2023-03-06 22:13:09.645 ServerApp] 302 GET / (127.0.0.1) 31.49ms
[I 2023-03-06 22:13:09.645 ServerApp] 302 GET / (127.0.0.1) 31.28ms
[I 2023-03-06 22:13:09.646 ServerApp] 302 GET / (127.0.0.1) 31.20ms
[I 2023-03-06 22:13:09.646 ServerApp] 302 GET / (127.0.0.1) 31.16ms
[I 2023-03-06 22:13:09.651 ServerApp] 302 GET / (127.0.0.1) 35.09ms
[I 2023-03-06 22:13:09.652 ServerApp] 302 GET / (127.0.0.1) 35.22ms
[I 2023-03-06 22:13:09.652 ServerApp] 302 GET / (127.0.0.1) 35.40ms
[I 2023-03-06 22:13:09.664 ServerApp] 302 GET / (127.0.0.1) 45.87ms
[I 2023-03-06 22:13:09.677 ServerApp] 302 GET / (127.0.0.1) 55.90ms

ノートブック動きました。

立ち上がるまでにめちゃ時間かかるのとても前時代的PC感あるのでなんとかしたいですね。

curl で DockerHub から公式イメージのタグ一覧を取得する方法

基本的には以下のやり方でOK。

qiita.com

curl https://hub.docker.com/v2/repositories/<repository name>/tags?page_size=100 | jq .results[].name

公式イメージを取得する場合は、リポジトリ名に library を指定する必要があるので注意。

以下では "magic string" と呼んでいる人がいる。公式ドキュメントに記載はない模様(見つけた人いたら教えて)。

stackoverflow.com

例えば python:3.6 の亜種を見つけたい場合は以下のようにする。

curl https://hub.docker.com/v2/repositories/library/python/tags?page_size=100 | jq .results[].name | grep 3.6

CKA, CKAD の試験では DockerHub を見ることができないので覚えておきたい。

が、、、そんな問題出るのか? Nginx Ingress だって試験中に公式ページ見れないけど全部覚える???

無理、そんなむずい問題でたら諦めます。

Serverless Framework で StepFunctions 作成とログ監視を設定する(1)

今回は Serverless Framework で StepFunctions を作成し、エラー時の Cloudwatch Alarm も併せて定義します。

構築する環境

今回構築する環境を超適当に書くとこんな感じです。

Step Functions の Map を使って後続の関数をスケールします。SQS 使わずに実装できてとても便利です。

また、Step Functions の実行がコケたときに通知するため、Alarm と SNS を実装しています。

プラグイン準備

今回は2つのプラグインを利用します。

$ npm install -g serverless-step-functions
$ npm install -g serverless-prune-plugin

www.serverless.com

www.serverless.com

Serverless プロジェクト作成

こんな感じでディレクトリを作成します。

$ tree stepfunctions-tutorial/
stepfunctions-tutorial/
├── email.yml
├── handler1.py
├── handler2.py
└── serverless.yml

0 directories, 4 files

email.yml は SNS トピックに設定するメールをブログでうっかり公開しないように serverless.yml から外出しするために作りました。

email.yml
address: hogehoge@fugafuga.com

Lambda 実装

適当に Lambda を実装します。

handler1.py
import json

def func1(event, context):
    body = {
        "message": "Go Serverless v3.0! Your function executed successfully!",
        "input": event,
        "targets": [0,1,2]
    }
    response = {"statusCode": 200, "body": body} # json.dumps しない。
    return response
handler2.py
import json

def func2(event, context):
    print(event)
    body = {
        "message": "Go Serverless v3.0! Your function executed successfully!",
        "input": event,
    }
    response = {"statusCode": 200, "body": json.dumps(body)}
    return response

handler1.py から body.targets 配列を返しています。Lambda の初期レスポンス値は json.dumps() されているのですが、dump するとエスケープされてしまうので json.dumps() しないように注意。

qiita.com

ちなみに、json.dumps() した値が後続処理に渡ると以下のようにマッピング失敗となります。

Serverless 設定

serverless.yml を書いていきます。

まずは全体像。

serverless.yml
org: talkeyboid
service: stepfunctions-tutorial
frameworkVersion: '3'

plugins:
  - serverless-step-functions
  - serverless-prune-plugin

provider:
  name: aws
  runtime: python3.9
  region: ap-northeast-1
  memorySize: 128
  timeout: 3
  logRetentionInDays: 14
  apiKeys:
    - ${self:service}-key
  usagePlan:
    quota:
      limit: 100
      offset: 0
      period: MONTH
    throttle:
      burstLimit: 3
      rateLimit: 1

custom:
  prune:
    automatic: true
    number: 3
  env:
    email: ${file(./email.yml)}

functions:
  func1:
    handler: handler1.func1
  func2:
    handler: handler2.func2

stepFunctions:
  stateMachines:
    myStateMachine1:
      name: myStateMachine1
      # role: "設定する場合はここにARN"
      loggingConfig:
        level: ALL
        includeExecutionData: true
        destinations:
        - Fn::GetAtt: [StepFunctionLogGroup, Arn]
      alarms:
        topics:
          alarm:
            Ref: EmailTopic
        metrics:
        - executionTimedOut
        - executionFailed
        - executionsAborted
        - executionThrottled
        treatMissingData: missing
      events:
      - http:
          path: hoge
          method: POST
          private: true
          # iamRole: "設定する場合はここにARN"
      definition:
        StartAt: Task1
        States:
          Task1:
            Type: Task
            Resource:
              Fn::GetAtt: [Func1LambdaFunction, Arn]
            Next: MapTask
          MapTask:
            Type: Map
            ItemsPath: "$.body.targets"
            Iterator:
              StartAt: Task2
              States:
                Task2:
                  Type: Task
                  Resource:
                    Fn::GetAtt: [Func2LambdaFunction, Arn]
                  End: true
            End: true

resources:
  Resources:
    EmailTopic:
      Type: AWS::SNS::Topic
      Properties:
        TopicName: EmailTopic
    EmailSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Protocol: email
        TopicArn: !Ref EmailTopic
        Endpoint: ${self:custom.env.email.address}
    StepFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      DeletionPolicy: Delete
      Properties:
        LogGroupName: /aws/states/${self:service}-${self:stepFunctions.stateMachines.myStateMachine1.name}-Logs
        RetentionInDays: 14

ポイントをいくつか。

provider

serverless.yml
provider:
  name: aws
  runtime: python3.9
  region: ap-northeast-1
  memorySize: 128
  timeout: 3
  logRetentionInDays: 14
  apiKeys:
    - ${self:service}-key
  usagePlan:
    quota:
      limit: 100
      offset: 0
      period: MONTH
    throttle:
      burstLimit: 3
      rateLimit: 1

provider で メモリサイズとタイムアウト等を明示的に指定しています。serverless のデフォルト値だとオーバースペックすぎるためです。

apiKeys, usagePlan で API Gateway のキーと使用量プランを設定しています。これは明示的に設定しない限り作成されないので自分で書く必要があります。API キーが不要な場合は記載しなくていいです。

custom

serverless.yml
custom:
  prune:
    automatic: true
    number: 3
  env:
    email: ${file(./email.yml)}

custom.prune で Lambda のバージョン世代保持数を指定しています。デフォルトでは無限に増殖するため、抑えないといけません。今回は検証なので特に指定する必要はなかったですが、後学のために設定しました。

dev.classmethod.jp

また、env.emailemail.yml ファイルを設定しています。そのため、メールアドレスを取得したければ ファイル内のプロパティと結合し、${self:custom.env.email.address} で取得できます。

stepFunctions

今回の本題、StepFunctions の部分です。

ロググループについて

serverless.yml
stepFunctions:
  stateMachines:
    myStateMachine1:
      name: myStateMachine1
      # role: "設定する場合はここにARN"
      loggingConfig:
        level: ALL
        includeExecutionData: true
        destinations:
        - Fn::GetAtt: [StepFunctionLogGroup, Arn]

StepFunctions のロググループは明示的に設定しないと作成されないようなので、後述する resources でロググループを作成し、それを loggingConfig.destinationsFn::GetAtt で Arn を取得し設定しています。

Alarm について

serverless.yml
      alarms:
        topics:
          alarm:
            Ref: EmailTopic
        metrics:
        - executionTimedOut
        - executionFailed
        - executionsAborted
        - executionThrottled
        treatMissingData: missing

Cloudwatch Alarm の設定もここでできます。alarms プロパティの中で、後述する resources で定義した SNS トピックを設定しています。メトリクスは以下を参照。

docs.aws.amazon.com

欠損データの取り扱いについては以下を参照。

docs.aws.amazon.com

ここでは書いていませんが、スレッショルドも設定できると思うので次やるときには設定したいと思います。

API Gateway について

API Gateway の設定は Lambda の定義と同じく、events に設定します。API キーを利用する場合、private=true に設定します。

serverless.yml
      events:
      - http:
          path: hoge
          method: POST
          private: true
          # iamRole: "設定する場合はここにARN"

データフローについて

serverless.yml
      definition:
        StartAt: Task1
        States:
          Task1:
            Type: Task
            Resource:
              Fn::GetAtt: [Func1LambdaFunction, Arn]
            Next: MapTask
          MapTask:
            Type: Map
            ItemsPath: "$.body.targets"
            Iterator:
              StartAt: Task2
              States:
                Task2:
                  Type: Task
                  Resource:
                    Fn::GetAtt: [Func2LambdaFunction, Arn]
                  End: true
            End: true

StepFunctions の中身を definition に設定します。ここはマネジメントコンソールのビジュアルエディタで作成した json の内容をほぼそのまま転記する形で記載できそうです。

ただし、どの Lambda 関数を実行するかは若干のハックが必要です。Arn を指定する必要があるため、Fn::GetAtt をしていますが、ここへは functions へ定義した関数名をパスカルケースにし、後ろに LambdaFunction を付けます。

functions:
  func1: # これをパスカルケースにする -> Func1、+ LambdaFunction = Func1LambdaFunction
    handler: handler1.func1
  func2: # これをパスカルケースにする -> Func2、+ LambdaFunction = Func2LambdaFunction
    handler: handler2.func2

これは serverless の仕様で、serverless deploy(package) するときに作成される CloudFormation テンプレートにそのように定義されるためです。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "The AWS CloudFormation template for this Serverless application",
  "Resources": {
  ...
    "Func1LambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        ...
        "Handler": "handler1.func1",
        "Runtime": "python3.9",
        "FunctionName": "stepfunctions-tutorial-dev-func1",
        ...
  }
}

resources

serverless.yml
resources:
  Resources:
    EmailTopic:
      Type: AWS::SNS::Topic
      Properties:
        TopicName: EmailTopic
    EmailSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Protocol: email
        TopicArn: !Ref EmailTopic
        Endpoint: ${self:custom.env.email.address}
    StepFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      DeletionPolicy: Delete
      Properties:
        LogGroupName: /aws/states/${self:service}-${self:stepFunctions.stateMachines.myStateMachine1.name}-Logs
        RetentionInDays: 14

Email 通知用の SNS トピック・サブスクリプションと、StepFunctions 用のロググループを作成しています。StepFunctions のデフォルトのロググループ名は /aws/vendedlogs/states/<statemachine name>-Logs のようになっているため、それに合わせています。※AWSで作成しているわけではないので、vendedlogs は抜いています。

RetentionInDays は完全に適当です。実務で使うときには考えましょう。

実行確認

マネジメントコンソールからエンドポイントとAPI キーを取得し、Postman に設定し、実行します。

項目
メソッド POST
URL ステージのエンドポイント + パス(https://~/dev/hoge
HTTPヘッダ x-api-key=APIキー

私はよくルートURLの後にパスをつけ忘れて 403 になってしまうことがあります。404 なら気づきようがあるのですが、403 だと「キー設定してるのにおかしいな...」となるので備忘として残しておきます。

実行結果は以下のように返ってきます。executionArn は ステートマシン名 + 実行ID(ランダム値)です。

{
    "executionArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:myStateMachine1:0fbeb2e9-581c-49ed-88b8-1c70e200a609",
    "startDate": 1.676463213988E9
}

感想

少々ハックはありますが、簡単に実装することができました。

Alarm の確認もしようと思ったのですが、結構時間がかかったのでまた次回。

Serverless Framework Functions プロパティ(2)

前回の記事

前回に引き続き serverless.yml の Functions プロパティについてまとめていきたいと思います。

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

www.serverless.com

アーキテクチャ

Lambda の CPU アーキテクチャを指定します。

provider.architecture, functions.<function name>.architecture に記載します。

arm64 の場合

serverless.yml
functions:
  hello:
    architecture: arm64

インテルの場合はドキュメントに記載がないですが、x86_64 を書けばよいと思います。

ランタイム管理

セキュリティパッチ等のランタイム更新タイミングをモードとして指定します。

provider.runtimeManagement, functions.<function name>.runtimeManagement に記載します。

モードは auto, onFunctionUpdate, manual(デフォルト値) が指定可能です。

各モードの詳細はこちら。

docs.aws.amazon.com

provider に設定する場合、auto, onFunctionUpdate が利用可能です。

serverless.yml
provider:
  runtimeManagement: onFunctionUpdate

functions に設定する場合は3つのモードすべて記述することができます。この場合、mode の他に arn を指定する必要があります。

serverless.yml
functions:
  hello:
    runtimeManagement:
      mode: manual
      arn: <aws runtime arn>

"aws runtime arn" って何でしょうか? CloudFormation での記載方法と同じように ランタイム識別子である python3.9 とか nodejs16.x とかを記載すればよいのでしょうか。

識別子の一覧は以下参照。

docs.aws.amazon.com

SnapStart

コールドスタート問題を解消するためにキャッシュされた関数のスナップショットから起動する機能です。2023/2 現在では Java11 ランタイムのみでサポートされます。他にもいろいろと制限があるのでご注意。

docs.aws.amazon.com

functions.<function name>.snapStart に記載します。

serverless.yml
functions:
  hello:
    runtime: java11
    snapStart: true

VPC 設定

VPC Lambda 用設定です。

provider.vpc, functions.<function name>.vpc に記載します。

serverless.yml
functions:
  hello:
    vpc:
      securityGroupIds:
        - securityGroupId1
        - securityGroupId2
      subnetIds:
        - subnetId1
        - subnetId2

provider に設定しているが、一部の関数には適用したくない(パブリック Lambdaとしたい)場合には ~ で継承しないように設定できます。

serverless.yml
provider:
  name: aws
  vpc:
    securityGroupIds:
      - securityGroupId1
      - securityGroupId2
    subnetIds:
      - subnetId1
      - subnetId2

functions:
  hello: # パブリックな Lambda となる
    handler: handler.hello
    vpc: ~
  users: # provider 継承により VPC Lambda となる
    handler: handler.users

実行ロールについて 上記の設定を行った場合、デフォルトでは "AWSLambdaVPCAccessExecutionRole" がアタッチされます。

カスタムロールを利用する場合は、ENI の create, describe, delete 権限が必要なことに注意してください。

docs.aws.amazon.com

インターネットアクセスについて

デフォルトでは VPC Lambda はインターネットアクセスができないため、VPC エンドポイントや NAT GW の設定が必要なことに注意してください。

環境変数

provider.environment, function.<function name>.environment に記載します。両方に同じ環境変数が存在する場合は functions の値で上書きされます。

serverless.yml
provider:
  name: aws
  environment:
    SYSTEM_NAME: mySystem
    TABLE_NAME: tableName1
 
functions:
  hello: # 2つの環境変数を継承する
    handler: handler.hello
  users:
    handler: handler.users
    environment: # SYSTEM_NAME を継承し、TABLE_NAME は上書きする
      TABLE_NAME: tableName2

外の値を参照したい場合の書き方については以下参照。

www.serverless.com

タグ

provider.tags, functions.<function name>.tags に記載します。両方に同じタグが記載されている場合は functions の値で上書きされます。

serverless.yml
provider:
  tags:
    foo: bar
    baz: qux

functions:
  hello: # 2つのタグを継承する
    handler: handler.hello
  users:
    handler: handler.users
    tags: # baz を継承し、foo は上書きする
      foo: quux

Lambda レイヤー

functions.<function name>.layers に記載します。

serverless.yml
functions:
  hello:
    handler: handler.hello
    layers:
      - arn:aws:lambda:region:XXXXXX:layer:LayerName:Y

ロググループ

functions.<function name> 直下に disableLogs, logRetentionInDays, logDataProtectionPolicy を指定します。

デフォルトでは Serverless Framework により Lambda のロググループを新規作成されますが、これを無効化するために disableLogs が利用できます。

logRetentionInDays は文字通りログ保管期間(日数)の設定です。

logDataProtectionPolicy はログのマスク処理の設定をします。Serverless 側のドキュメントには詳細な記載方法がなかったのですが、AWS 公式ドキュメント記載のポリシー構文に従って書けばよさそうです。

docs.aws.amazon.com

serverless.yml
functions:
  hello:
    handler: handler.hello
    disableLogs: true # ロググループを新規作成しない
  goodBye:
    handler: handler.goodBye
    logRetentionInDays: 14 # 14日間保持
    logDataProtectionPolicy: # ポリシー構文に従い記述
      Name: data-protection-policy

既存のロググループを指定するときはどうするんだろう...(ベストプラクティスからはかけ離れていることは承知の上で)。

バージョニング

Serverless Framework では serverless deploy の度にバージョンを上げていきます。古いバージョンはフレームワークの管理対象ではないため自分で削除していく必要があります。

serverless deploy を2回やったときの図

関数バージョンをオフにするには provider.versionFunctions = false を設定します。

serverless.yml
provider:
  versionFunctions: false

DLQ

リトライが失敗した際の DLQ 送信先を設定します。

functions.<function name>.onError に SNS トピック ARN を設定します。resources で定義した SNS を組込関数(Ref, Fn::GetAtt など)で指定することもできそうです。

serverless.yml
functions:
  hello:
    handler: handler.hello
    onError: arn:aws:sns:us-east-1:XXXXXX:test

SQS のサポートは現状はないようなので、今後に期待。

KMS キー

環境変数を暗号化するための KMS キーを設定します。

service.awsKmsKeyArn, functions.<function name>.awsKmsKeyArn で指定することができ、functionsservice の値を継承または上書きします。

serverless.yml
service:
  awsKmsKeyArn: arn:aws:kms:us-east-1:XXXXXX:key/some-hash

provider:
  name: aws
  environment:
    TABLE_NAME: tableName1

functions:
  hello:
    handler: handler.hello
    awsKmsKeyArn: arn:aws:kms:us-east-1:XXXXXX:key/some-hash # キー上書き
    environment:
      TABLE_NAME: tableName2
  goodbye: # キー継承
    handler: handler.goodbye

X-Ray

X-Ray トレースを有効化します。

provider.tracing, functions.<function name>.tracing で設定します。functionsprovider の設定を継承または上書きします。

serverless.yml
provider:
  name: aws
  runtime: nodejs14.x
  tracing:
    lambda: true

functions:
  hello:
    handler: handler.hello
    tracing: Active
  goodbye:
    handler: handler.goodbye
    tracing: PassThrough

定義可能な値は Active, PassThrough です。

docs.aws.amazon.com

非同期呼出

送信先

非同期に実行された Lambda 関数の処理結果を他の Lambda や AWS サービスに送ることができます。ターゲットとして同じサービス内の Lambda 関数、外部の Lambda 関数、EventBridge イベントバス、SQS キュー、SNS トピックを設定できます。

functions.<function name>.destinationsonSuccess, onFailure に識別子※を設定します。※同じ serverless.yml 内で解決可能な関数名もしくは ARN。

ARN に CloudFormation 組込関数を利用する場合は明示的に type を指定する必要があります。

typesns, sqs, eventBus, function から選択します。

serverless.yml
functions:
  asyncHello:
    handler: handler.asyncHello
    destinations:
      onSuccess: otherFunctionInService # 同じサービス内の他 Lambda 関数(serverless 内で解決可能)
      onFailure: arn:aws:sns:us-east-1:xxxx:some-topic-name # SNS トピック ARN
  asyncGoodBye:
    handler: handler.asyncGoodBye
    destinations:
      onFailure:
        type: sns # ARN に CloudFormation 組込関数を利用する場合
        arn:
          Ref: SomeTopicName

ほぼ SAM と同じ書きぶりですね。

docs.aws.amazon.com

リトライ試行

リトライ期間とリトライ回数を設定します。

リトライ期間は functions.<function name>.maximumEventAge, リトライ回数は functions.<function name>.maximumRetryAttempts に設定します。

それぞれ、60 ~ 21600 秒(6時間)、0 ~ 2 (回)で設定できます。

serverless.yml
functions:
  asyncHello:
    handler: handler.asyncHello
    maximumEventAge: 7200
    maximumRetryAttempts: 1

maximumEventAgemaximumRetryAttempts は片方しか必要なくても両方定義する必要があるようです。

blog.serverworks.co.jp

エラーハンドリングについては以下の記事がとても参考になります。

dev.classmethod.jp

EFS

これは個人的にほとんど使わなそうなので軽く。。。というか EFS マウントできるんですね。恥ずかしながら初めて知りました(VPC Lambda やったことないからという言い訳をしておきます)。

functions.<function name>.fileSystemConfig に マウント先の localMountPath と EFS の ARN arn を指定します。

serverless.yml
functions:
  hello:
    handler: handler.hello
    fileSystemConfig:
      localMountPath: /mnt/example
      arn: arn:aws:elasticfilesystem:us-east-1:111111111111:access-point/fsap-0d0d0d0d0d0d0d0d0
    vpc:
      securityGroupIds:
        - securityGroupId1
      subnetIds:
        - subnetId1

エフェメラルストレージ

Lambda の /tmp サイズです。

functions.<function name>.ephemeralStorageSize に設定します。512 ~ 10240 MB で指定可能です。

functions:
  helloEphemeral:
    handler: handler.handler
    ephemeralStorageSize: 1024

Lambda Hashing Algorithm 移行

この章は現在 v3 を利用していて provider.lambdaHashingVersion = 20200924 を設定している人のためのマイグレーションガイドのようです。

provider.lambdaHashingVersion は Serverless Framework が関数のパッケージングに利用するハッシュアルゴリズムのバージョンですが、v3 では諸々の問題が修正され、初めから v3 を利用する人にとってはあまり関係がなさそうなので割愛します。

現在は非推奨ですね。

www.serverless.com

感想

2回にわたって Functions プロパティを見てきました。大体把握しましたが実際に書いてみないとなんともという感じですね。

そういえば、Lambda の基本的な設定であるメモリやタイムアウト設定はどこにあるんでしょうか。

ドキュメント一番上の functions 記載例の中にしれっと memorySize, timeout, provisionedConcurrency などの設定が入っていますね。全部が全部親切にドキュメンテーションされているわけではないので詰まったら都度ググるが正解ですかね。

ちなみに今回はドキュメントを読んでいきましたが、最新版をいち早く見るにはやはりリポジトリ直で見るのがよさそうです。ついこの間GAされた機能もサポートされていてスピード感がすごい。examples もとても充実していますね。

github.com

以上で Function の回を終わります。次回は API Gateway 設定か StepFunctions 周りを見ていきたいと思います。

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