selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

Terraform / aws-cdk を比較してみた(個人の所感です)

プライベートにて、経験の浅いチームから aws-cdk と terraformどちらが良いのですか?と聞かれたのでまとめてみました。間違いがあればご指摘いただければ幸いです。

前提

  • 利用する人間はIaCの初心者である
  • 当然 CloudFormation等に関する基本的な知識がないものとする

結論

  • 初学者にとっては、IaCのツールとしてはTerraformを採用するのが良さそう
  • その理由として最も大きなものは、「学習コストが少ない」こと
  • 「学習コストが少ない」についてブレークダウンすると下記のようになる
    • NewRelic や Sentry、その他SaaSとの連携もIaC化をする場合、Terraformなら1つで対応できる
    • aws-cdk は CloudFormation、aws-cdk、TypeScript、AWSの各種リソース等理解しなければ十分に活用できない
      • 欲を言えば、プログラミングの設計スキルも必要となる
    • TerraformはTerraformとAWSの各種リソースに対する理解の2点のみで良い
      • Terraform自体のキャッチアップを1とするなら、TypeScriptは4~5くらいの規模感
    • aws-cdk は原則として、インフラリソースの手作業による更新が許容されない
      • そのため、consoleを動かして挙動確認しながら理解を深めることができない

背景

2020年現在、AWSのインフラリソースを定義するメジャーな方法は3つあります。AWS CloudFormation、aws-cdk、Terraformです。まずはそれらの立ち位置について簡単に説明していきましょう。

AWS CloudFormation

AWS CloudFormationAWSが公式で提供しているIaCのためのツールです。公式で提供しているということもあり、ほぼすべてのAWSインフラリソースをこれで定義することができます。一般的によく利用されるようなインフラ設定については、サンプルコードがAWS公式によって提供されています。そのため利用者は、大抵のシステムを公式が提供してくれるテンプレートに少し手を加えるだけで構築することができます。また CloudFormationを拡張し、Serverless Application開発を容易にした SAM(Serverless Application Model)という機能も存在します。SAM Localを利用すればコードを手元で動作確認しながら、サーバレスアプリケーションの開発ができます。

良い事の多い CloudFormationですが、実際にコードを見てみましょう。下記にRailsアプリケーションを作る際のCloudFormationの一部を示します。これをサクサク読み書きできるようになるには、相当な修練が必要であることは想像に難くないでしょう。

// https://s3.us-west-2.amazonaws.com/cloudformation-templates-us-west-2/Rails_Single_Instance.template
"Properties": {
  "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                      { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
  "InstanceType"   : { "Ref" : "InstanceType" },
  "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
  "KeyName"        : { "Ref" : "KeyName" },
  "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
    "#!/bin/bash -xe\n",
    "yum update -y aws-cfn-bootstrap\n",
    "/opt/aws/bin/cfn-init -v ",
    "         --stack ", { "Ref" : "AWS::StackId" },
    "         --resource WebServer ",
    "         --configsets full_install ",
    "         --region ", { "Ref" : "AWS::Region" }, "\n",

    "/opt/aws/bin/cfn-signal -e $? ",
    "         --stack ", { "Ref" : "AWS::StackId" },
    "         --resource WebServer ",
    "         --region ", { "Ref" : "AWS::Region" }, "\n"
    ]]}}        
 },
 "CreationPolicy" : {
  "ResourceSignal" : {
    "Timeout" : "PT30M"
  }
}

SAMを用いて小さいServerless Applicationを作るのであれば良いですが、ある程度の規模のシステムになると Cloud Formationについて深い理解をしていなければ、すべてを管理することはとてもむずかしいと思えます。ここで詳細は述べませんが、CloudFomartion自体はIaCの原理原則を守り、リソース間の依存関係の確実な管理をしてくれて、安全な更新が可能な良いものです。ですが、少し専門家向け過ぎるツールであると言えます。

aws-cdk

CloudFormationの辛さについては上述しました。AWSもその問題点を理解しており、CloudFormation Designer などのツールを公開し、GUI上の設定によってCloudFormationが自動生成されるような仕組みを用意していました。しかし、結局の所生成されるのは Cloud Formationです。Cloud Formation自体が読み難いという根本的な問題は解決していません。

