20211201のAWSに関する記事は30件です。

i3en.metalの2ホストSDDCをデプロイしてみる(VMware Cloud on AWS)

はじめに VMware Cloud on AWSではVMworld 2021でのアップデートに伴いi3en.metalについても本番環境用途として2ホストのSDDCがサポートされるようになりました。 外部に公開されているSDDCの作成のスクリーンショットや動画はi3.metalを利用したものがほとんどの為、今回i3en.metalを利用して2ホストSDDCのデプロイを行い、いくつか確認・検証を行ってみました。 目次 VMware Cloud on AWSで利用できるインスタンスタイプ i3en.metalを利用して2ホストSDDCをデプロイしてみる vCenterからベアメタルをどう認識しているか確認 大量のVMを起動してみる まとめ 参考文献 VMware Cloud on AWSで利用できるインスタンスタイプ VMware Cloud on AWSでは2021/12/01時点で以下の2つのインスタンスタイプが利用可能です。 i3.metal i3en.metal CPUタイプ Intel Xeon E5-2686 Intel Xeon Cascade Lake CPUコア 36 Cores @ 2.3GHz 48 Cores @ 2.5GHz (ハイパースレッド有効) メモリ 512 GiB 768 GiB ストレージ vSAN with local NVMe All Flash vSAN with local NVMe All Flash キャパシティ階層のストレージサイズ ~10.7 TiB (raw) ~45.8 TiB (raw) これまでは本番用途での利用としてi3.metalは2ホストから、i3en.metalについては3ホストからの利用だったのですが、VMworldにてi3en.metalについても2ホストから利用できるようになりました。 i3en.metalを利用して2ホストSDDCをデプロイしてみる SDDCの作成の手順はi3.metalの場合と同じです。 これまでI3enを選択した場合、ホスト数が3からしか選択できなかったのですが、2から選択できるようになっています。 SDDCの展開ボタンをクリックし、100分程度でSDDCの作成が完了しました。 vCenterからベアメタルをどう認識しているか確認 ではvCenter ServerにvSphere Clientでログインをしてベアメタルをどう認識している見てみたいと思います。 以下のように構成・認識されていました。 項目 結果 ハイパーバイザー VMware ESXi 7.0.3 モデル Amazon EC2 i3en.metal-2tb プロセッサ タイプ Intel Xeon Platinum 8259CL CPU @ 2.50GHz 論理プロセッサ 96 メモリ 767.64 GB ストレージ (2ホスト合計) 91.68 TB VMware Cloud on AWSで利用できるインスタンスタイプで紹介したのとほぼ同じ値で認識していますね! 大量のVMを起動してみる i3.metalでは2ホストSDDCの場合、HAアドミッションコントロールの関係で35VMまでしか起動できないという制限があります。(参考文献のVMware Configuration Maximumsを参照) i3en.metalではこういった制限はない認識ですが実際に大量のVMが稼働できるか検証してみます。 今回はTiny Core Linuxを100VM作成して全て起動できるか確認してみました。(vm001からvm100まで名前をつけて作成しました) 以下のスクリーンショットの通り100VM全部起動できました。 スクリーンショットだとわかりにくいのでPowerCLIでの出力結果も添付しておきます。 PS /Users/mtoyoda> Get-VM Name PowerState Num CPUs MemoryGB ---- ---------- -------- -------- NSX-Edge-0 PoweredOn 4 8.000 vcenter PoweredOn 8 28.000 NSX-Manager-1 PoweredOn 6 24.000 NSX-Manager-2 PoweredOn 6 24.000 NSX-Edge-1 PoweredOn 4 8.000 NSX-Manager-0 PoweredOn 6 24.000 vm099 PoweredOn 1 1.000 vm097 PoweredOn 1 1.000 vm033 PoweredOn 1 1.000 vm070 PoweredOn 1 1.000 vm032 PoweredOn 1 1.000 vm030 PoweredOn 1 1.000 vm029 PoweredOn 1 1.000 vm026 PoweredOn 1 1.000 vm100 PoweredOn 1 1.000 vm055 PoweredOn 1 1.000 vm050 PoweredOn 1 1.000 vm060 PoweredOn 1 1.000 vm057 PoweredOn 1 1.000 vm056 PoweredOn 1 1.000 vm024 PoweredOn 1 1.000 vm001 PoweredOn 1 1.000 vm027 PoweredOn 1 1.000 vm022 PoweredOn 1 1.000 vm058 PoweredOn 1 1.000 vm002 PoweredOn 1 1.000 vm017 PoweredOn 1 1.000 vm019 PoweredOn 1 1.000 vm020 PoweredOn 1 1.000 vm061 PoweredOn 1 1.000 vm011 PoweredOn 1 1.000 vm052 PoweredOn 1 1.000 vm021 PoweredOn 1 1.000 vm072 PoweredOn 1 1.000 vm054 PoweredOn 1 1.000 vm010 PoweredOn 1 1.000 vm025 PoweredOn 1 1.000 vm008 PoweredOn 1 1.000 vm005 PoweredOn 1 1.000 vm082 PoweredOn 1 1.000 vm004 PoweredOn 1 1.000 vm081 PoweredOn 1 1.000 vm003 PoweredOn 1 1.000 vm018 PoweredOn 1 1.000 vm013 PoweredOn 1 1.000 vm016 PoweredOn 1 1.000 vm044 PoweredOn 1 1.000 vm034 PoweredOn 1 1.000 vm015 PoweredOn 1 1.000 vm062 PoweredOn 1 1.000 vm031 PoweredOn 1 1.000 vm059 PoweredOn 1 1.000 vm093 PoweredOn 1 1.000 vm014 PoweredOn 1 1.000 vm053 PoweredOn 1 1.000 vm079 PoweredOn 1 1.000 vm098 PoweredOn 1 1.000 vm087 PoweredOn 1 1.000 vm040 PoweredOn 1 1.000 vm051 PoweredOn 1 1.000 vm012 PoweredOn 1 1.000 vm023 PoweredOn 1 1.000 vm064 PoweredOn 1 1.000 vm065 PoweredOn 1 1.000 vm035 PoweredOn 1 1.000 vm066 PoweredOn 1 1.000 vm091 PoweredOn 1 1.000 vm067 PoweredOn 1 1.000 vm048 PoweredOn 1 1.000 vm037 PoweredOn 1 1.000 vm009 PoweredOn 1 1.000 vm073 PoweredOn 1 1.000 vm068 PoweredOn 1 1.000 vm046 PoweredOn 1 1.000 vm038 PoweredOn 1 1.000 vm039 PoweredOn 1 1.000 vm071 PoweredOn 1 1.000 vm088 PoweredOn 1 1.000 vm041 PoweredOn 1 1.000 vm042 PoweredOn 1 1.000 vm007 PoweredOn 1 1.000 vm080 PoweredOn 1 1.000 vm043 PoweredOn 1 1.000 vm028 PoweredOn 1 1.000 vm074 PoweredOn 1 1.000 vm049 PoweredOn 1 1.000 vm075 PoweredOn 1 1.000 vm085 PoweredOn 1 1.000 vm045 PoweredOn 1 1.000 vm069 PoweredOn 1 1.000 vm063 PoweredOn 1 1.000 vm076 PoweredOn 1 1.000 vm047 PoweredOn 1 1.000 vm077 PoweredOn 1 1.000 vm090 PoweredOn 1 1.000 vm078 PoweredOn 1 1.000 vm036 PoweredOn 1 1.000 vm084 PoweredOn 1 1.000 vm006 PoweredOn 1 1.000 vm086 PoweredOn 1 1.000 vm089 PoweredOn 1 1.000 vm083 PoweredOn 1 1.000 vm092 PoweredOn 1 1.000 vm095 PoweredOn 1 1.000 vm096 PoweredOn 1 1.000 vm094 PoweredOn 1 1.000 PS /Users/mtoyoda> まとめ i3en.metalインスタンスでも2ホストSDDCが利用できるようになり、ストレージ容量の大きい仮想マシンが多く、小規模からVMCを利用したいといったケースにおいて利用のハードルがまた少し下がりました。 そしてi3.metal 2ホストの場合制限のある35VMまでしか起動できないという制約もなく利用しやすいかと思います。 i3en.metalでのデプロイの動画やスクリーンショットはほとんどなかったと思いますので、参考になれば幸いです。 参考文献 VMware Cloud on AWS: What’s New in Oct 2021 VMware Cloud on AWS Release Notes VMware Configuration Maximums
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CDK とCloudFormation を繋ぐ話

