AWS CLF 受かった(あたりまえ体操)

試験は1問〇点みたいなのじゃなくて、難易度毎に調整が入る方式だと思う(項目応答理論的な)。

なので単純計算はできないが、仮に単純計算したら、65問中5問も間違えてる計算。

何が駄目だったんだろう。昔はなかった持続可能性周りの範囲でやらかしたのかな。

とりま、SAA目指して頑張りますん(5年くらい前に1回取ったけどとっくの昔に有効期限切れてる)。

Import XXX could not be resolved from sourcePylance

書きたいネタはあるが整理する時間がないので今直面した小ネタを。

VSCode から WSL 2 につないでる状態で、python ライブラリをうまく検知できてなくてワーニングが出た。

これで解決。

maasaablog.com

$ python -c "import site; print (site.getsitepackages())"
['/home/talkeyboid/anaconda3/lib/python3.9/site-packages']

使う予定ないのに 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 の確認もしようと思ったのですが、結構時間がかかったのでまた次回。