その課題に対処するために作られたのが aws-cdk です。端的に言うと、VueやReactからHTMLを生成するように、TypeScriptで作ったプログラムからCloudFormationを生成できるツールです。下記の図のようにcdk がコンパイラとなってCloud Formationを生成します。

f:id:selmertsx:20210507095951p:plain

aws-cdk の登場によって、AWSはCloudFormationを assembly language というポジションにしました。これから先、主にユーザーが利用するのは aws-cdkであり、専門性高いエンジニアがツールを構築したり、cdkのOSSにコミットしたり、デバッグ等で利用するときに Cloud Formationの理解が必要になる。というのが AWS の考えだと思います。

実際に利用している aws-cdk のプログラムを下記に示します。TypeScriptに関する知識があることは前提になりますが、CloudFormationよりも格段に読みやすくなったのではないでしょうか。TypeScriptであるため IDEの補完機能による恩恵も受けやすく、明らかに間違った設定をすればコンパイルすることもできなくなります。

export class ApiGatewayStack extends Stack {
  public readonly api: Resource;
  public readonly authorizer: TokenAuthorizer;

  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const stage = tryGetStage(this.node);
    const cmsRestApi = new RestApi(this, `${stage}-rest-api`, this.getCmsRestApiProps(stage));
    this.authorizer = createAuthorizer(this, stage, cmsRestApi);
    this.api = RestApi.root.addResource("api");
  }

  private getRestApiProps(stage: string): RestApiProps {
    return {
      restApiName: `${stage}-rest-api`,
      deployOptions: {
        loggingLevel: MethodLoggingLevel.INFO,
        dataTraceEnabled: true
      },
      minimumCompressionSize: 1024,
      endpointConfiguration: {
        types: [EndpointType.REGIONAL]
      }
    };
  }
}

さらに、aws-cdk から CloudFormationを生成することができるため、CloudFormationで利用可能な各種ツールをそのまま利用できます。例えば生成したCloudFormation を CloudFormation Designerに取り入れれば、awsのインフラ図を生成することもできます。また、SAM Localと連携してローカル環境で動作させることもできます。最近ではAWS公式から serverless pattern というリポジトリが作られました。頻出する数多くのインフラパターンを詰めたコードのサンプル集です。これを参考にすれば、新しいシステムを作る際に、インフラのベストプラクティスを踏襲して安全なシステムを作りつつも、工数を大幅に削減することができます。

さて、ここまで良いところばかりを述べてきましたが、aws-cdkにもデメリットは数多くあります。まず最も大きなものとしては、「学習コストの大きさ」 です。少なくとも現状では、 CloudFormation、aws-cdk、TypeScriptの3点の知識が必要になります。さらに長期間に渡ってメンテナンス可能なIaCを実現しようとすれば、オブジェクト指向の理解・実践も必要となります。何より大きい課題は、「公式による力強い明確なベストプラクティスやコーディング規約の不在」 です。aws-cdkを取り入れるチームは、自ら設計指針を考え、適切なコーディング規約を作り、チーム内で合意する必要があります。そうしなければ、これまでの数多くのソフトウェアと同じように、負債を多く抱えたシステムになってしまうでしょう。Cloud Formationは、Stack間依存の取り扱いを非常に厳密に行っており、その厳密さがシステムに安定性を与える反面、理解せずに利用すると返却の難しい負債を作り出してしまいます。

これまでの内容をまとめると、「aws-cdk をチームで効果的に利用し続けるためには、CloudFormationやTypeScript、IaCの原則について一定以上の理解をしたエンジニアが集まり、0から設計指針やコーディング規約を作る能力・余地がある」というのがaws-cdkを採用する際の条件になります。

森岡個人としては、大きな開発組織のSREや、フルスタックエンジニア個人による利用では力を発揮するツールという印象を持ちました。

Terraform

