TECHSTEP

ITインフラ関連の記事を公開してます。

rainを使ってCloudFormationのデプロイを楽にする

今回はCloudFormationのデプロイをより便利にする rain コマンドを試しました。

rainとは

rainは複数のコマンドが用意されており、それぞれの特徴は公式リポジトリ に記載されています。特によく使いそうなものをいくつか取り上げます。

  • rain deployによるインタラクティブなデプロイ: rain deploy コマンドは aws cloudformation deploy コマンドのように、CloudFormationテンプレートの定義に従ってAWS環境にリソースを作成・更新するコマンドです。 rain deploy コマンドの場合、未指定のパラメータの入力の案内や変更内容の提示、実行後の進行状況の表示など、インタラクティブな要素が特徴となります。また、初回のStackデプロイに失敗しても前回分のStackの削除を実行することなく、deployコマンドを続けて実行することが可能です ( 先日の CloudFormationへのアップデートにより、 DeletionPolicyRetainExceptOnCreate を使うことで同様の効果を得ることはできるようになりました) 。
  • rain fmtによるテンプレートのフォーマット: rain fmt コマンドはCloudFormationテンプレートのフォーマットやJSON/YAML間の変換を行います。AWS CLIでは aws cloudformation validate-template などでテンプレートの検証はできましたが、テンプレートのフォーマットを行うことはできませんでした。
  • rain logによるログの表示: rain log コマンドはStackで発生したログを表示します。表示するログは、指定したStackに加えてそれに関連付けられたNested Stackからのログも結合します。これまではStackのログを見るためにAWSマネジメントコンソールに移動する必要がありました。
  • rain buildコマンドによるテンプレートの構築: rain buildコマンドは指定したAWSリソースのCloudFormationテンプレートを表示します。これによりCloudFormationテンプレートを作り始めるときのinitialテンプレートが簡単に手に入ります。

rainを試す

ここからrainコマンドを試してみます。実行した環境はAWS Cloud9になります。

インストール

インストール方法はいくつかありますが、ここではリポジトリで提供されたバイナリをダウンロードして使います。rainはGo言語製なのでバイナリを配置すれば使えます。

$ wget https://github.com/aws-cloudformation/rain/releases/download/v1.4.3/rain-v1.4.3_linux-amd64.zip

$ unzip rain-v1.4.3_linux-amd64.zip 
Archive:  rain-v1.4.3_linux-amd64.zip
   creating: rain-v1.4.3_linux-amd64/
  inflating: rain-v1.4.3_linux-amd64/README.md  
  inflating: rain-v1.4.3_linux-amd64/LICENSE  
  inflating: rain-v1.4.3_linux-amd64/rain  

$ sudo mv rain-v1.4.3_linux-amd64/rain /usr/local/bin/

# インストール後の確認
$ rain --version
Rain v1.4.2 linux/amd64

rain build / deployによるリソースの作成

初回のStack作成成功時

まずは基本的な操作として、テンプレートの生成とデプロイを行います。今回はAmazon SQSをターゲットにリソースを作成します。

まずは rain build コマンドでinitialテンプレートを生成します。

$ rain build AWS::SQS::Queue > sqs-test.yaml

$ cat sqs-test.yaml
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      ContentBasedDeduplication: false # Optional
      DeduplicationScope: CHANGEME # Optional
      DelaySeconds: 0 # Optional
      FifoQueue: false # Optional
      FifoThroughputLimit: CHANGEME # Optional
      KmsDataKeyReusePeriodSeconds: 0 # Optional
      KmsMasterKeyId: CHANGEME # Optional
      MaximumMessageSize: 0 # Optional
      MessageRetentionPeriod: 0 # Optional
      QueueName: CHANGEME # Optional
      ReceiveMessageWaitTimeSeconds: 0 # Optional
      RedriveAllowPolicy: '{"JSON": "CHANGEME"}' # Optional
      RedrivePolicy: '{"JSON": "CHANGEME"}' # Optional
      SqsManagedSseEnabled: false # Optional
      Tags:
        - Key: CHANGEME
          Value: CHANGEME
      VisibilityTimeout: 0 # Optional

