🐾

CloudFormationで構築されたインフラを引き継いで得た学び

2024/12/04に公開

今年10月にEDOCODEに入社した座主(ざす)と申します。多少AWSとCloudFormationの知識があったので、10月11月はCloudFormationで構築されていた既存インフラ周りの修正と構築のタスクを担当いたしました。
本記事ではその中で得た学びをいくつか紹介したいと思います。

CloudFormationとは

CloudFormationは、IaC(Infrastructure as code)の一種で、AWSの各種リソースをYAMLやJSONで記述したテンプレートファイルを使って管理するシステムです。

当記事ではIaCやCloudFormationに関する基本的な事柄は解説しません。
また、CloudFormationと毎回記述すると長いので、以降のセクションではCFnと記述いたします。

Type:でググる(リファレンス活用のススメ)

CFnに限らずですが、コードを書く際には公式リファレンスを最大限活用することが重要だと常々思っています。ですが、CFnのリファレンスは公式ユーザーガイドの「テンプレートリファレンス」の奥深くにあり、探し出すのが少々つらいです。

結論から書くと、CFnでリファレンスを活用したい場合は「リソースのTypeの値でググる」と公式リファレンスのページが直接ヒットする事が多いので、これがおすすめです。

以下は、とあるS3バケットを定義しているCFnテンプレートです。
たくさんのPropertiesが定義されていて頭が痛くなりそうですね。

Resources:
  DeploymentBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: edocode-xxxx-bucket
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
            BucketKeyEnabled: false
      LifecycleConfiguration:
        Rules:
          - Id: expiration
            Status: Enabled
            ExpirationInDays: 550
      VersioningConfiguration:
        Status: Suspended
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

「AWS::S3::Bucket」でググると上で書いた通りAWSの公式ドキュメントがトップにヒットします。

例えばBucketEncriptionプロパティについて知りたければ、ページ上部のJSONやYAMLのSyntaxの項からプロパティの詳細へと移動し、そこにあるTypeからさらに中身の定義へと順々に辿っていくことができます。

このように公式リファレンスをあたることでリソースの正確な仕様を把握できますし、また、「こういうプロパティもあったのか!」という具合に新たな学びが得られることも多いです。

テンプレートの適用時の変更セットの確認

次はテンプレートを編集して適用する際の変更セットの確認の重要性を解説します。

まずは以下のEC2インスタンスを定義しているテンプレートで新しくスタックを作成します。

AWSTemplateFormatVersion: "2010-09-09"

Resources:
  Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Role

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "-"
      VpcId: !ImportValue Edocode-VPC-VPCID

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile
      ImageId: !Sub "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64}}"
      InstanceType: t4g.nano
      KeyName: example-key
      SecurityGroupIds:
        - !Ref SecurityGroup
      SubnetId: !ImportValue Edocode-VPC-PublicSubnetAID
      AvailabilityZone: ap-northeast-1a

以下のようにスタックが作成されました。

次はInstanceのAvailabilityZoneをap-northeast-1cに変更してみます。

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ...
      AvailabilityZone: ap-northeast-1c

AWSコンソールから「更新」ボタンを押して、書き換えたテンプレートをアップロードして進んでいくと「変更セットのプレビュー」が見れます。

右側の「置換」のカラムが「True」となっています。

左下にある「変更セットの表示」ボタンを押すと、より詳しい内容が表示されます。

ポリシーアクションが「ReplaceAndDelete」と赤く表示されていて、なんだか危険な香りがしてきますね。

この変更セットは適用せずに削除して、今度はInstanceにTagをつけてみましょう。

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ...
      Tags:
        - Key: Name
          Value: CFn-Example-EC2

同様にして「変更セットのプレビュー」まで進みます。

右側の「置換」のカラムが「Conditional」となっていますね。

「変更セットの表示」ボタンを押すと、

「置換」のカラムが「False」と判明しました。

このカラムが意味するところは「このリソースを削除して置き換えるかどうか」です。

RoleやSecurityGroup等のテンプレートで定義されている内容が全てであるリソースは置換が発生しても特に問題ない事がほとんどです。

