2023年1月15日日曜日

環境(awsとlocalstack)を切り替えながらterraformを使ってみた


私がterraformでやりたかったこと

  • 環境(awsとlocalstack)を切り替えながらterraformを使いたい
  • 寝る前にAWSリソース破棄して、翌日、再構築したい(節約のため)
  • 個人の学習目的なので、terraformのbackendはlocalでいい
  • 自作moduleは、好きではないが、たまに作るかもしれない
  • 環境ごとにディレクトリを作りたくない
  • 環境切り替えにwokspaceは使いたくない
  • 環境差分は変数にして、awsとlocalstackで同一コードを実行したい
  • localstack停止後はリソース消えるので、terraformも初期状態にリセットしたい


私の環境

  • SparkyLinux6.5(debian系)
  • terraform1.3.6
  • tfenv3.0.0
  • tflint0.43.0
  • awscli2.9.4
  • localstack1.3.1.dev
  • make4.3
  • aws ec2キーペア(sandbox)


サンプルコード


https://github.com/blue21jp/aws-sample-terraform


注意)

サンプルコードのEC2はキーペア(sandbox)を使用します。

このキーペアは、terraformで作成しないので、事前に用意するか、コードを修正してください。


構成


コードの構成は下記のとおり。
terraformコマンドは、Makefileに記述して実行します。
makeの使用方法については後述。
.
├── Makefile
├── README.md
├── global/
│   ├── backend.tf
│   ├── ip.sh
│   ├── locals.tf
│   ├── providers_aws.tf
│   ├── providers_localstack.tf
│   └── versions.tf
├── modules/
│   ├── ec2-spot/
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── ecs-fargate/
│       ├── cwl.tf
│       ├── iam.tf
│       ├── main.tf
│       └── variables.tf
├── stacks_base/
│   ├── 00_check/
│   │   ├── Makefile
│   │   └── outputs.tf
│   ├── 01_network/
│   │   ├── Makefile
│   │   ├── datasources.tf
│   │   ├── main.tf
│   │   └── versions.tf
│   ├── 02_ec2_nat/
│   │   ├── Makefile
│   │   ├── datasources.tf
│   │   ├── locals.tf
│   │   └── main.tf
│   ├── Makefile
│   ├── locals_all.tf
│   ├── locals_dev.tf
│   └── locals_prd.tf
├── stacks_demo/
│   ├── 00_check/
│   │   ├── Makefile
│   │   └── outputs.tf
│   ├── 03_ec2/
│   │   ├── Makefile
│   │   ├── datasources.tf
│   │   ├── locals.tf
│   │   ├── locals.tf.dev
│   │   ├── locals.tf.prd
│   │   └── main.tf
│   ├── 04_ecs/
│   │   ├── Makefile
│   │   ├── datasources.tf
│   │   ├── locals.tf
│   │   ├── locals.tf.dev
│   │   ├── locals.tf.prd
│   │   ├── main.tf
│   │   ├── taskdef_amzn2.json
│   │   └── taskdef_nginx.json
│   ├── Makefile
│   ├── locals_all.tf
│   ├── locals_dev.tf
│   └── locals_prd.tf
└── stacks_template/
    ├── 00_check/
    │   ├── Makefile
    │   └── outputs.tf
    ├── Makefile
    ├── locals_all.tf
    ├── locals_dev.tf
    ├── locals_prd.tf
    └── locals_stg.tf

こんなイメージ


リソース構築用のterraformコードの格納場所

modules/

  • 自作のモジュールは、modules/配下にディレクトリを作成して、そこに置きます。

stacks_xxxx/

  • 構築するリソース(vpc,ec2,etc)は、共通用、デモ用などグループ化してstacks_xxxx/ ディレクトリを作成し、そこに置きます。

stacks_xxxx/NN_xxxxx

  • stacks_xxxx/ 配下は、「数値で始まるディレクトリ」を作成して、そこにコードを格納します。この「数値で始まるディレクトリ(00_check,01_netowprk,etc)」を、ここではスタックと呼ぶことにします。
  • スタックは、ライフサイクルが同じリソースの集まりです。
  • スタックの数値の小さいものから構築すると stacks_xxxx が完成するようにします。
  • 全環境で、同一コード(スタックのコード)を実行します。

環境差分の変数を設定する場所

global/

全環境の全スタックで参照できます。

  • locals.tf・・・local.global.XXXXで参照する