Outputs:
  MyQueueArn:
    Value: !GetAtt MyQueue.Arn

  MyQueueQueueName:
    Value: !GetAtt MyQueue.QueueName

  MyQueueQueueUrl:
    Value: !GetAtt MyQueue.QueueUrl

コマンドを実行して # Optional と書かれた箇所は必須でないパラメータになります。ここではQueneName とタグだけを指定しました。

AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      Tags:
        - Key: test
          Value: rain

次にテンプレートを指定してQueueリソースを作成しますが、 rain ls コマンドを実行すると、新規Stackを作成する前に既存のStackの状態を知ることができます(例えば新規に作成するStack名を検討するときなど)。

$ rain ls
CloudFormation stacks in ap-northeast-1:
  aws-cloud9-test01-1fd0ca9e39c449608d7d6484a29f4069: CREATE_COMPLETE

(以降割愛)

次にrain deploy コマンドを実行し、Stackを作成します。なお、特にオプションで指定しない場合、テンプレートのファイル名がStack名に使われます。

$ rain deploy sqs-test.yaml 
Rain needs to create an S3 bucket called 'rain-artifacts-123456789012-ap-northeast-1'. Continue? (Y/n) Y # 1. S3バケットの作成
CloudFormation will make the following changes: # 2. 変更予定内容の表示
Stack sqs-test:
  + AWS::SQS::Queue MyQueue
Do you wish to continue? (Y/n) Y
Deploying template 'sqs-test.yaml' as stack 'sqs-test' in ap-northeast-1.
Stack sqs-test: CREATE_COMPLETE # 3. 進行状況の表示
Successfully deployed sqs-test # 4. 完了メッセージ

コマンド実行時の流れを簡単に記載します。

  1. rain deploy コマンドの初回実行時は、テンプレートを配置するためのS3バケットの作成が必要となります。
  2. 変更予定内容を表示します。ここでは新規リソースの追加が表示されています
  3. ここはデプロイ後の進行状況が表示されます。テキストだけではイメージが湧かないと思うので、公式リポジトリのデモの様子を見たり、実際に使ってもらえればと思います。
  4. デプロイが完了するとその旨が表示されます。

もう一度 rain ls コマンドを実行すると、先ほどはなかった sqs-testというStackが追加されています。

$ rain ls
CloudFormation stacks in ap-northeast-1:
  aws-cloud9-test01-1fd0ca9e39c449608d7d6484a29f4069: CREATE_COMPLETE

(一部抜粋)

  sqs-test: CREATE_COMPLETE

ここで rain log コマンドを実行すると、先ほどのStack作成で発生したログが表示されます。ただしデフォルトではエラーなどの重要なログしか表示されないため、ここでは --all オプションを追加します。

$ rain logs sqs-test
No interesting log messages to display. To see everything, use the --all flag

$ rain logs sqs-test --all
Aug  5 07:29:41 sqs-test/sqs-test (AWS::CloudFormation::Stack) CREATE_COMPLETE
Aug  5 07:29:40 sqs-test/MyQueue (AWS::SQS::Queue) CREATE_COMPLETE
Aug  5 07:28:29 sqs-test/MyQueue (AWS::SQS::Queue) CREATE_IN_PROGRESS "Resource creation Initiated"
Aug  5 07:28:28 sqs-test/MyQueue (AWS::SQS::Queue) CREATE_IN_PROGRESS
Aug  5 07:28:25 sqs-test/sqs-test (AWS::CloudFormation::Stack) CREATE_IN_PROGRESS "User Initiated"
Aug  5 07:28:11 sqs-test/sqs-test (AWS::CloudFormation::Stack) REVIEW_IN_PROGRESS "User Initiated"

また rain cat コマンドを実行すると、指定したStackのテンプレート情報を取得できます。テンプレートファイルの記載内容と差分がないかを見たりするのに使えそうです。

$ rain cat sqs-test
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      Tags:
        - Key: test
          Value: rain

なお、テンプレートを修正してStackに再デプロイする時も、先ほどと同じく rain deploy コマンドを使えば実行されます。

