Open12

AWS CDK 触る

nukopynukopy

目標

  • CDK で実用するものを動かす
  • 毎日定時で AWS の料金を Slack で通知するやつ作る
nukopynukopy

いきなりドキュメントのタイトルがかわいそうなことになってる

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html

CFn のテンプレートを自動生成するためのコードを書くってことか

  • App
    • Stack
      • Construct

nukopynukopy

AWS CDK の役割と Construct

AWS CDK では、AWS リソースを抽象化した、App > Stack > AWS_Resource で構成される木構造を定義し、これをデプロイする役割を担う。

  • Construct
    • App, Stack AWS_Resource の基底クラス

https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/02-concepts-constructs

App Construct

Appは construct tree のルートとして使用することができる唯一の Construct だから、App は、任意の初期化引数を必要としません。App は、Stack インスタンスを定義するための Scope として使用できます。

nukopynukopy

App lifecycle

ref: https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/03-concepts-apps#app-lifecycle

cdk deploy を実行したときに CDK app(ソースコード)が通過するフェーズ

  1. CDK App を実装する(TypeScript、Go、Python、...)
  2. cdk deploy を実行
  3. CDK CLI が CDK App を呼び出す
    • Construct → Prepare → Validate → Synthesize
    • 初期化 → 準備 → 検証 → 合成
      • Construct フェーズでは、定義された全ての Construct のインスタンス化、リンクが行われる。全ての Construct(App, Stack, それらの子 Construct)がインスタンス化され、constructor chain が実行される。
      • Prepare フェーズは prepare メソッドを実装した Construct のみ参加する。
      • Validate フェーズは validate メソッドを実装した Construct のみ参加する。Construct を検証して正しくデプロイされる状態にあるかを確認できる。
      • Synthesis フェーズは app.synth() の呼び出しでトリガーされる。Construct tree(CDK で定義する AWS リソース群の木構造)をトラバースし、全ての Construct について synthesize メソッドを呼び出す。
      • ここまででデプロイアーティファクトが作られる。
        • デプロイアーティファクト
          • AWS CloudFormation テンプレート
          • AWS Lambda アプリケーションバンドル
          • ファイルと Docker イメージアセット
          • その他のデプロイアーティファクト
  4. デプロイ
    • Synthesize フェーズで生成されたデプロイアーティファクトを取得し、AWS 環境へデプロイする。
nukopynukopy

Stack, Stack API

ref: https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/04-concepts-stacks

AWS CDK でのデプロイの単位を Stack と呼ぶ。

Stack の Scope 内で直接または間接的に定義された全ての AWS リソースは単一のユニットとしてプロビジョニングされる。

Stack は AWS CloudFormation Stack を介して実装されているので、AWS CFn と同じ制限に従う。

CDK App では、任意の数の Stack を定義できる。

CDK では CFn のパラメータの扱いとは考え方が異なる

  • AWS CDK
    • 具体的なテンプレートが合成時に決定されるアプローチを取っている
  • AWS CFn
    • CloudFormation パラメータはデプロイ時にのみ解決される(つまり app のプログラム中でパラメータの値を参照できない)

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  EnvType:
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues:
      - prod
      - test
    ConstraintDescription: must specify prod or test.
## デプロイ時に解決される Conditions
Conditions:
  CreateProdResources: !Equals 
    - !Ref EnvType
    - prod
Resources:
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: ami-0ff8a91507f77f867
  MountPoint:
    Type: 'AWS::EC2::VolumeAttachment'
    Condition: CreateProdResources
    Properties:
      InstanceId: !Ref EC2Instance
      VolumeId: !Ref NewVolume
      Device: /dev/sdh
  NewVolume:
    Type: 'AWS::EC2::Volume'
    Condition: CreateProdResources
    Properties:
      Size: 100
      AvailabilityZone: !GetAtt 
        - EC2Instance
        - AvailabilityZone

Stack の命名規則

AWS CloudFormation stack の物理名は、ツリー内の Stack の Construct path パスに基づいて AWS CDK によって自動的に決定されます。デフォルトでは、Stack の名前は Stack オブジェクトの construct ID から派生しますが、次のように stackName prop を使用して明示的な名前を指定できます。

new MyStack(this, "not:a:stack:name", { stackName: "this-is-stack-name" });
nukopynukopy

Environments 環境

ref: https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/05-concepts-environments

デプロイ先の AWS アカウントやリージョンを切り替える方法や、それぞれの方法のメリットが説明されています。


app の各 Stack インスタンスは、明示的または暗黙的に環境(env)に関連付けられている。環境は、

  • スタックがデプロイされる予定のターゲット AWS アカウント
  • スタックがデプロイされる予定のターゲットリージョン
nukopynukopy

Stack をインスタンス化するときに環境を指定しない場合、Stack は environment-agnostic(環境非依存) と言われる。

このような environment-agnostic Stack から合成された AWS CloudFormation テンプレートは、stack.account, stack.region, stack.availabilityZones などの環境関連の属性について、デプロイ時に解決しようとする

cdk deploy 実行時のデプロイ先の解決

cdk deploy を使用して環境を問わない Stack をデプロイする場合、AWS CDK CLI は指定された AWS CLI プロファイル(または何も指定されていない場合はデフォルトプロファイル)を使用して、デプロイ先を決定する。

