自己紹介と本日のアジェンダ

渡邉洋平氏(以下、渡邉):「AWS Solutions Constructsで楽してシステム作りたいよ〜!」という話をします。よろしくお願いします。最初に今日のアジェンダです。まずは「AWS Solutions Constructsって何? を調べてみました」。商用でも、実践的な知見ではないことは予めご了承ください。次に「Solutions Constructs×CDK v2で×記述の抽象化・省力化をどこまで実現できるのか」。最後に、「これならプロダクトに組み込めそう」と感じてもらえたらゴールだと思っています。

自己紹介です。NTTの研究所系グループ会社であるNTTテクノクロス デジタルツイン事業部に勤めています。「@_watany」というアカウント名で、TwitterとQiitaをやっています。仕事でAWSを触っているのは2016年頃からで、メイン業務は全社テックサポートや、社内研修講師、ほかに本業でいろいろ受託しています。

AWSを追っていく中でどんどんCDKが出てきて、キャッチアップしなきゃと思いながら検証環境構築を味見したり、趣味でアドベントカレンダーに参加して、グループ内勉強会でCDKに触った話をしていました。

認定試験の勉強をしたエンジニアがいるだけでは解決しない、AWS環境作成

渡邉:まず、「Solutions Constructsとは」。「ぶっちゃけAWSで環境を作るのって?」という話ですが、外部のブログや公式を見て調べれば、ある1つのAWSのサービスを起動することはできます。ただ、実際の場面で需要があるのは数個から数十個のサービスを連携するシステムです。例えば、座学でAWSを勉強し、AWS Well-Architectedも理解し、認定試験の勉強もしたエンジニアがいるだけでは解決しないなにかが、実際に触るとある。それはなにかという話です。

AWSの各サービス群は「ビルディングブロック」形式とも呼ばれていますが、特有のシステム全体を設計して構築しきるという、ある種の複雑さ(があります)。「プラガブル」と呼ばれているところや、プラグするところまでがしんどいです。あとは、非機能要件をどこまで設計・設定しきれるか。セキュリティ要件やモニタリング要件など諸々が、実際に手を動かしてみるとつらいと感じます。

では、それらの問題はCDKを採用すれば楽になるのか。デフォルト値を深く考えずにいい感じのリソースを構築できるという点で、L2 Constructsはすごくいいと思います。数行でそれなり以上のVPCが作れる。ただ、1個のリソースを作ることは解決しましたが、各リソースの連携は明示的に書かなければいけません。また、非機能要件はユーザーの責任で設計や実装することも必要です。そもそも各リソースのつながりや付帯リソースがデフォルトであればいいのにと、少なくとも私は、書いていてよくそう思っています。

「AWS Solutions Constructs」とは

渡邉:次は、Solutions Constructsがあるという話です。AWSの公式の記述を引用すると、「2つ以上のCDKのリソースを組み合わせてロギングや暗号化などのベストプラクティスを実装する複数サービスのパターンを提供します」。抽象度が高い構成がスッと作れるのがL3 Constructsの魅力です。

v2を調べると、実際に手を動かして使えることがわかって安心しました。今のところ、TypeScriptとJavaScript、Python、Javaで利用が可能です。具体的に言うと、AWSを触っている人がどこかで見たような構成がモジュール化されています。よくあるCloudFrontとS3のホスティング構成や、ALBにVPCが付いてさらにコンテナ基盤であるFargateが付いたアプリケーション構成などがモジュール化されているのが魅力です。

Constructsの概要と留意点

渡邉:「そもそもConstructsとは何か」。今回のイベントでもみなさん話していますが、CDKでは、作成したい1つ以上のリソースをConstructsという単位で扱っています。L1はCloudFormationに準ずるローレベルな設定、一つ一つ設定したい場合に使うのがL1 Constructsです。L2 Constructsは、ある程度良いデフォルト値だったり、おまじないのコードがすでに内包されていたり、あるいは便利なロジックが入っていて各リソースの効率的な実装がなされていたり。L2 Constructsの事例が一番多いと思います。

L3 Constructs。ここで大事なのは、複数リソースの関係性を記述しなくても良いという選択肢です。必ずしもL3を選ぶ必要はありませんが、書きたくないという時や、スピードやその他の理由で書きたくないという場合に刺さるかなと思います。L2のVPCが各設定群、サブネットやNATゲートウェイをある程度覆い隠してくれるような抽象度でリソース作成ができる点が魅力です。

