Quantcast
Channel: kakakakakku blog
Viewing all 902 articles
Browse latest View live

LocalStack で AWS サポートへの問い合わせを API で操作できる「AWS Support API」を試そう

$
0
0

AWS サポートへの問い合わせを API で操作できる「AWS Support API」は気になるけどビジネスプラン以上じゃないと使えなくて気軽に試せないじゃーん😇と悩んでいる人がいたら LocalStack をおすすめしたい❗️実は LocalStack(無料版)で一部の AWS Support API がサポートされている💡

(あくまで練習として)問い合わせし放題だぁぁぁ〜 \( 'ω')/

docs.aws.amazon.com

現在「5種類」の API がサポートされている📝

docs.localstack.cloud

LocalStack で AWS Support API を試す💪

今回は LocalStack AWS CLI(awslocalコマンド)で試す.ちなみに東京リージョンで AWS Support API を実行する場合はバージニア北部リージョン (us-east-1) を指定する必要がある点は注意すること🚨

docs.aws.amazon.com

👾 support describe-cases

awscli.amazonaws.com

最初は問い合わせ0件になっている👌

$ awslocal support describe-cases --region us-east-1 | jq .
{"cases": []}

👾 support create-case

awscli.amazonaws.com

問い合わせできた👌

$ awslocal support create-case --region us-east-1 \--subject'[TEST CASE-Please ignore] LocalStack Support API テスト1'\--communication-body'東京リージョンの EC2(i-***)について質問です。'\--language ja | jq .
{"caseId": "case-12345678910-2020-4hJ2i1J38FbKAeh2"}

$ awslocal support create-case --region us-east-1 \--subject'[TEST CASE-Please ignore] LocalStack Support API テスト2'\--communication-body'東京リージョンの EC2(i-***)について質問です。'\--language ja | jq .
{"caseId": "case-12345678910-2020-KMmj62hbE43e578a"}

$ awslocal support describe-cases --region us-east-1 | jq .
{"cases": [{"caseId": "case-12345678910-2020-4hJ2i1J38FbKAeh2",
      "displayId": "foo_display_id",
      "subject": "[TEST CASE-Please ignore] LocalStack Support API テスト1",
      "status": "pending-customer-action",
      "submittedBy": "moto@moto.com",
      "timeCreated": "2024-09-27T05:25:30.764608",
      "recentCommunications": {"communications": [{"caseId": "case-12345678910-2020-4hJ2i1J38FbKAeh2",
            "body": "東京リージョンの EC2(i-***)について質問です。",
            "submittedBy": "moto@moto.com",
            "timeCreated": "2024-09-27T05:25:30.764612",
            "attachmentSet": [{"fileName": "support_file.txt"}]}],
        "nextToken": "foo_next_token"},
      "language": "ja"},
    {"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
      "displayId": "foo_display_id",
      "subject": "[TEST CASE-Please ignore] LocalStack Support API テスト2",
      "status": "pending-customer-action",
      "submittedBy": "moto@moto.com",
      "timeCreated": "2024-09-27T05:25:52.411090",
      "recentCommunications": {"communications": [{"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
            "body": "東京リージョンの EC2(i-***)について質問です。",
            "submittedBy": "moto@moto.com",
            "timeCreated": "2024-09-27T05:25:52.411096",
            "attachmentSet": [{"fileName": "support_file.txt"}]}],
        "nextToken": "foo_next_token"},
      "language": "ja"}]}

ちなみにドキュメントに以下のように書いてあって,今回は subject[TEST CASE-Please ignore]と付けてある📝

If you call the CreateCase operation to create test support cases, then we recommend that you include a subject line, such as TEST CASE-Please ignore. After you're done with your test support case, call the ResolveCase operation to resolve it.

👾 support resolve-case

awscli.amazonaws.com

問い合わせを解決できた👌

$ awslocal support resolve-case --region us-east-1 \--case-id case-12345678910-2020-4hJ2i1J38FbKAeh2 | jq .
{"initialCaseStatus": "reopened",
  "finalCaseStatus": "resolved"}

$ awslocal support describe-cases --region us-east-1 | jq .
{"cases": [{"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
      "displayId": "foo_display_id",
      "subject": "[TEST CASE-Please ignore] LocalStack Support API テスト2",
      "status": "reopened",
      "submittedBy": "moto@moto.com",
      "timeCreated": "2024-09-27T05:25:52.411090",
      "recentCommunications": {"communications": [{"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
            "body": "東京リージョンの EC2(i-***)について質問です。",
            "submittedBy": "moto@moto.com",
            "timeCreated": "2024-09-27T05:25:52.411096",
            "attachmentSet": [{"fileName": "support_file.txt"}]}],
        "nextToken": "foo_next_token"},
      "language": "ja"}]}

$ awslocal support describe-cases --region us-east-1 \--include-resolved-cases | jq .
{"cases": [{"caseId": "case-12345678910-2020-4hJ2i1J38FbKAeh2",
      "displayId": "foo_display_id",
      "subject": "[TEST CASE-Please ignore] LocalStack Support API テスト1",
      "status": "unassigned",
      "submittedBy": "moto@moto.com",
      "timeCreated": "2024-09-27T05:25:30.764608",
      "recentCommunications": {"communications": [{"caseId": "case-12345678910-2020-4hJ2i1J38FbKAeh2",
            "body": "東京リージョンの EC2(i-***)について質問です。",
            "submittedBy": "moto@moto.com",
            "timeCreated": "2024-09-27T05:25:30.764612",
            "attachmentSet": [{"fileName": "support_file.txt"}]}],
        "nextToken": "foo_next_token"},
      "language": "ja"},
    {"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
      "displayId": "foo_display_id",
      "subject": "[TEST CASE-Please ignore] LocalStack Support API テスト2",
      "status": "resolved",
      "submittedBy": "moto@moto.com",
      "timeCreated": "2024-09-27T05:25:52.411090",
      "recentCommunications": {"communications": [{"caseId": "case-12345678910-2020-KMmj62hbE43e578a",
            "body": "東京リージョンの EC2(i-***)について質問です。",
            "submittedBy": "moto@moto.com",
            "timeCreated": "2024-09-27T05:25:52.411096",
            "attachmentSet": [{"fileName": "support_file.txt"}]}],
        "nextToken": "foo_next_token"},
      "language": "ja"}]}

👾 support describe-services

未実装🙅‍♂️

$ awslocal support describe-services --region us-east-1
An error occurred (InternalFailure) when calling the DescribeServices operation: The describe_services action has not been implemented

👾 describe-severity-levels

未実装🙅‍♂️

$ awslocal support describe-severity-levels --region us-east-1
An error occurred (InternalFailure) when calling the DescribeSeverityLevels operation: The describe_severity_levels action has not been implemented

まとめ

LocalStack(無料版)で AWS Support API を試せるよ❗️という紹介でした \( 'ω')/

実際に問い合わせるときはガイドラインも確認しましょう〜📝

aws.amazon.com


AWS Step Functions でエラーハンドリングを実装する構成例

$
0
0

AWS Step Functions でタスクがエラーになったときに統一的なエラーハンドリング(エラー処理・リカバリ処理・通知処理など)が必要になることがある💡エラーハンドリングを実現する構成例をいくつか考えてみた👍

もちろん最終的には要件次第ではあって絶対にコレ❗️という答えはないと思う \( 'ω')/

案1: タスクごとに Catch する

まず最初に思い付くのはタスクごとに Catch する案だと思う.例えば以下のように AWS Lambda 関数(特に意味はなく Yay! という名前にした✌)を順番に3回呼び出す場合にタスクごとに Catch を実装して,エラーハンドリング用の処理 CustomErrorHandlerを実行するイメージ👌

案1: ワークフロー例

実行すると期待通りになる❗️タスクごとに異なるエラーハンドリングを実装できて自由度は高いけど,統一的なエラーハンドリングを前提にすると,すべてのタスクに Catch を実装する必要があって AWS Step Functions ワークフローの複雑さは課題になると思う.

案1: 実行結果

案2: Parallel を使ってタスクをグループ化する

AWS Step Functions には複数のタスクをまとめてグループ化する直接的な機能はないけど,タスクを並列に実行するときに使う Parallelは,実はブランチ(レーン)は1以上で使えるという仕様になっている👌

docs.aws.amazon.com

よって Parallel で片側にタスクをまとめると「グループ化」のように実装できるという Tips がある💡

案2: ワークフロー例

実行すると期待通りになる❗️グループ化のために Parallel を使うのはちょっと裏技感があるけど,統一的なエラーハンドリングで十分な場合は AWS Step Functions ワークフローがシンプルになって良いと思う.

案2: 実行結果

案3: AWS Step Functions ワークフローをネストする

AWS Step Functions の「サービス統合」を使うと AWS Step Functions ワークフローをネストして,別の AWS Step Functions ワークフローを呼び出せる.

docs.aws.amazon.com

複数のタスクを AWS Step Functions ワークフロー(子ワークフロー)にまとめて「グループ化」のようにして,子ワークフローを呼び出す AWS Step Functions ワークフロー(親ワークフロー)ではエラーハンドリングを実装する👌

案3: 子ワークフロー例

案3: 親ワークフロー例

実行すると期待通りになる❗️エラーハンドリングのために管理する AWS Step Functions ワークフローが増えてしまうのはデメリットだけど,AWS Step Functions ワークフロー全体がさらに複雑化する予定があったりするなら拡張しやすいと思う.

案3: 実行結果

案4: その他

エラーハンドリングを AWS Step Functions ワークフローから独立させて実現する案を2つ考えてみた📝

Amazon EventBridge を使う

Amazon EventBridge で Step Functions Execution Status ChangeABORTED / TIMED_OUT / FAILEDをトリガーして AWS Lambda 関数などのエラーハンドリングを実行できる💡

{"source": ["aws.states"
    ],
    "detail-type": ["Step Functions Execution Status Change"
    ],
    "detail": {"status": ["ABORTED",
            "TIMED_OUT",
            "FAILED"
        ],
        "stateMachineArn": ["arn:aws:states:ap-northeast-1:000000000000:stateMachine:xxxxx"
        ]}}

docs.aws.amazon.com

Amazon CloudWatch Alarm を使う

AWS Step Functions ワークフローのメトリクス ExecutionsAborted / ExecutionsTimedOut / ExecutionsFailedを Amazon CloudWatch Alarm でトリガーして Amazon SNS トピック経由で AWS Lambda 関数などのエラーハンドリングを実行できる💡

docs.aws.amazon.com

ちなみに最近では AWS Lambda 関数を直接実行することもできる👌

aws.amazon.com

まとめ

適材適所に考えよう〜 \( 'ω')/

Python の mimetypes.guess_type() で .geojson を変換できるようにする

$
0
0

Python 標準ライブラリ mimetypesを使って GeoJSON ファイル名 (.geojson) から MIME タイプに変換しようとしたら (None, None)になってしまった😇

>>> import mimetypes
>>> mimetypes.guess_type('example.geojson')
(None, None)

docs.python.org

前提条件

  • Python 3.12

mimetypes.types_mapを確認する

MIME タイプのマッピングは環境によって異なる場合があって,今回は macOS (Sonoma) と AWS Lambda 関数 (Python 3.12) で mimetypes.types_mapの値から JSON 関連のマッピングを確認してみた.やはり .geojsonのマッピングはなかった🥲

macOS (Sonoma)

{'.json': 'application/json', '.jsonml': 'application/jsonml+json'}

AWS Lambda 関数 (Python 3.12)

{'.json': 'application/json'}

mimetypes.add_type()で追加する

mimetypes.add_type()でマッピングを追加すれば OK👌

>>> mimetypes.add_type('application/geo+json', '.geojson')
>>> mimetypes.guess_type('example.geojson')
('application/geo+json', None)

knownfiles を確認する

MIME タイプのマッピング (knownfiles) は mimetypes.knownfilesで確認できる.

>>> mimetypes.knownfiles
['/etc/mime.types', '/etc/httpd/mime.types', '/etc/httpd/conf/mime.types', '/etc/apache/mime.types', '/etc/apache2/mime.types', '/usr/local/etc/httpd/conf/mime.types', '/usr/local/lib/netscape/mime.types', '/usr/local/etc/httpd/conf/mime.types', '/usr/local/etc/mime.types']

github.com

macOS だと /etc/apache2/mime.typesに knownfiles があって,JSON 関連のマッピングを確認してみた.

mimetypes.types_mapの値と同じで application/jsonapplication/jsonml+jsonはサポートされていて,application/geo+jsonはコメントアウトになっていた💡

$ grep-i json /etc/apache2/mime.types
# application/alto-costmap+json# application/alto-costmapfilter+json# application/alto-directory+json# application/alto-endpointcost+json# application/alto-endpointcostparams+json# application/alto-endpointprop+json# application/alto-endpointpropparams+json# application/alto-error+json# application/alto-networkmap+json# application/alto-networkmapfilter+json# application/calendar+json# application/coap-group+json# application/csvm+json# application/geo+json# application/jose+json# application/jrd+json
application/json                json
# application/json-patch+json# application/json-seq
application/jsonml+json             jsonml
# application/jwk+json# application/jwk-set+json# application/ld+json# application/merge-patch+json# application/ppsp-tracker+json# application/problem+json# application/rdap+json# application/reputon+json# application/scim+json# application/vcard+json# application/vnd.apache.thrift.json# application/vnd.api+json# application/vnd.bekitzur-stech+json# application/vnd.collection+json# application/vnd.collection.doc+json# application/vnd.collection.next+json# application/vnd.coreos.ignition+json# application/vnd.document+json# application/vnd.drive+json# application/vnd.geo+json# application/vnd.hal+json# application/vnd.heroku+json# application/vnd.hyperdrive+json# application/vnd.ims.lis.v2.result+json# application/vnd.ims.lti.v2.toolconsumerprofile+json# application/vnd.ims.lti.v2.toolproxy+json# application/vnd.ims.lti.v2.toolproxy.id+json# application/vnd.ims.lti.v2.toolsettings+json# application/vnd.ims.lti.v2.toolsettings.simple+json# application/vnd.mason+json# application/vnd.micro+json# application/vnd.miele+json# application/vnd.oftn.l10n+json# application/vnd.oma.lwm2m+json# application/vnd.oracle.resource+json# application/vnd.pagerduty+json# application/vnd.siren+json# application/vnd.vel+json# application/vnd.xacml+json# model/gltf+json

Python 3.13 だと

ちなみに Python 3.13 のドキュメントを読むと mimetypes.guess_type()soft deprecated(警告は出ずに引き続き使える非推奨)になっていて,今後は mimetypes.guess_file_type()を使うことになりそう📝 覚えておこう〜

Deprecated since version 3.13: Passing a file path instead of URL is soft deprecated. Use guess_file_type() for this.

>>> import mimetypes
>>> mimetypes.guess_file_type('example.json')
('application/json', None)

docs.python.org

参考サイト

www.iana.org

redocly lint コマンドを GitHub Actions に組み込んで OpenAPI の静的解析を実行する

$
0
0

Redocly CLIの lint 機能 (redocly lintコマンド) を使うと OpenAPI の静的解析ができる👌

今回は redocly lintコマンドを GitHub Actions に組み込んで OpenAPI の静的解析を実行できるようにしてみた \( 'ω')/

redocly.com

OpenAPI サンプル

今回は Petstore (OpenAPI 3.0)を Swagger サイトからダウンロードして使う🐶🐱

petstore3.swagger.io

Redocly CLI

まず redocly lintコマンドを実行するとエラー(10件)と警告(13件)が出てくる😇

$ redocly lint openapi.json
No configurations were provided -- using built in recommended configuration by default.

validating openapi.json...

(中略)

❌ Validation failed with 10 errors and 13 warnings.
run `redocly lint --generate-ignore-file` to add all problems to the ignore file.

以下のドキュメントを参考に --skip-ruleオプションを使って不要なビルトインルールを除外できる👌

redocly.com

すると警告(3件)まで減った❗️ちなみに redocly lintコマンドはエラーがなければ validと表示される.

$ redocly lint openapi.json \--skip-rule security-defined \--skip-rule operation-2xx-response \--skip-rule operation-4xx-response
No configurations were provided -- using built in recommended configuration by default.

validating openapi.json...
[1] openapi.json:359:7 at #/components/schemas/Customer

Component: "Customer" is never used.

357 |   "xml": {"name": "order"}358 | },
359 | "Customer": {360|"type": "object",
361|"properties": {

Warning was generated by the no-unused-components rule.


[2] openapi.json:414:7 at #/components/requestBodies/Pet

Component: "Pet" is never used.

412|},
413|"requestBodies": {414|"Pet": {415|"description": "Pet object that needs to be added to the store",
416|"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Pet"}}, "application/xml": {"schema": ...<24 chars>

Warning was generated by the no-unused-components rule.


[3] openapi.json:418:7 at #/components/requestBodies/UserArray

Component: "UserArray" is never used.

416|"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Pet"}}, "application/xml": {"schema": ...<24 chars>417|},
418|"UserArray": {"description": "List of user object", "content": {"application/json": {"schema": {"type": "array"...<35 chars>419|},
420|"securitySchemes": {

Warning was generated by the no-unused-components rule.


openapi.json: validated in 23ms

Woohoo! Your API description is valid. 🎉
You have 3 warnings.

さらに --generate-ignore-fileオプションを使うと具体的なエラー・警告レベルで除外するための .redocly.lint-ignore.yamlファイルを自動生成してくれる👌

$ redocly lint openapi.json \--skip-rule security-defined \--skip-rule operation-2xx-response \--skip-rule operation-4xx-response \--generate-ignore-file
No configurations were provided -- using built in recommended configuration by default.

validating openapi.json...
openapi.json: validated in 15ms

Generated ignore file with 3 problems.

.redocly.lint-ignore.yamlは以下のようになっていた📝

# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.# See https://redoc.ly/docs/cli/ for more information.openapi.json:no-unused-components:- '#/components/schemas/Customer'- '#/components/requestBodies/Pet'- '#/components/requestBodies/UserArray'

最終的にエラーも警告もなくなった🎉

$ redocly lint openapi.json \--skip-rule security-defined \--skip-rule operation-2xx-response \--skip-rule operation-4xx-response 
No configurations were provided -- using built in recommended configuration by default.

validating openapi.json...
openapi.json: validated in 15ms

Woohoo! Your API description is valid. 🎉
3 problems are explicitly ignored.

GitHub Actions

次に redocly lintコマンドを GitHub Actions に組み込む.公式に提供されているアクションはなさそうだったから,シンプルに npxで実行することにした.また --format github-actionsオプションを指定すると,GitHub Actions のワークフローコマンドとしてエラーと警告を出力できるため,プルリクエストに自動的にコメントできるようになる❗️

docs.github.com

name: Lint OpenAPI

on:push:branches:- main
  pull_request:branches:- main

jobs:lint:runs-on: ubuntu-latest
    steps:- uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:node-version:20- name: Lint OpenAPI
        run: |
          npx @redocly/cli@latest lint openapi.json \
            --skip-rule security-defined \
            --skip-rule operation-2xx-response \
            --skip-rule operation-4xx-response \
            --format github-actions

プルリクエストを作る

意図的に Path Parameter を削除して,ビルトインルール path-parameters-definedに該当するようにした💣️

redocly.com

プルリクエストを作ると GitHub Actions から自動的にコメントが入った👌便利〜

Terraform で Service Quotas のクォータリクエストを管理する

$
0
0

特にプロダクション環境でウェブサービスを稼働させるときにトラフィックの多さから AWS サービスのクォータ(サービス制限)に該当する可能性がある🛑 AWS Well-Architected Framework (Reliability Pillar) でも REL01-BP01 ~ REL01-BP06にクォータ関連のベストプラクティスが紹介されている.クォータを理解しつつ,Service Quotas を使ってクォータを管理・モニタリングすることが重要になる👌

docs.aws.amazon.com

クォータリクエストを自動化する

Service Quotas は API / AWS CLI / AWS SDK でサポートされている❗️よって,AWS SDK For Go に依存している Terraform AWS Provider の aws_servicequotas_service_quotaリソースを使えば Service Quotas のクォータ増加リクエストを管理できる(命名規則とは言え aws_servicequotas_service_quotaはスゴイなぁーw ).

👾 quotas.tf

今回はサンプルとして AWS Lambda 関数のクォータ「同時実行数」をデフォルトの 1000から 1500に変更するクォータリクエストを追加する.

# AWS Lambda: Concurrent executionsresource"aws_servicequotas_service_quota""lambda_concurrent_executions"{service_code = "lambda"quota_code   = "L-B99A9384"value        = 1500}

Before

After

実行するとクォータリクエストが「保留中」「リクエスト済み」となる.同時にサポートケースも作られて Amazon Web Services: You have opened a new Support case: 000000000000000というタイトルのメールが届く✉️

AWS CloudFormation 未サポート

少し意外ではあるけど,AWS CloudFormation (と AWS CDK) ではまだ Service Quotas がサポートされてなく,クォータを管理しにくいという課題がある.CloudFormation Public Coverage Roadmap には issue が起票されているけど,特にサポートされる流れではなさそうでツライ😇

github.com

Quota Monitor for AWS

関連するソリューションとして「Quota Monitor for AWS」もある.あくまで個人的にはソリューションは導入したくなく,マネージドサービスとして提供してくれれば使うのに〜という感じではあるけど,運用面まで考慮してニーズに合えば導入してみると良いのかなと思う👌

aws.amazon.com

GitHub Actions のジョブサマリーに Service Quotas のクォータ一覧を出力する

$
0
0

AWS Organizations 未導入のマルチアカウント環境でマネジメントコンソールに入らずに Service Quotas のクォータをシュッと確認できたらちょっと便利かも?と思って(あくまでアイデアとして),検証も兼ねて AWS SDK for Python (Boto3) と GitHub Actions でマルチアカウントのクォータ一覧を出力する仕組みを作ってみた💪

もちろん AWS Well-Architected Framework (Reliability Pillar) の REL01-BP05Tracking the quotas and usage in spreadsheets.というアンチパターンも紹介されていて,クォータを一覧するだけでは微妙なのは理解しつつも,クォータを IaC (Infrastructure as Code) で管理できてなく,マネジメントコンソールでクォータリクエストを送っているなら,クォータを把握しやすくて意外と便利という可能性もあるかな〜と💡

docs.aws.amazon.com

そもそも AWS CloudFormation (と AWS CDK) で Service Quotas をサポートしてくれれば IaC で管理しやすくなって,今回検証した仕組みも不要になるんだけどな〜とも思うから頼む🙏

github.com

👾 main.py

AWS SDK for Python (Boto3) で Service Quotas のクォータを取得する場合は list_service_quotas()関数(サービスコード指定)と get_service_quota()関数(サービスコードとクォータコード指定)がある.今回はどちらも試してみたく,サンプルとして AWS Lambda はサービスコード lambdaを指定して,AWS CloudFormation はサービスコード cloudformationとクォータコード L-0485CB21 (Stack count)を指定した.

boto3.amazonaws.com

boto3.amazonaws.com

そして python-tabulateを使って Markdown の表形式でクォータ一覧を出力できるようにした📈

github.com

import boto3
from tabulate import tabulate

applied_quotas = []


defmain():
    client = boto3.client('service-quotas', region_name='ap-northeast-1')

    append_quotas(client.list_service_quotas(ServiceCode='lambda'))
    append_quotas(client.get_service_quota(ServiceCode='cloudformation', QuotaCode='L-0485CB21'))

    print(tabulate(applied_quotas, headers=headers(), tablefmt='github'))


defheaders():
    return [
        'ServiceName',
        'QuotaName',
        'QuotaCode',
        'Value',
    ]


defappend_quotas(response):
    if'Quota'in response:
        # for get_service_quota()
        quotas = [response['Quota']]
    elif'Quotas'in response:
        # for list_service_quotas()
        quotas = response['Quotas']
    else:
        quotas = []

    for quota in quotas:
        applied_quotas.append(
            [
                quota['ServiceName'],
                quota['QuotaName'],
                quota['QuotaCode'],
                quota['Value'],
            ]
        )


if __name__ == '__main__':
    main()

👾 .github/workflows/quotas.yml

GitHub Actions ワークフローは以下のようにした.流れとしては AWS アカウント1で main.pyを実行して,AWS アカウント2で main.pyを実行している👌今回は Python プロジェクトを uvで管理しているため, setup-uvを使っているけど,普通に setup-pythonでも OK👌

そして ${GITHUB_STEP_SUMMARY}に出力結果を流し込むと GitHub Actions にジョブサマリーとしてレポート化できる❗️

docs.github.com

name: List Quotas

on:workflow_dispatch:permissions:id-token: write
  contents: read

jobs:list-quotas:runs-on: ubuntu-latest
    steps:- uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - name: Setup Python
        run: uv python install
      - name: Install dependencies
        run: uv sync
      - name: Set up AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:role-to-assume: ${{ secrets.ACCOUNT1_AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
      - name: Run
        run: |
            echo "## Account: $(aws sts get-caller-identity | jq -r .Account) 🐶">> ${GITHUB_STEP_SUMMARY}
            uv run main.py >> ${GITHUB_STEP_SUMMARY}
      - name: Set up AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:role-to-assume: ${{ secrets.ACCOUNT2_AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
      - name: Run
        run: |
            echo "## Account: $(aws sts get-caller-identity | jq -r .Account) 🐱">> ${GITHUB_STEP_SUMMARY}
            uv run main.py >> ${GITHUB_STEP_SUMMARY}

実行結果

個人用の AWS アカウント2つを使って GitHub Actions ワークフローを実行してみた.1つのアカウントは以下のブログ記事を書くときに AWS Lambda 関数のクォータ「同時実行数 (L-B99A9384)」を 1500 に変更していて,アカウント間の違いを確認できた👌

kakakakakku.hatenablog.com

まとめ

クォータ管理のベストプラクティスとは異なるにしろ,アイデアの検証結果としてはイメージ通りにできて良かった👏

LangChain / LangGraph を活用した RAG そして AI エージェントを体験しながら学べる新著「LangChain と LangGraph による RAG・AI エージェント[実践]入門」を読んだ

$
0
0

2024年11月9日に出版される新著「LangChain と LangGraph による RAG・AI エージェント[実践]入門」を読んだ📕

RAG そして AI エージェントとは!?という疑問に対して理解を深めることができて,実際に Python コードを実行しながら体験もできる.さらに最近の論文解説すらもあって,一石二鳥🐓いや一石三鳥って言えるほどにたくさん学べる素晴らしい一冊だった❗️

他には LLM アプリケーションを実装する前の基礎知識を整理できたり,RAG / Advanced RAG・LangChain・LangSmith の解説なども充実している.本書を手に取ったときは 496 ページもあって厚さに驚いたけど,納得のボリューム感だった.

もし「普段 ChatGPT などに質問することはあるけど自分で LLM アプリケーションを実装するなんて無理〜🙅‍♂️」と感じていたり,「LangChain という名前はちょっと聞いたことがあるけど LangGraph ってなんだろう?」「そもそも AI エージェントって何?」って思う人に特におすすめできる👌

本書は著者の一人 id:yoshidashingoに送っていただきました📕

去年に続き出版おめでとうございます \( 'ω')/

📚 目次

どの章も勉強になったけど,個人的には AI エージェントに関連する開発をしたことがなく,AI エージェント関連の「第8章〜第12章」を重点的に読んで体験してみて本当に良かった❗️

  • 第1章: LLM アプリケーション開発の基礎
  • 第2章: OpenAI のチャット API の基礎
  • 第3章: プロンプトエンジニアリング
  • 第4章: LangChain の基礎
  • 第5章: LangChain Expression Language(LCEL)徹底解説
  • 第6章: Advanced RAG
  • 第7章: LangSmith を使った RAG アプリケーションの評価
  • 第8章: AI エージェントとは
  • 第9章: LangGraph で作る AI エージェント実践入門
  • 第10章: 要件定義書生成 AI エージェントの開発
  • 第11章: エージェントデザインパターン
  • 第12章: LangChain/LangGraph で実装するエージェントデザインパターン

👇️詳細な目次は以下にある

gihyo.jp

本書に出てくるコード類は以下の GitHub リポジトリに公開されている👾

github.com

ちなみに本書の前半部分は,2023年10月18日に出版された「ChatGPT/LangChain によるチャットシステム構築[実践]入門」の大型アップデートという位置付けになっていて,一部は「このあたりは去年読んだ気がするな〜」と思い出しつつキャッチアップできたのも良かった😀

kakakakakku.hatenablog.com

📚 読書ログ

本書全体をザーッと読みつつ,気になったところ(第6章・第9章・第10章・第12章の一部)は実際に Google Colab で体験しながら「へぇ〜」「この実装はなんだろう〜」「なるほどな〜」とブツブツ言いながら進めていた(笑)全体の読書ログを載せると多すぎるから今回は AI エージェント関連の章に興味を持ってもらえるように紹介したいと思う❗️

第8章: AI エージェントとは

第8章には AI エージェントの解説(概念・基礎研究・フレームワーク・ツールなど)が載っていて,理解を深めつつ,トレンドを知ることができる.そして第9章以降の体験も進めていくと AI エージェントを理解できたな〜と感じられるけど,それでも改めて「AI エージェントってなんだろう」と感じることもあると思う(実際に僕自身があった).例えば AWS の用語集的なサイトにも解説が載っていて,複数の情報を読み比べしてみるのも良いと思う👌

aws.amazon.com

他にも「その仕事、AI エージェントがやっておきました。」という書籍も2023年12月に出版されていて,読んでみたいな〜と思ったりもした💡

第9章: LangGraph で作る AI エージェント実践入門

第9章からは LLM を活用したワークフローを構築できるライブラリ「LangGraph」を使って「Q&A アプリケーション」を体験する.Google Colab を使ってノートブックを実行すれば良く,特にハマるポイントもなかった👏(ある程度は Python に慣れておくとコードまで理解しやすいとは思う)

www.langchain.com

以下に 生成AIについて教えてくださいと質問した実行結果を載せておく📝

結果 (messages) を見ると ChatGPT に質問したのと同じでは?と感じてしまうけど,内部構造に着目すると質問内容を考慮して「生成AI製品エキスパート」から回答が返されていて (currect_role),さらに回答に対する品質チェック (judgement_reason) をしていることもわかってスゴイ❗️以下にテキストも載せておく.

{'query': '生成AIについて教えてください',
 'current_role': '生成AI製品エキスパート',
 'messages': ['生成AI製品エキスパートとしてお答えします。\n\n生成AIとは、人工知能の一分野であり、テキスト、画像、音声、動画などのコンテンツを自動的に生成する技術を指します。これには、自然言語処理(NLP)、コンピュータビジョン、音声合成などの技術が含まれます。生成AIは、ディープラーニングを基盤としており、大量のデータを学習することで、新しいコンテンツを創り出す能力を持っています。\n\n代表的な生成AIのモデルには、OpenAIのGPTシリーズやDALL-E、GoogleのBERT、DeepMindのWaveNetなどがあります。これらのモデルは、文章の自動生成、画像の生成、音声の合成など、さまざまな応用が可能です。\n\n生成AIの利点としては、クリエイティブな作業の効率化や新しいアイデアの創出が挙げられます。一方で、フェイクニュースの生成や著作権の問題など、倫理的な課題も存在します。生成AIの技術は急速に進化しており、今後も多くの分野での応用が期待されています。'],
 'current_judge': True,
 'judgement_reason': '回答は生成AIについての基本的な情報を網羅しており、技術の概要、代表的なモデル、利点、そして倫理的な課題についても触れています。内容は正確であり、質問に対して適切に答えています。'}

そして,個人的な追加課題としてカレーのレシピを教えてくださいと質問した実行結果を載せておく📝

今度は「一般知識エキスパート」から回答が返されていた❗️

{'query': 'カレーのレシピを教えてください',
 'current_role': '一般知識エキスパート',
 'messages': ['カレーのレシピをお教えします。ここでは、基本的なチキンカレーのレシピを紹介しますが、好みに応じて具材やスパイスを調整してください。\n\n**材料:**\n- 鶏もも肉: 500g(一口大に切る)\n- 玉ねぎ: 2個(みじん切り)\n- にんにく: 2片(みじん切り)\n- 生姜: 1片(みじん切り)\n- トマト: 2個(みじん切り)\n- カレー粉: 大さじ2\n- クミンパウダー: 小さじ1\n- コリアンダーパウダー: 小さじ1\n- ターメリックパウダー: 小さじ1/2\n- ガラムマサラ: 小さじ1\n- 塩: 適量\n- コショウ: 適量\n- ヨーグルト: 大さじ2\n- ココナッツミルクまたは水: 200ml\n- サラダ油またはギー: 大さじ2\n- フレッシュコリアンダー: 適量(飾り用)\n\n**作り方:**\n\n1. フライパンにサラダ油またはギーを熱し、玉ねぎを透明になるまで炒めます。\n2. にんにくと生姜を加え、香りが立つまで炒めます。\n3. トマトを加え、柔らかくなるまで炒めます。\n4. カレー粉、クミンパウダー、コリアンダーパウダー、ターメリックパウダーを加え、スパイスの香りが立つまで炒めます。\n5. 鶏肉を加え、全体にスパイスが絡むように炒めます。\n6. ヨーグルトを加え、さらに炒めます。\n7. ココナッツミルクまたは水を加え、沸騰させます。火を弱め、蓋をして15-20分煮込みます。\n8. 塩とコショウで味を調え、最後にガラムマサラを加えます。\n9. 器に盛り付け、フレッシュコリアンダーを飾って完成です。\n\nこのレシピは基本的なものですが、好みに応じて野菜や他のスパイスを追加してアレンジすることもできます。お楽しみください!'],
 'current_judge': True,
 'judgement_reason': '回答はユーザーの質問に対して適切で、詳細なチキンカレーのレシピを提供しています。材料と作り方が明確に記載されており、ユーザーがレシピを実行するのに十分な情報が含まれています。また、好みに応じてアレンジする提案もあり、柔軟性があります。'}

LangGraph でグラフ構造を出力した画像も載せておく.

さらに第7章で詳しく学んだ「LangSmith」を組み込んで内部構造のトレースも設定した❗️

www.langchain.com

以下のように AI エージェントの内部構造を LangSmith でも確認できた👌楽しすぎる \( 'ω')/

第10章: 要件定義書生成 AI エージェントの開発

第10章ではさらに実践的な「要件定義書生成 AI エージェント」を体験する.正直最初は「真面目なテーマだ...😅」と思ってしまったけど(笑)実際に取り組んでみると AI エージェントの内部構造の深さに驚かされた🔥

同じく Google Colab を使って スマートフォン向けの健康管理アプリを開発したいというコマンドを実行する.以下に実行結果を載せておく📝

#健康管理アプリ要件文書

## 1. プロジェクト概要
本プロジェクトは、スマートフォン向けの健康管理アプリを開発することを目的としています。このアプリは、ユーザーが日々の健康状態を効果的に管理し、ストレス、食事、運動、そして全体的な健康維持をサポートすることを目指します。ターゲットユーザーは、ストレス管理に関心のある中小企業経営者、食事と運動に関心のあるITエンジニア、ウォーキングを日課とする退職者、家族全員の健康管理を行う専業主婦、そして運動データの分析に興味がある大学生です。

## 2. 主要機能
1. **ストレス管理機能**
   -ストレスレベルのトラッキング(心拍数、睡眠パターンのモニタリング)
   -リラクゼーションエクササイズ(呼吸法、瞑想ガイド)
   -通知機能(休憩促進、ストレスリマインダー)
   -日記機能(感情や出来事の記録)
   -目標設定と進捗管理
   -コミュニティサポート

2. **食事管理機能**
   -栄養素の詳細分析(カロリー、タンパク質、脂質、炭水化物、ビタミン、ミネラル)
   -食事の写真認識による自動入力

3. **運動管理機能**
   -多様な運動の記録(ランニング、ウォーキング、ヨガ、筋トレ)
   -リアルタイムのフィードバック(心拍数、消費カロリー)
   -パーソナライズされたアドバイス(AIによる食事と運動の提案)
   -進捗の可視化(グラフやチャート)

4. **ウォーキングサポート機能**
   -歩数計、距離と時間の記録、カロリー計算
   -地図機能(歩いたルートの確認)
   -簡単な健康アドバイス

5. **家族管理機能**
   -家族全員の健康データの一元管理
   -簡易入力機能(食事の写真認識)

6. **運動データ分析機能**
   -リアルタイムのパフォーマンス分析
   -トレーニング効果の評価
   -フォームの分析
   -疲労と回復のモニタリング
   -パーソナライズされたトレーニングプラン

## 3. 非機能要件
- **ユーザビリティ**: シンプルで直感的なインターフェース、音声ガイド、チュートリアルの提供
- **パフォーマンス**: リアルタイムでのデータ処理とフィードバック
- **セキュリティ**: ユーザーデータの安全な保存とプライバシー保護
- **互換性**: iOSおよびAndroidプラットフォームでの動作

## 4. 制約条件
-開発期間は6ヶ月以内
-予算は500万円以内
-プライバシー規制(GDPR、個人情報保護法)への準拠

## 5. ターゲットユーザー
-中小企業経営者(ストレス管理に関心)
- ITエンジニア(食事と運動に関心)
-退職者(ウォーキングを日課)
-専業主婦(家族の健康管理)
-大学生(運動データの分析に興味)

## 6. 優先順位
1.ストレス管理機能
2.食事管理機能
3.運動管理機能
4.ウォーキングサポート機能
5.家族管理機能
6.運動データ分析機能

## 7. リスクと軽減策
- **技術的リスク**: 新技術の導入による開発遅延
  -軽減策: 技術調査とプロトタイプの早期開発
- **市場リスク**: 競合アプリとの差別化不足
  -軽減策: ユーザーインタビューを基にした独自機能の強化
- **法的リスク**: プライバシー規制への不適合
  -軽減策: 法務専門家の監修とコンプライアンスチェック

この要件文書は、健康管理アプリの開発における基本的な指針を提供し、プロジェクトの成功に向けた基盤を築くことを目的としています。

内部構造としては スマートフォン向けの健康管理アプリを開発したいという要件に対してペルソナ(典型的なユーザー像)を決めて,ペルソナに対してインタビューをして,引き出した情報を評価しながら最終的な実行結果を出力する😃スゴイ(語彙力のなさ...)

ペルソナ自体も AI エージェントのワークフローとして決まるようになっていて,今回実行したときは以下の5名になっていた.

  • 中小企業経営者(ストレス管理に関心)
  • ITエンジニア(食事と運動に関心)
  • 退職者(ウォーキングを日課)
  • 専業主婦(家族の健康管理)
  • 大学生(運動データの分析に興味)

第11章: エージェントデザインパターン

第11章では Agent Design Pattern Catalogue: A Collection of Architectural Patterns for Foundation Model based Agentsという2024年の論文で紹介されている「18種類」のエージェントデザインパターンが解説されていた📝もちろん論文を読むこともできるけど,図解を含めてポイントを理解しやすいように日本語で噛み砕いて解説されている点に価値があると感じた😀デザインパターンは日々のプロンプトエンジニアリングにも活用できる気がして「あ〜こうやってプロプティングすれば良いんだ〜」という気付きにもなった💡

arxiv.org

第12章: LangChain/LangGraph で実装するエージェントデザインパターン

さらに第12章では LangChain / LangGraph を使って以下のデザインパターンを体験する.個人的には「5. セルフリフレクション」「6. クロスリフレクション」という振り返りをするデザインパターンは目からウロコだった.

  • [1] パッシブゴールクリエイター
  • [2] プロンプト/レスポンス最適化
  • [3] シングルパスプランジェネレーター
  • [4] マルチパスプランジェネレーター
  • [5] セルフリフレクション
  • [6] クロスリフレクション
  • [7] 役割ベースの協調

📚 まとめ

2024年11月9日に出版される新著「LangChain と LangGraph による RAG・AI エージェント[実践]入門」を読んだ📕

日々 ChatGPT などを活用してはいるけど,実際に LLM アプリケーションを実装することに距離を感じていた僕はまさに本書にピッタリの読者層で,LLM 関連の理解をアップデートできて本当に良かった❗️2023年に「ChatGPT/LangChain によるチャットシステム構築[実践]入門」を読んで体験したときに感じた「ワクワクする楽しさ」とはまた違った感覚で,実用的な(ディスラプティブな)アプリケーションが生まれそうな「スゴさ」を感じながら本書を読んでいた.

ちなみに LangChain に関しては2024年4月の Technology RadarHoldに位置付けられていて,ネガティブなコメントもたまに見るけど,本書の第4章では非推奨になった構文の説明も入っているし,実際に LangChain を使ってみて判断するのが良いと思う👌

www.thoughtworks.com

改めて出版おめでとうございます \( 'ω')/

2024年11月9日出版ですッッッ📅 気になったら買いましょう〜

📚 関連記事

Generative Agents Tech Blog の記事📝 LLM の歴史的な進化を振り返りつつ,なぜ本書を執筆したのかという背景がまとまっていた.Agentic アプリケーション❗️さらにフォームから応募すると輪読会・もくもく会のサポートまでお願いできるとのことでスゴイ〜 \( 'ω')/

blog.generative-agents.co.jp

AI エージェントを学ぶのに参考になるスライド📝

speakerdeck.com

📚 関連リンク

github.com

github.com

AWS CDK で IAM Role に Cognito User Pools の ポリシーを設定する

$
0
0

AWS CDK で IAM Role に Amazon Cognito User Poolsのポリシーを設定する場合は grant(grantee, ...actions)メソッドを使う.

docs.aws.amazon.com

第二引数の ...actionsは可変長引数を受け取るため,以下のようにポリシーを並べて実装できる❗️

userPool.grant(
    role,
    'cognito-idp:AdminCreateUser',
    'cognito-idp:Describe*',
    'cognito-idp:Get*',
    'cognito-idp:List*',
)

もしくはスプレッド構文を使ってポリシーの配列を展開して実装することもできる👌

const actions = ['cognito-idp:AdminCreateUser',
    'cognito-idp:Describe*',
    'cognito-idp:Get*',
    'cognito-idp:List*',
]
userPool.grant(role, ...actions)

ちなみにポリシーのサンプルは以下のドキュメントを参考にした📝

docs.aws.amazon.com

IAM Role をデプロイするとポリシーは期待通りになっていた❗️

{"Version": "2012-10-17",
    "Statement": [{"Action": ["cognito-idp:AdminCreateUser",
                "cognito-idp:Describe*",
                "cognito-idp:Get*",
                "cognito-idp:List*"
            ],
            "Resource": "arn:aws:cognito-idp:ap-northeast-1:000000000000:userpool/ap-northeast-1_xxxxxxxxx",
            "Effect": "Allow"
        }]}

ちなみに今のところ UserPoolクラスには grant()メソッドしかなく,他の L2 コンストラクトに実装されているような grant*()メソッドもあると良いのでは?という issue もあった👀

github.com


AWS CDK で Amazon API Gateway の「CloudWatch ログのロール ARN」を設定する

$
0
0

Amazon API Gateway でアクセスログを有効化してデプロイしようとすると CloudWatch Logs role ARN must be set in account settings to enable loggingというエラーが出る場合がある😇もしかしたら Amazon API Gateway の設定「CloudWatch ログのロール ARN (CloudWatch log role ARN)」が不足している可能性がある.

「CloudWatch ログのロール ARN」は Amazon API Gateway の API 単位の設定ではなく「アカウント/リージョン」単位の設定で,少し珍しいと思う.もしマルチアカウント戦略を採用できてなく,複数のプロダクトで同じアカウントを共有していたりすると,管理が面倒になることもあると思う😇

AWS CDK でエラーに対処する

選択肢 1: aws_apigateway.RestApi

まず aws_apigateway.RestApiには cloudWatchRoleという設定があって(デフォルトは false),trueにすると自動的に IAM Role を追加して Amazon API Gateway に設定してくれる👌以下にサンプルコードを載せておく.

docs.aws.amazon.com

しかし後述する理由で使わない方が良いと思う💨

import{
    Stack,
    StackProps,
    aws_apigateway,
    aws_logs,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass ApiGatewayCloudWatchRoleStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

        const apiAccessLogGroup = new aws_logs.LogGroup(this, 'ApiGatewayAccessLogGroup', {logGroupName: 'sandbox-api-gateway-access-logs',
            retention: aws_logs.RetentionDays.ONE_MONTH,
        })

        const api = new aws_apigateway.RestApi(this, 'ApiGateway', {restApiName: 'sandbox-api-gateway-cloudwatch-role',
            deployOptions: {accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup),
                accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(),
            },
            cloudWatchRole: true,
        })
        api.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random'))
    }}

実際にデプロイすると arn:aws:iam::000000000000:role/ApiGatewayCloudWatchRoleS-ApiGatewayCloudWatchRoleA-nA8OCNOvnkCSという IAM Role が設定されていた💡

もし cloudWatchRoleを設定した Amazon API Gateway を2つ以上デプロイする場合に IAM Role が上書きされてしまうという課題がある.

import{
    Stack,
    StackProps,
    aws_apigateway,
    aws_logs,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass ApiGatewayCloudWatchRoleStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

        const apiAccessLogGroup = new aws_logs.LogGroup(this, 'ApiGatewayAccessLogGroup', {logGroupName: 'sandbox-api-gateway-access-logs',
            retention: aws_logs.RetentionDays.ONE_MONTH,
        })

        const api = new aws_apigateway.RestApi(this, 'ApiGateway', {restApiName: 'sandbox-api-gateway-cloudwatch-role',
            deployOptions: {accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup),
                accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(),
            },
            cloudWatchRole: true,
        })
        api.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random'))

        const api2 = new aws_apigateway.RestApi(this, 'ApiGateway2', {restApiName: 'sandbox-api-gateway-cloudwatch-role2',
            deployOptions: {accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup),
                accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(),
            },
            cloudWatchRole: true,
        })
        api2.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random'))
    }}

実際にデプロイすると arn:aws:iam::000000000000:role/ApiGatewayCloudWatchRoleS-ApiGateway2CloudWatchRole-gT1C2Om2Nr4Yという IAM Role に上書きされていた😇 AWS CDK でリソースを管理しているのにデプロイするリソースの順番によって「CloudWatch ログのロール ARN」の値が変わってしまうのは微妙だと思う.

選択肢 2: aws_apigateway.CfnAccount

そこで aws_apigateway.CfnAccountを使うと「CloudWatch ログのロール ARN」を独立したリソースとして管理できる❗️ドキュメントには以下のように書いてあった.できれば AWS CDK Stack も別にしておくと良いと思う👌

To avoid overwriting other roles, you should only have one AWS::ApiGateway::Account resource per region per account.

docs.aws.amazon.com

以下のように aws_iam.Roleaws_apigateway.CfnAccountを組み合わせて実装できる.

import{
    Duration,
    Stack,
    StackProps,
    aws_apigateway,
    aws_iam,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass ApiGatewayAccountStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

        const role = new aws_iam.Role(this, 'ApiGatewayLogsRole', {roleName: 'api-gateway-logs-role',
            assumedBy: new aws_iam.ServicePrincipal('apigateway.amazonaws.com'),
            managedPolicies: [
                aws_iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonAPIGatewayPushToCloudWatchLogs'),
            ],
            maxSessionDuration: Duration.hours(1),
        })

        new aws_apigateway.CfnAccount(this, 'ApiGatewayAccount', {cloudWatchRoleArn: role.roleArn,
        })
    }}

実際にデプロイすると arn:aws:iam::000000000000:role/api-gateway-logs-roleという指定した IAM Role を設定できていた👏

関連ドキュメント

docs.aws.amazon.com

Testcontainers for Python と Moto を組み合わせて Boto3 を使った Python コードをテストする

$
0
0

Motoで AWS サービスをモックして AWS SDK for Python (Boto3) を使った Python コードをテストする場合に @mock_awsデコレータや withブロックを使うという選択肢がある.詳しくは以下のドキュメントに載っている📝

docs.getmoto.org

また Moto には「Server Mode」というコンテナベースで起動する方法もある😀

docs.getmoto.org

普段 pytest を使って Python コードをテストする場合は MySQL や LocalStack などの依存関係を Testcontainers for Pythonで管理していて便利なので,Moto も使えたら良いのにな〜と思っていた💡残念ながら Testcontainers for Python は Moto をサポートしていないけど,任意のコンテナイメージを起動できる DockerContainerがあって,試してみることにした.結果的にイメージ通りに Testcontainers for Python と Moto を組み合わせることができた❗️

testcontainers-python.readthedocs.io

サンプルコード

👾 src/mymodel.py

今回はサンプルとして Moto のドキュメントに載っていた MyModelクラスを少し修正してテスト対象にする.save()関数を実行すると Amazon S3 にファイルをアップロードするシンプルな実装になっている💡

そして,Boto3 の Amazon S3 クライアントは環境変数 ENVによって3種類作れるようにしてある👌

  • local: ローカル開発用 (Moto)
  • test: テスト用 (testcontainers-python / Moto)
  • その他: 実際の AWS アカウント
import os

import boto3

if os.environ['ENV'] == 'local':
    s3 = boto3.client('s3', region_name='us-east-1', endpoint_url='http://localhost:5000')
elif os.environ['ENV'] == 'test':
    s3 = boto3.client(
        's3', region_name='us-east-1', endpoint_url=f'http://localhost:{os.environ['TESTCONTAINERS_MOTO_PORT']}'
    )
else:
    s3 = boto3.client('s3', region_name='us-east-1')


classMyModel:
    def__init__(self, name, value):
        self.name = name
        self.value = value

    defsave(self):
        s3.put_object(Bucket='mybucket', Key=self.name, Body=self.value)


if __name__ == '__main__':
    model_instance = MyModel('steve', 'is awesome')
    model_instance.save()

👾 tests/test_mymodel.py

テストコードでは,まずフィクスチャで Testcontainers for Python の DockerContainerを使って Moto のコンテナ (motoserver/moto) を起動していて,Amazon S3 バケット mybucketも追加している.なお Testcontainers for Python で Moto を起動するとポートはランダムに決まるため,環境変数 TESTCONTAINERS_MOTO_PORTに設定して src/mymodel.pyでも参照できるようにしている👌

そして,実際のテストでは save()関数によってアップロードされた Amazon S3 オブジェクトを取得して,中身を確認している.

import os

import boto3
import pytest
from testcontainers.core.container import DockerContainer


@pytest.fixture(scope='module', autouse=True)
def_setup():
    with DockerContainer('motoserver/moto').with_exposed_ports(5000) as container:
        os.environ['TESTCONTAINERS_MOTO_PORT'] = container.get_exposed_port(5000)

        s3 = boto3.client(
            's3', region_name='us-east-1', endpoint_url=f'http://localhost:{os.environ['TESTCONTAINERS_MOTO_PORT']}'
        )
        s3.create_bucket(Bucket='mybucket')

        yielddeftest_my_model_save():
    from mymodel import MyModel, s3

    model_instance = MyModel('steve', 'is awesome')
    model_instance.save()

    body = s3.get_object(Bucket='mybucket', Key='steve')['Body'].read().decode('utf-8')

    assert body == 'is awesome'

動作確認

今回は Python プロジェクトを uvで管理しているけど,環境変数 ENVを設定して pytest を実行するとイメージ通りに動いた👌pytest 実行時に一時的に Moto が起動して,テスト終了後は自動的に削除される.便利〜 \( 'ω')/

$ ENV=test uv run pytest -p no:warnings --verbose tests
=============================================================================================================================test session starts ==============================================================================================================================(中略)
configfile: pyproject.toml
collected 1 item

tests/test_mymodel.py::test_my_model_save PASSED                                                                                                                                                                                                                         [100%]==============================================================================================================================1 passed in1.48s ===============================================================================================================================

GitHub Actions でテストを実行する

ついでに GitHub Actions でも pytest を実行できるようにしてみた👌

👾 .github/workflows/pytest.yml

name: Run pytest

on:workflow_dispatch:push:branches:- main

env:AWS_PROFILE: moto

jobs:pytest:runs-on: ubuntu-latest
    steps:- uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - name: Setup Python
        run: uv python install
      - name: Install dependencies
        run: uv sync
      - name: Set AWS Profile for Moto
        run: |
          aws configure set --profile moto aws_access_key_id DUMMY
          aws configure set --profile moto aws_secret_access_key DUMMY
      - name: Run pytest
        run: ENV=test uv run pytest -p no:warnings --verbose tests

まとめ

Testcontainers for Python と Moto を組み合わせて pytest を実行できるようにしてみた❗️これは結構便利かな〜と思う.

ちなみにコードは GitHub に置いてあるので参考にどうぞ〜 \( 'ω')/

github.com

関連記事

kakakakakku.hatenablog.com

Moto を使って Cognito の cognito-idp API をローカル環境で試す

$
0
0

Amazon Cognito を操作するコード/テストを実装するときに LocalStack を使いたいな〜と思うけど,Amazon Cognito は LocalStack Pro Editionでサポートされていて,LocalStack Community Editionでは使えないと言う課題がある.

docs.localstack.cloud

もちろん LocalStack Pro Edition を契約して使うという選択肢もありつつ,単純に Amazon Cognito の基本的な API を実行できれば良いのであれば Motoを使うという選択肢もある❗️

github.com

例えば Amazon Cognito の cognito-idp API だと以下のようなカバレッジになっていて,Amazon Cognito User Pools の操作など主要な API はサポートされているかな〜という感じ👏

docs.getmoto.org

LocalStack と Moto

ちなみに LocalStack と Moto の関係性については Moto のドキュメントに以下のように書いてある📝 LocalStack は Moto よりも高度な機能を持っていると書いてあるけど,実際に LocalStack の内部で Moto に依存しているところも多く,Moto は LocalStack の重要な「コア部分」と言えると思う.

LocalStack is Moto’s bigger brother with more advanced features, such as EC2 VM’s that you can SSH into and Dockerized RDS-installations that you can connect to.

docs.getmoto.org

Moto: Server Mode

Moto は Python ライブラリとして使えるように実装されているけど,実は Server Mode という機能もあって LocalStack とほとんど同じ体験で使えるため,個人的には Moto の Server Mode が使いやすいかな〜と思う \( 'ω')/

docs.getmoto.org

前に Testcontainers for Python と pytest を組み合わせて Moto を自動テストに活用するという記事を書いたときも Server Mode を使っている👌

kakakakakku.hatenablog.com

Moto の Server Mode を起動する場合は以下のようにする❗️

👇️コンテナとして起動する例

$ docker run --rm-p 5000:5000 --name moto motoserver/moto

$ aws sts get-caller-identity \--endpoint http://127.0.0.1:5000
{"UserId": "AKIAIOSFODNN7EXAMPLE",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:user/moto"}

👇️ Python ライブラリから起動する例

$ pip install 'moto[server]'
$ moto_server

👇️ uvと uvx で起動する例

$ uvx --from'moto[server]' moto_server

AWS CLI で試す

今回は AWS CLI で Moto の基本的な cognito-idp API を試してみた❗️AWS CLI は同じでエンドポイントを --endpoint http://127.0.0.1:5000のようにすれば OK👌

  • aws cognito-idp list-user-pools
  • aws cognito-idp create-user-pool
  • aws cognito-idp create-user-pool-client
  • aws cognito-idp list-user-pool-clients
  • aws cognito-idp admin-create-user
  • aws cognito-idp list-users
  • aws cognito-idp admin-delete-user

aws cognito-idp list-user-pools

$ aws cognito-idp list-user-pools \--max-results2\--endpoint http://127.0.0.1:5000
{"UserPools": []}

awscli.amazonaws.com

aws cognito-idp create-user-pool

$ aws cognito-idp create-user-pool \--pool-name sandbox \--endpoint http://127.0.0.1:5000

$ aws cognito-idp list-user-pools \--max-results2\--endpoint http://127.0.0.1:5000
{"UserPools": [{"Id": "ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e",
            "Name": "sandbox",
            "LambdaConfig": {},
            "LastModifiedDate": "2024-11-07T23:00:00+09:00",
            "CreationDate": "2024-11-07T23:00:00+09:00"}]}

awscli.amazonaws.com

aws cognito-idp create-user-pool-client

$ aws cognito-idp create-user-pool-client \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--client-name sandbox \--endpoint http://127.0.0.1:5000
{"UserPoolClient": {"UserPoolId": "ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e",
        "ClientName": "sandbox",
        "ClientId": "9ujfbc613ayfsozlg5ar8v20ag",
        "RefreshTokenValidity": 30,
        "AllowedOAuthFlowsUserPoolClient": false,
        "EnableTokenRevocation": true,
        "EnablePropagateAdditionalUserContextData": false,
        "AuthSessionValidity": 3}}

awscli.amazonaws.com

aws cognito-idp list-user-pool-clients

$ aws cognito-idp list-user-pool-clients \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--endpoint http://127.0.0.1:5000
{"UserPoolClients": [{"ClientId": "9ujfbc613ayfsozlg5ar8v20ag",
            "UserPoolId": "ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e",
            "ClientName": "sandbox"}]}

awscli.amazonaws.com

aws cognito-idp admin-create-user

$ aws cognito-idp admin-create-user \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--username kakakakakku \--endpoint http://127.0.0.1:5000
{"User": {"Username": "kakakakakku",
        "Attributes": [{"Name": "sub",
                "Value": "1ce4bba2-35e0-4adc-8357-e5bb594538b0"}],
        "UserCreateDate": "2024-11-07T23:00:00+09:00",
        "UserLastModifiedDate": "2024-11-07T23:00:00+09:00",
        "Enabled": true,
        "UserStatus": "FORCE_CHANGE_PASSWORD",
        "MFAOptions": []}}

awscli.amazonaws.com

aws cognito-idp list-users

$ aws cognito-idp list-users \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--endpoint http://127.0.0.1:5000
{"Users": [{"Username": "kakakakakku",
            "Attributes": [{"Name": "sub",
                    "Value": "1ce4bba2-35e0-4adc-8357-e5bb594538b0"}],
            "UserCreateDate": "2024-11-07T23:00:00+09:00",
            "UserLastModifiedDate": "2024-11-07T23:00:00+09:00",
            "Enabled": true,
            "UserStatus": "FORCE_CHANGE_PASSWORD",
            "MFAOptions": []}]}

awscli.amazonaws.com

aws cognito-idp admin-delete-user

$ aws cognito-idp admin-delete-user \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--username kakakakakku \--endpoint http://127.0.0.1:5000

$ aws cognito-idp list-users \--user-pool-id ap-northeast-1_a709065b9e7c4161b45b83cdbf0b313e \--endpoint http://127.0.0.1:5000
{"Users": []}

awscli.amazonaws.com

crossRegionReferences: AWS CDK で Cognito User Pools にカスタムドメインを設定する

$
0
0

Amazon Cognito User Pools にカスタムドメインを設定する場合,内部的に追加される Amazon CloudFront で「バージニア北部リージョン (us-east-1)」の AWS Certificate Manager (ACM) 証明書が必要になる📝

しかし AWS CDK(もしくは AWS CloudFormation)ではクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)ができず,実装するときに AWS Systems Manager Parameter Store を使ったり,Props に静的に ARN (Amazon Resource Names) を設定したり,何かしら考慮が必要になるという課題がある.

docs.aws.amazon.com

ちなみに Amazon Cognito User Pools のカスタムドメインに限った話ではなく,例えば Amazon CloudFront と Amazon S3 を組み合わせる場合にも出てくる仕様になる.

docs.aws.amazon.com

実際に試すと以下のエラーが出る🔥

Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack.

crossRegionReferencesプロパティ

現在まだ実験的機能 (experimental) ではあるけど,AWS CDK の crossRegionReferencesプロパティを使うとイイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できる👌

仕組みをザックリと説明すると,AWS CloudFormation のカスタムリソース (AWS Lambda 関数) で us-east-1 リージョンの AWS Certificate Manager 証明書 ARN を ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に登録して,ap-northeast-1 リージョンの AWS CloudFormation スタックでは AWS Systems Manager Parameter Store の動的参照を使って AWS Certificate Manager 証明書 ARN を取得する.ザックリではあるけどアーキテクチャ図も描いておいた❗️

詳細な仕組みは AWS CDK ドキュメントを参照してもらえればと📝

docs.aws.amazon.com

サンプルコード

動作確認に使ったサンプルコードを載せておく👌

👾 bin/cdk.ts

ここで crossRegionReferencesプロパティを設定している❗️そして AWS Certificate Manager 証明書 ARN を CognitoCustomDomainStackに渡している.

const envVirginia = {account: '000000000000',
    region: 'us-east-1',
}const envTokyo = {account: '000000000000',
    region: 'ap-northeast-1',
}const cognitoCustomDomainAcmStack = new CognitoCustomDomainAcmStack(app, 'CognitoCustomDomainAcmStack', {env: envVirginia,
    crossRegionReferences: true,
})

new CognitoCustomDomainStack(app, 'CognitoCustomDomainStack', {env: envTokyo,
    crossRegionReferences: true,
    certificate: cognitoCustomDomainAcmStack.certificate,
})

👾 cognito-custom-domain-acm.ts

AWS Certificate Manager 証明書を作ってクロススタックで取得できるようにしておく.今回は userpool.xxxxx.netというカスタムドメインにした📝

import{
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_route53,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass CognitoCustomDomainAcmStack extends Stack {publicreadonlycertificate: aws_certificatemanager.Certificateconstructor(scope:Construct, id:string, props:StackProps) {super(scope, id, props)

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        this.certificate = new aws_certificatemanager.Certificate(this, 'Certificate', {domainName: 'userpool.xxxxx.net',
            validation: aws_certificatemanager.CertificateValidation.fromDns(hostedZone),
        })
    }}

👾 cognito-custom-domain.ts

AWS Certificate Manager 証明書を ARN を StackProps から取得して,Amazon Cognito User Pools のカスタムドメインに設定する👌

import{
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_cognito,
    aws_route53,
    aws_route53_targets,
}from'aws-cdk-lib'import{ Construct }from'constructs'interfaceCustomStackPropsextends StackProps {certificate: aws_certificatemanager.Certificate}exportclass CognitoCustomDomainStack extends Stack {constructor(scope:Construct, id:string, props:CustomStackProps) {super(scope, id, props)

        const userPool = new aws_cognito.UserPool(this, 'UserPool', {userPoolName: 'sandbox',
        })

        const userPoolDomain = userPool.addDomain('UserPoolDomain', {customDomain: {domainName: 'userpool.xxxxx.net',
                certificate: props.certificate,
            },
        })

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        new aws_route53.ARecord(this, 'ApexARecod', {zone: hostedZone,
            target: aws_route53.RecordTarget.fromIpAddresses('8.8.8.8'),
        })

        new aws_route53.ARecord(this, 'UserPoolARecord', {zone: hostedZone,
            recordName: 'userpool',
            target: aws_route53.RecordTarget.fromAlias(new aws_route53_targets.UserPoolDomainTarget(userPoolDomain)),
        })
    }}

デプロイ確認

実際にデプロイした結果(既にリソースは削除済)を抜粋して載せておく👌

👇️ us-east-1 リージョンに AWS Certificate Manager 証明書を登録できた

👇️ ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に AWS Certificate Manager 証明書 ARN が登録されていた

👇️ ap-northeast-1 リージョンの Amazon Cognito User Pools に us-east-1 リージョンの AWS Certificate Manager 証明書が登録されていた

まとめ

AWS CDK の crossRegionReferencesプロパティを使って,Amazon Cognito User Pools にカスタムドメインを設定してみた.イイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できて便利だった❗️

しかし現状ではまだ「実験的機能 (experimental)」でプロダクション環境では採用しにくく,最近似たような構成を仕事で実装したときは crossRegionReferencesプロパティは使わずに独自に実装をした.正式リリースを待つぞー \( 'ω')/

参考リンク

AWS CloudFormation のカスタムリソース実装は以下にある🔗

github.com

github.com

AWS CDK で Cognito User Pools の「メッセージテンプレート」を設定する

$
0
0

Amazon Cognito User Pools から自動的に送信される「招待メッセージ」はデフォルトでは以下のようにシンプルな設定になっていて,実運用では使いにくさがある😇 あと末尾の .までコピーしてログインしようとして「エラーになります」って言われることもよくあったりする🔥

emailSubject:
Your temporary password
emailBody:
Your username is {username} and temporary password is {####}.

そこで「メッセージテンプレート」を使うとある程度ならメールテンプレートをカスタマイズできる✉️

docs.aws.amazon.com

今回は AWS CDK でメッセージテンプレートを実装してみた❗️aws_cognito.UserPooluserInvitationを設定すれば OK👌

docs.aws.amazon.com

ちなみにメール本文はマークアップに対応してて,改行する場合は <br />を使う.

import{
    Stack,
    StackProps,
    aws_cognito,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass CognitoMessageTemplateStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

        new aws_cognito.UserPool(this, 'UserPool', {userPoolName: 'sandbox',
            signInAliases: {email: true,
            },
            userInvitation: {emailSubject: 'kakakakakku sandbox: Your temporary password',
                emailBody: `Thank you for signing up to kakakakakku sandbox \( 'ω')/<br />Your username is {username} and temporary password is {####}`,
            }})
    }}

デプロイして再度確認するとカスタマイズしたメッセージテンプレートになっていた👏

生成 AI パスポート試験(2024年 第3回)を受験してみた

$
0
0

2024年10月18日(受験可能期間は10月1日〜10月31日)に「生成 AI パスポート試験(2024年 第3回)」を受験して合格した👌試験自体は IBT (Internet Based Testing) だけど結果はすぐに出ず,2024年11月18日に発表された.個人的に結構イイ試験だな〜と感じたので「試験の紹介を目的として」出題内容には触れず簡単に振り返っておこうと思う.

guga.or.jp

試験概要

「生成 AI パスポート試験」はその名前の通り,最近よく話題になる「生成 AI」の基礎知識を問う試験になっていて,ウェブサイトには「生成 AI リスクを予防する資格試験」と表現されていたりもする.よって,少なくともこのぐらいは知っておくと良いぞ〜というポイント(歴史・技術・活用テクニックなど)を理解していることを証明できる💡

出題範囲(2024年 第3回 前提)

出題範囲はシラバス(生成 AI パスポート シラバス 8.03 最新確定版)に詳しく載っていて,ある程度理解していればシラバスを見ると「あ〜こういう感じか〜」と把握できると思う.ちなみにシラバスを見て「ちょっと古いのでは...?」と感じる人もいると思っていて,実は次回開催からはシラバスが改訂されるため,それに関しては記事の最後に紹介する❗️

  • 第1章: AI(人工知能)
  • 第2章: 生成 AI(ジェネレーティブ AI)
  • 第3章: 現在の生成 AI(ジェネレーティブ AI)の動向
  • 第4章: 情報リテラシー・基本理念とAI社会原則
  • 第5章: テキスト生成 AI のプロンプト制作と実例

あと試験としては合格率が高く難易度は低めだと思う.

  • 2024年 第2回(合格率 78.76%)
  • 2024年 第3回(合格率 75.76%)

guga.or.jp

guga.or.jp

なぜ受験したか

自分自身はソフトウェアエンジニアとして日々生成 AI を活用していて,ある程度の理解と体験があるためシラバスを見て基本的にはわかるかな〜という感じだった.よって,資格試験の受験者層とは少し外れている可能性はあるけど,今後もっと生成 AI が普及するためには重要な資格なのかと考えて,どんな資格試験なのか実際に体験しておきたいというモチベーションがあった💪

試験対策

書籍(テキスト・問題集)や研修・試験対策コンテンツなど十分に揃っていると思う.

今回は「生成 AI パスポート公式テキスト 第2版(現在 Amazon で買えるのは第3版だけど読んだのは第2版)」「生成 AI パスポート テキスト & 問題集」をザッと読んだ📕内容的には「公式テキスト」の方が詳しく解説されているけど,あくまでテキスト(教科書)なので,模擬テストを解きながら勉強するなら「テキスト & 問題集」の方が良いと思う.内容的には似ていて,両方読む必要はないかな〜という印象だった.

生成AIパスポート テキスト&問題集

生成AIパスポート テキスト&問題集

  • 日本能率協会マネジメントセンター
Amazon

あとは「おとなもこどもも知りたい 生成 AI の教室」も読んだ📕たまたま子供がこの本を読んでいて,読ませてもらったけど,結構「生成 AI パスポート試験」の出題範囲とカブっていておすすめ❗️小学生でも読める本だからイメージしやすく,本格的な試験対策に入る前に読んでおくと良いと思う.

他に Transformer / GAN / LSTM などの理解は YouTube「スタビジ」の解説動画を観た😀

www.youtube.com

さらにポッドキャスト「Misreading Chat」は普段から聴いていて,そういえば過去に GAN / VAE / Stable Diffusion 関連の論文紹介エピソードがあったな〜と思い出して,改めて聴き直したりもした👂️

misreading.chat

misreading.chat

misreading.chat

ザッとこんな感じ❗️よって,直接的に試験対策をするというよりは,出題範囲で理解が浅いな〜と感じた部分を中心に学び直していた感じだった \( 'ω')/

試験当日

試験自体は IBT で自宅から受験できる👌受験可能期間が1ヶ月間もあって,時間調整しやすいのは良かった.「IBT 受験に必要な機器および受験環境」「禁止事項」はウェブサイトに載っている📝

guga.or.jp

僕はよく AWS 認定試験・Kubernetes 認定資格・Terraform 認定試験などを受験していて,オンライン受験には慣れているため,普段と同じ環境で受験した.オペレーターに監視されることはなかった.

出題範囲(2025年2月以降)

実は次回開催(2025年2月)から出題範囲が改訂されるとアナウンスされている💡シラバス(シラバス改定 2025年2月試験版)も公開されていて,例えば GPT-4o / Claude / Gemini / Sora なども出題範囲になっていたり,AI 事業者ガイドライン(第1.0版)前提で改訂されていてイイ❗️試験自体の鮮度が保たれていることは重要だと思う.

www.meti.go.jp

さらに「生成 AI パスポート試験」の良いところに「生成 AI パスポート 資格更新テスト」という仕組みがあって,新シラバスに沿った知識を証明するために問題数を減らして再受験することができる👌

guga.or.jp

まとめ

個人的にイイ試験だと思う❗️受験して良かった.もし興味あるぞ〜って思ったら次回開催(2025年2月)を申し込んでみると良いじゃないでしょうか😀(申し込み期限は2025年1月31日📅)

1点改善点を挙げるなら IBT なのに合否発表まで遅すぎるのでもっと早く発表されると良いな〜とは思う.

Terraform で Service-Linked Role(サービスにリンクされたロール)を作る

$
0
0

マネジメントコンソールを使ってリソースを設定していると自動的に「Service-Linked Role(サービスにリンクされたロール)」が作られていることがある💡実は AWS CloudFormation や Terraform を使って Service-Linked Role を IaC (Infrastructure as Code) で管理することもできて,今回はお試しとして Terraform の aws_iam_service_linked_roleリソースを使って AWS Configの Service-Linked Role を作ってみる👌

👾 iam.tf

aws_iam_service_linked_roleリソースの aws_service_nameに AWS Config を表す config.amazonaws.comを設定しておけば OK👌 簡単❗️

resource"aws_iam_service_linked_role""config"{aws_service_name = "config.amazonaws.com"}

docs.aws.amazon.com

ちなみに Service-Linked Role をサポートしているサービス一覧は以下のドキュメントで確認できる📝

docs.aws.amazon.com

デプロイ確認

Terraform コードを apply すると Service-Linked Role AWSServiceRoleForConfigを追加できていた \( 'ω')/

AWS Security Hub と AWS Config

AWS Config には Service-Linked Role AWSServiceRoleForConfigもしくは マネージドポリシー AWS_ConfigRoleAWSConfigRoleではなく)をアタッチした IAM Role を使うとドキュメントには書いてある📝実際にポリシーを比較しても基本的には同じ設定になっていた.

docs.aws.amazon.com

しかし AWS Security Hub のコントロール [Config.1] AWS Config should be enabled and use the service-linked role for resource recordingはデフォルトで Service-Linked Role AWSServiceRoleForConfigを使うことを要求していて,マネージドポリシー AWS_ConfigRoleをアタッチした IAM Role を使っていると警告が出てしまう(includeConfigServiceLinkedRoleCheckパラメータで無効化することは可能).

よって,特殊な要件がなく AWS Security Hub に準拠するのであれば Service-Linked Role を使っておくのが良さそうだな〜と思った🤔

docs.aws.amazon.com


Step Functions の新機能「変数」と「JSONata」を AWS と LocalStack で試す

$
0
0

2024年11月22日に AWS Step Functions で新機能「変数 (variables)」「JSONata」がサポートされた❗️

aws.amazon.com

特に「変数」は待望の機能なんじゃないかな〜と思う.今までは AWS Step Functions でタスク結果を引き回すときにどうしてもバケツリレーが必要で,正直 ResultSelector / ResultPath という構文も難しく(何度書いても忘れてしまう😇)ある程度複雑なワークフローを実装するときはデータシュミレーターがないと無理に思えるほどだった.個人的には AWS Step Functions は動くようになったら最高に便利だけどイメージ通りに実装するまでの過程が結構大変〜というイメージがある💨

変数と JSONata を試す

さっそく変数と JSONata を試してみる❗️今回は AWS Lambda 関数から出力された数値同士を「掛け算」するサンプルにする.実は今までの AWS Step Functions 組み込み関数では States.MathAddを使った「足し算」しかサポートされてなく,過去に困った経験があった.

docs.aws.amazon.com

AWS Step Functions ワークフロー

以下のようなワークフローを作った.AWS Lambda 関数 returnXreturnYはランダムな整数を返して結果を「変数」設定し,最後の Passでは「JSONata」を使って returnXreturnYの値を計算(掛け算)する👌

ステートマシンは必要最低限にしていて,ポイントをザッと以下に挙げる📝

  • QueryLanguageJSONataを指定する
  • Assignを使って AWS Lambda 関数 returnXreturnYの結果 {% $states.result.body %}を変数に設定する
  • JSONata 式 {% $x * $y %}で計算(掛け算)する
{"QueryLanguage":"JSONata",
  "StartAt":"returnX",
  "States":{"returnX":{"Type":"Task",
      "Resource":"arn:aws:lambda:ap-northeast-1:000000000000:function:returnX",
      "Assign":{"x":"{% $states.result.body %}"},
      "Next":"returnY"},
    "returnY":{"Type":"Task",
      "Resource":"arn:aws:lambda:ap-northeast-1:000000000000:function:returnY",
      "Assign":{"y":"{% $states.result.body %}"},
      "Next":"multiplication"},
    "multiplication":{"Type":"Pass",
      "Output":{"multiplication":"{% $x * $y %}"},
      "End":true}}}

AWS Lambda 関数 returnXreturnYの実装はシンプル💡

import json
import random

deflambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': random.randint(1, 100)
    }

ちなみに 0 ≤ n < 1の範囲でランダムな値を取得するなら JSONata の $random()関数で表現できてしまう.あくまで今回は AWS Lambda 関数で何かしらの処理を実行するサンプルとして見てもらえればと👌

docs.jsonata.org

実行結果

AWS Step Functions ワークフローを実行すると期待通りに「変数」「JSONata」を使えた❗️

注意点

今回のリリースと同時に今まで何度もお世話になったデータフローシュミレーターがメンテナンスされなくなっていた😇

LocalStack で変数と JSONata を試す

今回さらにスゴイのは LocalStack も同時に変数と JSONata をサポートしている点で,リリースノートにも LocalStack の紹介文が載っていた❗️

We have also partnered with LocalStack and Datadog to ensure that their local emulation and observability experiences are updated to support Variables and JSONata.

そして LocalStack 側のブログにも記事が出ている📝

blog.localstack.cloud

正確には LocalStack v4.0.1 でサポートされているため,今回は最新の LocalStack v4.0.2 で試す.

github.com

AWS Step Functions ワークフロー

以下の AWS SAM テンプレートを準備した.マネジメントコンソールで試した構成とほぼ同じ👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:FunctionReturnX:Type: AWS::Serverless::Function
    Properties:FunctionName: returnX
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
  FunctionReturnY:Type: AWS::Serverless::Function
    Properties:FunctionName: returnY
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
  StateMachine:Type: AWS::Serverless::StateMachine
    Properties:Name: sandbox
      DefinitionUri: ./sandbox.asl.json
      DefinitionSubstitutions:FunctionXArn:!GetAtt FunctionReturnX.Arn
        FunctionYArn:!GetAtt FunctionReturnY.Arn

ステートマシン sandbox.asl.jsonもほぼ同じで,AWS Lambda 関数名の ARN を AWS SAM テンプレートから渡すところが違う👌

{"QueryLanguage": "JSONata",
  "StartAt": "returnX",
  "States": {"returnX": {"Type": "Task",
      "Resource": "${FunctionXArn}",
      "Assign": {"x": "{% $states.result.body %}"
      },
      "Next": "returnY"
    },
    "returnY": {"Type": "Task",
      "Resource": "${FunctionYArn}",
      "Assign": {"y": "{% $states.result.body %}"
      },
      "Next": "multiplication"
    },
    "multiplication": {"Type": "Pass",
      "Output": {"multiplication": "{% $x * $y %}"
      },
      "End": true}}}

次に LocalStack にデプロイすると LocalStack Resource Browser で確認できる❗️

$ samlocal build 
$ samlocal deploy

そして LocalStack 上の AWS Step Functions ワークフローを実行すると,{\"multiplication\":1344}と出力されていて期待通りに計算(掛け算)できている👏

最高だ〜 \( 'ω')/

$ awslocal stepfunctions start-execution --state-machine-arn arn:aws:states:ap-northeast-1:000000000000:stateMachine:sandbox
{"executionArn": "arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba",
    "startDate": "2024-11-28T00:00:00.000000+09:00"}

$ awslocal stepfunctions describe-execution --execution-arn arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba
{"executionArn": "arn:aws:states:ap-northeast-1:000000000000:execution:sandbox:ac69989b-6569-4881-aea7-23de22d124ba",
    "stateMachineArn": "arn:aws:states:ap-northeast-1:000000000000:stateMachine:sandbox",
    "name": "ac69989b-6569-4881-aea7-23de22d124ba",
    "status": "SUCCEEDED",
    "startDate": "2024-11-28T00:00:00.000000+09:00",
    "stopDate": "2024-11-28T00:00:00.000000+09:00",
    "input": "{}",
    "inputDetails": {"included": true},
    "output": "{\"multiplication\":1344}",
    "outputDetails": {"included": true}}

LocalStack Resource Browser でも実行結果を確認できた❗️(今のところ変数は表示されていなさそうだった)

関連記事

aws.amazon.com

関連ドキュメント

docs.jsonata.org

docs.aws.amazon.com

AWS CDK で Service Quotas のクォータをモニタリングする CloudWatch Alarm を設定する

$
0
0

特にプロダクション環境でウェブサービスを稼働させるときに AWS サービスのクォータ(サービス制限)を理解して運用することが重要で,例えば AWS Well-Architected Framework (Reliability Pillar) の REL01-BP04では Not configuring monitoring to check for service quota thresholdsというアンチパターンが紹介されていたりもする❗️

docs.aws.amazon.com

Service Quotas でクォータの使用状況をモニタリングする

実は Amazon CloudWatch で Service Quotas 使用状況をモニタリングすることができる🚨モニタリングできるサービスとメトリクスは以下のドキュメントに載っている.しかしサービスによっては詳細に載っていないこともあって判断しにくく,Service Quotas コンソールでメトリクスを選択して「モニタリング」タブと「アラーム」タブが表示されていればモニタリングできて,モニタリングできない場合は「リクエスト履歴」タブのみ表示されるので判断できる👌

docs.aws.amazon.com

Amazon SageMaker: Number of instances across all processing jobs

クォータによってメトリクスの設定が異なるため,実装する前にマネジメントコンソールからアラートを作って設定を確認しておくと良いと思う👌

具体例を挙げると,AWS Lambda のクォータ Concurrent executions (L-B99A9384)だと AWS/Lambda名前空間の ConcurrentExecutionsメトリクスを使って,Amazon DynamoDB のクォータ Maximum number of tables (L-F98FE922)だと AWS/Usage名前空間の ResourceCount (TableCount)メトリクスを使う.そして,Amazon SageMaker のクォータ Number of instances across all processing jobs (L-F311B08F)だと AWS/Usage名前空間の ResourceCount (processing-job/total_instance_count)メトリクスを使う.

👾 lib/service-quotas.ts

今回はサンプルとして Amazon SageMaker のクォータ Number of instances across all processing jobs (L-F311B08F)を使う❗️実装上のポイントは Amazon CloudWatch メトリクスの数式でクォータの使用状況を返す SERVICE_QUOTA関数を使うところ \( 'ω')/ 今回アラームアクションは割愛しているけど,実際には Amazon SNS トピックなどを設定しておくと良いと思う.

docs.aws.amazon.com

import{
    Duration,
    Stack,
    StackProps,
    aws_cloudwatch,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass ServiceQuotasStack extends Stack {constructor(scope:Construct, id:string, props:StackProps) {super(scope, id, props)

        const mathExpression = new aws_cloudwatch.MathExpression({expression: '(usage_data/SERVICE_QUOTA(usage_data))*100',
            label: '% 使用率',
            usingMetrics: {usage_data: new aws_cloudwatch.Metric({namespace: 'AWS/Usage',
                    metricName: 'ResourceCount',
                    dimensionsMap: {Type: 'Resource',
                        Service: 'SageMaker',
                        Resource: 'processing-job/total_instance_count',
                        Class: 'None',
                    },
                    statistic: 'Maximum',
                }),
            },
            period: Duration.hours(1),
        })

        mathExpression.createAlarm(this, 'QuotasAlarm_L-F311B08F', {alarmName: 'Number of instances across all processing jobs',
            threshold: 50,
            evaluationPeriods: 10,
            comparisonOperator: aws_cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
        })
    }}

デプロイ確認

デプロイするとマネジメントコンソールから作ったデフォルトのアラートとほとんど同じ設定になっていた❗️

関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

GitHub リポジトリで「リリースノート」を自動的に作ろう

$
0
0

GitHub にはリリースノートを自動的に作る機能があって,マージされたプルリクエストをまとめたりできる👌 OSS ライブラリで semver に沿ってリリースノートを作るのも便利だし,仕事で使っているプライベートリポジトリでも「いつどのプルリクエストがデプロイされたのか」をパッと確認することができて便利❗️

機能を試すだけなら簡単で,GitHub リポジトリで Releases > Draft a new release > Generate release notesと選択するとデフォルト設定でリリースノートを自動的に作ってくれる \( 'ω')/

docs.github.com

リリースノートをカスタマイズする

👾 .github/release.yml

リリースノートの本文をカスタマイズするために .github/release.ymlファイルを作る📝 設定はいくつかあって詳しくはドキュメントに載っているけど,今回はできる限りシンプルな設定にしてみた👌簡単に言うと Dependabot 経由のプルリクエスト(デフォルトで dependenciesラベルが付く)とそれ以外のプルリクエストをカテゴライズしてまとめている❗️

changelog:categories:- title: Dependencies 🤖
      labels:- dependencies
    - title: All Changes
      labels:- "*"

ドキュメントに載っている例だともっと細かくて,Semver-Majorラベルを付けたプルリクエストは Breaking Changes 🛠にカテゴライズして,Semver-Minorラベルを付けたプルリクエストは Exciting New Features 🎉にカテゴライズしている💡

# .github/release.ymlchangelog:exclude:labels:- ignore-for-release
    authors:- octocat
  categories:- title: Breaking Changes 🛠
      labels:- Semver-Major
        - breaking-change
    - title: Exciting New Features 🎉
      labels:- Semver-Minor
        - enhancement
    - title: Other Changes
      labels:- "*"

gh releaseコマンド

そして,リリースノートを自動的に作るときは GitHub リポジトリでポチポチすることもできるけど,gh releaseコマンドでシュッと作ることもできる👌

$ gh release create v1.0.0--generate-notes

cli.github.com

GitHub Actions でリリースノートを自動的に作る

さらに自動化を推進するために GitHub Actions でリリースブランチ(mainブランチ・prdブランチなど)にプルリクエストをマージしたら自動的にリリースノートを作れるようにする❗️

今回は softprops/action-gh-releaseを使う.個人的に一番使いやすそうだった.ちなみに GitHub Actions 公式の actions/create-releaseは2021年にアーカイブされている😇

github.com

👾 .github/workflows/create-release.yml

ザッとこんな感じ👌今回は mainブランチにプルリクエストをマージしたら実行するようにした.そして,個人プロジェクトでは semver は必要なく,リリース名(タグ名)はタイムスタンプにした.ちなみにデフォルトでは GitHub リポジトリのデフォルトブランチに対してリリースを作るけど,target_commitishパラメータを指定すれば任意のブランチを指定できる.

name: Create Release

on:push:branches:- main

env:TZ: Asia/Tokyo

permissions:contents: write

jobs:release:runs-on: ubuntu-latest
    steps:- uses: actions/checkout@v4
      - run: echo "TAG_NAME=$(date +'%Y%m%d-%H%M')">> ${GITHUB_ENV}
      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:tag_name: ${{ env.TAG_NAME }}
          generate_release_notes:truetarget_commitish: main

動作確認

個人的に Terraform の検証をするのに使っているサンドボックスリポジトリに導入してみて,2週間ほど運用しているけど,期待通りにリリースノートを作れている👏

これは便利だ〜 \( 'ω')/

S3 Object Lambda に入門できるチュートリアル「Using Amazon S3 Object Lambda to Dynamically Watermark Images as They Are Retrieved」

$
0
0

最近アーキテクチャレビュー会をしているときに「そういえば Amazon S3 Object Lambda ってあったな〜」という話になった😀

Amazon S3 Object Lambda は 2021年にリリースされてて,リリース直後に試した記憶はあるけど最近は使う機会がなかったな〜と思って,復習も兼ねて試してみることにした.

docs.aws.amazon.com

探したら楽しそうなチュートリアル「Using Amazon S3 Object Lambda to Dynamically Watermark Images as They Are Retrieved」があったのでやってみた❗️

aws.amazon.com

何をするか

チュートリアルでは Amazon S3 Object Lambda を使って Amazon S3 にアップロードしてある画像を取得するときに透かし(ウォーターマーク)を追加して返す👌固定の透かしであれば事前に生成できるけど,透かしとして使う文字列がダイナミックに変化するような場合はリクエスト単位で生成したくなったりする.画像ではないけど電子書籍 (PDF) を購入するとメールアドレスが透かしで入っていることがあったりもしてそういう感じ📕

以下のような画像を取得できる❗️ちなみにチュートリアルでは「AWS ロゴ画像」に透かしを入れていて,何となく権利的に嫌だな〜😇と思って今回は適当に画像を作った.あと Watermarkという固定文字列ではなく日付 (UTC) を追加するようにしてみた.

手順

チュートリアルの手順は以下のような感じ📝

  • Step 1: Create an Amazon S3 bucket
  • Step 2: Upload an object
  • Step 3: Create an S3 Access Point
  • Step 4: Create the Lambda function
  • Step 5: Create an S3 Object Lambda Access Point
  • Step 6: Download images from the S3 Object Lambda Access Point
  • Step 7: Clean up resources

ちなみに AWS Lambda 関数で透かしを追加するのは Python の Pillowライブラリを使っている🎨

追加課題

チュートリアルを楽しむために今回は個人的な追加課題として以下にも取り組んだ💪

  • AWS Lambda 関数のランタイムを Python 3.9 → Python 3.12 に変更する
  • Pillow ライブラリの AWS Lambda Layer を作らずに Klayersを使う
  • マネジメントコンソールを使わずに AWS SAM でデプロイする
  • 透かし(ウォーターマーク)を固定文字列ではなく日付 (UTC) にする

github.com

👾 template.yaml

AWS SAM テンプレートは以下のようにした📝(IAM Role は省略して既にあるものを使っている)

あと Klayers の ARN は Python 3.12(東京リージョン)AWS Lambda Layer 一覧で取得した👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:Bucket:Type: AWS::S3::Bucket
    Properties:BucketName: sandbox-s3-object-lambda-hands-on
  AccessPoint:Type: AWS::S3::AccessPoint
    Properties:Bucket:!Ref Bucket
      Name: sandbox-s3-object-lambda-hands-on
  ObjectLambdaAccessPoint:Type: AWS::S3ObjectLambda::AccessPoint
    Properties:Name: sandbox-s3-object-lambda-hands-on
      ObjectLambdaConfiguration:SupportingAccessPoint:!GetAtt AccessPoint.Arn
        TransformationConfigurations:- Actions:- GetObject
            ContentTransformation:AwsLambda:FunctionArn:!GetAtt Function.Arn
  Function:Type: AWS::Serverless::Function
    Properties:FunctionName: sandbox-s3-object-lambda-hands-on
      CodeUri: src/
      Handler: app.handler
      Runtime: python3.12
      MemorySize:256Timeout:10Architectures:- x86_64
      Role: arn:aws:iam::000000000000:role/xxxxx
      Layers:- arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p312-pillow:2

👾 src/app.py

AWS Lambda 関数コードは基本的にそのまま使いつつ,2個所修正した✔️

1つ目は Pillow で AttributeError: 'ImageDraw' object has no attribute 'textsize'というエラーが出るところ💨

-text_width, text_height = d.textsize(text, font)+text_bbox = d.textbbox((0, 0), text, font=font)+text_width = text_bbox[2] - text_bbox[0]+text_height = text_bbox[3] - text_bbox[1]

2つ目は透かしを日付 (UTC) にするところ📅

-img_result = add_watermark(img, parsed_qs.get('X-Amz-watermark', ['Watermark'])[0])+img_result = add_watermark(img, parsed_qs.get('X-Amz-watermark', [str(datetime.datetime.now())])[0])

最終的なディレクトリ構成は以下のようにした.

$ tree .
.
├── README.md
├── samconfig.toml
├── src
│   ├── AmazonEmber_Rg.ttf
│   └── app.py
└── template.yaml

2 directories, 5 files

動作確認

$ sam build
$ sam deploy

Amazon S3 Access Points・Amazon S3 Object Lambda Access Points などをデプロイできた👌(既にリソースは削除してある)

そして Amazon S3 バケットに kakakakakku-blog.pngをアップロードしてから画像を取得する❗️--bucketには Amazon S3 バケット名ではなく「Amazon S3 Object Lambda Access Points エイリアス」を設定する.

$ aws s3api get-object \--bucket sandbox-s3-object-la-sdsnt9xz1hsyxe4854zs8yfkapn1a--ol-s3 \--key kakakakakku-blog.png \
  watermarked-kakakakakku-blog.png

kakakakakku-blog.png

watermarked-kakakakakku-blog.png

動いたー \( 'ω')/

関連記事

aws.amazon.com

aws.amazon.com

OpenSearch Service に入門できるチュートリアル「Creating a search application with Amazon OpenSearch Service」を LocalStack で試そう

$
0
0

Amazon OpenSearch Service に入門したいな〜💡っていうときに活用できる公式チュートリアル「Creating a search application with Amazon OpenSearch Service」を最近仕事で紹介する機会があって,今回改めて自分でも試しつつ,さらに LocalStack で動かせるようにしてみた❗️

Amazon OpenSearch Service に入門したいけどコストが気になる〜という話をたまに聞くことがあって,LocalStack なら AWS アカウントがなくてもローカル環境でサクッと試せるので便利なのだ💪

docs.aws.amazon.com

何をするか

Amazon API Gateway と AWS Lambda 関数で Amazon OpenSearch Service から「映画」の検索結果を返す API を実装する🎥さらに簡易的なウェブサイトから API を実行して検索結果を表示する.ザッとアーキテクチャ図を描くと以下のようになる.

手順

チュートリアルの手順は以下のような感じ📝 Amazon OpenSearch Service ドメインをセットアップしてある前提になっているけど,今回は AWS SAM (AWS CloudFormation) でセットアップする.あと LocalStack で試すため IAM 関連は細かく気にしなくて OK👌

  • Step 1: Index sample data
  • Step 2: Create and deploy the Lambda function
  • Step 3: Create the API in API Gateway
  • Step 4: (Optional) Modify the domain access policy
  • Step 5: Test the web application

追加課題

チュートリアルを楽しむために今回は個人的な追加課題として以下にも取り組んだ💪

  • LocalStack で動かせるようにする
  • AWS Lambda 関数のランタイムを Python 3.9 → Python 3.12 に変更する
  • マネジメントコンソールを使わずに AWS SAM でデプロイする
  • AWS Lambda 関数コードの Amazon OpenSearch Service ドメイン設定を環境変数経由にする

試したコードと手順は GitHub にまとめてある❗️

github.com

👾 template.yaml

AWS SAM で Amazon OpenSearch Service / Amazon API Gateway / AWS Lambda 関数をデプロイする.Amazon OpenSearch Service の詳細な設定はせず,単純にドメイン名のみ指定している👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:OpenSearch:Type: AWS::OpenSearchService::Domain
    Properties:DomainName: movies
  Function:Type: AWS::Serverless::Function
    Properties:FunctionName: opensearch-function
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
      Environment:Variables:DOMAIN_ENDPOINT:!Sub http://${OpenSearch.DomainEndpoint}
      Events:Api:Type: Api
          Properties:Method: GET
            Path: /

Outputs:ApiId:Value:!Ref ServerlessRestApi

👾 src/app.py

AWS Lambda 関数コードは基本的にそのまま使いつつ,Amazon OpenSearch Service ドメイン設定を書き換える部分は環境変数で自動的に設定されるようにした✔️

-host = '' # The OpenSearch domain endpoint with https:// and without a trailing slash+host = os.environ['DOMAIN_ENDPOINT'] # The OpenSearch domain endpoint with https:// and without a trailing slash

動作確認

LocalStack を起動して,Amazon OpenSearch Service / Amazon API Gateway / AWS Lambda 関数をデプロイする.

$ samlocal build
$ samlocal deploy

そして curlコマンドで映画データセットを投入して,Amazon API Gateway に HTTP リクエストを送信するウェブサイトの JavaScript を修正すれば OK👌

$ curl -X POST -s-H'Content-Type: application/json' http://movies.ap-northeast-1.opensearch.localhost.localstack.cloud:4566/_bulk \--data-binary @datasets/sample-movies.bulk

以下は iron manと検索した結果で Amazon OpenSearch Service を使った検索アプリケーションを LocalStack で動かせた❗️

やったー \( 'ω')/

Amazon OpenSearch Service に入門したいけどコストが気になる〜という場合は LocalStack を使ってみたら良いかも〜😀

Viewing all 902 articles
Browse latest View live