stacks_xxxx/

たとえば、stacks_baseの下記ファイルに定義した変数は、stacks_base配下のスタックだけで参照できます。

  • locals_all.tf・・・全環境共通の変数。local.common_all.XXXXで参照する
  • locals_dev.tf・・・dev環境用の変数。local.common_env.XXXXで参照する
  • locals_stg.tf・・・stg環境用の変数。local.common_env.XXXXで参照する
  • lcoals_prd.tf・・・prd環境用の変数。local.common_env.XXXXで参照する
 

stacks_xxxd/NN_xxxx

たとえば、00_chackの下記ファイルに定義した変数は、00_checkだけで参照できます。

  • locals.tf.dev・・・dev環境用の変数。local.XXXXで参照する
  • locals.tf.stg・・・stg環境用の変数。local.XXXXで参照する
  • locals.tf.prd・・・prd環境用の変数。local.XXXXで参照する

terraformプラグインのキャッシュ場所

awsなどのプラグインは、「terraform init」時にダウンロードして、下記にキャッシュします。

  • ${HOME}/.teraform.d/plugin-cache

tfstateの格納場所

terraformのstateは、スタック用ディレクトリ(00_check, 01_network, etc)内に、環境ごとに作成します。

  • stacks_xxxx/NN_xxxx/tfstate.d/dev
  • stacks_xxxx/NN_xxxx/tfstate.d/stg
  • stacks_xxxx/NN_xxxx/tfstate.d/prd

プロビジョニング環境

構築する環境は3種類の前提

  • dev・・・ localstack
  • stg・・・ awsアカウント ※サンプルコードでは未使用
  • prd・・・ awsアカウント


プロビジョニング先の環境を指定する方法

locals_dev.tf, locales_stg.tf, locals_prd.tf でプロビジョニングする環境を指定します。

下記のようにして、awscliのプロファイル名と、リージョンを設定します。
env_typeは、環境を識別するための値(dev|stg|prd)を設定します。

locals {
  common_env = {
    env_type = "dev"
    profile  = "localstack"
    region   = "us-east-1"
  }
}

localstackで使用するサービスを設定する方法

global/provisers_localstack.tf で設定します。

下記のようにして使用するサービスのエンドポイントをlocalstackに向けます。

  endpoints {
    sts            = "http://${data.external.myip.result["ip"]}:4566"
    s3             = "http://${data.external.myip.result["ip"]}:4566"
    ec2            = "http://${data.external.myip.result["ip"]}:4566"
    route53        = "http://${data.external.myip.result["ip"]}:4566"
    cloudwatch     = "http://${data.external.myip.result["ip"]}:4566"
    iam            = "http://${data.external.myip.result["ip"]}:4566"
    eks            = "http://${data.external.myip.result["ip"]}:4566"
    wafregional    = "http://${data.external.myip.result["ip"]}:4566"
    wafv2          = "http://${data.external.myip.result["ip"]}:4566"
    waf            = "http://${data.external.myip.result["ip"]}:4566"
    cloudfront     = "http://${data.external.myip.result["ip"]}:4566"
    lambda         = "http://${data.external.myip.result["ip"]}:4566"
    secretsmanager = "http://${data.external.myip.result["ip"]}:4566"
    cloudformation = "http://${data.external.myip.result["ip"]}:4566"
    ssm            = "http://${data.external.myip.result["ip"]}:4566"
  }


使い方

terraformコマンドは、makeで実行します。

ここでは、特に記載がなければ、TOPディレクトリでmakeコマンドを実行します。

動作環境の確認

stacks_xxxx/00_check を使用します。このスタックは動作環境の情報を表示するだけでリソースは何もつくりません。

dev(localstack)に向ける場合

これで terraformの init, tflint, planが実行されます。

$ make plan -C stacks_base/00_check
実行前の00_checkは下記のとおり
Permissions Size User   Date Modified Name
.rw-r--r--     6 blue21 14 Jan 16:23  .terraform-version
.rw-r--r--  2.6k blue21 14 Jan 16:23  Makefile
.rw-r--r--   246 blue21 14 Jan 16:23  outputs.tf

実行後の00_checkは下記のようになります。

アンダースコアで始まるシンボリックリンク・ディレクトリは make で自動生成したものです。