注意しなければいけないのは、L3 Constructs≠Solutions Constructsという点です。L3自体はecs-patterns or aws-ecs-patternsもあるので、Solutions Constructsは複数リソースの中でもさらにベストプラクティスに準じたサービス群のアセットだと理解するのが良さそうです。

まずは「Solutions Constructs」を使ってみる

渡邉:2つ目は「Solutions Constructsでは何ができるのか」です。調べればいろいろなうんちくが出てきますが、まずは使ってみようということです。v1時代、Solutions Constructsが発表された時は25種類ほどでしたが、現在はどれぐらいでしょうか。スライドの右にあるクエリでConstruct Hubを調べてみました。

さっそく貼っていますが、(CDK Typeが)CDK、CDK Major Versionがv2、(Programming Languageが)TypeScript、PublisherがAWS。おあつらえ向きの、Solutions Constructsというキーワードがあるのでそれを調べます。2022年3月時点では、54個ほどありました。この中には安定的版と実験的版の2種類があります。あくまでも主観ですが、サーバーレス構成でよく出てくるパターンはLambda、SNSではSTABLEが安定しているパターンが多いと思います。反対に、VPC主体のALBはWAFやCognitoを使った構成はこれからというものが多かった印象です。今回はSTABLEのみで作ってみたいと思います。

(スライドを指して)「作りたい構成」です。各リソースのリソース名は省いてしまったので左の言葉で補いますが、API GatewayはAPIのサービス、Kinesis Data Streamsはストリームのサービス。ストリームのデータをどんどんファイル化しているFirehose。さらにファイル置き場としてS3 Bucket。左から右にどんどん流れるように置いていきます。

上と下にあるのがCloudWatch LogsとIAM Role。2つのバケツがアクセスログ用のBucketです。このような構成を作りたいと思います。

正味10行もいかずにコーディングが完成

渡邉:コーディング(1/3)です。今回はTypeScriptで作ってみますが、そこに導入するためのモジュールを並べたpackage.jsonです。右の図は今日時点のLatest。一昨日(※登壇当時)aws-cdk-libのバージョンが上がったので急いでやり直しました。

着目してほしい点として、v2はaws-cdk-libにまとまっているところがメリットです。v2でもSolutions Constructsの各コンポーネントごとに明示的に書く必要があることには注意が必要です。またCDKとSolutions ConstructsのLatestは少し違う点にも気をつけてください。

コーディング(2/3)。書いたものが右に並んでいますが、3行目でインポートしたApiGatewayToKinesisStreamsというモジュールから、new ApiGatewayToKinesisStreamsでAPI GatewayとKinesis Streamsを1つずつ作ります。13行目から15行目でnewしているだけです。デフォルト値で作っています。

この3行は、1行でも書けますがここで作っています。リソースを細かく設定したい場合はPropsでチューニングできますが、あまり細かくできない点がメリットであり、デメリットでもあります。API GatewayとKinesis Streamsとロググループの一部が設定変更できます。

コーディング(3/3)。右の図は同じですが、KinesisStreamsToKinesisFirehoseToS3というちょうど良いものがあるので、これでKinesis FirehoseとS3を作ってみます。また6行目からインポートしてnewして、そこに1つのパラメーターが入っています。これはexistingStreamObjで、上で作った.kinesisStreamのオブジェクトを指定しています。

ApiGwToKinesisStreams.KinesisStreamです。作成済みのこのオブジェクトを指定すると簡単につながります。Propsについては前のスライドと似た内容なので割愛します。正味10行もいかずに無事に完成しました。ビックリですが、このページ数で終わりました。

上がAWSのマネコン(Management Console)でAPI GatewayにPOST Recordした内容、下がKinesisFirehose経由でS3に格納されたファイルをcatで開いている図です。API Gatewayからstreamのデータを加工してファイル化したことを確認できました。

今回触れられていないのがKinesisです。createCloudWatchAlarmsというパラメーターのデフォルトがTrueになっていますが、良い感じにスロットリングや処理遅延、GetRecordのKinesisを使っていると、よくある遅延やスロットリングに動的に見るアラートが付いてくれるのもうれしい点だと思います。

AWS Solution Constructsで作成したリソースは”いい感じ”になっているか