# テンプレートを一部修正
$ cat sqs-test.yaml 
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      DelaySeconds: 60  # 追加
      Tags:
        - Key: test
          Value: rain


# 修正内容の反映
$ rain deploy sqs-test.yaml 
CloudFormation will make the following changes:
Stack sqs-test:
  > AWS::SQS::Queue MyQueue
Do you wish to continue? (Y/n) 
Deploying template 'sqs-test.yaml' as stack 'sqs-test' in ap-northeast-1.
Stack sqs-test: UPDATE_COMPLETE
Successfully updated sqs-test
初回のStack作成失敗時

次に初回のStack作成に失敗した場合を見てみます。ここでは先ほど作成したのと同じ QueueName を持つSQSリソースを指定した、別のテンプレートを用意します。

このテンプレートを指定して rain deploy コマンドを実行すると、以下のように作成に失敗します。エラーメッセージも表示されるので、簡単なケースならエラー原因をすぐに特定できます。

$ rain deploy fmt-sqs-test.yaml 
CloudFormation will make the following changes:
Stack fmt-sqs-test:
  + AWS::SQS::Queue MyQueue
Do you wish to continue? (Y/n) 
Deploying template 'fmt-sqs-test.yaml' as stack 'fmt-sqs-test' in ap-northeast-1.
Stack fmt-sqs-test: ROLLBACK_COMPLETE
Messages:
  - MyQueue: Resource handler returned message: "Resource of type 'AWS::SQS::Queue' with identifier 'test-sqs' already exists." (RequestToken: dae75161-d420-a85a-8610-f8af52a9f060, HandlerErrorCode: AlreadyExists)
failed deploying stack 'fmt-sqs-test'

この後に rain ls コマンドを実行すると、先ほど作成に失敗したStack名が確認できます。

$ rain ls
CloudFormation stacks in ap-northeast-1:
  aws-cloud9-test01-1fd0ca9e39c449608d7d6484a29f4069: CREATE_COMPLETE

(一部抜粋)

  fmt-sqs-test: ROLLBACK_COMPLETE

ここでQueneName を別名に修正し、再度 rain deploy コマンドを実行します。すると、Stackの作成前に既存のStackを削除している様子が確認できます。

$ rain deploy fmt-sqs-test.yaml 
Deleted existing, empty stack. # Stackを削除している
CloudFormation will make the following changes:
Stack fmt-sqs-test:
  + AWS::SQS::Queue MyQueue
Do you wish to continue? (Y/n) n # ここではデプロイは実施していません
user cancelled deployment

なお、Stackの初回作成以外でのエラー(例えば UPDATE_ROLLBACK_FAILEDなど)が発生している状態では rain deploy コマンドを実行してもエラーが返されるため、実行前にエラーを解消する必要があります。

※参考: AWS re:Post

$ rain deploy sqs-test.yaml 
stack 'sqs-test' could not be updated: UPDATE_ROLLBACK_FAILED

rain fmtによるテンプレートの修正

次に rain fmt コマンドを使ってみます。まずは以下のような、ハイフンの位置をどうするか迷うTagsの部分を少しいじったテンプレートを使ってみます。

AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      Tags:
      - Key: test  # ハイフンの位置を変更
        Value: rain

上記テンプレートにfmtコマンドを適用すると、以下のように修正されます。

$ rain fmt sqs-test.yaml 
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      Tags:
        - Key: test  # 修正されている
          Value: rain  # 修正されている

ただし、 rain fmt は極端にフォーマットが崩れたテンプレートに対しては効果は発揮されないかもしれません。例えば以下のように、各行をすべて先頭列に持ってきたテンプレートに対してfmtを実行します。

AWSTemplateFormatVersion: "2010-09-09"
Description: Template generated by rain
Resources:
MyQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: test-sqs
FakeParam: test-value
Tags:
  - Key: test

すると、以下のように想定通りにはフォーマットされない結果となりました。

$ rain fmt fmt-sqs-test-01.yaml 
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:

MyQueue:

Type: AWS::SQS::Queue

Properties:

QueueName: test-sqs

FakeParam: test-value

Tags:
  - Key: test