ですが、EC2インスタンスやS3など内部にデータを保持するリソースを置換してしまうと、中のデータは失われてしまいます。そういうタイプのリソースをCFnで扱う際は必ず変更セットを確認する事が重要ですね。

ちなみに「プロパティレベルの変更」カラムにある「詳細を表示」をクリックするとdiffのような表示で変更内容が確認できたりしますので、ここも有効活用できる事が多いです。

rainコマンド

前の項ではスタックの更新をWebのAWSコンソールから行いました。ですがコンソールから更新ボタンを押してアップロードするテンプレートを選んでボタンを押してチェックを入れてボタンを押して、、、と毎回やるのは骨が折れます。
また、awscliでやるのも長ったらしいオプションの指定が大量に必要で、煩わしいです。

というわけでrainコマンドを導入しましょう。

rain deploy <テンプレートファイル名> <スタック名>

とするだけでdeployする事ができます。
(Y/n)のプロンプトで止めておいて、AWSコンソールから(前項でやったような)変更セットの確認もできたりします。
非常に便利なのでCFnを使うならインストールしておくのがおすすめです。

欲を言えばCLI出力で変更内容の置換確認やdiff表示までできるとよいのですが。。
ちなみに後述のCDKでは、CLIでそのあたりも確認できてました。

rainコマンドの導入法や詳しい使い方等は公式ドキュメントを参照してください。

ドリフト

最後にドリフトです。

ドリフトとはCFnテンプレートで指定した設定と、実際のリソースの設定にずれが生じている事を指します。CFnでリソースを作成した後に、WebのAWSコンソールやawscli等を使って手動でリソースの設定を変更したりすると発生します。

そしてここが重要なのですが、ドリフトが発生しているとそのリソースを含むCFnスタックを、CFnを使って更新する事ができません。(手動では可能)

CFnで作られたインフラの設定タスクを任され、テンプレートのリポジトリとAWSのリソースの状況を確認した時、多くのスタックがこのドリフト状態にあることに気付きました。
これではIaCを使っていないのとほぼ同じ状況だと思ったので、取りうる選択肢は2つ。

  1. このまま手動設定で単純にタスクをこなす
  2. ドリフトを解消してIaC管理下に戻す

判断材料として、以下。

  • staging環境の各種設定が終わったら、ほぼ同じ内容のproduction環境の構築がある
  • 将来さらにリソースが増えていくのは確実である

1番で進めた場合は重複作業となり、手動設定に起因する細かな差異やミスでハマってしまう等のリスク、将来の管理コストが増大する等の懸念もあり、2番で行くこととしました。

ドリフトの解消方法自体を解説すると、それだけでいくつか記事が書けてしまうボリュームになりそうなので割愛します。検索すると公式ドキュメントやすばらしいブログ記事がいくつも出てくると思います。実際、それらを参考にドリフトの解消に取り組みました。

一時的には大変でしたが、今後のインフラ設定や、他のタスクでインフラ周りが関連してきた時にとても動きやすくなったかなと思っています。今のところは自己満足に過ぎないかもしれないですが。。
CFnのみならずIaC全般をチームとして使いこなせるようになるのが次の密かな目標です。

他のIaCツール

CloudFormation以外にも、AWS CDKやterraform等のIaCツールが存在しています。
AWS等のクラウドサービスを使っていく上でIaCはもはや必須だと思いますので、クラウドインフラに興味がある方は、何らかのIaCツールの使い方を覚えるといいと思います。

AWS CDK

AWS CDKは

  • TypeScript
  • JavaScript
  • Python
  • Java
  • C#
  • Go

などの汎用プログラミング言語でインフラ構成を記述するIaCシステムです。
上記言語で記述されたコードからCFnテンプレートを生成してデプロイする仕組みとなっています。
内部でCFnを使用している関係上、ある程度の規模になるとCFnの知識が必須となってきます。

実は前職ではCDKを使ってAWS環境構築を担当しておりました。
機会があればCDKを紹介する記事も書きたいと思います。

terraform

こちらも有名ですね!terraformはAWSのみならず、Google Cloud、Azure等のクラウドサービス全般に対応しているIaCツールです。

EDOCODEの僕が所属しているチーム内にはterraformに詳しい方が2名おられるので、僕自身もterraformが使えるようになりたいところです。

Discussion