ECS Fargate のメトリクスを Grafana の画面で確認している様子

📔 ECS Fargate のメトリクスを Prometheus Agent 使って AMP に送って Grafana で監視する

はじめに この記事は AWS Advent Calendar 2021 の 5 日目の記事です。 Fargate で Node.js アプリのメトリクスを Prometheus Agent をサイドカーコンテナとして動かして、Amazon Managed Service for Prometheus (AMP) に送信して Grafana で見られるようにしてみました。 ちなみに Promethus Agent はまだ 実験的な機能 なため、実務での利用は推奨しません。 本記事の環境構築には AWS CDK を利用しています。 動作環境 Node.js v16.13.0 AWS CDK 2.0.0 (build 4b6ce31) Prometheus 2.32.1 環境構築 早速環境構築を進めていきます。まだ AMP については CDK から操作できないようでしたので、ワークスペースの作成については AWS コンソールから手動で行います。(2021/12/06) aws-aps を利用することで AWS CDK からでも Amazon Managed Service for Prometheus のワークスペースを作成すること確認できましたので、そちらの利用を推奨いたします… 🙇🙇 lib/prometheus-agent-test-stack.ts のコードも修正済みで AWS CDK で Amazon Managed Service for Prometheus のワークスペースを作成するように編集しました。(2021/12/18 追記) ...

12月 5, 2021 · 6 分 · Me

📔 AWS Lightsail Containers に Actix web をデプロイする

はじめに Actix web で Web アプリケーションを作ったのですが、技術勉強も兼ねていたので、デプロイ先も今まで試したことがないものを試そうとしていました。そこで、日頃業務でも AWS を利用しているということもあり、去年末に発表された AWS Lightsail Containers をデプロイ先に採用しました。 AWS Lightsail Containers へのデプロイ自体は非常に簡単でした。また、デプロイにあたり Rust の Docker イメージ作成のやり方も学べました。今回はそのあたりの手順をまとめる形で記事として書き残しておくことにしました。 Actix web の Docker イメージを作成する 開発したアプリケーションでは React でフロントエンド開発をしていて、ビルドしたものを Actix web の public フォルダに配置する形で公開しています。そのため、下記の Dockerfile ではマルチステージビルドを利用しておりますが、本質的には FROM rust:1.49 以降の記述が Actix web に関するものとなります。 # React ビルド用のイメージ FROM node:14.15.4-alpine3.10 as client_builder ARG REACT_APP_API_URL ARG REACT_APP_GYAZO_AUTH_URL ARG REACT_APP_GA_UNIVERSAL_ID WORKDIR /client COPY ./client/package*.json . RUN yarn install ADD ./client . RUN yarn build # Actix web ビルド用のイメージ FROM rust:1.49 # Actix web にアクセスするためのポートを公開する EXPOSE 8080 # Actix web プロジェクトのフォルダをイメージに追加する WORKDIR /server ADD ./server . # プロジェクトフォルダ内で `cargo install` してビルドを生成する RUN cargo install --path . # 不要になったファイル群を削除する RUN ls | grep -v -E 'templates' | xargs rm -r # React ビルド用のイメージでビルドした内容を Actix web ビルド用イメージに追加する COPY --from=client_builder /client/build ./build RUN mkdir tmp # `cargo install` コマンドで生成したビルドを実行して Actix web を起動する # 下記のコマンド名称は Cargo.toml 内の [package.name] に準ずる CMD ["bloggimg-server"] また、Docker ビルド時のオプション管理を楽にするため、Docker Compose を利用しました。単一の Docker イメージをビルドする際にも利用しておくことで、後々コンテナを追加して連携させたいときにも即座に対応できたりでオススメです。 ...

1月 23, 2021 · 3 分 · Me

📔 MediaPackage 用の CloudFront ディストリビューションを AWS SDK で作成する