Terraform とは Hashcorp社により提供される IaCのツールです。最も大きな特徴としては特定のベンダーに閉じずに、様々なサービスの設定をこれでIaC化できることです。AWSGCP、Azureはもちろん、Auth0NewRelicなど、その他多種多様なSaaSに対応しています。さらに、Go言語に詳しければ自ら作ることもできます。

Terraformの記述方法は次のようになります。Terraformは設定をDSLで記述するため、aws-cdk ほど自由度はありませんが、CloudFormationよりはだいぶ楽に記述することができます。

resource "aws_iam_role_policy" "iam_policy_for_iot" {
  name = replace("${var.system_name_prefix}_iot_rule_to_kinesis_firehose_policy", "-", "_")
  role = aws_iam_role.iot_to_firehose.id

  policy = jsonencode(
    {
      "Version" : "2012-10-17",
      "Statement" : [
        {
          "Effect" : "Allow",
          "Action" : [
            "firehose:PutRecord",
            "firehose:PutRecordBatch"
          ],
          "Resource" : var.iot_data_firehose_arn
        }
      ]
    }
  )
}

また、Terraformは設計における数多くのベストプラクティスを公式で提供しています。そのため、公式のドキュメントをしっかり読み込めば、開発における大部分のユースケースには対応可能になっています。

ツールの比較

aws-cdkの背景からお話したように、これから先 CloudFormation を素のまま利用しないことを想定していると思われます。そのため、CloudFormationについては比較対象から除外します。よって、以降では aws-cdk と terraformについての比較を行っていきます。

比較表と結論

Terraform aws-cdk
学習容易性 ×
製造容易性
インフラの修正容易性 ×
コードの修正容易性

Terraformとaws-cdkの比較を行った表が上記になります。結果をざっくり一言で言えば、「分かってる人が使うならcdkの方が良いけど、そこまで分かってる人多分市場を探してもそうそういないよね」という言葉になります。以降、比較軸の内容について詳細に記述していきます。

学習容易性 Terraform◎ aws-cdk ×

学習容易性としては、Terraformを優勢としています。理由としては下記の通りです。

  • aws-cdkが必要とする知識は下記の通り
    • 土台としている CloudFormation に関する知識
      • 遠い将来はいらなくなる可能性が高いとはいえ、現状では必須
    • aws-cdk のフレームワーク自体の知識
    • TypeScriptの言語に関する知識
    • オブジェクト指向を用いた設計方法に関する知識
  • Terraform が必要とする知識は Terraformの使い方についてのみ
  • aws-cdkは公式でベストプラクティスを提供していないため自ら考え、構築しなければならない
  • Terraformは公式がベストプラクティスを提供しているため、それを守ればそれなりのものが作れる
  • なかなか良い書籍も出ており、これを読んでおけばある程度の成果物の品質を担保しやすい
  • NewRelic、Sentry等のSaaSもIaC化を検討しており、結局は Terraformを学習しなければならない

製造容易性 Terraform △ aws-cdk ◎

  • aws-cdkの terraform に対する優位性は下記の通り
    • TypeScriptで記述するため型推論による恩恵を受けられる
      • 必要な設定はIDEが教えてくれるし、間違った設定は実行前にコンパイルエラーになる
    • オブジェクト指向によるコードの整理が可能である

手動によるインフラ修正容易性 Terraform ◎ aws-cdk ×

この項目はAWS Console上から手動で行った変更を IaCツールで取り込む際の難しさを示すものです。

コードの修正容易性

  • terraformではmoduleレベルでのリファクタリングの難易度が非常に高い
    • 一つ一つのリソースを terraform state mv していく必要がある
  • aws-cdkは同一の成果物(CloudFormation)が生成されれば良いので、コード自体は柔軟に変更が可能
    • スタック自体を移動するのは難しい

結論

以上、Terraformとaws-cdkの比較を行いました。 経験の浅いチームがプロダクションで利用する状況においては、学習資料が整っているという観点から terraformを使うのが良いのではないでしょうか。 aws-cdkは非常に便利な道具であるものの、深く理解して慎重に使わなければ返済の難しい負債を作り出します。 なので、趣味アプリ等でなれてから本番運用に持っていくと良いのではないかなと思います。