渡邉:次は実用の話。システムは無事に作れたかという問題です。APIを叩くとS3にファイルがあります。では、商用に使えるのか。公式には「ベストプラクティスを実装」と書いてありますが、ユーザー的には実際に手を動かして書いていない部分の設定レベルを知りたいので、ビルド後の資材をテストしようというのが次のお話です。

テストで知りたいのは、「AWS Solutions Constructsで作ったリソースが“いい感じ”になっているか」。「いい感じ」って曖昧ですね。明らかにダメな設定がないか。例えばEC2のセキュリティグループの穴が全開になっているとか、rootアカウントのアクセスキーがなにかあるとか。そういった明らかにダメな設定をまず潰したいです。もしあれば、抽象化しても使いたくないと思います。

テストの「方針」。せっかくCDK v2でアサーションが出たのでやりたかったのですが、今回は生成したCFnテンプレートの設定値を既存のソリューションでチェックする方針でいきたいと思います。

テスト用ツールは大きく分けて2種類。1つはAWSのSecurity Hub Standardsで、AWSが選んでくれているConfigRuleの詰め合わせ。Standardsは、良くない設定値を指摘してくれるサービスの詰め合わせみたいなものです。大きく3種類あり、使っている方はご存じだと思いますが、一番メンテナンスが活発でアクティブなFSBP(AWS Foundational Security Best Practices standard)というルールセットを使いたいと思います。

ルールはたくさんありますが、その中でも今回関係ありそうなAPI Gateway、IAM、S3を見ていきます。OSSのLinterもいろいろありました。CloudFormation LinterはCDKが自動生成しているのであまり見なくてもいいと思いますが、一応書きました。ほかにはcfn-nagです。CloudFormation Guardという似たソリューションがあるのですが、デフォルトのルールが入っていないので、今回はこちらを選んで試作してみようと思います。CheckovはBridgeCrew社の静的解析ツールで、こちらもAWSのCloudFormationに対応しているので採用しました。ちなみにTerraformやコンテナ系にも対応しているようです。

Security HubとLinterのテスト結果

渡邉:Security Hubテストを試した結果、23個中19個くらいのルールはパスしていました。パスできなかったルールを簡単に列挙して見てみます。S3のアクセスログがEnable、LifesyclePolicyがEnableという点がちょっとコケていました。ただ、2つ作っておいたNGのバケットはアクセスログ用のみです。アクセスログのアクセスログを取ってもという設定意図は理解できますし、LifesyclePolicyもそのとおりだと思います。もし気になる場合は下の設定値で抑制するのも手だと思っています。

API Gatewayもバックエンド認証にSSL証明書を使っているか。これはセキュリティ要件次第で、デフォルトで満たしているかどうかはケースバイケースだと思っています。API GatewayにWAFが関連付けられているかどうかについては、今回のモジュール名から見てもわかるとおり、スコープ外なので納得といえば納得という感じです。

気になる方はAPI GatewayとWAFのSolutions Constructsがあるので採用するのもいいと思います。一応まだエクスペリメンタル状態なので、自己責任というステータスとのことです。

Linterですが、上の2つについてはぜんぜん引っかかりませんでした。後者についてはオチがあるので後ほど話します。Checkovは76件中70件通っていたのでいいのではないかと思いますが、通っていなかったところを見ると、CloudWatch Logsの暗号化やretention daysがうまくキャッシュしていませんでした。

テスト結果の振り返り

渡邉:テスト結果の振り返りです。コアなリソースの設定レベルは、わりとチェッカーをパスしたのではないかと思っています。あくまで一般的なチェックなので、あとでプロダクトに必要なテストはやるべきですが、ベースラインとしては悪くないと思っています。

付帯リソース。CloudWatch LogsとS3は引っかかるところもあるので、気になる場合はL2で補うのも手だと思います。先ほどLinterの1つについて、このあとお話しすると言いましたが、CFnテンプレートを見てみたところ、まさに私が確認していたcfn_nagというツールを抑制する記述もあったので、なるほどと思いました。つまり、1つ前のLinterついては、作っている側も意識していることがわかったので勉強になりました。まさに抑制しているところをCheckovが拾っていた気もします。開発者側が認識したうえであえて抑制していることがわかり、1つの学びになったと思います。

Solutions Constructsを使えばシステム作成は楽になるのか

