Help us understand the problem. What is going on with this article?

実践Terraformを読んでハマったこと

実践Terraform AWSにおけるシステム設計とベストプラクティスのコードを写経してみました。

感想はこちら
学びになったことはこちら

今回はやってみてハマったことについてです。

変数と文字列を混同する

Terraformにも型はあるため、そのようなミスはplan実行時に検出してもらえます。ただ、型さえ合えばplanは正常終了するため、
文字列の受け渡しについてはapplyの実行をしてはじめてエラーが発生して間違いがわかることちょこちょことありました。
以下はvpcのidを変数aws_vpc.example.idで参照しようとしたけど、クォートでくくってしまったために変数名の文字列が渡されてエラーになったときです。

Error: Error creating Security Group: InvalidVpcID.NotFound: The vpc ID 'aws_vpc.example.id' does not exist
        status code: 400, request id: 6f78b923-d690-42a6-b367-8fa4c71b3556

  on security_group/main.tf line 23, in resource "aws_security_group" "default":
  23: resource "aws_security_group" "default" {

初歩的といえば初歩的なミスなのですが、以下の例のようにTerraformではAWSで定義されている値を文字列で参照するときがあります。

# AWSが管理しているポリシーを参照
data "aws_iam_policy" "ecs_task_execution_role_policy" {
  # ECSタスク実行IAMロールでの使用が想定されているポリシー
  arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

Terraformで定義されているのか、クラウドサービスで定義されているのか区別ができていないときは頻発しました。

S3の名前はユニーク

S3バケット名は全世界でユニークでなければなりません。本に載っているサンプルコードとおりのバケット名にするとエラーになります。
ユニークにしないといけないことは本で説明がしっかりとされています。
しかし、バケット名を設定するすべてのサンプルコードに「※注意ここはユニークな値に書き換えること」とは書かれていないため、うっかりしているとエラーになります。
まあなってもそんなに大事にはならないですが、バケット名はユニークになるようにサフィックスに年月日(-2019-01-24)とかつけるとよいと思います。

FARGATEでヘルスチェックが行われるタイミング

9章「コンテナオーケストラレーション」ではまったところです。

1節から3節ではECS上でnginxコンテナを起動します。その後9章4節でヘルスチェックのログを確認できるようにします。
ヘルスチェックはコンテナ起動時にしかされないため、ログの設定をapplyで反映してもコンテナの再起動はされません。
したがってコンテナのヘルスチェックログを確認したい場合は一度destroyしてapplyし、作り直さないとヘルスチェックログを確認できません。

RDSの削除保護設定について

13章「データストア」でRDSを削除保護して構築します。

ここはサンプルコード通りに書いたのですが、apply実行時に以下のエラーが出ました。

※2020年2月12日 修正
エラーがでるのは、destroyをするときでした。
再度確認したところapplyは問題なく実行できました。

Error: DB Instance FinalSnapshotIdentifier is required when a final snapshot is required

インスタンス削除時にスナップショットをとる(skip_final_snapshot = true)ならば、final_snapshot_identifierが必須のようです。これはdestoryするときにのみ必要なパラメータです。

GitHubでもissueがあがっていたのと、公式ドキュメントでもskip_final_snapshotをtrueにするならば、final_snapshot_identifierは必須と書かれていました。

執筆時と仕様が変わったのかな?
※2020/01/25追記 書籍の問い合わせの方に問い合わせてみました。結果がわかればまた更新します。
※2020/02/12追記 回答をいただきました。仕様変更はないです。もともとこの仕様でした。
ログが残っていないのでもう詳細はわからないのですが、著者の野村さんがおっしゃているとおり、私がdestroyを伴う処理をしたからおきたことだと思います。

以下野村さんからいただいた回答です。

そこでAWSプロバイダの実装を確認したトコロ、「final_snapshot_identifier」は、DBインスタンス削除時のみ参照される値であることがわかりました。
https://github.com/terraform-providers/terraform-provider-aws/blob/v2.46.0/aws/resource_aws_db_instance.go#L1395-L1423
よってapplyエラーはもしかすると、DBインスタンスの「削除」を伴うapply時に起きたのではないかと予想しています。
詳しいことはログを見ないとわかりませんが、まっさらな状態でapplyできないか確認いただくとよいかもしれません。

→実際まっさらな状態ではapplyを正常に実行できました。destoryするときはこの仕様を考慮する必要があるということですね。

また、野村さんから以下のようなコメントもいただきました。

final_snapshot_identifierの必要性
ご指摘の通り、skip_final_snapshotをtrueにしてDBインスタンスを削除する場合は、final_snapshot_identifierの指定が必要です。
前述のとおり、DBインスタンス「作成」時にはなくても動きますが、「削除」時にエラーメッセージが出るようです。
正直このエラーの出方はわかりづらく、本来の用途でskip_final_snapshotを指定するなら、final_snapshot_identifierも指定したほうがよいと思います。

スナップショットをとるならば常にセットでパラメータを指定した方が良いということですね。ご意見も聞けてありがたかったです。

ちなみに書籍のコラム「RDSの削除」では、final_snapshot_identifierを指定するのではなく、skip_final_snapshotをfalseにするご案内をしておりました。
DBインスタンスを削除してよいケースというのは基本的に検証時だけで、検証用インスタンスのスナップショットはいらないと考えていたのでこのような記述になっていました。

この部分を見落としていました。きっちりと書かれているのに大変失礼しました。にもかかわらず、丁寧に確認と回答をしていただいてとてもありがたかったです。@tmknom さんありがとうございました。

※2020/02/12追記 はここまで

あとこのIDは英数字とハイフンのみ使われた文字列ではないとだめらしいです。

Error: only alphanumeric characters and hyphens allowed in "final_snapshot_identifier"

ECSで実行するコンテナで常時起動していなくてもいいものは明示的に設定する

ECSで実行したいコンテナの定義はcontainer_definitions.jsonで定義できます。
これは9章のサンプルコードではwebサーバーとしてnginx1つしか定義していません。

14章「デプロイメントパイプライン」でAWS CodePipelineを使い、CI/CD環境を構築したあと、
サンプルコードはなかったのですが、この環境でデプロイしたコンテナもECS上で実行させたくなってもう1つ定義したときにはまりました。

追加したコンテナはよくサンプルで使われるhello-worldイメージのものです。
このコンテナは起動して標準出力で'hello world'と出力したら終了します。以下のように定義しました。

[
    {
        "name": "example_nginx",
        "image": "nginx:latest",
        "essential": true,
        "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
                "awslogs-region": "ap-northeast-1",
                "awslogs-stream-prefix": "nginx",
                "awslogs-group": "/ecs/example"
            }
        },
        "portMappings": [
            {
                "protocol": "tcp",
                "containerPort": 80
            }
        ]
    },
    ***** 以下が追加したコンテナ *****
    {
        "name": "example_container",
        "image": "[アカウント名].dkr.ecr.ap-northeast-1.amazonaws.com/example:latest",
        "essential": true,
        "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
                "awslogs-region": "ap-northeast-1",
                "awslogs-stream-prefix": "example_container",
                "awslogs-group": "/ecs/example"
            }
        }
    }
]