また、以下のように存在しないパラメータを追加したテンプレートに対してもfmtを実行してみます。

AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      FakeParam: test-value  # 存在しないパラメータ
      Tags:
        - Key: test
          Value: rain

ここでは特にエラーも発生せず正常終了してしまいました。

$ rain fmt fake-param-sqs-test.yaml 
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      FakeParam: test-value
      Tags:
        - Key: test
          Value: rain

そもそも rain build などでテンプレートを生成してから作業することを想定しているかもしれませんが、fmtコマンドはあまり過信せず、cfn-lint などで構文チェックをしてから rain fmt を実行するのがよさそうです。

その他コマンド

rain forecast

rain foecast コマンドは実験的なコマンドとして用意されており、AWSアカウント内の制約やテンプレートの依存関係の設定ミスなど、デプロイに失敗する可能性のある要素がある場合に警告するものです。forecastのhelpオプションによると、コマンド実行ユーザーの権限やクォータ、ドリフトの問題などを検知してくれるようです。

$ rain forecast --help
Outputs warnings about potential deployment failures due to constraints in 
the account or misconfigurations in the template related to dependencies in 
the account.

NOTE: This is an experimental feature!

To use this command, add --experimental or -x as an argument.

This command is not a linter! Use cfn-lint for that. The forecast command 
is concerned with things that could go wrong during deployment, after the 
template has been checked to make sure it has a valid syntax.

This command checks for some common issues across all resources:

- The resource already exists
- You do not have permissions to create/update/delete the resource
- (More to come.. service quotas, drift issues)

Resource-specific checks:

- S3 bucket is not empty
- S3 bucket policy has an invalid principal
- (Many more to come...)

Usage:
  rain forecast --experimental <template> [stackName]

Flags:
  -c, --config string     YAML or JSON file to set tags and parameters
      --debug             Output debugging information
  -x, --experimental      Acknowledge that this is an experimental feature
  -h, --help              help for forecast
      --no-colour         Disable colour output
      --params strings    set parameter values; use the format key1=value1,key2=value2
  -p, --profile string    AWS profile name; read from the AWS CLI configuration file
  -r, --region string     AWS region to use
      --role-arn string   An optional execution role arn to use for predicting IAM failures
      --skip-iam          Skip permissions checks, which can take a long time
      --tags strings      add tags to the stack; use the format key1=value1,key2=value2
      --type string       Optional resource type to limit checks to only that type

なおこちらのコマンドはlinterとして利用することはできないことが明記されており、試しに fmtコマンドで使った不要なパラメータを含むテンプレートを指定してもエラーは発生しませんでした。

$ rain forecast --experimental fake-param-sqs-test.yaml sqs-test
Clear skies!   All 1 checks passed.

テンプレートの構文チェックをしたい場合はcfn-lintなど別のツールを使いましょう。

rain merge

rain merge コマンドは指定したテンプレートの内容を結合した結果を出力します。正直利用するケースがあまり思い浮かばなかったのですが、例えば以下のように、別のテンプレートで同じリソース名(ここでは MyQueue)を使っている場合はエラーが発生します。

$ rain merge sqs-test.yaml fmt-sqs-test.yaml 
templates have clashing Resources: MyQueue

成功した場合は以下のようになり、Descriptionの内容も結合している様子などが見えます。

$ rain merge sqs-test.yaml fmt-sqs-test.yaml 
AWSTemplateFormatVersion: "2010-09-09"

Description: |-
  Template generated by rain

  Template generated by rain

Resources:
  FmtMyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: fmt-test-sqs
      Tags:
        - Key: test
          Value: rain

  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: test-sqs
      Tags:
        - Key: test
          Value: rain
rain rm

rain rm コマンドは既存のStackを削除します。ここでは先ほど rain deploy コマンドで作成したStackを削除します。なお、デフォルトでは削除実行可否の選択が Noになっているため、誤って削除する危険性は少し軽減されています。

$ rain rm sqs-test
Stack sqs-test: UPDATE_COMPLETE
Are you sure you want to delete this stack? (y/N) y
Successfully deleted stack 'sqs-test'