はじめに とある事情で MediaPackage のエンドポイント用の CloudFront ディストリビューションを AWS SDK で作成する機会がありました。その際得た知見をソースコードを交えながら備忘録として記事に残しておきます。 本記事内容で紹介しているソースコードは Gist にも同じ内容でアップしてあります。 ちなみに MediaLive + MediaPackage + CloudFront の構成でインフラ構築したい場合は、CloudFormation が MediaPackage にも対応したので CloudFormation の利用を推奨します。 本記事内容はあくまでも何らかの事情で、後から CloudFront ディストリビューションを MediaPackage エンドポイントに紐づけたいケース等で参考になると思われます。 実装内容 作成したソースコードの内容は下記になります。 最下部の createDistributionForMediaPackage が本記事タイトルに該当する関数です。 import { CloudFront } from "aws-sdk"; import * as url from "url"; import { CreateDistributionWithTagsResult, GetDistributionResult, UpdateDistributionResult } from "aws-sdk/clients/cloudfront"; export class CloudFrontClientForMediaPackage { private cloudFront: CloudFront; constructor() { this.cloudFront = new CloudFront({ region: "ap-northeast-1", apiVersion: '2020-05-31', }); } /** * CloudFront ディストリビューションの情報を取得するために利用する * @param id CloudFront ディストリビューションの ID * @return ディストリビューションの情報を取得する */ async getDistribution(id: string): Promise<GetDistributionResult> { const distribution = await this.cloudFront.getDistribution({ Id: id }).promise() return distribution; } /** * CloudFront ディストリビューションの設定内容を取得するために利用する * @param id CloudFront ディストリビューションの ID * @return ディストリビューションの設定内容を取得する */ async getDistributionConfig(id: string): Promise<CloudFront.DistributionConfig> { const config = await this.cloudFront.getDistributionConfig({ Id: id }).promise() return config.DistributionConfig; } /** * CloudFront ディストリビューションを削除する * @param id 削除したい CloudFront ディストリビューションの ID */ async deleteDistribution(id: string) { const distribution = await this.getDistribution(id); await this.cloudFront.deleteDistribution({ Id: id, IfMatch: distribution.ETag }).promise() } /** * CloudFront ディストリビューションを無効化する * @param id 無効化したい CloudFront ディストリビューションの ID * @return 無効化した CloudFront ディストリビューションの情報 */ async disableDistribution(id: string): Promise<UpdateDistributionResult> { const distribution = await this.getDistribution(id); const config = distribution.Distribution.DistributionConfig; config.Enabled = false; return await this.cloudFront.updateDistribution({ Id: id, IfMatch: distribution.ETag, DistributionConfig: config }).promise(); } /** * MediaPackage のエンドポイント用の CloudFront ディストリビューションを作成する * @param id CloudFront ディストリビューションを判別するための ID * @param mediaPackageArn MediaPackage チャンネルの ARN * @param mediaPackageUrl MediaPackage エンドポイントの URL */ async createDistributionForMediaPackage( id: string, mediaPackageArn: string, mediaPackageUrl: string ): Promise<CreateDistributionWithTagsResult> { // 1. url モジュールを用いて URL 文字列をパースする const mediaPackageEndpoint = url.parse(mediaPackageUrl); /** 2. MediaPackage のエンドポイント URL から FQDN を取得する。 後述する CloudFront ディストリビューションのオリジンのドメイン名としても利用する */ const mediaPackageHostname = mediaPackageEndpoint.hostname; /** 3. MediaPackage のエンドポイント URL のフォーマットは https://<AccountID>.mediapackage.<Region>.amazonaws.com/**** となっているので、 FQDN の先頭部分を文字列分割で取り出すとアカウント ID が取得できる */ const accountId = mediaPackageHostname.split('.')[0]; // 4. 後述する CloudFront ディストリビューションのオリジン ID として、アカウント ID を利用する const targetOriginId = `MP-${accountId}` /** 5. createDistribution ではなく、createDistributionWithTags 関数で、 CloudFront ディストリビューションを作成する。MediaPackage との紐付けにタグを利用するため。 */ return await this.cloudFront.createDistributionWithTags({ DistributionConfigWithTags: { Tags: { Items: [ /** !!!!!重要!!!!! 6. CloudFront ディストリビューションに紐付けたい MediaPackage エンドポイントのチャンネル ARN を mediapackage:cloudfront_assoc で定義する。 mediapackage:cloudfront_assoc を定義することで、 CloudFront ディストリビューションと MediaPackage チャンネルを紐付けることが可能となる。 */ { Key: 'mediapackage:cloudfront_assoc', Value: mediaPackageArn }, { Key: 'Id', Value: id }, { Key: 'Product', Value: 'product' }, { Key: 'Stage', Value: 'dev' } ] }, DistributionConfig: { CallerReference: new Date().toISOString(), Comment: `Managed by MediaPackage - ${id}`, Enabled: true, /** 7. CloudFront ディストリビューションのオリジンには 2つ設定します。 1つが MediaPackage のエンドポイントに対するものと、 もう 1つが MediaPacakge サービスに対するものです。 基本的には MediaPackage のエンドポイントに対するオリジンを利用します。 例外時に向けるオリジンが MediaPacakge サービスに対するものになります。 */ Origins: { Quantity: 2, Items: [ { DomainName: mediaPackageHostname, Id: targetOriginId, CustomOriginConfig: { HTTPPort: 80, HTTPSPort: 443, OriginProtocolPolicy: 'match-viewer' } }, { DomainName: 'mediapackage.amazonaws.com', Id: "TEMP_ORIGIN_ID/channel", CustomOriginConfig: { HTTPPort: 80, HTTPSPort: 443, OriginProtocolPolicy: 'match-viewer' } } ] }, /** 8. CacheBehaviors のいずれにも当てはまらなかった場合の キャッシュの振る舞いを定義します。 MediaPackage は タイムシフト表示機能を使用する際等で、クエリ文字列に start, m, end を利用しています。 そのため、それらの文字列は WhitelistedNames に含め QueryString には true を指定しておきます。 DefaultCacheBehavior に引っかかる挙動は例外的扱いなので、 使用するオリジンは MediaPackage サービスのものを設定します。 */ DefaultCacheBehavior: { ForwardedValues: { Cookies: { Forward: 'whitelist', WhitelistedNames: { Quantity: 3, Items: [ 'end', 'm', 'start' ] } }, QueryString: true, Headers: { Quantity: 0 }, QueryStringCacheKeys: { Quantity: 0 } }, MinTTL: 6, TargetOriginId: "TEMP_ORIGIN_ID/channel", TrustedSigners: { Enabled: false, Quantity: 0 }, ViewerProtocolPolicy: 'redirect-to-https', AllowedMethods: { Items: [ 'GET', 'HEAD' ], Quantity: 2, }, MaxTTL: 60 }, /** 9. CloudFront のエラーコード全ての TTL に 1sec を設定します。 MediaPackage のエラーのキャッシュが長時間持続してしまうと、 その間は MediaPackage で正常に配信できているとしても、 復旧できない状態となるからです。 */ CustomErrorResponses: { Quantity: 10, Items: [ { ErrorCode: 400, ErrorCachingMinTTL: 1 }, { ErrorCode: 403, ErrorCachingMinTTL: 1 }, { ErrorCode: 404, ErrorCachingMinTTL: 1 }, { ErrorCode: 405, ErrorCachingMinTTL: 1 }, { ErrorCode: 414, ErrorCachingMinTTL: 1 }, { ErrorCode: 416, ErrorCachingMinTTL: 1 }, { ErrorCode: 500, ErrorCachingMinTTL: 1 }, { ErrorCode: 501, ErrorCachingMinTTL: 1 }, { ErrorCode: 502, ErrorCachingMinTTL: 1 }, { ErrorCode: 503, ErrorCachingMinTTL: 1 } ] }, /** 10. CloudFront ディストリビューションのキャッシュの振る舞いを 2つ定義します。 それぞれの設定内容は基本的に DefaultCacheBehavior で定義したものと同様です。 しかし、利用するオリジンは MediaPackage エンドポイントに向けたものを利用します。 1つは Microsoft Smooth Streaming での配信時に利用する index.ism に対するもので Smooth Streaming を true に設定しています。 もう 1つは上記 Microsoft Smooth Streaming 以外の 全てに当てはまるストリーミングに適用されるものになります。 */ CacheBehaviors: { Quantity: 2, Items: [{ MinTTL: 6, PathPattern: 'index.ism/*', TargetOriginId: targetOriginId, ViewerProtocolPolicy: 'redirect-to-https', AllowedMethods: { Items: [ 'GET', 'HEAD' ], Quantity: 2, }, ForwardedValues: { Cookies: { Forward: 'whitelist', WhitelistedNames: { Quantity: 3, Items: [ 'end', 'm', 'start' ] } }, QueryString: true, Headers: { Quantity: 0 }, QueryStringCacheKeys: { Quantity: 0 }, }, SmoothStreaming: true }, { MinTTL: 6, PathPattern: '*', TargetOriginId: targetOriginId, ViewerProtocolPolicy: 'redirect-to-https', AllowedMethods: { Items: [ 'GET', 'HEAD' ], Quantity: 2, }, ForwardedValues: { Cookies: { Forward: 'whitelist', WhitelistedNames: { Quantity: 3, Items: [ 'end', 'm', 'start' ] } }, QueryString: true, Headers: { Quantity: 0 }, QueryStringCacheKeys: { Quantity: 0 }, } }] }, PriceClass: 'PriceClass_All' } } }).promise() } } createDistributionForMediaPackage で作成したディストリビューションは、公式ページに記載された手順 で作成した CloudFront ディストリビューションと同等のものになります。 ...

12月 15, 2020 · 5 分 · Me