Permissions Size User   Date Modified Name
drwxr-xr-x     - blue21 14 Jan 19:04  .terraform
.rw-r--r--     6 blue21 14 Jan 16:23  .terraform-version
.rw-r--r--   464 blue21 14 Jan 19:04  .terraform.lock.hcl
lrwxrwxrwx    23 blue21 14 Jan 19:04  _backend.tf -> ../../global/backend.tf
lrwxrwxrwx    16 blue21 14 Jan 19:04  _locals_all.tf -> ../locals_all.tf
lrwxrwxrwx    16 blue21 14 Jan 19:04  _locals_env.tf -> ../locals_dev.tf
lrwxrwxrwx    22 blue21 14 Jan 19:04  _locals_global.tf -> ../../global/locals.tf
lrwxrwxrwx    36 blue21 14 Jan 19:04  _providers.tf -> ../../global/providers_localstack.tf
drwxr-xr-x     - blue21 14 Jan 19:04  _tfstate.d
lrwxrwxrwx    24 blue21 14 Jan 19:04  _versions.tf -> ../../global/versions.tf
.rw-r--r--  2.6k blue21 14 Jan 16:23  Makefile
.rw-r--r--   246 blue21 14 Jan 16:23  outputs.tf

prd(aws)に向ける場合

ENVで環境を指定します。(dev|stg|prd)

$ make plan ENV=prd -C stacks_base/00_check
シンボリックリンク先は prd 用に変わります。
ermissions Size User   Date Modified Name
drwxr-xr-x     - blue21 14 Jan 19:07  .terraform
.rw-r--r--     6 blue21 14 Jan 16:23  .terraform-version
.rw-r--r--   464 blue21 14 Jan 19:04  .terraform.lock.hcl
lrwxrwxrwx    23 blue21 14 Jan 19:07  _backend.tf -> ../../global/backend.tf
lrwxrwxrwx    16 blue21 14 Jan 19:07  _locals_all.tf -> ../locals_all.tf
lrwxrwxrwx    16 blue21 14 Jan 19:07  _locals_env.tf -> ../locals_prd.tf
lrwxrwxrwx    22 blue21 14 Jan 19:07  _locals_global.tf -> ../../global/locals.tf
lrwxrwxrwx    29 blue21 14 Jan 19:07  _providers.tf -> ../../global/providers_aws.tf
drwxr-xr-x     - blue21 14 Jan 19:07  _tfstate.d
lrwxrwxrwx    24 blue21 14 Jan 19:07  _versions.tf -> ../../global/versions.tf
.rw-r--r--  2.6k blue21 14 Jan 16:23  Makefile
.rw-r--r--   246 blue21 14 Jan 16:23  outputs.tf

スタックのapply

例として、stacks_base/01_network を使用します。

このスタックはVPC,Subnetを作成します。

dev(localstack)に向ける場合

$ make apply -C stacks_base/01_network

prd(aws)に向ける場合

$ make apply ENV=prd -C stacks_base/01_network

ちなみに、terraform apply に引数を追加したい場合は下記のようにします。

$ make apply ENV=prd OPT="-auto-approve" -C stacks_base/01_network

また、stacks_baseのスタックを一括でapplyしたい場合は以下のようにします。

$ make apply ENV=prd OPT="-auto-approve" -C stacks_base


スタックのdesttroy

例として、stacks_base/01_network を使用します。

使用方法は apply と同じです。

dev(localstack)に向ける場合

$ make destroy -C stacks_base/01_network

prd(aws)に向ける場合

$ make destroy ENV=prd -C stacks_base/01_network


初期化

localstack, awsのリソースを全部destroyしたあとに、下記を実行すると、tfstateやmakeが自動生成したシンボリックリンクを削除して初期状態に戻します。

$ make clean ENV=prd -C stacks_base/01_network


その他

  • make list -C stacks_xxxx/NN_xxxx
    tarraform state list を実行します。

  • make outputs -C stacks_xxxx/NN_xxxx
    terraform output を実行します。

  • make mv OPT="xxxx yyyy" -C stacks_xxxx/NN_xxxx
    terraform state mv $OPT を実行します。

  • make rm OPT="xxx" -C stacks_xxxx/NN_xxxx
    terraform state rm $OPT を実行します。

  • make import OPT="xxx yyyy" -C stacks_xxxx/NN_xxxx
    terrafprm import $OPT を実行します。


おまけ

サンプルコードを使って、demo_todo.orgの手順で実施した構築作業を動画にしました。


この動画で実行しているコマンド(aws-cliのalias)については、下記の記事を参照