AWS CDK CLI は、AWS CLI と同様のプロトコルに従って、AWS アカウントで操作を実行するときに使用する AWS credentials を決定する。

本番環境における「環境」の設定

なるほど

本番用 Stack では、env プロパティを使用してアプリ内の各 Stack の環境を明示的に指定することをお勧めします。次の例では、その 2 つの異なる Stack に対して異なる環境を指定しています。

const envEU = { account: "2383838383", region: "eu-west-1" };
const envUSA = { account: "8373873873", region: "us-west-2" };

new MyFirstStack(app, "first-stack-us", { env: envUSA });
new MyFirstStack(app, "first-stack-eu", { env: envEU });
nukopynukopy

訳者の追記なるほど

ref: https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/05-concepts-environments#ヤマタツ追記

ヤマタツ追記

秘匿情報でない限り、TS を使う場合には環境値も TS 内で定義するほうが一貫性があって扱いやすいと、個人的には思っています。その場合、デプロイ先の環境名をキーにして環境固有値を引けるようにすると扱いやすいと思います。

const ENV_NAMES = ["dev", "stg", "prd"] as const;
type EnvName = typeof ENV_NAMES[number];
type EnvValues = {
  envName: string;
  domainName: string;
  externalServiceEndpoint: string;
};

export function getEnv(): EnvValues {
  const envName = (process.env.ENV_NAME || "dev") as EnvName;
  switch (envName) {
    case "dev":
      return {
        envName,
        domainName: "dev.example.com",
        externalServiceEndpoint: "https://dev.external.example.com",
      };
    case "stg":
      return {
        envName,
        domainName: "stg.example.com",
        externalServiceEndpoint: "https://stg.external.example.com",
      };
    case "prd":
      return {
        envName,
        domainName: "example.com",
        externalServiceEndpoint: "https://external.example.com",
      };
    default:
      const _: never = envName;
      throw new Error(
        `Invalid environment variable ENV_NAME has given. ${envName}`
      );
  }
}

環境変数を cdk deploy 時に渡す

ENV_NAME=prd cdk deploy

cdk の context という機能もあるみたい。

https://docs.aws.amazon.com/cdk/v2/guide/context.html

cdk deploy -c ENV_NAME=prd

使ってみないとわからんね。context に他の用途があるかもしれない。

でも、多言語ならいざしらず、使い勝手が同じなら、Node.js が純粋にできること(環境変数の取得)をわざわざ CDK 独自機能で解決する必要はない、というのが個人的な考え方ではあります。

nukopynukopy

続きここから
なかなか長い

https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/06-concepts-resources

  • 07 Concepts - Resources
    • 読んどいた方が良さそう
    • AWS CDK を書いてて、ハマるケースの多くがこの章に書かれていると思う。
      コードブロックだけでも良いので眺めていって、気になるところを文章読んでみてもいいかも。

    • とにかく読んでほしい。

  • 08 Concepts - Identifiers
    • Construct IDs は必読
    • Construct IDsは読んでおいて損はないかと思う。第 2 引数に渡されるidの説明。重複が怒られるケースと怒られないケース(scope の概念)がわかる。

    • それ以降の項は全く知らなくても何ら問題ないと思う。

  • 09 Concepts - Tokens
  • 10 Concepts - Parameters
  • 11 Concepts - Tagging
    • すぐ必要にならないと思うけどどうせ必要になるから読んでおく
    • CDK でのタグの付け方。オプションとかも含めて学びが多い。タグ付けが必要になったときには読み直したい。

  • 12Concepts - Assets
    • CDK の Assets を知らない場合は是非読んで欲しい。インフラデプロイとアプリケーションデプロイの境目を消して、CD の構成を容易にし、フルサイクルエンジニアが DevOps で働くのを大いに助ける、CDK における重要な機能の説明。

  • 13 Concepts - Permissions
    • 訳者強く推奨
    • CDK を始める方は読んでおくことを強く推奨します。

    • CDK では L2 以上のコンストラクト(Cfnで始まってるクラス以外)を使っていれば、勝手に最小限の権限が与えられた Role が生成されて割り当てられるので、Terraform や CFn と違って Role をいくつも宣言する必要がありません。それでもリソースからリソースへ権限を渡すのにgrantメソッドをよく使うので、読んでおくとコーディングが捗ります。

  • 14Concepts - Context
  • <s>15 Concepts - Feature Flags</s>
  • 16 Concepts - Aspects
  • <s>17 Concepts - Escape Hatches</s>
  • 18 Concepts - Bootstrapping

読まなくて良さそう

ざっと目を通して今は必要なさそうな章

  • 15 Concepts Feature Flags
  • 17 Concepts Escape Hatches
    • https://zenn.dev/yamatatsu/books/aws-cdk-documentation-jp/viewer/16-concepts-escape-hatches
    • 「このクラス、Props にあの機能生えてないじゃん!CloudFormation ならできるのに!」ってときに読む。

    • このときが来たら読もう。使い始めていきなりは来ないはず。
    • もしその機能が AWS CloudFormation を通してではなく、直接の API コールを通してのみ利用できる場合、唯一の解決策は、必要な API コールを行うために AWS CloudFormation Custom Resource を書くことです。AWS CDK はこれらを書くことを容易にし、それらを通常のコンストラクトインターフェイスにラップするので、他のユーザーの観点から、機能はネイティブに感じられるのです。