これでデプロイを行うと、ECSで定義した上記2つのコンテナを実行するタスクは失敗となりました。
理由は、essenntialをtrueにしていたからでした。

コンテナの essential パラメータが true とマークされている場合、そのコンテナが何らかの理由で失敗または停止すると、タスクに含まれる他のすべてのコンテナは停止されます。

詳細コンテナ定義パラメータ 環境, https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_environment, (検索年月日:2019年1月24日

この値のデフォルトはtrueであるため、明示的にesselntial: falseと指定しないと標準出力をしたらすぐ停止してしまうhello-worldコンテナがいると、
nignxが正常に起動していたけどタスクは失敗となっていたというわけでした。
明示的に指定すると、タスクは成功しました。基本的なポリシーとして実行するコンテナは起動しっぱなしにする前提だからこういう設定になるのでしょうね。お試しでやっていたので、その前提が抜けていました。

さいごに

写経をしてみてTerraformは使いこなすと便利そうなのはたしかでした。

  • コード化しているため変更内容を管理しやすい
  • コード化していると、あるモジュールはどんなサービスをどう使っているのかわかりやすい。
    • たとえばCI/CD環境はECRとAWS CodePipeline, Code Buildを使って構築しているとか。

ただ、エラーがでたときにTerraformの使い方とAWSの使い方のどちらが悪いのかがわかりにくいというのは明らかでした。どちらの知識も中途半端だと、かえってデバッグの手間が多くなりそうです。
その手間が少なくなるようにどんどんコードを書いていこうと思います!!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした