kakakakakku blog Weekly Tech Blog: Keep on Learning! 2024-03-29T15:59:11+09:00 kakku22 Hatena::Blog hatenablog://blog/12921228815711368277 Terraform で無料利用枠の VPC IP Address Manager (IPAM) を設定する hatenablog://entry/6801883189094419172 2024-03-29T15:59:11+09:00 2024-03-29T15:59:11+09:00 2023年11月から VPC IP Address Manager (IPAM) に「無料枠利用枠」が追加されて Public IP Insights などの機能が無料で使えるようになった💡そして,2024年2月から課金対象になった IPv4 の最適化のために Public IP Insights を使いたいという場面もあると思う. aws.amazon.com aws.amazon.com Terraform で試す 実は Terraform AWS Provider では今まで aws_vpc_ipam の tier はサポートされていなかった.今日(2024年3月29日)にリリースされた… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240329/20240329135452.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>2023年11月から <strong>VPC IP Address Manager (IPAM)</strong> に<strong>「無料枠利用枠」</strong>が追加されて <strong>Public IP Insights</strong> などの機能が無料で使えるようになった💡そして,2024年2月から課金対象になった IPv4 の最適化のために Public IP Insights を使いたいという場面もあると思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fabout-aws%2Fwhats-new%2F2023%2F11%2Famazon-vpc-address-manager-free-features-tier%2F" title="Amazon VPC IP Address Manager に AWS Organization 全体の Public IP Insights を含む機能の無料利用枠が追加" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/about-aws/whats-new/2023/11/amazon-vpc-address-manager-free-features-tier/">aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Fnews%2Fidentify-and-optimize-public-ipv4-address-usage-on-aws%2F" title="AWS におけるパブリック IPv4 アドレスの使用状況の特定と最適化 | Amazon Web Services" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/blogs/news/identify-and-optimize-public-ipv4-address-usage-on-aws/">aws.amazon.com</a></cite></p> <h1 id="Terraform-で試す">Terraform で試す</h1> <p>実は Terraform AWS Provider では今まで <code>aws_vpc_ipam</code> の <code>tier</code> はサポートされていなかった.今日(2024年3月29日)にリリースされた <strong>v5.43.0</strong> でついにサポートされた❗️待ってました〜 \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fhashicorp%2Fterraform-provider-aws%2Freleases%2Ftag%2Fv5.43.0" title="Release v5.43.0 · hashicorp/terraform-provider-aws" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.43.0">github.com</a></cite></p> <h2 id="-ipamtf">👾 ipam.tf</h2> <p>設定自体は簡単で <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam">aws_vpc_ipam</a> に <code>tier = "free"</code> を追加すれば OK👌デフォルトは <code>advanced</code> なので注意しておくと良さそう.あと今回は <code>operating_regions</code> にバージニア北部リージョンを設定した💡もちろん複数リージョンを設定することもできる.</p> <pre class="code lang-hcl" data-lang="hcl" data-unlink><span class="synType">resource</span> <span class="synConstant">&quot;aws_vpc_ipam&quot;</span> <span class="synConstant">&quot;main&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">tier</span> = <span class="synConstant">&quot;free&quot;</span> <span class="synType">operating_regions</span> <span class="synSpecial">{</span> <span class="synIdentifier">region_name</span> = <span class="synConstant">&quot;us-east-1&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> </pre> <h1 id="関連記事">関連記事</h1> <p>AWS CDK で <strong>VPC IP Address Manager (IPAM)</strong> を設定する記事は過去に書いているので参考まで〜📝</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F01%2F04%2F134823" title="AWS CDK で VPC IP Address Manager (IPAM) を設定する - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/01/04/134823">kakakakakku.hatenablog.com</a></cite></p> kakku22 AWS CDK で外部パッケージを含む Python の AWS Lambda 関数をデプロイする hatenablog://entry/6801883189088973063 2024-03-27T20:16:53+09:00 2024-03-27T20:16:53+09:00 AWS CDK で外部パッケージを含む Python の AWS Lambda 関数をデプロイする場合,requirements.txt から依存関係を解決して,デプロイするアセットとして ZIP にまとめる(バンドルする)必要がある💡 今回は aws-cdk-lib.aws_lambda module と @aws-cdk/aws-lambda-python-alpha module を使う方法を試す❗️ 前提 今回はサンプルとして requests に依存したコードを以下のディレクトリ構成で置いてある前提とする \( 'ω')/ functions/requests ├── app.py … <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240307/20240307221910.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK で外部パッケージを含む Python の AWS Lambda 関数をデプロイする場合,<code>requirements.txt</code> から依存関係を解決して,デプロイするアセットとして ZIP にまとめる(バンドルする)必要がある💡</p> <p>今回は <a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html">aws-cdk-lib.aws_lambda module</a> と <a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-python-alpha-readme.html">@aws-cdk/aws-lambda-python-alpha module</a> を使う方法を試す❗️</p> <h1 id="前提">前提</h1> <p>今回はサンプルとして <a href="https://pypi.org/project/requests/">requests</a> に依存したコードを以下のディレクトリ構成で置いてある前提とする \( 'ω')/</p> <pre class="code" data-lang="" data-unlink>functions/requests ├── app.py └── requirements.txt</pre> <h1 id="aws_lambda-module-を使う">aws_lambda module を使う</h1> <p>まず,AWS CDK で AWS Lambda 関数をデプロイするときによく使う <strong>aws_lambda module</strong> では <code>Code.fromAsset</code> のオプションとして <strong>BundlingOptions</strong> を設定できる.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.BundlingOptions.html" title="interface BundlingOptions · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.BundlingOptions.html">docs.aws.amazon.com</a></cite></p> <p>実装としてはザッとこんな感じになる 👾</p> <p>Python のコンテナ内で <code>pip install</code> コマンドを実行して <code>/asset-output</code> ディレクトリに依存関係をインストールしたら,後はそのまま ZIP にまとめて(バンドルして)デプロイされる👌</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">new</span> aws_lambda.<span class="synSpecial">Function</span><span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'PythonFunction'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> functionName: <span class="synConstant">'sandbox-python-function'</span><span class="synStatement">,</span> runtime: aws_lambda.Runtime.PYTHON_3_12<span class="synStatement">,</span> handler: <span class="synConstant">'app.lambda_handler'</span><span class="synStatement">,</span> code: aws_lambda.Code.fromAsset<span class="synStatement">(</span>path.join<span class="synStatement">(</span><span class="synSpecial">__dirname</span><span class="synStatement">,</span> <span class="synConstant">'../functions/requests'</span><span class="synStatement">),</span> <span class="synIdentifier">{</span> bundling: <span class="synIdentifier">{</span> image: aws_lambda.Runtime.PYTHON_3_12.bundlingImage<span class="synStatement">,</span> command: <span class="synIdentifier">[</span> <span class="synConstant">'bash'</span><span class="synStatement">,</span> <span class="synConstant">'-c'</span><span class="synStatement">,</span> <span class="synConstant">'pip install -r requirements.txt -t /asset-output &amp;&amp; cp -au . /asset-output'</span> <span class="synIdentifier">]</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> </pre> <h1 id="aws-lambda-python-alpha-module-を使う">aws-lambda-python-alpha module を使う</h1> <p>まだ alpha ではあるけど <strong>aws-lambda-python-alpha module</strong> を使うと <code>entry</code> に指定したディレクトリにある <code>requirements.txt</code> や <code>Pipfile</code> から自動的に依存関係を解決してくれる👌</p> <p>実装としてはザッとこんな感じになる 👾</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">new</span> aws_lambda_python_alpha.PythonFunction<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'PythonFunctionAlpha'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> functionName: <span class="synConstant">'sandbox-python-function-alpha'</span><span class="synStatement">,</span> runtime: aws_lambda.Runtime.PYTHON_3_12<span class="synStatement">,</span> index: <span class="synConstant">'app.py'</span><span class="synStatement">,</span> handler: <span class="synConstant">'lambda_handler'</span><span class="synStatement">,</span> entry: path.join<span class="synStatement">(</span><span class="synSpecial">__dirname</span><span class="synStatement">,</span> <span class="synConstant">'../functions/requests'</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> </pre> <p><strong>aws_lambda.Function</strong> のプロパティもサポートされてるし,<a href="https://docs.aws.amazon.com/cdk/api/v2/docs/@aws-cdk_aws-lambda-python-alpha.PythonLayerVersion.html">Lambda Layer</a> も簡単にデプロイできるし便利〜 \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-lambda-python-alpha-readme.html" title="@aws-cdk/aws-lambda-python-alpha module · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-python-alpha-readme.html">docs.aws.amazon.com</a></cite></p> <h1 id="デプロイ確認">デプロイ確認</h1> <p>期待通りに <code>requirements.txt</code> に定義した <code>requests</code> をデプロイできている❗️</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240307/20240307220350.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> kakku22 JSON Schema で簡単にバリデーションを実装できる Powertools for AWS Lambda (Python) の Validation hatenablog://entry/6801883189091570347 2024-03-21T19:54:32+09:00 2024-03-21T19:54:32+09:00 Powertools for AWS Lambda (Python) の「Validation」を使うと AWS Lambda 関数に渡されたイベント情報のバリデーションを JSON Schema に沿って実現できる.例えば,必須パラメータ・文字数制限・ENUM・正規表現などをチェックできる👌 Powertools for AWS Lambda (Python) 自体は Tracer / Logger / Event Source Data Classes などをよく使うけど,Validation は今まで活用できてなく,試してみたらとても便利だったので,今回試した結果を簡単にまとめておく \… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240318/20240318162344.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><strong>Powertools for AWS Lambda (Python)</strong> の<strong>「Validation」</strong>を使うと AWS Lambda 関数に渡されたイベント情報のバリデーションを <a href="https://json-schema.org/">JSON Schema</a><a href="https://b.hatena.ne.jp/entry/https://json-schema.org/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://json-schema.org/" alt="" class="http-bookmark" /></a> に沿って実現できる.例えば,必須パラメータ・文字数制限・ENUM・正規表現などをチェックできる👌</p> <p>Powertools for AWS Lambda (Python) 自体は Tracer / Logger / Event Source Data Classes などをよく使うけど,Validation は今まで活用できてなく,試してみたらとても便利だったので,今回試した結果を簡単にまとめておく \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.powertools.aws.dev%2Flambda%2Fpython%2Flatest%2Futilities%2Fvalidation%2F" title="Validation - Powertools for AWS Lambda (Python)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/">docs.powertools.aws.dev</a></cite></p> <h1 id="検証環境">検証環境</h1> <p>今回は AWS SAM を使って Amazon API Gateway (REST API) と AWS Lambda 関数を構築する.あくまでサンプルとして Amazon API Gateway の <code>/</code> に POST リクエストを送るとバリデーションロジックを含んだ AWS Lambda 関数が実行されるようにした.また Powertools は Lambda Layer でセットアップする.</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">AWSTemplateFormatVersion</span><span class="synSpecial">:</span> 2010-09-09 <span class="synIdentifier">Transform</span><span class="synSpecial">:</span> AWS::Serverless-2016-10-31 <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">Function</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::Function <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">FunctionName</span><span class="synSpecial">:</span> powertools-validation <span class="synIdentifier">CodeUri</span><span class="synSpecial">:</span> src/ <span class="synIdentifier">Handler</span><span class="synSpecial">:</span> app.lambda_handler <span class="synIdentifier">Runtime</span><span class="synSpecial">:</span> python3.12 <span class="synIdentifier">Architectures</span><span class="synSpecial">:</span> <span class="synStatement">- </span>x86_64 <span class="synIdentifier">Layers</span><span class="synSpecial">:</span> <span class="synStatement">- </span>arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:67 <span class="synIdentifier">Events</span><span class="synSpecial">:</span> <span class="synIdentifier">Api</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> Api <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Path</span><span class="synSpecial">:</span> / <span class="synIdentifier">Method</span><span class="synSpecial">:</span> POST </pre> <p>最終的なディレクトリ構成は以下のようになる💡</p> <pre class="code" data-lang="" data-unlink>├── events │   └── event.json ├── samconfig.toml ├── src │   └── app.py └── template.yaml</pre> <h1 id="-apppy">👾 app.py</h1> <p>今回はバリデーションを紹介するサンプルとして,特にロジックはなく Amazon API Gateway への POST リクエストに対して <code>200 OK</code> もしくは <code>400 BAD_REQUEST</code> を返す実装にした.また <a href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/best-practices.html">AWS Lambda &#x95A2;&#x6570;&#x306E;&#x30D9;&#x30B9;&#x30C8;&#x30D7;&#x30E9;&#x30AF;&#x30C6;&#x30A3;&#x30B9;</a>を参考に Handler とロジックを分割して <code>main()</code> にまとめてある(実際にはもっと細かく分割しても良さそう).</p> <p>そして,今回は超簡易的な<strong>「TODO アプリ」</strong>を例として,<code>title / category / description / link</code> というパラメータを受け取る API のバリデーションを実装した.パラメータごとのバリデーション要件は以下のコードの <code>SCHEMA</code> を見てもらえればと❗️さらに Powertools for AWS Lambda (Python) の Validation ではバリデーションロジックを <code>@validator</code> デコレータを使った実装と <code>validate()</code> 関数を使った実装から選べる.どちらも試してみて,個人的には以下の2つの理由から <code>validate()</code> 関数を使うのが良いと思った💡</p> <ul> <li><code>validate()</code> 関数は Lambda コンテキストに依存してなくてローカル開発がしやすかった</li> <li>例外発生時のハンドリングなどを柔軟に実装しやすかった</li> </ul> <p>また <code>validate()</code> 関数を呼び出すときに <code>envelope</code> を指定できて,イベントオブジェクトの中からバリデーションする箇所を限定できる.今回は Amazon API Gateway (REST API) から渡されるイベントをバリデーションするため,<code>envelopes.API_GATEWAY_REST</code> を指定した.すると自動的に <code>body</code> がバリデーション対象になる👌現状は8種類の <code>envelope</code> が提供されている \( 'ω')/</p> <ul> <li>API_GATEWAY_HTTP</li> <li>API_GATEWAY_REST</li> <li>CLOUDWATCH_EVENTS_SCHEDULED</li> <li>CLOUDWATCH_LOGS</li> <li>EVENTBRIDGE</li> <li>KINESIS_DATA_STREAM</li> <li>SNS</li> <li>SQS</li> </ul> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">import</span> json <span class="synPreProc">from</span> aws_lambda_powertools.utilities.validation <span class="synPreProc">import</span> SchemaValidationError, envelopes, validate <span class="synPreProc">from</span> http <span class="synPreProc">import</span> HTTPStatus <span class="synPreProc">from</span> jmespath.exceptions <span class="synPreProc">import</span> JMESPathTypeError SCHEMA = { <span class="synConstant">'$schema'</span>: <span class="synConstant">'http://json-schema.org/draft-07/schema'</span>, <span class="synConstant">'type'</span>: <span class="synConstant">'object'</span>, <span class="synConstant">'required'</span>: [<span class="synConstant">'title'</span>, <span class="synConstant">'category'</span>], <span class="synConstant">'properties'</span>: { <span class="synConstant">'title'</span>: { <span class="synConstant">'type'</span>: <span class="synConstant">'string'</span>, <span class="synConstant">'pattern'</span>: <span class="synConstant">'^[a-zA-Z0-9_]*$'</span> }, <span class="synConstant">'category'</span>: { <span class="synConstant">'type'</span>: <span class="synConstant">'string'</span>, <span class="synConstant">'enum'</span>: [<span class="synConstant">'Python'</span>, <span class="synConstant">'Go'</span>, <span class="synConstant">'Java'</span>] }, <span class="synConstant">'description'</span>: { <span class="synConstant">'type'</span>: <span class="synConstant">'string'</span>, <span class="synConstant">'minLength'</span>: <span class="synConstant">10</span>, <span class="synConstant">'maxLength'</span>: <span class="synConstant">100</span> }, <span class="synConstant">'link'</span>: { <span class="synConstant">'type'</span>: <span class="synConstant">'string'</span>, <span class="synConstant">'format'</span>: <span class="synConstant">'uri'</span> } }, } <span class="synStatement">def</span> <span class="synIdentifier">main</span>(event): <span class="synStatement">try</span>: validate(event=event, schema=SCHEMA, envelope=envelopes.API_GATEWAY_REST) <span class="synStatement">except</span> JMESPathTypeError <span class="synStatement">as</span> e: <span class="synStatement">return</span> { <span class="synConstant">'statusCode'</span>: HTTPStatus.BAD_REQUEST, <span class="synConstant">'body'</span>: json.dumps({<span class="synConstant">'message'</span>: <span class="synIdentifier">str</span>(e)}) } <span class="synStatement">except</span> json.JSONDecodeError <span class="synStatement">as</span> e: <span class="synStatement">return</span> { <span class="synConstant">'statusCode'</span>: HTTPStatus.BAD_REQUEST, <span class="synConstant">'body'</span>: json.dumps({<span class="synConstant">'message'</span>: e.msg}) } <span class="synStatement">except</span> SchemaValidationError <span class="synStatement">as</span> e: <span class="synStatement">return</span> { <span class="synConstant">'statusCode'</span>: HTTPStatus.BAD_REQUEST, <span class="synConstant">'body'</span>: json.dumps({<span class="synConstant">'message'</span>: e.validation_message}) } <span class="synStatement">return</span> { <span class="synConstant">'statusCode'</span>: HTTPStatus.OK, <span class="synConstant">'body'</span>: json.dumps({<span class="synConstant">'message'</span>: <span class="synConstant">'ok'</span>}) } <span class="synStatement">def</span> <span class="synIdentifier">lambda_handler</span>(event, context): <span class="synStatement">return</span> main(event) <span class="synStatement">if</span> __name__ == <span class="synConstant">'__main__'</span>: <span class="synStatement">with</span> <span class="synIdentifier">open</span>(<span class="synConstant">'../events/event.json'</span>, <span class="synConstant">'r'</span>) <span class="synStatement">as</span> f: event = json.load(f) main(event) </pre> <h1 id="動作確認">動作確認</h1> <h2 id="data-must-contain-category-title-properties">data must contain ['category', 'title'] properties</h2> <p>event.json</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{}</span> </pre> <p>必須の <code>title</code> と <code>category</code> プロパティがなくバリデーションエラー🙅‍♂️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-X</span> POST <span class="synSpecial">--data</span> @event.json <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">message</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">data must contain ['category', 'title'] properties</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> <h2 id="datatitle-must-match-pattern-a-zA-Z0-9_">data.title must match pattern ^[a-zA-Z0-9_]*$</h2> <p>event.json</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">title</span>&quot;: &quot;<span class="synConstant">Powertoolsを試す</span>&quot;, &quot;<span class="synStatement">category</span>&quot;: &quot;<span class="synConstant">Python</span>&quot; <span class="synSpecial">}</span> </pre> <p><code>title</code> に日本語を含んでいるためバリデーションエラー🙅‍♂️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-X</span> POST <span class="synSpecial">--data</span> @event.json <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">message</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">data.title must match pattern ^[a-zA-Z0-9_]*$</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> <h2 id="datacategory-must-be-one-of-Python-Go-Java">data.category must be one of ['Python', 'Go', 'Java']</h2> <p>event.json</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">title</span>&quot;: &quot;<span class="synConstant">Powertools</span>&quot;, &quot;<span class="synStatement">category</span>&quot;: &quot;<span class="synConstant">AWS</span>&quot; <span class="synSpecial">}</span> </pre> <p><code>category</code> の ENUM 以外の値を指定しているためバリデーションエラー🙅‍♂️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-X</span> POST <span class="synSpecial">--data</span> @event.json <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">message</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">data.category must be one of ['Python', 'Go', 'Java']</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> <h2 id="datadescription-must-be-longer-than-or-equal-to-10-characters">data.description must be longer than or equal to 10 characters</h2> <p>event.json</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">title</span>&quot;: &quot;<span class="synConstant">Powertools</span>&quot;, &quot;<span class="synStatement">category</span>&quot;: &quot;<span class="synConstant">Python</span>&quot;, &quot;<span class="synStatement">description</span>&quot;: &quot;&quot; <span class="synSpecial">}</span> </pre> <p><code>description</code> が10文字以上になっていないためバリデーションエラー🙅‍♂️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-X</span> POST <span class="synSpecial">--data</span> @event.json <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">message</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">data.description must be longer than or equal to 10 characters</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> <h2 id="datalink-must-be-uri">data.link must be uri</h2> <p>event.json</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">title</span>&quot;: &quot;<span class="synConstant">Powertools</span>&quot;, &quot;<span class="synStatement">category</span>&quot;: &quot;<span class="synConstant">Python</span>&quot;, &quot;<span class="synStatement">description</span>&quot;: &quot;<span class="synConstant">Try Powertools for AWS Lambda.</span>&quot;, &quot;<span class="synStatement">link</span>&quot;: &quot;<span class="synConstant">docs.powertools.aws.dev</span>&quot; <span class="synSpecial">}</span> </pre> <p><code>link</code> が URL 形式になっていないためバリデーションエラー🙅‍♂️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-X</span> POST <span class="synSpecial">--data</span> @event.json <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">message</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">data.link must be uri</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> <h1 id="まとめ">まとめ</h1> <p><strong>Powertools for AWS Lambda (Python)</strong> の<strong>「Validation」</strong>を使うと AWS Lambda 関数に渡されたイベント情報のバリデーションを柔軟に実装できてとても便利だった❗️</p> <p>今後は積極的に使っていくぞー \( 'ω')/</p> <h1 id="関連リンク">関連リンク🔗</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws-powertools%2Fpowertools-lambda-python" title="GitHub - aws-powertools/powertools-lambda-python: A developer toolkit to implement Serverless best practices and increase developer velocity." class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws-powertools/powertools-lambda-python">github.com</a></cite></p> kakku22 testcontainers-python: pytest 実行時に使い捨て可能な LocalStack を起動する hatenablog://entry/6801883189091414349 2024-03-19T08:47:50+09:00 2024-03-19T08:47:50+09:00 Testcontainers を使うと,テストコードを実行するときに必要になるデータベース・キャッシュ・キューなどの依存関係をコード上で管理できて,実行後にはコンテナを自動的に消してくれるという使い捨て可能な仕組みを簡単に作れる❗️Testcontainers のサイトに載っている「Test dependencies as code」という表現はピッタリだと思う👌 testcontainers.com Testcontainers は Java / Go / .NET / Rust など多くの言語をサポートしているけど,今回は Python 用の testcontainers-python を… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240317/20240317135016.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><strong>Testcontainers</strong> を使うと,テストコードを実行するときに必要になるデータベース・キャッシュ・キューなどの依存関係をコード上で管理できて,実行後にはコンテナを自動的に消してくれるという使い捨て可能な仕組みを簡単に作れる❗️Testcontainers のサイトに載っている<strong>「Test dependencies as code」</strong>という表現はピッタリだと思う👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftestcontainers.com%2F" title="Testcontainers" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://testcontainers.com/">testcontainers.com</a></cite></p> <p>Testcontainers は Java / Go / .NET / Rust など多くの言語をサポートしているけど,今回は Python 用の <strong>testcontainers-python</strong> を試してみた.検証に使ったコードを紹介しつつ,簡単にまとめておく✍</p> <p>また Testcontainers Cloud もあったりする🌩</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftestcontainers.com%2Fcloud%2F" title="Testcontainers Cloud" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://testcontainers.com/cloud/">testcontainers.com</a></cite></p> <h1 id="前提">前提</h1> <p>今回は以下の前提で試す💡なお testcontainers-python は MySQL / PostgreSQL / Redis / Kafka / RabbitMQ / LocalStack など多くの依存関係をサポートしているので,組み合わせてテストコードを実行することもできる.今回は <strong>LocalStack</strong> に限定する.</p> <ul> <li>Amazon DynamoDB を操作するコードをテストする</li> <li>Amazon DynamoDB のエミュレーターとして <a href="https://www.localstack.cloud/">LocalStack</a><a href="https://b.hatena.ne.jp/entry/https://www.localstack.cloud/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.localstack.cloud/" alt="" class="http-bookmark" /></a> を使う</li> <li>テストフレームワークとして <a href="https://pytest.org/">pytest</a><a href="https://b.hatena.ne.jp/entry/https://pytest.org/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://pytest.org/" alt="" class="http-bookmark" /></a> を使う</li> <li><a href="https://testcontainers-python.readthedocs.io/">testcontainers-python</a><a href="https://b.hatena.ne.jp/entry/https://testcontainers-python.readthedocs.io/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://testcontainers-python.readthedocs.io/" alt="" class="http-bookmark" /></a> の <code>LocalStackContainer</code> を使う</li> </ul> <p>ディレクトリ構成は以下のようにした.特に決まってなく自由に変更できる👌</p> <pre class="code" data-lang="" data-unlink>. ├── README.md ├── pyproject.toml ├── requirements-test.txt ├── src │ └── app.py ├── tests │ └── test_app.py └── venv</pre> <h1 id="ドキュメント">ドキュメント</h1> <p>testcontainers-python に関しては以下のドキュメントを読むとイメージがつかめると思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftestcontainers-python.readthedocs.io%2Fen%2Flatest%2Flocalstack%2FREADME.html" title="&lt;no title&gt; — testcontainers 2.0.0 documentation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://testcontainers-python.readthedocs.io/en/latest/localstack/README.html">testcontainers-python.readthedocs.io</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftestcontainers.com%2Fguides%2Fgetting-started-with-testcontainers-for-python%2F" title="Getting started with Testcontainers for Python" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://testcontainers.com/guides/getting-started-with-testcontainers-for-python/">testcontainers.com</a></cite></p> <p>しかし,残念ながら testcontainers-python の <code>LocalStackContainer</code> に関するドキュメントはほとんどなく(見つけられず)実際に GitHub のコードを読みながらメソッドなどを探したりしていた.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Ftestcontainers%2Ftestcontainers-python%2Ftree%2Fmain%2Fmodules%2Flocalstack" title="testcontainers-python/modules/localstack at main · testcontainers/testcontainers-python" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/testcontainers/testcontainers-python/tree/main/modules/localstack">github.com</a></cite></p> <h1 id="サンプルコード">サンプルコード</h1> <h2 id="-apppy">👾 app.py</h2> <p>コードには特に意味はないけど,Amazon DynamoDB の <code>Forum</code> テーブルからアイテムを取得する <code>search_forum()</code> 関数を今回のテスト対象とする.<code>Forum</code> テーブルというのは <a href="https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/AppendixSampleTables.html">Amazon DynamoDB &#x306E;&#x30C9;&#x30AD;&#x30E5;&#x30E1;&#x30F3;&#x30C8;</a><a href="https://b.hatena.ne.jp/entry/https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/AppendixSampleTables.html" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/AppendixSampleTables.html" alt="" class="http-bookmark" /></a>に載っているサンプルでそのまま使うことにした.</p> <p>そして,boto3 client は環境変数 <code>ENV</code> によって3種類作れるようにしてある👌</p> <ul> <li><code>local</code>: ローカル開発用 (LocalStack)</li> <li><code>test</code>: テスト用 (testcontainers-python / LocalStack)</li> <li>その他: 実際の AWS アカウント</li> </ul> <p>また testcontainers-python の <code>LocalStackContainer</code> を使うと <code>http://localhost:63033</code> や <code>http://localhost:63058</code> など実行時のポートが変化するため,後述する <code>test_app.py</code> で環境変数 <code>TESTCONTAINERS_LOCALSTACK_ENDPOINT_URL</code> を設定することにした👌もっとイイ方法もありそう.ちなみに <code>LocalStackContainer</code> の <code>edge_port</code> は <code>0.0.0.0:63167-&gt;24566/tcp</code> のようにネットワークが構成されるため "コンテナ側のポート" を指定するオプションだった.</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">import</span> boto3 <span class="synPreProc">import</span> os TABLE_NAME = <span class="synConstant">'Forum'</span> <span class="synStatement">if</span> os.environ[<span class="synConstant">'ENV'</span>] == <span class="synConstant">'local'</span>: dynamodb = boto3.client(<span class="synConstant">'dynamodb'</span>, endpoint_url=<span class="synConstant">'http://localhost:4566'</span>) <span class="synStatement">elif</span> os.environ[<span class="synConstant">'ENV'</span>] == <span class="synConstant">'test'</span>: dynamodb = boto3.client(<span class="synConstant">'dynamodb'</span>, endpoint_url=os.environ[<span class="synConstant">'TESTCONTAINERS_LOCALSTACK_ENDPOINT_URL'</span>]) <span class="synStatement">else</span>: dynamodb = boto3.client(<span class="synConstant">'dynamodb'</span>) <span class="synStatement">def</span> <span class="synIdentifier">search_forum</span>(name): <span class="synStatement">return</span> dynamodb.get_item( TableName=TABLE_NAME, Key={<span class="synConstant">'Name'</span>: {<span class="synConstant">'S'</span>: name}} ) </pre> <h2 id="-test_apppy">👾 test_app.py</h2> <p>pytest 実行時に呼び出すフィクスチャとして <code>@pytest.fixture</code> デコレータで <code>setup()</code> 関数を実装した.<code>LocalStackContainer</code> で LocalStack のコンテナイメージを設定しているため,テストコードを実行する前に自動的に LocalStack コンテナが起動される👌ちなみに <code>LocalStackContainer</code> の <code>get_client()</code> 関数は boto3 client を返しているため,boto3 に慣れていれば普段と同じように実装できる.</p> <p>そして,Amazon DynamoDB テーブル <code>Forum</code> を作って,サンプルデータ(アイテム)を1つ登録している.テスト観点によっては <a href="https://github.com/joke2k/faker">Faker</a><a href="https://b.hatena.ne.jp/entry/https://github.com/joke2k/faker" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://github.com/joke2k/faker" alt="" class="http-bookmark" /></a> などを使ってリアルなサンプルデータを登録すると良さそう.</p> <p>最後にテストケースとしては <code>search_forum()</code> 関数を呼び出して<strong>「アイテムを取得できる場合」</strong>と<strong>「アイテムを取得できない場合」</strong>を確認している✔️</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">import</span> os <span class="synPreProc">import</span> pytest <span class="synPreProc">from</span> testcontainers.localstack <span class="synPreProc">import</span> LocalStackContainer TABLE_NAME = <span class="synConstant">'Forum'</span> <span class="synPreProc">@</span><span class="synIdentifier">pytest.fixture</span>(scope=<span class="synConstant">'module'</span>, autouse=<span class="synIdentifier">True</span>) <span class="synStatement">def</span> <span class="synIdentifier">setup</span>(): <span class="synStatement">with</span> LocalStackContainer(image=<span class="synConstant">'localstack/localstack:3'</span>, region_name=<span class="synConstant">'ap-northeast-1'</span>) <span class="synStatement">as</span> localstack: os.environ[<span class="synConstant">'TESTCONTAINERS_LOCALSTACK_ENDPOINT_URL'</span>] = localstack.get_url() dynamodb = localstack.get_client(<span class="synConstant">'dynamodb'</span>) dynamodb.create_table( TableName=TABLE_NAME, KeySchema=[ { <span class="synConstant">'AttributeName'</span>: <span class="synConstant">'Name'</span>, <span class="synConstant">'KeyType'</span>: <span class="synConstant">'HASH'</span>, } ], AttributeDefinitions=[ { <span class="synConstant">'AttributeName'</span>: <span class="synConstant">'Name'</span>, <span class="synConstant">'AttributeType'</span>: <span class="synConstant">'S'</span>, } ], BillingMode=<span class="synConstant">'PAY_PER_REQUEST'</span>, ) item = { <span class="synConstant">'Name'</span>: {<span class="synConstant">'S'</span>: <span class="synConstant">'Amazon DynamoDB'</span>}, <span class="synConstant">'Category'</span>: {<span class="synConstant">'S'</span>: <span class="synConstant">'Amazon Web Services'</span>}, <span class="synConstant">'Threads'</span>: {<span class="synConstant">'N'</span>: <span class="synConstant">'2'</span>}, <span class="synConstant">'Messages'</span>: {<span class="synConstant">'N'</span>: <span class="synConstant">'4'</span>}, <span class="synConstant">'Views'</span>: {<span class="synConstant">'N'</span>: <span class="synConstant">'1000'</span>}, } dynamodb.put_item(TableName=TABLE_NAME, Item=item) <span class="synStatement">yield</span> localstack <span class="synStatement">def</span> <span class="synIdentifier">test_search_forum</span>(): <span class="synPreProc">from</span> app <span class="synPreProc">import</span> search_forum item = search_forum(<span class="synConstant">'Amazon DynamoDB'</span>) <span class="synStatement">assert</span> item[<span class="synConstant">'Item'</span>][<span class="synConstant">'Category'</span>][<span class="synConstant">'S'</span>] == <span class="synConstant">'Amazon Web Services'</span> <span class="synStatement">assert</span> item[<span class="synConstant">'Item'</span>][<span class="synConstant">'Views'</span>][<span class="synConstant">'N'</span>] == <span class="synConstant">'1000'</span> item = search_forum(<span class="synConstant">'Amazon S3'</span>) <span class="synStatement">assert</span> <span class="synConstant">'Item'</span> <span class="synStatement">not</span> <span class="synStatement">in</span> item </pre> <p>testcontainers-python と LocalStack の検証はザッとこんな感じ \( 'ω')/</p> <h2 id="動作確認">動作確認</h2> <p>期待通り実行できた👏</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ <span class="synIdentifier">ENV</span>=test pytest <span class="synSpecial">-p</span> no:warnings <span class="synStatement">===========================================================================================</span> <span class="synStatement">test</span> session starts <span class="synStatement">============================================================================================</span> (中略) configfile: pyproject.toml collected <span class="synConstant">1</span> item tests/test_app.py<span class="synStatement"> . </span> <span class="synStatement">[</span><span class="synConstant">100</span>%<span class="synStatement">]</span> <span class="synStatement">============================================================================================</span> <span class="synConstant">1</span> passed <span class="synError">in</span> <span class="synConstant">4</span>.97s <span class="synStatement">=============================================================================================</span> </pre> <p>現時点だと Python 3.12 で boto3 の DeprecationWarning が出るため <code>-p no:warnings</code> で抑止している🛑</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fboto%2Fboto3%2Fissues%2F3889" title="Boto3 UTC DeprecationWarnings · Issue #3889 · boto/boto3" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/boto/boto3/issues/3889">github.com</a></cite></p> kakku22 Lambda オーソライザーのポリシーを簡単に出力できる Powertools for AWS Lambda (Python) の APIGatewayAuthorizerResponse hatenablog://entry/6801883189089871865 2024-03-18T08:32:29+09:00 2024-03-18T08:32:29+09:00 Amazon API Gateway の Lambda オーソライザー(旧カスタムオーソライザー)を使ってアクセス制御をするときに Lambda オーソライザーの仕様に沿ったポリシーを出力する必要がある💡詳しくは以下のドキュメントに載っている. docs.aws.amazon.com 今まではドキュメントに載っているコードを参考に実装することが多かったけど,Powertools for AWS Lambda (Python) の Event Source Data Classes で APIGatewayAuthorizerResponse を使うと比較的簡単にポリシーを出力できて良かった❗️… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240311/20240311143238.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>Amazon API Gateway の Lambda オーソライザー(旧カスタムオーソライザー)を使ってアクセス制御をするときに Lambda オーソライザーの仕様に沿ったポリシーを出力する必要がある💡詳しくは以下のドキュメントに載っている.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fapigateway%2Flatest%2Fdeveloperguide%2Fapigateway-use-lambda-authorizer.html" title="API Gateway Lambda オーソライザーを使用する - Amazon API Gateway" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html">docs.aws.amazon.com</a></cite></p> <p>今まではドキュメントに載っているコードを参考に実装することが多かったけど,<strong>Powertools for AWS Lambda (Python)</strong> の <strong>Event Source Data Classes</strong> で <code>APIGatewayAuthorizerResponse</code> を使うと比較的簡単にポリシーを出力できて良かった❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.powertools.aws.dev%2Flambda%2Fpython%2Flatest%2Futilities%2Fdata_classes%2F" title="Event Source Data Classes - Powertools for AWS Lambda (Python)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.powertools.aws.dev/lambda/python/latest/utilities/data_classes/">docs.powertools.aws.dev</a></cite></p> <h2 id="サンプルコード">サンプルコード</h2> <p>今回トークンベースの Lambda オーソライザーで検証したときに使ったコードを載せておく📝</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">from</span> aws_lambda_powertools.utilities.data_classes <span class="synPreProc">import</span> event_source <span class="synPreProc">from</span> aws_lambda_powertools.utilities.data_classes.api_gateway_authorizer_event <span class="synPreProc">import</span> (APIGatewayAuthorizerTokenEvent, APIGatewayAuthorizerResponse) <span class="synPreProc">from</span> aws_lambda_powertools.utilities.typing <span class="synPreProc">import</span> LambdaContext <span class="synPreProc">@</span><span class="synIdentifier">event_source</span>(data_class=APIGatewayAuthorizerTokenEvent) <span class="synStatement">def</span> <span class="synIdentifier">lambda_handler</span>(event: APIGatewayAuthorizerTokenEvent, context: LambdaContext): arn = event.parsed_arn policy = APIGatewayAuthorizerResponse( principal_id=<span class="synConstant">'user'</span>, region=arn.region, aws_account_id=arn.aws_account_id, api_id=arn.api_id, stage=arn.stage ) <span class="synStatement">if</span> event.authorization_token == <span class="synConstant">'allow'</span>: policy.context = { <span class="synConstant">'key1'</span>: <span class="synConstant">'value1'</span>, <span class="synConstant">'key2'</span>: <span class="synConstant">'value2'</span>, <span class="synConstant">'key3'</span>: <span class="synConstant">'value3'</span> } policy.allow_all_routes() <span class="synStatement">else</span>: policy.deny_all_routes() <span class="synStatement">return</span> policy.asdict() </pre> <p>ドキュメントに載っている <code>Authorization</code> ヘッダーに <code>allow</code> という文字列が設定されていれば OK という簡易的な実装だけど,ポリシーを出力するときは <code>APIGatewayAuthorizerResponse</code> に値を設定して <code>allow_all_routes()</code> 関数もしくは <code>deny_all_routes()</code> 関数で出力すれば良くて簡単👍</p> <p>また認証後に実行される AWS Lambda 関数などに追加情報を渡す場合は <code>context</code> に dict で Key-Value を設定すれば OK👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fapigateway%2Flatest%2Fdeveloperguide%2Fapi-gateway-lambda-authorizer-output.html" title="Amazon API Gateway Lambda オーソライザーからの出力 - Amazon API Gateway" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html">docs.aws.amazon.com</a></cite></p> <h2 id="動作確認">動作確認</h2> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-H</span> <span class="synStatement">'</span><span class="synConstant">Authorization: allow</span><span class="synStatement">'</span> <span class="synPreProc">${ENDPOINT}</span> ok $ curl <span class="synSpecial">-H</span> <span class="synStatement">'</span><span class="synConstant">Authorization: deny</span><span class="synStatement">'</span> <span class="synPreProc">${ENDPOINT}</span> <span class="synSpecial">{</span><span class="synStatement">&quot;</span><span class="synConstant">Message</span><span class="synStatement">&quot;</span>:<span class="synStatement">&quot;</span><span class="synConstant">User is not authorized to access this resource with an explicit deny</span><span class="synStatement">&quot;</span><span class="synSpecial">}</span> </pre> kakku22 source-version-override: aws-actions/aws-codebuild-run-build でプルリクエストブランチを AWS CodeBuild のビルド対象にする hatenablog://entry/6801883189089346337 2024-03-12T12:19:14+09:00 2024-03-12T12:19:14+09:00 AWS CodeBuild Run Build for GitHub Actions (aws-actions/aws-codebuild-run-build) を使って GitHub Actions から AWS CodeBuild のビルドを実行すると buildspec.yml やビルド環境タイプを上書きできて便利〜という話は前にまとめた👌 kakakakakku.hatenablog.com 今回は source-version-override パラメータを活用して,プルリクエストを出したときにプルリクエストブランチを AWS CodeBuild のビルド対象にする仕組みを試してみた… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240310/20240310183033.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CodeBuild Run Build for GitHub Actions (<a href="https://github.com/aws-actions/aws-codebuild-run-build">aws-actions/aws-codebuild-run-build</a><a href="https://b.hatena.ne.jp/entry/https://github.com/aws-actions/aws-codebuild-run-build" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://github.com/aws-actions/aws-codebuild-run-build" alt="" class="http-bookmark" /></a>) を使って GitHub Actions から AWS CodeBuild のビルドを実行すると <code>buildspec.yml</code> やビルド環境タイプを上書きできて便利〜という話は前にまとめた👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F02%2F13%2F202423" title="GitHub Actions から AWS CodeBuild のビルドを実行できる「aws-actions/aws-codebuild-run-build」 - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/02/13/202423">kakakakakku.hatenablog.com</a></cite></p> <p>今回は <code>source-version-override</code> パラメータを活用して,プルリクエストを出したときにプルリクエストブランチを AWS CodeBuild のビルド対象にする仕組みを試してみた.以下に検証用の GitHub Actions ワークフローを載せておく📝プルリクエストをマージする前に動作確認ができるようになるから便利なパラメータだと思う❗️</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">on</span><span class="synSpecial">:</span> <span class="synIdentifier">workflow_dispatch</span><span class="synSpecial">:</span> <span class="synIdentifier">push</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">pull_request</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">permissions</span><span class="synSpecial">:</span> <span class="synIdentifier">id-token</span><span class="synSpecial">:</span> write <span class="synIdentifier">contents</span><span class="synSpecial">:</span> read <span class="synIdentifier">jobs</span><span class="synSpecial">:</span> <span class="synIdentifier">build</span><span class="synSpecial">:</span> <span class="synIdentifier">runs-on</span><span class="synSpecial">:</span> ubuntu-latest <span class="synIdentifier">steps</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> actions/checkout@v4 <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/configure-aws-credentials@v4 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">role-to-assume</span><span class="synSpecial">:</span> ${{ secrets.AWS_ROLE_ARN }} <span class="synIdentifier">aws-region</span><span class="synSpecial">:</span> ap-northeast-1 <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> ${{ github.head_ref }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'pull_request'</span> <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> pr/${{ github.event.pull_request.number }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'pull_request'</span> <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> ${{ github.ref_name }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'push'</span> </pre> <p>AWS CodeBuild Run Build for GitHub Actions の <code>source-version-override</code> パラメータには GitHub の場合<strong>「コミット ID/プルリクエスト ID/ブランチ名/タグ名」</strong>を指定できる.AWS CodeBuild の <a href="https://docs.aws.amazon.com/codebuild/latest/APIReference/API_StartBuild.html">API Reference (StartBuild)</a> に以下のように書いてあった📝</p> <blockquote><p>The commit ID, pull request ID, branch name, or tag name that corresponds to the version of the source code you want to build. If a pull request ID is specified, it must use the format pr/pull-request-ID (for example pr/25). If a branch name is specified, the branch's HEAD commit ID is used. If not specified, the default branch's HEAD commit ID is used.</p></blockquote> <h2 id="プルリクエストブランチ">プルリクエストブランチ</h2> <p>プルリクエストを作った場合は <code>github.event_name == 'pull_request'</code> で判定しつつ,<code>source-version-override</code> パラメータにブランチ名を表す <code>${{ github.head_ref }}</code> を設定すれば OK👌</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> ${{ github.head_ref }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'pull_request'</span> </pre> <p>もしブランチ名ではなく<strong>「プルリクエスト ID」</strong>を指定する場合は <code>pr/pull-request-ID</code> というフォーマットにする必要がある.<code>source-version-override</code> パラメータにプルリクエスト ID を表す <code>pr/${{ github.event.pull_request.number }}</code> を設定すれば OK👌</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> pr/${{ github.event.pull_request.number }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'pull_request'</span> </pre> <h2 id="メインブランチ">メインブランチ</h2> <p>プルリクエストをマージした場合は <code>github.event_name == 'push'</code> で判定しつつ,<code>source-version-override</code> パラメータにブランチ名を表す <code>${{ github.ref_name }}</code> を設定すれば OK👌</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">source-version-override</span><span class="synSpecial">:</span> ${{ github.ref_name }} <span class="synIdentifier">if</span><span class="synSpecial">:</span> github.event_name == <span class="synConstant">'push'</span> </pre> <h1 id="動作確認">動作確認</h1> <p>期待通りに動いたー👏</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240310/20240310181906.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> kakku22 AWS CDK で Amazon EventBridge Pipes の「ターゲット入力トランスフォーマー」を設定する hatenablog://entry/6801883189086629236 2024-03-05T18:28:26+09:00 2024-03-05T18:28:26+09:00 AWS CDK で Amazon SQS x Amazon EventBridge Pipes x AWS Step Functions の構成を設定する流れは前にまとめた📝 kakakakakku.hatenablog.com 前にまとめた設定では Amazon SQS キューに登録したメッセージをデフォルト設定のまま Amazon EventBridge Pipes 経由で AWS Step Functions に流しているけど,実際に使ってみると Amazon SQS のメッセージ形式のまま AWS Step Functions に流れてくるため,AWS Step Functions 側… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240229/20240229215417.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK で Amazon SQS x Amazon EventBridge Pipes x AWS Step Functions の構成を設定する流れは前にまとめた📝</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F01%2F10%2F213151" title="AWS CDK で Amazon EventBridge Pipes(SQS ソース・Step Functions ターゲット)を設定する - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/01/10/213151">kakakakakku.hatenablog.com</a></cite></p> <p>前にまとめた設定では Amazon SQS キューに登録したメッセージをデフォルト設定のまま Amazon EventBridge Pipes 経由で AWS Step Functions に流しているけど,実際に使ってみると Amazon SQS のメッセージ形式のまま AWS Step Functions に流れてくるため,AWS Step Functions 側でインプットの取り回しがしにくく微妙に使いにくいことに気付く💨</p> <h1 id="具体例">具体例</h1> <p>例えば以下の JSON を Amazon SQS キューに登録する.</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">key1</span>&quot;: &quot;<span class="synConstant">value1</span>&quot;, &quot;<span class="synStatement">key2</span>&quot;: &quot;<span class="synConstant">value2</span>&quot;, &quot;<span class="synStatement">key3</span>&quot;: &quot;<span class="synConstant">value3</span>&quot; <span class="synSpecial">}</span> </pre> <p>AWS Step Functions の入力としては以下のような JSON が流れてくる(一部の値は書き換えた).AWS Step Functions 側では <code>body</code> に含まれている JSON のみで十分ということも多いと思う💡</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">messageId</span>&quot;: &quot;<span class="synConstant">0f6ddca6-8fc2-45e3-8c51-226eca45dbb0</span>&quot;, &quot;<span class="synStatement">receiptHandle</span>&quot;: &quot;<span class="synConstant">xxxxx</span>&quot;, &quot;<span class="synStatement">body</span>&quot;: &quot;<span class="synStatement">{\n \&quot;key1\</span>&quot;: \&quot;<span class="synStatement">value1\&quot;,\n \&quot;key2\</span>&quot;: \&quot;<span class="synStatement">value2\&quot;,\n \&quot;key3\</span>&quot;: \&quot;<span class="synConstant">value3</span><span class="synSpecial">\&quot;\n</span><span class="synConstant">}</span>&quot;, &quot;<span class="synStatement">attributes</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">ApproximateReceiveCount</span>&quot;: &quot;<span class="synConstant">1</span>&quot;, &quot;<span class="synStatement">SentTimestamp</span>&quot;: &quot;<span class="synConstant">1709209227935</span>&quot;, &quot;<span class="synStatement">SenderId</span>&quot;: &quot;<span class="synConstant">xxxxx</span>&quot;, &quot;<span class="synStatement">ApproximateFirstReceiveTimestamp</span>&quot;: &quot;<span class="synConstant">1709209227937</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">messageAttributes</span>&quot;: <span class="synSpecial">{}</span>, &quot;<span class="synStatement">md5OfBody</span>&quot;: &quot;<span class="synConstant">b4fc128c9cb169639ef7083a9a4d78dd</span>&quot;, &quot;<span class="synStatement">eventSource</span>&quot;: &quot;<span class="synConstant">aws:sqs</span>&quot;, &quot;<span class="synStatement">eventSourceARN</span>&quot;: &quot;<span class="synConstant">arn:aws:sqs:ap-northeast-1:000000000000:sandbox-cdk-sqs-pipes-stepfunctions-queue</span>&quot;, &quot;<span class="synStatement">awsRegion</span>&quot;: &quot;<span class="synConstant">ap-northeast-1</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> </pre> <h1 id="ターゲット入力トランスフォーマーを使う">ターゲット入力トランスフォーマーを使う</h1> <p>そんなときは Amazon EventBridge Pipes で<strong>「ターゲット入力トランスフォーマー (Target Input Transformer)」</strong>を設定すれば OK👌今回は Amazon SQS キューに登録したメッセージの <code>body</code> のみ AWS Step Functions に流したいため,以下のように設定する❗️</p> <h2 id="マネジメントコンソールなら">マネジメントコンソールなら</h2> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">body</span>&quot;: &lt;$.<span class="synError">body</span>&gt; <span class="synSpecial">}</span> </pre> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240229/20240229213147.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2 id="AWS-CDK-なら">AWS CDK なら</h2> <p><code>CfnPipe</code> の <code>inputTemplate</code> に設定する.AWS CDK コード全体は <a href="https://kakakakakku.hatenablog.com/entry/2024/01/10/213151">AWS CDK &#x3067; Amazon EventBridge Pipes&#xFF08;SQS &#x30BD;&#x30FC;&#x30B9;&#x30FB;Step Functions &#x30BF;&#x30FC;&#x30B2;&#x30C3;&#x30C8;&#xFF09;&#x3092;&#x8A2D;&#x5B9A;&#x3059;&#x308B; - kakakakakku blog</a> 参照📝</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">new</span> aws_pipes.CfnPipe<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkPipes'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> name: <span class="synConstant">'sandbox-cdk-sqs-pipes-stepfunctions-pipes'</span><span class="synStatement">,</span> roleArn: pipeRole.roleArn<span class="synStatement">,</span> source: queue.queueArn<span class="synStatement">,</span> target: stateMachine.stateMachineArn<span class="synStatement">,</span> targetParameters: <span class="synIdentifier">{</span> inputTemplate: <span class="synConstant">'{ &quot;body&quot;: &lt;$.body&gt; }'</span><span class="synStatement">,</span> stepFunctionStateMachineParameters: <span class="synIdentifier">{</span> invocationType: <span class="synConstant">'FIRE_AND_FORGET'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> </pre> <h2 id="実行結果">実行結果</h2> <p><strong>「ターゲット入力トランスフォーマー」</strong>を設定してもう1度 Amazon SQS キューに同じ JSON を登録すると,以下のように <code>body</code> に含まれている JSON のみ AWS Step Functions に流せるようになった \( 'ω')/</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">body</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">key1</span>&quot;: &quot;<span class="synConstant">value1</span>&quot;, &quot;<span class="synStatement">key2</span>&quot;: &quot;<span class="synConstant">value2</span>&quot;, &quot;<span class="synStatement">key3</span>&quot;: &quot;<span class="synConstant">value3</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">}</span> <span class="synSpecial">]</span> </pre> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240229/20240229213950.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>ちなみに AWS Step Functions 側で JSON 配列になっているのは Amazon EventBridge Pipes の仕様で,ドキュメントには <code>Lambda または Step Functions エンリッチメントまたはターゲットの場合、バッチサイズが 1 であっても、バッチは JSON 配列としてターゲットに配信されます。</code> と書いてある.これはハマりポイントの1つかも💨</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Feventbridge%2Flatest%2Fuserguide%2Feb-pipes-input-transformation.html" title="Amazon EventBridge Pipes の入力変換 - Amazon EventBridge" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-pipes-input-transformation.html">docs.aws.amazon.com</a></cite></p> kakku22 Dependabot で Terraform Provider を自動的にアップデートしよう hatenablog://entry/6801883189080695897 2024-02-29T21:57:13+09:00 2024-02-29T21:57:13+09:00 Dependabot version updates を使うと Terraform Provider のアップデートを自動化できる❗️設定は比較的簡単で package-ecosystem に terraform を設定して,あとは必須の directory と schedule.interval でアップデートの対象ディレクトリとスケジュールを決めれば OK👌個人的な Terraform 検証用プライベートリポジトリに設定して数週間試してみた \( 'ω')/ さらに package-ecosystem に github-actions を設定すると actions/checkout@v4 … <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240205/20240205171147.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><strong>Dependabot version updates</strong> を使うと Terraform Provider のアップデートを自動化できる❗️設定は比較的簡単で <code>package-ecosystem</code> に <code>terraform</code> を設定して,あとは必須の <code>directory</code> と <code>schedule.interval</code> でアップデートの対象ディレクトリとスケジュールを決めれば OK👌個人的な Terraform 検証用プライベートリポジトリに設定して数週間試してみた \( 'ω')/</p> <p>さらに <code>package-ecosystem</code> に <code>github-actions</code> を設定すると <code>actions/checkout@v4</code> など GitHub Actions のアクションも自動的にアップデートできる❗️一度入れたらそのままということもよくあるし助かる〜.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.github.com%2Fen%2Fcode-security%2Fdependabot%2Fdependabot-version-updates%2Fconfiguration-options-for-the-dependabot.yml-file" title="Configuration options for the dependabot.yml file - GitHub Docs" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file">docs.github.com</a></cite></p> <h1 id="-githubdependabotyml">🤖 .github/dependabot.yml</h1> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">version</span><span class="synSpecial">:</span> <span class="synConstant">2</span> <span class="synIdentifier">updates</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">package-ecosystem</span><span class="synSpecial">:</span> terraform <span class="synIdentifier">directory</span><span class="synSpecial">:</span> / <span class="synIdentifier">schedule</span><span class="synSpecial">:</span> <span class="synIdentifier">interval</span><span class="synSpecial">:</span> daily <span class="synIdentifier">open-pull-requests-limit</span><span class="synSpecial">:</span> <span class="synConstant">2</span> <span class="synIdentifier">target-branch</span><span class="synSpecial">:</span> master <span class="synIdentifier">ignore</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">dependency-name</span><span class="synSpecial">:</span> <span class="synConstant">&quot;*&quot;</span> <span class="synIdentifier">update-types</span><span class="synSpecial">:</span> <span class="synSpecial">[</span><span class="synConstant">&quot;version-update:semver-major&quot;</span><span class="synSpecial">]</span> <span class="synStatement">- </span><span class="synIdentifier">package-ecosystem</span><span class="synSpecial">:</span> github-actions <span class="synIdentifier">directory</span><span class="synSpecial">:</span> / <span class="synIdentifier">schedule</span><span class="synSpecial">:</span> <span class="synIdentifier">interval</span><span class="synSpecial">:</span> daily <span class="synIdentifier">open-pull-requests-limit</span><span class="synSpecial">:</span> <span class="synConstant">2</span> <span class="synIdentifier">target-branch</span><span class="synSpecial">:</span> master </pre> <h1 id="動作確認">動作確認</h1> <p>Terraform の AWS Provider と Terraform の GitHub リポジトリに設定してる GitHub Actions のアクション (<code>actions/checkout</code>) を自動的にアップデートするプルリクエストが作れたー👏</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240227/20240227201509.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="関連記事">関連記事</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F02%2F12%2F151158" title="Dependabot で AWS CDK を自動的にアップデートしよう - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/02/12/151158">kakakakakku.hatenablog.com</a></cite></p> kakku22 AWS CDK の DockerImageAsset と cdk-ecr-deployment でビルドしたイメージを Amazon ECR に保存する hatenablog://entry/6801883189084103629 2024-02-27T19:15:08+09:00 2024-02-27T19:15:08+09:00 AWS CDK で「Dockerfile をビルドして Amazon ECR リポジトリにイメージを保存する」選択肢として DockerImageAsset と cdklabs/cdk-ecr-deployment を紹介する❗️ aws_ecr_assets.DockerImageAsset を使う まず,1番簡単なのは aws_ecr_assets.DockerImageAsset を使うという選択肢だと思う. docs.aws.amazon.com 👾 sandbox-cdk-ecr-deployment-stack.ts (Step.1) Dockerfile を準備して(今回は ..… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240218/20240218173441.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK で<strong>「Dockerfile をビルドして Amazon ECR リポジトリにイメージを保存する」</strong>選択肢として <strong>DockerImageAsset</strong> と <strong>cdklabs/cdk-ecr-deployment</strong> を紹介する❗️</p> <h1 id="aws_ecr_assetsDockerImageAsset-を使う"><code>aws_ecr_assets.DockerImageAsset</code> を使う</h1> <p>まず,1番簡単なのは <code>aws_ecr_assets.DockerImageAsset</code> を使うという選択肢だと思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_ecr_assets-readme.html" title="aws-cdk-lib.aws_ecr_assets module · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecr_assets-readme.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-ecr-deployment-stackts-Step1">👾 sandbox-cdk-ecr-deployment-stack.ts (Step.1)</h2> <p><code>Dockerfile</code> を準備して(今回は <code>../images/app</code> ディレクトリに置いた)以下のような AWS CDK コードを実装すれば OK👌</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_ecr_assets<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">import</span> path <span class="synStatement">=</span> require<span class="synStatement">(</span><span class="synConstant">'path'</span><span class="synStatement">)</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkEcrDeploymentStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synStatement">new</span> aws_ecr_assets.DockerImageAsset<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxAppImage'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> directory: path.join<span class="synStatement">(</span><span class="synSpecial">__dirname</span><span class="synStatement">,</span> <span class="synConstant">'../images/app'</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p>デプロイすると自動的に <code>cdk-hnb659fds-container-assets-${ACCOUNTID}-ap-northeast-1</code> という Amazon ECR リポジトリにイメージが保存される.<code>hnb659fds</code> は AWS CDK 側で付けられた名前で<a href="https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/bootstrapping.html">&#x30C9;&#x30AD;&#x30E5;&#x30E1;&#x30F3;&#x30C8;</a>に <code>この値には意味がありません</code> と書かれてておもしろい😃実装も少なく1番お手軽だと思う (๑•̀ㅂ•́)و✧</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240218/20240218171132.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>デメリットとしては任意の Amazon ECR リポジトリにイメージを保存できないというところだと思う.言い換えるとリポジトリ名・ライフサイクルポリシー・リポジトリタグなどは自由に設定できず,AWS CDK 側に管理されてしまうため微妙な使いづらさがある.ちなみにこれはドキュメントにも明記されていて<strong>「そういう意図である」</strong>と読み取れる📝</p> <blockquote><p>DockerImageAsset is designed for seamless build &amp; consumption of image assets by CDK code deployed to multiple environments through the CDK CLI or through CI/CD workflows. To that end, the ECR repository behind this construct is controlled by the AWS CDK. The mechanics of where these images are published and how are intentionally kept as an implementation detail, and the construct does not support customizations such as specifying the ECR repository name or tags.</p></blockquote> <h1 id="cdklabscdk-ecr-deployment-と組み合わせて使う"><code>cdklabs/cdk-ecr-deployment</code> と組み合わせて使う</h1> <p>そこで <code>aws_ecr_assets.DockerImageAsset</code> と <code>cdklabs/cdk-ecr-deployment</code> を組み合わせて使うという選択肢もある💡<code>cdklabs/cdk-ecr-deployment</code> を使うと,指定した Docker Hub・Amazon ECR リポジトリなどのイメージを任意の Amazon ECR リポジトリにコピーできる.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fcdklabs%2Fcdk-ecr-deployment" title="GitHub - cdklabs/cdk-ecr-deployment: A CDK construct to deploy docker image to Amazon ECR" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/cdklabs/cdk-ecr-deployment">github.com</a></cite></p> <h2 id="-sandbox-cdk-ecr-deployment-stackts-Step2">👾 sandbox-cdk-ecr-deployment-stack.ts (Step.2)</h2> <p>例えば,以下のような AWS CDK コードを実装してデプロイすると,Docker Hub の <code>amazonlinux:2023</code> イメージを自分で実装した Amazon ECR リポジトリ(今回は <code>sandbox-repository</code>)にコピーできる.</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_ecr<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> * <span class="synStatement">as</span> ecrdeploy <span class="synStatement">from</span> <span class="synConstant">'cdk-ecr-deployment'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkEcrDeploymentStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synType">const</span> repository <span class="synStatement">=</span> <span class="synStatement">new</span> aws_ecr.Repository<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxRepository'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> repositoryName: <span class="synConstant">'sandbox-repository'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> ecrdeploy.ECRDeployment<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxImageDeployment'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> src: <span class="synStatement">new</span> ecrdeploy.DockerImageName<span class="synStatement">(</span><span class="synConstant">'amazonlinux:2023'</span><span class="synStatement">),</span> dest: <span class="synStatement">new</span> ecrdeploy.DockerImageName<span class="synStatement">(</span>repository.repositoryUriForTag<span class="synStatement">(</span><span class="synConstant">'2023'</span><span class="synStatement">)),</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240218/20240218171145.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2 id="-sandbox-cdk-ecr-deployment-stackts-Step3">👾 sandbox-cdk-ecr-deployment-stack.ts (Step.3)</h2> <p>よって <code>aws_ecr_assets.DockerImageAsset</code> で Amazon ECR リポジトリ <code>cdk-hnb659fds-container-assets-${ACCOUNTID}-ap-northeast-1</code> に保存されたイメージを自分で実装した Amazon ECR リポジトリにコピーすることで,Dockerfile をビルドして Amazon ECR リポジトリにイメージを保存する流れを AWS CDK で柔軟に実現できる👏</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_ecr<span class="synStatement">,</span> aws_ecr_assets<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> * <span class="synStatement">as</span> ecrdeploy <span class="synStatement">from</span> <span class="synConstant">'cdk-ecr-deployment'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">import</span> path <span class="synStatement">=</span> require<span class="synStatement">(</span><span class="synConstant">'path'</span><span class="synStatement">)</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkEcrDeploymentStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synType">const</span> image <span class="synStatement">=</span> <span class="synStatement">new</span> aws_ecr_assets.DockerImageAsset<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxAppImage'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> directory: path.join<span class="synStatement">(</span><span class="synSpecial">__dirname</span><span class="synStatement">,</span> <span class="synConstant">'../images/app'</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synType">const</span> repository <span class="synStatement">=</span> <span class="synStatement">new</span> aws_ecr.Repository<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxRepository'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> repositoryName: <span class="synConstant">'sandbox-repository'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> ecrdeploy.ECRDeployment<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxImageDeployment'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> src: <span class="synStatement">new</span> ecrdeploy.DockerImageName<span class="synStatement">(</span>image.imageUri<span class="synStatement">),</span> dest: <span class="synStatement">new</span> ecrdeploy.DockerImageName<span class="synStatement">(</span>repository.repositoryUriForTag<span class="synStatement">(</span><span class="synConstant">'my-tag'</span><span class="synStatement">)),</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240218/20240218171207.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="cdk-ecr-deployment-で事前にビルドされたイメージを使う">cdk-ecr-deployment で事前にビルドされたイメージを使う</h1> <p><code>cdklabs/cdk-ecr-deployment</code> は内部的にイメージをコピーするための AWS Lambda 関数もデプロイされている.AWS CDK をデプロイするときに環境変数として <code>FORCE_PREBUILT_LAMBDA=1</code> を設定しておくと,AWS Lambda 関数を作るときに事前にビルドされたイメージを使うようになる.ビルド時間を短縮できるので使っても良いと思う👌実装を見たところ GitHub リポジトリのリリースアセットからダウンロードしているようだった.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fcdklabs%2Fcdk-ecr-deployment%2Freleases" title="Releases · cdklabs/cdk-ecr-deployment" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/cdklabs/cdk-ecr-deployment/releases">github.com</a></cite></p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240218/20240218172742.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> kakku22 Terraform で「最新の」Amazon ECS タスク定義を追跡できる aws_ecs_task_definition の track_latest オプション hatenablog://entry/6801883189084561259 2024-02-21T12:35:29+09:00 2024-02-21T12:36:55+09:00 Terraform で Amazon ECS タスク定義を作りつつ,アプリケーションのライフサイクルとして GitHub Actions などの「Terraform 以外で」イメージタグを差し替えて Amazon ECS タスク定義を更新(正確には更新ではなくリビジョン追加)する運用を選択することがあると思う.さらにデプロイを繰り返すと使わなくなった Amazon ECS タスク定義が増えるため,定期的に「登録解除 (INACTIVE)」をすることもあると思う.ちなみに2023年2月からは削除もできるようになっているけど〜 \( 'ω')/ track_latest とは しかし Amazon… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240220/20240220170251.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>Terraform で Amazon ECS タスク定義を作りつつ,アプリケーションのライフサイクルとして GitHub Actions などの<strong>「Terraform 以外で」</strong>イメージタグを差し替えて Amazon ECS タスク定義を更新(正確には更新ではなくリビジョン追加)する運用を選択することがあると思う.さらにデプロイを繰り返すと使わなくなった Amazon ECS タスク定義が増えるため,定期的に<strong>「登録解除 (INACTIVE)」</strong>をすることもあると思う.ちなみに2023年2月からは<a href="https://aws.amazon.com/jp/about-aws/whats-new/2023/02/amazon-ecs-deletion-inactive-task-definition-revisions/">&#x524A;&#x9664;&#x3082;&#x3067;&#x304D;&#x308B;&#x3088;&#x3046;&#x306B;</a>なっているけど〜 \( 'ω')/</p> <h1 id="track_latest-とは"><code>track_latest</code> とは</h1> <p>しかし Amazon ECS タスク定義を登録解除すると Terraform でリソースを追跡できず,terraform plan を実行すると Amazon ECS タスク定義を作り直そうとしてしまう.少し前置きは長くなったけど,2024年2月16日にリリースされた <strong>Terraform AWS Provider v5.37.0</strong> で <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition"> aws_ecs_task_definition</a> に新しく <code>track_latest</code> オプションが追加された❗️</p> <blockquote><p>track_latest - (Optional) Whether should track latest task definition or the one created with the resource. Default is false.</p></blockquote> <p>この <code>track_latest</code> を使うと Terraform 側は常に<strong>「最新の」</strong>Amazon ECS タスク定義を追跡して比較してくれるため,Amazon ECS タスク定義の作り直しがなくなり,運用しやすくなる可能性がある👏</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fhashicorp%2Fterraform-provider-aws%2Freleases%2Ftag%2Fv5.37.0" title="Release v5.37.0 · hashicorp/terraform-provider-aws" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.37.0">github.com</a></cite></p> <p>とは言え実際に試さないとイメージが沸かず,プルリクエストに書いてあるシナリオを参考に試してみた.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fhashicorp%2Fterraform-provider-aws%2Fpull%2F30154" title="r/aws_ecs_task_definition: add track_latest attribute by GerardSoleCa · Pull Request #30154 · hashicorp/terraform-provider-aws" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/hashicorp/terraform-provider-aws/pull/30154">github.com</a></cite></p> <h1 id="デフォルト-or-track_latest--false-の場合">デフォルト or <code>track_latest = false</code> の場合</h1> <h2 id="Step1">Step.1</h2> <p>今回はイメージの例として <a href="https://hub.docker.com/_/amazonlinux">amazonlinux</a> のタグを <code>2.0.20231218.0</code> → <code>2.0.20240109.0</code> → <code>2.0.20240131.0</code> と更新する流れを試す.まずは Terraform で Amazon ECS タスク定義(リビジョン1)を作る.</p> <pre class="code lang-hcl" data-lang="hcl" data-unlink><span class="synType">resource</span> <span class="synConstant">&quot;aws_ecs_task_definition&quot;</span> <span class="synConstant">&quot;sandbox_track_latest_false&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">family</span> = <span class="synConstant">&quot;sandbox-track-latest-false&quot;</span> <span class="synIdentifier">network_mode</span> = <span class="synConstant">&quot;awsvpc&quot;</span> <span class="synIdentifier">requires_compatibilities</span> = <span class="synSpecial">[</span><span class="synConstant">&quot;FARGATE&quot;</span><span class="synSpecial">]</span> <span class="synIdentifier">cpu</span> = <span class="synConstant">&quot;256&quot;</span> <span class="synIdentifier">memory</span> = <span class="synConstant">&quot;512&quot;</span> <span class="synIdentifier">container_definitions</span> = <span class="synIdentifier">jsonencode</span>(<span class="synSpecial">[</span> <span class="synSpecial">{</span> <span class="synIdentifier">name</span> = <span class="synConstant">&quot;amazonlinux&quot;</span>, <span class="synIdentifier">image</span> = <span class="synConstant">&quot;amazonlinux:2.0.20231218.0&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">]</span>) } </pre> <p><figure class="figure-image figure-image-fotolife" title="Terraform で Amazon ECS タスク定義(リビジョン1)を作った"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219234543.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Terraform で Amazon ECS タスク定義(リビジョン1)を作った</figcaption></figure></p> <h2 id="Step2">Step.2</h2> <p>次は Terraform 以外(今回は AWS CLI を使う)でイメージタグを更新した Amazon ECS タスク定義を2個を作る(リビジョン2・リビジョン3).この状態でアプリケーションとして稼働する最新の Amazon ECS タスク定義は<strong>「リビジョン3」</strong>となる.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws ecs register-task-definition <span class="synSpecial">--cli-input-json</span> fileb://taskdefinition-2.<span class="synConstant">0</span>.<span class="synConstant">20240109</span>.<span class="synConstant">0</span>.json $ aws ecs register-task-definition <span class="synSpecial">--cli-input-json</span> fileb://taskdefinition-2.<span class="synConstant">0</span>.<span class="synConstant">20240131</span>.<span class="synConstant">0</span>.json </pre> <h3 id="-taskdefinition-20202401090json">📝 taskdefinition-2.0.20240109.0.json</h3> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">family</span>&quot;: &quot;<span class="synConstant">sandbox-track-latest-false</span>&quot;, &quot;<span class="synStatement">networkMode</span>&quot;: &quot;<span class="synConstant">awsvpc</span>&quot;, &quot;<span class="synStatement">requiresCompatibilities</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">FARGATE</span>&quot; <span class="synSpecial">]</span>, &quot;<span class="synStatement">cpu</span>&quot;: &quot;<span class="synConstant">256</span>&quot;, &quot;<span class="synStatement">memory</span>&quot;: &quot;<span class="synConstant">512</span>&quot;, &quot;<span class="synStatement">containerDefinitions</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">name</span>&quot;: &quot;<span class="synConstant">amazonlinux</span>&quot;, &quot;<span class="synStatement">image</span>&quot;: &quot;<span class="synConstant">amazonlinux:2.0.20240109.0</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span> </pre> <h3 id="-taskdefinition-20202401310json">📝 taskdefinition-2.0.20240131.0.json</h3> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">family</span>&quot;: &quot;<span class="synConstant">sandbox-track-latest-false</span>&quot;, &quot;<span class="synStatement">networkMode</span>&quot;: &quot;<span class="synConstant">awsvpc</span>&quot;, &quot;<span class="synStatement">requiresCompatibilities</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">FARGATE</span>&quot; <span class="synSpecial">]</span>, &quot;<span class="synStatement">cpu</span>&quot;: &quot;<span class="synConstant">256</span>&quot;, &quot;<span class="synStatement">memory</span>&quot;: &quot;<span class="synConstant">512</span>&quot;, &quot;<span class="synStatement">containerDefinitions</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">name</span>&quot;: &quot;<span class="synConstant">amazonlinux</span>&quot;, &quot;<span class="synStatement">image</span>&quot;: &quot;<span class="synConstant">amazonlinux:2.0.20240131.0</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span> </pre> <p><figure class="figure-image figure-image-fotolife" title="AWS CLI で Amazon ECS タスク定義(リビジョン2・リビジョン3)を作った"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240220/20240220090748.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CLI で Amazon ECS タスク定義(リビジョン2・リビジョン3)を作った</figcaption></figure></p> <h2 id="Step3">Step.3</h2> <p>ここで使わなくなった Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除する.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws ecs deregister-task-definition <span class="synSpecial">--task-definition</span> sandbox-track-latest-false:1 $ aws ecs deregister-task-definition <span class="synSpecial">--task-definition</span> sandbox-track-latest-false:2 </pre> <p><figure class="figure-image figure-image-fotolife" title="Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除した"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219234610.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除した</figcaption></figure></p> <h2 id="Step4">Step.4</h2> <p>最後に terraform plan を実行すると Amazon ECS タスク定義を作り直そうとする😨 ちなみに個人用の Terraform Cloud のキャプチャを載せている.</p> <p><figure class="figure-image figure-image-fotolife" title="ff"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219235112.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>terraform plan を実行した</figcaption></figure></p> <h1 id="track_latest--true-の場合"><code>track_latest = true</code> の場合</h1> <h2 id="Step1-1">Step.1</h2> <p>基本的には同じだけど,名前を <code>sandbox-track-latest-true</code> に変更して,<code>track_latest = true</code> も設定した.まずは Terraform で Amazon ECS タスク定義(リビジョン1)を作る.</p> <pre class="code lang-hcl" data-lang="hcl" data-unlink><span class="synType">resource</span> <span class="synConstant">&quot;aws_ecs_task_definition&quot;</span> <span class="synConstant">&quot;sandbox_track_latest_true&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">family</span> = <span class="synConstant">&quot;sandbox-track-latest-true&quot;</span> <span class="synIdentifier">network_mode</span> = <span class="synConstant">&quot;awsvpc&quot;</span> <span class="synIdentifier">requires_compatibilities</span> = <span class="synSpecial">[</span><span class="synConstant">&quot;FARGATE&quot;</span><span class="synSpecial">]</span> <span class="synIdentifier">cpu</span> = <span class="synConstant">&quot;256&quot;</span> <span class="synIdentifier">memory</span> = <span class="synConstant">&quot;512&quot;</span> <span class="synIdentifier">track_latest</span> = <span class="synConstant">true</span> <span class="synIdentifier">container_definitions</span> = <span class="synIdentifier">jsonencode</span>(<span class="synSpecial">[</span> <span class="synSpecial">{</span> <span class="synIdentifier">name</span> = <span class="synConstant">&quot;amazonlinux&quot;</span>, <span class="synIdentifier">image</span> = <span class="synConstant">&quot;amazonlinux:2.0.20231218.0&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">]</span>) } </pre> <h2 id="Step2-1">Step.2</h2> <p>Step.2 も基本的には同じ.Amazon ECS タスク定義を2個作る(リビジョン2・リビジョン3).</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws ecs register-task-definition <span class="synSpecial">--cli-input-json</span> fileb://taskdefinition-2.<span class="synConstant">0</span>.<span class="synConstant">20240109</span>.<span class="synConstant">0</span>.json $ aws ecs register-task-definition <span class="synSpecial">--cli-input-json</span> fileb://taskdefinition-2.<span class="synConstant">0</span>.<span class="synConstant">20240131</span>.<span class="synConstant">0</span>.json </pre> <h3 id="-taskdefinition-20202401090json-1">📝 taskdefinition-2.0.20240109.0.json</h3> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">family</span>&quot;: &quot;<span class="synConstant">sandbox-track-latest-true</span>&quot;, &quot;<span class="synStatement">networkMode</span>&quot;: &quot;<span class="synConstant">awsvpc</span>&quot;, &quot;<span class="synStatement">requiresCompatibilities</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">FARGATE</span>&quot; <span class="synSpecial">]</span>, &quot;<span class="synStatement">cpu</span>&quot;: &quot;<span class="synConstant">256</span>&quot;, &quot;<span class="synStatement">memory</span>&quot;: &quot;<span class="synConstant">512</span>&quot;, &quot;<span class="synStatement">containerDefinitions</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">name</span>&quot;: &quot;<span class="synConstant">amazonlinux</span>&quot;, &quot;<span class="synStatement">image</span>&quot;: &quot;<span class="synConstant">amazonlinux:2.0.20240109.0</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span> </pre> <h3 id="-taskdefinition-20202401310json-1">📝 taskdefinition-2.0.20240131.0.json</h3> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">family</span>&quot;: &quot;<span class="synConstant">sandbox-track-latest-true</span>&quot;, &quot;<span class="synStatement">networkMode</span>&quot;: &quot;<span class="synConstant">awsvpc</span>&quot;, &quot;<span class="synStatement">requiresCompatibilities</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">FARGATE</span>&quot; <span class="synSpecial">]</span>, &quot;<span class="synStatement">cpu</span>&quot;: &quot;<span class="synConstant">256</span>&quot;, &quot;<span class="synStatement">memory</span>&quot;: &quot;<span class="synConstant">512</span>&quot;, &quot;<span class="synStatement">containerDefinitions</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">name</span>&quot;: &quot;<span class="synConstant">amazonlinux</span>&quot;, &quot;<span class="synStatement">image</span>&quot;: &quot;<span class="synConstant">amazonlinux:2.0.20240131.0</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span> </pre> <h2 id="Step3-1">Step.3</h2> <p>ここで使わなくなった Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除する.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws ecs deregister-task-definition <span class="synSpecial">--task-definition</span> sandbox-track-latest-true:1 $ aws ecs deregister-task-definition <span class="synSpecial">--task-definition</span> sandbox-track-latest-true:2 </pre> <p><figure class="figure-image figure-image-fotolife" title="Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除した"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219235146.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon ECS タスク定義(リビジョン1・リビジョン2)を登録解除した</figcaption></figure></p> <h2 id="Step4-1">Step.4</h2> <p><code>track_latest = true</code> を設定してると Step.4 から挙動が変わってくる💡ここで terraform plan を実行すると Amazon ECS タスク定義を作り直しではなく<strong>「最新の」</strong>Amazon ECS タスク定義を追跡して比較してくれる.実際に <code>containerDefinitions.image</code> の値を最新の <code>amazonlinux:2.0.20240131.0</code> から,Terraform コードで指定している <code>amazonlinux:2.0.20231218.0</code> に戻すような差分が出ている❗️</p> <p><figure class="figure-image figure-image-fotolife" title="terraform plan を実行した"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219235156.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>terraform plan を実行した</figcaption></figure></p> <p>次に Terraform コードを修正して最新の Amazon ECS タスク定義と一致させる📝</p> <pre class="code lang-hcl" data-lang="hcl" data-unlink><span class="synType">resource</span> <span class="synConstant">&quot;aws_ecs_task_definition&quot;</span> <span class="synConstant">&quot;sandbox_track_latest_true&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">family</span> = <span class="synConstant">&quot;sandbox-track-latest-true&quot;</span> <span class="synIdentifier">network_mode</span> = <span class="synConstant">&quot;awsvpc&quot;</span> <span class="synIdentifier">requires_compatibilities</span> = <span class="synSpecial">[</span><span class="synConstant">&quot;FARGATE&quot;</span><span class="synSpecial">]</span> <span class="synIdentifier">cpu</span> = <span class="synConstant">&quot;256&quot;</span> <span class="synIdentifier">memory</span> = <span class="synConstant">&quot;512&quot;</span> <span class="synIdentifier">track_latest</span> = <span class="synConstant">true</span> <span class="synIdentifier">container_definitions</span> = <span class="synIdentifier">jsonencode</span>(<span class="synSpecial">[</span> <span class="synSpecial">{</span> <span class="synIdentifier">name</span> = <span class="synConstant">&quot;amazonlinux&quot;</span>, <span class="synIdentifier">image</span> = <span class="synConstant">&quot;amazonlinux:2.0.20240131.0&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">]</span>) } </pre> <p>もう一度 terraform plan を実行すると <strong>No changes</strong> で差分なしになる👌</p> <p>なるほどー \( 'ω')/</p> <p><figure class="figure-image figure-image-fotolife" title="terraform plan を実行した"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240219/20240219235211.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>terraform plan を実行した</figcaption></figure></p> <h1 id="まとめ">まとめ</h1> <p>2024年2月16日にリリースされた <strong>Terraform AWS Provider v5.37.0</strong> で追加された <code>aws_ecs_task_definition</code> の新しいオプション <code>track_latest</code> オプションを試してみた❗️ドキュメントを読むだけではイメージが沸かず,実際に試してみて良かった.とは言え <code>track_latest = true</code> を設定するとアプリケーションを頻繁にデプロイするときに変更の追随も大変そうなので <code>ignore_changes</code> で変更を無視する運用も引き続き残りそう.まずは <code>track_latest = true</code> の存在を覚えておこうー💡</p> kakku22 位置情報や地図を仕事で使う前のキャッチアップに最適な「現場のプロがわかりやすく教える位置情報エンジニア養成講座」を読んだ hatenablog://entry/6801883189083333035 2024-02-19T09:31:17+09:00 2024-02-19T09:31:17+09:00 「現場のプロがわかりやすく教える位置情報エンジニア養成講座」を読んだ📕 今まで GIS: Geographic Information System など位置情報を取り扱うための技術的な知識や経験がなく,地図アプリケーションを使うと「便利だな〜w」という感想しか出てこないようなレベルだった💨 本書を読んで,そして本書の後半では実際にアプリケーションを写経しながら実装したことによって,位置情報を取り扱うための最低限の知識と開発の流れを把握できるようになった❗️本書の冒頭には「これから位置情報を扱うエンジニア」が読者層だと書いてあったけど,まさにその通りだと思った.位置情報や地図を仕事で使う前のキ… <p><strong>「現場のプロがわかりやすく教える位置情報エンジニア養成講座」</strong>を読んだ📕</p> <p>今まで <a href="https://www.gsi.go.jp/GIS/whatisgis.html">GIS: Geographic Information System</a><a href="https://b.hatena.ne.jp/entry/https://www.gsi.go.jp/GIS/whatisgis.html" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.gsi.go.jp/GIS/whatisgis.html" alt="" class="http-bookmark" /></a> など位置情報を取り扱うための技術的な知識や経験がなく,地図アプリケーションを使うと<strong>「便利だな〜w」</strong>という感想しか出てこないようなレベルだった💨</p> <p>本書を読んで,そして本書の後半では実際にアプリケーションを写経しながら実装したことによって,位置情報を取り扱うための最低限の知識と開発の流れを把握できるようになった❗️本書の冒頭には<strong>「これから位置情報を扱うエンジニア」</strong>が読者層だと書いてあったけど,まさにその通りだと思った.位置情報や地図を仕事で使う前のキャッチアップに最適な一冊だった🗾</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4798068926?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51LePZY5NRL._SL500_.jpg" class="hatena-asin-detail-image" alt="現場のプロがわかりやすく教える位置情報エンジニア養成講座" title="現場のプロがわかりやすく教える位置情報エンジニア養成講座"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4798068926?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">現場のプロがわかりやすく教える位置情報エンジニア養成講座</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%B0%E6%B8%FD%C1%D5%C2%E7" class="keyword">井口奏大</a></li><li>秀和システム</li></ul><a href="https://www.amazon.co.jp/dp/4798068926?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>さらに OpenStreetMap / 国土数値情報 / 地理院タイルなど,地図関連の公開データ(ライセンスには注意)が多くあることも今までは全然知らなかった.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.openstreetmap.org%2F" title="OpenStreetMap" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://www.openstreetmap.org/">www.openstreetmap.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fnlftp.mlit.go.jp%2F" title="国土数値情報ダウンロードサイト" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://nlftp.mlit.go.jp/">nlftp.mlit.go.jp</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fmaps.gsi.go.jp%2Fdevelopment%2Fichiran.html" title="地理院地図|地理院タイル一覧" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://maps.gsi.go.jp/development/ichiran.html">maps.gsi.go.jp</a></cite></p> <h1 id="目次">目次</h1> <p>どの章も良かったけど,特に<strong>「第2章」</strong>で位置情報の知識を整理できたことと<strong>「第5章」</strong>でアプリケーションを一歩一歩動かしながら実装できたところが良かった👌</p> <ul> <li><strong>第1章: 位置情報の世界</strong> <ul> <li>1-1 位置情報とは</li> <li>1-2 位置情報アプリケーション</li> <li>1-3 位置情報アプリケーションの特徴</li> <li>1-4 自由に使える位置情報データ</li> </ul> </li> <li><strong>第2章: 位置情報の基本</strong> <ul> <li>2-1 位置を表す方法:経緯度</li> <li>2-2 丸い地球をどう地図にするか:地図投影法</li> <li>2-3 位置情報のデータ形式</li> <li>2-4 位置情報のファイル形式</li> <li>2-5 位置情報の配信方法</li> <li>2-6 サーバーレスの潮流:Cloud Optimized</li> </ul> </li> <li><strong>第3章: 位置情報データの取得・加工</strong> <ul> <li>3-1 QGISとは</li> <li>3-2 QGISのダウンロードとインストール</li> <li>3-3 位置情報データの取得</li> <li>3-4 位置情報データの表示</li> <li>3-5 位置情報データの加工:ベクトルデータ編</li> <li>3-6 位置情報データの加工:ラスターデータ編 </li> </ul> </li> <li><strong>第4章: 位置情報アプリケーション開発:入門編</strong> <ul> <li>4-1 位置情報ライブラリの紹介</li> <li>4-2 テーマ別ハンズオン</li> <li>4-3 その他の位置情報技術</li> </ul> </li> <li><strong>第5章: 位置情報アプリケーション開発:実践編</strong> <ul> <li>5-1 入門編との違い</li> <li>5-2 実践編Part1:「防災マップ」を作成する</li> <li>5-3 実践編Part2:スマートフォンで利用できるようにする</li> </ul> </li> </ul> <p>目次の詳細は出版社サイトを見てもらえればと!</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.shuwasystem.co.jp%2Fbook%2F9784798068923.html" title="現場のプロがわかりやすく教える 位置情報エンジニア養成講座 - 秀和システム あなたの学びをサポート!" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://www.shuwasystem.co.jp/book/9784798068923.html">www.shuwasystem.co.jp</a></cite></p> <h1 id="実装したアプリケーション">実装したアプリケーション</h1> <p><strong>「第4章」</strong>では <a href="https://leafletjs.com/">Leaflet</a><a href="https://b.hatena.ne.jp/entry/https://leafletjs.com/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://leafletjs.com/" alt="" class="http-bookmark" /></a> を使って,多くの地図表現を実装しながら学ぶ.そして<strong>「第5章」</strong>では <a href="https://maplibre.org/maplibre-gl-js/docs/">MapLibre GL JS</a><a href="https://b.hatena.ne.jp/entry/https://maplibre.org/maplibre-gl-js/docs/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://maplibre.org/maplibre-gl-js/docs/" alt="" class="http-bookmark" /></a> を使って,地図アプリケーション(防災マップ)を実装する.以下のような特徴がある👏</p> <ul> <li>背景の地図は OpenStreetMap のデータを使う</li> <li>ハザードマップ(洪水浸水想定区域・津波浸水想定区域など)は「<a href="https://disaportal.gsi.go.jp/">&#x30CF;&#x30B6;&#x30FC;&#x30C9;&#x30DE;&#x30C3;&#x30D7;&#x30DD;&#x30FC;&#x30BF;&#x30EB;&#x30B5;&#x30A4;&#x30C8;</a><a href="https://b.hatena.ne.jp/entry/https://disaportal.gsi.go.jp/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://disaportal.gsi.go.jp/" alt="" class="http-bookmark" /></a>」のデータを使う</li> <li>指定緊急避難場所は「<a href="https://www.gsi.go.jp/bousaichiri/hinanbasho.html">&#x56FD;&#x571F;&#x5730;&#x7406;&#x9662;</a><a href="https://b.hatena.ne.jp/entry/https://www.gsi.go.jp/bousaichiri/hinanbasho.html" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.gsi.go.jp/bousaichiri/hinanbasho.html" alt="" class="http-bookmark" /></a>」のデータを使う</li> <li>etc</li> </ul> <p>今回はできる限り写経しながら進めつつ,コピペで十分なところは本書のコードが公開されている GitHub リポジトリを参考にした.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2FKanahiro%2Flocation-tech-sample-v1" title="GitHub - Kanahiro/location-tech-sample-v1: 秀和システム「現場のプロがわかりやすく教える位置情報エンジニア養成講座」サンプルコード" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/Kanahiro/location-tech-sample-v1">github.com</a></cite></p> <p>そして実際にデプロイしたアプリケーション(一部)のキャプチャを紹介する❗️</p> <p><figure class="figure-image figure-image-fotolife" title="防災マップ: 初期表示"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240215/20240215230717.png" width="1146" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>防災マップ: 初期表示</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="防災マップ: 津波浸水想定区域と津波の指定緊急避難場所"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240215/20240215231034.png" width="1146" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>防災マップ: 津波浸水想定区域と津波の指定緊急避難場所</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="防災マップ: 3D 表示"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240215/20240215231332.png" width="1146" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>防災マップ: 3D 表示</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="防災マップ: 指定緊急避難所の詳細情報など"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240215/20240215231819.png" width="1146" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>防災マップ: 指定緊急避難所の詳細情報など</figcaption></figure></p> <p>本書のデモサイトも <strong>GitHub Pages</strong> にデプロイされていて実際に試せる👌ちなみに僕は GitHub Pages ではなく <strong>Vercel</strong> にデプロイして動作確認をしていた.</p> <ul> <li><a href="https://kanahiro.github.io/location-tech-sample-v1/01_basic/index.html">&#x4F4D;&#x7F6E;&#x60C5;&#x5831;&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x990A;&#x6210;&#x8B1B;&#x5EA7; - &#x4F4D;&#x7F6E;&#x60C5;&#x5831;&#x30A2;&#x30D7;&#x30EA;&#x30B1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x958B;&#x767A;&#x301C;&#x5165;&#x9580;&#x7DE8;</a></li> <li><a href="https://kanahiro.github.io/location-tech-sample-v1/02_advanced/dist/index.html">&#x4F4D;&#x7F6E;&#x60C5;&#x5831;&#x30A2;&#x30D7;&#x30EA;&#x30B1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x958B;&#x767A;&#x5B9F;&#x8DF5;&#x7DE8;</a></li> </ul> <p>ちなみにキャプチャを載せたアプリケーションは完成後に復習も兼ねて <strong>Mapbox Controls</strong> の inspect と zoom を追加で導入してるので,右下のボタンは少しカスタマイズしてある💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fkorywka%2Fmapbox-controls" title="GitHub - korywka/mapbox-controls: Monorepo for Mapbox controls" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/korywka/mapbox-controls">github.com</a></cite></p> <h1 id="その他メモ">その他メモ</h1> <ul> <li>経緯度(十進法表記・度分秒表記)</li> <li>地図投影法(ウェブメルカトル・<a href="https://www.gsi.go.jp/sokuchikijun/jpc.html">&#x5E73;&#x9762;&#x76F4;&#x89D2;&#x5EA7;&#x6A19;&#x7CFB;</a><a href="https://b.hatena.ne.jp/entry/https://www.gsi.go.jp/sokuchikijun/jpc.html" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.gsi.go.jp/sokuchikijun/jpc.html" alt="" class="http-bookmark" /></a>)</li> <li>ベクトルデータ(<a href="https://geojson.org/">GeoJSON</a><a href="https://b.hatena.ne.jp/entry/https://geojson.org/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://geojson.org/" alt="" class="http-bookmark" /></a>・ESRI Shapefile など)</li> <li>ラスターデータ(GeoTIFF など)</li> <li>配信方法(バウンディングボックス・地図タイル)</li> <li>COG: Cloud Optimized GeoTIFF</li> <li><a href="https://www.qgis.org/ja/site/">QGIS</a><a href="https://b.hatena.ne.jp/entry/https://www.qgis.org/ja/site/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.qgis.org/ja/site/" alt="" class="http-bookmark" /></a></li> <li><a href="https://github.com/mapbox/tippecanoe">GitHub - mapbox/tippecanoe: Build vector tilesets from large collections of GeoJSON features.</a><a href="https://b.hatena.ne.jp/entry/https://github.com/mapbox/tippecanoe" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://github.com/mapbox/tippecanoe" alt="" class="http-bookmark" /></a></li> <li><a href="https://maplibre.org/maplibre-gl-js/docs/plugins/">Plugins - MapLibre GL JS</a><a href="https://b.hatena.ne.jp/entry/https://maplibre.org/maplibre-gl-js/docs/plugins/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://maplibre.org/maplibre-gl-js/docs/plugins/" alt="" class="http-bookmark" /></a></li> </ul> <h1 id="誤植">誤植</h1> <p>読みながら気付いたところをメモしておく📕</p> <ul> <li>P.195: <code>開発実践編朱></code> → <code>開発実践編</code></li> <li>P.233: <code>5-2-6</code> が2個ある</li> </ul> <h1 id="まとめ">まとめ</h1> <p><strong>「現場のプロがわかりやすく教える位置情報エンジニア養成講座」</strong>を読んだ📕</p> <p>GIS: Geographic Information System や位置情報に関する知識がなかった僕にとってはとてもおもしろく勉強になる一冊だった.ユーザーとしては日々地図アプリケーションを使う機会があるため,今後は<strong>「どういう技術を使っているのかな〜」</strong>という目線で考えながら使ってみたいと思う.本当に読んで写経して良かった❗️</p> <p>ちなみに本書には出てこなかったけど,AWS 関連で調べていたら <strong>Registry of Open Data on AWS</strong> に地理空間データが公開されていたり,事例もあったりして,今後はこのあたりの領域もウォッチしよう〜 \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fregistry.opendata.aws%2F" title="Registry of Open Data on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://registry.opendata.aws/">registry.opendata.aws</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fearth%2F" title="Earth on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/earth/">aws.amazon.com</a></cite></p> kakku22 Habitify API で習慣データを取得しよう hatenablog://entry/6801883189082341746 2024-02-14T20:18:44+09:00 2024-02-14T20:18:44+09:00 2024年から Habitify で個人的な習慣化の管理をしていて,もうとにかく便利で毎日の習慣化を支えてもらっている❗️朝活・読書・サプリメントなどを記録しつつ,Habitify のチャレンジ機能を使って毎日プランクを続けていたりもする💪Habitify は無料でも十分使えるけど,僕は Premium に課金して使っている〜 \( 'ω')/ www.habitify.me 実は Habitify を使うまでは数年間 Google Sheets で習慣化の管理をしていて,例えば朝活を例にすると「その日に取り組んだこと」を必ず記録していた.Habitify にも「習慣メモ」という機能があって記… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240211/20240211214744.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>2024年から <strong>Habitify</strong> で個人的な習慣化の管理をしていて,もうとにかく便利で毎日の習慣化を支えてもらっている❗️朝活・読書・サプリメントなどを記録しつつ,Habitify のチャレンジ機能を使って毎日プランクを続けていたりもする💪Habitify は無料でも十分使えるけど,僕は Premium に課金して使っている〜 \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.habitify.me%2F" title="Habitify: Personalized Habit Tracker App | Build Better Habits Today" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://www.habitify.me/">www.habitify.me</a></cite></p> <p>実は Habitify を使うまでは数年間 Google Sheets で習慣化の管理をしていて,例えば朝活を例にすると<strong>「その日に取り組んだこと」</strong>を必ず記録していた.Habitify にも<strong>「習慣メモ」</strong>という機能があって記録はしてるけど,振り返りのために定期的に集計したく,今回は Habitify API を試してみた❗️API ドキュメントには以下にある.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.habitify.me%2F" title="Getting Started - API Documentation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.habitify.me/">docs.habitify.me</a></cite></p> <h1 id="準備">準備</h1> <p>まず Habitify API の <strong>API Key</strong> を取得する.ドキュメントにはウェブとアプリで取得できると書いてあったけど,現時点だとウェブでは取得できなさそうだった.Habitify をウェブで使うことはほとんどなく,普段使っているアプリ (Android) から API Key を取得した.</p> <h1 id="API-習慣を取得する">API: 習慣を取得する</h1> <p><code>/habits</code> API (GET) で習慣化の一覧を取得できる👌(id は省略)今は4種類の習慣を登録している.ちなみにチャレンジ機能のデータは Habitify API では取得できなさそうだった.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">-H</span> <span class="synStatement">&quot;</span><span class="synConstant">Authorization: </span><span class="synPreProc">${API_KEY}</span><span class="synStatement">&quot;</span> <span class="synStatement">\</span> https://api.habitify.me/habits | jq <span class="synSpecial">-r</span> <span class="synStatement">'</span><span class="synConstant">.data[] | {id, name, goal: {periodicity: .goal.periodicity}}</span><span class="synStatement">'</span> <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">id</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">wwwwwwwwwwwwwwwwwwww</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">name</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">ビタミンを摂る</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">goal</span><span class="synStatement">&quot;</span>: <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">periodicity</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">daily</span><span class="synStatement">&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">id</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">xxxxxxxxxxxxxxxxxxxx</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">name</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">朝活をする</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">goal</span><span class="synStatement">&quot;</span>: <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">periodicity</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">daily</span><span class="synStatement">&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">id</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">yyyyyyyyyyyyyyyyyyyy</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">name</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">1週間を振り返る</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">goal</span><span class="synStatement">&quot;</span>: <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">periodicity</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">weekly</span><span class="synStatement">&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">id</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">zzzzzzzzzzzzzzzzzzzz</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">name</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">本を読む</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">goal</span><span class="synStatement">&quot;</span>: <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">periodicity</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">daily</span><span class="synStatement">&quot;</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span> </pre> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.habitify.me%2Fcore-resources%2Fhabits" title="Habits - API Documentation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.habitify.me/core-resources/habits">docs.habitify.me</a></cite></p> <h1 id="API-習慣メモを取得する">API: 習慣メモを取得する</h1> <p><code>/notes</code> API (GET) で習慣メモを取得できる👌パスパラメータには <code>/habits</code> API (GET) で取得した <code>habit_id</code> を設定する.クエリパラメータには <strong>Date Format</strong> で <code>from</code> と <code>to</code> を設定する.ただし普通にクエリパラメータを設定すると <code>Only accept the format of from is YYYY-MM-DDThh:mm:ss±hh:mm</code> というエラーになってしまうため,URL エンコードを忘れずに❗️</p> <p>習慣メモを取得できるのは特に嬉しくて,今日までだと以下のような結果(数値は合計日数)だった.<a href="https://nextjs.org/learn">Start building with Next.js</a><a href="https://b.hatena.ne.jp/entry/https://nextjs.org/learn" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://nextjs.org/learn" alt="" class="http-bookmark" /></a> に取り組んだり,<a href="https://www.amazon.co.jp/dp/4798068926?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">現場のプロがわかりやすく教える位置情報エンジニア養成講座</a>を読み進めている結果を確認できた👏</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ curl <span class="synSpecial">-s</span> <span class="synSpecial">--get</span> <span class="synSpecial">-H</span> <span class="synStatement">&quot;</span><span class="synConstant">Authorization: </span><span class="synPreProc">${API_KEY}</span><span class="synStatement">&quot;</span> <span class="synStatement">\</span> <span class="synSpecial">--data-urlencode</span> <span class="synStatement">&quot;</span><span class="synConstant">from=2024-01-01T00:00:00+09:00</span><span class="synStatement">&quot;</span> <span class="synStatement">\</span> <span class="synSpecial">--data-urlencode</span> <span class="synStatement">&quot;</span><span class="synConstant">to=2024-02-14T23:59:59+09:00</span><span class="synStatement">&quot;</span> <span class="synStatement">\</span> https://api.habitify.me/notes/xxxxxxxxxxxxxxxxxxxx | jq <span class="synSpecial">-r</span> <span class="synStatement">'</span><span class="synConstant">.data[].content</span><span class="synStatement">'</span> | <span class="synStatement">sort</span> | uniq <span class="synSpecial">-c</span> | <span class="synStatement">sort</span> <span class="synSpecial">--reverse</span> <span class="synConstant">17</span> Start building with Next.js <span class="synConstant">15</span> 位置情報エンジニア養成講座 <span class="synConstant">8</span> Bedrock ワークショップ <span class="synConstant">2</span> amazon-s3-multipart-upload-transfer-acceleration <span class="synConstant">1</span> Hasura Tutorial PostgreSQL <span class="synConstant">1</span> CDK x EventBridge Pipes <span class="synConstant">1</span> Bedrock デジタルトレーニング </pre> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.habitify.me%2Fcore-resources%2Fnotes" title="Notes - API Documentation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.habitify.me/core-resources/notes">docs.habitify.me</a></cite></p> <h1 id="まとめ">まとめ</h1> <p>Habitify API の基本的なリソース <code>Habits</code> と <code>Notes</code> の取得を試してみた❗️個人的には習慣メモを取得できるのは助かる👌他にも実装されてる API はあるけど,取得できるデータが微妙に不足してたり,開発が止まってるように見えるのはちょっと気になる😨例えば習慣の<strong>「連続記録」</strong>などは API では取得できなかったりする💨</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.habitify.me%2Fchange-log" title="Changelog - API Documentation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.habitify.me/change-log">docs.habitify.me</a></cite></p> <p>今後も Habitify を毎日使っていくぞー \( 'ω')/</p> kakku22 GitHub Actions から AWS CodeBuild のビルドを実行できる「aws-actions/aws-codebuild-run-build」 hatenablog://entry/6801883189081281396 2024-02-13T20:24:23+09:00 2024-02-13T20:24:23+09:00 AWS CodeBuild Run Build for GitHub Actions (aws-actions/aws-codebuild-run-build) を使うと GitHub Actions から AWS CodeBuild のビルドを実行できる👌 github.com シンプルに AWS CodeBuild のビルドを実行するだけなら project-name パラメータを指定すれば良くて簡単〜 \( 'ω')/ name: Start AWS CodeBuild build on: workflow_dispatch: push: branches: - master pull_… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240208/20240208152448.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CodeBuild Run Build for GitHub Actions (<code>aws-actions/aws-codebuild-run-build</code>) を使うと GitHub Actions から AWS CodeBuild のビルドを実行できる👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws-actions%2Faws-codebuild-run-build" title="GitHub - aws-actions/aws-codebuild-run-build: Run an AWS CodeBuild project as a step in a GitHub Actions workflow job." class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws-actions/aws-codebuild-run-build">github.com</a></cite></p> <p>シンプルに AWS CodeBuild のビルドを実行するだけなら <code>project-name</code> パラメータを指定すれば良くて簡単〜 \( 'ω')/</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">on</span><span class="synSpecial">:</span> <span class="synIdentifier">workflow_dispatch</span><span class="synSpecial">:</span> <span class="synIdentifier">push</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">pull_request</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">permissions</span><span class="synSpecial">:</span> <span class="synIdentifier">id-token</span><span class="synSpecial">:</span> write <span class="synIdentifier">contents</span><span class="synSpecial">:</span> read <span class="synIdentifier">jobs</span><span class="synSpecial">:</span> <span class="synIdentifier">build</span><span class="synSpecial">:</span> <span class="synIdentifier">runs-on</span><span class="synSpecial">:</span> ubuntu-latest <span class="synIdentifier">steps</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> actions/checkout@v4 <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/configure-aws-credentials@v4 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">role-to-assume</span><span class="synSpecial">:</span> ${{ secrets.AWS_ROLE_ARN }} <span class="synIdentifier">aws-region</span><span class="synSpecial">:</span> ap-northeast-1 <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox </pre> <p>ちなみに GitHub Actions で AWS アカウントの権限を取得する仕組みは以下の記事にまとめてある!</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F07%2F14%2F095049" title="GitHub Actions と AWS を OIDC で連携するときに自動的に証明書の検証をしてくれるようになった - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/07/14/095049">kakakakakku.hatenablog.com</a></cite></p> <p>もし GitHub リポジトリをモノリポ構成(例えば Yarn Workspaces など)で運用していて,アプリケーションごとに AWS CodeBuild を作らず共有するという選択肢もあると思う.</p> <p><code>buildspec-override</code> パラメータに <code>packgages/app1/buildspec.yml</code> などアプリケーションごとの <code>buildspec.yml</code> を設定すれば OK👌 さらにアプリケーションごとにビルド環境に必要なスペックが異なる場合は <code>compute-type-override</code> パラメータで <code>BUILD_GENERAL1_SMALL</code> や <code>BUILD_GENERAL1_MEDIUM</code> などの環境タイプを設定すれば OK👌柔軟に AWS CodeBuild のビルドを実行できて便利❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodebuild%2Flatest%2Fuserguide%2Fbuild-env-ref-compute-types.html" title="ビルド環境のコンピューティングモードおよびタイプ - AWS CodeBuild" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-compute-types.html">docs.aws.amazon.com</a></cite></p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build <span class="synIdentifier">on</span><span class="synSpecial">:</span> <span class="synIdentifier">workflow_dispatch</span><span class="synSpecial">:</span> <span class="synIdentifier">push</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">pull_request</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">permissions</span><span class="synSpecial">:</span> <span class="synIdentifier">id-token</span><span class="synSpecial">:</span> write <span class="synIdentifier">contents</span><span class="synSpecial">:</span> read <span class="synIdentifier">jobs</span><span class="synSpecial">:</span> <span class="synIdentifier">build</span><span class="synSpecial">:</span> <span class="synIdentifier">runs-on</span><span class="synSpecial">:</span> ubuntu-latest <span class="synIdentifier">steps</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> actions/checkout@v4 <span class="synStatement">- </span><span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/configure-aws-credentials@v4 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">role-to-assume</span><span class="synSpecial">:</span> ${{ secrets.AWS_ROLE_ARN }} <span class="synIdentifier">aws-region</span><span class="synSpecial">:</span> ap-northeast-1 <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build / app1 <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">buildspec-override</span><span class="synSpecial">:</span> packgages/app1/buildspec.yml <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build / app2 <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">buildspec-override</span><span class="synSpecial">:</span> packgages/app2/buildspec.yml <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Start AWS CodeBuild build / app3 <span class="synIdentifier">uses</span><span class="synSpecial">:</span> aws-actions/aws-codebuild-run-build@v1 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">project-name</span><span class="synSpecial">:</span> sandbox <span class="synIdentifier">buildspec-override</span><span class="synSpecial">:</span> packgages/app3/buildspec.yml <span class="synIdentifier">compute-type-override</span><span class="synSpecial">:</span> BUILD_GENERAL1_MEDIUM </pre> kakku22 Dependabot で AWS CDK を自動的にアップデートしよう hatenablog://entry/6801883189079324769 2024-02-12T15:11:58+09:00 2024-02-12T15:11:58+09:00 Dependabot version updates を使うと package.json に指定しているパッケージのアップデートを自動化できる❗️設定は比較的簡単で package-ecosystem に npm を設定して,あとは必須の directory と schedule.interval でアップデートの対象ディレクトリとスケジュールを決めれば OK👌個人的な AWS CDK 検証用プライベートリポジトリに設定して数週間試してみた \( 'ω')/ docs.github.com ちなみに以下の AWS CDK ドキュメントでは Dependabot と npm-check-upda… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240207/20240207182231.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><strong>Dependabot version updates</strong> を使うと <code>package.json</code> に指定しているパッケージのアップデートを自動化できる❗️設定は比較的簡単で <code>package-ecosystem</code> に <code>npm</code> を設定して,あとは必須の <code>directory</code> と <code>schedule.interval</code> でアップデートの対象ディレクトリとスケジュールを決めれば OK👌個人的な AWS CDK 検証用プライベートリポジトリに設定して数週間試してみた \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.github.com%2Fen%2Fcode-security%2Fdependabot%2Fdependabot-version-updates%2Fconfiguration-options-for-the-dependabot.yml-file" title="Configuration options for the dependabot.yml file - GitHub Docs" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file">docs.github.com</a></cite></p> <p>ちなみに以下の AWS CDK ドキュメントでは <strong>Dependabot</strong> と <strong>npm-check-updates</strong> が紹介されていた.他には <strong>reviewdog</strong> を使うという選択肢もあると思う🐶</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcdk%2Fv2%2Fguide%2Fmanage-dependencies.html" title="依存関係の管理 - AWS Cloud Development Kit (AWS CDK) v2" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/manage-dependencies.html">docs.aws.amazon.com</a></cite></p> <h2 id="-githubdependabotyml">🤖 .github/dependabot.yml</h2> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">version</span><span class="synSpecial">:</span> <span class="synConstant">2</span> <span class="synIdentifier">updates</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">package-ecosystem</span><span class="synSpecial">:</span> npm <span class="synIdentifier">directory</span><span class="synSpecial">:</span> / <span class="synIdentifier">schedule</span><span class="synSpecial">:</span> <span class="synIdentifier">interval</span><span class="synSpecial">:</span> daily <span class="synIdentifier">open-pull-requests-limit</span><span class="synSpecial">:</span> <span class="synConstant">2</span> <span class="synIdentifier">target-branch</span><span class="synSpecial">:</span> master <span class="synIdentifier">groups</span><span class="synSpecial">:</span> <span class="synIdentifier">aws-cdk-dependencies</span><span class="synSpecial">:</span> <span class="synIdentifier">patterns</span><span class="synSpecial">:</span> <span class="synStatement">- </span>aws-cdk <span class="synStatement">- </span>aws-cdk-lib <span class="synIdentifier">update-types</span><span class="synSpecial">:</span> <span class="synStatement">- </span>minor <span class="synStatement">- </span>patch <span class="synIdentifier">ignore</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">dependency-name</span><span class="synSpecial">:</span> <span class="synConstant">&quot;*&quot;</span> <span class="synIdentifier">update-types</span><span class="synSpecial">:</span> <span class="synSpecial">[</span><span class="synConstant">&quot;version-update:semver-major&quot;</span><span class="synSpecial">]</span> </pre> <p>ちなみに aws-cdk と aws-cdk-lib に関しては同じプルリクエストにまとめるために <code>groups</code> を設定している👌 <code>groups</code> は2023年8月にリリースされている❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.blog%2Fchangelog%2F2023-08-24-grouped-version-updates-for-dependabot-are-generally-available%2F" title="Grouped version updates for Dependabot are generally available" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.blog/changelog/2023-08-24-grouped-version-updates-for-dependabot-are-generally-available/">github.blog</a></cite></p> <h1 id="動作確認">動作確認</h1> <p>AWS CDK の <strong>aws-cdk</strong> と <strong>aws-cdk-lib</strong> を自動的にアップデートするプルリクエストが作れたー👏</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240212/20240212150907.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> kakku22 Python の独自パッケージを作って AWS CodeArtifact で管理する hatenablog://entry/6801883189079292861 2024-02-08T20:34:01+09:00 2024-02-08T20:34:26+09:00 開発チームで共通的に使うコードを独自ライブラリ(パッケージ)にして管理したい場面はあると思う.今回 AWS CodeArtifact で管理する流れを試す❗AWS CodeArtifact は npm / pip / Maven など複数のパッケージマネージャーをサポートしているけど,今回は pip (Python) を前提にする. aws.amazon.com AWS CodeArtifact と AWS CDK AWS CodeArtifact には大きく「ドメイン」と「リポジトリ」という概念がある.詳しくは以下のドキュメントにまとまっている👌 docs.aws.amazon.com AW… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240130/20240130234200.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>開発チームで共通的に使うコードを独自ライブラリ(パッケージ)にして管理したい場面はあると思う.今回 <strong>AWS CodeArtifact</strong> で管理する流れを試す❗AWS CodeArtifact は npm / pip / Maven など複数のパッケージマネージャーをサポートしているけど,今回は pip (Python) を前提にする.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fcodeartifact%2F" title="アーティファクトリポジトリ - AWS CodeArtifact - AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/codeartifact/">aws.amazon.com</a></cite></p> <h1 id="AWS-CodeArtifact-と-AWS-CDK">AWS CodeArtifact と AWS CDK</h1> <p>AWS CodeArtifact には大きく<strong>「ドメイン」</strong>と<strong>「リポジトリ」</strong>という概念がある.詳しくは以下のドキュメントにまとまっている👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodeartifact%2Flatest%2Fug%2Fcodeartifact-concepts.html" title="AWS CodeArtifact の概念 - CodeArtifact" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codeartifact/latest/ug/codeartifact-concepts.html">docs.aws.amazon.com</a></cite></p> <p>AWS CDK で AWS CodeArtifact を設定する場合,AWS CloudFormation に沿った <strong>L1 Construct</strong> の <code>CfnDomain</code> と <code>CfnRepository</code> を使う.設定次第ではあるけど,特に難しいところはないかなーと思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_codeartifact.CfnDomain.html" title="class CfnDomain (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codeartifact.CfnDomain.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_codeartifact.CfnRepository.html" title="class CfnRepository (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codeartifact.CfnRepository.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-codeartifact-stackts">👾 sandbox-cdk-codeartifact-stack.ts</h2> <p>今回は AWS CodeArtifact に <code>kakakakakku</code> ドメインと <code>sandbox-cdk-repository</code> リポジトリを設定する.リポジトリにアップストリームリポジトリとして <strong>pypi-store (Python Packaging Authority)</strong> を設定すると外部のパブリックリポジトリとして透過的にパッケージを扱えるようになる.今回はプライベートなパッケージのみを AWS CodeArtifact で管理する形を試したく,アップストリームリポジトリは設定しなかった.</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_codeartifact<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkCodeArtifactStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">);</span> <span class="synType">const</span> domain <span class="synStatement">=</span> <span class="synStatement">new</span> aws_codeartifact.CfnDomain<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkDomain'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> domainName: <span class="synConstant">'kakakakakku'</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> aws_codeartifact.CfnRepository<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkRepository'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> domainName: domain.domainName<span class="synStatement">,</span> repositoryName: <span class="synConstant">'sandbox-cdk-repository'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <h1 id="独自パッケージをパッケージングする">独自パッケージをパッケージングする</h1> <p>まずは以下の <strong>Python Packaging User Guide</strong> を参考にサンプルパッケージを実装する.今回はドキュメントに沿って <code>example_package_kakakakakku</code> という名前にした.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fpackaging.python.org%2Fen%2Flatest%2Ftutorials%2Fpackaging-projects%2F" title="Packaging Python Projects - Python Packaging User Guide" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/">packaging.python.org</a></cite></p> <p>コード的には数値を +1 する <code>add_one()</code> 関数を追加している👌</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">add_one</span>(number): <span class="synStatement">return</span> number + <span class="synConstant">1</span> </pre> <p>最終的に以下のようなディレクトリ階層になった.</p> <pre class="code tree" data-lang="tree" data-unlink>$ tree . -a -I dist . ├── .gitignore ├── LICENSE ├── README.md ├── pyproject.toml └── src └── example_package_kakakakakku ├── __init__.py └── example.py</pre> <p>検証中に出たエラーも残しておく💨</p> <pre class="code" data-lang="" data-unlink>ERROR &#39;example_package_kakakakakku-0.0.1/.gitignore&#39; is a link to an absolute path ERROR Backend subprocess exited when trying to invoke build_wheel</pre> <p>うまくパッケージングもできた❗</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ python3 <span class="synSpecial">-m</span> build (中略) Successfully built example_package_kakakakakku-0.<span class="synConstant">0</span>.<span class="synConstant">1</span>.tar.gz and example_package_kakakakakku-0.<span class="synConstant">0</span>.1-py3-none-any.whl $ <span class="synStatement">ls</span> <span class="synConstant">-1</span> dist example_package_kakakakakku-0.<span class="synConstant">0</span>.1-py3-none-any.whl example_package_kakakakakku-0.<span class="synConstant">0</span>.<span class="synConstant">1</span>.tar.gz </pre> <h1 id="パッケージを-AWS-CodeArtifact-にアップロードする">パッケージを AWS CodeArtifact にアップロードする</h1> <p>ドキュメントに書いてある通り,パッケージを AWS CodeArtifact にアップロードする場合は <strong>twine</strong> を使う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodeartifact%2Flatest%2Fug%2Fpython-configure-twine.html" title="CodeArtifact で twine を設定して使用する - CodeArtifact" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codeartifact/latest/ug/python-configure-twine.html">docs.aws.amazon.com</a></cite></p> <p><code>aws codeartifact login --tool twine</code> コマンドを使って認証トークンを取得しつつ,<code>twine upload --repository codeartifact</code> コマンドでパッケージをアップロードする.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws codeartifact login <span class="synSpecial">--tool</span> twine <span class="synSpecial">--domain</span> kakakakakku <span class="synSpecial">--repository</span> sandbox-cdk-repository <span class="synSpecial">--region</span> ap-northeast-1 $ twine upload <span class="synSpecial">--repository</span> codeartifact dist/* </pre> <p><figure class="figure-image figure-image-fotolife" title="AWS CodeArtifact にアップロードできた"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240130/20240130225257.png" width="1200" height="545" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CodeArtifact にアップロードできた</figcaption></figure></p> <h1 id="パッケージを使う">パッケージを使う</h1> <p>今度は AWS CodeArtifact にアップロードしたパッケージを使ったコードを実装する.ちなみに Python を Homebrew でインストールしていると <code>aws codeartifact login --tool pip</code> コマンドを実行したときに <code>pip was not found. Please verify installation.</code> というエラーが出てしまうという既知の問題がある.venv を使うと回避できるため今回は venv で仮想環境を作る.あとは普通に <code>pip install</code> コマンドで <code>example-package-kakakakakku</code> を取得する.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ python <span class="synSpecial">-m</span> venv venv $ <span class="synStatement">source</span> venv/bin/activate <span class="synPreProc">(</span><span class="synSpecial">venv</span><span class="synPreProc">)</span> $ aws codeartifact login <span class="synSpecial">--tool</span> pip <span class="synSpecial">--domain</span> kakakakakku <span class="synSpecial">--repository</span> sandbox-cdk-repository <span class="synSpecial">--region</span> ap-northeast-1 <span class="synPreProc">(</span><span class="synSpecial">venv</span><span class="synPreProc">)</span> $ pip install example-package-kakakakakku Successfully installed example-package-kakakakakku-0.<span class="synConstant">0</span>.<span class="synConstant">1</span> <span class="synPreProc">(</span><span class="synSpecial">venv</span><span class="synPreProc">)</span> $ pip list Package Version --------------------------- <span class="synSpecial">-------</span> example_package_kakakakakku <span class="synConstant">0</span>.<span class="synConstant">0</span>.<span class="synConstant">1</span> pip <span class="synConstant">23</span>.<span class="synConstant">3</span>.<span class="synConstant">1</span> setuptools <span class="synConstant">69</span>.<span class="synConstant">0</span>.<span class="synConstant">2</span> </pre> <p>そして以下のような <code>main.py</code> を実行するとうまく実行できる👏</p> <h2 id="-mainpy">👾 main.py</h2> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">from</span> example_package_kakakakakku <span class="synPreProc">import</span> example <span class="synIdentifier">print</span>(example.add_one(<span class="synConstant">1</span>)) <span class="synIdentifier">print</span>(example.add_one(<span class="synConstant">2</span>)) <span class="synIdentifier">print</span>(example.add_one(<span class="synConstant">3</span>)) </pre> <h1 id="注意点">注意点</h1> <p>今回は AWS CodeArtifact リポジトリにアップストリームリポジトリを設定しなかったため,例えば <code>requests</code> のような一般的なパッケージをインストールしようとするとエラーになってしまう💨</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synPreProc">(</span><span class="synSpecial">venv</span><span class="synPreProc">)</span> $ pip install requests Looking <span class="synError">in</span> indexes: https://aws:****@kakakakakku-000000000000.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/sandbox-cdk-repository/simple/ ERROR: Could not <span class="synStatement">find</span> a version that satisfies the requirement requests <span class="synPreProc">(</span><span class="synSpecial">from versions: none</span><span class="synPreProc">)</span> ERROR: No matching distribution found <span class="synStatement">for</span> requests </pre> <p>以下のドキュメントに載っている通り,<code>pip</code> コマンドの <code>-i / --index-url</code> オプションを使って pypi を明示的に指定するとインストールできるようになる.ただし正直これだと使いにくく感じるため,アップストリームリポジトリを設定すると良いのかなぁー \( 'ω')/ コストとのトレードオフになりそう.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ pip install <span class="synSpecial">--index-url</span> https://pypi.org/simple requests </pre> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodeartifact%2Flatest%2Fug%2Fpython-configure-pip.html" title="CodeArtifact で pip を設定して使用する - CodeArtifact" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codeartifact/latest/ug/python-configure-pip.html">docs.aws.amazon.com</a></cite></p> <p>あと AWS CodeArtifact の認証回りでうまく <code>pip</code> コマンドを実行できなくなったら以下の2ファイルを確認しつつ,削除してみると改善するかも💡</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ <span class="synStatement">rm</span> ~/.config/pip/pip.conf $ <span class="synStatement">rm</span> ~/.pypirc </pre> <p>今回はここまで❗️</p> <p>実は今まで <strong>AWS CodeArtifact</strong> は試したことがなくて勉強になった〜 \( 'ω')/</p> kakku22 AWS Lambda 関数 (Python) の import 時間を計測しよう / -X importtime オプション or 環境変数 PYTHONPROFILEIMPORTTIME hatenablog://entry/6801883189080018019 2024-02-05T13:53:54+09:00 2024-02-05T13:53:54+09:00 AWS Lambda の初期化フェーズ (INIT) は「10秒」に制限されている.例えば,AWS Lambda 関数の「ベストプラクティス」を意識してハンドラ外に実装した処理が長くなったり,AWS Lambda 関数 (Python) で機械学習系のライブラリなどを多く import しようとして遅くなり,結果的に INIT のタイムアウトが出てしまうことがあったりする. INIT_REPORT Init Duration: 10020.95 ms Phase: init Status: timeout AWS Lambda 関数 (Python) で import の最適化をするためには「… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240202/20240202222747.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> AWS Lambda の初期化フェーズ (INIT) は<a href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtime-environment.html">&#x300C;10&#x79D2;&#x300D;</a>に制限されている.例えば,AWS Lambda 関数の<a href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/best-practices.html">&#x300C;&#x30D9;&#x30B9;&#x30C8;&#x30D7;&#x30E9;&#x30AF;&#x30C6;&#x30A3;&#x30B9;&#x300D;</a>を意識してハンドラ外に実装した処理が長くなったり,AWS Lambda 関数 (Python) で機械学習系のライブラリなどを多く import しようとして遅くなり,結果的に INIT のタイムアウトが出てしまうことがあったりする.</p> <pre class="code" data-lang="" data-unlink>INIT_REPORT Init Duration: 10020.95 ms Phase: init Status: timeout</pre> <p>AWS Lambda 関数 (Python) で import の最適化をするためには<strong>「そもそもどの import が遅いのか」</strong>を確認したく,import の前後に計測用のログを仕込むこともできるけど,Python 3.7 から使える <code>-X importtime</code> オプション(もしくは環境変数 <code>PYTHONPROFILEIMPORTTIME</code>)が便利で AWS Lambda 関数でも使えるので紹介したいと思う💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.python.org%2Fja%2F3%2Fusing%2Fcmdline.html" title="1. コマンドラインと環境" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.python.org/ja/3/using/cmdline.html">docs.python.org</a></cite></p> <p>今回は以下の<strong>「計4パターン」</strong>で試していく❗️</p> <ol> <li>AWS Lambda 関数 (ZIP) x <code>PYTHONPROFILEIMPORTTIME</code> 環境変数</li> <li>AWS Lambda 関数 (ZIP) x Layer x <code>-X importtime</code> オプション</li> <li>AWS Lambda 関数 (Image) x <code>PYTHONPROFILEIMPORTTIME</code> 環境変数</li> <li>AWS Lambda 関数 (Image) x <code>-X importtime</code> オプション</li> </ol> <h1 id="サンプルコード">サンプルコード</h1> <p>あくまでサンプルなので意味はないけど,今回は boto3 / json / numpy / requests を import するコードを準備した👌</p> <h2 id="-requirementstxt">👾 requirements.txt</h2> <pre class="code" data-lang="" data-unlink>boto3 numpy requests</pre> <h2 id="-apppy">👾 app.py</h2> <pre class="code lang-sh" data-lang="sh" data-unlink>import boto3 import json import numpy as np import requests def lambda_handler<span class="synPreProc">(</span><span class="synSpecial">event, context</span><span class="synPreProc">)</span>: <span class="synStatement">return</span> <span class="synSpecial">{</span> <span class="synStatement">'</span><span class="synConstant">statusCode</span><span class="synStatement">'</span>: <span class="synConstant">200</span>, <span class="synStatement">'</span><span class="synConstant">body</span><span class="synStatement">'</span>: json.dumps<span class="synPreProc">(</span><span class="synSpecial">{</span><span class="synStatement">'</span><span class="synConstant">message</span><span class="synStatement">'</span>: <span class="synStatement">'</span><span class="synConstant">hello world</span><span class="synStatement">'</span><span class="synSpecial">}</span><span class="synPreProc">)</span> <span class="synSpecial">}</span> </pre> <h1 id="1-AWS-Lambda-関数-ZIP-x-PYTHONPROFILEIMPORTTIME-環境変数">1. AWS Lambda 関数 (ZIP) x <code>PYTHONPROFILEIMPORTTIME</code> 環境変数</h1> <h2 id="-templateyaml">👾 template.yaml</h2> <p>AWS SAM の <code>template.yaml</code> を以下のように書いた.</p> <p><code>Environment.Variables</code> で <code>PYTHONPROFILEIMPORTTIME: 1</code> を設定している😃</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">AWSTemplateFormatVersion</span><span class="synSpecial">:</span> 2010-09-09 <span class="synIdentifier">Transform</span><span class="synSpecial">:</span> AWS::Serverless-2016-10-31 <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">Function</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::Function <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">FunctionName</span><span class="synSpecial">:</span> importtime-env <span class="synIdentifier">CodeUri</span><span class="synSpecial">:</span> src/ <span class="synIdentifier">Handler</span><span class="synSpecial">:</span> app.lambda_handler <span class="synIdentifier">Runtime</span><span class="synSpecial">:</span> python3.12 <span class="synIdentifier">Architectures</span><span class="synSpecial">:</span> <span class="synStatement">- </span>x86_64 <span class="synIdentifier">Environment</span><span class="synSpecial">:</span> <span class="synIdentifier">Variables</span><span class="synSpecial">:</span> <span class="synIdentifier">PYTHONPROFILEIMPORTTIME</span><span class="synSpecial">:</span> <span class="synConstant">1</span> </pre> <p>AWS Lambda 関数 <code>importtime-env</code> をデプロイして実行すると以下のように import 時間がログに出力されていた❗️(ちなみに AWS CloudWatch Logs に出力されたログは表形式が崩れてしまっていて見にくかったため,以下のログは AWS SAM の <code>sam local invoke</code> コマンドの結果を載せている🙏)</p> <pre class="code" data-lang="" data-unlink>import time: self [us] | cumulative | imported package import time: 1041 | 1041 | _io import time: 89 | 89 | marshal import time: 826 | 826 | posix import time: 2987 | 4941 | _frozen_importlib_external import time: 361 | 361 | time import time: 446 | 807 | zipimport import time: 33 | 33 | _codecs import time: 424 | 456 | codecs import time: 542 | 542 | encodings.aliases import time: 3553 | 4551 | encodings import time: 170 | 170 | encodings.utf_8 import time: 158 | 158 | _signal import time: 44 | 44 | _abc import time: 427 | 471 | abc import time: 857 | 1327 | io import time: 82 | 82 | _stat import time: 100 | 181 | stat import time: 1870 | 1870 | _collections_abc import time: 38 | 38 | genericpath import time: 82 | 120 | posixpath import time: 992 | 3162 | os import time: 76 | 76 | _sitebuiltins import time: 186 | 186 | sitecustomize import time: 53 | 53 | usercustomize import time: 1784 | 5259 | site import time: 391 | 391 | types import time: 65 | 65 | _operator import time: 492 | 557 | operator import time: 226 | 226 | itertools import time: 142 | 142 | keyword import time: 217 | 217 | reprlib import time: 105 | 105 | _collections import time: 739 | 1426 | collections import time: 87 | 87 | _functools import time: 1574 | 3087 | functools import time: 1797 | 5829 | enum import time: 70 | 70 | _sre import time: 288 | 288 | re._constants import time: 349 | 636 | re._parser import time: 215 | 215 | re._casefix import time: 373 | 1294 | re._compiler import time: 165 | 165 | copyreg import time: 1651 | 8938 | re import time: 906 | 906 | _json import time: 1298 | 2203 | json.scanner import time: 1010 | 12151 | json.decoder import time: 1093 | 1093 | json.encoder import time: 452 | 13694 | json import time: 142 | 142 | collections.abc import time: 152 | 152 | token import time: 56 | 56 | _tokenize import time: 1129 | 1337 | tokenize import time: 151 | 1487 | linecache import time: 890 | 890 | textwrap import time: 464 | 464 | contextlib import time: 671 | 3653 | traceback import time: 266 | 266 | warnings import time: 621 | 621 | _weakrefset import time: 479 | 1100 | weakref import time: 40 | 40 | _string import time: 569 | 609 | string import time: 580 | 580 | threading import time: 41 | 41 | atexit import time: 2290 | 8536 | logging import time: 174 | 174 | awslambdaric import time: 309 | 309 | importlib import time: 188 | 188 | awslambdaric.lambda_context import time: 97 | 97 | awslambdaric.lambda_runtime_exception import time: 1399 | 1399 | runtime_client import time: 289 | 289 | numbers import time: 979 | 1267 | _decimal import time: 134 | 1401 | decimal import time: 263 | 263 | math import time: 131 | 131 | __future__ import time: 211 | 211 | simplejson.errors import time: 123 | 123 | simplejson.raw_json import time: 192 | 192 | _struct import time: 111 | 303 | struct import time: 128 | 128 | simplejson.compat import time: 206 | 206 | simplejson._speedups import time: 156 | 362 | simplejson.scanner import time: 247 | 1040 | simplejson.decoder import time: 542 | 542 | simplejson.encoder import time: 368 | 2412 | simplejson import time: 232 | 4305 | awslambdaric.lambda_runtime_marshaller import time: 553 | 6353 | awslambdaric.lambda_runtime_client import time: 510 | 510 | awslambdaric.lambda_runtime_log_utils import time: 330 | 7689 | awslambdaric.bootstrap import time: 166 | 8028 | awslambdaric.__main__ import time: 684 | 684 | concurrent import time: 653 | 653 | concurrent.futures._base import time: 233 | 1569 | concurrent.futures import time: 150 | 150 | _heapq import time: 178 | 327 | heapq import time: 215 | 215 | _queue import time: 306 | 847 | queue import time: 298 | 1145 | concurrent.futures.thread import time: 225 | 225 | errno import time: 715 | 715 | _socket import time: 199 | 199 | select import time: 616 | 815 | selectors import time: 320 | 320 | array import time: 1888 | 3736 | socket import time: 2242 | 2242 | botocore import time: 1241 | 1241 | botocore.vendored import time: 2355 | 2355 | botocore.vendored.requests.packages.urllib3.exceptions import time: 1555 | 3910 | botocore.vendored.requests.packages.urllib3 import time: 1041 | 4950 | botocore.vendored.requests.packages import time: 18 | 4968 | botocore.vendored.requests.packages.urllib3 import time: 24 | 4991 | botocore.vendored.requests.packages.urllib3.exceptions import time: 1767 | 6758 | botocore.vendored.requests.exceptions import time: 1630 | 8387 | botocore.vendored.requests import time: 8803 | 20672 | botocore.exceptions import time: 2310 | 22981 | boto3.exceptions import time: 4605 | 31546 | boto3.compat import time: 208 | 208 | copy import time: 86 | 86 | _wmi import time: 491 | 576 | platform import time: 193 | 193 | _bisect import time: 132 | 325 | bisect import time: 189 | 189 | _random import time: 82 | 82 | _sha512 import time: 7551 | 7551 | _hashlib import time: 211 | 211 | _blake2 import time: 1203 | 8965 | hashlib import time: 932 | 10490 | random import time: 190 | 190 | _ast import time: 1253 | 1442 | ast import time: 160 | 160 | _opcode import time: 548 | 708 | opcode import time: 735 | 1443 | dis import time: 68 | 68 | importlib.machinery import time: 2190 | 5141 | inspect import time: 751 | 5892 | jmespath.compat import time: 1454 | 7346 | jmespath.exceptions import time: 1945 | 9291 | jmespath.lexer import time: 1121 | 1121 | jmespath.ast import time: 2542 | 2542 | jmespath.functions import time: 2458 | 5000 | jmespath.visitor import time: 5000 | 30900 | jmespath.parser import time: 1373 | 32273 | jmespath import time: 894 | 894 | botocore.docs.bcdoc import time: 570 | 570 | _datetime import time: 161 | 730 | datetime import time: 694 | 694 | http import time: 158 | 158 | email import time: 484 | 484 | email.errors import time: 218 | 218 | binascii import time: 291 | 291 | email.quoprimime import time: 344 | 344 | base64 import time: 192 | 535 | email.base64mime import time: 153 | 153 | quopri import time: 105 | 257 | email.encoders import time: 223 | 480 | email.charset import time: 695 | 2216 | email.header import time: 181 | 181 | urllib import time: 1539 | 1539 | ipaddress import time: 1116 | 2835 | urllib.parse import time: 87 | 87 | _locale import time: 700 | 786 | locale import time: 1121 | 1906 | calendar import time: 311 | 2217 | email._parseaddr import time: 503 | 5554 | email.utils import time: 321 | 8090 | email._policybase import time: 464 | 9038 | email.feedparser import time: 349 | 9544 | email.parser import time: 205 | 205 | email._encoded_words import time: 118 | 118 | email.iterators import time: 444 | 766 | email.message import time: 1176 | 1176 | _ssl import time: 2240 | 3415 | ssl import time: 916 | 15334 | http.client import time: 273 | 273 | shlex import time: 192 | 192 | importlib._abc import time: 137 | 328 | importlib.util import time: 6200 | 6527 | botocore.vendored.six import time: 2665 | 2665 | dateutil._version import time: 1312 | 3976 | dateutil import time: 4745 | 4745 | six import time: 45 | 45 | six.moves import time: 2056 | 2056 | dateutil.tz._common import time: 1089 | 1089 | dateutil.tz._factories import time: 29 | 29 | six.moves.winreg import time: 1994 | 2022 | dateutil.tz.win import time: 8267 | 18223 | dateutil.tz.tz import time: 912 | 23110 | dateutil.tz import time: 58 | 58 | _typing import time: 2975 | 3033 | typing import time: 5996 | 5996 | urllib3.exceptions import time: 1576 | 1576 | urllib3.util.timeout import time: 3763 | 5338 | urllib3.util.connection import time: 925 | 925 | urllib3.util.util import time: 100 | 100 | brotlicffi import time: 87 | 87 | brotli import time: 89 | 89 | zstandard import time: 2076 | 3275 | urllib3.util.request import time: 854 | 854 | urllib3.util.response import time: 2573 | 2573 | urllib3.util.retry import time: 252 | 252 | hmac import time: 8511 | 8511 | urllib3.util.url import time: 2046 | 2046 | urllib3.util.ssltransport import time: 2199 | 13006 | urllib3.util.ssl_ import time: 1111 | 1111 | urllib3.util.wait import time: 1007 | 27162 | urllib3.util import time: 30 | 27192 | urllib3.util.connection import time: 1851 | 29043 | urllib3._base_connection import time: 3180 | 3180 | urllib3._collections import time: 580 | 580 | urllib3._version import time: 78 | 78 | _winapi import time: 102 | 102 | winreg import time: 434 | 612 | mimetypes import time: 1646 | 2258 | urllib3.fields import time: 982 | 3240 | urllib3.filepost import time: 331 | 331 | zlib import time: 97 | 97 | brotlicffi import time: 85 | 85 | brotli import time: 84 | 84 | zstandard import time: 1295 | 1295 | urllib3.util.ssl_match_hostname import time: 3902 | 5197 | urllib3.connection import time: 5173 | 10965 | urllib3.response import time: 1379 | 15583 | urllib3._request_methods import time: 619 | 619 | urllib3.util.proxy import time: 3896 | 20097 | urllib3.connectionpool import time: 3176 | 3176 | urllib3.poolmanager import time: 98 | 98 | urllib3_secure_extra import time: 2025 | 67224 | urllib3 import time: 198 | 198 | xml import time: 219 | 416 | xml.etree import time: 368 | 368 | xml.etree.ElementPath import time: 543 | 543 | pyexpat import time: 333 | 875 | _elementtree import time: 746 | 1988 | xml.etree.ElementTree import time: 172 | 2575 | xml.etree.cElementTree import time: 88 | 88 | awscrt import time: 29 | 117 | awscrt.auth import time: 169 | 169 | _compression import time: 327 | 495 | gzip import time: 3166 | 119546 | botocore.compat import time: 824 | 824 | html.entities import time: 382 | 1206 | html import time: 406 | 406 | _markupbase import time: 1612 | 3223 | html.parser import time: 2229 | 5451 | botocore.docs.bcdoc.docstringparser import time: 2839 | 2839 | botocore.docs.bcdoc.style import time: 2818 | 131547 | botocore.docs.bcdoc.restdoc import time: 135 | 135 | fnmatch import time: 85 | 85 | _winapi import time: 79 | 79 | nt import time: 70 | 70 | nt import time: 82 | 82 | nt import time: 72 | 72 | nt import time: 73 | 73 | nt import time: 70 | 70 | nt import time: 131 | 659 | ntpath import time: 716 | 1509 | pathlib import time: 286 | 286 | _bz2 import time: 330 | 615 | bz2 import time: 378 | 378 | _lzma import time: 478 | 855 | lzma import time: 512 | 1982 | shutil import time: 315 | 2296 | tempfile import time: 175 | 175 | urllib.response import time: 208 | 382 | urllib.error import time: 1552 | 4229 | urllib.request import time: 1050 | 1050 | dateutil._common import time: 2968 | 4018 | dateutil.relativedelta import time: 7682 | 11699 | dateutil.parser._parser import time: 2059 | 2059 | dateutil.parser.isoparser import time: 1534 | 15291 | dateutil.parser import time: 3007 | 3007 | botocore.awsrequest import time: 920 | 920 | urllib3.contrib import time: 74 | 74 | OpenSSL import time: 25 | 99 | OpenSSL.SSL import time: 4066 | 5083 | urllib3.contrib.pyopenssl import time: 359 | 359 | importlib.resources.abc import time: 353 | 353 | importlib.resources._adapters import time: 477 | 1188 | importlib.resources._common import time: 223 | 223 | importlib.resources._legacy import time: 265 | 1675 | importlib.resources import time: 2328 | 4003 | certifi.core import time: 1107 | 5109 | certifi import time: 2877 | 13068 | botocore.httpsession import time: 15381 | 52482 | botocore.utils import time: 1084 | 53566 | botocore.docs.shape import time: 1522 | 1522 | botocore.docs.utils import time: 1627 | 56713 | botocore.docs.example import time: 1995 | 1995 | botocore.docs.params import time: 1450 | 3445 | botocore.docs.method import time: 1605 | 1605 | botocore.docs.sharedexample import time: 2610 | 64372 | botocore.docs.client import time: 1318 | 1318 | botocore.docs.paginator import time: 1093 | 1093 | botocore.docs.waiter import time: 3153 | 201481 | botocore.docs.service import time: 1636 | 203117 | botocore.docs import time: 1037 | 204154 | botocore.docs.docstring import time: 2504 | 238930 | botocore.waiter import time: 2959 | 2959 | botocore.eventstream import time: 4886 | 7844 | botocore.parsers import time: 2668 | 2668 | botocore.validate import time: 3865 | 6532 | botocore.serialize import time: 73 | 73 | _uuid import time: 529 | 602 | uuid import time: 979 | 979 | botocore.history import time: 2781 | 2781 | botocore.hooks import time: 1370 | 1370 | botocore.response import time: 2575 | 3944 | botocore.httpchecksum import time: 2545 | 10850 | botocore.endpoint import time: 1332 | 12182 | botocore.config import time: 5471 | 5471 | botocore.auth import time: 957 | 957 | botocore.crt import time: 3826 | 3826 | botocore.endpoint_provider import time: 3396 | 13649 | botocore.regions import time: 2541 | 2541 | botocore.signers import time: 37 | 37 | botocore.customizations import time: 26 | 63 | botocore.customizations.useragent import time: 2052 | 2115 | botocore.useragent import time: 2907 | 47767 | botocore.args import time: 1283 | 1283 | botocore.compress import time: 304 | 304 | termios import time: 200 | 503 | getpass import time: 484 | 484 | signal import time: 276 | 276 | fcntl import time: 89 | 89 | msvcrt import time: 169 | 169 | _posixsubprocess import time: 625 | 1641 | subprocess import time: 1175 | 1175 | configparser import time: 1404 | 2579 | botocore.configloader import time: 2315 | 2315 | botocore.tokens import time: 8087 | 15123 | botocore.credentials import time: 4083 | 4083 | botocore.model import time: 2047 | 6129 | botocore.discovery import time: 3074 | 3074 | botocore.paginate import time: 1002 | 1002 | botocore.retries import time: 1167 | 1167 | botocore.retries.bucket import time: 837 | 837 | botocore.retries.quota import time: 612 | 612 | botocore.retries.base import time: 882 | 1494 | botocore.retries.special import time: 2382 | 4712 | botocore.retries.standard import time: 873 | 873 | botocore.retries.throttling import time: 2707 | 9457 | botocore.retries.adaptive import time: 5685 | 328446 | botocore.client import time: 1741 | 1741 | botocore.retryhandler import time: 678 | 678 | botocore.translate import time: 5960 | 8378 | botocore.handlers import time: 2656 | 2656 | botocore.monitoring import time: 3620 | 3620 | botocore.configprovider import time: 1016 | 1016 | botocore.errorfactory import time: 1885 | 1885 | botocore.loaders import time: 5061 | 351634 | botocore.session import time: 939 | 939 | boto3.utils import time: 748 | 748 | boto3.resources import time: 738 | 738 | boto3.docs.client import time: 911 | 911 | boto3.docs.base import time: 878 | 878 | boto3.docs.method import time: 1310 | 1310 | boto3.docs.utils import time: 1322 | 4420 | boto3.docs.action import time: 812 | 812 | boto3.docs.attr import time: 1542 | 1542 | boto3.docs.collection import time: 1242 | 1242 | boto3.docs.subresource import time: 1202 | 1202 | boto3.docs.waiter import time: 2142 | 11359 | boto3.docs.resource import time: 4560 | 16656 | boto3.docs.service import time: 1104 | 17759 | boto3.docs import time: 967 | 967 | boto3.docs.docstring import time: 2357 | 2357 | boto3.resources.model import time: 1125 | 1125 | boto3.resources.params import time: 1329 | 1329 | boto3.resources.response import time: 1261 | 6071 | boto3.resources.action import time: 1101 | 1101 | boto3.resources.base import time: 1884 | 1884 | boto3.resources.collection import time: 4119 | 32645 | boto3.resources.factory import time: 2010 | 387435 | boto3.session import time: 3584 | 422564 | boto3 import time: 1792 | 1792 | numpy._utils._convertions import time: 1065 | 2856 | numpy._utils import time: 4987 | 7843 | numpy._globals import time: 1247 | 1247 | numpy.exceptions import time: 812 | 812 | numpy.version import time: 37 | 37 | numpy._distributor_init_local import time: 1031 | 1067 | numpy._distributor_init import time: 1391 | 1391 | numpy._utils._inspect import time: 4203 | 4203 | numpy.core._exceptions import time: 2220 | 2220 | numpy.dtypes import time: 47163 | 53585 | numpy.core._multiarray_umath import time: 2926 | 57902 | numpy.core.overrides import time: 7971 | 65872 | numpy.core.multiarray import time: 5783 | 5783 | numpy.core.umath import time: 2581 | 2581 | numpy.core._string_helpers import time: 213 | 213 | pickle5 import time: 577 | 577 | _compat_pickle import time: 977 | 977 | _pickle import time: 2721 | 4274 | pickle import time: 4231 | 8717 | numpy.compat.py3k import time: 3481 | 12197 | numpy.compat import time: 5065 | 5065 | numpy.core._dtype import time: 6900 | 24161 | numpy.core._type_aliases import time: 8037 | 34778 | numpy.core.numerictypes import time: 469 | 469 | _contextvars import time: 689 | 1157 | contextvars import time: 3892 | 5048 | numpy.core._ufunc_config import time: 7476 | 12524 | numpy.core._methods import time: 14225 | 26748 | numpy.core.fromnumeric import time: 7659 | 34407 | numpy.core.shape_base import time: 18204 | 18204 | numpy.core.arrayprint import time: 3078 | 3078 | numpy.core._asarray import time: 17565 | 73253 | numpy.core.numeric import time: 17894 | 17894 | numpy.core.defchararray import time: 10865 | 10865 | numpy.core.records import time: 8270 | 8270 | numpy.core.memmap import time: 8022 | 8022 | numpy.core.function_base import time: 10691 | 10691 | numpy.core._machar import time: 12212 | 12212 | numpy.core.getlimits import time: 18510 | 18510 | numpy.core.einsumfunc import time: 4183 | 4183 | numpy.core._multiarray_tests import time: 19182 | 23365 | numpy.core._add_newdocs import time: 2135 | 2135 | numpy.core._add_newdocs_scalars import time: 1353 | 1353 | numpy.core._dtype_ctypes import time: 512 | 512 | _ctypes import time: 290 | 290 | ctypes._endian import time: 2091 | 2892 | ctypes import time: 4432 | 7324 | numpy.core._internal import time: 1356 | 1356 | numpy._pytesttester import time: 8381 | 310056 | numpy.core import time: 34 | 310089 | numpy.core._multiarray_umath import time: 1585 | 311673 | numpy.__config__ import time: 6496 | 6496 | numpy.lib.mixins import time: 1105 | 1105 | numpy.lib.ufunclike import time: 2173 | 3278 | numpy.lib.type_check import time: 3751 | 7028 | numpy.lib.scimath import time: 1968 | 1968 | numpy.lib.stride_tricks import time: 2291 | 4259 | numpy.lib.twodim_base import time: 1709 | 1709 | numpy.linalg._umath_linalg import time: 3938 | 3938 | numpy._typing._nested_sequence import time: 668 | 668 | numpy._typing._nbit import time: 1909 | 1909 | numpy._typing._char_codes import time: 901 | 901 | numpy._typing._scalars import time: 579 | 579 | numpy._typing._shape import time: 2284 | 2284 | numpy._typing._dtype_like import time: 2896 | 2896 | numpy._typing._array_like import time: 1954 | 15125 | numpy._typing import time: 8540 | 29631 | numpy.linalg.linalg import time: 1296 | 30926 | numpy.linalg import time: 4079 | 35005 | numpy.matrixlib.defmatrix import time: 1447 | 36451 | numpy.matrixlib import time: 3048 | 3048 | numpy.lib.histograms import time: 13808 | 16856 | numpy.lib.function_base import time: 3018 | 56324 | numpy.lib.index_tricks import time: 3627 | 3627 | numpy.lib.nanfunctions import time: 2984 | 2984 | numpy.lib.shape_base import time: 4077 | 4077 | numpy.lib.polynomial import time: 4251 | 4251 | numpy.lib.utils import time: 2735 | 2735 | numpy.lib.arraysetops import time: 2799 | 2799 | numpy.lib.format import time: 2171 | 2171 | numpy.lib._datasource import time: 3395 | 3395 | numpy.lib._iotools import time: 7182 | 15546 | numpy.lib.npyio import time: 1428 | 1428 | numpy.lib.arrayterator import time: 2928 | 2928 | numpy.lib.arraypad import time: 1181 | 1181 | numpy.lib._version import time: 2177 | 110776 | numpy.lib import time: 943 | 943 | numpy.fft._pocketfft_internal import time: 3747 | 4690 | numpy.fft._pocketfft import time: 1083 | 1083 | numpy.fft.helper import time: 1119 | 6891 | numpy.fft import time: 2703 | 2703 | numpy.polynomial.polyutils import time: 3927 | 3927 | numpy.polynomial._polybase import time: 8857 | 15487 | numpy.polynomial.polynomial import time: 3503 | 3503 | numpy.polynomial.chebyshev import time: 2790 | 2790 | numpy.polynomial.legendre import time: 2856 | 2856 | numpy.polynomial.hermite import time: 2929 | 2929 | numpy.polynomial.hermite_e import time: 2790 | 2790 | numpy.polynomial.laguerre import time: 2001 | 32353 | numpy.polynomial import time: 78 | 78 | backports_abc import time: 4047 | 4124 | numpy.random._common import time: 172 | 172 | secrets import time: 2772 | 7067 | numpy.random.bit_generator import time: 1662 | 1662 | numpy.random._bounded_integers import time: 1472 | 1472 | numpy.random._mt19937 import time: 8317 | 18517 | numpy.random.mtrand import time: 1542 | 1542 | numpy.random._philox import time: 1664 | 1664 | numpy.random._pcg64 import time: 1384 | 1384 | numpy.random._sfc64 import time: 3873 | 3873 | numpy.random._generator import time: 4611 | 31589 | numpy.random._pickle import time: 1380 | 32968 | numpy.random import time: 2748 | 2748 | numpy.ctypeslib import time: 29009 | 29009 | numpy.ma.core import time: 6710 | 6710 | numpy.ma.extras import time: 1133 | 36852 | numpy.ma import time: 13048 | 558273 | numpy import time: 89 | 89 | chardet import time: 4947 | 4947 | charset_normalizer.constant import time: 1019 | 1019 | charset_normalizer.md__mypyc import time: 292 | 292 | unicodedata import time: 240 | 240 | _multibytecodec import time: 2500 | 3031 | charset_normalizer.utils import time: 2105 | 6154 | charset_normalizer.md import time: 3039 | 3039 | charset_normalizer.models import time: 2431 | 16570 | charset_normalizer.cd import time: 4711 | 21280 | charset_normalizer.api import time: 992 | 992 | charset_normalizer.legacy import time: 717 | 717 | charset_normalizer.version import time: 1189 | 24176 | charset_normalizer import time: 2043 | 2043 | http.cookiejar import time: 1044 | 1044 | http.cookies import time: 964 | 28314 | requests.compat import time: 6070 | 34383 | requests.exceptions import time: 110 | 110 | chardet import time: 70 | 70 | chardet import time: 2833 | 2833 | idna.package_data import time: 3689 | 3689 | idna.idnadata import time: 905 | 905 | idna.intranges import time: 3078 | 7671 | idna.core import time: 1230 | 11732 | idna import time: 1293 | 13094 | requests.packages import time: 96 | 96 | zipfile._path.glob import time: 401 | 497 | zipfile._path import time: 139 | 139 | zipfile.__main__ import time: 651 | 1285 | zipfile import time: 670 | 670 | requests.certs import time: 904 | 904 | requests.__version__ import time: 991 | 991 | requests._internal_utils import time: 2560 | 2560 | requests.cookies import time: 1027 | 1027 | requests.structures import time: 91 | 91 | importlib.resources._itertools import time: 287 | 378 | importlib.resources.readers import time: 144 | 522 | importlib.readers import time: 4215 | 12171 | requests.utils import time: 1928 | 1928 | requests.auth import time: 256 | 256 | stringprep import time: 277 | 533 | encodings.idna import time: 750 | 750 | requests.hooks import time: 1594 | 1594 | requests.status_codes import time: 3883 | 6759 | requests.models import time: 80 | 80 | socks import time: 1465 | 1545 | urllib3.contrib.socks import time: 2259 | 12489 | requests.adapters import time: 2922 | 15411 | requests.sessions import time: 940 | 16351 | requests.api import time: 2190 | 78298 | requests</pre> <h1 id="2-AWS-Lambda-関数-ZIP-x-Layer-x--X-importtime-オプション">2. AWS Lambda 関数 (ZIP) x Layer x <code>-X importtime</code> オプション</h1> <p>AWS Lambda では拡張機能 (Lambda extensions) を使ってランタイムに任意のオプションを追加することができる.以下のドキュメントにはラッパースクリプトを書いて Python ランタイムに <code>-X importtime</code> オプションを追加する例が載っている👀</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Flambda%2Flatest%2Fdg%2Fruntimes-modify.html" title="ランタイム環境の変更 - AWS Lambda" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-modify.html">docs.aws.amazon.com</a></cite></p> <h2 id="-templateyaml-1">👾 template.yaml</h2> <p>AWS SAM の <code>template.yaml</code> を以下のように書いた.</p> <p>Python ランタイムに <code>-X importtime</code> オプションを追加するラッパースクリプトを含んだ AWS Lambda Layer を作る.そして AWS Lambda 関数の環境変数 <code>AWS_LAMBDA_EXEC_WRAPPER</code> に <code>/opt/importtime_wrapper</code> を設定する.</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">AWSTemplateFormatVersion</span><span class="synSpecial">:</span> 2010-09-09 <span class="synIdentifier">Transform</span><span class="synSpecial">:</span> AWS::Serverless-2016-10-31 <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">Function</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::Function <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">FunctionName</span><span class="synSpecial">:</span> importtime-wrapper <span class="synIdentifier">CodeUri</span><span class="synSpecial">:</span> src/ <span class="synIdentifier">Handler</span><span class="synSpecial">:</span> app.lambda_handler <span class="synIdentifier">Runtime</span><span class="synSpecial">:</span> python3.12 <span class="synIdentifier">Architectures</span><span class="synSpecial">:</span> <span class="synStatement">- </span>x86_64 <span class="synIdentifier">Layers</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synType">!Ref</span> Layer <span class="synIdentifier">Environment</span><span class="synSpecial">:</span> <span class="synIdentifier">Variables</span><span class="synSpecial">:</span> <span class="synIdentifier">AWS_LAMBDA_EXEC_WRAPPER</span><span class="synSpecial">:</span> /opt/importtime_wrapper <span class="synIdentifier">Layer</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::LayerVersion <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">LayerName</span><span class="synSpecial">:</span> importtime-wrapper-layer <span class="synIdentifier">ContentUri</span><span class="synSpecial">:</span> layer/src/ <span class="synIdentifier">CompatibleRuntimes</span><span class="synSpecial">:</span> <span class="synStatement">- </span>python3.12 </pre> <h2 id="-importtime_wrapper">👾 importtime_wrapper</h2> <p><code>importtime_wrapper</code> はドキュメントの通りにしておく.そして,デプロイする前に忘れずに <code>chmod +x layer/src/importtime_wrapper</code> コマンドを実行してラッパースクリプトに実行権限を与えておく.</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synComment">#!/bin/bash</span> <span class="synComment"># the path to the interpreter and all of the originally intended arguments</span> <span class="synIdentifier">args</span>=<span class="synPreProc">(</span><span class="synStatement">&quot;</span><span class="synPreProc">$@</span><span class="synStatement">&quot;</span><span class="synPreProc">)</span> <span class="synComment"># the extra options to pass to the interpreter</span> <span class="synIdentifier">extra_args</span>=<span class="synPreProc">(</span><span class="synStatement">&quot;</span><span class="synConstant">-X</span><span class="synStatement">&quot;</span> <span class="synStatement">&quot;</span><span class="synConstant">importtime</span><span class="synStatement">&quot;</span><span class="synPreProc">)</span> <span class="synComment"># insert the extra options</span> <span class="synIdentifier">args</span>=<span class="synPreProc">(</span><span class="synStatement">&quot;</span><span class="synPreProc">${args[</span>@<span class="synPreProc">]</span><span class="synError">:</span><span class="synPreProc">0</span><span class="synStatement">:</span><span class="synPreProc">$#</span><span class="synStatement">-1</span><span class="synPreProc">}</span><span class="synStatement">&quot;</span> <span class="synStatement">&quot;</span><span class="synPreProc">${extra_args[</span>@<span class="synPreProc">]}</span><span class="synStatement">&quot;</span> <span class="synStatement">&quot;</span><span class="synPreProc">${args[</span>@<span class="synPreProc">]</span><span class="synError">: -</span><span class="synPreProc">1}</span><span class="synStatement">&quot;</span><span class="synPreProc">)</span> <span class="synComment"># start the runtime with the extra options</span> <span class="synStatement">exec</span> <span class="synStatement">&quot;</span><span class="synPreProc">${args[</span>@<span class="synPreProc">]}</span><span class="synStatement">&quot;</span> </pre> <h2 id="実行結果割愛">実行結果(割愛)</h2> <p>AWS Lambda 関数 <code>importtime-wrapper</code> をデプロイして実行すると同じく import 時間がログに出力されていた❗️</p> <h1 id="3-AWS-Lambda-関数-Image-x-PYTHONPROFILEIMPORTTIME-環境変数">3. AWS Lambda 関数 (Image) x <code>PYTHONPROFILEIMPORTTIME</code> 環境変数</h1> <h2 id="-templateyaml-2">👾 template.yaml</h2> <p>AWS SAM の <code>template.yaml</code> を以下のように書いた.</p> <p><code>Environment.Variables</code> で <code>PYTHONPROFILEIMPORTTIME: 1</code> を設定している😃</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">AWSTemplateFormatVersion</span><span class="synSpecial">:</span> 2010-09-09 <span class="synIdentifier">Transform</span><span class="synSpecial">:</span> AWS::Serverless-2016-10-31 <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">Function</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::Function <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">FunctionName</span><span class="synSpecial">:</span> importtime-env-image <span class="synIdentifier">PackageType</span><span class="synSpecial">:</span> Image <span class="synIdentifier">Architectures</span><span class="synSpecial">:</span> <span class="synStatement">- </span>x86_64 <span class="synIdentifier">Environment</span><span class="synSpecial">:</span> <span class="synIdentifier">Variables</span><span class="synSpecial">:</span> <span class="synIdentifier">PYTHONPROFILEIMPORTTIME</span><span class="synSpecial">:</span> <span class="synConstant">1</span> <span class="synIdentifier">Metadata</span><span class="synSpecial">:</span> <span class="synIdentifier">Dockerfile</span><span class="synSpecial">:</span> Dockerfile <span class="synIdentifier">DockerContext</span><span class="synSpecial">:</span> ./src <span class="synIdentifier">DockerTag</span><span class="synSpecial">:</span> python3.12-v1 </pre> <h2 id="-Dockerfile">👾 Dockerfile</h2> <pre class="code lang-sh" data-lang="sh" data-unlink>FROM public.ecr.aws/lambda/python:3.<span class="synConstant">12</span> COPY app.py requirements.txt ./ RUN python3.<span class="synConstant">12</span> <span class="synSpecial">-m</span> pip install <span class="synSpecial">-r</span> requirements.txt <span class="synSpecial">-t</span> . CMD <span class="synStatement">[&quot;</span><span class="synConstant">app.lambda_handler</span><span class="synStatement">&quot;]</span> </pre> <h2 id="実行結果割愛-1">実行結果(割愛)</h2> <p>AWS Lambda 関数 <code>importtime-env-image</code> をデプロイして実行すると同じく import 時間がログに出力されていた❗️</p> <h1 id="4-AWS-Lambda-関数-Image-x--X-importtime-オプション">4. AWS Lambda 関数 (Image) x <code>-X importtime</code> オプション</h1> <p>以下のドキュメントを参考に <code>python:3.12</code> など一般的なコンテナイメージ (non-AWS base image) に AWS Lambda Runtime Interface Client (RIC) をインストールして実行するパターンを試す.Dockerfile のサンプルもドキュメントを参考にした.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Flambda%2Flatest%2Fdg%2Fpython-image.html" title="コンテナイメージで Python Lambda 関数をデプロイする - AWS Lambda" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-image.html">docs.aws.amazon.com</a></cite></p> <h2 id="-templateyaml-3">👾 template.yaml</h2> <p>AWS SAM の <code>template.yaml</code> を以下のように書いた.</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">AWSTemplateFormatVersion</span><span class="synSpecial">:</span> 2010-09-09 <span class="synIdentifier">Transform</span><span class="synSpecial">:</span> AWS::Serverless-2016-10-31 <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">Function</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> AWS::Serverless::Function <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">FunctionName</span><span class="synSpecial">:</span> importtime-option-image <span class="synIdentifier">PackageType</span><span class="synSpecial">:</span> Image <span class="synIdentifier">Architectures</span><span class="synSpecial">:</span> <span class="synStatement">- </span>x86_64 <span class="synIdentifier">Metadata</span><span class="synSpecial">:</span> <span class="synIdentifier">Dockerfile</span><span class="synSpecial">:</span> Dockerfile <span class="synIdentifier">DockerContext</span><span class="synSpecial">:</span> ./src <span class="synIdentifier">DockerTag</span><span class="synSpecial">:</span> python3.12-v1 </pre> <h2 id="-Dockerfile-1">👾 Dockerfile</h2> <p>ポイントは <code>ENTRYPOINT</code> に <code>-X importtime</code> オプションを追加しているところ📝</p> <pre class="code lang-sh" data-lang="sh" data-unlink>ARG <span class="synIdentifier">FUNCTION_DIR</span>=<span class="synStatement">&quot;</span><span class="synConstant">/function</span><span class="synStatement">&quot;</span> FROM python:3.<span class="synConstant">12</span> as build-image ARG FUNCTION_DIR RUN <span class="synStatement">mkdir</span> <span class="synSpecial">-p</span> <span class="synPreProc">${FUNCTION_DIR}</span> COPY<span class="synStatement"> . </span><span class="synPreProc">${FUNCTION_DIR}</span> RUN pip install <span class="synSpecial">--target</span> <span class="synPreProc">${FUNCTION_DIR}</span> <span class="synSpecial">-r</span> <span class="synPreProc">${FUNCTION_DIR}</span>/requirements.txt FROM python:3.12-slim ARG FUNCTION_DIR WORKDIR <span class="synPreProc">${FUNCTION_DIR}</span> COPY <span class="synSpecial">--from=build-image</span> <span class="synPreProc">${FUNCTION_DIR}</span> <span class="synPreProc">${FUNCTION_DIR}</span> ENTRYPOINT <span class="synStatement">[</span> <span class="synStatement">&quot;</span><span class="synConstant">/usr/local/bin/python</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">-X</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">importtime</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">-m</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">awslambdaric</span><span class="synStatement">&quot;</span> <span class="synStatement">]</span> CMD <span class="synStatement">[&quot;</span><span class="synConstant">app.lambda_handler</span><span class="synStatement">&quot;]</span> </pre> <h2 id="-requirementstxt-1">👾 requirements.txt</h2> <p><code>requirements.txt</code> に <code>awslambdaric</code> を追加した.</p> <pre class="code" data-lang="" data-unlink>awslambdaric boto3 numpy requests</pre> <h2 id="実行結果割愛-2">実行結果(割愛)</h2> <p>AWS Lambda 関数 <code>importtime-option-image</code> をデプロイして実行すると同じく import 時間がログに出力されていた❗️</p> <h1 id="まとめ">まとめ</h1> <p>AWS Lambda 関数 (Python) で import の最適化をするときには Python の <code>-X importtime</code> オプション(もしくは環境変数 <code>PYTHONPROFILEIMPORTTIME</code>)が便利〜👏 という紹介記事でした \( 'ω')/</p> <h1 id="参考記事">参考記事</h1> <p>以下の記事でも AWS Lambda 関数 (Python) に <code>PYTHONPROFILEIMPORTTIME</code> 環境変数を設定して INIT を最適化する方法が紹介されているので合わせて読んでみると良いかも📝</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Fcompute%2Fbuilding-well-architected-serverless-applications-optimizing-application-performance-part-1%2F" title="Building well-architected serverless applications: Optimizing application performance – part 1 | Amazon Web Services" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/blogs/compute/building-well-architected-serverless-applications-optimizing-application-performance-part-1/">aws.amazon.com</a></cite></p> kakku22 AWS CDK の cdk import コマンドを使って既存リソースをインポートする hatenablog://entry/6801883189077505421 2024-01-31T19:05:11+09:00 2024-01-31T19:05:11+09:00 AWS CDK の cdk import コマンドを使って既存リソースをインポートする(取り込む)手順を試してみた❗️何かしらの理由があって Infrastructure as Code (IaC) に組み込めず,そのままプロダクションにリリースされてしまうということもあると思う. Amazon S3 バケットと Amazon CloudWatch Logs ロググループ 今回は比較的シンプルな構成として,Amazon S3 バケットと Amazon CloudWatch Logs ロググループをインポートをする.実際の現場ではもっと多くのリソースをインポートしたいという場面もあるとは思うけど… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240124/20240124004757.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK の <code>cdk import</code> コマンドを使って既存リソースをインポートする(取り込む)手順を試してみた❗️何かしらの理由があって Infrastructure as Code (IaC) に組み込めず,そのままプロダクションにリリースされてしまうということもあると思う.</p> <h2 id="Amazon-S3-バケットと-Amazon-CloudWatch-Logs-ロググループ">Amazon S3 バケットと Amazon CloudWatch Logs ロググループ</h2> <p>今回は比較的シンプルな構成として,Amazon S3 バケットと Amazon CloudWatch Logs ロググループをインポートをする.実際の現場ではもっと多くのリソースをインポートしたいという場面もあるとは思うけど...!</p> <p>まずはマネジメントコンソールを使って以下のように設定しておく👌</p> <ul> <li>Amazon S3 バケット <ul> <li>バケット名: <code>kakakakakku-sandbox-cdk-import</code></li> <li>バージョニング: <code>有効</code></li> </ul> </li> <li>Amazon CloudWatch Logs ロググループ <ul> <li>ロググループ名: <code>kakakakakku-sandbox-cdk-import</code></li> <li>保持期間: <code>7日間</code></li> <li>ログクラス: <code>低頻度アクセス</code></li> </ul> </li> </ul> <p>Amazon CloudWatch Logs ロググループのログクラス(低頻度アクセス)に関しては以下の記事も参照してもらえればと💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F12%2F24%2F165737" title="AWS CDK で ログクラス(低頻度アクセス)の Amazon CloudWatch Logs ロググループを追加する - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/12/24/165737">kakakakakku.hatenablog.com</a></cite></p> <h1 id="Step1">Step.1</h1> <p>まずは AWS CDK でインポート用のスタックを作る.</p> <h2 id="-sandbox-cdk-import-stackts">👾 sandbox-cdk-import-stack.ts</h2> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkImportStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p>一度スタックをデプロイしておく✔️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ cdk deploy SandboxCdkImportStack </pre> <p><figure class="figure-image figure-image-fotolife" title="AWS CloudFormation: SandboxCdkImportStack スタック"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123230429.png" width="1200" height="551" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CloudFormation: SandboxCdkImportStack スタック</figcaption></figure></p> <h1 id="Step2">Step.2</h1> <p>次に Amazon S3 バケットと Amazon CloudWatch Logs ロググループの設定を確認しつつ,AWS CDK で同じように実装する.<code>removalPolicy</code> は必ず <code>RemovalPolicy.RETAIN</code> にしておく.ちなみに今回は通常のプロパティ以外に <code>bucketName</code> と <code>logGroupName</code> も設定しているけど,AWS CDK のドキュメントを読むとリソース名は設定しないことが推奨と書かれていたりする📝このあたりはチームの戦略によるところかなーと.</p> <blockquote><p>We usually recommend to not include resource names into your AWS CDK resource definitions so that it becomes easier to deploy your resources multiple times.</p></blockquote> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fv2%2Fguide%2Fcli.html" title="AWS CDK Toolkit (cdk command) - AWS Cloud Development Kit (AWS CDK) v2" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/v2/guide/cli.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-import-stackts-1">👾 sandbox-cdk-import-stack.ts</h2> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> RemovalPolicy<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_logs<span class="synStatement">,</span> aws_s3<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkImportStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synStatement">new</span> aws_s3.Bucket<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkImportBucket'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> bucketName: <span class="synConstant">'kakakakakku-sandbox-cdk-import'</span><span class="synStatement">,</span> versioned: <span class="synConstant">true</span><span class="synStatement">,</span> removalPolicy: RemovalPolicy.RETAIN <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> aws_logs.LogGroup<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkImportLogGroup'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> logGroupName: <span class="synConstant">'kakakakakku-sandbox-cdk-import'</span><span class="synStatement">,</span> retention: aws_logs.RetentionDays.ONE_WEEK<span class="synStatement">,</span> logGroupClass: aws_logs.LogGroupClass.INFREQUENT_ACCESS<span class="synStatement">,</span> removalPolicy: RemovalPolicy.RETAIN <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p>そして <code>cdk import</code> コマンドを実行する.リソース名を省略する場合はリソース名を入力するようにプロンプトが表示されるけど,今回はリソース名を設定しているため <code>yes/no</code> を入力するようにプロンプトが表示された.どちらも <code>yes</code> にしておく.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ cdk import SandboxCdkImportStack SandboxCdkImportStack SandboxCdkImportStack/SandboxCdkImportBucket/Resource <span class="synPreProc">(</span><span class="synSpecial">AWS::S3::Bucket</span><span class="synPreProc">)</span>: import with <span class="synIdentifier">BucketName</span>=kakakakakku-sandbox-cdk-import <span class="synPreProc">(</span><span class="synSpecial">yes/no</span><span class="synPreProc">)</span> <span class="synStatement">[</span>default: yes<span class="synStatement">]</span>? yes SandboxCdkImportStack/SandboxCdkImportLogGroup/Resource <span class="synPreProc">(</span><span class="synSpecial">AWS::Logs::LogGroup</span><span class="synPreProc">)</span>: import with <span class="synIdentifier">LogGroupName</span>=kakakakakku-sandbox-cdk-import <span class="synPreProc">(</span><span class="synSpecial">yes/no</span><span class="synPreProc">)</span> <span class="synStatement">[</span>default: yes<span class="synStatement">]</span>? yes SandboxCdkImportStack: importing resources into stack... SandboxCdkImportStack: creating CloudFormation changeset... ✅ SandboxCdkImportStack Import operation <span class="synStatement">complete</span>. We recommend you run a drift detection operation to confirm your CDK app resource definitions are up-to-date. Read more here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/detect-drift-stack.html </pre> <p>すると以下のようにインポートできた✔️</p> <p><figure class="figure-image figure-image-fotolife" title="AWS CloudFormation: SandboxCdkImportStack スタック"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123230459.png" width="1200" height="551" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CloudFormation: SandboxCdkImportStack スタック</figcaption></figure></p> <h1 id="Step3">Step.3</h1> <p><code>cdk import</code> コマンドの実行時に <code>We recommend you run a drift detection operation to confirm your CDK app resource definitions are up-to-date.</code> というログが出ているため,AWS CloudFormation のドリフト検出機能を実行しておく.今回は設定項目も少なく,1発で <strong>IN_SYNC(ドリフトなし)</strong>になった.</p> <p><figure class="figure-image figure-image-fotolife" title="AWS CloudFormation: SandboxCdkImportStack スタック(ドリフト検出結果)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123230530.png" width="1200" height="545" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CloudFormation: SandboxCdkImportStack スタック(ドリフト検出結果)</figcaption></figure></p> <p>注意点としては AWS CloudFormation のドリフト検出機能は対象リソースが限られているため,ドリフト検出結果が <strong>IN_SYNC</strong> だとしても,必ずしも<strong>「完全一致」</strong>になっているとは言えない可能性があるというところ🚨</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2FAWSCloudFormation%2Flatest%2FUserGuide%2Fresource-import-supported-resources.html" title="Resources that support import and drift detection operations - AWS CloudFormation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html">docs.aws.amazon.com</a></cite></p> <h1 id="Step4">Step.4</h1> <p>最後はインポートしたリソースを変更する.今回は Amazon CloudWatch Logs ロググループの保管期間を <code>aws_logs.RetentionDays.ONE_WEEK(7日間)</code> から <code>aws_logs.RetentionDays.ONE_MONTH(1ヶ月間)</code> に変更する.</p> <h2 id="-sandbox-cdk-import-stackts-2">👾 sandbox-cdk-import-stack.ts</h2> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> RemovalPolicy<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_logs<span class="synStatement">,</span> aws_s3<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkImportStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synStatement">new</span> aws_s3.Bucket<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkImportBucket'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> bucketName: <span class="synConstant">'kakakakakku-sandbox-cdk-import'</span><span class="synStatement">,</span> versioned: <span class="synConstant">true</span><span class="synStatement">,</span> removalPolicy: RemovalPolicy.RETAIN <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> aws_logs.LogGroup<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkImportLogGroup'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> logGroupName: <span class="synConstant">'kakakakakku-sandbox-cdk-import'</span><span class="synStatement">,</span> retention: aws_logs.RetentionDays.ONE_MONTH<span class="synStatement">,</span> logGroupClass: aws_logs.LogGroupClass.INFREQUENT_ACCESS<span class="synStatement">,</span> removalPolicy: RemovalPolicy.RETAIN <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <p>そして <code>cdk diff</code> コマンドと <code>cdk deploy</code> コマンドを実行して,期待通りに Amazon CloudWatch Logs ロググループの保管期間を変更できた✔️</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ cdk diff SandboxCdkImportStack Stack SandboxCdkImportStack Resources <span class="synStatement">[</span>~<span class="synStatement">]</span> AWS::Logs::LogGroup SandboxCdkImportLogGroup SandboxCdkImportLogGroupD75F1BC6 └─ <span class="synStatement">[</span>~<span class="synStatement">]</span> RetentionInDays ├─ <span class="synStatement">[</span>-<span class="synStatement">]</span> <span class="synConstant">7</span> └─ <span class="synStatement">[</span>+<span class="synStatement">]</span> <span class="synConstant">30</span> ✨ Number of stacks with differences: <span class="synConstant">1</span> $ cdk deploy SandboxCdkImportStack ✨ Synthesis time: <span class="synConstant">2</span>.66s (中略) ✅ SandboxCdkImportStack ✨ Deployment time: <span class="synConstant">22</span>.23s Stack ARN: arn:aws:cloudformation:ap-northeast-1:000000000000:stack/SandboxCdkImportStack/d36b1dd0-b9f6-11ee-9501-0ee3eafa3ebd ✨ Total time: <span class="synConstant">24</span>.89s </pre> <p><figure class="figure-image figure-image-fotolife" title="AWS CloudFormation: SandboxCdkImportStack スタック"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123230601.png" width="1200" height="545" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CloudFormation: SandboxCdkImportStack スタック</figcaption></figure></p> <h1 id="まとめ">まとめ</h1> <p>AWS CDK の <code>cdk import</code> コマンドを使って既存リソースをインポートする(取り込む)手順を試してみて,流れを把握できたのは良かった❗️もちろんプロダクション環境などに影響を出さずにリソースを新しく作り直せる(差し替えられる)なら AWS CDK で作り直した方が良いとは思うけど,どうしてもインポートが必要なステートフルなリソースもあると思う.そういうときに今回の <code>cdk import</code> コマンドを検討したいと思う👌</p> <h1 id="関連記事">関連記事</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Fdevops%2Fhow-to-import-existing-resources-into-aws-cdk-stacks%2F" title="How to import existing resources into AWS CDK Stacks | Amazon Web Services" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/blogs/devops/how-to-import-existing-resources-into-aws-cdk-stacks/">aws.amazon.com</a></cite></p> kakku22 AWS CodeBuild の buildspec.yml でコマンドに改行を含める hatenablog://entry/6801883189079264113 2024-01-30T21:18:05+09:00 2024-01-30T21:18:05+09:00 AWS CodeBuild の buildspec.yml でコマンドに改行を含める場合は以下のように記述できる❗ コマンドのオプションが多かったりすると改行したくなることもあると思う \( 'ω')/ 1. シンプルに改行する バックスラッシュ \ を含めずシンプルに改行すれば OK👌 以下はサンプルとして find コマンドを使っている. version: 0.2 phases: build: commands: - find /home -type d -ls 2. YAML の複数行記法を使う もしくは YAML の複数行記法とバックスラッシュ \ を組み合わせて使うこともできる👌 v… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240130/20240130201043.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CodeBuild の <code>buildspec.yml</code> でコマンドに改行を含める場合は以下のように記述できる❗</p> <p>コマンドのオプションが多かったりすると改行したくなることもあると思う \( 'ω')/</p> <h2 id="1-シンプルに改行する">1. シンプルに改行する</h2> <p>バックスラッシュ <code>\</code> を含めずシンプルに改行すれば OK👌</p> <p>以下はサンプルとして <code>find</code> コマンドを使っている.</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">version</span><span class="synSpecial">:</span> <span class="synConstant">0.2</span> <span class="synIdentifier">phases</span><span class="synSpecial">:</span> <span class="synIdentifier">build</span><span class="synSpecial">:</span> <span class="synIdentifier">commands</span><span class="synSpecial">:</span> <span class="synStatement">- </span>find /home -type d -ls </pre> <h2 id="2-YAML-の複数行記法を使う">2. YAML の複数行記法を使う</h2> <p>もしくは YAML の複数行記法とバックスラッシュ <code>\</code> を組み合わせて使うこともできる👌</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">version</span><span class="synSpecial">:</span> <span class="synConstant">0.2</span> <span class="synIdentifier">phases</span><span class="synSpecial">:</span> <span class="synIdentifier">build</span><span class="synSpecial">:</span> <span class="synIdentifier">commands</span><span class="synSpecial">:</span> <span class="synStatement">- </span>|- find /home \ -type d \ -ls </pre> kakku22 GitHub Actions で [skip ci] のような「カスタムメッセージ」でワークフローを制御する hatenablog://entry/6801883189076085993 2024-01-29T13:46:36+09:00 2024-01-29T13:46:36+09:00 GitHub Actions を使っていて,コミットはプッシュするけどビルドはまだ実行したくないというときに [skip ci] をよく使っている.GitHub Actions では2021年にサポートされていて,他にも [no ci] や [skip actions] などのメッセージも使える👌 docs.github.com github.blog 最近 GitHub Actions ワークフローを実装しているときに [skip ci] のような「カスタムメッセージ」を使ってワークフローのステップを制御できたら便利かもしれない💡と思って試してみた.結論としてはワークフローのステップで if… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240118/20240118211917.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>GitHub Actions を使っていて,コミットはプッシュするけどビルドはまだ実行したくないというときに <code>[skip ci]</code> をよく使っている.GitHub Actions では2021年にサポートされていて,他にも <code>[no ci]</code> や <code>[skip actions]</code> などのメッセージも使える👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.github.com%2Fja%2Factions%2Fmanaging-workflow-runs%2Fskipping-workflow-runs" title="ワークフロー実行をスキップする - GitHub Docs" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.github.com/ja/actions/managing-workflow-runs/skipping-workflow-runs">docs.github.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.blog%2Fchangelog%2F2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci%2F" title="GitHub Actions: Skip pull request and push workflows with [skip ci]" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/">github.blog</a></cite></p> <p>最近 GitHub Actions ワークフローを実装しているときに <code>[skip ci]</code> のような<strong>「カスタムメッセージ」</strong>を使ってワークフローのステップを制御できたら便利かもしれない💡と思って試してみた.結論としてはワークフローのステップで <code>if: contains(github.event.head_commit.message, '[do something]')</code> にように実装すれば簡単に実現できた❗️</p> <h2 id="-workflowyml-サンプル">🐰 workflow.yml サンプル</h2> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">name</span><span class="synSpecial">:</span> Sandbox Custom Message <span class="synIdentifier">on</span><span class="synSpecial">:</span> <span class="synIdentifier">push</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">pull_request</span><span class="synSpecial">:</span> <span class="synIdentifier">branches</span><span class="synSpecial">:</span> <span class="synStatement">- </span>master <span class="synIdentifier">jobs</span><span class="synSpecial">:</span> <span class="synIdentifier">sandbox</span><span class="synSpecial">:</span> <span class="synIdentifier">runs-on</span><span class="synSpecial">:</span> ubuntu-latest <span class="synIdentifier">steps</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> START <span class="synIdentifier">run</span><span class="synSpecial">:</span> echo start! <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> DO SOMETHING <span class="synIdentifier">run</span><span class="synSpecial">:</span> echo yay! <span class="synIdentifier">if</span><span class="synSpecial">:</span> contains(github.event.head_commit.message, <span class="synConstant">'[do something]'</span>) <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> END <span class="synIdentifier">run</span><span class="synSpecial">:</span> echo end! </pre> <h2 id="動作確認">動作確認</h2> <p><figure class="figure-image figure-image-fotolife" title="実行結果"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240118/20240118193012.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>実行結果</figcaption></figure></p> kakku22 トラフィックの急増に耐えられるソリューション!? Virtual Waiting Room on AWS を試した hatenablog://entry/6801883189076582700 2024-01-25T18:58:27+09:00 2024-01-25T18:58:27+09:00 ウェブサービスのトラフィックが急増して障害が発生してしまうことはあると思う.そういうときにリクエストを一時的にバッファリングして,適切に入場制限をする仕組みとして「Virtual Waiting Room(仮想待合室)」という仕組みがある.実際にショップ・病院などに行ったときに番号札を受け取って待合室で待つという体験をしたことがあると思う. Virtual Waiting Room on AWS AWS ではソリューションとして(サービスではなく)「Virtual Waiting Room on AWS」を提供している.Virtual Waiting Room on AWS をデプロイすると,… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121162716.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>ウェブサービスのトラフィックが急増して障害が発生してしまうことはあると思う.そういうときにリクエストを一時的にバッファリングして,適切に入場制限をする仕組みとして<strong>「Virtual Waiting Room(仮想待合室)」</strong>という仕組みがある.実際にショップ・病院などに行ったときに番号札を受け取って待合室で待つという体験をしたことがあると思う.</p> <h1 id="Virtual-Waiting-Room-on-AWS">Virtual Waiting Room on AWS</h1> <p>AWS ではソリューションとして(サービスではなく)<strong>「Virtual Waiting Room on AWS」</strong>を提供している.Virtual Waiting Room on AWS をデプロイすると,サービス側に対するトラフィックを<strong>「仮想待合室」</strong>で制御できる.</p> <p>AWS 以外で関連するサービスとしては <a href="https://www.cloudflare.com/application-services/products/waiting-room/">Cloudflare Waiting Room</a> や <a href="https://queue-it.com/">Queue-it</a> などもある❗️個人的には 018 サポートサイトで Cloudflare Waiting Room・トイザらスサイトで Queue-it が導入されているのを体験したことがある👀</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fsolutions%2Fimplementations%2Fvirtual-waiting-room-on-aws%2F" title="Virtual Waiting Room on AWS | AWS Solutions" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/solutions/implementations/virtual-waiting-room-on-aws/">aws.amazon.com</a></cite></p> <h1 id="Virtual-Waiting-Room-on-AWS-関連サイト">Virtual Waiting Room on AWS 関連サイト</h1> <p>ソリューションサイト以外に Virtual Waiting Room on AWS に関連するサイトとして,ソリューションを詳細に解説した Implementation Guide と Amazon Web Services Blog の記事がある.Virtual Waiting Room on AWS を試す前に一度ザッと読んでおくと良いと思う💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fsolutions%2Flatest%2Fvirtual-waiting-room-on-aws%2Fwelcome.html" title="Absorb large bursts of traffic to your website with the Virtual Waiting Room on AWS - Virtual Waiting Room on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/welcome.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Fnews%2Fintroducing-aws-virtual-waiting-room%2F" title="AWS Virtual Waiting Roomのご紹介 | Amazon Web Services" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/blogs/news/introducing-aws-virtual-waiting-room/">aws.amazon.com</a></cite></p> <p>また GitHub には AWS CloudFormation テンプレート・AWS Lambda 関数のコードなども公開されていて参考になる❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws-solutions%2Fvirtual-waiting-room-on-aws" title="GitHub - aws-solutions/virtual-waiting-room-on-aws: Virtual Waiting Room on AWS solution helps absorb and control incoming user requests to your website during an unusually large burst of traffic, usually due to a large-scale event." class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws-solutions/virtual-waiting-room-on-aws">github.com</a></cite></p> <p>さらに <a href="https://twitter.com/_kensh">@_kensh</a> さんが <strong>ServerlessDays Tokyo 2023</strong> で発表された資料<strong>「サーバーレスで仮想待合室を作ろう!」</strong>で Virtual Waiting Room on AWS が必要になるシチュエーションと実装詳細の解説がまとまってて最高だった👏</p> <p><iframe id="talk_frame_1081485" class="speakerdeck-iframe" src="//speakerdeck.com/player/9c1d2856a200492998abb239572ccf55" width="710" height="399" style="aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;" frameborder="0" allowtransparency="true" allowfullscreen="allowfullscreen"></iframe> <cite class="hatena-citation"><a href="https://speakerdeck.com/_kensh/serverless-virtual-waiting-room">speakerdeck.com</a></cite></p> <h1 id="Virtual-Waiting-Room-on-AWS-の動作イメージ">Virtual Waiting Room on AWS の動作イメージ</h1> <p>最初に Virtual Waiting Room on AWS の動作イメージを見てもらうとわかりやすくなると思う💡まず,待合室サイトの初期ページにアクセスして,表示されている <strong>Reserve</strong> ボタンを押すと順番待ちに入れる.</p> <p><figure class="figure-image figure-image-fotolife" title="待合室サイトの初期ページにアクセスする"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121132600.png" width="1200" height="592" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>待合室サイトの初期ページにアクセスする</figcaption></figure></p> <p>以下のキャプチャは5回ほど待合室サイトにアクセスしたところ.<strong>You are number 5 in line</strong> と表示されている通り,今は5名待っていることになる.この <strong>Waiting Room</strong> 画面では <strong>My Position</strong> や <strong>Serving Position</strong> など待合室のステータスを確認できる.</p> <p><figure class="figure-image figure-image-fotolife" title="待合室サイトで待つ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121132801.png" width="1200" height="592" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>待合室サイトで待つ</figcaption></figure></p> <p>今度はコントロールパネル(管理画面)にアクセスする.この画面でも待合室のステータスを確認できる.そして,デフォルトでは <strong>Serving Counter 0</strong> になっていて全員ブロックされているため,<strong>Increment Serving Counter</strong> の <strong>Increment by: 100</strong> で <strong>Change</strong> ボタンを押してサービス側に通す人数制限を緩和する.</p> <p><figure class="figure-image figure-image-fotolife" title="コントロールパネルにアクセスする"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121134247.png" width="1144" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>コントロールパネルにアクセスする</figcaption></figure></p> <p>すると,待合室サイト側で <strong>Waiting for line to advance</strong> ボタンが押せるようになり,今度は実際のサービスサイト(今回は EC サイトのモックアップ)にアクセスできる.ザッとこんな感じ❗️</p> <p><figure class="figure-image figure-image-fotolife" title="サービスサイトにアクセスする"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121134836.png" width="1200" height="628" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>サービスサイトにアクセスする</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="Purchase now ボタンを押して購入操作を試す"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121135104.png" width="1200" height="629" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Purchase now ボタンを押して購入操作を試す</figcaption></figure></p> <h1 id="Virtual-Waiting-Room-on-AWS-をセットアップする">Virtual Waiting Room on AWS をセットアップする</h1> <p>Virtual Waiting Room on AWS をセットアップするときは <strong>AWS Solutions Library</strong> のサイトにある <code>Launch in the AWS Console</code> ボタンを押して <code>us-east-1</code> リージョンに AWS CloudFormation スタックをデプロイすれば OK👌2024年1月20日時点で問題なく動く😃</p> <p>しかし1点注意点があって,AWS CloudFormation スタック名は短くしておく必要がある.僕は <code>vwr-on-aws</code> にして動作確認できたので,真似してもらって良いと思う.詳しくは記事の最後に書くけど,最初スタック名を <code>virtual-waiting-room-on-aws</code> にしたらデプロイエラーになって時間を無駄にした💣</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fsolutions%2Fimplementations%2Fvirtual-waiting-room-on-aws%2F" title="Virtual Waiting Room on AWS | AWS Solutions" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/solutions/implementations/virtual-waiting-room-on-aws/">aws.amazon.com</a></cite></p> <p>あとコントロールパネルに Amazon API Gateway のエンドポイントを実行するための権限を持ったアクセスキーを設定する必要がある.今回は <code>vwr-on-aws</code> という名前の IAM User にした.ポリシーは AWS CloudFormation で作られている IAM Group があってそこにアタッチすれば引き継がれる👌Implementation Guide に IAM User を作ると載ってるけど,隅々まで読まないと気付きにくいと思う.</p> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">Version</span>&quot;: &quot;<span class="synConstant">2012-10-17</span>&quot;, &quot;<span class="synStatement">Statement</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">Action</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">execute-api:Invoke</span>&quot; <span class="synSpecial">]</span>, &quot;<span class="synStatement">Resource</span>&quot;: &quot;<span class="synConstant">arn:aws:execute-api:us-east-1:000000000000:m6y97xvptd/*</span>&quot;, &quot;<span class="synStatement">Effect</span>&quot;: &quot;<span class="synConstant">Allow</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span> </pre> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fsolutions%2Flatest%2Fvirtual-waiting-room-on-aws%2Fautomated-deployment.html" title="Automated deployment - Virtual Waiting Room on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/automated-deployment.html">docs.aws.amazon.com</a></cite></p> <p>AWS CloudFormation スタックは基本機能だと大きく3つのネストスタックに分割されている🧩</p> <ul> <li>vwr-on-aws <ul> <li>CoreModuleStack</li> <li>AuthorizersModuleStack</li> <li>SampleModuleStack</li> </ul> </li> </ul> <p><figure class="figure-image figure-image-fotolife" title="AWS CloudFormation スタック一覧"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240120/20240120174419.png" width="1092" height="429" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CloudFormation スタック一覧</figcaption></figure></p> <p>デプロイ時間は<strong>「27分ぐらい」</strong>だった.特に Amazon ElastiCache クラスタの構築(論理名 <code>RedisReplicationGroup</code>)に10分ぐらい待たされるため,もしデプロイが進んでいるのか不安になったら Amazon ElastiCache コンソールを確認してみると良いと思う.</p> <pre class="code" data-lang="" data-unlink>2024-01-20 11:34:00 UTC+0900 2024-01-20 12:00:56 UTC+0900</pre> <p>すべてデプロイが終わったら AWS CloudFormation スタックのアウトプットから <code>ControlPanelURL</code> と <code>WaitingRoomURL</code> の値を控えておく.既に検証環境は削除していてアクセスできないけど,実際に以下のような URL になっていた📝</p> <pre class="code" data-lang="" data-unlink># ControlPanelURL https://d1096am4wacsdc.cloudfront.net/control-panel/index.html?eventId=Sample&amp;publicApiUrl=https://d3bxth0af8tkl8.cloudfront.net&amp;privateApiUrl=https://m6y97xvptd.execute-api.us-east-1.amazonaws.com/api&amp;regionName=us-east-1 # WaitingRoomURL https://d1096am4wacsdc.cloudfront.net/waiting-room-site/index.html?eventId=Sample&amp;publicApiUrl=https://d3bxth0af8tkl8.cloudfront.net&amp;commerceApiUrl=https://fv5dbf3ata.execute-api.us-east-1.amazonaws.com/store</pre> <h1 id="アーキテクチャ図">アーキテクチャ図</h1> <p>Virtual Waiting Room on AWS のアーキテクチャ図は Implementation Guide に載っている.コンポーネントも多く複雑に感じるけど,<strong>optional</strong> と書いてある部分を除けば <strong>Public and private APIs</strong> と書いてある部分が中心になる.</p> <p><figure class="figure-image figure-image-fotolife" title="Architecture overview"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240120/20240120220709.png" width="1200" height="674" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/architecture-overview.html">Architecture overview - Virtual Waiting Room on AWS</a> より引用</figcaption></figure></p> <p>しかし AWS Lambda / Amazon DynamoDB などはあくまで概要レベルのアーキテクチャ図になっていて,詳細も Implementation Guide に載っている.Amazon API Gateway から呼び出される AWS Lambda 関数の多さに驚く👀</p> <p><figure class="figure-image figure-image-fotolife" title="Solution components"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240120/20240120220744.png" width="1200" height="675" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/solution-components.html">Solution components - Virtual Waiting Room on AWS</a> より引用</figcaption></figure></p> <p>Virtual Waiting Room on AWS の仕組みと各コンポーネントの挙動に関しては Implementation Guide の <strong>How the solution works</strong> と <strong>Solution components</strong> に詳しく載っている.これらを読みながら実際にデプロイされた AWS リソースの設定などを探っていくのが良いと思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fsolutions%2Flatest%2Fvirtual-waiting-room-on-aws%2Fhow-the-solution-works.html" title="How the solution works - Virtual Waiting Room on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/how-the-solution-works.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fsolutions%2Flatest%2Fvirtual-waiting-room-on-aws%2Fsolution-components.html" title="Solution components - Virtual Waiting Room on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/solution-components.html">docs.aws.amazon.com</a></cite></p> <p>個人的に挙動を調査したログを残しておく.まず <code>AssignQueueNum</code> API は <code>{"api_request_id": "581280c8-79b3-4212-b3eb-7b3e84c9b91f"}</code> のようなレスポンスを返していて,<code>GetQueueNumber</code> API は <code>{"queue_number": 2, "entry_time": 1705721798, "event_id": "Sample", "status": 1}</code> のようなレスポンスを返している💡</p> <p>また Virtual Waiting Room で待機してるときは <code>GetServingNumber</code> API と <code>GetWaitingNum</code> API を定期的に呼び出していて(ポーリングしていて)それぞれ <code>{"waiting_num": 3}</code> や <code>{"serving_counter": "1"}</code> のようなレスポンスを返していて,Virtual Waiting Room から次に進めるかどうかを制御している😀DevTools で待機中の API 呼び出しを眺めていると理解しやすいと思う👌</p> <p><figure class="figure-image figure-image-fotolife" title="DevTools"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240120/20240120222231.png" width="1200" height="502" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>待機中の API 呼び出し</figcaption></figure></p> <p>順番が来たら JWT トークンを取得して,サービス側の Amazon API Gateway の認証に使う.Amazon API Gateway の設定を見ると <code>WaitingRoomAuthorizer</code> という Amazon API Gateway Lambda オーソライザーの設定が見つかる🔑</p> <p><figure class="figure-image figure-image-fotolife" title="Amazon API Gateway Lambda オーソライザー"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240120/20240120222812.png" width="1200" height="545" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon API Gateway Lambda オーソライザー</figcaption></figure></p> <h1 id="AWS-CloudFormation-スタックを削除する">AWS CloudFormation スタックを削除する</h1> <p>そのままにしておくとコストが発生するため,Virtual Waiting Room on AWS の検証が終わったら AWS CloudFormation スタック <code>vwr-on-aws</code> を削除する.削除する順番としては「1. IAM User」→「2. AWS CloudFormation スタック」→「3. Amazon S3 バケット(2つ)」で,詳しくは <strong>Implementation Guide</strong> にすべて書いてある👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fsolutions%2Flatest%2Fvirtual-waiting-room-on-aws%2Funinstall-the-solution.html" title="Uninstall the solution - Virtual Waiting Room on AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/solutions/latest/virtual-waiting-room-on-aws/uninstall-the-solution.html">docs.aws.amazon.com</a></cite></p> <h1 id="AWS-CloudFormation-スタック名に注意する">AWS CloudFormation スタック名に注意する</h1> <p>最初 AWS CloudFormation スタック名を <code>virtual-waiting-room-on-aws</code> にしたらデプロイの後半でエラーになって,ロールバックの時間も含めて1時間ほど無駄にしてしまった.エラーになったのは Amazon EventBridge で「<a href="https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_Target.html">targetId &#x306E;64&#x6587;&#x5B57;&#x5236;&#x9650;</a>」に該当してしまった.確かに <code>virtual-waiting-room-on-aws-CoreModuleStack-SDAGBI8A4ER1-expiredEvents</code> は70文字ある💨</p> <pre class="code" data-lang="" data-unlink>Resource handler returned message: &#34;1 validation error detected: Value &#39;virtual-waiting-room-on-aws-CoreModuleStack-SDAGBI8A4ER1-expiredEvents&#39; at &#39;targets.1.member.id&#39; failed to satisfy constraint: Member must have length less than or equal to 64 (Service: EventBridge, Status Code: 400, Request ID: 72b173d6-99c8-445c-9755-d0351b64ab19)&#34; (RequestToken: c69baab4-b984-4a92-e37e-75f30dca430d, HandlerErrorCode: GeneralServiceException)</pre> <p>ちなみに AWS CloudFormation テンプレートを読むと,Amazon SQS キュー名は AWS CloudFormation の<strong>「カスタムリソース」</strong>を使って文字数を制御しているため,同じ仕組みが入っていれば良いのに〜と思った💡</p> <pre class="code lang-json" data-lang="json" data-unlink>&quot;<span class="synStatement">WaitingRoomQueue</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">Type</span>&quot;: &quot;<span class="synConstant">AWS::SQS::Queue</span>&quot;, &quot;<span class="synStatement">Properties</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">QueueName</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">Fn::Join</span>&quot;: <span class="synSpecial">[</span>&quot;<span class="synConstant">-</span>&quot;, <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">Fn::GetAtt</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">InvokeShortenStackNameLambda</span>&quot;, &quot;<span class="synConstant">returnValue</span>&quot; <span class="synSpecial">]</span> <span class="synSpecial">}</span>, &quot;<span class="synConstant">WaitingRoomQueue</span>&quot;<span class="synSpecial">]</span> <span class="synSpecial">]</span> <span class="synSpecial">}</span>, &quot;<span class="synStatement">KmsMasterKeyId</span>&quot;: &quot;<span class="synConstant">alias/aws/sqs</span>&quot;, &quot;<span class="synStatement">KmsDataKeyReusePeriodSeconds</span>&quot;: <span class="synConstant">300</span>, &quot;<span class="synStatement">RedrivePolicy</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">deadLetterTargetArn</span>&quot; : <span class="synSpecial">{</span>&quot;<span class="synStatement">Fn::GetAtt</span>&quot;: <span class="synSpecial">[</span> &quot;<span class="synConstant">WaitingRoomDeadLetterQueue</span>&quot;, &quot;<span class="synConstant">Arn</span>&quot; <span class="synSpecial">]}</span>, &quot;<span class="synStatement">maxReceiveCount</span>&quot; : <span class="synConstant">2</span> <span class="synSpecial">}</span>, &quot;<span class="synStatement">VisibilityTimeout</span>&quot; : <span class="synConstant">30</span> <span class="synSpecial">}</span> <span class="synSpecial">}</span>, </pre> <h1 id="その他実施ログ">その他実施ログ</h1> <p><figure class="figure-image figure-image-fotolife" title="AWS Lambda 関数は計23種類ある"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121140305.png" width="1200" height="753" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS Lambda 関数は計23種類ある</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="Amazon DynamoDB テーブル QueuePositionEntryTimeTable のアイテム例"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240121/20240121140531.png" width="1200" height="539" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon DynamoDB テーブル QueuePositionEntryTimeTable のデータ例</figcaption></figure></p> <h1 id="まとめ">まとめ</h1> <p><strong>「Virtual Waiting Room(仮想待合室)」</strong>という仕組みが欲しくなることはあると思う.AWS を活用して実現できるソリューションとして<strong>「Virtual Waiting Room on AWS」</strong>を実際に試して紹介した❗️それなりに構成は複雑ですべてを理解しようとすると大変だけど,Virtual Waiting Room on AWS にはカスタマイズ性もあって,既存サービスに組み込みやすく作られている👌とは言え,あくまで個人的にはソリューションではなく<strong>「マネージドサービスとして」</strong>提供されたらもっと良いのにな〜と思ったりはする💨</p> <p>ということで今回は <strong>Virtual Waiting Room on AWS</strong> の紹介でした \( 'ω')/</p> kakku22 AWS CDK で AWS CodeBuild の GitHub Webhook を設定する hatenablog://entry/6801883189077469789 2024-01-23T22:18:14+09:00 2024-01-23T22:18:14+09:00 AWS CodeBuild で GitHub リポジトリにプッシュをしたら Webhook 経由で自動的にビルドを開始する構成を AWS CDK で実装してみた💡ちなみに AWS CDK で AWS CodeBuild の Webhook を設定するだけだと以下のように Failed to call CreateWebhook というエラーが出る場合がある🔥 Failed to call CreateWebhook, reason: Could not find access token for server type github 今回は GitHub Personal Access Tok… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123220928.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> AWS CodeBuild で GitHub リポジトリにプッシュをしたら Webhook 経由で自動的にビルドを開始する構成を AWS CDK で実装してみた💡ちなみに AWS CDK で AWS CodeBuild の Webhook を設定するだけだと以下のように <code>Failed to call CreateWebhook</code> というエラーが出る場合がある🔥</p> <pre class="code" data-lang="" data-unlink>Failed to call CreateWebhook, reason: Could not find access token for server type github</pre> <p>今回は GitHub Personal Access Tokens (PAT) を AWS Secrets Manager に設定して,AWS CDK の <code>aws_codebuild.GitHubSourceCredentials</code> で認証情報を設定する👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_codebuild.GitHubSourceCredentials.html" title="class GitHubSourceCredentials (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codebuild.GitHubSourceCredentials.html">docs.aws.amazon.com</a></cite></p> <h2 id="GitHub-Personal-Access-Tokens-PAT">GitHub Personal Access Tokens (PAT)</h2> <p>まずは GitHub の <strong>Personal Access Tokens (PAT)</strong> を取得する.現時点だとトークンは <code>classic</code> と <code>Fine-grained (Beta)</code> の2種類があるけど,今回は <code>classic</code> を使う.<strong>Select scopes</strong> でトークンに指定するスコープでは <code>repo</code> と <code>admin:repo_hook</code> を許可しておく.スコープに関しては以下のドキュメントにも書いてあった.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodebuild%2Flatest%2Fuserguide%2Faccess-tokens.html" title="CodeBuild でソースプロバイダにアクセスする - AWS CodeBuild" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/access-tokens.html">docs.aws.amazon.com</a></cite></p> <h2 id="AWS-Secrets-Manager">AWS Secrets Manager</h2> <p>取得した Personal Access Tokens (PAT) を AWS CLI で AWS Secrets Manager に登録する.名前は <code>github-pat</code> にした.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws secretsmanager create-secret <span class="synSpecial">--name</span> github-pat <span class="synSpecial">--secret-string</span> XXXXX </pre> <h2 id="-sandbox-cdk-codebuild-stackts">👾 sandbox-cdk-codebuild-stack.ts</h2> <p>今回は GitHub リポジトリの <code>master</code> ブランチにプッシュをしたら Webhook 経由で自動的にビルドを開始するように設定した.AWS CodeBuild の buildspec は特に意味はなく <code>echo</code> コマンドにしてある.</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> RemovalPolicy<span class="synStatement">,</span> SecretValue<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_codebuild<span class="synStatement">,</span> aws_logs<span class="synStatement">,</span> aws_iam<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkCodeBuildStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">)</span> <span class="synType">const</span> role <span class="synStatement">=</span> <span class="synStatement">new</span> aws_iam.Role<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkCodeBuildRole'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> roleName: <span class="synConstant">'sandbox-cdk-codebuild-role'</span><span class="synStatement">,</span> assumedBy: <span class="synStatement">new</span> aws_iam.ServicePrincipal<span class="synStatement">(</span><span class="synConstant">'codebuild.amazonaws.com'</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synType">const</span> logGroup <span class="synStatement">=</span> <span class="synStatement">new</span> aws_logs.LogGroup<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkCodeBuildLogs'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> logGroupName: <span class="synConstant">'sandbox-cdk-codebuild-logs'</span><span class="synStatement">,</span> retention: aws_logs.RetentionDays.ONE_WEEK<span class="synStatement">,</span> removalPolicy: RemovalPolicy.DESTROY<span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> logGroup.grantWrite<span class="synStatement">(</span>role<span class="synStatement">)</span> <span class="synStatement">new</span> aws_codebuild.GitHubSourceCredentials<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkCodeBuildGitHubCredential'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> accessToken: SecretValue.secretsManager<span class="synStatement">(</span><span class="synConstant">'github-pat'</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synType">const</span> source <span class="synStatement">=</span> aws_codebuild.Source.gitHub<span class="synStatement">(</span><span class="synIdentifier">{</span> owner: <span class="synConstant">'kakakakakku'</span><span class="synStatement">,</span> repo: <span class="synConstant">'xxxxx'</span><span class="synStatement">,</span> branchOrRef: <span class="synConstant">'master'</span><span class="synStatement">,</span> webhook: <span class="synConstant">true</span><span class="synStatement">,</span> webhookFilters: <span class="synIdentifier">[</span> aws_codebuild.FilterGroup .inEventOf<span class="synStatement">(</span>aws_codebuild.EventAction.PUSH<span class="synStatement">)</span> .andBranchIs<span class="synStatement">(</span><span class="synConstant">'master'</span><span class="synStatement">)</span> <span class="synIdentifier">]</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synStatement">new</span> aws_codebuild.Project<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkCodeBuild'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> projectName: <span class="synConstant">'sandbox-cdk-codebuild'</span><span class="synStatement">,</span> source: source<span class="synStatement">,</span> buildSpec: aws_codebuild.BuildSpec.fromObject<span class="synStatement">(</span> <span class="synIdentifier">{</span> version: <span class="synConstant">'0.2'</span><span class="synStatement">,</span> phases: <span class="synIdentifier">{</span> build: <span class="synIdentifier">{</span> commands: <span class="synIdentifier">[</span> <span class="synConstant">'echo Hello!'</span> <span class="synIdentifier">]</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synStatement">),</span> environment: <span class="synIdentifier">{</span> buildImage: aws_codebuild.LinuxBuildImage.fromCodeBuildImageId<span class="synStatement">(</span><span class="synConstant">'aws/codebuild/amazonlinux2-x86_64-standard:5.0'</span><span class="synStatement">),</span> computeType: aws_codebuild.ComputeType.SMALL<span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> role: role<span class="synStatement">,</span> logging: <span class="synIdentifier">{</span> cloudWatch: <span class="synIdentifier">{</span> logGroup: logGroup<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <h2 id="動作確認">動作確認</h2> <p>GitHub リポジトリに適当なコミットをプッシュすると AWS CodeBuild のビルドが自動的に開始された👌送信者は Webhook を意味する <a href="https://docs.github.com/en/webhooks/webhook-events-and-payloads">GitHub-Hookshot</a> プレフィックスになっていた.</p> <p><figure class="figure-image figure-image-fotolife" title="AWS CodeBuild ビルド履歴"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240123/20240123214928.png" width="1200" height="225" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS CodeBuild ビルド履歴</figcaption></figure></p> <p>ちなみに <code>aws codebuild list-source-credentials</code> コマンドを使って認証情報の設定を確認できる.今回は Personal Access Tokens (PAT) を使ったため <code>authType</code> が <code>PERSONAL_ACCESS_TOKEN</code> になっていた.</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ aws codebuild list-source-credentials <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">sourceCredentialsInfos</span><span class="synStatement">&quot;</span>: <span class="synStatement">[</span> <span class="synSpecial">{</span> <span class="synStatement">&quot;</span><span class="synConstant">arn</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">arn:aws:codebuild:ap-northeast-1:000000000000:token/github</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">serverType</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">GITHUB</span><span class="synStatement">&quot;</span>, <span class="synStatement">&quot;</span><span class="synConstant">authType</span><span class="synStatement">&quot;</span>: <span class="synStatement">&quot;</span><span class="synConstant">PERSONAL_ACCESS_TOKEN</span><span class="synStatement">&quot;</span> <span class="synSpecial">}</span> <span class="synStatement">]</span> <span class="synSpecial">}</span> </pre> <h1 id="関連ドキュメント">関連ドキュメント</h1> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcodebuild%2Flatest%2Fuserguide%2Fgithub-webhook.html" title="GitHub ウェブフックイベント - AWS CodeBuild" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/github-webhook.html">docs.aws.amazon.com</a></cite></p> kakku22 AWS CDK でリソースに一括でタグを設定する hatenablog://entry/6801883189076366825 2024-01-22T08:58:24+09:00 2024-01-22T08:58:24+09:00 AWS CDK を使っていてリソースに「一括でタグを設定したい」と思ったら Tags.of(SCOPE).add() を使えば簡単に設定できる💡Tags.of に指定する SCOPE は IConstruct インタフェースを実装していれば良くて App も Stack も指定できる👌 docs.aws.amazon.com App(複数スタックに一括でタグを設定するなら) const app = new cdk.App(); cdk.Tags.of(app).add('Project', 'Sandbox'); Stack(スタックごとに一括でタグを設定するなら) const app = n… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240119/20240119214839.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK を使っていてリソースに<strong>「一括でタグを設定したい」</strong>と思ったら <code>Tags.of(SCOPE).add()</code> を使えば簡単に設定できる💡<code>Tags.of</code> に指定する <code>SCOPE</code> は <code>IConstruct</code> インタフェースを実装していれば良くて <code>App</code> も <code>Stack</code> も指定できる👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fv2%2Fguide%2Ftagging.html" title="Tagging - AWS Cloud Development Kit (AWS CDK) v2" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/v2/guide/tagging.html">docs.aws.amazon.com</a></cite></p> <h2 id="App複数スタックに一括でタグを設定するなら"><code>App</code>(複数スタックに一括でタグを設定するなら)</h2> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synType">const</span> app <span class="synStatement">=</span> <span class="synStatement">new</span> cdk.App<span class="synStatement">();</span> cdk.Tags.<span class="synStatement">of(</span>app<span class="synStatement">)</span>.add<span class="synStatement">(</span><span class="synConstant">'Project'</span><span class="synStatement">,</span> <span class="synConstant">'Sandbox'</span><span class="synStatement">);</span> </pre> <h2 id="Stackスタックごとに一括でタグを設定するなら"><code>Stack</code>(スタックごとに一括でタグを設定するなら)</h2> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synType">const</span> app <span class="synStatement">=</span> <span class="synStatement">new</span> cdk.App<span class="synStatement">();</span> <span class="synType">const</span> stack <span class="synStatement">=</span> <span class="synStatement">new</span> SandboxStack<span class="synStatement">(</span>app<span class="synStatement">,</span> <span class="synConstant">'SandboxStack'</span><span class="synStatement">,</span> <span class="synIdentifier">{}</span><span class="synStatement">);</span> cdk.Tags.<span class="synStatement">of(</span>stack<span class="synStatement">)</span>.add<span class="synStatement">(</span><span class="synConstant">'Project'</span><span class="synStatement">,</span> <span class="synConstant">'Sandbox'</span><span class="synStatement">);</span> </pre> <p>小ネタ紹介でしたー \( 'ω')/</p> kakku22 Cyber-Dojo で Fizz Buzz の次におすすめするエクササイズ5選 hatenablog://entry/4207112889949884624 2024-01-19T21:01:58+09:00 2024-01-19T21:04:40+09:00 環境構築に悩むことなくペアプログラミング・モブプログラミングの練習ができるので「Cyber-Dojo」をよく使っていて,モブプログラミング未経験のチームに導入を支援するときにも使っている👌 Cyber-Dojo には数年前に紹介した「結果予測機能」もあってテスト駆動開発の練習に使うこともできる❗️ cyber-dojo.org Cyber-Dojo では多くのプログラミング言語とテストフレームワークの組み合わせから選べるだけでなく,さらに「60 種類」のエクササイズ(アルゴリズム)から選べるという特徴がある. Cyber-Dojo の create a new practice 画面 まずは … <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20231231/20231231122813.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>環境構築に悩むことなくペアプログラミング・モブプログラミングの練習ができるので<strong>「Cyber-Dojo」</strong>をよく使っていて,モブプログラミング未経験のチームに導入を支援するときにも使っている👌 Cyber-Dojo には数年前に紹介した<a href="https://kakakakakku.hatenablog.com/entry/2020/08/03/072440">&#x300C;&#x7D50;&#x679C;&#x4E88;&#x6E2C;&#x6A5F;&#x80FD;&#x300D;</a>もあってテスト駆動開発の練習に使うこともできる❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcyber-dojo.org%2F" title="cyber-dojo" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://cyber-dojo.org/">cyber-dojo.org</a></cite></p> <p>Cyber-Dojo では多くのプログラミング言語とテストフレームワークの組み合わせから選べるだけでなく,さらに<strong>「60 種類」</strong>のエクササイズ(アルゴリズム)から選べるという特徴がある.</p> <p><figure class="figure-image figure-image-fotolife" title="Cyber-Dojo の create a new practice 画面"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20231231/20231231113640.png" width="1200" height="592" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Cyber-Dojo の create a new practice 画面</figcaption></figure></p> <h1 id="まずは-Fizz-Buzz-と-Fizz-Buzz-Plus-から">まずは Fizz Buzz と Fizz Buzz Plus から</h1> <p>もしプログラミング初学者やモブプログラミング未経験者に教える場合,1番使いやすいのは <strong>Fizz Buzz</strong> だと思う.アルゴリズムは単純だし,初学者にも理解しやすく,テストコードも含めて程よく楽しめる.</p> <h2 id="-Fizz-Buzz">🎲 Fizz Buzz</h2> <p>与えられた数値をそのまま出力しつつ,もし数値が 3 の倍数なら "Fizz" を出力し,5 の倍数なら "Buzz" を出力する.もし 3 と 5 の倍数なら "Fizz Buzz" を出力する.有名な入門アルゴリズムで難易度は低めだと思う.</p> <h2 id="-Fizz-Buzz-Plus">🎲 Fizz Buzz Plus</h2> <p>Fizz Buzz を少し発展させて,もし数値が 3 を文字列として含むときは "Fizz" を出力し,5 を文字列として含むときは "Buzz" を出力する.よって,31 や 32 は Fizz Buzz Plus だと "Fizz" になるし,52 や 56 は Fizz Buzz Plus だと "Buzz " になる.同じく難易度は低めだと思う.最初から Fizz Buzz Plus を選んでも良いし,まず Fizz Buzz を実装してから,仕様変更があったというストーリーで Fizz Buzz Plus に移行していくのも良いと思う.</p> <p>ちなみに1年以上前の話だけど,2022年8月に YouTube でライブ配信を担当した <a href="https://www.youtube.com/watch?v=VQPSG0nLHn8">&#x300C;AWS Developer Live Show: &#x30E2;&#x30D6;&#x30D7;&#x30ED;&#x30B0;&#x30E9;&#x30DF;&#x30F3;&#x30B0;&#x8D85;&#x5165;&#x9580;&#x30E9;&#x30A4;&#x30D6;&#xFF01;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://www.youtube.com/watch?v=VQPSG0nLHn8" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.youtube.com/watch?v=VQPSG0nLHn8" alt="" class="http-bookmark" /></a>でもモブプログラミングのテーマとして <strong>Fizz Buzz</strong> と <strong>Fizz Buzz Plus</strong> を選んだ❗️</p> <p><figure class="figure-image figure-image-fotolife" title="モブプログラミング中のイメージ図"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20231231/20231231114200.png" width="450" height="450" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>モブプログラミング中のイメージ図</figcaption></figure></p> <h1 id="Cyber-Dojo-で-Fizz-Buzz-の次におすすめするエクササイズ5選">Cyber-Dojo で Fizz Buzz の次におすすめするエクササイズ5選</h1> <p>Cyber-Dojo を使ってモブプログラミングの導入を支援しているときに <strong>Fizz Buzz</strong> の次に試すエクササイズのおすすめはあるー?と聞かれることがある.基本的にどのエクササイズでも良いと思うけど,聞かれたときに答えている個人的におすすめのエクササイズ5選を紹介したいと思う.Cyber-Dojo 自体のコードは GitHub に公開されていて,エクササイズ情報(説明文など)は <strong>cyber-dojo/exercises-start-points</strong> リポジトリで管理されている.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fcyber-dojo%2Fexercises-start-points%2Ftree%2Fmain%2Fstart-points" title="exercises-start-points/start-points at main · cyber-dojo/exercises-start-points" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/cyber-dojo/exercises-start-points/tree/main/start-points">github.com</a></cite></p> <h2 id="-Leap-Years">🎲 Leap Years</h2> <p>与えられた年が<strong>「閏年」</strong>かどうかを判定する.アルゴリズム的には与えられた年が 4 で割り切れるなら閏年とする.しかし与えられた年が 100 で割り切れて,400 で割り切れない場合は閏年ではないという例外がある.難易度は低めだと思うので,Fizz Buzz の次に試してみると良いかなーと👌</p> <p>また Python だと <code>calendar</code> モジュールに <code>isleap()</code> 関数が用意されているため,実際に使われている実装とテストコードと答え合わせするのもおすすめ〜 \( 'ω')/</p> <ul> <li><a href="https://github.com/python/cpython/blob/3.11/Lib/calendar.py">cpython/Lib/calendar.py at 3.11 &middot; python/cpython &middot; GitHub</a></li> <li><a href="https://github.com/python/cpython/blob/3.11/Lib/test/test_calendar.py">cpython/Lib/test/test_calendar.py at 3.11 &middot; python/cpython &middot; GitHub</a><a href="https://b.hatena.ne.jp/entry/https://github.com/python/cpython/blob/3.11/Lib/test/test_calendar.py" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://github.com/python/cpython/blob/3.11/Lib/test/test_calendar.py" alt="" class="http-bookmark" /></a></li> </ul> <h2 id="-Anagrams">🎲 Anagrams</h2> <p>与えられた文字列から作れる組み合わせを出力する.例えば <strong>biro</strong> を入力すると <strong>bior</strong> や <strong>brio</strong> など他にも多くの組み合わせを出力する.難易度は低めだとは思うけど,与えられた文字列の文字数を固定にするのかどうかや,与えられた文字列に重複した文字があるのかどうかによっても難易度は少し変わると思う.</p> <pre class="code" data-lang="" data-unlink>biro bior brio broi boir bori ibro ibor irbo irob iobr iorb rbio rboi ribo riob roib robi obir obri oibr oirb orbi orib</pre> <h2 id="-ISBN">🎲 ISBN</h2> <p>与えられた ISBN コードが規格に沿っているかどうかを判定する.最後の一桁がチェックディジットになっていて,そこを正しく計算できているかどうかがポイントになる.Fizz Buzz と比べると少し難易度は高くなると思う.</p> <pre class="code" data-lang="" data-unlink>ISBN-10: 0471958697 0 471 60695 2 0-470-84525-2 0-321-14653-0 ISBN-13: 9780470059029 978 0 471 48648 0 978-0596809485 978-0-13-149505-0 978-0-262-13472-9</pre> <h2 id="-Harry-Potter">🎲 Harry Potter</h2> <p>ハリー・ポッターの書籍を同時に複数冊買うと割引になるというカートの計算をする.2冊買うと 5% 割引!というのは誰しも体験したことがあるはずで,イメージしやすいエクササイズだと思う.とは言え,Fizz Buzz と比べると少し難易度は高くなると思う.</p> <pre class="code" data-lang="" data-unlink>2 copies of the first book 2 copies of the second book 2 copies of the third book 1 copy of the fourth book 1 copy of the fifth book</pre> <p>このエクササイズは <strong>Coding Dojo</strong> の Kata に <strong>Potter</strong> という名前で載っていて,テストコードも参考になる📕(実は Coding Dojo に Leap Years や Anagram また次に紹介する Bowling も載っている💡)</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcodingdojo.org%2Fkata%2FPotter%2F" title="Coding Dojo" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://codingdojo.org/kata/Potter/">codingdojo.org</a></cite></p> <h2 id="-Bowling-Game">🎲 Bowling Game</h2> <p>ボーリングのスコアを計算する.倒したピンの本数だけではなくストライク・スペアによってスコアが変化するところがポイントで,さらに10ゲーム目は最大3回投げられるという例外もある.ボーリングをしたことがあればイメージしやすいエクササイズだと思うけど,Fizz Buzz と比べると少し難易度は高くなると思う.</p> <ul> <li><code>X</code> : ストライク</li> <li><code>/</code> : スペア</li> <li><code>-</code> : 失敗</li> <li><code>|</code> : 境界線</li> <li><code>||</code> : 最終フレームのボーナス投球</li> </ul> <h1 id="まとめ">まとめ</h1> <p>Cyber-Dojo で Fizz Buzz の次におすすめするエクササイズ5選を紹介した❗️他にも良いエクササイズがあったらまた紹介したいと思う \( 'ω')/</p> kakku22 AWS CDK で Amazon CloudWatch Alarm の AWS Lambda アクションを設定する hatenablog://entry/6801883189075445698 2024-01-17T08:35:41+09:00 2024-01-17T08:35:41+09:00 2023年12月のリリースで Amazon CloudWatch Alarm から直接 AWS Lambda 関数を呼び出して,何かしらのアクション(復旧処理など)を実行できるようになった❗️今までは Amazon SNS と組み合わせて実行する必要があって,今までよりもシンプルに統合できるようになった.もちろん Amazon SNS でファンアウトすることによる拡張性も重要なので,個人的には AWS Lambda 関数を呼び出せれば十分というシンプルな場面で使えそうだなーと思っていたりする \( 'ω')/ aws.amazon.com AWS CDK で試す AWS CDK では v2.1… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240116/20240116203815.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>2023年12月のリリースで Amazon CloudWatch Alarm から直接 AWS Lambda 関数を呼び出して,何かしらのアクション(復旧処理など)を実行できるようになった❗️今までは Amazon SNS と組み合わせて実行する必要があって,今までよりもシンプルに統合できるようになった.もちろん Amazon SNS でファンアウトすることによる拡張性も重要なので,個人的には AWS Lambda 関数を呼び出せれば十分というシンプルな場面で使えそうだなーと思っていたりする \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fabout-aws%2Fwhats-new%2F2023%2F12%2Famazon-cloudwatch-alarms-lambda-change-action%2F" title="Amazon CloudWatch アラームにアラーム状態変更アクションとして AWS Lambda が追加" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/about-aws/whats-new/2023/12/amazon-cloudwatch-alarms-lambda-change-action/">aws.amazon.com</a></cite></p> <h1 id="AWS-CDK-で試す">AWS CDK で試す</h1> <p>AWS CDK では <strong>v2.119.0</strong> から Amazon CloudWatch Alarm の Lambda アクションを設定できるようになっていた❗️試してみたサンプルコード (TypeScript) を載せておく.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws%2Faws-cdk%2Freleases%2Ftag%2Fv2.119.0" title="Release v2.119.0 · aws/aws-cdk" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws/aws-cdk/releases/tag/v2.119.0">github.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_cloudwatch_actions-readme.html" title="aws-cdk-lib.aws_cloudwatch_actions module · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudwatch_actions-readme.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-cloudwatch-lambda-stackts">👾 sandbox-cdk-cloudwatch-lambda-stack.ts</h2> <p>Amazon SQS キューの <code>ApproximateNumberOfMessagesVisible</code> メトリクスを監視して,メッセージ数が増えたら Amazon CloudWatch Alarm から直接 AWS Lambda 関数を呼び出すようにした(Lambda 関数部分は割愛).今回のポイントは最後の <code>addAlarmAction()</code> に <code>aws_cloudwatch_actions.LambdaAction</code> を指定しているところ❗️</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Duration<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_cloudwatch<span class="synStatement">,</span> aws_cloudwatch_actions<span class="synStatement">,</span> aws_iam<span class="synStatement">,</span> aws_lambda<span class="synStatement">,</span> aws_sqs<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span><span class="synStatement">;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span><span class="synStatement">;</span> <span class="synStatement">import</span> path <span class="synStatement">=</span> require<span class="synStatement">(</span><span class="synConstant">'path'</span><span class="synStatement">);</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkCloudWatchLambdaStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">);</span> <span class="synType">const</span> queue <span class="synStatement">=</span> <span class="synStatement">new</span> aws_sqs.Queue<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkQueue'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> queueName: <span class="synConstant">'sandbox-cdk-cloudwatch-lambda-queue'</span><span class="synStatement">,</span> visibilityTimeout: Duration.seconds<span class="synStatement">(</span><span class="synConstant">30</span><span class="synStatement">),</span> receiveMessageWaitTime: Duration.seconds<span class="synStatement">(</span><span class="synConstant">20</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> lambdaBasicExecutionRole <span class="synStatement">=</span> <span class="synStatement">new</span> aws_iam.Role<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkLambdaBasicExecutionRole'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> assumedBy: <span class="synStatement">new</span> aws_iam.ServicePrincipal<span class="synStatement">(</span><span class="synConstant">'lambda.amazonaws.com'</span><span class="synStatement">),</span> managedPolicies: <span class="synIdentifier">[</span> aws_iam.ManagedPolicy.fromAwsManagedPolicyName<span class="synStatement">(</span><span class="synConstant">'service-role/AWSLambdaBasicExecutionRole'</span><span class="synStatement">)</span> <span class="synIdentifier">]</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> lambda <span class="synStatement">=</span> <span class="synStatement">new</span> aws_lambda.<span class="synSpecial">Function</span><span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkLambdaFunction'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> functionName: <span class="synConstant">'sandbox-cdk-cloudwatch-lambda-function'</span><span class="synStatement">,</span> runtime: aws_lambda.Runtime.PYTHON_3_12<span class="synStatement">,</span> code: aws_lambda.Code.fromAsset<span class="synStatement">(</span>path.join<span class="synStatement">(</span><span class="synSpecial">__dirname</span><span class="synStatement">,</span> <span class="synConstant">'../functions/hello'</span><span class="synStatement">)),</span> handler: <span class="synConstant">'app.lambda_handler'</span><span class="synStatement">,</span> role: lambdaBasicExecutionRole<span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> alarm <span class="synStatement">=</span> <span class="synStatement">new</span> aws_cloudwatch.Alarm<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkCloudWatchAlarm'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> alarmName: <span class="synConstant">'sandbox-cdk-cloudwatch-lambda-queue-alarm'</span><span class="synStatement">,</span> metric: <span class="synStatement">new</span> aws_cloudwatch.Metric<span class="synStatement">(</span><span class="synIdentifier">{</span> <span class="synStatement">namespace</span>: <span class="synConstant">'AWS/SQS'</span><span class="synStatement">,</span> metricName: <span class="synConstant">'ApproximateNumberOfMessagesVisible'</span><span class="synStatement">,</span> dimensionsMap: <span class="synIdentifier">{</span> QueueName: queue.queueName <span class="synIdentifier">}</span><span class="synStatement">,</span> period: Duration.seconds<span class="synStatement">(</span><span class="synConstant">60</span><span class="synStatement">),</span> statistic: aws_cloudwatch.Stats.SUM<span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">),</span> threshold: <span class="synConstant">5</span><span class="synStatement">,</span> evaluationPeriods: <span class="synConstant">1</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> alarm.addAlarmAction<span class="synStatement">(new</span> aws_cloudwatch_actions.LambdaAction<span class="synStatement">(</span>lambda<span class="synStatement">));</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <h2 id="結果">結果</h2> <p>Amazon SQS キューにメッセージを5件以上送信すると(今回は10件)アラーム状態になって AWS Lambda 関数が呼び出された👌</p> <p><figure class="figure-image figure-image-fotolife" title="アラーム状態と AWS Lambda アクション設定"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240116/20240116203929.png" width="1200" height="739" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>アラーム状態と AWS Lambda アクション設定</figcaption></figure></p> <h2 id="注意点">注意点</h2> <p>現時点(最新は v2.121.1)では同じ AWS Lambda 関数を複数の Amazon CloudWatch Alarm に紐付けるとエラーになってしまうという問題がある.<a href="https://github.com/aws/aws-cdk/pull/28712">&#x30D7;&#x30EB;&#x30EA;&#x30AF;&#x30A8;&#x30B9;&#x30C8;</a>も出ていて,近々直りそうな気もするけど注意しておくと良いかなーと👀</p> <pre class="code" data-lang="" data-unlink>Error: There is already a Construct with name &#39;AlarmPermission&#39; in Function [SandboxCdkLambdaFunction]</pre> kakku22 Terraform の count で制御したリソースを one 関数で output する hatenablog://entry/6801883189074033103 2024-01-16T19:43:19+09:00 2024-01-16T19:43:19+09:00 Terraform で count Meta-Argument を if のように使ってリソースを作るかどうかを制御できるという Tips がある.リソース間で参照するときは,同じく count で制御しつつ,例えば aws_iam_user.kakakakakku[0].name のようにインデックスを指定できるけど,Terraform の Output Values から参照する場合は output で制御できず,リソースを作らない場合に Error: Invalid index というエラーが出てしまう場合がある💨 one 関数と組み合わせる 対策の一つとして,Terraform の o… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240114/20240114195233.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>Terraform で <a href="https://developer.hashicorp.com/terraform/language/meta-arguments/count">count Meta-Argument</a> を <code>if</code> のように使ってリソースを作るかどうかを制御できるという Tips がある.リソース間で参照するときは,同じく <code>count</code> で制御しつつ,例えば <code>aws_iam_user.kakakakakku[0].name</code> のようにインデックスを指定できるけど,Terraform の <a href="https://developer.hashicorp.com/terraform/language/values/outputs">Output Values</a> から参照する場合は <code>output</code> で制御できず,リソースを作らない場合に <code>Error: Invalid index</code> というエラーが出てしまう場合がある💨</p> <h1 id="one-関数と組み合わせる">one 関数と組み合わせる</h1> <p>対策の一つとして,Terraform の <a href="https://developer.hashicorp.com/terraform/language/functions/one">one Function&#xFF08;one &#x95A2;&#x6570;&#xFF09;</a>と <a href="https://developer.hashicorp.com/terraform/language/expressions/splat"> Splat Expressions&#xFF08;&#x30B9;&#x30D7;&#x30E9;&#x30C3;&#x30C8;&#x6F14;&#x7B97;&#x5B50;&#xFF09;</a>を組み合わせて解決できる.以下にサンプルコードを載せておく❗️実装には特に意図はなくあくまでサンプルとして.</p> <pre class="code lang-hcl" data-lang="hcl" data-unlink><span class="synType">variable</span> <span class="synConstant">&quot;flag&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">type</span> = bool <span class="synIdentifier">default</span> = <span class="synConstant">true</span> <span class="synSpecial">}</span> <span class="synType">resource</span> <span class="synConstant">&quot;aws_iam_user&quot;</span> <span class="synConstant">&quot;kakakakakku&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">count</span> = var.flag ? <span class="synConstant">1</span> : <span class="synConstant">0</span> <span class="synIdentifier">name</span> = <span class="synConstant">&quot;sandbox-user-kakakakakku&quot;</span> <span class="synSpecial">}</span> <span class="synType">output</span> <span class="synConstant">&quot;username&quot;</span> <span class="synSpecial">{</span> <span class="synIdentifier">value</span> = <span class="synIdentifier">one</span>(aws_iam_user.kakakakakku<span class="synSpecial">[</span>*<span class="synSpecial">]</span>.name) <span class="synSpecial">}</span> </pre> <h1 id="参考">参考</h1> <p>ちなみに<strong>「詳解 Terraform 第3版」</strong>の5章で<strong>「5.2 条件分岐」</strong>のところに少し構成は違うけど one 関数とスプラット演算子を組み合わせた output の例が載っている📕</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4814400527?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51X+EauUEqL._SL500_.jpg" class="hatena-asin-detail-image" alt="詳解 Terraform 第3版 ―Infrastructure as Codeを実現する" title="詳解 Terraform 第3版 ―Infrastructure as Codeを実現する"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4814400527?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">詳解 Terraform 第3版 ―Infrastructure as Codeを実現する</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/Yevgeniy%20Brikman" class="keyword">Yevgeniy Brikman</a></li><li>オーム社</li></ul><a href="https://www.amazon.co.jp/dp/4814400527?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> kakku22 Amazon Bedrock のユースケースに実践的に入門できる「Amazon Bedrock Workshop」 hatenablog://entry/6801883189074567406 2024-01-15T13:08:44+09:00 2024-01-15T13:08:44+09:00 Amazon Bedrock で基盤モデル (Foundation Models) を実践的に活用する流れを体験できる「Amazon Bedrock Workshop」を実施した❗️Amazon Bedrock をマネジメントコンソールの playground で試すのは簡単だけど,Python (boto3) でどう実装するのかというコードレベルまで学べる💡 現在 Amazon Bedrock Workshop には以下のコンテンツがあって,Amazon Bedrock を活用できるユースケースの多さにワクワクする.実際にどのコンテンツもとてもおもしろかった👏 Prompt Engineer… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240113/20240113120421.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>Amazon Bedrock で基盤モデル (Foundation Models) を実践的に活用する流れを体験できる<strong>「Amazon Bedrock Workshop」</strong>を実施した❗️Amazon Bedrock をマネジメントコンソールの playground で試すのは簡単だけど,Python (boto3) でどう実装するのかというコードレベルまで学べる💡</p> <p>現在 Amazon Bedrock Workshop には以下のコンテンツがあって,Amazon Bedrock を活用できるユースケースの多さにワクワクする.実際にどのコンテンツもとてもおもしろかった👏</p> <ul> <li>Prompt Engineering(プロンプトエンジニアリング)</li> <li>Text Generation(テキスト生成)</li> <li>Text Summarization(テキスト要約)</li> <li>Question Answering(質問と回答)</li> <li>Conversational Interfaces - Chatbots(チャットボット)</li> <li>Image Generation(画像生成)</li> <li>Code Generation(コード生成)</li> <li>Agents for Bedrock(エージェント)</li> <li>Entity Extraction(エンティティ抽出)</li> <li>Guardrails for Chatbots(ガードレール)</li> </ul> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcatalog.us-east-1.prod.workshops.aws%2Fworkshops%2Fa4bdb007-5600-4368-81c5-ff5b4154f518%2Fen-US" title="Amazon Bedrock Workshop" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/en-US">catalog.us-east-1.prod.workshops.aws</a></cite></p> <h1 id="実施する前にインプットしておこう">実施する前にインプットしておこう</h1> <p>僕自身は Amazon Bedrock に詳しくなく,Amazon Bedrock Workshop を実施する前に必要最低限のインプットをしておこうと思った.<strong>AWS Skill Builder</strong> で無料で学べるデジタルコース<strong>「Amazon Bedrock Getting Started (Japanese) (Sub) 日本語字幕版」</strong>があって受講した💡コンパクトにまとまってて良かった \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fexplore.skillbuilder.aws%2Flearn%2Fcourse%2Fexternal%2Fview%2Felearning%2F18290%2FAmazon-Bedrock-Getting-Started-Japanese-Sub-%25E6%2597%25A5%25E6%259C%25AC%25E8%25AA%259E%25E5%25AD%2597%25E5%25B9%2595%25E7%2589%2588" title="Self-paced digital training on AWS - AWS Skill Builder" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://explore.skillbuilder.aws/learn/course/external/view/elearning/18290/Amazon-Bedrock-Getting-Started-Japanese-Sub-%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%AD%97%E5%B9%95%E7%89%88">explore.skillbuilder.aws</a></cite></p> <p>また LLM や LangChain 回りは以下の書籍を読んで写経したときの知識があって関連する部分もあった.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F10%2F16%2F085525" title="LLM を組み込んだチャットアプリケーションを写経しながら実装できる「ChatGPT/LangChain によるチャットシステム構築[実践]入門」を読んだ - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/10/16/085525">kakakakakku.hatenablog.com</a></cite></p> <h1 id="Amazon-Bedrock-Workshop-でハマったところなど">Amazon Bedrock Workshop でハマったところなど</h1> <p>あくまで個人的にではあるけど,Amazon Bedrock Workshop を実施したときに少しハマったので,少しメモしておこうと思う❗️ちなみに実施した期間は <code>2024/01/06 - 2024/01/12</code> の1週間📅 もちろん環境依存な部分もあると思うし,僕自身の勘違いもあると思うし,既に GitHub 側で修正されていることもあると思う.参考レベルとして \( 'ω')/</p> <h2 id="手順書は英語を使おう">手順書は英語を使おう</h2> <p>最初 Amazon Bedrock Workshop を見たときに日本語もあるじゃん〜と思って期待したけど,現時点では最新に追随されてなさそうだし(例えば Agents for Bedrock / Guardrails for Chatbots など),そもそも Jupyter Notebook へのリンクがないところは致命的だと思った🚨最初どうやって進めれば良いのか全然わからなかった🌀</p> <p>また GitHub で管理されてる Jupyter Notebook は比較的頻繁に更新されてるため,最新になっているか確認するのも重要だと思う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws-samples%2Famazon-bedrock-workshop" title="GitHub - aws-samples/amazon-bedrock-workshop: This is a workshop designed for Amazon Bedrock a foundational model service." class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws-samples/amazon-bedrock-workshop">github.com</a></cite></p> <h2 id="Model-access-で許可しておこう">Model access で許可しておこう</h2> <p>Amazon Bedrock Workshop を実施する前に Amazon Bedrock の <strong>Model access</strong> で許可しておく必要がある.最初は Titan のみ許可しておけば良いかな〜と思ったけど,Claude と Stable Diffusion XL を使う手順もあって,最終的に以下のようにしてみた.ちなみに Claude は <strong>Use case details</strong> も追加で入力する必要があって,Amazon Bedrock Workshop のためと書いたけど(おそらく)問題なさそうだった.</p> <p><figure class="figure-image figure-image-fotolife" title="Model access"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240113/20240113082310.png" width="1200" height="803" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Model access</figcaption></figure></p> <p>ちなみに Model access の手順に関しては以下の issue もあって,これは僕もあると良いかなと思った.特に Amazon Bedrock Workshop は初学者も試すだろうしハマりポイントが多いと挫折しやすくなってしまう.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Faws-samples%2Famazon-bedrock-workshop%2Fissues%2F87" title="Add instructions in readme.md to enable models before starting · Issue #87 · aws-samples/amazon-bedrock-workshop" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/aws-samples/amazon-bedrock-workshop/issues/87">github.com</a></cite></p> <h2 id="Amazon-Titan-モデルに旧名称があることを覚えておこう">Amazon Titan モデルに旧名称があることを覚えておこう</h2> <p>例えば <code>01_Generation/00_generate_w_bedrock.ipynb</code> を見ると <code>modelId = 'amazon.titan-tg1-large'</code> と実装されていて,Amazon Bedrock の Model access で何を許可すれば良いんだろ〜と悩んだ.調べたところモデルの旧名称らしく,そのままでも動くけど <code>amazon.titan-text-express-v1</code> に変更しても動く👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fbedrock%2Ftitan%2F" title="生成系 AI の基盤モデル - Amazon Titan - AWS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/bedrock/titan/">aws.amazon.com</a></cite></p> <h2 id="Python-仮想環境を使おう">Python 仮想環境を使おう</h2> <p>Amazon Bedrock Workshop ではセットアップするライブラリも多く環境によっては依存解決に問題が出る可能性があるため,venv など何かしらの Python 仮想環境で実行すると良いと思う.ちなみに VS Code で Jupyter Notebook を実行する場合はカーネル設定から venv を選べば OK👌</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ python <span class="synSpecial">-m</span> venv bedrockenv </pre> <h2 id="リージョンは-us-east-1-にしよう">リージョンは us-east-1 にしよう</h2> <p>セットアップの手順に書いてあるから大丈夫だと思うけど,今回僕は VS Code で Amazon Bedrock Workshop を実施したため,毎回プロファイルなどの環境変数を設定した.少し面倒ではあるけど,最初からコメントアウトされてるため,忘れることはないと思う.</p> <pre class="code lang-python" data-lang="python" data-unlink>os.environ[<span class="synConstant">&quot;AWS_DEFAULT_REGION&quot;</span>] = <span class="synConstant">&quot;us-east-1&quot;</span> os.environ[<span class="synConstant">&quot;AWS_PROFILE&quot;</span>] = <span class="synConstant">&quot;xxxxx&quot;</span> </pre> <p>ちなみに実行すると以下のように表示されるけど <code>us-east-1</code> になってることは確認しておくと良いと思う.1回設定を間違えていることに気付かず進んだら <code>AccessDeniedException</code> や <code>ResourceNotFoundException</code> が出てしまって,原因は <code>ap-northeast-1</code> で <code>bedrock-runtime</code> が初期化されてしまっていた💨</p> <pre class="code" data-lang="" data-unlink>Create new client Using region: us-east-1 Using profile: xxxxx boto3 Bedrock client successfully created! bedrock-runtime(https://bedrock-runtime.us-east-1.amazonaws.com)</pre> <h1 id="特に印象的だったコンテンツ">特に印象的だったコンテンツ</h1> <p>正直どのコンテンツも勉強になったけど,特に印象的だった3つを抜粋してみた❗️</p> <h2 id="Image-Generation">Image Generation</h2> <p><strong>Stable Diffusion</strong> 自体はオンラインで試したことはあるけど,Amazon Bedrock と組み合わせて Text to Image / Image to Image / Image Inpainting を試せる.以下は <code>a beautiful mountain landscape</code> というプロンプトでマスク画像と組み合わせた Image Inpainting の結果例🎨</p> <p><figure class="figure-image figure-image-fotolife" title="Image Generation: a beautiful mountain landscape"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240112/20240112233632.png" width="768" height="512" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Image Generation: a beautiful mountain landscape</figcaption></figure></p> <h2 id="Agents-for-Bedrock">Agents for Bedrock</h2> <p>2023年12月に正式リリースになった <strong>Agents for Amazon Bedrock</strong> も試せる.オンライン靴屋のチャットボットを実装しつつ,顧客名に紐付くデータを <strong>Lambda (+SQLite)</strong> で取得してレコメンドを返したりする.RAG / ReAct の仕組み・アクショングループの OpenAPI 定義など Agents for Amazon Bedrock の実装イメージを掴めて良かった❗️</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Faws.amazon.com%2Fjp%2Fblogs%2Fnews%2Fagents-for-amazon-bedrock-is-now-available-with-improved-control-of-orchestration-and-visibility-into-reasoning%2F" title="オーケストレーションの制御が強化され、推論の可視性が向上した Agents for Amazon Bedrock が利用可能になりました | Amazon Web Services" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://aws.amazon.com/jp/blogs/news/agents-for-amazon-bedrock-is-now-available-with-improved-control-of-orchestration-and-visibility-into-reasoning/">aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fbedrock%2Flatest%2Fuserguide%2Fagents-api-schema.html" title="Action group OpenAPI schemas - Amazon Bedrock" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-api-schema.html">docs.aws.amazon.com</a></cite></p> <p><figure class="figure-image figure-image-fotolife" title="Agents for Amazon Bedrock(CoT トレース付き)"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240112/20240112232318.png" width="1200" height="550" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Agents for Amazon Bedrock(CoT トレース付き)</figcaption></figure></p> <h2 id="Guardrails-for-Chatbots">Guardrails for Chatbots</h2> <p>Amazon Bedrock と <strong>NeMo Guardrails</strong> を組み合わせて,回答したくないことなどをガードレールとして制御する仕組みを試せる.例えば以下は <strong>Topical Rail</strong> を試した結果で,事前に設定したトピックを逸脱した <code>who should I vote for?</code> という質問をしたときにガードレールとして機能している🚧</p> <p><figure class="figure-image figure-image-fotolife" title="Amazon Bedrock と NeMo Guardrails を組み合わせる"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240112/20240112233349.png" width="1200" height="697" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon Bedrock と NeMo Guardrails を組み合わせる</figcaption></figure></p> <h1 id="実施ログ抜粋">実施ログ抜粋</h1> <p>その他実施ログも一部載せておく📝</p> <p>最初に <code>00_Intro/bedrock_boto3_setup.ipynb</code> で基盤モデル (Foundation Models) を取得するコードをちょっと書き換えて <code>modelId</code> のみを取得してみた.</p> <pre class="code lang-python" data-lang="python" data-unlink>models = boto3_bedrock.list_foundation_models() <span class="synStatement">for</span> model <span class="synStatement">in</span> models[<span class="synConstant">'modelSummaries'</span>]: <span class="synIdentifier">print</span>(model[<span class="synConstant">'modelId'</span>]) </pre> <p>取得できたモデル一覧は以下だった.</p> <pre class="code" data-lang="" data-unlink>amazon.titan-tg1-large amazon.titan-image-generator-v1:0 amazon.titan-image-generator-v1 amazon.titan-embed-g1-text-02 amazon.titan-text-lite-v1:0:4k amazon.titan-text-lite-v1 amazon.titan-text-express-v1:0:8k amazon.titan-text-express-v1 amazon.titan-embed-text-v1:2:8k amazon.titan-embed-text-v1 amazon.titan-embed-image-v1:0 amazon.titan-embed-image-v1 stability.stable-diffusion-xl stability.stable-diffusion-xl-v0 stability.stable-diffusion-xl-v1:0 stability.stable-diffusion-xl-v1 ai21.j2-grande-instruct ai21.j2-jumbo-instruct ai21.j2-mid ai21.j2-mid-v1 ai21.j2-ultra ai21.j2-ultra-v1 anthropic.claude-instant-v1:2:100k anthropic.claude-instant-v1 anthropic.claude-v1 anthropic.claude-v2:0:18k anthropic.claude-v2:0:100k anthropic.claude-v2:1:18k anthropic.claude-v2:1:200k anthropic.claude-v2:1 anthropic.claude-v2 cohere.command-text-v14:7:4k cohere.command-text-v14 cohere.command-light-text-v14:7:4k cohere.command-light-text-v14 cohere.embed-english-v3 cohere.embed-multilingual-v3 meta.llama2-13b-chat-v1:0:4k meta.llama2-13b-chat-v1 meta.llama2-70b-chat-v1:0:4k meta.llama2-70b-chat-v1 meta.llama2-13b-v1:0:4k meta.llama2-13b-v1 meta.llama2-70b-v1:0:4k meta.llama2-70b-v1</pre> <p><code>modelId</code> は以下のドキュメントにも載っていた📝</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fbedrock%2Flatest%2Fuserguide%2Fmodel-ids-arns.html" title="Amazon Bedrock Base model IDs (on-demand throughput) - Amazon Bedrock" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html">docs.aws.amazon.com</a></cite></p> <p>また同じ <strong>Text</strong> 系のモデルなら Claude を Titan に差し替えても動くのかな〜という思ってコードを書き換えながら試行錯誤してたら,モデルによって推論パラメータが違うことを学んだ.例えば Claude に <code>top_k</code> はあるけど Titan はないなど.もちろん精度は異なると思うけど,Jupyter Notebook を単に実行するだけでなく,コードを書き換えながら実行するのも勉強になった💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fbedrock%2Flatest%2Fuserguide%2Fmodel-parameters.html" title="Inference parameters for foundation models - Amazon Bedrock" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html">docs.aws.amazon.com</a></cite></p> <h1 id="まとめ">まとめ</h1> <p><strong>「Amazon Bedrock Workshop」</strong>を実施した❗️コンテンツが充実してて,実践的に学べて良かった.個人的には少しハマりポイントがあったけど,今後実施する人の参考になればと思う💡Amazon Bedrock Workshop おすすめでーす \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcatalog.us-east-1.prod.workshops.aws%2Fworkshops%2Fa4bdb007-5600-4368-81c5-ff5b4154f518%2Fen-US" title="Amazon Bedrock Workshop" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/en-US">catalog.us-east-1.prod.workshops.aws</a></cite></p> <p>次は <strong>Building with Amazon Bedrock and LangChain</strong> ワークショップも気になるー👀</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcatalog.workshops.aws%2Fbuilding-with-amazon-bedrock%2Fen-US" title="Building with Amazon Bedrock and LangChain" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://catalog.workshops.aws/building-with-amazon-bedrock/en-US">catalog.workshops.aws</a></cite></p> kakku22 2023年の振り返りと2024年の抱負 hatenablog://entry/6801883189073508632 2024-01-14T11:32:40+09:00 2024-01-14T11:32:40+09:00 2023年の振り返り🎉 技術を学び続けた 2019年から5年連続で「幅広く技術を語れるように学び続ける」を年間目標にしていて,さらに2023年は「実戦投入力」を意識することも追加していた.後ほど紹介するけど,2023年は kakakakakku blog に年間100記事書くことができて,それほどインプットとアウトプットを繰り返して学び続けられたと思う.特に2023年4月からは開発現場に戻ったため,今まで以上に学ぶことも増えて,そしてブログにアウトプットしたいことも増えた.本当に日々ブログネタが見つかる状態で,ふと2017年に発表した「ブログを書く技術」の一言を思い出したりもしていた💡 201… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240108/20240108184016.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="2023年の振り返り">2023年の振り返り🎉</h1> <h2 id="技術を学び続けた">技術を学び続けた</h2> <p>2019年から5年連続で<strong>「幅広く技術を語れるように学び続ける」</strong>を年間目標にしていて,さらに2023年は<strong>「実戦投入力」</strong>を意識することも追加していた.後ほど紹介するけど,2023年は <strong>kakakakakku blog</strong> に年間100記事書くことができて,それほどインプットとアウトプットを繰り返して学び続けられたと思う.特に2023年4月からは開発現場に戻ったため,今まで以上に学ぶことも増えて,そしてブログにアウトプットしたいことも増えた.本当に日々ブログネタが見つかる状態で,ふと2017年に発表した<strong>「ブログを書く技術」</strong>の一言を思い出したりもしていた💡</p> <p><figure class="figure-image figure-image-fotolife" title="ブログを書く技術"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240108/20240108222600.png" width="1024" height="576" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption><a href="https://kakakakakku.hatenablog.com/entry/2017/11/27/202252">2017&#x5E74;&#x306B;&#x767A;&#x8868;&#x3057;&#x305F;&#x300C;&#x30D6;&#x30ED;&#x30B0;&#x3092;&#x66F8;&#x304F;&#x6280;&#x8853;&#x300D;&#x304B;&#x3089;&#x5F15;&#x7528;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2017/11/27/202252" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2017/11/27/202252" alt="" class="http-bookmark" /></a></figcaption></figure></p> <p>そして,2023年の四半期ごとに掲げていた中目標も紹介する.2022年10月から本格的に英語を学び直していて,2023年3月に先輩の住むシアトルに1人で遊びに行って実践してみたり,技術的には今までほとんど経験がなかった Terraform を本気で勉強したり,AWS でのセキュリティ対策全般の設計と構築にゼロベースで取り組んだりもした.</p> <ul> <li>1-3月 : 英語学習に再入門する</li> <li>4-6月 : AWS のノウハウを積極的にアウトプットする</li> <li>7-9月 : Terraform を実践的に学ぶ</li> <li>10-12月 : AWS のセキュリティ対策全般を学ぶ</li> </ul> <h2 id="フリーランスになった">フリーランスになった</h2> <p>2023年3月末で4年半ほど続けていた技術講師(AWS シニアテクニカルトレーナー)の仕事を辞めて,2023年4月からフリーランスとして働き始めたことは2023年の中で1番大きな変化だったと思う.開発現場に戻って,アーキテクチャを設計したり・開発したり・運用したり・開発チームの育成を支援したり,とにかく毎日楽しすぎて気付いたら年末になっていたような状態😀そして,技術講師時代は<strong>「技術を教えるための知識」</strong>を重視して学んでいたけど,開発現場に戻ってからは<strong>「動き続けるサービスをより良くするための実践的な知識」</strong>を重視して学んでいて,観点の違う学びを融合しながら仕事に活かすことができた1年間だった.</p> <p>またフリーランスになってからは週4日(月〜木)を仕事日にして,週1日(金)は YouTube の動画編集を勉強したり,気になっている技術の勉強をしたり,自由に使える自己研鑽の日にしていた.仕事が週4日なのでメンタル的にも常に安定していて,収入も減ることなく,とにかく大満足な日々を過ごせた.<a href="https://kakakakakku.hatenablog.com/entry/2022/01/18/100051">&#x300C;2021&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2022/01/18/100051" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2022/01/18/100051" alt="" class="http-bookmark" /></a>と<a href="https://kakakakakku.hatenablog.com/entry/2023/01/07/201740">&#x300C;2022&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/01/07/201740" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/01/07/201740" alt="" class="http-bookmark" /></a>に<strong>「1年間を通して余裕がなかった」</strong>と書いているけど,2023年は本当に心に余裕があってたくさん新しいことに挑戦することもできて最高だった👌 仕事関連の振り返りはまた別途まとめる予定.</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240108/20240108225314.png" width="400" height="400" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2 id="技術書を出版した">技術書を出版した</h2> <p>2023年1月21日に<strong>「AWS で実現するモダンアプリケーション入門」</strong>を出版したことは個人的にインパクトのある成果だった📕今まで技術書を執筆した経験はなく,出版までの道のりはそれはもう大変だったけど,出版社様から直接お話をもらって,企画をまとめて,多くの方々にサポートしてもらって,最終的に出版することができた.技術書をゼロベースで企画・執筆して出版できたことは今後にも活きる本当に貴重な経験になった❗️</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4297133261?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51pPURigx6L._SL500_.jpg" class="hatena-asin-detail-image" alt="AWSで実現するモダンアプリケーション入門 〜サーバーレス、コンテナ、マイクロサービスで何ができるのか" title="AWSで実現するモダンアプリケーション入門 〜サーバーレス、コンテナ、マイクロサービスで何ができるのか"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4297133261?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">AWSで実現するモダンアプリケーション入門 〜サーバーレス、コンテナ、マイクロサービスで何ができるのか</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%CD%EE%BF%E5%20%B6%B3%B2%F0" class="keyword">落水 恭介</a>,<a href="https://d.hatena.ne.jp/keyword/%B5%C8%C5%C4%20%B7%C4%BE%CF" class="keyword">吉田 慶章</a></li><li>技術評論社</li></ul><a href="https://www.amazon.co.jp/dp/4297133261?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="YouTube-をはじめた">YouTube をはじめた</h2> <p>詳しくは振り返り記事にまとめたけど,2023年に<strong>「動画編集に入門する」</strong>という目標があって YouTube をはじめた.マイクラ実況というテーマではあるけど,YouTube 動画の企画・収録・編集・公開という流れを経験できたことは本当に価値があって,今後のアウトプットの新しい選択肢にもできそうだなという感覚もある💡特に動画編集の難しさはやればやるほど感じることができて,もっと本格的に学びたいな〜と思っている.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F01%2F07%2F174200" title="カックマイクラ実況 YouTube 振り返り(2023年) - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/01/07/174200">kakakakakku.hatenablog.com</a></cite></p> <h2 id="朝活をはじめた">朝活をはじめた</h2> <p>詳しくは振り返り記事にまとめたけど,2023年は<strong>「朝活」</strong>の習慣化に取り組んで,多くのインプット・アウトプットにつなげることができた☕とにかくもう朝活最高〜な気持ち \( 'ω')/ 2024年も継続中っっっ</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F01%2F08%2F105017" title="朝活 振り返り(2023年) - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/01/08/105017">kakakakakku.hatenablog.com</a></cite></p> <h1 id="インプットアウトプット">インプット/アウトプット💡</h1> <h2 id="テックブログ">テックブログ</h2> <p>2023年は<strong>「週1記事(52記事)」</strong>のノルマとストレッチゴールの<strong>「70記事」</strong>を掲げていたけど,ノルマを達成できなかった週はなく,さらに目標を大幅に超えて<strong>「年間100記事」</strong>を達成できた❗️個人的に年間100記事というのは2016年以来到達できてなく,それほどに2023年は多くのインプットとアウトプットを楽しめた1年間だったと言える.2023年からはまた AWS 関連の記事をたくさん書こうと決めていて,2023年4月から<strong>「42記事」</strong>も書いたことも影響している👌</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">2023年の kakakakakku blog は合計100記事公開を達成しましたー❗️ありがとうございます❗️うおおおー \( &#39;ω&#39;)/ そして100記事到達はなんと2016年以来の快挙✌️個人的に2023年はたくさん学べたな〜と思うぞッッッ <a href="https://t.co/oAzubmfw3Y">pic.twitter.com/oAzubmfw3Y</a></p>&mdash; カック (@kakakakakku) <a href="https://twitter.com/kakakakakku/status/1740343781126443331?ref_src=twsrc%5Etfw">2023年12月28日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>累計ブクマ数は<strong>「18644 → 20687 (+2043)」</strong>となり,グラフ的に大きく伸びている3箇所は<a href="https://kakakakakku.hatenablog.com/entry/2023/02/22/084137">&#x300C;&#x9650;&#x308A;&#x3042;&#x308B;&#x6642;&#x9593;&#x306E;&#x4F7F;&#x3044;&#x65B9;&#x306E;&#x66F8;&#x8A55;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/02/22/084137" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/02/22/084137" alt="" class="http-bookmark" /></a>と<a href="https://kakakakakku.hatenablog.com/entry/2023/03/21/105935">&#x300C;Learn Go with Tests &#x306E;&#x7D39;&#x4ECB;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/03/21/105935" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/03/21/105935" alt="" class="http-bookmark" /></a>と<a href="https://kakakakakku.hatenablog.com/entry/2023/05/08/094426">&#x300C;AWS Lambda Operator Guide &#x306E;&#x7D39;&#x4ECB;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/05/08/094426" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/05/08/094426" alt="" class="http-bookmark" /></a>の影響だった📈 また Google Analytics を見て,この3記事以外に PV の多かった記事は<a href="https://kakakakakku.hatenablog.com/entry/2023/02/02/111644">&#x300C;act &#x306E;&#x7D39;&#x4ECB;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/02/02/111644" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/02/02/111644" alt="" class="http-bookmark" /></a>と<a href="https://kakakakakku.hatenablog.com/entry/2023/03/01/101539">&#x300C;&#x30DE;&#x30E9;&#x30F3;&#x30C4;&#x30D7;&#x30ED; M4U &#x306E;&#x7D39;&#x4ECB;&#x300D;</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/03/01/101539" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/03/01/101539" alt="" class="http-bookmark" /></a>だった.</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240108/20240108185325.png" width="600" height="371" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2 id="プルリクエスト">プルリクエスト</h2> <p>詳しくは振り返り記事にまとめた.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F12%2F22%2F221554" title="2023年のプルリクエストを振り返る - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/12/22/221554">kakakakakku.hatenablog.com</a></cite></p> <h2 id="読んだ本">読んだ本</h2> <p>2023年は<strong><a href="https://kakakakakku.hatenablog.com/archive/category/%E6%9C%AC">&#x300C;&#x8A08;14&#x518A;&#x300D;</a></strong>を読んで書評を書いた.また2023年は書評記事を書かずに X (Twitter) に読んだ感想をポストするだけでも OK にしたことで,実際には<strong>「計20冊」</strong>も読めた.どうしても書評記事を書こうとすると準備負荷が高くなってしまうため,自分に最適な読書スタイルを見つけられた気がする.</p> <p>2023年に読んだ本の中で特に良かったものを2冊選ぶとするならば<strong>「完璧を求める心理」</strong>と<strong>「システム運用アンチパターン」</strong>かな📕特に<strong>「完璧を求める心理」</strong>は良くも悪くも完璧主義だと言われる自分自身をよく知ることができたし,完璧主義とは何かという理解の解像度を上げることができて,もっと早く読みたかったほどに良かった.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F03%2F13%2F133057" title="完璧主義だと言われる僕が自分の性格をもっと理解するために「完璧を求める心理」を読んだ - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/03/13/133057">kakakakakku.hatenablog.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2023%2F10%2F30%2F090954" title="&quot;何かうまくいってない&quot; 開発組織でよく見る光景と打開策がまとまった「システム運用アンチパターン」を読んだ - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2023/10/30/090954">kakakakakku.hatenablog.com</a></cite></p> <h1 id="買って良かったモノ">買って良かったモノ🎁</h1> <p>2023年12月に買ったポモドーロタイマー<strong>「TIME TIMER」</strong>は本当に良くて買ってからはほぼ毎日使ってる🕐</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B08K9G2KS9?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/41HvhTIxgsL._SL500_.jpg" class="hatena-asin-detail-image" alt="タイムタイマー(TIME TIMER) 勉強タイマー MOD Home Edition モッド ペールグレー 9cm 60分 学習アラーム TTM9-HPS-W" title="タイムタイマー(TIME TIMER) 勉強タイマー MOD Home Edition モッド ペールグレー 9cm 60分 学習アラーム TTM9-HPS-W"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B08K9G2KS9?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">タイムタイマー(TIME TIMER) 勉強タイマー MOD Home Edition モッド ペールグレー 9cm 60分 学習アラーム TTM9-HPS-W</a></p><ul class="hatena-asin-detail-meta"><li>Time Timer</li></ul><a href="https://www.amazon.co.jp/dp/B08K9G2KS9?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ポモドーロを回すときにアプリとガジェットをその時の気分で選んでるんだけど,今まで使ってたサイコロ型のタイマーが壊れたから新しく TIME TIMER を買って使ってるんだけど良いぞ❗子供に使ってもらうのもあり👌<a href="https://t.co/8GNQX7xgn0">https://t.co/8GNQX7xgn0</a></p>&mdash; カック (@kakakakakku) <a href="https://twitter.com/kakakakakku/status/1737027236065349739?ref_src=twsrc%5Etfw">2023年12月19日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>2023年10月に届いた<strong>「HHKB 用タイピングベッド PW-HHC-2」</strong>も気に入っていて毎日使ってる👌慣れるまでに数日かかったけど買って良かったな〜という気持ち \( 'ω')/</p> <ul> <li><a href="https://thundervolt.club/products/hhkb-pw-hhc-2">HHKB&#x7528;&#x30BF;&#x30A4;&#x30D4;&#x30F3;&#x30B0;&#x30D9;&#x30C3;&#x30C9; PW-HHC-2 | CLUB ThunderVolt</a><a href="https://b.hatena.ne.jp/entry/https://thundervolt.club/products/hhkb-pw-hhc-2" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://thundervolt.club/products/hhkb-pw-hhc-2" alt="" class="http-bookmark" /></a></li> </ul> <h1 id="2024年の抱負">2024年の抱負✨</h1> <h2 id="お仕事を楽しく頑張って貢献する">お仕事を楽しく頑張って貢献する</h2> <p>2024年も引き続きフリーランスとして活動していく予定なので,定量的な評価は難しいけど,支援させてもらってる開発現場に最大限貢献したいと思う💪今ある経験ですぐに貢献できることもあるけど,もちろんそれだけでは十分ではなく技術的に未経験のことにも積極的に挑戦しながら貢献できるようにする.また技術だけでは解決できないこともあるので<strong>「対話」</strong>も大切にしたいと思う.とにかく僕の Super Power(強み)は楽しく学び続けることができることなので,自分自身の価値も高めていく❗️</p> <p>そのためにはインプット・アウトプットを続けていく.2024年もテックブログの定量的な目標は継続するけど,ストレッチゴールも目指す.2023年に<strong>「年間100記事」</strong>を達成できた喜びを2024年も感じたいと思う.ちなみに100記事を目指す場合,週2記事ペースで書く必要があるけど,仕事などによって1週間で使える時間に波があるため,週ノルマとしては週1にしている👌</p> <ul> <li>ブログ<strong>「週1記事(52記事)」</strong>ノルマ</li> <li>ストレッチゴールとして1年間で<strong>「100記事」</strong></li> </ul> <p><a href="https://www.amazon.jobs/content/jp/our-workplace/leadership-principles">Leadership Principles</a><a href="https://b.hatena.ne.jp/entry/https://www.amazon.jobs/content/jp/our-workplace/leadership-principles" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.amazon.jobs/content/jp/our-workplace/leadership-principles" alt="" class="http-bookmark" /></a> で言うと特に <strong>Deliver Results</strong> / <strong>Have Backbone; Disagree and Commit</strong> / <strong>Learn and Be Curious</strong> を実践できるように楽しく頑張る❗️他にもお仕事の機会などありましたらぜひお声がけくださいませ〜 \( 'ω')/</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240113/20240113212457.png" width="365" height="400" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2 id="興味を持ってもらえる動画を作る">興味を持ってもらえる動画を作る</h2> <p>2024年も引き続き YouTube 動画を作っていく👾2023年は動画編集初体験ってこともあって,誰にも見られなくても気にせず動画編集を経験することを優先していたけど,2024年はもう少し見てもらえるように興味を持ってもらえる動画を作れるように工夫してみたいと思う.今はマイクラ実況のみだけど,テクノロジー系の YouTube 動画も作れたら良いなぁー😃</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240108/20240108225201.png" width="500" height="337" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="まとめ">まとめ</h1> <p>2023年はたくさん挑戦できた変化の1年でした.</p> <p>2024年も楽しむぞ〜❗️よろしくお願いしまーすっっっ</p> <h1 id="過去の振り返り">過去の振り返り</h1> <ul> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/01/07/201740">2022&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2023&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/01/07/201740" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/01/07/201740" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2022/01/18/100051">2021&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2022&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2022/01/18/100051" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2022/01/18/100051" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2021/01/01/184735">2020&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2021&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2021/01/01/184735" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2021/01/01/184735" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2020/01/21/184451">2019&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2020&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2020/01/21/184451" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2020/01/21/184451" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2019/01/01/030004">2018&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2019&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2019/01/01/030004" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2019/01/01/030004" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2018/01/02/034802">2017&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2018&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2018/01/02/034802" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2018/01/02/034802" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2017/01/03/002038">2016&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2017&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2017/01/03/002038" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2017/01/03/002038" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2016/01/02/191155">2015&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2016&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2016/01/02/191155" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2016/01/02/191155" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2015/01/04/210728">2014&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2015&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2015/01/04/210728" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2015/01/04/210728" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2014/01/06/000410">2013&#x5E74;&#x306E;&#x632F;&#x308A;&#x8FD4;&#x308A;&#x3068;2014&#x5E74;&#x306E;&#x62B1;&#x8CA0; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2014/01/06/000410" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2014/01/06/000410" alt="" class="http-bookmark" /></a></li> </ul> kakku22 AWS CDK で Amazon EventBridge Pipes(SQS ソース・Step Functions ターゲット)を設定する hatenablog://entry/6801883189071627036 2024-01-10T21:31:51+09:00 2024-01-10T21:31:51+09:00 AWS CDK で Amazon EventBridge Pipes を設定する場合,AWS CloudFormation に沿った L1 Construct の CfnPipeを使う必要がある.今回はソース(Amazon SQS キュー)・ターゲット(AWS Step Functions ステートマシン)で Amazon EventBridge Pipes を構成する👌 docs.aws.amazon.com ちなみに Amazon EventBridge Pipes をソース(Amazon SQS キュー)・エンリッチメント(AWS Lambda 関数)・ターゲット(Amazon ECS … <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240101/20240101224217.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK で <strong>Amazon EventBridge Pipes</strong> を設定する場合,AWS CloudFormation に沿った <strong>L1 Construct</strong> の <code>CfnPipe</code>を使う必要がある.今回はソース(Amazon SQS キュー)・ターゲット(AWS Step Functions ステートマシン)で Amazon EventBridge Pipes を構成する👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.html" title="class CfnPipe (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.html">docs.aws.amazon.com</a></cite></p> <p>ちなみに Amazon EventBridge Pipes をソース(Amazon SQS キュー)・エンリッチメント(AWS Lambda 関数)・ターゲット(Amazon ECS タスク)で構成するのは以下の記事にまとめた🔗 似てるところもあるので参考になれば〜 \( 'ω')/</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkakakakakku.hatenablog.com%2Fentry%2F2024%2F01%2F09%2F225726" title="AWS CDK で Amazon EventBridge Pipes(SQS ソース・Lambda エンリッチメント・ECS ターゲット)を設定する - kakakakakku blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://kakakakakku.hatenablog.com/entry/2024/01/09/225726">kakakakakku.hatenablog.com</a></cite></p> <h1 id="AWS-CDK-で試す">AWS CDK で試す</h1> <p>L1 Construct なので AWS CloudFormation の <code>AWS::Pipes::Pipe</code> のドキュメントも参考にしながら進めた.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2FAWSCloudFormation%2Flatest%2FUserGuide%2Faws-resource-pipes-pipe.html" title="AWS::Pipes::Pipe - AWS CloudFormation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-pipes-pipe.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-sqs-pipes-stepfunctions-stackts">👾 sandbox-cdk-sqs-pipes-stepfunctions-stack.ts</h2> <p>実装を簡単にするため,AWS Step Functions ステートマシンは <code>Succeed</code> のみにしている💡</p> <p><figure class="figure-image figure-image-fotolife" title="AWS Step Functions ステートマシン"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240101/20240101231701.png" width="264" height="231" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>AWS Step Functions ステートマシン</figcaption></figure></p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Duration<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_iam<span class="synStatement">,</span> aws_pipes<span class="synStatement">,</span> aws_sqs<span class="synStatement">,</span> aws_stepfunctions<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span><span class="synStatement">;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span><span class="synStatement">;</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkSqsPipesStepfunctionsStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">);</span> <span class="synType">const</span> queue <span class="synStatement">=</span> <span class="synStatement">new</span> aws_sqs.Queue<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkQueue'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> queueName: <span class="synConstant">'sandbox-cdk-sqs-pipes-stepfunctions-queue'</span><span class="synStatement">,</span> visibilityTimeout: Duration.seconds<span class="synStatement">(</span><span class="synConstant">30</span><span class="synStatement">),</span> receiveMessageWaitTime: Duration.seconds<span class="synStatement">(</span><span class="synConstant">20</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> definition <span class="synStatement">=</span> <span class="synStatement">new</span> aws_stepfunctions.Succeed<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'Success!'</span><span class="synStatement">);</span> <span class="synType">const</span> stateMachine <span class="synStatement">=</span> <span class="synStatement">new</span> aws_stepfunctions.StateMachine<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkStepfunctions'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> stateMachineName: <span class="synConstant">'sandbox-cdk-sqs-pipes-stepfunctions-state-machine'</span><span class="synStatement">,</span> definitionBody: aws_stepfunctions.DefinitionBody.fromChainable<span class="synStatement">(</span>definition<span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> pipeRole <span class="synStatement">=</span> <span class="synStatement">new</span> aws_iam.Role<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkPipesRole'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> roleName: <span class="synConstant">'sandbox-cdk-sqs-pipes-stepfunctions-role'</span><span class="synStatement">,</span> assumedBy: <span class="synStatement">new</span> aws_iam.ServicePrincipal<span class="synStatement">(</span><span class="synConstant">'pipes.amazonaws.com'</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> queue.grantConsumeMessages<span class="synStatement">(</span>pipeRole<span class="synStatement">)</span> stateMachine.grantStartExecution<span class="synStatement">(</span>pipeRole<span class="synStatement">)</span> <span class="synStatement">new</span> aws_pipes.CfnPipe<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkPipes'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> name: <span class="synConstant">'sandbox-cdk-sqs-pipes-stepfunctions-pipes'</span><span class="synStatement">,</span> roleArn: pipeRole.roleArn<span class="synStatement">,</span> source: queue.queueArn<span class="synStatement">,</span> target: stateMachine.stateMachineArn<span class="synStatement">,</span> targetParameters: <span class="synIdentifier">{</span> stepFunctionStateMachineParameters: <span class="synIdentifier">{</span> invocationType: <span class="synConstant">'FIRE_AND_FORGET'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <h2 id="結果">結果</h2> <p>以下のように期待通り Amazon EventBridge Pipes を設定できた❗️また動作確認として Amazon SQS キューにメッセージを送信すると,最終的に AWS Step Functions ステートマシンが実行された👌</p> <p><figure class="figure-image figure-image-fotolife" title="Amazon EventBridge Pipes 画面"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240101/20240101231242.png" width="1200" height="653" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon EventBridge Pipes 画面</figcaption></figure></p> <h2 id="ポイントソースターゲット">ポイント(ソース・ターゲット)</h2> <p>Amazon SQS キューを Amazon EventBridge Pipes の<strong>「ソース」</strong>に設定する場合は <code>source</code> に ARN を設定する.細かくパラメータを設定する場合は <code>SourceParameters</code> の <code>sqsQueueParameters</code> を使う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.PipeSourceSqsQueueParametersProperty.html" title="interface PipeSourceSqsQueueParametersProperty · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.PipeSourceSqsQueueParametersProperty.html">docs.aws.amazon.com</a></cite></p> <p>AWS Step Functions ステートマシンを Amazon EventBridge Pipes の<strong>「ターゲット」</strong>に設定する場合は <code>target</code> に ARN を設定する.細かくパラメータを設定する場合は <code>targetParameters</code> の <code>stepFunctionStateMachineParameters</code> を使う.標準ステートマシンを実行する場合は <code>invocationType</code> に <code>FIRE_AND_FORGET</code> を設定すれば OK👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.PipeTargetStateMachineParametersProperty.html" title="interface PipeTargetStateMachineParametersProperty · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.PipeTargetStateMachineParametersProperty.html">docs.aws.amazon.com</a></cite></p> <h2 id="ポイント実行ロール">ポイント(実行ロール)</h2> <p>Amazon EventBridge Pipes の実行ロールに Amazon SQS キューの読み取り権限・AWS Step Functions ステートマシンの実行権限を与える必要がある.最小権限を考えるとポリシーの設定が大変だけど <code>grantConsumeMessages(grantee)</code> / <code>grantStartExecution(grantee)</code> を使えば簡単に設定できる.AWS CDK の <code>grant</code> は本当に便利だ〜💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_sqs.Queue.html" title="class Queue (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_sqs.Queue.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_stepfunctions.StateMachine.html" title="class StateMachine (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_stepfunctions.StateMachine.html">docs.aws.amazon.com</a></cite></p> kakku22 AWS CDK で Amazon EventBridge Pipes(SQS ソース・Lambda エンリッチメント・ECS ターゲット)を設定する hatenablog://entry/6801883189071599505 2024-01-09T22:57:26+09:00 2024-01-09T22:57:26+09:00 AWS CDK で Amazon EventBridge Pipes を設定する場合,AWS CloudFormation に沿った L1 Construct の CfnPipeを使う必要がある.今回はソース(Amazon SQS キュー)・エンリッチメント(AWS Lambda 関数)・ターゲット(Amazon ECS タスク)で Amazon EventBridge Pipes を構成する👌 docs.aws.amazon.com AWS CDK で試す L1 Construct なので AWS CloudFormation の AWS::Pipes::Pipe のドキュメントも参考にしな… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240101/20240101213718.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>AWS CDK で <strong>Amazon EventBridge Pipes</strong> を設定する場合,AWS CloudFormation に沿った <strong>L1 Construct</strong> の <code>CfnPipe</code>を使う必要がある.今回はソース(Amazon SQS キュー)・エンリッチメント(AWS Lambda 関数)・ターゲット(Amazon ECS タスク)で Amazon EventBridge Pipes を構成する👌</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.html" title="class CfnPipe (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.html">docs.aws.amazon.com</a></cite></p> <h1 id="AWS-CDK-で試す">AWS CDK で試す</h1> <p>L1 Construct なので AWS CloudFormation の <code>AWS::Pipes::Pipe</code> のドキュメントも参考にしながら進めた.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2FAWSCloudFormation%2Flatest%2FUserGuide%2Faws-resource-pipes-pipe.html" title="AWS::Pipes::Pipe - AWS CloudFormation" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-pipes-pipe.html">docs.aws.amazon.com</a></cite></p> <h2 id="-sandbox-cdk-sqs-pipes-ecs-stackts">👾 sandbox-cdk-sqs-pipes-ecs-stack.ts</h2> <p>実装を簡単にするため,以下のリソースは別途作ったリソースを参照している💡また実行する Amazon ECS タスクはサンプルとして <a href="https://hub.docker.com/_/hello-world">hello-world</a><a href="https://b.hatena.ne.jp/entry/https://hub.docker.com/_/hello-world" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://hub.docker.com/_/hello-world" alt="" class="http-bookmark" /></a> を使っている🐳</p> <ul> <li>AWS Lambda 関数</li> <li>Amazon VPC</li> <li>Amazon ECS クラスタ</li> </ul> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">import</span> <span class="synIdentifier">{</span> Duration<span class="synStatement">,</span> Stack<span class="synStatement">,</span> StackProps<span class="synStatement">,</span> aws_ec2<span class="synStatement">,</span> aws_ecs<span class="synStatement">,</span> aws_iam<span class="synStatement">,</span> aws_lambda<span class="synStatement">,</span> aws_logs<span class="synStatement">,</span> aws_pipes<span class="synStatement">,</span> aws_sqs<span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'aws-cdk-lib'</span><span class="synStatement">;</span> <span class="synStatement">import</span> <span class="synIdentifier">{</span> Construct <span class="synIdentifier">}</span> <span class="synStatement">from</span> <span class="synConstant">'constructs'</span><span class="synStatement">;</span> <span class="synStatement">export</span> <span class="synStatement">class</span> SandboxCdkSqsPipesEcsStack <span class="synStatement">extends</span> Stack <span class="synIdentifier">{</span> <span class="synStatement">constructor(</span>scope: Construct<span class="synStatement">,</span> id: <span class="synType">string</span><span class="synStatement">,</span> props?: StackProps<span class="synStatement">)</span> <span class="synIdentifier">{</span> <span class="synStatement">super(</span>scope<span class="synStatement">,</span> id<span class="synStatement">,</span> props<span class="synStatement">);</span> <span class="synType">const</span> queue <span class="synStatement">=</span> <span class="synStatement">new</span> aws_sqs.Queue<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkQueue'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> queueName: <span class="synConstant">'sandbox-cdk-sqs-pipes-ecs-queue'</span><span class="synStatement">,</span> visibilityTimeout: Duration.seconds<span class="synStatement">(</span><span class="synConstant">30</span><span class="synStatement">),</span> receiveMessageWaitTime: Duration.seconds<span class="synStatement">(</span><span class="synConstant">20</span><span class="synStatement">),</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> lambda <span class="synStatement">=</span> aws_lambda.<span class="synSpecial">Function</span>.fromFunctionArn<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkLambdaFunctionHello'</span><span class="synStatement">,</span> <span class="synConstant">'arn:aws:lambda:ap-northeast-1:111111111111:function:xxx'</span><span class="synStatement">);</span> <span class="synType">const</span> vpc <span class="synStatement">=</span> aws_ec2.Vpc.fromLookup<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkVpc'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> vpcId: <span class="synConstant">'vpc-00000000000000000'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> ecsCluster <span class="synStatement">=</span> aws_ecs.Cluster.fromClusterArn<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkEcsCluster'</span><span class="synStatement">,</span> <span class="synConstant">'arn:aws:ecs:ap-northeast-1:111111111111:cluster/xxx'</span><span class="synStatement">);</span> <span class="synType">const</span> ecsTaskDefinition <span class="synStatement">=</span> <span class="synStatement">new</span> aws_ecs.FargateTaskDefinition<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkEcsTaskDefinition'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> cpu: <span class="synConstant">256</span><span class="synStatement">,</span> memoryLimitMiB: <span class="synConstant">512</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> logGroup <span class="synStatement">=</span> <span class="synStatement">new</span> aws_logs.LogGroup<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkLogGroup'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> logGroupName: <span class="synConstant">'sandbox-cdk-sqs-pipes-ecs-log-group'</span><span class="synStatement">,</span> retention: aws_logs.RetentionDays.ONE_WEEK<span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> ecsTaskDefinition.addContainer<span class="synStatement">(</span><span class="synConstant">'defaultContainer'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> image: aws_ecs.ContainerImage.fromRegistry<span class="synStatement">(</span><span class="synConstant">'hello-world'</span><span class="synStatement">),</span> logging: aws_ecs.LogDriver.awsLogs<span class="synStatement">(</span><span class="synIdentifier">{</span> logGroup: logGroup<span class="synStatement">,</span> streamPrefix: <span class="synConstant">'/hello-world/'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synType">const</span> pipeRole <span class="synStatement">=</span> <span class="synStatement">new</span> aws_iam.Role<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkPipesRole'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> roleName: <span class="synConstant">'sandbox-cdk-sqs-pipes-ecs-role'</span><span class="synStatement">,</span> assumedBy: <span class="synStatement">new</span> aws_iam.ServicePrincipal<span class="synStatement">(</span><span class="synConstant">'pipes.amazonaws.com'</span><span class="synStatement">)</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> queue.grantConsumeMessages<span class="synStatement">(</span>pipeRole<span class="synStatement">)</span> ecsTaskDefinition.grantRun<span class="synStatement">(</span>pipeRole<span class="synStatement">)</span> lambda.grantInvoke<span class="synStatement">(</span>pipeRole<span class="synStatement">)</span> <span class="synStatement">new</span> aws_pipes.CfnPipe<span class="synStatement">(</span><span class="synIdentifier">this</span><span class="synStatement">,</span> <span class="synConstant">'SandboxCdkPipes'</span><span class="synStatement">,</span> <span class="synIdentifier">{</span> name: <span class="synConstant">'sandbox-cdk-sqs-pipes-ecs-pipes'</span><span class="synStatement">,</span> roleArn: pipeRole.roleArn<span class="synStatement">,</span> source: queue.queueArn<span class="synStatement">,</span> enrichment: lambda.functionArn<span class="synStatement">,</span> target: ecsCluster.clusterArn<span class="synStatement">,</span> targetParameters: <span class="synIdentifier">{</span> ecsTaskParameters: <span class="synIdentifier">{</span> launchType: aws_ecs.LaunchType.FARGATE<span class="synStatement">,</span> taskDefinitionArn: ecsTaskDefinition.taskDefinitionArn<span class="synStatement">,</span> taskCount: <span class="synConstant">1</span><span class="synStatement">,</span> networkConfiguration: <span class="synIdentifier">{</span> awsvpcConfiguration: <span class="synIdentifier">{</span> subnets: vpc.selectSubnets<span class="synStatement">(</span><span class="synIdentifier">{</span> subnetType: aws_ec2.SubnetType.PUBLIC <span class="synIdentifier">}</span><span class="synStatement">)</span>.subnetIds<span class="synStatement">,</span> assignPublicIp: <span class="synConstant">'ENABLED'</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span><span class="synStatement">,</span> <span class="synIdentifier">}</span><span class="synStatement">);</span> <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> </pre> <h2 id="結果">結果</h2> <p>以下のように期待通り Amazon EventBridge Pipes を設定できた❗️また動作確認として Amazon SQS キューにメッセージを送信すると,最終的に Amazon ECS タスクが実行された👌</p> <p><figure class="figure-image figure-image-fotolife" title="Amazon EventBridge Pipes 画面"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240101/20240101201753.png" width="1200" height="650" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Amazon EventBridge Pipes 画面</figcaption></figure></p> <h2 id="ポイントソースエンリッチメントターゲット">ポイント(ソース・エンリッチメント・ターゲット)</h2> <p>Amazon SQS キューを Amazon EventBridge Pipes の<strong>「ソース」</strong>に設定する場合は <code>source</code> に ARN を設定する.細かくパラメータを設定する場合は <code>SourceParameters</code> の <code>sqsQueueParameters</code> を使う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.PipeSourceSqsQueueParametersProperty.html" title="interface PipeSourceSqsQueueParametersProperty · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.PipeSourceSqsQueueParametersProperty.html">docs.aws.amazon.com</a></cite></p> <p>AWS Lambda 関数を Amazon EventBridge Pipes の<strong>「エンリッチメント(強化)」</strong>に設定する場合は <code>enrichment</code> に ARN を設定する.細かくパラメータを設定する場合は <code>enrichmentParameters</code> を使う.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.PipeEnrichmentParametersProperty.html" title="interface PipeEnrichmentParametersProperty · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.PipeEnrichmentParametersProperty.html">docs.aws.amazon.com</a></cite></p> <p>Amazon ECS タスクを Amazon EventBridge Pipes の<strong>「ターゲット」</strong>に設定する場合は <code>target</code> に ARN を設定する.細かくパラメータを設定する場合は <code>targetParameters</code> の <code>ecsTaskParameters</code> を使う.<code>ecsTaskParameters</code> の設定項目は多いけど,今回は必要最低限で <code>launchType</code> / <code>taskDefinitionArn</code> / <code>taskCount</code> / <code>networkConfiguration</code> のみ設定した.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_pipes.CfnPipe.PipeTargetEcsTaskParametersProperty.html" title="interface PipeTargetEcsTaskParametersProperty · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pipes.CfnPipe.PipeTargetEcsTaskParametersProperty.html">docs.aws.amazon.com</a></cite></p> <h2 id="ポイント実行ロール">ポイント(実行ロール)</h2> <p>Amazon EventBridge Pipes の実行ロールに Amazon SQS キューの読み取り権限・AWS Lambda 関数の実行権限・Amazon ECS タスクの実行権限を与える必要がある.最小権限を考えるとポリシーの設定が大変だけど <code>grantConsumeMessages(grantee)</code> / <code>grantRun(grantee)</code> / <code>grantInvoke(grantee)</code> を使えば簡単に設定できる.AWS CDK の <code>grant</code> は本当に便利だ〜💡</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_sqs.Queue.html" title="class Queue (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_sqs.Queue.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_ecs.FargateTaskDefinition.html" title="class FargateTaskDefinition (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs.FargateTaskDefinition.html">docs.aws.amazon.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_lambda.Function.html" title="class Function (construct) · AWS CDK" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html">docs.aws.amazon.com</a></cite></p> kakku22 朝活 振り返り(2023年) hatenablog://entry/6801883189072940483 2024-01-08T10:50:17+09:00 2024-01-08T13:33:02+09:00 2023年2月から「朝活」を習慣化できるように取り組んでいて,2023年の年末まで10ヶ月半ほど続けられたので簡単に振り返っておく🌅 本当に朝活をはじめて良かったと思うし,2023年のインプット・アウトプットは朝活に支えられていたと言えるほどでもある❗️朝活に興味がある人の参考になれば〜 背景 もともと僕自身は夜型で,朝は全然起きれないけど夜はいくらでも起きてられるという感じだった.とは言え「朝活への憧れ」は昔からあって,今まで何度も朝活に挑戦したけど続かずに挫折を繰り返していた.それでも日々忙しく,時間の捻出ができないことを言い訳にしたくなく「誰にも邪魔されない時間」を確保するべくまた朝活に… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240107/20240107153213.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> 2023年2月から<strong>「朝活」</strong>を習慣化できるように取り組んでいて,2023年の年末まで10ヶ月半ほど続けられたので簡単に振り返っておく🌅 本当に朝活をはじめて良かったと思うし,2023年のインプット・アウトプットは朝活に支えられていたと言えるほどでもある❗️朝活に興味がある人の参考になれば〜</p> <h1 id="背景">背景</h1> <p>もともと僕自身は夜型で,朝は全然起きれないけど夜はいくらでも起きてられるという感じだった.とは言え<strong>「朝活への憧れ」</strong>は昔からあって,今まで何度も朝活に挑戦したけど続かずに挫折を繰り返していた.それでも日々忙しく,時間の捻出ができないことを言い訳にしたくなく<strong>「誰にも邪魔されない時間」</strong>を確保するべくまた朝活に挑戦することにした💪もっと詳しく書くと,ちょうど2023年2月から退職前の有給消化をしていて,心にゆとりがあったので始めやすい時期でもあった.</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kakku22/20240107/20240107093353.png" width="400" height="370" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1 id="朝活とは">朝活とは</h1> <p>僕自身が取り組んでいた朝活を雑に定義すると<strong>「技術的なインプットに集中する時間」</strong>という感じ.例えば以下など.逆にテックブログを書いたりするような執筆系のアウトプットや仕事は朝活ではやらないと決めていた🚫</p> <ul> <li>コードを書く</li> <li>ワークショップ・チュートリアルを実施する</li> <li>技術書を読む</li> <li>資格の勉強をする</li> <li>AWS のアップデートを確認する</li> <li>etc</li> </ul> <p>また時間やノルマを完璧に決めてしまうと,最初はモチベーションも高く続けられるけど,1度途切れてしまうと戻りにくくなってしまうため<strong>「イレギュラーを受け入れて」</strong>無理はしないと決めていた.個人的な朝活ルールは以下のような感じ.</p> <ul> <li>朝活開始時間は朝6時〜朝9時の枠で自由に決めて OK</li> <li>朝活時間は最低1ポモドーロで OK</li> <li>朝活をしない日があっても OK(飲み会の翌日・深夜の障害対応をした日・旅行中など)</li> </ul> <h1 id="個人的な朝活-Tips">個人的な朝活 Tips</h1> <p>朝活を習慣化するために個人的に意識したことを順不同で紹介する❗️紹介する朝活関連本に影響された部分も多くある.</p> <ul> <li>朝活前に X にポストする🐤</li> <li>朝に飲みたいな〜と思えるコーヒーを買っておく☕</li> <li>朝活で取り組みたいな〜と思えるワクワクする技術的なテーマを選ぶ💎</li> <li>ポモドーロタイマーで気持ちを切り替える⌛</li> <li>音楽は聞かずに耳栓を使って雑音をシャットアウトする👂(3M の耳栓を使ってておすすめ)</li> <li>自宅だと気分が乗らないと感じたらすぐにカフェに移動する☕(近くに朝7時から営業してるスタバとドトールがある)</li> <li>朝活を終了したら習慣化用の Google Sheet に◯を付ける✅</li> <li>定期的に朝活関連本を読んでモチベーションを保つ📕</li> <li>毎日遅くても24時までには寝る😪</li> <li>寝る前にダラダラとスマホを見ず寝ることに集中する📱</li> </ul> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B07XRJBSNN?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/318LKx1+UKL._SL500_.jpg" class="hatena-asin-detail-image" alt="3M(スリーエム) E-A-Rsoft Yellow Neons Made in U.S.A 【イヤピース・遮音性耳栓】 (型番1250) (5)" title="3M(スリーエム) E-A-Rsoft Yellow Neons Made in U.S.A 【イヤピース・遮音性耳栓】 (型番1250) (5)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B07XRJBSNN?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">3M(スリーエム) E-A-Rsoft Yellow Neons Made in U.S.A 【イヤピース・遮音性耳栓】 (型番1250) (5)</a></p><ul class="hatena-asin-detail-meta"><li>スリーエム(3M)</li></ul><a href="https://www.amazon.co.jp/dp/B07XRJBSNN?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h1 id="朝活-Stats">朝活 Stats</h1> <p>朝活をはじめたのは <code>2023年2月17日 (金)</code> で <code>2023年12月31日 (日)</code> までだと計318日となる.Google Sheet の朝活記録を見ると<strong>「計223日」</strong>だったので,期間中の 70% ぐらいは朝活に取り組めていた💡</p> <h1 id="朝活で取り組んだこと">朝活で取り組んだこと</h1> <p>習慣化ログをサマリーして取り組んだことをまとめてみた❗️細かくは他にもあるけど朝活でこんなに取り組めて本当に良かったな〜と思う \( 'ω')/</p> <ul> <li><strong>Go 関連</strong> <ul> <li><a href="https://andmorefine.gitbook.io/learn-go-with-tests/">&#x30C6;&#x30B9;&#x30C8;&#x99C6;&#x52D5;&#x958B;&#x767A;&#x3067;GO&#x8A00;&#x8A9E;&#x3092;&#x5B66;&#x3073;&#x307E;&#x3057;&#x3087;&#x3046; - &#x30C6;&#x30B9;&#x30C8;&#x99C6;&#x52D5;&#x958B;&#x767A;&#x3067;GO&#x8A00;&#x8A9E;&#x3092;&#x5B66;&#x3073;&#x307E;&#x3057;&#x3087;&#x3046;</a><a href="https://b.hatena.ne.jp/entry/https://andmorefine.gitbook.io/learn-go-with-tests/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://andmorefine.gitbook.io/learn-go-with-tests/" alt="" class="http-bookmark" /></a></li> <li><a href="https://gophercises.com/">Gophercises - Coding exercises for budding gophers</a><a href="https://b.hatena.ne.jp/entry/https://gophercises.com/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://gophercises.com/" alt="" class="http-bookmark" /></a></li> </ul> </li> <li><strong>AWS 関連</strong> <ul> <li><a href="https://hashicorp-terraform.awsworkshop.io/">AWS Modernization Workshop :: MP DevOps Series</a><a href="https://b.hatena.ne.jp/entry/https://hashicorp-terraform.awsworkshop.io/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://hashicorp-terraform.awsworkshop.io/" alt="" class="http-bookmark" /></a></li> <li><a href="https://catalog.us-east-1.prod.workshops.aws/workshops/fa518794-acd4-4178-80d3-97fa62f9deff/en-US">Code Quality Workshop</a><a href="https://b.hatena.ne.jp/entry/https://catalog.us-east-1.prod.workshops.aws/workshops/fa518794-acd4-4178-80d3-97fa62f9deff/en-US" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://catalog.us-east-1.prod.workshops.aws/workshops/fa518794-acd4-4178-80d3-97fa62f9deff/en-US" alt="" class="http-bookmark" /></a></li> <li><a href="https://www.udemy.com/course/aws-certified-security-specialty-practice-exams-course/">AWS Certified Security Specialty Practice Exams SCS-C02 2023 | Udemy</a><a href="https://b.hatena.ne.jp/entry/https://www.udemy.com/course/aws-certified-security-specialty-practice-exams-course/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.udemy.com/course/aws-certified-security-specialty-practice-exams-course/" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/12/24/165737">AWS CDK &#x3067; &#x30ED;&#x30B0;&#x30AF;&#x30E9;&#x30B9;&#xFF08;&#x4F4E;&#x983B;&#x5EA6;&#x30A2;&#x30AF;&#x30BB;&#x30B9;&#xFF09;&#x306E; Amazon CloudWatch Logs &#x30ED;&#x30B0;&#x30B0;&#x30EB;&#x30FC;&#x30D7;&#x3092;&#x8FFD;&#x52A0;&#x3059;&#x308B; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/12/24/165737" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/12/24/165737" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/12/25/133925">AWS CDK &#x3067; AWS Lambda &#x95A2;&#x6570;&#x306E;&#x9AD8;&#x5EA6;&#x306A;&#x30ED;&#x30B0;&#x5236;&#x5FA1;&#x6A5F;&#x80FD;&#xFF08;&#x30D5;&#x30A9;&#x30FC;&#x30DE;&#x30C3;&#x30C8;&#x30FB;&#x30ED;&#x30B0;&#x30EC;&#x30D9;&#x30EB;&#x30FB;&#x30ED;&#x30B0;&#x96C6;&#x7D04;&#xFF09;&#x3092;&#x8A2D;&#x5B9A;&#x3059;&#x308B; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/12/25/133925" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/12/25/133925" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/12/26/231844">AWS CDK &#x3067; NLB &#x306B;&#x30BB;&#x30AD;&#x30E5;&#x30EA;&#x30C6;&#x30A3;&#x30B0;&#x30EB;&#x30FC;&#x30D7;&#x3092;&#x8A2D;&#x5B9A;&#x3059;&#x308B; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/12/26/231844" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/12/26/231844" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/12/27/183433">AWS CDK &#x306E;&#x30A2;&#x30EB;&#x30D5;&#x30A1;&#x30E2;&#x30B8;&#x30E5;&#x30FC;&#x30EB;&#x3067; Amazon EventBridge Scheduler &#x3092;&#x8A2D;&#x5B9A;&#x3059;&#x308B; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/12/27/183433" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/12/27/183433" alt="" class="http-bookmark" /></a></li> </ul> </li> <li><strong>Terraform 関連</strong> <ul> <li><a href="https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework">Custom Framework Providers | Terraform | HashiCorp Developer</a><a href="https://b.hatena.ne.jp/entry/https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework" alt="" class="http-bookmark" /></a></li> <li><a href="https://developer.hashicorp.com/terraform/tutorials/state/state-import">Import Terraform configuration | Terraform | HashiCorp Developer</a><a href="https://b.hatena.ne.jp/entry/https://developer.hashicorp.com/terraform/tutorials/state/state-import" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://developer.hashicorp.com/terraform/tutorials/state/state-import" alt="" class="http-bookmark" /></a></li> <li><a href="https://developer.hashicorp.com/terraform/tutorials/aws/aws-cloud-control">Manage new AWS resources with the Cloud Control provider | Terraform | HashiCorp Developer</a><a href="https://b.hatena.ne.jp/entry/https://developer.hashicorp.com/terraform/tutorials/aws/aws-cloud-control" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://developer.hashicorp.com/terraform/tutorials/aws/aws-cloud-control" alt="" class="http-bookmark" /></a></li> <li><a href="https://developer.hashicorp.com/terraform/tutorials/configuration-language/test">Write Terraform Tests | Terraform | HashiCorp Developer</a><a href="https://b.hatena.ne.jp/entry/https://developer.hashicorp.com/terraform/tutorials/configuration-language/test" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://developer.hashicorp.com/terraform/tutorials/configuration-language/test" alt="" class="http-bookmark" /></a></li> <li><a href="https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials-003">Associate Tutorials | Terraform | HashiCorp Developer</a><a href="https://b.hatena.ne.jp/entry/https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials-003" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials-003" alt="" class="http-bookmark" /></a></li> <li><a href="https://www.udemy.com/course/terraform-associate-practice-exam/">HashiCorp Certified: Terraform Associate Practice Exam 2023 | Udemy</a><a href="https://b.hatena.ne.jp/entry/https://www.udemy.com/course/terraform-associate-practice-exam/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://www.udemy.com/course/terraform-associate-practice-exam/" alt="" class="http-bookmark" /></a></li> </ul> </li> <li><strong>Hasura 関連</strong> <ul> <li><a href="https://hasura.io/learn/graphql/backend-stack/languages/python/">GraphQL Server with Python | Backend Tutorial</a><a href="https://b.hatena.ne.jp/entry/https://hasura.io/learn/graphql/backend-stack/languages/python/" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://hasura.io/learn/graphql/backend-stack/languages/python/" alt="" class="http-bookmark" /></a></li> </ul> </li> <li><strong>技術書</strong> <ul> <li><a href="https://www.amazon.co.jp/dp/429607055X?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">スタッフエンジニア マネジメントを超えるリーダーシップ</a></li> <li><a href="https://www.amazon.co.jp/dp/480059121X?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">LOVED 市場を形づくり製品を定着に導くプロダクトマーケティング</a></li> <li><a href="https://www.amazon.co.jp/dp/4873117917?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム</a></li> <li><a href="https://www.amazon.co.jp/dp/4814400349?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">SLO サービスレベル目標 ―SLI、SLO、エラーバジェット導入の実践ガイド</a></li> <li><a href="https://www.amazon.co.jp/dp/4873119847?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">システム運用アンチパターン ―エンジニアがDevOpsで解決する組織・自動化・コミュニケーション</a></li> <li><a href="https://www.amazon.co.jp/dp/4297138395?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">ChatGPT/LangChainによるチャットシステム構築[実践]入門</a></li> <li><a href="https://www.amazon.co.jp/dp/4910313036?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1">AWSではじめるクラウドセキュリティ: クラウドで学ぶセキュリティ設計/実装</a></li> </ul> </li> </ul> <p>また朝活でインプットした内容を別途テックブログにまとめてアウトプットすることも意識していた💡</p> <ul> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/03/21/105935">Learn Go with Tests: &#x30C6;&#x30B9;&#x30C8;&#x99C6;&#x52D5;&#x958B;&#x767A;&#x3092;&#x4F53;&#x9A13;&#x3057;&#x306A;&#x304C;&#x3089; Go &#x3092;&#x5B66;&#x307C;&#x3046; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/03/21/105935" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/03/21/105935" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/06/14/090244">Gophercises: &#x5B9F;&#x8DF5;&#x7684;&#x306A;20&#x7A2E;&#x985E;&#x306E;&#x30A8;&#x30AF;&#x30B5;&#x30B5;&#x30A4;&#x30BA;&#x3067; Go &#x3092;&#x5B66;&#x307C;&#x3046; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/06/14/090244" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/06/14/090244" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/05/25/105821">Terraform Cloud x AWS &#x306B;&#x5165;&#x9580;&#x3067;&#x304D;&#x308B;&#x300C;AWS Modernization Workshop with HashiCorp Terraform Cloud&#x300D; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/05/25/105821" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/05/25/105821" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/11/22/203807">Amazon CodeGuru Reviewer &#x3068; Amazon CodeGuru Profiler &#x306B;&#x5165;&#x9580;&#x3057;&#x3088;&#x3046;&#xFF01;&#x300C;Code Quality Workshop&#x300D;&#x3092;&#x8A66;&#x3057;&#x305F; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/11/22/203807" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/11/22/203807" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/07/27/214349">&#x3069;&#x306E;&#x3088;&#x3046;&#x306A;&#x30AD;&#x30E3;&#x30EA;&#x30A2;&#x30D1;&#x30B9;&#x3067;&#x7D44;&#x7E54;&#x306B;&#x8CA2;&#x732E;&#x3059;&#x308B;&#x304B; /&#x300C;&#x30B9;&#x30BF;&#x30C3;&#x30D5;&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/07/27/214349" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/07/27/214349" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/08/07/140332">&#x306A;&#x305C;&#x30D7;&#x30ED;&#x30C0;&#x30AF;&#x30C8;&#x30DE;&#x30FC;&#x30B1;&#x30C6;&#x30A3;&#x30F3;&#x30B0;&#x304C;&#x91CD;&#x8981;&#x306A;&#x306E;&#x304B; /&#x300C;LOVED&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/08/07/140332" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/08/07/140332" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/09/04/093032">&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x306E;&#x4FE1;&#x983C;&#x6027;&#x306B;&#x7126;&#x70B9;&#x3092;&#x7F6E;&#x304F; SRE /&#x300C;SRE &#x30B5;&#x30A4;&#x30C8;&#x30EA;&#x30E9;&#x30A4;&#x30A2;&#x30D3;&#x30EA;&#x30C6;&#x30A3;&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x30EA;&#x30F3;&#x30B0;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/09/04/093032" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/09/04/093032" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/09/19/085605">SLO &#x3092;&#x30D9;&#x30FC;&#x30B9;&#x3068;&#x3057;&#x305F;&#x30A2;&#x30D7;&#x30ED;&#x30FC;&#x30C1;&#x3092;&#x3069;&#x3046;&#x7D44;&#x7E54;&#x306E;&#x6587;&#x5316;&#x306B;&#x3059;&#x308B;&#x306E;&#x304B; /&#x300C;SLO &#x30B5;&#x30FC;&#x30D3;&#x30B9;&#x30EC;&#x30D9;&#x30EB;&#x76EE;&#x6A19;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/09/19/085605" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/09/19/085605" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/10/30/090954">&quot;&#x4F55;&#x304B;&#x3046;&#x307E;&#x304F;&#x3044;&#x3063;&#x3066;&#x306A;&#x3044;&quot; &#x958B;&#x767A;&#x7D44;&#x7E54;&#x3067;&#x3088;&#x304F;&#x898B;&#x308B;&#x5149;&#x666F;&#x3068;&#x6253;&#x958B;&#x7B56;&#x304C;&#x307E;&#x3068;&#x307E;&#x3063;&#x305F;&#x300C;&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x904B;&#x7528;&#x30A2;&#x30F3;&#x30C1;&#x30D1;&#x30BF;&#x30FC;&#x30F3;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/10/30/090954" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/10/30/090954" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/10/16/085525">LLM &#x3092;&#x7D44;&#x307F;&#x8FBC;&#x3093;&#x3060;&#x30C1;&#x30E3;&#x30C3;&#x30C8;&#x30A2;&#x30D7;&#x30EA;&#x30B1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x3092;&#x5199;&#x7D4C;&#x3057;&#x306A;&#x304C;&#x3089;&#x5B9F;&#x88C5;&#x3067;&#x304D;&#x308B;&#x300C;ChatGPT/LangChain &#x306B;&#x3088;&#x308B;&#x30C1;&#x30E3;&#x30C3;&#x30C8;&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x69CB;&#x7BC9;&#xFF3B;&#x5B9F;&#x8DF5;&#xFF3D;&#x5165;&#x9580;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/10/16/085525" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/10/16/085525" alt="" class="http-bookmark" /></a></li> <li><a href="https://kakakakakku.hatenablog.com/entry/2023/11/07/095124">AWS x &#x30BB;&#x30AD;&#x30E5;&#x30EA;&#x30C6;&#x30A3;&#x306B;&#x5165;&#x9580;&#x3059;&#x308B;&#x306A;&#x3089;&#x307E;&#x305A;&#x3053;&#x306E;&#x4E00;&#x518A; /&#x300C;AWS &#x3067;&#x306F;&#x3058;&#x3081;&#x308B;&#x30AF;&#x30E9;&#x30A6;&#x30C9;&#x30BB;&#x30AD;&#x30E5;&#x30EA;&#x30C6;&#x30A3;&#x300D;&#x3092;&#x8AAD;&#x3093;&#x3060; - kakakakakku blog</a><a href="https://b.hatena.ne.jp/entry/https://kakakakakku.hatenablog.com/entry/2023/11/07/095124" class="http-bookmark"><img src="https://b.hatena.ne.jp/entry/image/https://kakakakakku.hatenablog.com/entry/2023/11/07/095124" alt="" class="http-bookmark" /></a></li> </ul> <h1 id="読んだ本">読んだ本</h1> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4799107771?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51z+-gbDPiL._SL500_.jpg" class="hatena-asin-detail-image" alt="頭が冴える! 毎日が充実する! スゴい早起き" title="頭が冴える! 毎日が充実する! スゴい早起き"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4799107771?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">頭が冴える! 毎日が充実する! スゴい早起き</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%C4%CD%CB%DC%20%CE%BC" class="keyword">塚本 亮</a></li><li>すばる舎</li></ul><a href="https://www.amazon.co.jp/dp/4799107771?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4866801697?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51zgqTbK06L._SL500_.jpg" class="hatena-asin-detail-image" alt="働くあなたの快眠地図" title="働くあなたの快眠地図"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4866801697?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">働くあなたの快眠地図</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%B3%D1%C3%AB%20%A5%EA%A5%E7%A5%A6" class="keyword">角谷 リョウ</a></li><li>フォレスト出版</li></ul><a href="https://www.amazon.co.jp/dp/4866801697?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4862808778?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51pCtMTHoJL._SL500_.jpg" class="hatena-asin-detail-image" alt="僕たちに残されている時間は「朝」しかない。" title="僕たちに残されている時間は「朝」しかない。"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4862808778?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">僕たちに残されている時間は「朝」しかない。</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%C0%D0%C0%EE%20%CF%C2%C3%CB" class="keyword">石川 和男</a></li><li>総合法令出版</li></ul><a href="https://www.amazon.co.jp/dp/4862808778?tag=kakakakakku-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h1 id="2024年は">2024年は?</h1> <p><strong>「誰にも邪魔されない時間」</strong>として,朝活に取り組んでいなかったら2023年にここまで充実したインプット・アウトプットができていなかったことは間違いなくて,本当に取り組んで良かったと思う.朝活最高❗️2024年もこのまま続けていくぞ〜 \( 'ω')/</p> <p>ちなみに今までは Google Sheet を使って習慣化の記録をしてたけど,実は前から気になってたサービス<strong>「Habitify」</strong>を2024年になったタイミングから導入していて最高に便利👌無料でも十分に使えるけど,Google Sheet から移行するためには多くの習慣化を登録したく,さっそく<strong>「プレミアム版」</strong>を買って使い始めたところ❗️Habitify おすすめでーす.</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.habitify.me%2F" title="Habitify: Personalized Habit Tracker App | Build Better Habits Today" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://www.habitify.me/">www.habitify.me</a></cite></p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">まずは朝活記録から Habitify に移行したけどこんな感じ❗元旦から今日まで毎日朝活が続いてるのは Habitify のおかげもあるかも〜 <a href="https://t.co/pbuHALnZYb">pic.twitter.com/pbuHALnZYb</a></p>&mdash; カック (@kakakakakku) <a href="https://twitter.com/kakakakakku/status/1743881233191625033?ref_src=twsrc%5Etfw">2024年1月7日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> kakku22