これは CDK Advent Calendar 2021 の 1日目の記事です。 みなさんこんにちは。大村(@yktko) です。 AWS CDK の Advent Calendar の初日なのですが、ここでは、あえて CDK と AWS CloudFormation (CFn) の間を繋ぐ話をします。このエントリで CDK コードを書く人は CFn を、 CFn テンプレートを書く人は CDK を、相互の理解を深めていただくきっかけになればと思います。 CDK と CloudFormation の関係 ご存知のように、CDK は TypeScript や Python など一般の言語で AWS の環境を定義できるツールセットです。 そして、その実体は CloudFormation のテンプレートを生成するテンプレートエンジンです。デプロイメントはあくまで CloudFormation が行う。そして CDK はあくまで CFn テンプレートを書きやすくするためのツールであると割り切ってしまうと、悩むことが少なくなります。 CFn を使わずに CDK から入られた方は、「オブジェクト指向のコードでインフラ構成が書ける!」「少ないコードで AWS の環境の定義ができる!」というメリットを感じられていると思います。一方で、一般のアプリケーションコードのようにオブジェクト指向的にコードを書き、クラスを抽象化し、重複がないようコードを書いていくと、課題に突き当たることが多いのではないかと思います。たとえば、ハイレベルコンストラクト(L2 Construct)が使えないので CDK では管理できないリソースが出てきたとか、あるいは指定したいプロパティがハイレベルコンストラクトで指定できないとか、そういう状況です。 CDK は便利なツールキットなのですが、その下に存在する CloudFormation テンプレートの存在をうっすら意識することで、コードを書くときのトレードオフを判断しやすくなります。この記事ではそのための Tips をいくつか紹介したいと思います。 なお、CDK のコードの中で、SDK による API 呼び出しを直接行うコードを書くことはできますが、これは注意が必要です。本来 CDK は(CFn テンプレートという)状態を記述するためのコードですが、そのコードの中に手続きを記述することになります。この場合、リソースの変更や削除といった処理の際にも適切な手続きを記載しないと構成とコードの整合が難しくなります。CDK の Construct の中にもカスタムリソースとして Lambda ファンクションを利用しているものがありますが、これはあくまで例外として捉えるべきでしょう。 CDK が「よしなに」やってくれる部分の確認方法 CFn に慣れている人が CDK でコードこうとして一番心配になるのは、CDK が「よしなに」設定を生成してくれる部分ではないでしょうか。 良くも悪くもすべての必須パラメータを細かく明示的に指定しなければいけない CloudFormation に対して、CDK は Construct Library によって、最小限のコードで AWS のベストプラクティスに沿った設定を自動的に生成してくれます。 たとえばマルチ AZ かつパブリックとプライベートでセグメントを分ける VPC を生成する場合。 CDK ではその定義は1行ですが、 cdk-vpc-stack.ts const vpc = new ec2.Vpc(this, "MyVPC"); CloudFormation では694行のテンプレートになります。 CdkVpcStack.template.json { "Resources": { "MyVPCAFB07A31": { "Type": "AWS::EC2::VPC", "Properties": { "CidrBlock": "10.0.0.0/16", "EnableDnsHostnames": true, "EnableDnsSupport": true, "InstanceTenancy": "default", (略) コードが少なくなるのは、メンテナンス上大きなアドバンテージですが、一方で自分の意図しない設定が入ってしまうのではないかという点が気になると思います。 そういう場合は cdk synth を実行した後に作られる cdk.out ディレクトリを確認してください。 スタックごとに CFn テンプレートが生成されており、実際にデプロイされる設定がどのようになっているのかを確認できます。 JSON 形式で見にくいという方は cfn-flip を使って YAML 形式に変換するのも良いでしょう。 この CFn テンプレートはデプロイ時にエラーになる場合の原因調査にも役立ちます。cdk deploy でエラーになったとき、コマンド結果の出力や CloudFormation のマネジメントコンソールにエラーの詳細が表示されます。 デプロイ対象となるリソースの論理名は cdk synth したときに自動的に生成されますが、CDK コードから直感的にわからない場合もあります。 CFn のエラーメッセージから原因を探るにはデプロイしようとしている論理名が書かれた CFn テンプレートを見た方が、より早く原因を突き止められるでしょう。 CDK から使う CloudFormation の機能 CDK を使うときは cdk deploy でデプロイまで行えますが、そのバックエンドで動くのは基本的に CloudFormation のスタック作成処理です。 これを理解していればデプロイにまつわる処理がどのように実装されているか、どのように使うべきかを理解することができます。 最近 CDK に追加された disable rollback 機能と hotswap 機能を例にとって詳細を見てみます。 disable rollback CFn で開発を行なっていると、デプロイして発生したエラーメッセージを見てテンプレートを修正する、という試行錯誤を行うことが多くなります。CDK では type safe な言語やエディタのサジェストを使っうことで、コードを書くする段階でエラーが混入する可能性を減らすことができますが、それでも実環境に依存するエラーもあり、デプロイしないとわからなないことが多くあります。CDK よるデプロイでは、スタックのデプロイに失敗したとき、デフォルトではロールバックを実行してそのスタックのリソース全てを削除して綺麗な状態にしてくれます。これによって常にクリーンな状態でリソースを作ることができます。 しかし試行錯誤を行っているときは、このロールバックが仇になります。スタック作成の最後の段階にエラーがあるとどうでしょう ロールバックで全てのリソースが削除されるまで、ミスを混入した自分を責めながら虚無の時間を過ごすことになります。特に、作成に時間がかかる RDS などのリソースの後に、しょうもないミスが混入していると、RDS インスタンスの作成と削除の間、ずっと待ち続けることになってしまいます。辛いですね。 2021 年 10 月に CloudFormation にアップデートがあり、 スタック操作でエラーが発生した場合、正常にデプロイされたリソースのロールバックを行わないようにする機能 が追加されました。個人的には 2021 年最高のアップデートの一つだと思います。 CDK はその週のうちにこの機能を取り込み、cdk deploy に --no-rollback オプションが追加されました。これによって CDK で開発している時も、リソースの作成に失敗した場合それまでのリソースは維持され、コードを修正して作成に失敗したリソースからデプロイを再開できるようになりました。本来は 1 回のデプロイコマンドで正常にデプロイが完了する必要があります。またエラーになったときに環境をクリーンに保つ必要があるため、これを常用することはお勧めできませんが、試行錯誤の多い開発においては有用な機能だと言えます。これは、CFn の機能強化が CDK の機能強化にもつながった例と言えます。CDK で開発していたとしても、CFn のアップデートに注目が必要ですね。 Auroraを作成した後にエラーが発生して、Rollbackせずに処理が停止した時の例はこんな感じです。 hotswap disable rollback と同時期に CDK に hotswap 機能がリリース されました。これは Lambda などアプリケーションコードを含むリソースをデプロイする際に、--hotswapを指定することで迅速にアプリケーションコードを入れ替える機能です。これは先程の disable rollback と逆に、CDK が独自に追加した機能です。 本来 CDK でアプリケーションコードを入れ替える際、アプリケーションコードを変更した CFn テンプレートを生成し、Changeset を作り、CFnでデプロイします。これは CDK コードに記述した通りの構成を展開するという目的では必要なのですが、アプリケーションコードをわずかに修正するだけでも、数十秒のデプロイ時間を必要としていました。そこで、hotswap ではアプリケーションコードの入れ替えに CFn ではなく Lambda の API を直接使うことで、アプリケーションの修正デプロイを数秒で完了するようにしました。実装をみるとわかるように、 CDK の hotswap 機能は、CDKコードの設定内容とリリースしたアプリケーションコードが一致しないようになるため、厳密な構成管理が必要な本番環境に使えるものではありません。あくまで開発を迅速に行うための機能であるということに注意が必要です。 このように、 disble rollbackも、hotswapも、CDK の背後にあるデプロイメントの仕組みを理解していると、その用途を理解しやすくなります。 CloudFormation テンプレートを CDK の ローレベル(L1) Construct で作る 本日一番お勧めしたいのがこちらの内容です。 仮に CloudFormation テンプレートを作る場合であっても、CDK を使った方がよい(かもしれない)ということです。 CDK にはオブジェクト指向的にリソースを定義できるハイレベルコンストラクト(L2 Construct)と、CFn リソースと 1:1 で対応しているローレベルコンストラクト(L1 Construct)があります。 このうち、L1 Construct をもっと積極的に使ってはどうか、という考え方です。 L1 Construct は CFn のリソースがリリースされると、その定義に従って Construct Library が自動生成され、次の CDK アップデートで取り込まれます。従って、L1 Construct を使えば、CFn で定義可能な全てのリソースが CDK でも利用可能です。L1 Construct を使う際は CFn と同様に必須となる全てのパラメータを明示的に指定する必要があります。 ですが、エディタによるサジェストが強力であるため、どのプロパティが必須の指定なのか、どういった値を設定するべきなのか、といったことがコードを書きながら確認できます。 私の場合 YAML を直接書くときより、[CFn リファレンス(https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)を参照する回数は明らかに減りました。 こちらは VisualStudio Code で、SQS を L1 Construct (CfnQueue) で作った時の例です。サジェストにより指定可能なプロパティ名、必須かどうか(?の有無)、そしてプロパティの型がわかります。 AWSの環境を管理する際、CDK ではなく CFn テンプレートが必要な場合があります。たとえば CFn StackSets で同じ設定を展開したい場合などです。私は、こういう場合でも、まず CDK の L2 あるいは L1Construct を使ってベースとなるテンプレートを生成して、そこからパラメータをつけるなどの加工を行っています。 また、CDK の L2 Construct がまだ作られていないリソースがあります。たとえば AWS WAFv2 などです。 こういったものを定義するときも、躊躇なく L1 Construct を使います。 たとえば AWS Samples で公開している BLEA(Baseline Environment on AWS) ではこの部分で WAF を L1 Construct で定義しています。 blea-waf-stack.ts const webAcl = new wafv2.CfnWebACL(this, 'WebAcl', { defaultAction: { allow: {} }, name: 'BLEAWebAcl', scope: props.scope, visibilityConfig: { cloudWatchMetricsEnabled: true, metricName: 'BLEAWebAcl', sampledRequestsEnabled: true, }, rules: [ { priority: 1, そして L2 Construct で作った ALB と WAF との紐付けもこのように L1 Construct で行っています。 blea-frontend-simple-stack.ts new wafv2.CfnWebACLAssociation(this, 'WebAclAssociation', { resourceArn: lbForApp.loadBalancerArn, webAclArn: props.webAcl.attrArn, }); このように、あくまで CDK は CFn テンプレートを生成しているという考えのもと、L1 Construct を柔軟に使用して、CDK による管理を行うことを考えてみてはいかがでしょうか。 既存の CFn テンプレートを CDK で編集する 最後に、既存の CFn テンプレートが既にあるときに CDK にどのように取り組むのかを考えます。 CDK には cloudformation-include モジュールがあり、これを使って既存の CFn テンプレートの資産を活かしつつ、また既存の CFn で作ったリソースも再作成することなく、CDK による管理へ移行することができます。 こちらもBLEAでの実装例でご紹介します。 AWS ControlTower で提供している AWS ConfigRules の Detective Guardrail はCFnテンプレートがこちらで公開されています。 BLEAではこのテンプレートをそのまま使用してControlTowerと同様のガードレールを展開するよう設定していますが、CDKでこれを再実装するのではなく、cloudformation-include モジュールを使って CFn テンプレートをインポートすることで実装しています。これによってCFnテンプレートの定義をそのまま使って CDK でデプロイできます。 blea-config-ct-guardrail-stack.ts import * as cdk from '@aws-cdk/core'; import * as cfn_inc from '@aws-cdk/cloudformation-include'; export class BLEAConfigCtGuardrailStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // https://github.com/awslabs/aws-config-rules/tree/master/aws-config-conformance-packs new cfn_inc.CfnInclude(this, 'ConfigCtGr', { templateFile: 'cfn/AWS-Control-Tower-Detective-Guardrails.yaml', }); } } CfnInclude()でインポートしたテンプレートからリソースオブジェクトを参照し、L1 または L2 Construct を取得することで、対象リソースのプロパティを変更することも可能です。ただ、プロパティはコンストラクタにしか指定できないものもあり、そういったプロパティを変更する場合はテンプレート自体を変更する必要があります。このようにして、既存の CFn テンプレートを活かしたまま、CDK の管理に移行していくことが可能です。 さらに、既存のテンプレートですでにスタックを作成している場合、CDK コード内で指定するスタック名として、既存のスタック名を指定することで、以後そのスタックを CDK で管理できるようになります。一見不思議な動きをしているように見えるかもしれませんが、CDK はあくまで CFn テンプレートを生成しているだけで、その CFn テンプレートでスタックを生成していると考えると、動きが理解できると思います。 CFn からみると、既存のテンプレートと同様の、CDK によって生成されたテンプレートが新しく提示されているだけです。そのテンプレートは既存のテンプレートを include して CDK によって生成されており、メタデータ以外は変わらない内容である、ということになります。 cloudformation-includeについてはブログが公開されています。詳しくはそちらもご覧ください。 おわりに CDK と CloudFormation の関係について解説しました。いかがでしたでしょうか。 すでにCDK による管理をやっている方は一歩 DiveDeep して CFn の世界をチラ見する。 すでにCFn による管理をやっている方は一歩引いてみて CDK を使ったテンプレート作成の世界を見る。 それぞれの皆さんのお役に立てれば幸いです。 Happy Coding! & Happy Operating! 参考 AWS Blackbelt - AWS CDK AWS Blackbelt - AWS CloudFormation AWS Blackbelt - AWS CloudFormation DeepDive AWS環境にセキュアなベースラインを提供するテンプレート「Baseline Environment on AWS」のご紹介
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBアプリプログラマーがAWSクラウドプラクティショナーの資格勉強でハマったインフラ用語をご紹介

はじめに 皆さん、はじめまして。(会社のアドベントカレンダーを埋めるべく)今回初めてQiitaに投稿させて頂きます。 私はWEBアプリ開発の現場でフロントエンドもバックエンドも薄く広く2年だけ経験してきた、フルスタック(?)でロースキルな新人プログラマーです。 そんなへなちょこエンジニアの自分が一念発起してAWSクラウドプラクティショナー資格取得を目指し勉強に励んだのですが、 その際にAWSのサービス名だけではなく、一般的なインフラ用語でも詰まってしまいました…。 そんなわけで、今回は私のような新人プログラマーには耳馴染みの無かったインフラ用語を列挙して解説していこうと思います。 自分のような『開発系プログラマーだけどインフラも勉強してみたい!AWSを使ってみたい!』という方に少しでもお役に立てば幸いです。 対象読者 AWSクラウドプラクティショナー資格を取得したい人 その中でも基本的なインフラの用語が知りたい人 用語 スケールイン システムを構成する仮想マシン(≒サーバー)の台数を減らすこと。対義語は『スケールアウト』。 例えば深夜にリクエスト数が低下するようなWEBサービスを、AWSのEC2インスタンス複数台を利用して構築している場合、 深夜の時間帯には余剰なEC2インスタンスを停止することで、AWS費用の低下が期待できる。 このリソースの削減をスケールインと呼ぶ。 また、t2.largeからt2.microに変更するといったように、利用している仮想マシンのスペックを下げることは『スケールダウン』と呼ばれる。 可用性 システムを極力障害で停止させること無く運用し続けられること。 単語としては『可用性のあるWEBサービス』のように使用される。 その指標としては、稼働率で表現される。 総時間 = サービス提供時間(稼働時間) + サービス停止時間 の関係にあるとき、 稼働率 = サービス提供時間(稼働時間)/ 総時間 で計算される。 ホスティング (物理・仮想を問わず)レンタルサーバーを借りること。 日本国内では『レンタルサーバーを利用する』というが、海外では『ホスティングする』と言うのが一般的らしい。 え、前者のほうが理解しやすくない?とか言わない。文化の違いですね。 CIDR Classless Inter-Domain Routingの頭文字をとったもの。 クラスを気にせずIPアドレスを使用するための仕組み。 端的な解説は困難なので、下記リンクを参照。 CIDRとは ROI Return on Investmentの頭文字をとったもの。 ざっくり言うと、費用対効果やコストパフォーマンス、などに相当する概念。 得られた利益に対してAWS利用にかかる費用を割ったもの。 ROIが高いサービスほど、ビジネス的価値が高いと言える。 厳密に言うと若干異なるので、詳しく知りたい人は下記リンクを参照。 ROIの意味は?計算式まで理解して投資効果を正しく見極めよう! NAT Network Address Translationの頭文字をとったもの。 効率的にネットワークを分離し、IPアドレスを変換するための仕組み。 数種類あり、詳細は下記リンクを参照。 【図解】初心者にも分かるNATの仕組みと種類 ~静的/動的NAT/NAPT/(PAT),セキュリティ等メリット/デメリット~ Zone Apex 以下のようなサブドメインを含まないドメイン名。 sample.co.jp sample.com つまり、www.のような接頭辞がつかないドメインのこと。 APEXとは頂点を意味する英単語であり、ゲームのApex Legendsもそれが由来と思われる。 つまりはVictory Royaleであり、ドン勝でもある。 …それはさておき詳細を知りたい人は以下のリンクを参照。 Zone Apex (Naked Domain) CDN CNAME Canonical Nameの略。 ドメイン名のエイリアス(≒あだ名)である。 これを利用することで、IPアドレスの割り当てロジックと命名を分離することができるため、IPアドレスの変更に強くなる(らしい)。 詳細は下記リンクを参照。 AレコードとCNAMEレコードを整理する OAuth ユーザー認証のプロトコルの1つ。 「Googleでログイン」はこのOAuth認証を利用している。 最近のユーザーログイン方式の流行りの1つなので、押さえておくと吉。 ベンダーロックイン 特定ベンダー提供の機能を利用しすぎるあまり、他ベンダーへの乗り換えが困難になること。 例えば、某ベンダーのサーバーのAPIを利用してブートのロジックを実装していたら、 別ベンダーのサーバーに移行する際に開発工数が発生してしまう、など。 モノリシック ソフトウェア設計において、機能を少数のモジュールに詰め込むこと。 対義語は『マイクロ』。 マイクロサービス化して疎結合なアーキテクチャを実現するといった文脈で利用される。 AWSでは各サービスがAPIにより疎結合となっており、マイクロサービスの代表例ともいえる。 また、一般に、マイクロサービス化が進んだシステムのほうが運用しやすく追加開発も行ないやすいとされている。 ただ、初期開発はモノリシックのほうが工期やスキルの面で何かと都合がよいことも…? そういえば先日、IPAがこんなものを出していました。 ~DXを実現するためのあるべきITシステム「スサノオ・フレームワーク」を紹介~ 内容はさておいて、カッコイイ名前ですよね。 ブート領域 コンピュータの起動に必要なプログラムや設定情報が記録されたディスク上の領域。 私にはこの知識だけで充分だったのでそれ以上は調べず…。 もし詳しく知りたい方は、下記リンクを参照。 ブートセクタとは何か まとめ いかがでしたでしょうか。 あ~基本情報で見たことあるな~という用語もちらほらありましたね。 本記事が皆様のお役に立てば幸いです。 それでは皆さん、よいエンジニアリングライフを。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VPCプレフィックスリストとCloudFormationで、セキュリティグループのIP管理をグッと楽にする

AWSのEC2インスタンスやALBをパブリックサブネットに置く場合、セキュリティグループで特定のIPからのみアクセスを許可することがあります。 例えば、社内システムなので事業所ネットワークの出口IPからのみHTTPSアクセス可能にしたいといったケースです。 セキュリティグループ設定例 TCP 443 本社ビルネットワークの出口IP/32 TCP 443 大阪事業所ネットワークの出口IP/32 IP管理の問題点 セキュリティグループでの設定は簡単ではありますが、同じようなセキュリティグループが増えてしまうと、IPの追加や変更があった場合に修正が面倒で修正漏れも起きやすくなります。 IP変更であれば、以前のIPは他者が取得する可能性もあるので、セキュリティリスク になる可能性があります。 また、IPの説明を詳しく書いていなければ、時間が経つにつれて 謎のIPと化す というのもよくある話です(経験談)。 NGなセキュリティグループの例 TCP 443 新本社ビルネットワークの出口IP/32 ※これはOK TCP 443 新大阪事業所ネットワークの出口IP/32 ※これもOK TCP 443 古い出口IP/32 ※削除漏れ TCP 443 なんか不明なIP/32 ※削除漏れ且つ謎IP化 VPCプレフィックスリストとは VPCプレフィックスリストとは、よく使うIPグループを事前に定義しておき、複数のセキュリティグループで使い回すことができる機能です。2020年6月にリリースされた、比較的新しい機能になります。 これがあれば、例えば「社内ネットワークの出口IPリスト」を1つ作っておき、セキュリティグループではプレフィックスリストのIDを指定するだけでよくなります。 さらに、IPの追加や変更も、その共通のプレフィックスリストを修正するだけで完了するので、管理がとても楽になります。 ※ちなみに、セキュリティグループ以外にもルートテーブルでも使えますが、この記事では割愛します。 設定例 AWSコンソールから、VPC → マネージドプレフィックスリスト → プレフィックスリストを作成 を選択します。CIDRブロックのところに、IPアドレスと説明を入れます。 セキュリティグループでは、カスタムソースとして pl- から始まるプレフィックスリストを指定できます。固定されるのはあくまでIPだけなので、ポートはルール毎に自由に設定できます。 プレフィックスリスト設定のポイント IPには必ず説明を入れる 説明が空のIPは、謎IP化する確率が高くなります。プレフィックスリストなら一度入れるだけでいいので、面倒くさがらず必ず記載しておきましょう。 ポート設定する単位でグループ化する とりあえず使いそうなIPを全部一つのプレフィックスリストに入れてしまうと、不要なIPにまでポートを開けることになってしまい本末転倒です。「オフィスの出口IPリスト」、「外部サービスの出口IPリスト」など、使い回ししやすい単位で作成しましょう。 CloudFormationを使う場合 AWSリソースはCloudFormation(やCDK)で管理している場合も多いと思います。 プレフィックスリストももちろんCloudFormationで定義できます。 以下の例では、将来的なIP変更をコンソールからすることを想定し、パラメータとしてCIDRを入力させています。 AWSTemplateFormatVersion: 2010-09-09 Parameters: MainExitIP: Type: String Description: "本社ビルネットワークの出口IP" Default: x.x.x.x/32 AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) OsakaExitIP: Type: String Description: "大阪事業所ネットワークの出口IP" Default: x.x.x.x/32 AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) ConstraintDescription: "CIDRは x.x.x.x/x の形で指定してください" Resources: OfficeNetworkExitIps: Type: AWS::EC2::PrefixList Properties: AddressFamily: IPv4 Entries: - Cidr: !Ref MainExitIP Description: "本社ビルネットワークの出口IP(CIDR)" - Cidr: !Ref OsakaExitIP Description: "大阪事業所ネットワークの出口IP(CIDR)" MaxEntries: 20 PrefixListName: office-network-exit-ips Tags: - Key: Name Value: office-network-exit-ips プレフィックスリストを複数作る場合のひと工夫 複数のプレフィックスリストを作る場合には、1つのCloudFormation stackに入れておいて他stackからはImportValueで参照すると、プレフィックスリストのIDを書く必要がないので便利です。 プレフィックスリスト用 stack AWSTemplateFormatVersion: 2010-09-09 Resources: OfficeNetworkExitIps: Type: AWS::EC2::PrefixList Properties: AddressFamily: IPv4 Entries: - Cidr: x.x.x.x/32 Description: "本社ビルネットワークの出口IP(CIDR)" - Cidr: x.x.x.x/32 Description: "大阪事業所ネットワークの出口IP(CIDR)" MaxEntries: 20 PrefixListName: office-network-exit-ips Tags: - Key: Name Value: office-network-exit-ips ExternalServiceExitIps: Type: AWS::EC2::PrefixList Properties: AddressFamily: IPv4 Entries: - Cidr: x.x.x.x/24 Description: サービスA - Cidr: x.x.x.x/32 Description: サービスB MaxEntries: 20 PrefixListName: external-service-exit-ips Tags: - Key: Name Value: external-service-exit-ips Outputs: ExternalServiceExitIpsOutput: Description: "" Value: !Ref OfficeNetworkExitIps Export: Name: !Sub "${AWS::StackName}-OfficeNetworkExitIpsPrefixList" ExternalServiceExitIpsOutput: Description: "" Value: !Ref ExternalServiceExitIps Export: Name: !Sub "${AWS::StackName}-ExternalServiceExitIpsPrefixList" プレフィックスリストを利用するstack パラメータとしてstack名を指定させています。Security Groupでは、SourcePrefixListId: !ImportValue を使ってプレフィックスリストを読み込んでいます(ImportValueのAWSドキュメント)。 AWSTemplateFormatVersion: 2010-09-09 Parameters: PrefixListStackName: Type: String Default: test-vpc-prefix-list Description: VPCプレフィックスリストのstack名 Resources: TestVpc: Type: AWS::EC2::VPC Properties: CidrBlock: 10.100.0.0/24 EnableDnsHostnames: true Tags: - Key: Name Value: TestVpc TestSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: 社内と外部システムからのアクセス許可 GroupName: test SecurityGroupIngress: - SourcePrefixListId: !ImportValue 'Fn::Sub': '${PrefixListStackName}-OfficeNetworkExitIpsPrefixList' FromPort: 443 ToPort: 443 IpProtocol: tcp - SourcePrefixListId: !ImportValue 'Fn::Sub': '${PrefixListStackName}-ExternalServiceExitIpsPrefixList' FromPort: 443 ToPort: 443 IpProtocol: tcp VpcId: !Ref TestVpc 最後に VPCプレフィックスリストとCloudFormationを使えば、セキュリティグループの作成もその後のIP管理もグッと楽になります。 IP管理で悩んでいる方はぜひ使ってみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Lambda】【Python】import requestsで Unable to import module エラーが発生したとき解決方法

pip install requests -t . → ERROR: Can not combine '--user' and '--target' エラーが出たので取り急ぎターゲットディレクトリを指定せずにインストールしました。 pip install request だけのコマンドだったらいけました。 ただし、どこに保存されているのかな? pip install requests コマンドでインストールしたパッケージがどこにあるか確認するためには ↓
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CloudWatch Logs Agentに埋め込みメトリクスを(力技で)導入する

TL; DR CloudWatch Agentが使えたら、そちらを使いましょう。 はじめに CloudWatch Logsには、埋め込みメトリクス (Embedded Metric) という機能が存在します。 こちらを利用すると、CloudWatch Logsに含まれる情報を、自動でCloudWatch Metricsへ転送してくれます。 ログファイルの構造とメトリクス抽出の設定を二重管理する必要がなくなるため、非常に便利な機能です。 高カーディナリティログの取り込みと CloudWatch 埋め込みメトリクスフォーマットによるメトリクスの生成 - Amazon CloudWatch 現在ではCloudWatch Agentを使用することが一般的ですが、かつては、CloudWatch Logs Agentというツールを使ってログファイルをCloudWatch Logsに送信していました。 名前が似ているので間違いやすいのですが、CloudWatch Logs Agentは旧ツールとなっており、使えなくなることが予想されます(と言われて数年経っているわけですが)。 このリファレンスは、廃止が予定されている古い CloudWatch Logs エージェント用です。代わりに統合 CloudWatch エージェントを使用することを強くお勧めします。エージェント詳細については、CloudWatch エージェントを使用して Amazon EC2 インスタンスとオンプレミスサーバーからメトリクスとログを収集するを参照してください。 CloudWatch Logs エージェントのリファレンス - Amazon CloudWatch Logs CloudWatch Logs Agentの開発は、埋め込みメトリクスが実装される前には既に終了しており、今後も埋め込みメトリクスが実装されるのは望み薄でしょう。 しかし、CloudWatch Agentが利用できないプラットフォームでは、現在でもCloudWatch Logs Agentを使わざるをえない、そういった状況も存在するかと思います。 そのような状況下、埋め込みメトリクスが使えたら便利だなぁと思うこともありますよね。 それでも、どうしても埋め込みメトリクスを使いたい! そんな時に、力技で利用可能にする方法を説明します。 CloudWatch Logs Agentに埋め込みメトリクスを導入する方法 以降、CloudWatch Logs Agentに埋め込みメトリクスを導入する方法を、補足を交えつつ説明します。 事前準備 CloudWatch Logs AgentはPython2.6以上Python3.6未満での実行を想定して書かれています。 また、事前にAWS-CLIはインストール済みである必要があります。 利用する際は、以下のコマンドでサーバー等にセットアップします。 CloudWatch-Logs-Agentをセットアップするスクリプト #!/usr/bin/bash function restart_cloudwatch_logs_agent() { if [ -f /etc/system-release ] && [ $(cat /etc/system-release | grep "Amazon Linux release 2" | wc -l) -eq 1 ]; then # OSがAmazon Linux 2の場合 sudo service awslogsd restart else # それ以外の場合 sudo service awslogs restart fi } # セットアップツールの導入 curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O # セットアップの実行 sudo python awslogs-agent-setup.py --region ${REGION_NAME} # サービスのリスタート restart_cloudwatch_logs_agent() AWS Command Line Interface とは - AWS Command Line Interface AWS CLI のインストール、更新、アンインストール - AWS Command Line Interface CloudWatch Logs エージェントのリファレンス - Amazon CloudWatch Logs AmazonLinux2の判定方法 | ハックノート 本記事では、ログの送信は既にできているものとして話を進めます。 できていない場合は、AWSのドキュメントを参考に設定ファイルおよびIAMロールを設定してください。 そのままでは導入できない埋め込みメトリクス 本節は確認のための節であるため、埋め込みメトリクスの導入方法だけ知りたい場合は読み飛ばしても大丈夫です。 CloudWatch Logs Agentで送信する対象ログファイルに、以下のようなJSON形式の文字列を出力してみてください。 下記が正常に埋め込みメトリクスとして認識されていれば、 time が100のメトリクスとして登録されるはずです。 「埋め込みメトリクスフォーマットの例とJSONスキーマ」節に記載されているJSON例 { "_aws": { "Timestamp": 1574109732004, "CloudWatchMetrics": [ { "Namespace": "lambda-function-metrics", "Dimensions": [["functionVersion"]], "Metrics": [ { "Name": "time", "Unit": "Milliseconds" } ] } ] }, "functionVersion": "$LATEST", "time": 100, "requestId": "989ffbf8-9ace-4817-a57c-e4dd734019ee" } 仕様: 埋め込みメトリクスフォーマット - Amazon CloudWatch しかし実際には、CloudWatch Logsにログが送信されていることは確認できるものの、メトリクスは生成されません。 仕様と実装の確認そして導入 埋め込みメトリクスの仕様 まず、埋め込みメトリクスの仕様を確認してみましょう。 どうやら、APIのリクエストヘッダーに x-amzn-logs-format: json/emf を設定する必要があるみたいです。 仕様: 埋め込みメトリクスフォーマット - Amazon CloudWatch 実際、boto3ライブラリ(Pythonで利用可能なAWS-SDK)で埋め込みメトリクスを導入するには、以下のようなソースコードを書けば良いことがわかっています。 boto3ライブラリを利用して埋め込みメトリクスを送信するPython実装例 def add_emf_header(request, **kwargs): request.headers.add_header('x-amzn-logs-format', 'json/emf') print(request.headers) # Remove this after testing of course. # You can even do this with watchtower! Use this instead, client = watchtower_handler.cwl_client client = boto3.client('logs') client.meta.events.register_first('before-sign.cloudwatch-logs.PutLogEvents', add_emf_header) Injecting custom http headers in boto3 · Issue #2251 · boto/boto3 CloudWatch Logs Agentの実装 次に、CloudWatch Logs Agentの実装を確認してみましょう。 先駆者様方により、 /var/awslogs/lib/python/site-packages/cwlogs/push.py のソースコードが実行されていることが把握されています。 libディレクトリ名がlib64だったり、pythonディレクトリ名がpython2.6やpython3.5だったりするため、そこはセットアップした環境に合わせて適宜読み替えてください。 以降、 /var/awslogs/lib/python/site-packages ディレクトリまでのパスを ${PYTHON_PACKAGES} と書き替えて説明します。 クラス図およびシーケンス図で表現すると、以下の通りです。 説明に不要な要素(クラス、メソッド、メソッドの引数など)は省略しています。 ${PYTHON_PACKAGES}/cwlogs/push.py のLogsPushCommandクラス、および、継承元クラスである ${PYTHON_PACKAGES}/awscli/customizations/commands.py のBasicCommandクラスを確認すると、 BasicCommand::__call__() メソッド(フロー1)で呼出している ${PYTHON_PACKAGES}/cwlogs/push.py 183行目の LogsPushCommand::_run_main() メソッド(フロー2)で、pushコマンドを動かしていることが確認できます。 また、 ${PYTHON_PACKAGES}/cwlogs/push.py のWatcherクラス、Watcherクラスが呼出しているStreamクラス、Streamクラスが呼出しているEventBatchPublisherクラス、EventBatchPublisherクラスが継承しているPublisherクラス、Publisherクラスが継承している ${PYTHON_PACKAGES}/cwlogs/threads.py のBaseThreadクラスを確認すると、 BaseThread::run() メソッド(フロー19)で呼出している EventBatchPublisher::_run() メソッド(フロー20)で呼出している Publisher::_publish_event_batch() メソッド(フロー21)で呼出している Publisher::_put_log_events() メソッド(フロー22)内の1247行目 self.logs_service.put_log_events(**params) メソッド(フロー23)で、実際にログファイルをCloudWatch Logsに送信していることが確認できます。 同様に、Streamクラス、Streamクラスが呼出しているFileEventBatchReaderクラス、FileEventBatchReaderクラスが継承しているFileEventsReaderクラスを確認すると、 FileEventsReader::_run() メソッド(フロー26)でログファイルの変更を読込んでいることが確認できます。 複雑ですね。 CloudWatch Logsエージェントの中身を(途中まで)調べてみた | mooapp CloudWatch Logs Agentの中身を探してみた - Qiita CloudWatch Logs Agent の挙動について調べたことのまとめ - Qiita CloudWatch Logs Agentへの埋め込みメトリクスの導入方法 上記2点をまとめると、CloudWatch Logs送信時にヘッダーとして x-amzn-logs-format: json/emf を設定すれば良いですね。 ${PYTHON_PACKAGES}/cwlogs/push.py ファイルの LogsPushCommand::_run_main()メソッドは、以下のような実装になっているはずです。 非常にわかりづらいですが、 self.logs = self._session.create_client('logs', **client_args) で生成されている self.logs オブジェクトが、boto3(実際にはbotocore)で生成されたlogsクライアントです。 push.pyにおけるLogsPushCommandクラスの_run_mainメソッド(L183-L221) def _run_main(self, args, parsed_globals): # enable basic logging initially. This will be overriden if a python logging config # file is provided in the agent config. logging.basicConfig( level=logging.INFO, format=('%(asctime)s - %(name)s - %(levelname)s - ' '%(process)d - %(threadName)s - %(message)s')) for handler in logging.root.handlers: handler.addFilter(logging.Filter('cwlogs')) # Parse a dummy string to bypass a bug before using strptime in thread # https://bugs.launchpad.net/openobject-server/+bug/947231 datetime.strptime('2012-01-01', '%Y-%m-%d') client_args = { 'region_name': None, 'verify': None } if parsed_globals.region is not None: client_args['region_name'] = parsed_globals.region if parsed_globals.verify_ssl is not None: client_args['verify'] = parsed_globals.verify_ssl if parsed_globals.endpoint_url is not None: client_args['endpoint_url'] = parsed_globals.endpoint_url # Initialize services and append cwlogs version to user agent self._session.user_agent_extra += 'cwlogs/' + cwlogs.__version__ self.logs = self._session.create_client('logs', **client_args) # This unregister call will go away once the client switchover # is done, but for now we're relying on Logs catching a ClientError # when we check if a stream exists, so we need to ensure the # botocore ClientError is raised instead of the CLI's error handler. self.logs.meta.events.unregister('after-call', unique_id='awscli-error-handler') self._validate_arguments(args) # Run the command and report success if args.config_file: self._call_push_file(args, parsed_globals) else: self._call_push_stdin(args, parsed_globals) return 0 つまり、下記スクリプトを走らせて、push.pyファイルを書換えちゃえば使えることになります! push.pyファイルを書換えるスクリプト # ディレクトリパスを設定する PYTHON_PACKAGES="/var/awslogs/lib/python/site-packages" # 一応、push.pyのファイルが想定した文字列になっているかMD5で判定する echo "8efb83571499e29142adfa5e129f67da ${PYTHON_PACKAGES}/cwlogs/push.py" | md5sum -c if [ $? == 0 ]; then # 214行目に実行コードを書込む sed -i "214i \ self.logs.meta.events.register_first('before-sign.cloudwatch-logs.PutLogEvents', lambda request, **kwargs: request.headers.add_header('x-amzn-logs-format', 'json/emf'))" ${PYTHON_PACKAGES}/cwlogs/push.py else echo "ERROR: push.py file is invalid!" fi # awslogs/awslogsdのリスタートも忘れずに restart_cloudwatch_logs_agent() 上記スクリプト実行後のpush.pyにおけるLogsPushCommandクラスの_run_mainメソッド(L213-L215) self.logs.meta.events.unregister('after-call', unique_id='awscli-error-handler') + self.logs.meta.events.register_first('before-sign.cloudwatch-logs.PutLogEvents', lambda request, **kwargs: request.headers.add_header('x-amzn-logs-format', 'json/emf')) self._validate_arguments(args) 動作確認として、先ほどと同様のJSON形式でログ出力してみてください。 CloudWatch Metricsにメトリクスが出現しているはずです。 おわりに 開発が終了しているライブラリだからこそできる、かなり無理矢理な方法でした。 CloudWatch Logs Agentは、いつ利用できなくなるかもわからないので、気をつけながら使いましょう。 CloudWatch Agentが利用できるにも関わらずCloudWatch Logs Agentを利用している場合は、今すぐ移行することをオススメします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLF/SAA合格時のUdemy活用法

概要 今年、機会もあり、AWS Cloud Practionar(以後、CLF)とSolution Architect(以後、SAA)に合格しました。 その際、いくつかの参考書とUdemyが大いに役立ちました。 参考にどのように活用したのか記載したいと思います。 前提 ・機械系の修士卒業卒 ・SIerに数値解析アプリのエンジニアとして入社し、2年目 ・アプリ開発がメインのため、インフラ関連の知識は入社時の研修で学んだ程度 AWS受験の経緯  入社時の研修でAWSの講習を受けた際にAWS CLFのバウチャーコードを無料でもらったためです。  元々、興味を持っておりましたが資格と言ったらひとまずIPA関連の基本情報や応用情報を取得してからとりくみたいと思っており、優先順位は高くありませんでした。 会社から支給されたバウチャーコードには受験日の期限と合否の結果にかかわらず報告が必要であり、どうせならと思い、勉強を始めました。 CLF取得後はAWS自体に興味を持つようになり、自己啓発的に継続して勉強を行うようになりました。 試験の準備期間  CLFは1ヶ月、SAAは2ヶ月コツコツ積み重ねて勉強しました。大体、CLFは50h,SAAは80時間ほどの時間をかけたかと思います(効率が悪い人間です)。 AWS CLF取得の勉強方法について 1.参考書を購入し、一通りのサービスを勉強  研修時に学べなかったAWSの技術を一通り勉強したいと思い、購入しました。 AWSの勉強を行う際、参考書を購入するのは賛否があるかと思います。 技術の移り変わりによりテキストに掲載されている技術が最新ではなかったり、合格するには不十分だったりします。 ただ、インターネットからそれぞれのAWSが提供しているサービスを検索し、確認するよりは手っ取り早くサービスの概要を学べるかと思います。 また、書籍等は動画等に比べるとネットワーク環境に関係なく読み進めることができるため通勤やスキマ時間にコツコツ読み進めました。 参考書:AWS認定資格試験テキスト AWS認定 クラウドプラクティショナー 2.デジタルコースで復習  AWSが提供しているデジタルコースで各クラウドサービスをカフェのサービス等に置き換えて説明してます。 個人的には楽しく、わかりやすく学べますし、英語ではありますが字幕の日本語も本試験に比べたら丁寧に訳されております(本試験の和訳は酷いです)。 上記の参考書でいまいちわからなかったり、掲載されていないサービスはこちらのデジタルコースで勉強しました。 デジタルコース:AWS クラウドプラクティショナーの基礎知識 (第 2 版) 3.AWS公式のサンプル問題で試験内容を把握  AWSが公式で提供しているサンプル問題があり、英語ですが試験形式や内容を把握できます。 サンプル問題や模擬試験は早めに取り組み、試験の概要を把握するのは大事ですが、ある程度の知識がないと内容も把握できないのではないかと思い、このタイミングでサンプル問題を解きました。  私個人の反省点としてデジタルコースを受講する前に受験してもよかったなと思いました。 AWSサンプル問題:問題と解答 4.Udemyでひたすら模擬試験を解く  AWS CLFの模擬試験をいくつか受験したいと思い、Udemyの下記講座を購入し、ひたすら問題演習を行いました。 Udemyの下記講座は通常時に購入すると比較的高い(5000円くらい?)ですが、セール時などでぐっと安くなり、1000円台になりますのでその時に購入するのが狙い目です。 Udemyのイベントなので少し持ち上げて話しますとUdemyの問題演習を購入するメリットは最新の試験形式に基づいた問題を解くことができる点です。 Udemyに掲載されている講座の多くは投稿者が上げた後に改良されています。 例えばAWSですとサービスの提供リージョンが増えたり、ストレージサービスのオプションが終了したりします。 エンジニアとしてそれらの技術をキャッチアップするのは当たり前ですが、資格取得の勉強ではサービスの変化に気づけない場合があります。 参考書等の模擬試験ですと掲載されている問題は比較的難易度が高く、オプショナル>>応用レベル>基本レベル=本試験といった感覚でした。 CLF合格だけを考慮すると全ての問題を解く必要がなく、応用レベルまで問題演習をこなせば十分合格レベルに達するかと思います。 アウトプットできる教材が限られている中でこの教材は使い勝手も含めて非常に良いテキストだったかと思います。 この問題だけで合格可能!AWS 認定クラウドプラクティショナー 模擬試験問題集(7回分455問) AWS SAA取得時に使用 1.Udemyのハンズオン講座で基本サービスに触れる  順番としてCLF受験前に受講した方がより良いとは思いますが、期限がなかったのでCLF取得後に受講しました。 その時までテキストベースで主に勉強していたため実際にIAMやEC2等のサービスを触ることでより理解が深まりSAA取得時にも大いに役立ちました。 これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座(SAA-C02試験対応版) 参考書を購入し、一通りのサービスを復習  結果として下記のテキストを買う必要はなかったと思いますが、知識の漏れを網羅的に短時間で確認するのには良かったと思います(買う必要はないと思います)。 参考書:(模擬問題付き)改訂新版 徹底攻略 AWS認定 ソリューションアーキテクト − アソシエイト教科書[SAA-C02]対応 3.Udemyでひたすら模擬試験を解く  AWS CLFと同様にUdemyの下記講座を購入し、ひたすら問題演習を行いました。 Udemyのイベントなのでこちらでも少し持ち上げて話しますとUdemyの問題演習を購入するメリットにスマホもしくはタブレットのアプリを用いて勉強ができるところも魅力かと思います。 スキマ時間や通勤時間にもちろん使用でき、演習問題は途中で停止することが可能なため、少し時間があるときに開いて一問解くという使い方もできますし、アプリで時間が計測されているため本番を想定して使用も可能です。 こちらの問題集はセールを用いるとコスパが良いのですが、解説や問題文等は正直わかりずらかったです(本試験と同等の翻訳レベルなので模試としての完成度は高いかもしれません)。 こちらで理解を深めるために使用するというよりかは試験形式に慣れるのに使用した方がよろしいかもしれません。 【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) 4.Cloud Techの模擬試験を用いて理解を深める  偶然Twitterで知って上記のUdemy講座に満足しておりませんでしたので試験の1週間前にこちらのサービスを使用して最後、追い込みをしました。 無料で200問ほどSAA対策の問題を演習できますが、各問題の解説はわかりやすく理解が深まりましたので、上記3のUdemy演習問題よりは優先して取り組むべきだと思います。 ウェブサイト:Cloud Tech 結果   CLFは900点くらいでSAAは780点くらいでどちらも合格しました。個人としてはSAAで時間をかけて勉強した割には良いスコアが取れず少し残念ではありました。 今後の取り組み  SAA取得後、AWS関連の業務を任せてもらう機会が増えていい経験をしており、以前に比べ業務に対するモチベーションも高いので引き続きAWSの資格(DVA等)を受験していきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのアカウント周りを整理する(ルートユーザ、IAM、ロール、ポリシー・・・)

はじめに どーも、のぶこふです。 この記事はGFAMアドベントカレンダー2021の2日目の記事です。 AWSを触っていくにあたり、なにはともあれ、アカウントの作成ですよね。 今回は、アカウント周りについて、まとめていこうと思います。 参考資料は、みんな大好きBlack Beltより拝借しております。1 2 今回のサービス一覧 Service名とか 概要 ルートユーザ アドミン アクセスキー IDとパスワード IAMユーザ ユーザ IAMポリシー アクセス許可の定義 IAMグループ IAMユーザの集合 IAMロール 一時的なアクセス権限 STS IAMロールが生成する ※IAM(AWS Identity and Access Management) ※STS(AWS Security Token Service) 関係性 名前とか文章だけだと分かりづらいので、図にしてまとまっているのがコチラです。3 各サービスについて、もう少し掘り下げます。 ルートユーザ アカウントの全てのAWSサービス・リソースに完全なアクセス権を持つユーザー つまり、めちゃんこ強いです。何でも出来ます。出来てしまいます。→乗っ取られるとヤバい! ので、極力、使うことは避けましょう。 使用例は後述の通り マネジメントコンソールへは、AWSアカウントを作成した時のメールアドレス・パスワードでログイン ルートユーザ=AWSアカウントとほぼ同義 ルートユーザの認証が必要なタスクの例 ※あくまでも一例です * ルートユーザのメールアドレスやパスワード変更 * 支払いオプションやサポートプランの変更 * AWSアカウントの解約 アクセスキー ルートユーザ、IAMユーザの長期的な認証情報 外部からAWSへのサービスへプログラムを通じてアクセスが可能になる ルートユーザのアクセスキー作成は推奨されていません そもそも、ルートユーザの使用が推奨されていません アクセスキーID・シークレットアクセスキーで構成される つまり、IDとパスワードで構成されている アクセスキーを使うよりも、(IAMユーザに必要最低限のIAMポリシーを付与した)IAMロールを使用するのが推奨されています。 アクセスキーは共有しないこと 複数人がAWSリソースへのアクセス権を共有したい時でも、共有はNG。 AWSへのアクセスが必要なアプリケーションの場合は、STSを取得する(後述) 取り扱いには注意 Githubリポジトリ AMIへの埋め込み 設計書などのドキュメントに記載 ハードコーディング IAM AWSで作成するエンティティ 名前と認証情報で構成される フレンドリ名:ユーザの作成時に指定 例)nobkovskii ARN(Amazon Resource Name):リソースポリシーのPrincipalで指定 例)arn:aws:iam::0123456789012:user/nobkovskii 一意の識別子:フレンドリ名を再利用したいとき等に権限のエスカレーションを避けることができる 認証情報 コンソールパスワード アクセスキー MFAを有効化しよう MFA(Multi-Factor Authentication:多要素認証) パスワードやアクセスキーとは別に、セキュリティを強化する仕組み ルートユーザ、IAMユーザの各IDに個別設定可能 MFA条件を指定したポリシーを関連付け可能対象 IAMユーザ、IAMグループ S3バケット、SQSキュー、SNSトピック等 IAMの信頼ポリシー 不要な認証情報を削除しよう 社員の入社、退社、部署異動、役割変更など人員のライフサイクルと連動させて、最近使用していないパスワードやアクセスキー等は削除対象とする 認証情報を定期的にローテーションしよう IAMユーザのパスワード アクセスキー ポリシー IAMアイデンティティやAWSリソースに関連づけることによってアクセス許可を定義することができるオブジェクト ポリシータイプとして分類されている。4 有効権限については、割愛5 ポリシータイプ 概要 有効権限(同一アカウント) 有効権限(クロスアカウント) アイデンティティベースポリシー 管理ポリシー、インラインポリシーに分類 OR OR リソースベースポリシー IAMロールの信頼ポリシー、S3バケットポリシーとか OR AND パーミッションパウンダリー IAMアクセス許可の境界、Organizationサービスコントロールポリシー(SCP) AND AND アクセスコントロールポリシー(ACL) S3バケットのACL、VPCサブネットのACL セッションポリシー 一時セッションをプログラムで作成する際にパラメータとして渡すポリシー AND AND AWS管理ポリシー AWSにより事前定義されたポリシー AWSが作成、管理しており、編集は不可能 AWSが更新する すべてのAWSアカウントで利用可能 例)AmazonEC2FullAccess、AdministratorAccess 多くのユースケースで、すぐにポリシーの適用が開始できる カスタマー管理ポリシー AWSアカウントで管理できるカスタムポリシー AWS管理ポリシーでは要件を満たせない場合等で使用する インラインポリシー 1つのIAMエンティティ(ユーザ、グループ、ロール)に、直接埋め込まれるポリシー IAMエンティティに紐付いた固有のオブジェクト IAMエンティティを削除すると、インラインポリシーも削除される IAMエンティティとポリシーが厳密な1対1の関係を維持する場合等に使用する インラインポリシーではなく、カスタマー管理ポリシーを使おう カスタマー管理ポリシーは・・・ カスタマイズ可能 再利用性も高い すべての管理ポリシーを一箇所で確認できる インラインポリシーは、カスタマー管理ポリシーに変換可能 IAMグループ IAMユーザの集合 IAMグループやIAMロールをIAMグループに所属させることは不可 IAMグループに関連付けられたIAMポリシーは、所属するIAMユーザに継承される IAMグループを使用して、IAMユーザにアクセス許可を割り当てよう 組織やジョブ機能に関連したIAMグループを作成して、IAMグループに対してアクセスIAMポリシーを関連付ける 会社内で組織移動があった場合は、対象のIAMユーザが所属するグループを変更するだけ! IAMロール AWSサービスやアプリケーション等のエンティティに対して、AWSリソースの操作権限を付与するための仕組み IAMユーザやIAMグループには紐付かない ロールを一時的に引き受けることで関連付けられたアクセス許可を受けることができる 一時的なセキュリティ認証情報 短期的な有効期限付きのアクセスキーID・シークレットアクセスキー・セキュリティトークンで構成 ローテーションしたり、明示的に削除する必要なし ユーザのリクエストでSTSを動的に作成 STS 一時的なセキュリティ認証情報を生成するサービス 期限付きのアクセスキーID・シークレットアクセスキー・セッショントークン ロールを使用してアクセス許可を委任しよう アカウント間でのセキュリティ認証情報の共有は御法度 EC2上のアプリにロールを適用 認証情報をEC2に持たせる必要なし 認証情報は自動的にローテーション SDKで認証情報取得とお有効期限切れ前の再取得を自動的に実施 IAMロールのユースケース クロスアカウントアクセス Swich Role IDフェデレーション おわりに さくっと書くつもりが、思いの外、大容量になってしまいました。 次回からは、スッキリ書けるように気をつけます。 今回はここまでです。 ありがとうございました。 BlackBelt_IAM_Part1 ↩ BlackBelt_IAM_Part2 ↩ 当然、BlackBeltの資料から拝借してます ↩ AWSドキュメントを見たほうが良い ↩ 資料みてください ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS DOP合格記(2021/12/1投稿)

はじめに この度AWS認定のDevOps Proを受験してきましたので、勉強した内容と受験した感想を書いておきます。 これから受験される方の参考になれば幸いです。 今回でAWS認定は7冠を達成しました。 2019-07-04 AWS Certified Cloud Practitioner 2019-07-24 AWS Certified Solutions Architect - Associate 2021-02-25 AWS Certified Data Analytics - Specialty (DAS) 2021-07-23 AWS Certified Developer - Associate (DVA) 2021-10-26 AWS Certified SysOps Administrator - Associate (SOA) 2021-11-13 AWS Certified Security - Specialty (SCS) 2021-12-01 AWS Certified DevOps Engineer - Professional (DOP) ← 今回 前提 AWS経験は2年と半年程度 普段はデータ分析系のサービスを使うことが多い 最近は業務でセキュリティ周りのこともやる機会が増えてきた Code系はほとんど使わない コンテナ周りは少々触る 学習内容 今回は以下の順番で勉強を進めました。 ①練習試験 ②Web問題集 練習問題 これまではAWS公式の模擬問題を使ってましたが、最近公式から無料版の練習問題が提供され始めたので、今回はこちらを利用して最初に学習しました。 20問というのは変わってないですが、ちゃんと問題に対して正答と解説がついているので模擬問題の頃に比べてだいぶ親切になっています。 20問解いた後はこんな感じでどの部分が間違いが多いかを見れるようになっているので、間違った項目に関連するサービスを調べる→再度解く、というサイクルで進めました。 これは3回ぐらい解いて正答率が90%を超えるまでやりました。 Web問題集 あとはいつもどおりkoiwaclubの問題集を利用しました。(というか残りはほとんどこれしかしていない こちらも前回と同じようにscrapboxに回答結果をメモしながら進めました。 合格記を見ると何週もしましたというコメントを見るのですが、自分は忘れやすいので100%になるまで繰り返し解いて覚えています。 あとBeanstalkやCodeシリーズはAWS公式のチュートリアルを試しに動かすところまでやってます。 ( ) 時間の関係もあったので、問題集の25問目以降45問目しかやっておらず、このせいで明らかに知識が足りない部分があったので、時間がある人はちゃんと全部解いてみるのをオススメします。 受験した感想 問題集と類似している問題はそこまで多くなかったですが、問題集を解きながらデプロイ方式や各サービスを利用する際の特徴などはつかめていたのでそこまで悩みはしませんでした。 ただ明らかに知識が不足していると思われる問題はあったので、そういった問題は回答するのに4分ぐらいかかってました。 この機能知らないというものが出ても問題文を読んで冷静に選択肢を除外していると案外解けた気がします。 完全にわからないという問題は5問ぐらいでした。 受験結果 スコアは852と自分の思うよりも100ぐらい多かったです笑 絶対受かってもギリギリだと思ったんですけどね。 おわりに Codeシリーズは業務でほとんど使ってなかったので正直受かるとは思わなかったですが、何とか無事合格できて良かったです。 この勢いで次はDBスペシャリティあたりの合格を目指してみようかと思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【API Gateway と Cognito の連携】API Gateway の Authorizer を設定してみる

Authorizer が必要になる場面とは  ある特定のユーザーにだけ、特定のAPIへのアクセスを許可したい場合などに用いられます。例えば、サインインしているユーザーは API1 と API2 が叩けるが、サインインしていない全てのユーザーは API 1 のみが叩けるようにしたい場合などです。具体的アクションとしては、Twitter のようにタイムラインを見る(GET)ことや、投稿(POST)があげられます。 どのようにユーザーを判別しているか  Cognito に対しサインインアクションを行うとID トークンが返されます。ID トークンはブラウザのローカルストレージに格納されています。デベロッパーツールを開くと、図のように確認することができます(Safari での確認)。  ID トークンはその保有者が認証されているかどうかを保証する、いわば身分証のようなものです。API Gateway 側で、身分証を持っているか、または身分証が有効なものかを判断することができれば、サインインしているユーザーとしていないユーザーで API へのアクセスを制御できます。  今回の記事では既に Cognito に登録されたユーザーが存在しているとして進めます。 API Gateway で Authorizer の設定を行う REST API の作成  Example API として PetStore という API が用意されています。今回はこれをインポートしてさくっと API を作成してしまいます。  Create API > Import (REST API) > Example API にチェック > Import の順に進めると、PetStore という名前の API Gateway が作成されます。 PetStore の挙動の確認  実際に API をデプロイして挙動を確認してみましょう。  Action > API のデプロイ > デプロイされるステージで [新しいステージ] を選択 > ステージ名に prod と記入 > デプロイ の順に進めるとデプロイされます。  遷移後の画面ではデプロイされた API の URL が確認できます。URL をコピーし、末尾に /pets を加えてアクセスしてみましょう。 例)https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets すると図のようなリスト形式の文字列が返ってくることが確認できます。 [ { "id": 1, "type": "dog", "price": 249.99 }, { "id": 2, "type": "cat", "price": 124.99 }, { "id": 3, "type": "fish", "price": 0.99 } ] Authorizer の設定  左メニューからオーソライザーを選択し、新しいオーソライザーの作成 を押します。名前、タイプ、Cognito ユーザープール、トークンのソースの4つのパラメータを設定する必要があります。ここでは以下のように設定します。  /pets の GET メソッドに対してオーソライザーを設定します。  リソース > /pets の GET > メソッドリクエスト の順に進みます。  認可のペンアイコンをクリックし、先ほど作成した authorizer-test を選択し、チェックマークアイコンを押します。以下の図のように設定されれば OK です。API をデプロイして設定を反映させましょう。 ※ authorizer-test が選択肢に現れない場合は画面をリロードしてみてください。  /pets に対応するメソッドにオーソライザーを設定したので、https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets にアクセスすると {"message":"Unauthorized"} が返されることが確認できます。  {"message":"Unauthorized"} となったのは、GET リクエストのヘッダーに Authorization のフィールドが設定されていないので想定通りの挙動になります。 Authorizer の確認 ❗️注意❗️ ヘッダーに Authorization フィールドを挿入(上書き?)すると、他のアプリと干渉が生じるようです。私は Youtube が勝手にログアウトされ、Twitter が見れなくなりました。その場合は焦らず ModHeader から設定を削除すれば大丈夫です。  HTTP ヘッダーに Authorization フィールドを設定し、API にアクセスできるか確認してみます。ヘッダーにフィールドを追加するツールとして、ModHeader を使用します。  https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets にアクセスすると{"message":"Unauthorized"} が表示されます。この状態で ModHeader を開き、フィールド名 Authorization と ID トークン ZZZZZ... を記入します(ID トークン ZZZZZ... がわからない場合は AWS CLI で ID トークンを取得 を参考にしてみてください)。  画面をリロードし、ペットの情報が返ってくれば成功です。これで認証されたユーザー(有効な ID トークンを保有しているユーザー)がアクセスできる API を設定することができました。 AWS CLI で ID トークンを取得  Webアプリ等で認証機能が設定済みであれば、ID トークンはブラウザのローカルストレージから確認することができるかと思います。私は AWS CLI から ID トークンの情報を取得して、ヘッダーに追加して検証を行いました。そのコマンドも記しておきます。 aws cognito-idp admin-initiate-auth --user-pool-id ap-northeast-1_xxxxx --client-id yyyyy --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=hogehoge,PASSWORD=fugafuga ap-northeast-1_xxxxx: ユーザープールのID yyyyy: アプリクライアントのID hogehoge: ユーザー名 fugafuga: hogehogeのパスワード 参考資料 Cognitoで認証されたユーザーだけがAPI Gatewayを呼び出せるオーソライザーを使ってみた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSってなんだっけ?

はじめに どーも、のぶこふです。 この記事はGFAMアドベントカレンダー2021の1日目の記事です。 弊社では、今年(2021年)よりAWSに注力していくという方針となり、 自身を奮い立たすためにも、技術ブログ的にAWS向けのアドベントカレンダーを立ち上げて、完走を目指します。 で、AWSってなんだっけ? タイトルです。 今更、「え、そこ?」となるかもしれませんが、 「敵を知り己を知れば百戦殆うからず」 と昔のエライ人が言っていたように、まずは知るところから始めます。 AWSとは AWS(Amazon Web Service s:アマゾン ウェブ サービス)は、Amazon.comにより提供されているクラウドコンピューティングサービスです。1 AWSには200を超えるサービスがあります。 皆様おなじみのEC2といったコンピューティングから、データベース、私も少しかじっていたブロックチェーン、IoTやXR関連まであります。2 めっちゃ多いですね。 試験ではサービス名はバンバン出てきますが、触ったことがないのが大多数なので、今回のアドベントカレンダーで、色々と触っていこうと思います。 AWSの強み クラウドってなんだっけ?とか入ると、とてもまとまりきらないので、AWSの強みを見てみましょう。(まるっと引用します)3 1.豊富な機能 AWS には、コンピューティング、ストレージ、データベースなどのインフラストラクチャテクノロジーから機械学習、AI、データレイクと分析、IoT などの最新鋭のテクノロジーに至るまで、他のどのクラウドプロバイダーよりもはるかに多くのサービスを提供しており、それぞれのサービスが豊富な機能を備えています。より速く、より簡単に、より高い費用対効果で既存のアプリケーションをクラウドに移行し、想像できるものはほぼすべて構築することが可能です。 AWS サービスは最も充実した機能を提供します。例えば、AWS はさまざまな種類のアプリケーション専用に設計された幅広いデータベースを提供しているため、ジョブに最適なツールを選択して、最高のコストとパフォーマンスを実現できます。 →めっちゃサービスあるよ!! 2.顧客とパートナーをつなぐ最大のコミュニティ AWS には最も活発で最大規模のコミュニティがあり、世界中に何百万ものアクティブなお客様と数万のパートナーがいます。ほとんどの業界で、スタートアップ、大企業、公共部門の組織が、お客様の規模を問わず考えつく限りの場面で AWS を実行しています。AWS パートナーネットワーク (APN) には、AWS のサービスを専門とする多くのシステムインテグレーターと、AWS で機能するように自社のテクノロジーを適応させた多数の独立系ソフトウェアベンダー (ISV) が参加しています。 →いろんなコミュニティがめっちゃあるよ!!みんな使ってるよ!! 3.最もセキュア AWS のクラウドコンピューティング環境は、現時点で最高レベルとなる柔軟性とセキュリティを発揮するよう設計されています。AWS のコアインフラストラクチャは、軍隊、国際展開している銀行、およびその他高い機密性が求められる組織のセキュリティ要件を満たすように構築されています。こうしたセキュリティをクラウドで実現するための強力なツールには、230 のセキュリティ、コンプライアンス、およびガバナンスのサービスと機能が含まれています。また、AWS は 90 のセキュリティ標準とコンプライアンス認証をサポートしており、お客様のデータを保存する 117 の AWS のサービスすべてがデータ暗号化機能を提供しています。 →セキュリティはしっかりしてるよ(ただし上に乗せるアプリについては、利用者によるため、その限りではない)!! 4.最速の革新ペース AWS を使用すると、最新のテクノロジーを活用して、より迅速に実験と革新を行うことができます。AWS ではお客様のビジネスの変革に使用できるまったく新しいテクノロジーを発明するために、イノベーションのペースを絶えず加速させています。例えば、2014 年には AWS Lambda の発売によりサーバーレスコンピューティングの分野を開拓しました。これにより、デベロッパーはサーバーをプロビジョニングしたり管理することなくコードを実行できるようになりました。また、AWS は Amazon SageMaker を開発しました。この完全マネージド型の機械学習サービスにより、デベロッパーと科学者は、経験がなくても日常的に機械学習を使用できるようになりました。 →ばんばん新しいサービスが出て、色々できるようになるよ!! 5.最も実績のある運用上の専門知識 AWS は、成長を続ける中で、どこよりもお客様に信頼いただけるセキュリティとパフォーマンスを提供してきました。こうして培ってきた比類のない経験をお客様の最も重要なアプリケーションにご利用いただけます。AWS は 15 年以上にわたり、世界中の数百万のお客様にクラウドサービスを提供し続け、さまざまな場面で活用されてきました。AWS は、クラウドプロバイダーの中で最も豊富な運用経験を持っています。 →経験値、あるよ!! おわりに すごいざっくりとした説明ですが、AWSの強みは触ってこそ実感できると思います。 次回からは、実際にサービスを触っていき、理解を深めていこうと思います。 今回はここまでです。 ありがとうございました。 Wikipediaより ↩ AWSの製品・サービス一覧 ↩ what-is-aws ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

aws-cli-oidcで一時的なAWSアクセスキーを使う

昨年のAdvent Calendarで書きました「KeycloakのToken Exchangeを活用した一時的なAWSアクセスキーの発行 」の続編ネタです。この中で紹介した aws-cli-oidc というCLIツールの便利な使い方を紹介します。 aws-cli-oidcとは AWS管理コンソールへのログインを、Keycloakなどの外部IdPとSAMLまたはOIDCで連携させている場合に、aws-cli などのAWS向け各種CLIツールで利用可能な一時的なアクセスキーを取得してくれるちょっとしたCLIツールです。このツールの目的や説明は昨年の記事を読んでいただければと思いますが、この手の他のツールと異なり、利用者のクレデンシャルを本ツールに渡さずにアクセスキーを発行できるところがポイントです。AWSと外部IdP間のフェデレーションには、OIDCまたはSAML 2.0が利用できますがその両方に対応しています(ただし、SAMLの場合は外部IdPがToken Exchangeに対応している必要があります)。 下図は両パターンのフローを表したものです。AWS STSに対してOIDCのIDトークンまたはSAML Assertionを含むSAML Responseを送ると、一時的なアクセスキーを返してくれるためこれを利用しています。 OIDCの場合 SAML 2.0の場合 使い方 前提 外部IdP - AWS間のフェデレーションが設定されている必要があります。 外部IdP - aws-cli-oidc 間のフェデレーションが設定されている必要があります。 設定方法はお使いの外部IdPに依存するため本記事では割愛します。Keycloak用の設定については別途紹介したと思います。 インストール aws-cli-oidcのリリースページよりプラットフォームに合ったバイナリをダウンロードし、解凍して実行ファイルを配置するだけです。 セットアップ aws-cli-oidc setupコマンドを実行すると、インタラクティブに設定が可能です。以下、セットアップ実行例です。 $ aws-cli-oidc setup Using config file: /Users/wadahiro/.aws-cli-oidc/config.yaml OIDC provider name: Enter a value: IdP名 OIDC provider metadata URL (https://your-oidc-provider/.well-known/openid-configuration): Enter a value: https://your-idp.example.com/auth/realms/example/.well-known/openid-configuration Additional query for OIDC authentication request (Default: none): Enter a value: Successful redirect URL (Default: none): Enter a value: Failure redirect URL (Default: none): Enter a value: Client ID which is registered in the OIDC provider: Enter a value: aws-cli Client secret which is registered in the OIDC provider (Default: none): Enter a value: Choose type of AWS federation [oidc/saml2]: Enter a value: saml2 The max session duration, in seconds, of the role session [900-43200] (Default: 3600): Enter a value (Default is 3600): 43200 The default IAM Role ARN when you have multiple roles, as arn:aws:iam::<account-id>:role/<role-name> (Default: none): Enter a value: arn:aws:iam::123456789012:role/developer Select the subject token type to exchange for SAML2 assertion: 1. Access Token (urn:ietf:params:oauth:token-type:access_token) 2. ID Token (urn:ietf:params:oauth:token-type:id_token) Enter a value: 1 Audience for token exchange: Enter a value: urn:amazon:webservices Saved /Users/wadahiro/.aws-cli-oidc/config.yaml $HOME/.aws-cli-oidc/config.yamlとして設定は保存されますので、今後何か変更する際はこのファイルを修正すればOKです。 使い方(Basic) aws-cli-oidc get-cred -p <IdP名>コマンドを実行するとブラウザが起動し、指定したIdPの認証を経て(ここでクレデンシャルの入力となるので、ツールにはクレデンシャルが渡りません)、一時的なAWSアクセスキーが標準出力でコンソールに出力されます(Linux、Macならexport、Windowsならset付きで)。後はこれをコピペして環境変数に設定すれば、aws-cliコマンドが使える、というものです。もちろん、世の中にある他のAWS用のCLIツールも使うことができます。 aws-cli-oidc get-cred -p myop Using config file: /home/wadahiro/.aws-cli-oidc/config.yaml Login successful! Selected role: arn:aws:iam::123456789012:role/developer PrincipalARN: arn:aws:iam::123456789012:saml-provider/myop RoleARN: arn:aws:iam::123456789012:role/developer export AWS_ACCESS_KEY_ID=ASIAT...... export AWS_SECRET_ACCESS_KEY=9bkS0whPelMYQ....... export AWS_SESSION_TOKEN=FQoGZXIvYXdzENz....... また、pbcopyのようなクリップボードコピーのCLIコマンドと組み合わせれば aws-cli-oidc get-cred -p myop | pbcopy のように実行することで一気にクリップボードにコピーすることも可能です(最後の環境変数設定用出力の部分だけが標準出力になっています)。 使い方(Advanced) 2ヶ月ほど前にリリースした v0.6.0では、credential_processに対応しました credential_processを使うと、aws-cliなどのCLIツールが参照する$HOME/.aws/configファイルの中に外部プログラム呼び出しを記述することで、aws-cli-oidcなどの外部のCLIツールから直接一時アクセスキーを渡すことができます。つまり、一々コピペして環境変数に設定する、という作業は不要になるわけです。 設定例 $HOME/.aws/configファイルに以下のようにプロファイルを設定しておきます。 [profile developer] output=json region=ap-northeast-1 credential_process=aws-cli-oidc get-cred -p myop -r arn:aws:iam::123456789012:role/developer -j -s -d 43200 各オプションの説明を補足しておきます。 -r: aws-cli-oidcでどのIAMロールとして一時アクセスキーを取得するか、IAMロールを指定します。 -j: credential_processに対応したJSONデータとして一時アクセスキーを標準出力に出力するようになります。 -s: 一度得た一時アクセスキーをOSのシークレットストアに保存するモードになります。-sをつけないと、例えばaws-cliのコマンドを実行するたびにcredential_processが評価される動きになるため、毎回認証が必要になりかえって使いづらくなります。そこで-sオプションで、一時アクセスキーが有効な間はOSの安全な領域にキャッシュするようにします。 -d: 要求する一時アクセスキーの有効期限(秒)を指定します。ただし、IAMロール側で制限されている場合はその期限を超えて要求はできません。 これで、あとはプロファイルを切り替えてaws-cliなどのCLIツールを実行するだけです!プロファイルの切り替えは色々方法がありますが、例えばexport AWS_PROFILE=developerのように環境変数でプロファイル名を指定します。その後aws sts get-caller-identityを実行し、想定したIAMロールでアクセス可能か確認することができます(この時、ブラウザが起動してお使いのIdPに対して認証が要求されます)。 $ aws sts get-caller-identity { "UserId": "AROA4P2G6HA6APGGSL2BG:foo@example.com", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:assumed-role/developer/foo@example.com" } スイッチロールも簡単! credential_processを使うと、スイッチロールも簡単にできるようになります。先程の設定例のdeveloperプロファイルから、adminプロファイルにスイッチロールする場合、以下のように$HOME/.aws/configファイルに書いておきます。スイッチロール先のプロファイルの定義にsource_profile=developerと書くだけです。 [profile developer] output=json region=ap-northeast-1 credential_process=aws-cli-oidc get-cred -p myop -r arn:aws:iam::123456789012:role/developer -j -s -d 43200 [profile admin] output=json region=ap-northeast-1 role_arn=arn:aws:iam::123456789012:role/admin source_profile=developer 後はadminプロファイルに切り替えるだけです。これで自動的にスイッチロールが行われて各種CLIツールが使えます。以下のように、aws sts get-caller-identityでスイッチロールがうまくできているか確認することができます。 $ export AWS_PROFILE=admin $ aws sts get-caller-identity { "UserId": "AROAIY7G3JPSAU5P2AI1E:botocore-session-1638348595", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:assumed-role/admin/botocore-session-1638348595" } まとめ というわけで NRI OpenStandia Advent Calendar 2021 の2日目の記事として、aws-cli-oidcについて紹介しました。credential_processを利用した使い方にすると、たまにブラウザで認証が求められはしますが、一時的なアクセスキーの存在を忘れて各種CLIツールを利用することができるようになりオススメです。AWSとフェデレーション済みの外部IdPがある方は是非試してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS SAM Go】Go*SAM で環境変数を導入するには

前提 AWS SAM を用いてAWSのリソースをコードで管理できるようにしています。(Infrastructure as Code) 今回はSAMでAWS lambdaを作成し、その上でGoで書かれた関数を実行できるようにしております。 また、今回利用するSAMはAWS SAM テンプレートを使用してサーバーレスアプリケーションを作って作成しました。 環境はdefault環境のみで動作させてます。 結論 ローカル起動時(sam local start-api / sam local invoke)とデプロイ時(sam deploy)とで、環境変数の読み込み方法には違いがあります. ローカル起動時に環境変数を読み込むには 1. env.jsonを利用する方法 ルートディレクトリにenv.jsonの配置 template.yamlにて、Globalsセクション、あるいは、ResourcesセクションのEnvironment > Variables 配下に環境変数のKeyを設置 local起動時に、env.jsonを読み込んでもらうオプションを指定する が必要になります。 ルートディレクトリにenv.jsonの配置 { "Parameters": { "DB_USERNAME": "ユーザー名", "DB_PASSWORD": "パスワード", "HOSTNAME": "ホスト名", "PORT": "ポート名", "DB_NAME": "DB名" } } template.yamlにて、Globalsセクション、あるいは、ResourcesセクションのEnvironment > Variables 配下に環境変数のKeyを設置 Globals: Function: Timeout: 5 Environment: Variables: DB_USERNAME: DB_PASSWORD: HOSTNAME: PORT: DB_NAME: local起動時に、env.jsonを読み込んでもらうオプションを指定する > sam local start-api --env-vars env.json これであとはコード内で環境変数を読み込めばOKです! main.go func Connect() *sql.DB{ db_source_name := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", os.Getenv("DB_USERNAME"), os.Getenv("DB_PASSWORD"), os.Getenv("HOSTNAME"), os.Getenv("PORT"), os.Getenv("DB_NAME")) log.Print(db_source_name) db, err := sql.Open("mysql", db_source_name) または、次のような方法もあります。 2. samconfig.tomlを利用する方法 sam deploy --guidedを行うことで作成されるsamconfig.tomlにstart-apiあるいはlocal-invokeをする際に送られるパラメーターを上書きする方法になります。 version = 0.1 [default] # start-apiする場合 [default.local_start_api.parameters] parameter_overrides = "DbUsername=DB名 DbPassword=パスワード Hostname=ホスト名 Port=ポート名 DbName=DB名" # local invokeする場合 [default.local_invoke.parameters] parameter_overrides = "DbUsername=DB名 DbPassword=パスワード Hostname=ホスト名 Port=ポート名 DbName=DB名" [default.deploy] [default.deploy.parameters] parameter_overridesに、上書きしたいパラメーターを記述します。 注意として、この上書きしたいパラメーターを記述する際のKey名はキャメルケースでないと正常に環境変数を読み込んでくれません。 次に、template.yamlに、パラメータを渡します。 # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 5 Environment: Variables: DB_USERNAME: !Ref DbUsername DB_PASSWORD: !Ref DbPassword HOSTNAME: !Ref Hostname PORT: !Ref Port DB_NAME: !Ref DbName 任意のFunction内でのみ環境変数を渡したいときは以下のようにResourcesの中に書いてあげてください。 Globalsに書くと全てのFunctionsに環境変数が適用されます。 Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: Runtime: go1.x Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: DB_USERNAME: !Ref DbUsername DB_PASSWORD: !Ref DbPassword HOSTNAME: !Ref Hostname PORT: !Ref Port DB_NAME: !Ref DbName これであとはコード内で環境変数を読み込めばOKです! main.go func Connect() *sql.DB{ db_source_name := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", os.Getenv("DB_USERNAME"), os.Getenv("DB_PASSWORD"), os.Getenv("HOSTNAME"), os.Getenv("PORT"), os.Getenv("DB_NAME")) log.Print(db_source_name) db, err := sql.Open("mysql", db_source_name) デプロイ時に環境変数を読み込むには samconfig.yamlにパラメーターを上書きするための記述をする version = 0.1 [default] [default.deploy] [default.deploy.parameters] parameter_overrides = "DbUsername=DB名 DbPassword=パスワード Hostname=ホスト名 Port=ポート名 DbName=DB名" 次に、template.yamlに、パラメータを渡します。 # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 5 Environment: Variables: DB_USERNAME: !Ref DbUsername DB_PASSWORD: !Ref DbPassword HOSTNAME: !Ref Hostname PORT: !Ref Port DB_NAME: !Ref DbName 次に渡すパラメーターの型をParametersセクションに追加します template.yaml Parameters: DbUsername: Type: String DbPassword: Type: String Hostname: Type: String Port: Type: String DbName: Type: String Globals: Function: Timeout: 5 Environment: Variables: DB_USERNAME: !Ref DbUsername DB_PASSWORD: !Ref DbPassword HOSTNAME: !Ref Hostname PORT: !Ref Port DB_NAME: !Ref DbName これであとはコード内で環境変数を読み込めばOKです! main.go func Connect() *sql.DB{ db_source_name := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", os.Getenv("DB_USERNAME"), os.Getenv("DB_PASSWORD"), os.Getenv("HOSTNAME"), os.Getenv("PORT"), os.Getenv("DB_NAME")) log.Print(db_source_name) db, err := sql.Open("mysql", db_source_name) 参照 パラメーターの上書きの仕方 AWS SAM CLI の設定ファイル localで環境変数の読み込ませ方 sam local start-api GLobalな環境変数の指定の仕方と、Resourcesごとの環境変数の指定の仕方 AWS SAM テンプレートの Globals セクション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

udemyを使って資格を取得した話(AWS認定 SAP:ソリューションアーキテクト–プロフェッショナル、Microsoft認定 AZ-900:Microsoft Azure Fundamentals)

Udemyを使って資格を取得した合格体験記 こんにちは。 この記事は、IT資格取得をテーマに学びをシェアしよう!【PR】Udemy Advent Calendar 2021の6日目の記事となります。 今年、AWS認定のソリューションアーキテクト–プロフェッショナルとMicrosoft認定 AZ-900:Microsoft Azure Fundamentalsに合格することができました。取得するためにどのように勉強したかをまとめたいと思います。これから取得しようとしている方がいらっしゃいましたら、参考にしていただけると嬉しいです。 勉強に使用したもの どちらも試験勉強に使用したものはズバリ2つです!それは、、、 教科書とudemyの問題集です! 教科書 用意した教科書は1つの資格あたり、1冊です。金額としては3000円程度になります。 どちらの教科書も詳細にまとめられていて、資格勉強にとても活躍しました。 AWS SAP AWS SAPの勉強で使用した教科書はAWS認定ソリューションアーキテクト-プロフェッショナル ~試験特性から導き出した演習問題と詳細解説です。 AZ-900 AZ-900の勉強で使用した教科書は(模擬問題付き)徹底攻略 Microsoft Azure Fundamentals教科書[AZ-900]対応です。 udemy 上記で紹介した教科書にも問題はついていますが、もっと問題を解きたいと思い、udemyで売られている問題集を購入しました。問題集を解くことで、教科書で学んだことを復習できるため、資格を取得する上でとても良かったと思います。udemyでは定期的にセールを行っており、1500円前後で購入することができます。セールしていたら購入しておくと良いと思います。 AWS SAP AWS SAPの勉強で使用した問題集は、AWS 認定ソリューションアーキテクト プロフェッショナル模擬試験問題集(全5回分375問)です。 AZ-900 AZ-900の勉強で使用した問題集はこれだけで合格!AZ-900: Microsoft Azure Fundamentals模擬試験問題集(7回分430問)です。 勉強方法 勉強方法はどちらも共通して下記の4点になります。 試験概要を確認する 教科書を読む 問題集を解く 復習を行う はじめに、試験概要を必ず確認しましょう。確認ポイントは、どのような問題がでるか試験範囲を確認しましょう。 AWS Certified Solutions Architect - Professional 本試験の受験対象 AWS Certified Solutions Architect - Professional は、AWS でのクラウドアーキテクチャの設計とデプロイにおいて 2 年以上の実践的な経験を持つ個人を対象とするものです。この試験を受ける前に、以下のことをお勧めします。 試験 AZ-900: Microsoft Azure の基礎 この試験の候補には、クラウド サービスに関する基礎知識と、これらのサービスがクラウド サービスでどのように提供されるのMicrosoft Azure。 この試験は、クラウドベースのソリューションとサービスの使用を開始し始め始め、または Azure を初めとする候補を対象にしています。 教科書は最低でも2回は読みましょう。1回目はサラッと目を通す形で、2回目にしっかり読むようにしましょう。個人的に1度、目を通しておくと、2回目読むのが楽になります。教科書についている問題は100%正解になるようにしましょう。 教科書の内容が理解できるようになったら問題集を解きましょう。問題集はたくさん問題あるので、1日1回分問題解く!のようにノルマを決めると良いと思います。私は1日1回分問題を解きました。次の日に前日、間違えた問題を解くようにしてました。問題の意味がわからない時は、教科書を読みましょう。 最後に復習です。教科書と問題集についている問題を全て解いて100%解けるようにしましょう。 udemyの資格勉強するときの使い方 udemyの問題集は問題を全部解かないと、解答が確認できないようになっております。私は1問解いたらすぐに解答を確認したいため、ブラウザを2つ起動し、片方は解答を表示して、もう片方は問題を表示させて問題を解いていました。分けて解くことで、解答がすぐわかるので、間違えた問題がすぐわかります。間違えた問題はメモ帳アプリとかで問題番号をメモしておきましょう。復習のときに間違えた問題だけ解くようにしましょう。 おわりに 資格取得すると、モチベーションにつながるので、定期的に資格を取得することは良いことだと思います。ベンダー試験の問題集は高かったりしますが、udemyで購入するととても安く揃えることができます。引き続き資格勉強をしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分でMFAの管理ができるIAMユーザーの作り方

ごきげんよう、@An_nAです! この記事はハンズラボ Advent Calendar 2021 6日目の記事です。 突然ですが、AWSをご利用のみなさん、MFA(多要素認証)の設定は行なっていますか? アカウントの乗っ取りは怖いですからね、不正ログインを防止するためにも未設定の方はぜひこれを機に設定してくださいね。 AWSのユーザーガイド、よろしければどうぞ。 AWS での多要素認証 (MFA) の使用 自分でMFAの管理ができるようIAMユーザーに権限を付与しよう さてさて、ここからが本題ですよ。 あるうららかな日、AWSアカウント管理者のところにシステム利用者(そうですね、仮にAさんとしましょう)がやってきて言いました。 「AWSマネジメントコンソールにサインインしてやりたい作業があるの」 「今後、Bさんにも同じ作業をお願いするかもしれないわ」 そこで、AWSアカウント管理者はシステム利用者用のIAMグループを作成しました。 このIAMグループにはAさんがやりたい作業に必要な権限をばっちりつけています。 次に、システム利用者用IAMグループに所属するAさん用のIAMユーザーを作成しました。 そして、Aさんにユーザー名、パスワードといったサインイン情報を伝えました。 MFAを設定してくださいね、とお願いを添えて。 ふぅ、一仕事終えましたね。コーヒーでも飲みましょう。 ところがほどなくして、Aさんからこんな連絡が入ります。 「マネジメントコンソールへはサインインできたわ」 「やりたい作業もできてるの」 「だけど、MFAの設定ができないのよね〜」 なぜでしょう? タイトルからお察しですね。 そう、システム利用者用IAMグループに、Aさんがやりたい作業に必要な権限はばっちり付与されていたものの、MFA管理に必要な権限がすぽっと抜けていたんです。 というわけで、前置きが長くなりましたがMFA管理に必要な権限たちをご紹介します! MFA管理を許可するのに必要な権限たち 以下の権限をAllowしてください。 先程の小芝居の例で言うと、システム利用者用IAMグループにこれらの権限がついていればOKです! Action どんな権限? リソース iam:ListVirtualMFADevices 仮想MFAデバイスを割り当て状況別に一覧表示 arn:aws:iam::<アカウントID>:mfa/ iam:CreateVirtualMFADevice 新しい仮想MFAデバイスの作成 arn:aws:iam::<アカウントID>:mfa/${aws:username} iam:DeleteVirtualMFADevice 仮想MFAデバイスの削除 同上 iam:EnableMFADevice MFAデバイスを有効にし、指定されたIAMユーザーと関連付ける arn:aws:iam::<アカウントID>:user/${aws:username} iam:DeactivateMFADevice 指定されたMFAデバイスを無効化&最初に有効化されたIAMユーザーとの関連付けを解除 同上 iam:ResyncMFADevice 指定したMFAデバイスとそのIAMエンティティ(ユーザーまたはロール)を同期 同上 iam:ListMFADevices IAMユーザーのMFAデバイスを一覧表示 同上 IAMコンソールのユーザーページが表示できないんだけど... ここまでにご紹介した権限がシステム利用者用IAMグループに付与済みであれば、このグループに所属しているAさんは自ユーザーのMFAの管理ができるようになっています。 が、しかし。 この権限では、AさんはIAMコンソールからユーザーページを表示することはできないんです。 では、AさんはどこからMFAの設定をすれば良いの? 答えは「セキュリティ認証情報」です。 マネジメントコンソールにサインイン後、ユーザー情報が表示されている右上あたりをクリックすると選べますよ。 「セキュリティ認証情報」をクリックすると、以下の画面に遷移します。 「多要素認証(MFA)」にある「MFAデバイスの割り当て」から設定をしてもらいましょう。 もしAさんに「どうしてもIAMコンソールのユーザーページも見たいわ」と言われたら以下の権限もAllowしてあげてくださいね。 Action どんな権限? リソース iam:ListUsers 指定されたパス・プレフィックスを持つIAMユーザーを一覧表示 arn:aws:iam::<アカウントID>:user/ (おまけ)Serverless Frameworkで書いてみよう ここまでにご紹介した権限を持つIAMグループの定義をServerless Frameworkで書くとこんな感じです。グループ名やポリシー名を適切なものに変更して、あとは許可する作業用の権限を足せばOKです。IAMコンソールのユーザーページ表示の権限は付いていないので、必要に応じて追加してくださいね。 あ!ちなみにこちらはServerless Framework 2.50.0以降の記法です。 お使いのバージョンが2.50.0より前な場合は、こちらのブログが参考になるかもしれません。良かったらご覧ください。 [Tips]Serverless Frameworkでプラグイン「serverless-pseudo-parameters」にさよならバイバイしたお話 !Joinしている理由が気になった方にはこちらがおすすめですよ! [Tips]Serverless Frameworkで${aws:username}と書きたいときのお話 serverless.yml ExampleGroup: Type: AWS::IAM::Group Properties: GroupName: ${self:service}-ExampleGroup Path: / Policies: - PolicyName: ${self:service}-ExamplePolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: iam:ListVirtualMFADevices Resource: arn:aws:iam::#{AWS::AccountId}:mfa/ - Effect: Allow Action: - iam:DeleteVirtualMFADevice - iam:CreateVirtualMFADevice Resource: - !Join - '' - - 'arn:aws:iam::#{AWS::AccountId}:mfa/' - '$' - '{aws:username}' - Effect: Allow Action: - iam:EnableMFADevice - iam:DeactivateMFADevice - iam:ResyncMFADevice - iam:ListMFADevices Resource: - !Join - '' - - 'arn:aws:iam::#{AWS::AccountId}:user/' - '$' - '{aws:username}' 参考資料 AWSのユーザーガイドでは、「MFA認証されていない場合に特定の作業を禁止する」ポリシーの例も紹介されています。 AWS: MFA で認証された IAM ユーザーが My Security Credentials (マイセキュリティ資格情報) ページで自分の認証情報を管理できるようにする IAM: IAM ユーザーに MFA デバイスの自己管理を許可する それでは、よいMFAライフを!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS re:Invent 2021 Amazon Redshift Serverless (Preview) の紹介

はじめに AWS re:Invent 2021 が12/1(水)より始まりました。 今回は、AWS CEO の Adam Selipsky 氏より発表がありました Amazon Redshift Serverless(Preview) について、そちらを簡単にまとめていきたいと思います。 公式のリンクは下記参照 Introducing Amazon Redshift Serverless – Run Analytics At Any Scale Without Having to Manage Data Warehouse Infrastructure Announcing Amazon Redshift Serverless (Preview) Amazon Redshift Serverless について Amazon Redshift とは Amazon Redshift(以下、Redshift) は、AWS が提供するデータウェアハウスサービスです。データウェアハウス(DWH)は、さまざまなデータ元からデータを収集・統合・蓄積し、分析のため保管しておくシステムのことを指します。Redshift の主な特徴は以下となります。 ■Amazon Redshift の特徴 フルマネージド型のサービス ペタバイトスケールのデータウェアハウス 数百ギガバイトのデータから、ペタバイト以上まで拡張が可能 大容量データの読み出し処理に最適 Amazon Redshift Serverless(Preview) とは 今回発表された Amazon Redshift Serverless(Preview)(以下、Redshift Serverless )は、従来の Redshift の機能を有しつつ、サーバレスとなったことでより使い勝手が良くなったかと思います。 Redshift Serverless の主な特徴は以下となります。 ■Amazon Redshift Serverless の特徴 サーバレスとなり、自動で適正なコンピュータ容量をプロビジョニングすることが可能に ノードタイプやノード数の選択、ワークロード管理、スケーリングなどの手動設定が不要なので、 簡単にデプロイとスケーリングが可能 既存のAmazon Redshiftのプロビジョンドクラスタースナップショットをリストアすることが可能 従量課金制 今までの Redshift とは異なり、自動でのプロビジョニングやスケーリングが可能であることと、従量課金となっているのが大きな特徴かと思います。 Amazon Redshift Serverless(Preview) の利用について Redshift Serverless は、現在東京リージョンで利用することが可能です。東京以外の他のリージョンだと、US East (北部バージニア)、US West (北部カリフォルニア、オレゴン)、Europe (フランクフルト、アイルランド) で利用が可能です。 AWS コンソール画面にて Redshift を検索すると、右上に「Try Amazon Redshift Serverless(Preview)」と表示されているので、そこから試して頂くことが可能です。 確認すると、「デフォルト設定」・「カスタマイズ設定」が用意されております。 「デフォルト設定」を選択すると、Redshift Serverless を立ち上げるために、既存の VPC や SG 等が既に選択されているので、その内容で問題なければ、右下にある「作成」を押せば Redshift Serverless 作成は完了します。特に細かい設定を考える必要も無いので、試しの利用としても非常に扱いやすいかと思います。 「カスタマイズ設定」を選択することで、VPC や SG 等をユーザの方で選択していただくことが可能です。 おわりに Amazon Redshift Serverless(Preview) についての紹介は以上となります。 他にも様々な AWS の新サービスや機能の発表が re:Invent の Keynoteで行われているので、是非チェックしてみて下さい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cisco Secure Dynamic Attributes Connector (CSDAC) を使ってみた

はじめに この記事はFUJITSU Advent Calender 2021の2日目の記事です。 Advent Calender初投稿になります。 まずCSDACってなんやねーんと思う人が多いはず。 CSDACは他にも複数ありますがCisco Secure Firewallの7.0から追加された機能の一つです。 今回は出たばかりなのもあり、調べても情報が少ないので使ってみた所感や見え方、動作を確認していきます。 また、画像多めですがお許しください。 Cisco Secure Dynamic Attributes Connector (CSDAC) は、クラウドプロバイダーからデータ(ネットワークやIPアドレスなど)を収集し、Firepower Management Center (FMC) に送信することで、アクセス制御ルールに使用することができます。 長々と解説は省きますが詳細が気になる方はここが参考になります。 Cisco Secure Dynamic Attribute Connector (CSDAC) 動的にAWSやAzureで生成されたIP情報をFMCに教えれる。 →FMCが教えてもらったIPで制御を行う事ができる。 割愛した説明はこんな感じ。 環境 ・Cisco Firepower Threat Defense for VMware Version 7.0.0 ・Cisco Firepower Management Center for VMware Version 7.0.0 ・Ubuntu 18.04 LTS (CSDACと利用者サーバ) 構成図 利用者→Web Serverへアクセスさせます。 えんじにあ君は勝手にWeb Serverとか作っちゃいます。 やりたい事 1.AWS InstanceのIPをFMCが把握できるのか確認する。  ☞CSDACがちゃんとIP情報渡せてるのか? 2.利用者→Web Server のアクセスが制御できるのか確認する。  ☞IPわかっても制御ちゃんとできるのか? 環境の準備 インストール編は今回は割愛します。 諸々の環境構築は以下の動画を参考にしてます。 Cisco Secure Firewall 7.0 Release - Deployment of CSDAC AWSでInstanceを作成 AWSでWebサーバを建てます。 ここで書いてあるIPを覚えておきます。 タグ情報をCSDACに登録するので、あらかじめ確認しておく。 CSDACの設定 以下の3つが設定できる。 ユニークな値以外は参考にした動画を参考にしてます。 ・Connectors : AWSやAzureへのAccess key等を設定する。 ・Adapters : FMCの情報を登録する。 ・Dynamic Attributes Filters :FMCへ教える対象を設定する。 ここでは「AWS_department_engineering」を作成しています。 OperationはEqualsかContainsを選択できます。 今回はEqualsを選択します。 「Valuesの値が一致したタグの情報を持ってくる」設定になります。 取得できてる~?ヨシ! Equalsを設定した後だとこんな感じでIPを貰えてます。 FMC側 FMCでの制御はこんな感じ。 注目するところは見ずらいですが、IP情報で登録してるわけではなく「Destination Dynamic Attributes」にAWS_department...を登録してるところです。 IPは動的に変わったり増えたりするので、オブジェクトを登録指定してあげてます。 CSDAC側で作成した”Dynamic Attributes Filters”を登録する事になるので、CSDACの設定を先にしましょう。 動作確認(成功パターン) 利用者PCからWeb Serverへのアクセスを行ってみましょう。 アクセスできたAPACHEのよく見る画面が見れました。 他にはWeb ServerへのPingと8.8.8.8へのPingも行って疎通できています。 次はアクセスできないパターンを試してみましょう。 AWSのタグ情報を変更します? CSDACの設定が”Equals”なので、一致してない場合どうなるのか確認してみましょう。 動作確認(失敗パターン) Web serverへのアクセスとPingが通信不可になっています。 さらに、Dynamic Attributes Filters で取得していたIP情報も見えなくなっています。 続いてタグ情報の一致が条件の”Equals”ではなく、”Contains”を設定した場合も確認します。 設定後? ”Contains”なのでタグ情報が含まれていればいいのでWebserver_hondaEEEEでもIP情報取得できて制御できてますね。 最後に CSDACを触ってみてどんな動き方をするのか確認できた。 不特定多数の利用者がクラウドへのアクセスをしたい時、タグだけルール化してしまえばFMCで制御できるのでわざわざ登録する手間がなくなり幸せになる人が居そう。幸あれ 詰まったところ(番外編) CSDACとAWS間で時間がずれていると認証情報をpassできない。 →Ubuntu のNTPを同期して治ったが、エラーの出方が「An error occurred (AuthFailure) when calling the DescribeInstances operation」なのでNTP同期の関係に気づくのに時間がかかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS][board] AWS Single Sign-On での board への SAML 認証によるログイン設定

弊社では board というサービスを使用して、見積書発行、請求書発行などの業務を行っております。 このサービスは、SAML による SSO ログインに対応しているので、AWS Single Sign-On からログインできるように設定してみました。 board の FAQ には Okta とか Azure AD とかの設定方法は書いてあったけど、AWS Single Sign-On での設定方法は書いてなかったので、メモ的に残しておきます。 AWS SSO へのアプリケーション登録 Applications から New Application として Add a Custom SAML 2.0 application を選択します。 次に表示される画面で AWS SSO SAML metadata file をダウンロードしておきます。 他の箇所は特に必要する必要はないです。 Display name と Description だけ、お好みで変更してください。 board でのシングルサインオン設定 マスターアカウントで「シングルサインオン」アドオンを有効にする必要があります。 アドオンを有効にしたら、あとはマスターアカウントの権限は必要ないため、管理者権限があるアカウントで設定可能です。 参考: SAML認証によるシングルサインオン(SSO) 管理者権限のあるアカウントで「組織設定」 - 「シングルサインオン設定」を選択します。 ID プロバイダ設定 SAMLメタデータとして AWS SSO で作成したアプリケーションからダウンロードした AWS SSO SAML metadata file をアップロードして、「登録」をクリックします。 サービスプロバイダー情報 「サービスプロバイダー情報」タブにある以下の情報をコピーしておいてください。 ACS URL Audience SSO動作モード 「SSO動作モード」タブで「通常ログイン・シングルサインオン併用」を選択して「登録」をクリックしてください。 全てのユーザの AWS SSO からのログインが確認できたら、この設定は「シングルサインオンのみ」に変更した方が良いでしょう。 board 側の設定はこれで完了です。 AWS SSO アプリケーションの設定変更 Application metadata の設定 AWS SSO の管理画面に戻って、先ほど作成したアプリケーションの設定を変更します。 Application metadata を以下のように設定します。 Application ACS URL : board の「サービスプロバイダー情報」の ACS URL Application SAML audience : board の「サービスプロバイダー情報」の Audience Attribute mappings の設定 以下のように設定します。 User attribute in the application Maps to this string value or user attribute in AWS SSO Format Subject ${user:email} persistent last_name ${user:familyName} unspecified first_name ${user:givenName} unspecified email ${user:email} unspecified これで、設定完了です。 AWS SSO から board にログインできるようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サーバーレスでWebシステム作った話

初めて記事を書きます! フリーランス2年目のシステムエンジニアです。 不慣れな部分ありますが、温かい目でお願いしますm(_ _)m また、次回に自己紹介を書こうと思いますw 今回は?? フリーランスエンジニアとして、個人で受けた初仕事としてユーザー管理システム的なものを作りました。 そのシステムをどのようなアーキティクチャー、技術で作ったかを紹介したいと思います。 ※サービス名はここでは伏せさせてもらいます、、、 仕事を受けた時の話、「こんなWebサービスつくりたんだよね〜」って言われて、色々ヒアリング開始! ベンチャー企業だったということもあり社内で使えるサーバーも保守、運用できる人員もいないので、 当時、私がAWSの勉強中だったこともあり、サーバーレスで作ってみようと提案し進めることになりましたw システム構成 さっくり描くとこんな感じの構成のものになる。 使用技術と使い方 Kotlin WebAPI実装 TypeScript+React 静的ページ実装 AWS S3 Webサイトのホスティング AWS APIGateway AWS Lambda APIGatewayと合わせてWebAPI AWS Cognito ユーザー認証 AWS CloudFront S3の前段でCDNとして使用 証明書の設定 AWS RDS MySQLを使用 ハマったこと これから描くこと以外にもハマったポイントはたくさんあったのですが、 パッと思い出した範囲で、、、 本番環境と検証環境の分離 ベストプラクティスとして環境ごとにアカウントを分離させる方法があげられますが、 アカウントは1つしか用意できず、どうしようと迷いました。 アカウントは1つで分離された環境を2つ用意、、、 結局、AWSアカウントを分割せず、VPCごとに環境を分割と環境変数で頑張りました。 今後のプロダクトの拡張次第でここら辺は改善したいですね。。。 Cognito ログイン画面をS3にデプロイし、Cognitoで認証、認可。会員用ページに遷移する。 というものにしたかったが、なかなかうまくいかず、、、 認証はうまくされているようなのにURLにパラメータが追加され、会員用ページに遷移されそうで遷移されなくて結構時間かかった。。 納期の関係もあって結局、Amplifyを使ってCognitoとログイン画面を作ったらうまく動いてくれたのでそれで進めました。 Cognito難しい、、勉強のやり直しですね、、、( ;∀;) まとめ 今年1月に企画がスタートして10月に一旦リリース完了しました。 ひとまず、致命的なバグなく動いていて安心しています! 今は、ユーザーから機能の改善案や追加機能の実装を行っています。 実際にユーザーに使ってもらって改善していくはプロダクトが育ってる感じがしていいと実感しています^^ 今回の対応を経て、壁にぶち当たったら文献漁って、解決、たまに諦めるの繰り返しでした。 知識不足をあらためて感じた次第でございます、、、( ;∀;) 今後も精進ですね、、、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ちゃんとしていなかったStepFunctionsワークフローをちゃんとする

この記事はコープさっぽろ オープン社内報 Advent Calendar 2021の4日目の記事です。 この記事は? 動いているStepFunctionsワークフローを「失敗しても簡単に再実行できる」ように修正した記録。 何がしたいの? いくつかのLambdaや外部APIを呼び出しているStepFunctions ワークフロー(以下「ワークフロー」)があります。 これを定期的に動かしているのですが、外部APIの都合でしばしば異常終了してしまいます。 ワークフローは以下のような状況です。 呼び出している外部APIは複数あり、この内のいくつかでたまにエラーが発生します。 基幹システムを呼び出すPutMemberは 同じ条件で2回呼び出すとエラーになります。 可能なものはLambdaの中で自力でリトライを仕掛けていますが、リトライ回数の制限を超えてLambdaがエラーになる場合があります。 こんなにちょいちょい落ちると思っていなかった ワークフローがエラーになった場合、AWS Management Consoleから簡単に再実行できますが、先頭から実行したときに 途中のタスクから再実行はできないので、 Lambdaをワークフローから呼ばれたときと同じパラメーターで手動で一つずつ起動するということをやっていました。 上の図の例の場合、2番めのLambdaから10このLambdaを手動で実行する必要があります。 手動でLambdaを1つずつ起動するのがいい加減面倒になったので、ワークフローの再実行でもエラーにならないように「一度正常終了したタスクは実行をスキップする」をワークフローに組み込みたいと思います。 方針 実行状況を保存するDynamoDB Table(以下「実行状態保存テーブル」)を用意する。 ワークフローの各Lambdaの実行が正常終了したら実行状態保存テーブルに記録する。 ワークフローは各Lambdaの実行前に実行状態保存テーブルを見て、実行済みであればLambdaを呼ばずに次のタスクを実行する。 これらを新しくLambda Functionなどを作ることなく実現する。 これを各Lambdaでやればうまくいくはずです。 この方式を実現したあと、ワークフローがエラーになった時にはこのような運用ができるようになるはずです。 ワークフローでエラーになったLambdaを把握する。 エラーになったLambdaのエラーの原因を取り除く。 エラーの原因を取り除いたあと そのLambdaを再実行できるのであればそのまま ワークフロー先頭から実行する そのLambdaをワークフローから実行しなくてよいのであれば実行状態保存テーブルに「そのLambdaは実行済み」と設定してワークフローを先頭から実行する。 いずれのケースでもワークフローは先頭から実行できるようになります。 まずは準備 実行状態保存テーブルの作成とワークフローのロールの更新 実行状態保存テーブルをDynamoDB Tableとして作成します。 テーブル名 : MemberRegistrationExecution パーティションキー : key(string) ワークフローからDynamoDBにアクセスするので、ワークフローのロールに実行状態保存テーブルへの操作を許可します。 (Cloud Formationテンプレートの抜粋) - Effect: Allow Action: - dynamodb:Query - dynamodb:GetItem - dynamodb:UpdateItem - dynamodb:DeleteItem Resource: - !Sub "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${MemberRegistrationExecutionManagementTableName}" 実装開始 StateMachineを考える この修正を入れる前のワークフローのStateMachineはこのように定義しています。 { "member": { "memberCode": "登録データのユニークキー", : (もろもろの属性) } } ワークフローで実行状態保存テーブルから取得した前回実行状態を持ち回りたいので、StateMachineは以下のように定義します。 (定義しますと書きましたが、このような定義がどこかにあるわけではなく、こうなるように各処理を設定していきます) { "member": { "memberCode": "登録データのユニークキー", : }, "MemberRegistrationExecution" : { "Item": { "key": { "S": "登録データのユニークキー"}, "Lambda Function名": { "S": "終了時刻"} } } } 実行状態保存テーブルから取得したデータを MemberRegistrationExecution というキーで持ち回ります。 実行状態保存テーブルから前回の実行結果を取得する StepFunctionsで用意されているGetItemアクションを使います。 "GetExecutionStateItem": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:getItem", "Parameters": { "TableName": "実行状態保存テーブル名", "Key": { "key": { "S.$": "$.member.memberCode" } } }, "Next": "IsExecutionStateExists", "ResultPath": "$.MemberRegistrationExecution" }, この定義は GetExecutionStateItemという名前のStateは TableName で指定したDynamoDB Tableの key という名前のキーが StateMachineの member.memberCodeと一致する項目をGetItemして 結果をStateMachineの MemberRegistrationExecution に設定する という意味になります。 ちなみに、 "S.$": "$.member.memberCode" プロパティ名に .$ をつけると、StepFunctionsが $.member.memberCode を実際の値に展開してくれます。 実行状態保存テーブルに項目がなければ作る Lambdaの実行結果の実行状態保存テーブルへの書き込みはUpdateItemで行う予定なので、GetExecutionStateItemで値を取れなかった場合は項目を事前に作成しておく必要があります。 "IsExecutionStateExists": { "Type": "Choice", "Choices": [ { "Variable": "$.MemberRegistrationExecution.Item.key", "IsPresent": true, "Next": "IsSkipPutRegistrationStartedAt" } ], "Default": "PutExecutionStateItem", "Comment": "DynamoDBからExecutionStateが取得できない場合は初回実行としてPutItem" }, このChoice Stateは以下の意味です。 StateMachineの MemberRegistrationExecution.Item.key がある場合は次の処理(IsSkipPutRegistrationStartedAt)に行き それ以外の場合は PutExecutionStateItemに行く PutExecutionStateItem StateはDynamoDBに項目をPutItemします。 "PutExecutionStateItem": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:putItem", "Parameters": { "TableName": "実行状態保存テーブル名", "Item": { "key": { "S.$": "$.member.memberCode" } } }, "Next": "IsSkipPutRegistrationStartedAt", "ResultPath": null }, ResultPathにnullを設定しないと、PutItemの結果でStateMachineが書き換わってしまうので忘れずに設定しましょう。後述のUpdateItem、DeleteItemでも同様です。 ワークフローに上記を追加してWorkflow Studioで表示するとこのようになります。 実行状態に次のLambdaの完了が記録されていればLambdaをスキップし、無ければLambdaを実行する 前のステップでStateMachineのMemberRegistrationExecutionに実行状態が保存されているはずなのでそれをChoiseステートで確認します。 "IsSkipPutRegistrationStartedAt": { "Type": "Choice", "Choices": [ { "Variable": "$.MemberRegistrationExecution.Item.PutRegistrationStartedAt", "IsPresent": true, "Next": "IsSkipIsDuplicatedBeforeReg" } ], "Default": "PutRegistrationStartedAt" }, このChoice Stateは以下の意味です。 StateMachineの MemberRegistrationExecution.Item.PutRegistrationStartedAt がある場合は次の処理(IsSkipIsDuplicatedBeforeReg)に行き それ以外の場合は PutRegistrationStartedAtに行く 次にLambdaの実行が完了したら実行状態保存テーブルに書き込むStateを作ります。 "UpdateStatePutRegistrationStartedAt": { "Type": "Task", "Resource": "arn:aws:states:::dynamodb:updateItem", "Parameters": { "TableName": "テーブル名", "Key": { "key": { "S.$": "$.member.memberCode" } }, "UpdateExpression": "SET #key = :val", "ExpressionAttributeNames": { "#key": "PutRegistrationStartedAt" }, "ExpressionAttributeValues": { ":val": { "S.$": "$$.State.EnteredTime" } } }, "Next": "IsSkipIsDuplicatedBeforeReg", "ResultPath": null }, このStateは以下の意味です。 テーブル名 にupdateItemを実行する。 KeyはStateMachineのmember.memberCode プロパティPutRegistrationStartedAtに値としてこのStateの開始時刻を設定する $$はStepFunctiosのコンテキストオブジェクトを示します updateItemの結果はStateMachineに反映しない これを追加したWorkflow Studioの定義は以下のようになります。 これをスキップしたいLambdaに順次組み込んでいきます。 最後に実行状態保存テーブルから状態を削除する ワークフローが最後まで実行されたら実行状態保存テーブルから項目を削除します。 "DeleteExecutionStateItem": { "End": true, "Parameters": { "Key": { "key": { "S.$": "$.member.memberCode" } }, "TableName": "dev-deli-websubscription-MemberRegistrationExecution" }, "Resource": "arn:aws:states:::dynamodb:deleteItem", "Type": "Task", "ResultPath": null }, ここについては、実行結果を保存するテーブルなので残しておいてもよいかもしれませんが、削除するタイミングがないでここで削除してしまいます。 残していたとして、ワークフローを再実行したら全部スキップされるから意味もないですし。 実行結果のエビデンスはStepFunctions自身のステータスを見るということにします。 完成! 試してみる いい感じにエラーになりました。 4つ目のLambda RefreshAuth0Tokens でエラーになっています。 実行状態保存テーブルを見てみます。 成功した3つのLambdaだけ書き込まれています。 では何も手を加えずにワークフローを再実行してみます。 前回成功している最初の3つのLambda PutRegistrationStartedAt,IsDuplicatedBeforeReg, PutMemberが想定通りスキップされています! ワークフローが全て完了したら実行状態保存テーブルから項目が削除されているのも確認できたので作戦大成功です。 まとめ 深く考えずにワークフローの途中から実行が実現できたので運用負荷が軽くなったはず。 ロジック本体(Lambda)と実行状態の管理を分離して定義することができた。 JSONPath構文むずい。 ワークフローのテストむずい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【速報】新たに発表されたAmazon SageMaker Canvasについて紹介!

はじめに こんにちは、ナレコムの井手です。 11月30日からAWS最大イベントre:Invent 2021が始まりました! 今年は2年ぶりにラスベガスでの現地開催もあり、大変盛り上がっているようです。 On the ground in #Vegas... Incredible innovations, customer stories, and partner highlights soon to be revealed at the 10th #AWS #reInvent ! pic.twitter.com/kXFd1LTsxR— Adam Selipsky (@aselipsky) November 28, 2021 今回は、本日発表された「Amazon SageMaker Canvas(以下、「SageMaker Canvas」と称する)」をご紹介していきたいと思います。 なにやら非エンジニアでも手軽に使用できるサービスになっていることで、非常に興味深い情報がたくさんでした!! 目次 1.そもそもAmazon SageMakerとは 2.Amazon SageMaker Canvasを紹介! 3.ドラッグ&ドロップユーザーインターフェイスを採用 4.おわりに 1. そもそもAmazon SageMakerとは まずは、そもそもAmazon SageMaker(以下、「SageMaker」と称する)とは何か?に関しての振り返りをしたいと思います。 SageMaker は機械学習モデルを支援するMLサービスです。 SageMakerには既にモジュールが容易されているため、手軽かつスピーディーに機械学習を行うことができます。 Amazon SageMaker は、ML 専用に構築された幅広い一連の機能をまとめて提供することにより、データサイエンティストとデベロッパーが高品質の機械学習 (ML) モデルを迅速に準備、構築、トレーニング、およびデプロイするのを支援します。 SageMakerの特徴は以下になります。 ・フルマネージド型 ・機械学習モデルの構築やトレーニングが簡単に可能 ・既存の機械学習アルゴリズムも利用可能 SageMakerに関して詳しく知りたい方は以下をご参照ください。 ナレコムAWSレシピ SageMaker編 2. Amazon SageMaker Canvasを紹介! では、今回発表されたSageMaker Canvasをご紹介していきます。 SageMaker Canvasは、コードを記述したり、機械学習の専門知識を必要とせずに、ビジネスアナリストが正確な機械学習予測をするのに役立つサービスになっています。 従来のSageMakerでは、機械学習への知見やNoteBookへの基本知識、Pythonをはじめとするコード技術、機械学習フレームワーク等の知識が必要でした。 また、非機械学習エンジニアに向けたAmazon SageMaker Jumpstartというサービスもありましたが、やはりNotebookやPythonの知識が必要で、ビジネスアナリストにとってはハードルの高いサービスとなっております。 今回のSageMaker Canvasでは、従来のサービスのハードルを下げたものとなっており、誰でも簡単に最適な予測モデルを特定し、組み込みのレポートとして独自のインサイトを得ることができます。 3.ドラッグ&ドロップユーザーインターフェイスを採用 UIもより使いやすいものに進化しており、ドラッグ&ドロップユーザーインターフェイスを採用。 データセットをUIにインポートするだけで、簡単にモデルが作れちゃうようです。 では、UIに関してさらっと見ていきましょう!! (インポート画面) 画面の「+import」よりCSVデータをインポートします。 もちろん、S3やAmazonRedshiftなどのAWSサービスやSnowflakeなどの他のクラウドまたはオンプレミスのデータソースに接続したりすることもできます。 (データビュー画面) データをインポートしたあとは、データの列や値に関してプレビューすることができます。 (データ選択画面) データの確認後(結合が終わった後)、結果の列を視覚化し、欠落している値または無効な値を識別し、オプションで不要な列の選択を解除することができます。 この画面ではデータセットに新しい名前を付けます。 次に、モデルを作成し、左側のメニューの[model]セクションで[new model]を選択します。 (データセット選択画面) ここでは、データセットを選択していきます。 SageMaker Canvasは値の分布を示しており、適切なモデルタイプである2つのカテゴリ分類を推奨しています。 (分析レポート画面) モデルトレーニングの前に分析レポート生成することができます。 分析により、予測の精度や各列の相関関係などを得ることができます。 (モデル評価画面) 出来上がったモデルはダウンロード後Amazon SageMaker Studio(機械学習のための完全統合開発環境 (IDE))に入れてエンジニアに連携することもできれば、その場で対象データを入力して推論することもできます。 4. おわりに 現時点での情報ではありますが、SageMaker Canvasについて紹介していきました。 使用できるリージョンは、オハイオ、バージニア、オレゴン、フランクフルト、アイルランドと限定されているようですが、かなり強力なサービスであるので実装が待ち遠しいですね。 今後は実際に触ってみて、よりサービスについて深く発信できればと思います。 参考リンク AWS公式ページ Amazon SageMaker AWS公式ページ Amazon SageMaker Canvas AWS公式ページ Amazon SageMaker Studio
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Contact Lens for Amazon Connectが日本語対応したので試してみた

はじめに KDDI Engineer&Designer Advent Calendar 2021の7日目の記事です。 今回はContact Lens for Amazon Connectというサービスを取り上げたいと思います。 簡単にいうと、Amazon Connectの拡張サービスで、音声データからテキストの書き起こしや、感情分析の機能を提供するものです。 サービス自体は2019年12月に発表されたのですが、2021年9月になってようやく日本語対応されました。 なお、テキスト書き起こしの音声認識エンジンはAmazon Transcribeが、感情認識エンジンはAmazon Comprehendが裏で利用されているようですが、使う側からすればそれらはあまり意識する必要はなく、Amazon Connectに組み込むことが可能です。 ということで、さっそくこれを試してみたいと思います。 なお、Amazon Connectについて前提知識がない方は、今までにいくつか記事を書いてますのでよろしければご覧ください。 【3分でわかる】Amazon Connectは何がすごいのか? 【ハンズオン】Amazon ConnectからLambdaを実行しEC2インスタンスを操作する 【超簡単】Amazon Connectを使って無料で電話する方法 Connect Lens for Amazon Connectを組み込む準備 必須設定 すでにAmazon Connectのインスタンスは作成済みで、ひととおり問い合わせフローは作成できている前提でスタートします。 まずは、Amazon Connectのインスタンスに対して、「Analytics tools」から「Contact Lensを有効にする」をチェックします。 次にAmazon Connectインスタンスの中身の操作に入ります。 「問い合わせフロー」の途中に「記録と分析の動作を設定」を組み込みます。 そして、この「記録と分析の動作を設定」の中身を設定します。 まず「通話記録」に関しては「オン」にして「エージェントおよび顧客」を選択します。 そして、「Contact Lensの音声分析を有効にします。」をチェックします。 「通話後の分析」と「リアルタイムおよび通話後の分析」が選べますが、前者は通話終了後のバッチ処理になるので認識精度はやや上がり、後者はリアルタイム処理になるので認識精度はやや下がるようです。 ここでは、前者の「通話後の分析」を選択することにします。用途によってここは変更してください。 また、言語に関しては「日本語」を設定して「Save」します。 次に「セキュリティプロファイル」からユーザーが使うロールに対して必要な権限を与えます。 以下の9つについてチェックすればよいようです。 これで一通りの準備は完了です。 任意設定(キーワード検出ルール) ここでは、キーワードの検出ルールの設定を行っいます。この設定は必須ではありません。 まず「ルールを作成」から「Contact Lens」をクリックします。 そして、ここでは顧客またはオペレータのいずれかから「解約」という言葉が発せられた場合に検知するルールを作成したいと思います。 以下のように設定して、ルール名を名付ければOKです。 シナリオを組んでテストする。 これで準備が整いましたので、実際に電話をかけて、顧客とオペレータを模した通話を試してみたいと思います。 コンタクトセンターでありがちなシナリオを想定して台本を書いてみました。 なお、実際に通話をするので協力者が必要になります。 今回は、@minorun365に顧客役をお願いしました。 オペ:お電話ありがとうございます。本日はどのようなお問い合わせでしょうか? 顧客:回線を解約したいです。 オペ:解約ですね。少々おまちください。 ---保留(5秒程度)--- オペ:お待たせしました。契約の確認ができました。解約してもよろしいでしょうか? 顧客:はい。大丈夫です。 オペ:では手続きします。このまましばらくお待ちください。 ---保留(5秒程度)--- オペ:お待たせしました。解約手続きが完了しました。 顧客:ありがとうございます。 オペ:手続きは以上となります。ほかにご質問はございますか? 顧客:特にないです。 オペ:ではこれで終了となります。ありがとうございました。 実際にやってみたデータは「問い合わせの検索」から確認することができます。 なお、通話録音データは通話終了後2~3分ほどで再生できるようになりましたが、テキスト書き起こしデータと感情分析データは10分ほど経ってから遅れて参照できるようです。 まず、顧客感情の傾向や、どちらが喋っている時間が長いかといったデータがざっくりとグラフ化されます。 また、通話録音データの再生も可能です。0.5倍速、2倍速、3倍速も選べます。 通話録音データで顧客側が赤く表示されてるということは、どちらかというとネガティブな通話であるということが分かります。 (Amazon Comprehendは、顧客感情をテキストの内容で判断していて、声色などでは判断していないようです) 次に、テキストの書き起こしです。 冒頭のあいさつ部分が切れてしまいましたが、「解約」というワードはちゃんと拾えてフラグが立っていました。 また、顔文字で顧客の感情が表現されていますが、これは参考程度にしかならないでしょう。 なお、顧客側の音声認識に「フォルド」「ホルド」という謎の認識ワードがあったのですが、これはオペレータ側で保留ボタンを押した際、「You are on hold(保留中です)」という言葉がAmazon Pollyによって読み上げられてしまい、それを顧客側の音声として拾ってしまったようです。 会話自体は、ちゃんとキャッチボールする形で順番に読み上げたのですが、結果をみると会話のキャッチボールが連結されてしまってる部分もあり、チグハグ差がみえますね。 このあたりは、今後の改善に期待しましょう。 さいごに ということで、Contact Lens for Amazon Connectを試してみました。 私は以前、コンタクトセンターのシステムを構築する仕事をしていましたが、通話録音、音声認識、感情認識といったオプション機能を組み込むのは結構大変です。 それが1時間もかからないくらいで、ここまで組み込むことができました。 ミッションクリティカルで大規模なコンタクトセンターで使うにはまだまだ心許ないかもしれませんが、比較的小規模なコンタクトセンターならこれで十分のようにも思えます。 今後もAmazon Connectや、その周辺のアップデートに期待したいと思います。 参考サイト https://tech.systems-inc.com/col-aws-cn2/ https://dev.classmethod.jp/articles/contact-lens-for-amazon-connect-japanese/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS DevOps - CloudWatchのダッシュボードをAuto Scalingに追従して自動で設定する

はじめに Auto Scaling グループの各インスタンスのCPU使用率を一目で確認したい場合、CloudWatchのダッシュボードを作成して各インスタンスのCPU使用率を表示するウィジェットを追加することで実現可能です。 しかし、スケールアウト、スケールインに合わせてダッシュボードの内容を動的に変更する機能は本記事の投稿日の時点では存在しないため、スケールアウト、スケールインの度にダッシュボードの対象インスタンスを追加、削除する必要があります。これを手動で実施するのは手間なので、Lambda関数で自動化する方法を紹介します。 なお、今回紹介する方法はCPU使用率のみでなく、ディスクI/Oや、ネットワークI/O、メモリ使用率、ディスク使用率といったメトリクスを確認したい場合にも応用可能ですので参考にしていただければと思います1。 課題 下図のようなAuto Scaling グループを管理しているとします。 Auto Scaling グループには以下3台のEC2インスタンスが存在します。 各EC2インスタンスのCPU使用率を確認したいのですが、Auto Scaling グループのCloudWatch メトリクスでは、以下のように全インスタンスの平均CPU使用率しか確認できません。 そのため、CloudWatchで各EC2インスタンスのCPU使用率を確認したい場合は以下のようにCloudWatchのダッシュボードで各インスタンスのCPUUtilizationを表示するウィジェットを作成する必要があります。 しかし、Auto Scaling グループにおいてスケールアウトが発生した場合、どうなるかを見てみましょう。 実験のためAuto Scaling グループの希望する容量および最小キャパシティを3から5に変更してみます。 少し待つとAuto Scaling グループのインスタンスが2台増えて5台になりました。 一方でダッシュボードのウィジェットに表示されるCPU使用率のグラフは、当たり前ですが、もともと存在した3台分のCPU使用率しか表示されていません。 スケールインの場合も同様で、Auto Scaling グループから削除されたインスタンスがダッシュボードから削除されない状態という問題が発生します。 対処方針 上記のダッシュボードはJSON形式で以下のように定義されています。 Auto Scaling グループのスケールアウト/スケールインをトリガーとしてLambda関数を起動し、LambdaでこのJSONのmetricsの部分にインスタンスIDを追加、または削除することでAuto Scaling グループへの追従を実現します。 { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 6, "height": 6, "properties": { "view": "timeSeries", "stacked": false, "metrics": [ [ "AWS/EC2", "CPUUtilization", "InstanceId", "i-08be7c869e37389d9" ], [ "...", "i-0a37908112466619f" ], [ "...", "i-0e459f75258cde7db" ] ], "region": "ap-northeast-1", "title": "CPUUtilization" } } ] } 構成 Amazon EventBridgeのルールを作成し、イベントをSQSのFIFO キュー経由でLambdaを実行するよう設定します。 LambdaではAWS SDKを使用してCloudWatchのAPIを叩き、ダッシュボードへのインスタンスIDの追加、削除を実施します。 なお、以下でEventBridgeから直接Lambdaを実行せず、SQSのFIFO キューを経由しているのはLambdaの同時実行数を1に制限するためです。 Lambdaの同時実行数を1に制限する理由については後述します。 構築手順 前提 AWS SAMを使用してリソースを作成するため、AWS SAM CLIがインストール済みであることを前提とします。 以下では対象のAuto Scaling グループ自体は既に存在し、ダッシュボードは未作成という状態を想定して手順を記載します。 ダッシュボードおよびウィジェットの作成 ダッシュボードの作成 マネジメントコンソールの CloudWatch > ダッシュボード で「ダッシュボードの作成」をクリックし、ダッシュボード名を入力して「ダッシュボードの作成」をクリックしてダッシュボードを作成します。 ウィジェットの作成 マネジメントコンソールの CloudWatch > ダッシュボード で作成したダッシュボードをクリックします。 「ウィジェットの追加」をクリックします。 「ウィジェットの追加」で「線」をクリックします。 「これをダッシュボードに追加」で「メトリクス」をクリックします。 「メトリクスグラフの追加」で「EC2」をクリックします。 「インスタンス別メトリクス」をクリックします。 検索ボックスに「CPUUtilization」と入力してエンターキーを押下し、表示された検索結果のうち、対象Auto Scaling グループに含まれるインスタンスのチェックボックスをすべてチェックして「ウィジェットの作成」をクリックします。 ダッシュボードの画面に戻るので、作成されたウィジェットの名前の横の編集ボタンをクリックして「ウィジェットの名前変更」画面でウィジェット名を入力して「適用」をクリックします。ウィジェット名を変更する必要が無い場合は、編集ボタンをクリック後、そのまま「適用」をクリックしてください。本記事の投稿日の時点で、本手順を実行しないとダッシュボードのソースで作成したウィジェットにtitleが作成されず、以下で作成するLambdaが正常に動作しません。 sam initの実行 任意のディレクトリで以下のコマンドを実行してサーバーレスアプリケーションのイニシャライズを行います。 $ sam init --package-type Zip --runtime python3.9 --name AutoScalingCloudWatchLambda --app-template hello-world SAMテンプレートの作成 AutoScalingCloudWatchLambdaディレクトリに作成されたtemplate.yamlを以下の内容に変更します。 なお、SAMテンプレート中に登場するEventBridgeのイベントパターン(AutoScalingCloudWatchEventBridgeのEventPattern)、デッドレターキュー(AutoScalingCloudWatchFIFOSQSDLQ)、SQSのアクセスポリシー(AutoScalingCloudWatchFIFOSQSPolicy)については後述の補足にて詳細を解説していますので必要に応じてご参照ください。 template.yaml AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > AutoScalingCloudWatchLambda SAM Template for AutoScalingCloudWatchLambda Parameters: AutoScalingGroupName: Type: String DashboardName: Type: String WidgetName: Type: String Resources: AutoScalingCloudWatchFunction: Type: AWS::Serverless::Function Properties: FunctionName: auto-scaling-cloudwatch-lambda CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Timeout: 30 Environment: Variables: DASHBOARD_NAME: !Ref DashboardName WIDGET_NAME: !Ref WidgetName Policies: - Statement: - Sid: CloudWatchDashboardPolicy Effect: Allow Action: - cloudwatch:PutDashboard - cloudwatch:GetDashboard Resource: !Sub arn:${AWS::Partition}:cloudwatch::${AWS::AccountId}:dashboard/${DashboardName} Events: AutoScalingCloudWatchFIFOSQSEvent: Type: SQS Properties: Queue: Fn::GetAtt: - AutoScalingCloudWatchFIFOSQS - Arn BatchSize: 1 AutoScalingCloudWatchFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub - /aws/lambda/${FuncName} - {FuncName: !Ref AutoScalingCloudWatchFunction} RetentionInDays: 365 AutoScalingCloudWatchFIFOSQS: Type: AWS::SQS::Queue Properties: FifoQueue: true ContentBasedDeduplication: true QueueName: auto-scaling-cloudwatch-sqs.fifo VisibilityTimeout: 30 RedrivePolicy: deadLetterTargetArn: Fn::GetAtt: - AutoScalingCloudWatchFIFOSQSDLQ - Arn maxReceiveCount: 1 AutoScalingCloudWatchFIFOSQSPolicy: Type: AWS::SQS::QueuePolicy Properties: Queues: - !Ref AutoScalingCloudWatchFIFOSQS PolicyDocument: Version: "2012-10-17" Id: AutoScalingCloudWatchFIFOSQSPolicy Statement: - Sid: AllowSendMessage Action: - sqs:SendMessage Effect: Allow Resource: Fn::GetAtt: - AutoScalingCloudWatchFIFOSQS - Arn Principal: AWS: "*" Condition: ArnEquals: aws:SourceArn: Fn::GetAtt: - AutoScalingCloudWatchEventBridge - Arn AutoScalingCloudWatchFIFOSQSDLQ: Type: AWS::SQS::Queue Properties: FifoQueue: true QueueName: auto-scaling-cloudwatch-sqs-dlq.fifo MessageRetentionPeriod: 60 AutoScalingCloudWatchEventBridge: Type: AWS::Events::Rule Properties: Name: auto-scaling-cloudwatch-eventbridge-rule EventPattern: source: [ aws.autoscaling ] detail-type: - EC2 Instance Launch Successful - EC2 Instance Terminate Successful detail: AutoScalingGroupName: [ !Ref AutoScalingGroupName ] Targets: - Arn: Fn::GetAtt: - AutoScalingCloudWatchFIFOSQS - Arn Id: Fn::GetAtt: - AutoScalingCloudWatchFIFOSQS - QueueName SqsParameters: MessageGroupId: auto-scaling-cloudwatch-message-group RetryPolicy: MaximumRetryAttempts: 0 Lambda関数の作成 AutoScalingCloudWatchLambda/hello_world/app.pyを以下の内容に変更します。 app.py import boto3 import json from os import getenv from logging import getLogger cw = boto3.client('cloudwatch') logger = getLogger(__name__) dashboard_name = getenv('DASHBOARD_NAME') widget_name = getenv('WIDGET_NAME') def lambda_handler(event, context): instance_id = '' event_type = '' try: auto_scaling_event = json.loads(event['Records'][0]['body']) instance_id = auto_scaling_event['detail']['EC2InstanceId'] event_type = auto_scaling_event['detail-type'] except Exception as e: logger.error('Failed to get Information. [event] ' + json.dumps(event)) raise(e) if event_type == 'EC2 Instance Launch Successful': add_instance_to_dashboard(instance_id, logger) elif event_type == 'EC2 Instance Terminate Successful': delete_instance_from_dashboard(instance_id, logger) return def add_instance_to_dashboard(instance_id, logger): try: #ダッシュボード情報取得 dashboard_body_dic = get_dashboard(dashboard_name) #対象インスタンスをダッシュボードのウィジェットに追加 for widget in dashboard_body_dic['widgets']: widget_prop = widget['properties'] if widget_prop['title'] == widget_name: #冪等性確保のため、当該インスタンスIDが無かったらappend i = -1 for i, metric in enumerate(widget_prop['metrics']): target_instance_id = metric[-1] if(target_instance_id == instance_id): break if i == -1: #metricsリストが空 widget_prop['metrics'].append([ "AWS/EC2", "CPUUtilization", "InstanceId", instance_id]) elif i == len(widget_prop['metrics']) - 1: #該当インスタンスIDなし widget_prop['metrics'].append(["...", instance_id]) put_dashboard(dashboard_name, json.dumps(dashboard_body_dic)) except Exception as e: logger.error('Failed to add the instance to the dashboard. ' + '[DashboardName] ' + dashboard_name + ' [InstanceId] ' + instance_id + ' [Exception] ' + str(e)) raise(e) return def delete_instance_from_dashboard(instance_id, logger): try: #ダッシュボード情報取得 dashboard_body_dic = get_dashboard(dashboard_name) #対象インスタンスをダッシュボードのウィジェットから削除 for widget in dashboard_body_dic['widgets']: widget_prop = widget['properties'] if widget_prop['title'] == widget_name: i = -1 for i, metric in enumerate(widget_prop['metrics']): target_instance_id = metric[-1] if(target_instance_id == instance_id): break if i >= 0: #対象インスタンスがmetricsリストに存在 if(i == 0 and len(widget_prop['metrics']) > 1): #metricsリストの要素が2つ以上かつ対象インスタンスがmetricsリストの先頭 widget_prop['metrics'][i][-1] = widget_prop['metrics'][i+1][-1] widget_prop['metrics'].pop(i+1) else: widget_prop['metrics'].pop(i) put_dashboard(dashboard_name, json.dumps(dashboard_body_dic)) except Exception as e: logger.error('Failed to delete the instance from the dashboard. ' + '[DashboardName] ' + dashboard_name + ' [InstanceId] ' + instance_id + ' [Exception] ' + str(e)) raise(e) return def get_dashboard(DashboardName): try: response = cw.get_dashboard( DashboardName = DashboardName ) dashboard_body_json = response['DashboardBody'] return json.loads(dashboard_body_json) except Exception as e: msg = 'Failed to get the dashboard information. Detail: ' + str(e) raise(e) def put_dashboard(DashboardName, DashboardBody): response = cw.put_dashboard( DashboardName=DashboardName, DashboardBody=DashboardBody ) if len(response['DashboardValidationMessages']) != 0: msg = 'Failed to update the dashboard.' if('DataPath' in response['DashboardValidationMessages']): msg += ' DataPath: ' + response['DashboardValidationMessages']['DataPath'] if('Message' in response['DashboardValidationMessages']): msg += ' Message: ' + response['DashboardValidationMessages']['Message'] raise Exception(msg) return sam deployの実行 以下のようにsam deploy --guidedを実行してサーバレスアプリケーションのデプロイを実行します。 $ sam deploy --guided Configuring SAM deploy ====================== Looking for config file [samconfig.toml] : Not found Setting default arguments for 'sam deploy' ========================================= Stack Name [sam-app]: auto-scaling-cw-lambda AWS Region [ap-northeast-1]: Parameter AutoScalingGroupName []: test-asg Parameter DashboardName []: test-dashboard Parameter WidgetName []: CPUUtilization #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: Save arguments to configuration file [Y/n]: SAM configuration file [samconfig.toml]: SAM configuration environment [default]: なお、パラメータAutoScalingGroupName、DashboardName、WidgetNameはそれぞれ対象のAuto Scaling グループ名、対象のCloudWatch ダッシュボード名、対象のウィジェット名を指定してください。 動作確認 Auto Scaling グループには以下3台のEC2インスタンスが存在します。 CloudWatchのダッシュボードには3台のインスタンスのCPU使用率が表示されています。 Auto Scaling グループの希望する容量および最小キャパシティを3から5に変更してみます。 少し待って、インスタンスが5台になってからダッシュボードを確認すると、追加されたインスタンスのCPU使用率も表示されています。 続けてAuto Scaling グループの希望する容量および最小キャパシティを5から1に変更してみます。 少し待って、インスタンスが1台になってからダッシュボードを確認すると、残った1台のインスタンスのCPU使用率のみ表示されています。 これで、CloudWatchのダッシュボードをAuto Scalingに追従して自動で設定するという目的を達成できました。 補足 EventBridgeのイベントパターンについて 以下を指定することで、対象オートスケーリンググループのスケールアウトによるインスタンス追加時およびスケールインによるインスタンス削除時にSQSにイベントを送信することができます。SAMテンプレートではこれをAWS::Events::RuleのEventPatternにYAML形式で指定しています。 { "detail-type": ["EC2 Instance Launch Successful", "EC2 Instance Terminate Successful"], "source": ["aws.autoscaling"], "detail": { "AutoScalingGroupName": ["<対象オートスケーリンググループ名>"] } } SQSについて SQSのFIFO キューを使用する理由 EventBridgeでは直接イベントをLambdaに送信することもできるので、なぜSQSのFIFO キューを経由するのか疑問に思う方がいるかもしれません。そのため、ここで理由を説明します。 今回作成するLambda関数では、以下の処理を行います。 get_dashboard()によるダッシュボードのJSON取得、JSON編集 put_dashboard()によるダッシュボード更新 上記を踏まえた上で、Auto Scalingによるスケールアウトで2台のインスタンスが追加され、Lambda関数が同時に実行されるケースを考えると時系列で以下の処理順となった場合にダッシュボードの状態が不正となります。以下では追加インスタンス1の延長で起動したLambdaをLambda①、追加インスタンス2の延長で起動したLambdaをLambda②とします。 [Lambda①] get_dashboard()によるダッシュボードのJSON取得、JSON編集 [Lambda②] get_dashboard()によるダッシュボードのJSON取得、JSON編集(※) [Lambda①] put_dashboard()によるダッシュボード更新 [Lambda②] put_dashboard()によるダッシュボード更新(※) ※ダッシュボードのJSONを取得した後に当該ダッシュボードが更新され、その後に古いJSONをもとに編集したJSONでダッシュボード更新するので追加インスタンス1の情報が消えてしまう。 スケールイン時も同様です。 そのため、Lambda関数の同時実行数を1に制限する必要があります。 Lambdaの同時実行数を1に制限するという要件は、SQS の FIFO キューを Lambda 関数のトリガーとすることで達成できます。2 デッドレターキュー SQSによってトリガーされたLambda関数がエラーになった場合、 今回はエラー時にリトライせずにイベントを破棄したいので、AutoScalingCloudWatchFIFOSQSのRedrivePolicyでmaxReceiveCountを1にして、エラー発生時にメッセージを即デッドレターキューに移動するように設定し、移動先のデッドレターキューAutoScalingCloudWatchFIFOSQSDLQのMessageRetentionPeriodを最小値の60とすることで、最短で破棄する挙動としています。実際に使用する場合は保持期間を長くする、デッドレターキューにメッセージが移動されたら通知するよう設定する等、要件に合わせて修正してください。 アクセスポリシー EventBridgeからSQSのキューにイベントを送信するためには、対象キューのアクセスポリシーで以下のようにEventBridgeのルールに対してsqs:SendMessageを許可する必要があります。SQSキューのアクセスポリシーはSAMテンプレートではAWS::SQS::QueuePolicyというリソースで定義します。 { "Version": "2012-10-17", "Id": "AutoScalingCloudWatchFIFOSQSPolicy", "Statement": [ { "Sid": "AllowSendMessage", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "sqs:SendMessage", "Resource": "<SQSキューのARN>", "Condition": { "ArnEquals": { "aws:SourceArn": "<EventBridgeのルールのARN>" } } } ] } まとめ Lambda関数を使用してCloudWatchのダッシュボードをAuto Scalingに追従して自動で設定する方法を紹介しました。 Auto Scaling グループの各インスタンスのCPU使用率やその他のメトリクスをダッシュボードで確認したいというケースがありましたらご活用いただければ幸いです。 EC2インスタンスのメトリクスについて、CPU使用率、ディスクI/O、ネットワークI/Oは特別な設定なしにCloudWatchで取得可能ですが、メモリ使用率、ディスク使用率を取得するためにはCloudWatch エージェントのインストールが必要です。詳細はCloudWatch エージェントの公式ドキュメントをご参照ください。 ↩ 当初はSAMテンプレートでAWS::Serverless::FunctionのReservedConcurrentExecutionsを1にすることで同時実行数を1に制限できるのでは無いかと思ったのですが、ReservedConcurrentExecutionsはあくまでも同時実行数を予約するための設定(User Guide参照)であり、指定した実行枠が確保されるというものです。ReservedConcurrentExecutionsが1のときに同時実行数が1となることが保証される(2以上とならない)というドキュメント記載は見当たらず、AWSサポートに問い合わせてみたところ、ReservedConcurrentExecutionsを1にしても同時実行数が1であることを保証するようなドキュメントは無く、代わりにSQSのFIFOキューを使用し、単一のメッセージグループIDを使用することで条件を満たすことができるという回答をいただきました(詳細はAWS Compute Blogの該当記事参照)。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS DevOps - TaskCat を使って CloudFormation テンプレートの自動テストをしよう