渡邉:まとめです。結局、Solutions Constructsで楽できるようになったのかについては、実装でかなり楽ができました。要件に応じたテストは必要ですが、リソース間の結びつきも含めて数行で説明できます。非機能設計も、CloudWatch LogsやIAM Logsも今回かなりうまくいきました。特にコードを書くことすらなく、ベストなものが作れました。結局細かく書きたくなってL2やL1をもう一度書き直すにしても、今回の設定を下地にすることで学ぶべき点は多いと思います。

設計で楽できる点もスライドにチェックを付けようかと思ったのですが、Solutions Constructsは抽象化単位が一般的な単位になっていて、これとシステム構成が合致するとは限らないので設計意図は残す方向でいいと思います。

また、Propsでどんどんおまじないを増やしていくとだんだんカオスになってくるので、ドキュメント化していくほうがいいと思います。

CDKはL1、L2 Constructsだけでも充分利用できますが、CDKを使うのであれば選択肢の1つとして持っておきたいと、今回Solutions Constructsを使ってみてわかりました。みなさんもチャレンジしてくれるとうれしいです。ありがとうございました。

質疑応答

吉田真吾氏(以下、吉田):ありがとうございました。ソリューションと言いつつ、プリミティブな感じに見えるものが多い気はします。業務系のエンジニアが「ソリューション」と聞くと、業務プロセスがこのセットアップでボンッみたいな、イベントデータを通したら出来上がり、みたいに勘違いしちゃうかもしれません。

少なくとも、ありがちなL2 Constructsで一つ一つのサービスを立ち上げて、つなぎ方だったり。例えばAPI GatewayとLambdaなら、ロールが適切にセットアップされているか、ソリューションで追加のConfigを入れれば使えるレベルで、設定のし忘れや実装のし忘れをなくすことができるのかなと思いました。単純に充実するといいですよね。

渡邉:そうですね。VPC系やALB系があればサーバーレスでヒットしない人でも使えると思うので、そこは期待しています。

吉田:そうですね。僕がサーバーレス以外でやる気が起きない原因は、途端に扱うリソースの数が増えて、どこか設定をし忘れると丸っきり動かなくなって、「やだ、もういい」みたいな(笑)。サーバーレスでやるからいいとなりがちなんですよ(笑)。そういうのも含めて入れるといいですよね。

吉田:新井田さん、質問が来ていればお願いします。

新井田晃史氏(以下、新井田):質問が来ています。「L3 Constructsは、ALBとかVPCとかを意識しなくてよく、VPCとかも指定できるので既存VPCへのデプロイもできそうですが、カスタマイズにも制約があると思います。カスタマイズをしたいかどうかでL2、L3を使い分けるのでしょうか」。

渡邉:考え方としては、L2とL1の使い分けに近いのではと思っていて、まずL3、L2で始めてみる。L3がフィットするようであればそこから始めるという選択肢が今回新しく増えた。これが今回の発表の肝だと思っているので、そういう観点です。

もちろん、意識しなくても、まずはALBとFargateのことだけを考えたいというユースケースもあると思うので、そういう方はL3にチャレンジしてみてもいいんじゃないかという観点です。正直L2を軸にするのが1つの方向性として正しい気もします。L3はプラスのアドオンというか、早く作りたい時の武器として持っておくと良いと感じています。

吉田:同一のVPCで、ソリューションという言い方をしますが、1つのソリューションが既存のVPC上にすでに乗っていて、もう1つ別のソリューションが同一のVPCに同居しなければいけない場合に解決しなければいけない問題なんだろうと思ったんですが、なにかありますか?

渡邉:指定できるものとできないものがあって、existing〇〇で指定できるものについては既存のVPCのIDをCDKの何かで取ってきて、それを指定してあげるだけでいい。うまくくっつかなかったり、Propsがうまく合わなかったりした時に、初めてもう少しL2でレベルを下げて書いてみようという気になると思います。

吉田:なるほど。そういう課題が発生してもやれなくはないから、やる方向で問題ない。やる時に、L2でやるのかを検討する必要があるということですね。

渡邉:そうだと思っています。

吉田:藤井さんはいかがですか?

藤井貴昭氏(以下、藤井):中身を覗いてみて、EKSを作るのが面倒くさいものも部品が揃っていそうなので、渡邉さんのように中身をチェックしてうまく使えるものを使っていくと、すごく楽になるんじゃないかと思いました。

吉田:渡邉さんはここまでとします。なにかまた質問があればTwitterで拾うので引き続きよろしくお願いします。

渡邉:よろしくお願いします。