はじめに みなさん、こんにちは。今回は AWS TaskCat というオープンソースを利用した AWS CloudFormation (CFn) テンプレートの自動テストについてのお話です。 CFn テンプレートを扱っていると構文エラーチェックはパスしたものの、いざ動かしてみたらスタックの作成でエラーになってしまうといった経験をすることがあるかと思います。AWS TaskCat は多くの方にとってあまり馴染みのないツールだと思いますが、実際に使ってみるととても手軽に CFn テンプレートの自動テストをすることができます。 今回は Linux 上に開発環境を作るところからはじめて、簡素なサンプルを用いたテストの実行、テスト自動化を組み込んだシンプルな CI/CD パイプラインの構築まで紹介していきたいと思います。これから CFn テンプレート開発されている方で自動テストをやりたいと考えている方は参考にしてみてはいかがでしょうか。 AWS TaskCat とは AWS TaskCat とは、AWS CloudFormation (CFn) テンプレートの自動テストを行う Python 製のテストツールです。 このツールを利用することで、指定した各リージョンに CFn テンプレートから環境を一時的にデプロイ、各リージョンでのデプロイ可否結果のレポート生成、テストで一時的に作成した環境を削除、といった一連の流れを自動化することができます。 なお、AWS TackCat はローカルでテストを実行する際に Docker が必要となるため、Docker をサポートしていない AWS CloudShell では利用することができないのでご注意ください。 AWS TaskCat を使ってみよう 「はじめに」で既に述べたとおり、今回は Linux 上に開発環境を作るところからはじめて、簡素なサンプルを用いたテストの実行、テスト自動化を組み込んだシンプルな CI/CD パイプラインの構築まで紹介していきたいと思います。 まずは開発環境の設定から それでは Linux 上に開発環境を作っていきたいと思います。まず AWS TaskCat をインストールする事前準備として Python の仮想環境を作成していきましょう。なお、今回の例で使用している Linux ディストリビューションは Amazon Linux 2 です。 $ sudo yum install -y python3 $ python3 --version Python 3.7.10 $ python3 -m venv venv37 $ . venv37/bin/activate 次に、AWS TaskCat をインストールします。 $ python3 -m pip install taskcat $ taskcat --version _ _ _ | |_ __ _ ___| | _____ __ _| |_ | __/ _` / __| |/ / __/ _` | __| | || (_| \__ \ < (_| (_| | |_ \__\__,_|___/_|\_\___\__,_|\__| version 0.9.25 0.9.25 TaskCat のテストに必要な docker サービスやこの後のステップで利用する git を追加で設定します。 $ sudo yum install -y docker git $ sudo systemctl start docker 最後に、AWS CLI の設定をして開発環境の構築は完了です。 $ aws configure 手動でテストを実行してみよう では簡単なサンプルを用いて AWS TaskCat を使ったテストを行っていきましょう。今回は、東京リージョン(ap-northeast-1)と大阪リージョン(ap-northeast-3)の 2 つのリージョンに対して、同じテンプレートを使ってスタックの作成ができるかを確認していきたいと思います。 Step1. テスト対象のテンプレートを作成しよう 今回は VPC を作るだけのとてもシンプルなテンプレートを用意しました。 my-vpc.yaml AWSTemplateFormatVersion: "2010-09-09" Description: Sample CloudFormation Template Parameters: vpcIpv4CicdBlock: Type: String Default: 10.0.0.0/16 vpcNameTag: Type: String Resources: myVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcIpv4CicdBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Ref vpcNameTag Outputs: myVpcId: Description: VPC ID Value: !Ref myVPC Export: Name: myVpcId Step2. TaskCat のテスト定義ファイルを作成しよう 次に TaskCat のテスト定義ファイル .taskcat.yml を作成します。今回の例では東京リージョン(ap-northeast-1)と大阪リージョン(ap-northeast-3)でテストを実施するように定義しています。 .taskcat.yml project: name: sample-taskcat-project regions: - ap-northeast-1 - ap-northeast-3 tests: test-my-vpc: parameters: vpcIpv4CicdBlock: 10.255.0.0/16 vpcNameTag: test-vpc template: my-vpc.yml Step3. テストを実行してみよう では次のコマンドでテストを実行していきましょう。テストを実行すると、スタック作成が東京リージョンと大阪リージョンに対して並列で実行され、各リージョンでのスタック作成の成否結果の収集、スタック削除まで自動的に行われます。 $ taskcat test run _ _ _ | |_ __ _ ___| | _____ __ _| |_ | __/ _` / __| |/ / __/ _` | __| | || (_| \__ \ < (_| (_| | |_ \__\__,_|___/_|\_\___\__,_|\__| version 0.9.25 [INFO ] : Linting passed for file: /root/aws-taskcat-sample/my-vpc.yaml [S3: -> ] s3://tcat-sample-taskcat-project-XXXXXXX/sample-taskcat-project/my-vpc.yaml [INFO ] : ┏ stack Ⓜ tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f [INFO ] : ┣ region: ap-northeast-1 [INFO ] : ┗ status: CREATE_COMPLETE [INFO ] : ┏ stack Ⓜ tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f [INFO ] : ┣ region: ap-northeast-3 [INFO ] : ┗ status: CREATE_COMPLETE [INFO ] : Reporting on arn:aws:cloudformation:ap-northeast-1:<AWSアカウント名>:stack/tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f/XXXXXXX [INFO ] : Reporting on arn:aws:cloudformation:ap-northeast-3:<AWSアカウント名>:stack/tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f/XXXXXXX [INFO ] : Deleting stack: arn:aws:cloudformation:ap-northeast-3:<AWSアカウント名>:stack/tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f/XXXXXXX [INFO ] : Deleting stack: arn:aws:cloudformation:ap-northeast-1:<AWSアカウント名>:stack/tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f/XXXXXXX [INFO ] : ┏ stack Ⓜ tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f [INFO ] : ┣ region: ap-northeast-1 [INFO ] : ┗ status: DELETE_COMPLETE [INFO ] : ┏ stack Ⓜ tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f [INFO ] : ┣ region: ap-northeast-3 [INFO ] : ┗ status: DELETE_COMPLETE 実行後は taskcat_outputs ディレクトリにリージョンごとの実行ログが出力されますので、こちらからスタック作成の成否結果を確認することができます。 $ tree -a taskcat_outputs/ taskcat_outputs/ ├── index.html ├── tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f-ap-northeast-1-cfnlogs.txt └── tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f-ap-northeast-3-cfnlogs.txt 0 directories, 3 files $ cat taskcat_outputs/*-ap-northeast-3-*.txt ----------------------------------------------------------------------------- Region: ap-northeast-3 StackName: tCaT-sample-taskcat-project-test-my-vpc-d27c255f120c414fb370e9d2827a215f ***************************************************************************** ResourceStatusReason: Stack launch was successful ***************************************************************************** : 以上、ローカル環境で CFn テンプレートのテストを実行する方法のご紹介でした。 CI/CD パイプラインに組み込んでみよう 次はもう一歩進んで、ソースコードの更新をトリガーに自動でテストを実行する CI/CD パイプラインを構築し、実際に動かすところまで行ってみたいと思います。なお、今回は AWS リソース作成を簡略化するため AWS クイックスタートを利用して CI/CD パイプラインを構築していきたいと思います。 なお、今回作成するパイプラインでは開発ブランチ(例では develop ブランチ)に対して更新が入ると、それをトリガーに AWS TackCat を活用した自動テストを実行し、テストに成功したら特定ブランチ(例では main ブランチ)へマージするという一連の流れを自動化しています。 Step1. GitHub にリポジトリを作ろう まずは枠だけ作っておきましょう。中身は後続のステップで入れます。 Step2. GitHub でアクセストークンを作ろう GitHub > Setting > Developer settings > Personal access tokens > Generate new tokens から、指定の通り「repo」と「admin:repo_hook」のスコープを選択したトークンを作ります。 作成に成功したら次のようにトークンが表示されます。スタック作成時に利用しますのでコピーしておきましょう。なお、トークン情報が漏れると大変なことになりますので絶対に漏らさないように注意しましょう。 Step3. クイックスタートを起動します ではクイックスタートを活用して環境を構築していきましょう。まずはクイックスタートサイトの「クイックスタートを起動します」をクリックします。 リンクをクリックすると自動的にスタックの作成画面へ遷移します。作成先のリージョンがデフォルトだとオレゴンなので適宜変更して「次へ」をクリックします。 次にパラメータを入力していきます。 必要に応じてタグなどの設定を実施し、確認画面で入力ミスがないかを確認したら「スタックの作成」を実行します。 あとはステータスが「CREATE_COMPLETE」になるまで待つだけです。 以上で、CI/CD パイプラインに必要な AWS リソースの構築まで完了しました。 Step4. CI/CD パイプラインの動作確認 ではソースコードを GitHub に登録して CI/CD パイプラインが動作することを確認していきましょう。登録するソースコードは先ほど作成したこちらのファイルです。 $ tree -a . ├── my-vpc.yaml └── .taskcat.yml 0 directories, 2 files スタック作成時に指定した GitHub の監視対象ブランチ(例では develop ブランチ)へソースコードをプッシュします。 $ git init . $ git remote add origin <GitHub上のリポジトリ> $ git pull origin main $ git branch develop $ git checkout develop $ git add . $ git commit -m "initial commit" $ git push origin develop さてここからは再び AWS マネジメントコンソールへ移ります。先ほどのプッシュをトリガーにパイプラインが起動していることを確認するため、まずは CodePipeline 画面へ行きましょう。次のように CI/CD パイプラインが動作していることが確認できるかと思います。 ここからドリルダウンでビルドログなどを見ることができます。Build ステージの CodeBuild アクションボックスの「詳細」ボタンをクリックし、ビルドログを確認しましょう。出力例では指定したリージョンでのテストに成功していることがわかります。 以上、TaskCat によるテストを組み込んだ CI/CD パイプラインの作成のご紹介でした。 終わりに AWS TaskCat はいかがだったでしょうか? 今回はクイックスタートを使って環境を構築しましたが、既にパイプラインを構築されている方はリポジトリに .taskcat.yml を追加して、pip install tackcat と taskcat test run をステップに追加するだけで簡単に CFn テンプレートの自動テストを組み込むことができます。気になった方はぜひ触ってみていただければと思います。 以上、AWS CloudFormation テンプレートの自動テストを実現する「AWS TaskCat」のご紹介でした。 AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。 GitHub は、GitHub Inc. の商標または登録商標です。 Linux は、Linus Torvalds 氏 の日本およびその他の国における登録商標または商標です。 その他、本資料に記述してある会社名、製品名は、各社の登録商品または商標です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS DevOps - AWS Chalice を使ってサーバレスアプリケーションを開発しよう

はじめに みなさん、こんにちは。今回は「AWS Chalice」を活用したサーバレスアプリケーション開発についてのお話です。AWS Summit Online Japan 2021 の日立製作所の講演で軽く触れたこともあり、どこかで紹介したいと思っておりました。 さて、AWS Chalice は多くの方にとってあまり馴染みのないツールだと思いますが、実際に使ってみるととても手軽に Amazon API Gateway と AWS Lambda を使用するサーバレスアプリケーションを作成してデプロイすることができます。もちろん、どんなツールにも得手不得手はあるので「すべてのプロジェクトで AWS Chalice の活用が最適か?」と言われればもちろん「No!」なのですが、ちょっと試しに動く Web API をサッと手軽に作りたい、といったケースにはとても良いソリューションだと思います。 今回は AWS CloudShell 上に開発環境を作るところからはじめて、簡素なサンプルを用いた一連の開発の流れ、テスト自動化を組み込んだシンプルな CI/CD パイプラインの構築まで紹介していきたいと思います。これから実際に動くサーバレスアプリケーションを手軽に作りたいと思われている方は参考にしてみてはいかがでしょうか。 AWS Chalice とは AWS Chalice とは、Amazon AWS Gateway や AWS Lambda を用いたサーバレスアプリケーションを、お手軽に開発できるようにする Python 製のサーバレスアプリケーションフレームワークです。具体的には、Web API をシンプルで直感的なコードで実装できるようにする機能や、作成したコードからアプリケーションの作成やデプロイを実行するコマンドラインインタフェース(CLI)といった開発者にやさしい機能を提供してくれます。 普段から Python に触れている方であれば似たような機能として Flask や Bottle をイメージされるかと思いますが、これらにサーバレス環境へデプロイする機能が追加で付与されたものが AWS Chalice、とイメージしていただくと良いのかなと思います。 AWS Chalice を使ってみよう 「はじめに」で既に述べたとおり、今回は AWS CloudShell 上に開発環境を作るところからはじめて、簡素なサンプルを用いた一連の開発の流れ、テスト自動化を組み込んだシンプルな CI/CD パイプラインの構築まで紹介していきたいと思います。 まずは開発環境の構築から 今回は AWS CloudShell 上に開発環境を作っていきたいと思います。 それではまず AWS Chalice をインストールする事前準備として Python の仮想環境を作成していきましょう。 $ sudo yum install -y python3 $ python3 --version Python 3.7.10 $ python3 -m venv venv37 $ . venv37/bin/activate 次に、AWS Chalice をインストールします。 $ python3 -m pip install chalice $ chalice --version chalice 1.26.0, python 3.7.10, linux 4.14.243-185.433.amzn2.x86_64 最後に、AWS CLI の設定をして開発環境の構築は完了です。 $ aws configure 簡単なアプリケーションを動かしてみよう ここでは AWS Chalice を使ったアプリケーションの作成からデプロイまでの流れを、次のような簡単なサンプルを用いて紹介していきたいと思います。 Step1. 新規プロジェクトの作成 まずは chalice new-project コマンドを実行して新しいプロジェクトを作成します。出力例のようにプロジェクトを新しく作るとデフォルトでサンプルプログラムも生成されます。 $ chalice new-project <任意のプロジェクト名> $ cd <任意のプロジェクト名> $ tree -a . ├── app.py # APIの実装を行うファイル ├── .chalice │ └── config.json # Chaliceの設定を行うファイル ├── .gitignore └── requirements.txt # 利用するライブラリの定義を行うファイル 1 directory, 4 file Step2. ソースコードの編集 今回はデフォルトで作られたソースコードにちょっとだけ手を加えました。修正後のファイルの中身は次の通りです。実際の例を見ていただけるとわかる通り、AWS Chalice を用いた実装は Python を普段使わないという方でも直感的でわかりやすい構文になっているのではないでしょうか。 app.py from chalice import Chalice app = Chalice(app_name='<任意のプロジェクト名>') app.log.setLevel(logging.INFO) @app.route('/hello') def hello(): app.log.debug("Invoking from function hello") return {'hello': 'world'} @app.route('/hello', methods=['POST'], content_types=['application/json'], cors=True) def hello_post(): app.log.debug("Invoking from function hello_post") request = app.current_request return {'result': request.json_body['payload']} requirements.txt chalice .chalice/config.json { "version": "2.0", "app_name": "<任意のプロジェクト名>", "stages": { "dev": { "api_gateway_stage": "api" } } } Step3. ローカル環境で動作確認 AWS 上へデプロイする前にローカル環境で軽く動作をしたい場合は、chalice local コマンドを実行します。 実行例のように期待通りのレスポンスが返ってくるようであれば、いよいよ AWS 上へデプロイをしていきます。 Termina1 $ chalice local Serving on http://127.0.0.1:8000 Terminal2 $ curl http://127.0.0.1:8000/hello {"hello":"world"} $ curl -X POST -H "Content-Type: application/json" -d '{"payload":"hello, world"}' http://127.0.0.1:8000/hello {"result":"hello, world"} Step4. AWS 上へデプロイ AWS 上へアプリケーションのデプロイをする場合は、chalice deploy コマンドを実行します。実行例ではメッセージから新たに Lambda 関数、API Gateway と IAM ロールが作られていることがわかります。 $ chalice deploy --stage dev Creating deployment package. Creating IAM role: <任意のプロジェクト名>-dev Creating lambda function: <任意のプロジェクト名>-dev Creating Rest API Resources deployed: - Lambda ARN: arn:aws:lambda:ap-northeast-1:<AWSアカウント名>:function:<任意のプロジェクト名>-dev - Rest API URL: https://<文字列>.execute-api.ap-northeast-1.amazonaws.com/api/ デプロイ完了後は期待するレスポンスが返ってくるか確認しましょう。 $ curl https://<文字列>.execute-api.ap-northeast-1.amazonaws.com/api/hello {"hello":"world"} $ curl -X POST -H "Content-Type: application/json" -d '{"payload":"hello, world"}' https://<文字列>.execute-api.ap-northeast-1.amazonaws.com/api/hello {"result":"hello, world"} 以上、AWS Chalice を使ったアプリケーションの作成からデプロイまでの一連の流れでした。 StepEX. デプロイしたアプリケーションの削除 不要になったアプリケーションは chalice delete コマンドを使って削除しておきましょう。 $ chalice delete Deleting Rest API: <文字列> Deleting function: arn:aws:lambda:ap-northeast-1:<AWSアカウント名>:function:<任意のプロジェクト名>-dev Deleting IAM role: <任意のプロジェクト名>-dev より実践的な開発を行うにあたって ユニットテストを自動化しましょう 近頃はユニットテストの自動化は一般的に行われているかと思います。AWS Chalice でも Python の一般的なテストツール Pytest を使ってユニットテストを実装することができます。例として、今回は次のサンプルに対するテストコードを実装してみます。 app.py(テスト対象のプログラム) from chalice import Chalice app = Chalice(app_name='<任意のプロジェクト名>') app.log.setLevel(logging.INFO) @app.route('/hello') def hello(): app.log.debug("Invoking from function hello") return {'hello': 'world'} @app.route('/hello', methods=['POST'], content_types=['application/json'], cors=True) def hello_post(): app.log.debug("Invoking from function hello_post") request = app.current_request return {'result': request.json_body['payload']} Step1. テストコードの実装 では、まず必要なファイルを用意していきます。中身は後で入れるとしてここでは空ファイルだけ作ります。 $ touch test_requirements.txt # テストプログラムの依存ライブラリ定義 $ mkdir tests $ touch tests/__init__.py $ touch tests/conftest.py # テストプログラム間で共通の処理を実装 $ touch tests/test_app.py # テストプログラムを実装 次に各ファイルを編集していきます。今回は HTTP レスポンスのステータスコードとボディの中身を確認するだけの簡単なテストプログラムを用意しました。 test_requirements.txt(依存ライブラリの定義) pytest-chalice pytest-cov tests/__init__.py (EOF) tests/conftest.py(共通処理の実装) import pytest from app import app as chalice_app @pytest.fixture def app(): return chalice_app tests/test_app.py(テストの実装) from http import HTTPStatus import json def test_hello_get(client): response = client.get('/hello') assert response.status_code == HTTPStatus.OK assert response.json == {'hello': 'world'} def test_hello_post(client): headers = {'Content-type':'application/json'} payload = {'payload':'hello, world'} response = client.post('/hello', headers=headers, body=json.dumps(payload)) assert response.status_code == HTTPStatus.OK assert response.json == {'result': 'hello, world'} def test_hello_put(client): response = client.put('/hello') assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED assert response.json == {"Code":"MethodNotAllowedError","Message":"Unsupported method: PUT"} Step2. ユニットテストの実行 ではテストプログラムが用意できたので pytest コマンドを使ってユニットテストを実行しましょう。実行例では、出力メッセージからユニットテスト 3 件が実行され、3 件とも成功 (PASSED) して、C1 カバレッジが 100% 網羅されていることがわかります。 テストの実行 $ python3 -m pip install -r test_requirements.txt $ pytest -v --cov --cov-branch =========================== test session starts ============================ platform linux -- Python 3.7.10, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- /home/cloudshell-user/venv37/bin/python3 cachedir: .pytest_cache rootdir: /home/cloudshell-user/<任意のプロジェクト名> plugins: cov-3.0.0, chalice-0.0.5 collected 3 items tests/test_app.py::test_hello_get PASSED [ 33%] tests/test_app.py::test_hello_post PASSED [ 66%] tests/test_app.py::test_hello_put PASSED [100%] ---------- coverage: platform linux, python 3.7.10-final-0 ----------- Name Stmts Miss Branch BrPart Cover ----------------------------------------------------- app.py 11 0 0 0 100% tests/__init__.py 0 0 0 0 100% tests/conftest.py 4 0 0 0 100% tests/test_app.py 16 0 0 0 100% ----------------------------------------------------- TOTAL 31 0 0 0 100% ============================ 3 passed in 0.05s ============================= 以上、ちょっとしたユニットテストの自動化のご紹介でした。 CI/CD パイプラインを構築しよう 次はもう一歩進んで、ソースコードの更新をトリガーに自動でテストを実行してデプロイまで実施する CI/CD パイプラインを構築し、実際に動かすところまで行ってみたいと思います。 Step1. CI/CD パイプラインの作成 AWS Chalice には CI/CD パイプライン用の CloudFormation(CFn) テンプレートを自動生成してくれるとても便利な chalice generate-pipeline コマンドがあります。今回の例では、先ほど作ったユニットテストもパイプラインの中で実行するようにしますので、buildspec.yml を分離する -b オプションも付与して実行します。 $ chalice generate-pipeline -b buildspec.yml <テンプレートファイル名> 作成された CFn テンプレートは中身が大きいのでここには貼り付けませんが、おおよそ次のような AWS リソース作成が定義されています。 AWS CodeCommit リポジトリ AWS CodeBuild プロジェクト AWS CodePipeline パイプライン Amazon Simple Storage Service(S3) バケット AWS IAM ロールおよびポリシー なお、今回はユニットテストの結果をレポート出力できるように作成されたテンプレートファイルを編集して CodeBuild に割り当てる権限を追加しました。 CFnテンプレート(編集後) "CodeBuildPolicy": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyName": "CodeBuildPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Resource": "*", + "Effect": "Allow" + }, { "Action": [ では CFn テンプレートからリソースをデプロイしましょう。実行例のようにスタック作成の成功と出力されたら完了です。 $ aws cloudformation deploy --stack-name <スタック名> --template-file <テンプレートファイル名> --capabilities CAPABILITY_IAM : Successfully created/updated stack - <スタック名> Step2. パイプラインジョブ定義の編集 次に chalice generate-pipeline で生成されたパイプラインジョブの定義を編集して、ユニットテストも自動で実行するようにしていきましょう。編集前のファイル内容はこちらです。 buildspec.yml(編集前) artifacts: files: - transformed.yaml type: zip phases: install: commands: - sudo pip install --upgrade awscli - aws --version - sudo pip install 'chalice>=1.26.0,<1.27.0' - sudo pip install -r requirements.txt - chalice package /tmp/packaged - aws cloudformation package --template-file /tmp/packaged/sam.json --s3-bucket ${APP_S3_BUCKET} --output-template-file transformed.yaml version: '0.1' 編集後のファイルは次のようにしてみました。記載順序の入れ替えやビルドフェーズの分割も併せて実施していてわかりにくくなっていますが、要約すると test_requirements.txt の読み込みと pytest コマンドの実行をステップとして追加しています。 buildspec.yml(編集後) version: 0.2 phases: install: commands: - python --version pre_build: commands: - sudo pip install --upgrade awscli pip - aws --version - sudo pip install -r requirements.txt -r test_requirements.txt build: commands: - pytest -v -junit-xml=test-result.xml --cov --cov-branch --cov-report=xml --cov-report=term post_build: commands: - chalice package /tmp/packaged - aws cloudformation package --template-file /tmp/packaged/sam.json --s3-bucket ${APP_S3_BUCKET} --output-template-file transformed.yaml reports: pytest_reports: files: - test-result.xml file-format: JUNITXML cobertura_reports: files: - coverage.xml file-format: COBERTURAXML artifacts: files: - transformed.yaml Step3. CI/CD パイプラインの動作確認 環境もパイプラインジョブの定義も整いましたので、あとは作成された CodeCommit リポジトリへソースコードを登録して、それをトリガーに自動でパイプラインが実行されるところまで見ていきましょう。 まずはローカルに git リポジトリを作成して、ソースコードのコミットまでしていきます。今回の .gitignore ファイルは GitHub さんが公開する Python 向けテンプレートを使ってます。 $ git init . $ curl https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore > .gitignore $ git add . $ git commit -m "Initial commit" 次に、ソースコードを格納する AWS CodeCommit リポジトリの URL を確認します。 $ aws cloudformation describe-stacks --stack-name <スタック名> --query 'Stacks[0].Outputs' : - OutputKey: SourceRepoURL OutputValue: https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/<プロジェクト名> : 先ほど調べたリポジトリ情報を元に、リモートリポジトリを追加します。 $ git remote add codecommit https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/<プロジェクト名> CodeCommit へ Push できるように認証情報ヘルパーを設定します。 $ git config --global credential.helper '!aws codecommit credential-helper $@' $ git config --global credential.UseHttpPath true ではソースコードをリモートレポジトリへ push してみましょう。 $ git push codecommit master さてここからは AWS マネジメントコンソールに移ります。先ほどの git push をトリガーにパイプラインが起動していることを確認するためまずは CodePipeline 画面へ。次のように CI/CD パイプラインが動作していることが確認できるかと思います。 では次に、ビルド処理に追加したユニットテストが動いているかを確認しましょう。Build ステージの CodeBuild アクションボックスの「成功しました」メッセージのすぐ下にある「詳細」ボタンをクリックして CodeBuild 画面へ進みましょう。次のようにビルドログからユニットテストが実行されていることが確認できるかと思います。 また、テストとカバレッジのレポートについてはレポートグループの方にも登録されていることが確認できるかと思います。 では CodePipeline 画面へ戻りましょう。Beta ステージで実施しているアプリケーションのデプロイにも成功していることがわかりますね。ExcecuteChangeSet アクションボックスの「詳細」ボタンをクリックし、CloudFormation 画面へ進みましょう。 CloudFormation 画面では出力の一覧から EndpointURL 値を確認しましょう。 最後に、期待するレスポンスが返ってくるか生成された EndpointURL に向けて HTTP リクエストを発行して確認しましょう。実行例では期待するレスポンスが返ってきてますね。 $ curl https://<文字列>.execute-api.ap-northeast-1.amazonaws.com/api/hello {"hello":"world"} $ curl -X POST -H "Content-Type: application/json" -d '{"payload":"hello, world"}' https://<文字列>.execute-api.ap-northeast-1.amazonaws.com/api/hello {"result":"hello, world"} 本来はもっとパイプラインに条件分岐を持たせたりすると思いますが、ひとまずこれでソースコードのプッシュを契機にテストを走らせてからデプロイを行う、という最低限の流れを自動化することができました。 以上、ちょっとした CI/CD パイプラインの作成でした。 ちゃんとしたアプリケーションを開発するには 公式ドキュメントを活用しよう ちゃんとしたアプリケーションにするにはもちろん色々と機能を実装しないといけませんよね。こんなときにいつも私がお世話になっているのが公式ドキュメントです。トピックごとに簡単なサンプルを交えた実装方法について記載がありますのでとても参考になるかと思います。 サービス別資料を参考にしよう もちろん AWS さんのサービス別資料もとても参考になります。こちらも目を通していただけると良いかと思います。 余談ですが 本記事執筆のきっかけとなった AWS Summit Online Japan 2021 で行った日立の講演についてもご紹介させてください。 この講演では、IoT やサーバレス技術を活用した異なるタイプ(データ分析系とアプリケーション開発系)の事例を 1 件ずつお話させていただきました。AWS Chalice については、2 つ目のアプリケーション開発系の事例にて本当に軽くですが触れておりますのでよろしければご参照いただけると幸いです。 この講演を通じて「ほほう、日立ってこんなこともしてたんだ」と多少なりとも良いイメージを抱いてもらえるきっかけになってくれると嬉しいなと思ってます σ(,,´∀ `,,) 終わりに AWS Chalice はいかがだったでしょうか? AWS には、AWS SAM (Serverless Application Model)といった別のサーバレスアプリケーション開発用フレームワークがありますが、こちらと比較すると機能がかなり限定されることもあって、今回ご紹介した AWS Chalice は多くの方にとって馴染みもなければ、なかなかに触れる機会も少ないのかもしれません。 ただ、実際に触ってみていただけばわかる通り、非常に簡単・手軽にサーバレスアプリケーションを作ることができますので、迅速性を求められる PoC(概念実証)や PoV(価値実証)といった場面で特に有用なのでは、と考えています。気になった方はぜひ触ってみて、そのお手軽さを実際に体験していただければと思います。 以上、AWS 上でサーバレスアプリケーションを手軽に開発できるようにする「AWS Chalice」のご紹介でした。 AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。 その他、本資料に記述してある会社名、製品名は、各社の登録商品または商標です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel de Lambda

PHP Laravelをサーバーレス化する方法として、AWS SAMを使用した手順はAmazon Web Services ブログで公開されています。 今回はコンテナイメージ化することで、より簡単にAWS Lambda上で動かす手順を記載します。 前提 PHPをサーバーレス化するための必須ライブラリ bref 拡張機能 brefphp/extra-php-extensions Laravel用Package brefphp/laravel-bridge Laravelのセットアップ Laravelインストーラー composer global require laravel/installer プロジェクト作成 laravel new laravel-function ログ出力先として標準出力の追加 config/logging.php ... 'channels' => [ ... 'stdout' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'stream' => 'php://stdout', ], ], ], ... 必須ライブラリの追加 bref composer require bref/laravel-bridge --update-with-dependencies worker.phpの作成 SQSからイベントメッセージを受信するためのエントリーポイントを作成 php artisan vendor:publish --tag=serverless-worker aws composer require aws/aws-sdk-php-laravel composer require aws/aws-sdk-php config/aws.phpの作成 php artisan vendor:publish --provider="Aws\Laravel\AwsServiceProvider" AWS用プロバイダ設定追加 config/app.php 'providers' => [ ... Aws\Laravel\AwsServiceProvider::class, ... ], 'aliases' => [ ... 'AWS' => Aws\Laravel\AwsFacade::class, ] Credentials defaultの認証情報を利用するため、不要な設定は削除 config/queue.php ... 'sqs' => [ 'driver' => 'sqs', // 'key' => env('AWS_ACCESS_KEY_ID'), // 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 'queue' => env('SQS_QUEUE', 'default'), 'suffix' => env('SQS_SUFFIX'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'after_commit' => false, ], ... 処理の実装 API routes/api.php ... // API 例 Route::get('/greeting', function () { return 'Hello World'; }); // 非同期Job呼び出し用 Route::get('/job', function (Request $request) { $message = $request->get("msg"); \App\Jobs\SimpleJob::dispatch($message); return 'OK'; }); Job SQS経由で非同期に実行するJob app/Jobs/SimpleJob.php <?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; class SimpleJob implements ShouldQueue { use Dispatchable; use InteractsWithQueue; use Queueable; use SerializesModels; /** @var string */ private $message; public function __construct(string $message) { $this->message = $message; } /** * Execute the job. * * @return void */ public function handle() { Log::info('Message: ' . $this->message); } } ローカル実行環境 localstack localstackの初期化用シェルを作成 docker/sqs/init.sh #!/bin/sh awslocal sqs create-queue --queue-name localstack_dead_letter_queue awslocal sqs create-queue --queue-name LaravelQueue \ --attributes '{"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:ap-northeast-1:000000000000:localstack_dead_letter_queue\",\"maxReceiveCount\":3}"}' # Lambdaの作成 awslocal lambda create-function \ --function-name 'laravel-lambda-job-bridge' \ --runtime=provided.al2 \ --role=dummyrole \ --handler=worker.php awslocal lambda create-event-source-mapping \ --event-source-arn arn:aws:sqs:ap-northeast-1:000000000000:LaravelQueue \ --function-name "laravel-lambda-job-bridge" xdebug 設定ファイルを作成 docker/php/xdebug.ini [xdebug] xdebug.start_with_request=yes xdebug.client_host=host.docker.internal xdebug.client_port=9003 xdebug.log=/tmp/xdebug.log デバッグ用Dockerfile API用 debug.Dockerfile FROM bref/php-81-fpm-dev COPY --from=bref/extra-redis-php-81:0.11.19 /opt /opt COPY --from=bref/extra-xdebug-php-81:0.11.19 /opt /opt COPY docker/php/xdebug.ini /opt/bref/etc/php/conf.d/zz-xdebug.ini COPY --from=composer:2.1.12 /usr/bin/composer /usr/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer JOB用 jobs.debug.Dockerfile FROM bref/php-81 COPY --from=bref/extra-redis-php-81:0.11.19 /opt /opt COPY --from=bref/extra-xdebug-php-81:0.11.19 /opt /opt COPY docker/php/xdebug.ini /opt/bref/etc/php/conf.d/zz-xdebug.ini COPY --from=composer:2.1.12 /usr/bin/composer /usr/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer docker-compose docker-compose.yml version: "3.5" services: web: image: bref/fpm-dev-gateway ports: - '8000:80' volumes: - .:/var/task links: - php environment: HANDLER: public/index.php DOCUMENT_ROOT: public php: build: context: . dockerfile: debug.Dockerfile env_file: - .env.example environment: LOG_CHANNEL: stdout QUEUE_CONNECTION: sqs SQS_PREFIX: 'http://localstack:4566/000000000000' SQS_QUEUE: LaravelQueue AWS_DEFAULT_REGION: ap-northeast-1 XDEBUG_MODE: develop,debug PHP_IDE_CONFIG: serverName=docker volumes: - ~/.aws:/.aws - .:/var/task:ro # sqs localstack: image: localstack/localstack:latest environment: HOSTNAME_EXTERNAL: localstack DEFAULT_REGION: ap-northeast-1 DATA_DIR: /tmp/localstack/data SERVICES: sqs ports: - "4566:4566" volumes: - ./docker/sqs:/docker-entrypoint-initaws.d job: build: context: . dockerfile: jobs.debug.Dockerfile depends_on: - localstack env_file: - .env.example environment: LOG_CHANNEL: stdout QUEUE_CONNECTION: sqs SQS_PREFIX: 'http://localstack:4566/000000000000' SQS_QUEUE: LaravelQueue AWS_CONFIG_FILE: /.aws/config AWS_SHARED_CREDENTIALS_FILE: /.aws/credentials AWS_DEFAULT_REGION: ap-northeast-1 XDEBUG_MODE: develop,debug PHP_IDE_CONFIG: serverName=docker entrypoint: - php - artisan - queue:work volumes: - ~/.aws:/.aws - .:/var/task:ro ローカル実行 docker-compose up -d 動作確認 > curl 'http://localhost:8000/api/greeting' Hello World > curl 'http://localhost:8000/api/job?msg=Success' OK Dockerイメージの作成 Rest API用 Dockerfile bref/php-81-fpmを使用 Redisを利用する場合、brefphp/extra-php-extensionsから extra-redis-php-81を使用 今回は例示として追加 Dockerfile FROM bref/php-81-fpm COPY --from=bref/extra-redis-php-81:0.11.18 /opt /opt COPY . /var/task CMD [ "public/index.php" ] docker build -t {AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/laravel-api-function:latest . Job 用 jobs.Dockerfile bref/php-81を使用 Redisを利用する場合、brefphp/extra-php-extensionsから extra-redis-php-81を使用 今回は例示として追加 jobs.Dockerfile FROM bref/php-81 COPY --from=bref/extra-redis-php-81:0.11.18 /opt /opt COPY . /var/task CMD [ "worker.php" ] docker build -f jobs.Dockerfile -t {AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/laravel-jobs-function:latest . ECR dockerイメージを保存するリポジトリを作成 laravel-api-function aws ecr create-repository --repository-name laravel-api-function --region ap-northeast-1 laravel-jobs-function aws ecr create-repository --repository-name laravel-jobs-function --region ap-northeast-1 dockerイメージをリポジトリにプッシュ ECRログイン aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com リポジトリにプッシュ laravel-api-function docker push {AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/laravel-api-function:latest laravel-jobs-function docker push {AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/laravel-jobs-function:latest SQS 動作確認用のSQSキューを作成 aws sqs create-queue --queue-name LaravelQueue デプロイ CloudFormation API GatewayからLambda Functionを起動する CloudFormationファイルの作成 api.yaml AWSTemplateFormatVersion: "2010-09-09" Parameters: LogGroupName: Type: String Default: /aws/api-gateway/laravel FunctionName: Type: String Default: laravel-api-function QueueName: Type: String Default: LaravelQueue Resources: ApiFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Ref FunctionName MemorySize: 1024 Timeout: 300 Code: ImageUri: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/laravel-api-function:latest' ImageConfig: Command: - public/index.php PackageType: Image Environment: Variables: APP_ENV: development LOG_LEVEL: debug LOG_CHANNEL: stdout QUEUE_CONNECTION: sqs SQS_PREFIX: !Sub 'https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}' SQS_QUEUE: LaravelQueue Role: !GetAtt Role.Arn Role: Type: AWS::IAM::Role Properties: RoleName: RoleForLaravelApiFunction AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: "" Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: LaravelApiFunctionPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*' - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*' - Effect: Allow Action: - sqs:SendMessage - sqs:GetQueueAttributes Resource: - !Sub 'arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${QueueName}' HttpAPI: Type: AWS::ApiGatewayV2::Api Properties: Name: !Sub '${AWS::StackName}-apigw' ProtocolType: HTTP DefaultStage: Type: AWS::ApiGatewayV2::Stage Properties: StageName: $default ApiId: !Ref HttpAPI AutoDeploy: true AccessLogSettings: DestinationArn: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroupName}' Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength" }' DependsOn: - ApiGatewayLog ApiGatewayLog: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Ref LogGroupName RetentionInDays: 3 GetGreeting: Type: AWS::ApiGatewayV2::Route Properties: RouteKey: "GET /api/greeting" ApiId: !Ref HttpAPI Target: !Join - "/" - - "integrations" - !Ref GETIntegration GetJob: Type: AWS::ApiGatewayV2::Route Properties: RouteKey: "GET /api/job" ApiId: !Ref HttpAPI Target: !Join - "/" - - "integrations" - !Ref GETIntegration GETIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref HttpAPI PayloadFormatVersion: "2.0" IntegrationType: AWS_PROXY IntegrationMethod: GET IntegrationUri: !GetAtt ApiFunction.Arn Permission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref ApiFunction Principal: apigateway.amazonaws.com SourceArn: !Sub - 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${HttpAPI}/*' - HttpAPI: !Ref HttpAPI CloudFormationデプロイ aws cloudformation deploy --template api.yaml --stack-name laravel-api-function --capabilities CAPABILITY_NAMED_IAM SQSをトリガーに起動する CloudFormationファイルを作成 jobs.yaml AWSTemplateFormatVersion: "2010-09-09" Parameters: FunctionName: Type: String Default: laravel-jobs-function QueueName: Type: String Default: LaravelQueue Resources: JobFunction: Type: "AWS::Lambda::Function" Properties: FunctionName: !Ref FunctionName MemorySize: 1024 Code: ImageUri: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/laravel-jobs-function:latest' ImageConfig: Command: - worker.php PackageType: Image Environment: Variables: APP_ENV: development LOG_LEVEL: debug LOG_CHANNEL: stdout QUEUE_CONNECTION: sqs SQS_PREFIX: !Sub 'https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}' SQS_QUEUE: !Ref QueueName Role: !GetAtt Role.Arn Role: Type: AWS::IAM::Role Properties: RoleName: RoleForLaravelJobFunction AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: "" Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: LaravelJobFunctionPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*' - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*' - Effect: Allow Action: - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:GetQueueAttributes Resource: - !Sub 'arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${QueueName}' SQSEvent: Type: AWS::Lambda::EventSourceMapping Properties: BatchSize: 10 EventSourceArn: !Sub 'arn:aws:sqs:ap-northeast-1:${AWS::AccountId}:${QueueName}' FunctionName: !GetAtt JobFunction.Arn Enabled: true CloudFormationデプロイ aws cloudformation deploy --template jobs.yaml --stack-name laravel-jobs-function --capabilities CAPABILITY_NAMED_IAM 動作確認 ApiEndpointの取得 > aws apigatewayv2 get-apis { "Items": [ { "ApiEndpoint": "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com", "ApiId": "xxxxxxxxxx", "ApiKeySelectionExpression": "$request.header.x-api-key", "CreatedDate": "2021-12-02T00:00:00+00:00", "DisableExecuteApiEndpoint": false, "Name": "laravel-api-function-apigw", "ProtocolType": "HTTP", "RouteSelectionExpression": "$request.method $request.path", "Tags": {} } ] } apiの確認 /api/greeting > curl '{ApiEndpoint}/api/greeting' Hello World レスポンスとしてHello Worldが表示される jobの動作確認 /api/job > curl '{ApiEndpoint}/api/job?msg=Success' OK レスポンスとしてOKが表示される CloudWatch Logsの/aws/lambda/laravel-jobs-function に Message: Successのログが出力される まとめ PHP Laravelのコンテナイメージ化を行い、AWS Lambdaにデプロイ、動作確認するまでの手順を簡単に記載しました。 このまま業務に使用できるものではなく、実際にはVPCやRDSProxy等に接続するための設定、実装が必要になるでしょう。 また、Jobを非同期化するとしてもイベントメッセージがPHPに依存したJobクラスのシリアライズデータとなるため抽象化も難しく、APIとJobで実装をきれいに分けるのも困難です。 そのため、最適解ではありませんが、既存システムがPHP Laravelに依存しており、エンジニアに別の言語やフレームワーク等への移行を求める余裕がない場合、あるいはマイクロサービス化ほどの大げさなことにはしたくない場合、負荷を分散させる方法として手段の一つにはなると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenSearch を使ってみる

この記事は「弁護士ドットコム Advent Calendar 2021」の 2 日目の記事です。よろしくお願いいたします。 はじめに OpenSearch は Elasticsearch と Kibana から派生した検索・分析ソフトウェアです。 OpenSearch が作成された理由は簡単に言うとライセンスの問題です。 詳しくは以下のページをご覧ください。 起動 Docker Hub にあるイメージを使用します。 $ docker run \ --name opensearch \ -p 19200:9200 \ -p 19600:9600 \ -e discovery.type=single-node \ --rm \ opensearchproject/opensearch:1.2.0 2021/12/01 現在のバージョンは 1.2.0 です。 Docker Hub はダウンロード回数制限 (100 回 / 6 時間) があるので注意が必要です。 https://docs.docker.com/docker-hub/download-rate-limit/ --name は opensearch を設定しています。無指定だと適当な名前が付きます。 -e で single-node 指定で起動します。 特定のネットワークを利用したい時は --network を指定します。 無指定だと bridge に含まれます。 --net-alias でネットワーク範囲内のエイリアスを指定することもできます。 データを保持したい場合は --volume でボリュームをマウントします。 起動確認 $ docker ps | grep opensearch 3762adccdb82 opensearchproject/opensearch:1.2.0 "./opensearch-docker…" About a minute ago Up About a minute 9300/tcp, 9650/tcp, 0.0.0.0:19200->9200/tcp, 0.0.0.0:19600->9600/tcp opensearch アクセス $ curl -u "admin:admin" -k -X GET "https://localhost:19200" { "name" : "3762adccdb82", "cluster_name" : "docker-cluster", "cluster_uuid" : "ahZ6QxH0SgWDL-g11y6WGA", "version" : { "distribution" : "opensearch", "number" : "1.2.0", "build_type" : "tar", "build_hash" : "c459282fd67ddb17dcc545ec9bcdc805880bcbec", "build_date" : "2021-11-22T16:57:18.360386Z", "build_snapshot" : false, "lucene_version" : "8.10.1", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "The OpenSearch Project: https://opensearch.org/" } https でアクセスします。http では繋がりません。 SSL のエラーを無視する為 -k オプション (insecure) を指定します。 ユーザー認証で -u "admin:admin" を指定します。 デフォルトの管理ユーザーを使用しています。 $ curl -u "admin:admin" -k -X GET "https://localhost:19200/_cat/master?v=true" id host ip node MMokSh0DQNaN6o1oQvbOxQ 172.17.0.2 172.17.0.2 3762adccdb82 $ curl -u "admin:admin" -k -X GET "https://localhost:19200/_cat/health?v=true" epoch timestamp cluster status node.total node.data discovered_master shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1637917926 09:12:06 docker-cluster green 1 1 true 1 1 0 0 0 0 - 100.0% $ curl -u "admin:admin" -k -X GET "https://localhost:19200/_cat/nodes?v=true" ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 172.17.0.2 16 47 5 0.39 0.27 0.14 dimr * 3762adccdb82 single-node で起動しているのでマスターノードのみ起動しています。 作成 サンプルデータは青空文庫を利用させていただきました。 Index API Index API を使ってインデックスを作成できます。 $ curl -u "admin:admin" -k -X PUT -H "Content-Type: application/json" "https://localhost:19200/books/_doc/1" -d '{ "id": 1, "name": "こころ", "body": "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。", "author": "夏目漱石" }' $ curl -u "admin:admin" -k -X PUT -H "Content-Type: application/json" "https://localhost:19200/books/_doc/2" -d '{ "id": 2, "name": "羅生門", "body": "ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。広い門の下には、この男のほかに誰もいない。ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。それが、この男のほかには誰もいない。", "author": "芥川龍之介" }' $ curl -u "admin:admin" -k -X PUT -H "Content-Type: application/json" "https://localhost:19200/books/_doc/3" -d '{ "id": 3, "name": "走れメロス", "body": "メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。けれども邪悪に対しては、人一倍に敏感であった。きょう未明メロスは村を出発し、野を越え山越え、十里はなれた此のシラクスの市にやって来た。", "author": "太宰治" }' Bulk API Bulk API を使うことで複数のインデックス作成や削除を一括で実行することもできます。 $ curl -u "admin:admin" -k -X POST -H "Content-Type: application/x-ndjson" "https://localhost:19200/books/_bulk" -d ' { "index" : { "_index" : "books", "_id" : "4" } } { "id": 4,"name": "坊っちゃん", "body": "親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるかも知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃したからである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと答えた。", "author": "夏目漱石" } { "index" : { "_index" : "books", "_id" : "5" } } { "id": 5, "name": "注文の多い料理店", "body": "二人の若い紳士が、すっかりイギリスの兵隊のかたちをして、ぴかぴかする鉄砲をかついで、白熊のような犬を二疋つれて、だいぶ山奥の、木の葉のかさかさしたとこを、こんなことを云いながら、あるいておりました。", "author": "宮沢賢治" } { "index" : { "_index" : "books", "_id" : "6" } } { "id": 6, "name": "学問のすすめ", "body": "「天は人の上に人を造らず人の下に人を造らず」と言えり。されば天より人を生ずるには、万人は万人みな同じ位にして、生まれながら貴賤上下の差別なく、万物の霊たる身と心との働きをもって天地の間にあるよろずの物を資り、もって衣食住の用を達し、自由自在、互いに人の妨げをなさずしておのおの安楽にこの世を渡らしめ給うの趣意なり。", "author": "福沢諭吉" } ' 各行の区切りに \n を使用する ndjson というフォーマットです。 http://ndjson.org/ "Content-Type: application/x-ndjson" を指定します。 "Content-Type: application/json" を指定しても動きます。 区切りに \n を使用するので JSON ソース内には改行を入れずに 1 行で指定する必要があります。 データの最終行は、必ず改行文字 \n で終わります。 確認 $ curl -u "admin:admin" -k -X GET "https://localhost:19200/_cat/indices?v=true" health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open books Gymb2sviS5mOwU5ab5vRIw 1 1 6 0 36.2kb 36.2kb green open .opendistro_security jlFkWQkFQVG4UobikntRqQ 1 0 9 0 59.2kb 59.2kb yellow open security-auditlog-2021.12.01 PaibhuqETwCzDEFShloQWg 1 1 3 0 40.3kb 40.3kb single-node で起動しているので、health は yellow になります。 この時点での index books の docs.count は 6 件です。 他にも index .opendistro_security と security-auditlog-YYYY.MM.DD が表示されます。 参照 ユーザー追加 管理ユーザーを使い続けるのは良くないので、参照用のユーザーを追加します。 $ curl -u "admin:admin" -k -X PUT -H "Content-Type: application/json" "https://localhost:19200/_plugins/_security/api/internalusers/sampleuser" -d '{ "password": "pass1234", "opendistro_security_roles": ["readall"] }' readall 権限のユーザーを追加します。 https://opensearch.org/docs/security-plugin/access-control/users-roles#predefined-roles パスワードはプレーンテキスト password で指定します。 予めハッシュ化したパスワードであれば hash で指定できます。 https://opensearch.org/docs/security-plugin/access-control/api#create-user 権限(ロール)は opendistro_security_roles に設定しています。 backend_roles もありますが、こちらは任意の文字列や外部の認証システム (LDAP/Active Directory など) から送られてきた任意の文字列を指定するようです。 https://opensearch.org/docs/security-plugin/access-control/index/ ユーザー確認 $ curl -u "admin:admin" -k -X GET "https://localhost:19200/_plugins/_security/api/internalusers/" | jq '.sampleuser' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1348 100 1348 0 0 3786 0 --:--:-- --:--:-- --:--:-- 3786 { "hash": "", "reserved": false, "hidden": false, "backend_roles": [], "attributes": {}, "opendistro_security_roles": [ "readall" ], "static": false } 1 件 $ curl -u "sampleuser:pass1234" -k -X GET "https://localhost:19200/books/_doc/1" {"_index":"books","_type":"_doc","_id":"1","_version":1,"_seq_no":0,"_primary_term":1,"found":true,"_source":{ "id": 1, "name": "こころ", "body": "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。", "author": "夏目漱石" }}% 全件 $ curl -u "sampleuser:pass1234" -k -X GET "https://localhost:19200/books/_search" {"took":118,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":6,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"books","_type":"_doc","_id":"1","_score":1.0,"_source":{ "id": 1, "name": "こころ", "body": "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。", "author": "夏目漱石" }},{"_index":"books","_type":"_doc","_id":"2","_score":1.0,"_source":{ "id": 2, "name": "羅生門", "body": "ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。広い門の下には、この男のほかに誰もいない。ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。それが、この男のほかには誰もいない。", "author": "芥川龍之介" }},{"_index":"books","_type":"_doc","_id":"3","_score":1.0,"_source":{ "id": 3, "name": "走れメロス", "body": "メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。けれども邪悪に対しては、人一倍に敏感であった。きょう未明メロスは村を出発し、野を越え山越え、十里はなれた此のシラクスの市にやって来た。", "author": "太宰治" }},{"_index":"books","_type":"_doc","_id":"4","_score":1.0,"_source":{ "id": 4,"name": "坊っちゃん", "body": "親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるかも知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃したからである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと答えた。", "author": "夏目漱石" }},{"_index":"books","_type":"_doc","_id":"5","_score":1.0,"_source":{ "id": 5, "name": "注文の多い料理店", "body": "二人の若い紳士が、すっかりイギリスの兵隊のかたちをして、ぴかぴかする鉄砲をかついで、白熊のような犬を二疋つれて、だいぶ山奥の、木の葉のかさかさしたとこを、こんなことを云いながら、あるいておりました。", "author": "宮沢賢治" }},{"_index":"books","_type":"_doc","_id":"6","_score":1.0,"_source":{ "id": 6, "name": "学問のすすめ", "body": "「天は人の上に人を造らず人の下に人を造らず」と言えり。されば天より人を生ずるには、万人は万人みな同じ位にして、生まれながら貴賤上下の差別なく、万物の霊たる身と心との働きをもって天地の間にあるよろずの物を資り、もって衣食住の用を達し、自由自在、互いに人の妨げをなさずしておのおの安楽にこの世を渡らしめ給うの趣意なり。", "author": "福沢諭吉" }}]}}% Bulk API で作成した箇所が 1 行で表示されてしまうので、見やすくしたい場合は jq などで整形するのが良いと思います。 検索 $ curl -u "sampleuser:pass1234" -k -X GET -H "Content-Type: application/json" "https://localhost:19200/books/_search" -d '{ "query": { "simple_query_string": { "query": "一人", "fields": ["name", "body"], "default_operator": "and" } } }' | jq . % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2405 100 2259 100 146 34227 2212 --:--:-- --:--:-- --:--:-- 37000 { "took": 5, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 3, "relation": "eq" }, "max_score": 1.0708848, "hits": [ { "_index": "books", "_type": "_doc", "_id": "2", "_score": 1.0708848, "_source": { "id": 2, "name": "羅生門", "body": "ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。広い門の下には、この男のほかに誰もいない。ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。それが、この男のほかには誰もいない。", "author": "芥川龍之介" } }, { "_index": "books", "_type": "_doc", "_id": "4", "_score": 0.9264894, "_source": { "id": 4, "name": "坊っちゃん", "body": "親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるかも知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃したからである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと答えた。", "author": "夏目漱石" } }, { "_index": "books", "_type": "_doc", "_id": "3", "_score": 0.85215265, "_source": { "id": 3, "name": "走れメロス", "body": "メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。けれども邪悪に対しては、人一倍に敏感であった。きょう未明メロスは村を出発し、野を越え山越え、十里はなれた此のシラクスの市にやって来た。", "author": "太宰治" } } ] } } $ curl -u "sampleuser:pass1234" -k -X GET -H "Content-Type: application/json" "https://localhost:19200/books/_search" -d '{ "query": { "simple_query_string": { "query": "夏目", "fields": ["author"], "default_operator": "and" } } }' | jq . % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1766 100 1626 100 140 20074 1728 --:--:-- --:--:-- --:--:-- 21802 { "took": 7, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 2, "relation": "eq" }, "max_score": 2.059239, "hits": [ { "_index": "books", "_type": "_doc", "_id": "1", "_score": 2.059239, "_source": { "id": 1, "name": "こころ", "body": "私はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執っても心持は同じ事である。よそよそしい頭文字などはとても使う気にならない。", "author": "夏目漱石" } }, { "_index": "books", "_type": "_doc", "_id": "4", "_score": 2.059239, "_source": { "id": 4, "name": "坊っちゃん", "body": "親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるかも知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃したからである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと答えた。", "author": "夏目漱石" } } ] } } 日本語検索 デフォルトの状態でもそれなりに動いているように見えますが、精度の高い日本語検索をする為にはプラグインで analysis-kuromoji analysis-icu などをインストールして設定する必要があります。 Dockerfile に記載してインストールします。 FROM opensearchproject/opensearch:1.2.0 RUN bin/opensearch-plugin install analysis-kuromoji \ && bin/opensearch-plugin install analysis-icu https://opensearch.org/docs/latest/opensearch/install/plugins/ また、以下のようなマッピング定義を反映させておく必要があります。 "mappings": { "dynamic": "strict", "properties": { "id": { "type": "integer" }, "name": { "type": "text", "analyzer": "ja_kuromoji_index_analyzer" }, "body": { "type": "text", "analyzer": "ja_kuromoji_index_analyzer" }, "author": { "type": "text", "analyzer": "ja_kuromoji_index_analyzer" } } } "dynamic": "strict" を指定しておくと、新しいフィールドが検出された場合に例外が発生します。 フィールドの増減を制御して明示的にしたい場合には指定しておくと良いかと思います。 "analyzer": "ja_kuromoji_index_analyzer" は形態素解析用のアナライザーです。 別途、事前に設定しておく必要があります。その際に analysis-kuromoji や analysis-icu が必要になります。 先にデータ投入してフィールドが作成されてしまっていると、後からマッピング定義を反映できない場合があります。 その場合は再インデックスが必要になります。 Elasticsearch の記事にはなりますが、日本語の全文検索については以下などを一読されると良いかと思います。 Term vectors API 日本語検索の設定ができていれば、Term vectors API で用語の情報や統計を取得できます。 $ curl -s -u "sampleuser:pass1234" -k -X GET -H "Content-Type: application/json" "https://localhost:19200/books/_termvectors/1" -d '{ "fields" : ["text"], "term_statistics": true, "field_statistics": true, "positions": false, "offsets": false, "filter": { "max_num_terms": 30, "min_term_freq": 1, "min_doc_freq": 1, "min_word_length": 1 } }' | jq -r '.term_vectors.text.terms | keys, [ .[].score ] | @tsv' | ruby -e 'readlines.map { |x| x.chomp.split("\t") }.transpose.sort_by { |x| x[1].to_f }.reverse.map { |x| puts "#{x[0]},#{x[1]}" }' | column -t -s ',' セキュリティ 今回はデモなので、admin などのユーザーを使ったり HTTPS で通信しながら SSL 警告を無視したりしていますが、本番環境ではセキュリティ証明書や設定用 YAML ファイルを独自のものに置き換える必要があります。 Dockerfile に記載して置き換えることができます。 例えば、初期ユーザーを変更したい場合には internal_users.yml を置き換えます。 FROM opensearchproject/opensearch:1.2.0 COPY --chown=opensearch:opensearch internal_users.yml /usr/share/opensearch/plugins/opensearch-security/securityconfig/ https://opensearch.org/docs/latest/security-plugin/configuration/yaml/#internal_usersyml またデモインストーラー自体を無効したい場合には、環境変数 DISABLE_INSTALL_DEMO_CONFIGを true に設定することも可能です。 セキュリティプラグインを削除したい場合は以下のように記述します。 FROM opensearchproject/opensearch:1.2.0 RUN /usr/share/opensearch/bin/opensearch-plugin remove opensearch-security COPY --chown=opensearch:opensearch opensearch.yml /usr/share/opensearch/config/ 詳しくは以下のページなどをご覧ください。 さいごに 今回はざっくり OpenSearch を使ってみる内容でした。 足掛かりにしていだたき OpenSearch に対する理解が進むことを期待しております。 Amazon OpenSearch Service でも利用されていますので、これからも進化することを願いつつ、便利に使っていきたいところです。 この記事が皆様の一助となれば幸いです。 さて、明日 (12/3) の「弁護士ドットコム Advent Calendar 2021 | 3日目」は @k-anz さんの「リモート入社の体験」になります!お楽しみに!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新規案件の設計について, 使用したサービスの紹介

はじめに 公開が遅くなり申し訳ございません。。(11/31があるものだと勘違いしておりました) 「CYBIRDエンジニア Advent Calendar 2021」1日目担当を担当します、@ntrvです。 ここ2年ほどは恋愛ゲームのサーバサイド開発をメインに担当していたりします。 アドベントカレンダー再開にあたって かつてサイバードでも2014年~2017年までは毎年アドベントカレンダーに参加していました。 しかし次第に一部の人がいくつも記事を書く必要がでてきて 記事を執筆することが大きな負担となってきたことから 近年はアドベントカレンダーに参加できずにおりました。 一方かつてのアドベントカレンダーの記事を見て新たに入社してきた方も増え 「記事を執筆したい!」というメンバーが増えてきたこともあり それにつれアドベントカレンダーを再開しようという機運が高まってきました。 そして今年から、サイバードの紹介・我々エンジニアのアウトプットのスキルを磨くという観点で アドベントカレンダーを再開させていただくこととなりました。 Disclaimer 記載させていただきました内容の正確性については保証できかねます。 なのであくまで参考程度に留めていただけると幸いです。 概要 今回新たに立ち上げました新規案件での開発の設計について どのようなサービスを使用して構成されているかお話しできればなと思います。 新規案件の要件としてはざっくり以下のようになっておりました。 CtoC型の音声プラットフォーム, Voicyをイメージ Twitter認証でログインする 月額のサブスクリプションを設定して自分のチャンネルで音声配信が可能(配信者) チャンネルを作成し、チャンネルでの売上を一定割合で受け取ることができる チャンネル開設にあたっての審査が(基本的には)存在しない サブスクリプションを購読し、チャンネル登録を行う(視聴者) 長時間の音声をアップロードする場合があるので、プログレッシブダウンロードに対応する いいね数、再生回数の表示 私は全体の設計・開発スケジュール管理・インフラとフロントエンドの開発を担当しておりました。 サーバーサイドの開発については、別の方1名ないし2名に担当していただいておりました。 構成 上記の要件と開発側の事情を踏まえ、以下の構成となりました。 メインシステムと音声変換システムに分離して構築することになりました。 共通 Sentry 各所(フロントエンド・サーバーサイド・Lambda)で発生したエラーを集約・監視し、詳細を分析することができる。 Terraform 構成が複雑かつ、本番環境・テスト環境でそれぞれ用意する必要がありますのでTerraformを導入している GitHub メインシステム Laravel(PHP) PHPであれば社内誰でも使用することができるので採用した DIやMiddleware, Seedingの仕組みが印象的 Angular + Ionic NestJSとAngularをプライベートで軽く使用していたので、採用した サーバーサイドのエンジニアだったので、DIの仕組みに親近感があった デフォルトでTypescriptとなっておりフレームワークとして完成していたので、ライブラリ選定で迷うことがないと思った これを機にReactiveX(RxJS)を再度学習しておきたかった ドキュメントが体系的にまとまっている印象で、いざというときにも対応できる安心感があった ECS Fargate EC2の管理も含めて極力インフラの保守をしない構成にしたかった Fargateを使用したことがないので経験してみたかった Fargate Spotを利用することで、EC2と比較しても安く手軽に使用することができた Aurora 恋愛ゲーム案件で広く使用しているAuroraを採用 RDS for MySQLと比較して、パラメータチューニングの必要性が比較的少ないので採用したということもある ElastiCache for Redis セッション管理, いいね数・再生回数を保存 CloudFront, S3 SPAのホスティング プロフィール画像の保存・配信 CodeBuild, Codepipeline CI/CDに使用している 同じAWSなのでアカウント管理が煩雑にならない VPCに接続できるので、CodeBuildからマイグレーションを行うことができる Angularのビルド・デプロイ・SourceMapをSentryにアップロード Dockerイメージを作成し、マイグレーション(があれば)実行、作成したイメージをECSにデプロイ マイグレーションをCodeBuildで行うために、VPC接続を有効にする プライベートサブネットに接続するので、実質NATゲートウェイは必須 Stripe 決済サービス CtoCの決済サービス(Stripe Connect)が使用できる 利用するための審査にかかる時間がなかった(vs 他の決済サービス) 英語だがドキュメントが手厚い SDKが存在している Firebase Authentication Twitterでのソーシャルログインに使用 特にFirestoreを使用していないので、セッションCookieの仕組みをサーバーサイドに実装する Mackerel SaaSのサーバー監視サービス サーバーの各種メトリクスの監視と外形監視 AWSインテグレーションからメトリクスを取得 追加のメトリクスや死活監視は踏み台(bastion)にインストールしたエージェントから取得している ボイス変換システム 音声変換についてはMediaConvertを使用しており、非同期を前提としています。そのため別システムとして切り出すことにいたしました。 Lambda 1. S3への音声ファイルアップロードをトリガーに、MediaConvertのAPIを叩き音声ファイルの変換を開始している 2. 変換完了後にSQSにキューが入りますので、それをトリガーにメインシステム側に変換完了を通知 サーバー側のステータスを変更する Angularに合わせてTypescriptを使用、rollupでトランスパイルしている S3 変換前と変換後の音声ファイルを保存しておく MediaConvert アップロードした音声ファイルのHLS形式への変換で使用している Mackerel Lambdaの同時実行数の制限を監視 SQSの監視を行い、Lambdaでエラーが発生していないかも見ている 開発の振り返り とにかく工数見積が甘かった Stripe ConnectやAngular, Media Convert, ECS Fargate等の未使用技術をふんだんに使用していた 見積もり当初から厳しいことが想像できたにも関わらず、悲観的な見積もりができなかった。 昔先輩からきいた、"ざっくり見積もった工数を3倍すると実際の工数に近いものになる"という都市伝説?通りとなってしまった 他案件にも参加していたので、そちらにも迷惑がかかってしまった 各方面の助けを借りてなんとかオンスケで開発することができたが、最終的に締め切り駆動開発となってしまった ペーペーな自分でも案件の開発に参加することができた インフラ・サーバーサイドしか担当してこなかったが、未経験の分野であっても参加することができた フルリモートワーク下の開発の苦労 開発期間中に1回目の緊急事態宣言が開始したので、最初のうちはコミュニケーションのとり方に苦労した 技術的な反省点 動的なOGP対応 Twitter等でボイスを共有するユースケースが多いと考えられるので、要件にはなかったが対応したい 既にサービスクローズしていますが Lambda@Edgeでmetaタグだけ返すダイナミックレンダリングを考える この新規案件の開発を改めて振り返ると 他の会社と比較して、弊社では未経験の分野であっても大抜擢してもらえることがあるので 経験がない分、修羅の道を歩むこともありますが 最終的に多くの経験値を得ることができると考えております。 続きの話として書きたいこと 上記の内容を踏まえて実際に 苦労した話・失敗した話、注意する部分や"ここにこだわった!"みたいな話も具体的にしたかったのですが 最後力尽き、全体の構成と採用理由等を話すだけで終了してしまいました。。 機会があれば以下の中から続きの話をしたいと思います! フロントエンド HLSをAngularで再生するために行ったことと失敗したこと(Directiveや動的コンポーネント追加) ngrxを導入して状態管理した話とストアの設計に失敗した話 よく使用したrxjsのoperatorと使用例 AsyncPipeの初期値null問題でやっちまったこと ソースコードの整形・linter周りでやったこと バックエンド サーバーサイドでのStripeの実装例(クライアントサイドの例を含めて) DIの使用方法やProviderなどのLaravelの基礎的な話 開発環境の構成(Seeding, Factory周り) Lambdaの実装例 インフラ Terraform周りで小ネタ 細かい注意点等(SQSのVisibility Timeout, awsvpcモードにdocker-composeをあわせる話, SourceMap周り) 上記を構想する上で参考にさせていただいた記事 最後に 「CYBIRDエンジニア Advent Calendar 2021」, 明日は@march_fさんの「ラズパイで居留守を極める」です。 ラズパイの有効な利用方法について興味があります! どのような話をしてくれるのでしょうか?乞うご期待! また、CYBIRDでは好奇心旺盛なエンジニアを絶賛募集しております。 フルリモートワーク下ではありますが 話を聞くだけでも一度、遊びに来てください。 https://www.cybird.co.jp/recruit/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】VPCについて理解を深める!

はじめに なぜこの記事を書こうと思ったのか アプリ開発の際にDockerを少し触って、仮想サーバーと初対面を果たした。 サーバーと言えば、オンプレミスの物理的なデータセンターをイメージしており、工場で働いていた時も計算室という部屋にサーバーが所狭しと置いてあったのを思い出す。 そこで今回、AWSを学習している際にVPCという仮想サーバーに触れたので自分なりにまとめてみる。 VPCとは Amazon Virtual Private Cloud (以下、VPC) は、AWS上に作成できるプライベート仮想ネットワーク空間です。AWSアカウント内に専用のネットワークを作成でき、このネットワーク内に「EC2」(Amazon EC2)などのAWSリソースを配置できます。    1つのVPCを"まとまり"として分離でき、複数のVPC間との接続も可能です。インターネットに公開するパブリックなVPCや、VPNなどを使用して接続するプライベートなVPCの構築もできます。 ↑で言う"まとまり"を具体的に言うと、VPCはそれぞれの動物のエリアのようなイメージですね。動物たちがごちゃまぜにならないように、「ここはゾウさんエリアだからコアラとライオンは入ってこないでね!」とエリアを区切ることで、動物たちは安心して過ごすことができます。  仮にオンプレミス環境ではネットワーク環境を構築する場合、データセンター、ハードウェア、回線など用意すべきものが多く、準備期間と初期コストがかかります。  しかし、AWSでVPCをネットワーク環境として準備する場合、いくつかの操作を行えば数分でネットワークを構築できます。ルータ機能、DNS、NTPなどネットワークで必要な基本機能はAWS側で提供されます。 VPCの主な概念 Virtual Private Cloud (VPC) — AWS アカウント専用の仮想ネットワーク。 サブネット — VPC の IP アドレスの範囲。 ルートテーブル — ネットワークトラフィックの経路を判断する際に使用される、ルートと呼ばれる一連のルール。    インターネットゲートウェイ — VPC 内のリソースとインターネット間の通信を可能にするために VPC にアタッチするゲートウェイ。    VPC エンドポイント - PrivateLink を使用してサポートされている AWS サービスや VPC エンドポイントサービスに VPC をプライベートに接続できます。インターネットゲートウェイ、NAT デバイス、VPN 接続、および AWS Direct Connect 接続は必要ありません。VPC のインスタンスは、サービスのリソースと通信するためにパブリック IP アドレスを必要としません。VPC と他のサービス間のトラフィックは、Amazon ネットワークを離れません。詳細については、「」を参照してください AWS PrivateLink および VPC エンドポイント    CIDR ブロック —クラスレスドメイン間ルーティング。インターネットプロトコルアドレスの割り当てとルート集計方法。 AWS VPCの使用用途とは? インターネット向けシステム インターネットを経由するシステムを提供する用途で使われるケースです。インターネット接続を待ち受けるインスタンスにはグローバルIPを設定しますが、必ず全てのインスタンスに付与する必要はありません。 ELB(Elastic Load Balancing)から複数のEC2にトラフィックを転送する構成がしばしば用いられていますが、高可用システムを作成するために使うことも可能なので覚えておきましょう。 オンプレミス向けシステム オンプレミスとは、サーバーやソフトウェアなどの情報システムを自社で管理し、運用すること、つまり従来のサーバー運用形態のことをさしています。 オンプレミスのネットワークの延長としてVPCを相互に接続することで、データセンターのサーバーや企業の拠点PC向けシステムを提供しています。 インターネット兼オンプレス向けシステム これは、先ほどのインターネット向けのシステムとオンプレス向けのシステムとの両方の接続をVPCに設定する構成です。普通は、クラウドか、オンプレスかの2択なので、珍しい使い方です。ルーティングテーブルのセキュリティ設定で対応するようにしましょう。 まとめ VPCとはリソースのまとまりを区別してIPアドレスの振り分けやセキュリティ管理を行っていたんですね! 次は実際にマネジメントコンソールを使用して、ハンズオンを行ってみようと思います。 初めてのやってみた!系の記事を投稿するので楽しみです!! 最後まで読んでいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

W3 CloudFormation 2021 振り返り

概要 弊社が展開している物流クラウドサービス W3 では、AWS CloudFormation (以下 CFn) を使って環境構築を自動化しています。 この記事では、そんなW3のCFnテンプレートで2021年に実施したアップデートをおさらいします。 W3のシステム構成 W3は2013年に誕生し、2015年からは現在のクラウド型サービスとして多くのお客様に使っていただいています。 システム構成はEC2, RDSを中心としたレガシーなアーキテクチャーとなっており、SaaSモデルのほか、お客様専有環境でのパッケージ形態での提供も行っています。 今年の W3 CFn アップデート 最近はセキュリティ関連のご要望をいただくことが増え、2021年は特に暗号化関連での対応を中心に行いました。 これまで追加で手動対応していた箇所を自動化したものが多くなっていますが、主な変更箇所を以下にまとめました。 なお、テンプレートはJSON形式となっており、いずれも変更箇所を抜粋したものになります。 各種暗号化 使用するAWSサービスの暗号化設定をテンプレートに反映しました。 これまでは構築後に担当者が個別に設定し直していました。 EC2 EbsのEncryptedを有効化し、KMSキーのARNを指定しました。 ※KMSキーは事前に作成しておきます。 "Ec2": { "Type": "AWS::EC2::Instance", "Properties": { "BlockDeviceMappings": [ { "Ebs": { "Encrypted": "true", "KmsKeyId": { "Fn::Sub": "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/***" } } } ] } } SQS KmsMasterKeyIdにKMSキーのARNを追加しました。 "Sqs": { "Type": "AWS::SQS::Queue", "Properties": { "KmsMasterKeyId": { "Fn::Sub": "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/***" } } S3 ServerSideEncryptionConfigurationでBucketKeyEnabledを有効化、ServerSideEncryptionByDefaultはではKMSによる暗号化を指定しました。 "S3bucket": { "Type": "AWS::S3::Bucket", "Properties": { "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "BucketKeyEnabled": true, "ServerSideEncryptionByDefault": { "SSEAlgorithm": "aws:kms", "KMSMasterKeyID": { "Fn::Sub": "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/***" } } } ] } } } RDS (証明書設定) RDSはSSL証明書を有効化しました。 アプリケーション側でも証明書(ap-northeast-1-bundle.pem)を読み込むように設定しています。 "Rds": { "Type": "AWS::RDS::DBInstance", "Properties": { "CACertificateIdentifier": "rds-ca-2019" } } RDS (SecretsManager) これまではRDSのパスワードを個別に指定していましたが、SecretsManagerで自動生成するようにしました。 アプリケーション側にはEC2のUserDataを使って反映しています。 "RdsSec": { "Type": "AWS::SecretsManager::Secret", "Properties": { "GenerateSecretString": { "SecretStringTemplate": "{\"username\": \"***\"}", "GenerateStringKey": "password", "PasswordLength": 32, "ExcludeCharacters": "\"$'@`|/\\" } } }, "RdsCluster": { "Type": "AWS::RDS::DBCluster", "MasterUsername": { "Fn::Sub": "{{resolve:secretsmanager:${RdsSec}::username}}" }, "MasterUserPassword": { "Fn::Sub": "{{resolve:secretsmanager:${RdsSec}:SecretSt ring:password}}" } }, "RdsSecAtt": { "Type": "AWS::SecretsManager::SecretTargetAttachment", "Properties": { "SecretId": { "Ref": "RdsSec" }, "TargetId": { "Ref": "RdsCluster" }, "TargetType": "AWS::RDS::DBCluster" } }, "Ec2": { "Type": "AWS::EC2::Instance", "Properties": { "UserData": { "Fn::Base64": { "Fn::Sub": [ "Content-Type: multipart/mixed; boundary=\"//\"\nMIME-Version: 1.0\n\n--//\nConte nt-Type: text/cloud-config; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encodi ng: 7bit\nContent-Disposition: attachment; filename=\"cloud-config.txt\"\n\n#cloud-config\nc loud_final_modules:\n - [scripts-user, always]\noutput: { all : '| tee -a /var/log/cloud-ini t-output.log' }\n\n--//\nContent-Type: text/x-shellscript; charset=\"us-ascii\"\nMIME-Versio n: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename=\"userdat a.txt\"\n\n#!/bin/bash\nsecret_id=\"${secret_id}\" region=\"${AWS::Region}\" /opt/cloud-init. sh\n--/--", { "secret_id": { "Ref": "rdspwd" } } ]}} } } 冗長化 ElastiCache(Redis) TransitEncryptionEnabledで通信の暗号化を有効化し、アプリケーション側もtlsプロトコル接続に変更しました。 また、これまではシングル構成の定義のみだったため、本番環境の場合にはAWS管理コンソールから冗長化構成で追加していましたが、これをCFnのパラメータで指定できるようにし、TypeをAWS::ElastiCache::CacheClusterからAWS::ElastiCache::ReplicationGroupに変更、シャードを有効化して冗長化構成をしやすくしました。 変更前(抜粋) "Redis": { "Type": "AWS::ElastiCache::CacheCluster", "Properties": { "NumCacheNodes": "1" } }, 変更後(抜粋) "Redis": { "Condition": "noCacheMultiAz", "Type": "AWS::ElastiCache::ReplicationGroup", "Properties": { "AtRestEncryptionEnabled": "true", "MultiAZEnabled": "false", "NodeGroupConfiguration": [ { "PrimaryAvailabilityZone": "ap-northeast-1a", "ReplicaCount": "0" } ], "NumNodeGroups": "1", "TransitEncryptionEnabled": "true" } }, "RedisRg": { "Condition": "enCacheMultiAz", "Type": "AWS::ElastiCache::ReplicationGroup", "Properties": { "AtRestEncryptionEnabled": "true", "MultiAZEnabled": "true", "NodeGroupConfiguration": [ { "PrimaryAvailabilityZone": "ap-northeast-1a", "ReplicaAvailabilityZones": [ "ap-northeast-1c" ], "ReplicaCount": "1" } ], "NumNodeGroups": "1", "TransitEncryptionEnabled": "true" } } 来年に向けて 最初にW3が誕生してから早くも8年が経過しました。 このW3 CFnテンプレートもアップデートを重ねてきましたが、容量制限(51200バイト)ぎりぎりで、そもそもJSONのメンテが疲れるので、来年にはAWS Cloud Development Kitで書き直したいと考えています。 できれば実行基盤もモダンなものにしたい... 株式会社ダイアログについて 『物流業界をテクノロジーの力でHAPPYに』 弊社は「物流xITx経営」をキーワードに、W3、Tariffeeといった自社サービスを中心に、他サービス連携やデータプラットフォーム化を通してよりHAPPYな物流を目指して活動しています。 ただいま一緒に働くエンジニアを募集中です。 物流、ロジテック、サービス連携、データプラットフォーム等に興味のある方、カジュアル面談で一度お話しませんか。 詳しい採用情報は Wantedly にて。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む