Memo/Terraform

https://dexlab.net:443/pukiwiki/index.php?Memo/Terraform
 

Terraform

  • https://www.terraform.io/
    • CHANGELOG.md
    • HashiCorp?製ツール(Vagrant, Packer, Serf等の開発元)
    • AWS, Heroku等の複数のクラウドサービスに対応している
    • Terraformの不具合の多さを理解しつつ、Terraformの不具合報告等のオープンソース活動を行える余裕がある人向け。
    • https://github.com/hashicorp/terraform/issues を見ると分かるが1000 issue以上が作成されており、不具合の修正が間に合っていない印象
    • 状態をステートファイル(terraform.tfstate)に持ち、これを元に差分更新する。
      • 無くすと大変。更新ができなくなる。
      • ステートファイルはAWS S3等に置く事はできる。
      • ステートファイルの管理が面倒。リソースの差分がうまく比較できないようで、リソースの更新には不向き。新規作成・破棄を繰り返す用途向き
    • ドキュメント・サンプルがほとんど無い。α版の印象
    • マイナーバージョンが0.0.1変わっても、仕様が大きく変わり、互換性が無くなる事も多い。
      • 開発方針がdevしかなく、stable等の安定版が無い
    • AWS:対応しているリソースの新規立ち上げは楽だが、非対応の項目がかなりあるため、自分が必要としている機能をサポートしているか要確認
    • 遭遇したトラブル
      • v0.0.1上がるとコードに無いheroku configを消すように仕様変更
      • API GatewayのSSL証明書の登録が成功したように見えるが、実は成功していない。
      • destroy時は削除確認があるが、EC2セキュリティグループの変更で、EC2が作り直される時には警告はない。plan時に予期しない変更が無いか強く確認が必要。さもなければ簡単にリソースが消える
      • セキュリティグループに変更を加えると、EC2を作り直そうとする
      • ELBにリスナー追加しようとすると、リスナー全て消えた
      • SSL証明書のアップロードが成功したように見えて壊れている
      • EC2にEBSを追加しようすると、EC2を作り直そうとする

locals: ローカル変数

  • NG: variableのdefaultで別変数を参照すると「default may not contain interpolations」エラーになる
    variable "cidr_whitelist" {
      type = "list"
      default = [
        { "value" = "${var.cidr_office}" type = "IPV4" },
      ]
  • OK: locals は別変数の参照が可能
    locals {
      cidr_whitelist = [
        { "value" = "${var.cidr_office}" type = "IPV4" },
      ]
    }

terraform-landscape: planの結果を見やすく


配列の要素の末尾に文字列を追加

配列の要素の末尾に文字列を結合したい場合がある。

  • 元変数: ["arn:aws:s3:::example1","arn:aws:s3:::example2"]
  • 求める出力: ["arn:aws:s3:::example1/*","arn:aws:s3:::example2/*"]
  • Terraform v0.11.7
  • 例:Amazon S3: 特定の S3 バケットへの読み取りと書き込みアクセスを許可する のポリシーを生成したい場合
    • File not found: "terraform.example.multiple-resources.zip" at page "Memo/Terraform"[添付]
      terraform init
      terraform plan -var aws_profile=example
      terraform apply -var aws_profile=example
      ...
      Outputs:
      aws_iam_policy_document.s3_bucket_policy.json = {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Sid": "",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": [
              "arn:aws:s3:::example2",
              "arn:aws:s3:::example1"
            ]
          },
          {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
              "s3:PutObject",
              "s3:GetObject"
            ],
            "Resource": [
              "arn:aws:s3:::example2/*",
              "arn:aws:s3:::example1/*"
            ]
          }
        ]
      }

ランダムID/文字列の生成

RDSのroot userパスワード等に使える。


Terratest: インフラ自動テストツール

Terraformで起動、チェック実行、破棄までを自動化


タイムアウトの延長

リソースの作成/削除等で、timeoutする場合がある。

  • Timeouts
  • aws_db_instanceの場合
    resource "aws_db_instance" "timeout_example" {
      name              = "mydb"
      # ...
      timeouts {
        create = "60m"
        delete = "2h"
      }
    }

alicloud(aliyun alibaba)

中国のalicloud用プロバイダー


ヒアドキュメントでJSON等を綺麗に書く

  • terraformでは引用が「"」のみで、JSON等を書こうとすると、「\"」のエスケープが必要で見難い
  • JSON Formatter & Validator JSONの検証と整形
  • ヒアドキュメント「var = <<EOF 〜 EOF」が使える
    resource "aws_iam_policy" "policy" {
        name = "test_policy"
        path = "/"
        description = "My test policy"
        policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": [
            "ec2:Describe*"
          ],
          "Effect": "Allow",
          "Resource": "*"
        }
      ]
    }
    EOF
    }

変数にmap(連想配列)を使う

  • type = "map" は省略できる
  • aws.tf
    # https://aws.amazon.com/marketplace/fulfillment?productId=b7ee8a69-ee97-4a49-9e68-afaee216db2e&ref=cns_srchrow&versionTitle=1708
    # "${lookup(var.aws_ami_ids, "ap-northeast-1_centos7_2017-10-12")}"
    variable "aws_ami_ids" {
      default = {
        "us-east-1_centos7_2017-10-12"      = "ami-db48ada1"
        "us-west-2_centos7_2017-10-12"      = "ami-e535c59d"
        "ap-southeast-1_centos7_2017-10-12" = "ami-1fbad07c"
        "ap-northeast-1_centos7_2017-10-12" = "ami-e4599a82"
        "eu-central-1_centos7_2017-10-12"   = "ami-2540f74a"
        "eu-west-1_centos7_2017-10-12"      = "ami-5f76b626"
      }
    }
    
    output "aws_ami_ids" { value = "${var.aws_ami_ids}" }
    output "aws_ami_id_centos7" { value = "${lookup(var.aws_ami_ids, "ap-northeast-1_centos7_2017-10-12")}" }
  • 実行結果
    terraform apply
    ...
    aws_ami_id_centos7 = ami-e4599a82
    aws_ami_ids = {
      ap-northeast-1_centos7_2017-10-12 = ami-e4599a82
      ap-southeast-1_centos7_2017-10-12 = ami-1fbad07c
      eu-central-1_centos7_2017-10-12 = ami-2540f74a
      eu-west-1_centos7_2017-10-12 = ami-5f76b626
      us-east-1_centos7_2017-10-12 = ami-db48ada1
      us-west-2_centos7_2017-10-12 = ami-e535c59d

import: 既存のリソースからtfstateを作成する

GitHub - dtan4/terraforming? でやっていた事が公式で一部可能になった。

  • Command: import - Terraform by HashiCorp
    • 既存リソースからtf, tfstateファイルを生成できる
    • リソースIDを一つ一つ指定しなければいけないので面倒
    • v0.9.4: route53をimportしようとするとcrashした

変数

  • パスワード等はtfファイルに書かないようにしたい場合。「-var "key=value"」が複数使える
    cat rds.tf
    variable db_root_password {}
    
    db_root_password=$(mkpasswd -l 16 -s 0)
    terraform plan -var "db_root_password=$db_root_password"
  • 別ファイルにしたい場合: 「-var-file="secret.tfvars"」

バージョン変更によるアップグレード方法


VPCの作成


lifecycle: 指定リソースの差分を無視する

EBSを追加したり、SGを追加しようとするとEC2を作り直してしまう場合がある。

  • Configuring Resources - Terraform by HashiCorp
    • create_before_destroy: 既存リソースが有った場合、削除してから作成する
    • prevent_destroy: リソース保護。削除する時にエラーになる
    • ignore_changes: 差分があっても無視する
  • 後から追加したroot_block_deviceの差分を無視したい。
    lifecycle {
      ignore_changes = ["root_block_device"]
    }

デバッグ

  • デバッグログを出す
    TF_LOG=DEBUG terraform plan

output: 出力だけを見る

public dns, IP等、後で参照したい情報を出力するのに便利

terraform output

outputで複数リソースの値を出力

Output Variablesでは1つの値しか出力できないようだ。

  • いつのバージョンからかJSONで出力できるようになっていた
    output "web.private_ip" { value = "${aws_instance.web.*.private_ip}" }
    
    Outputs:
    web.private_ip = [
        i-1111,
        i-2222
    ]
  • 複数EC2のprivate ipをカンマ区切りで出力
    output "web.private_ip" { value = "${join(",",aws_instance.web.*.private_ip)}" }

backend: : S3等に変数を置く

  • terraform.tf:
    • このファイル内で変数は使えなかった。「configuration cannot contain interpolations」エラー
      terraform {
        backend "s3" {
          profile = "myprofile"
      #    shared_credentials_file = "~/.aws/config" # v0.9.3 で試しても失敗
          region  = "ap-northeast-1"
          bucket  = "mybucket"
          key     = "web/terraform.tfstate" # path/file で1バケットに複数のtfstateを置ける
        }
      }
  • 実行: terraform v0.9.3
    aws --profile myprofile configure
    
    # ~/.aws/credentials に該当キーが出来ているのが重要。
    
    terraform init
    
    terraform plan

REMOTE STATE: S3等に変数を置く

  • terraform_remote_state
    • v0.6.14,15ではうまく動作しなかった。terraform.tfstate にユーザのAWS KEYSが入ってしまうので注意。
    • v0.7で廃止。

REMOTE CONFIG: S3等にterraform.tfstateを置く

terraform.tfstate にAWS KEYSやパスワード等が入っているため、git/svnにコミットしにくい。
S3等にpushする機能がある。

  • 「terraform.tfstate」がS3のバケットにアップロードされる。localの「terraform.tfstate」は「.terraform/terraform.tfstate」に移動する。
    export AWS_ACCESS_KEY_ID=<YOUR ACCESS KEY>
    export AWS_SECRET_ACCESS_KEY=<YOUR SECRET KEY>
    terraform remote config \
     -backend=S3 \
     -backend-config="region=ap-northeast-1" \
     -backend-config="bucket=mybucket-terraform" \
     -backend-config="key=terraform.tfstate"
  • localにある「terraform.tfstate」をremoteにpushする
    terraform remote push
  • remoteにある「terraform.tfstate」をlocalに持ってくる
    terraform remote pull
  • 無効化する。localの「terraform.tfstate」が復活する。remoteのファイルは消えなかった
    terraform remote config -disable

指定したリソースだけplan/apply

  • 「-target=resource 」オプションがある。複数指定したい場合は「-target=resource1 -target=resource2」とする
    terraform plan -target=aws_instance.web[0]
  • Command: plan - Terraform by HashiCorp

変数で配列が扱えない

  • 変数に配列が使用できない。(v0.6.6で確認) v0.7以降で使用可能
  • [v0.7以降]
    # example.tf
    variable "security_groups" {                                                                                                                                                                                                              
      default = ["sg-xx", "sg-x,sg-xxx"]
    }
    
    resource "aws_instance" "web" {
      ...
      security_groups = "${var.security_groups}"
    }
  • Store arrays in variables Issue #57 hashicorp/terraform GitHubで要望は上がっているようだ。
  • [v0.7未満] 擬似的に配列を設定する
    # example.tf
    variable "security_groups" {                                                                                                                                                                                                              
      default = "sg-xx,sg-x,sg-xxx"
    }
    
    resource "aws_instance" "web" {
      ...
      security_groups = "${split(",", var.security_groups)}"
    }

Terraforming: 既存のインフラを Terraform で管理できるように

terraform v0.7.0からimport機能が実装されたため、Terraformingは不要かもしれない。

terraform.tfstate や tf形式のソースを生成する [#f45434d2]

  • CentOS6.xの場合。ruby 2.1.0以上なので、rbenvやrvmで新しいrubyを使えるようにする
    rvm use 2.1.5
    ruby --version
    ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
    
    rvmsudo gem install terraforming aws-sdk
    ln -s ~/.aws/config ~/.aws/credentials
    
    # なぜか --profileオプションが動作せず
    export AWS_ACCESS_KEY_ID=****
    export AWS_SECRET_ACCESS_KEY=****
    export AWS_REGION=ap-northeast-1
    terraforming ec2 > terraforming.ec2.tf
    terraforming ec2 --tfstate >terraforming.terraform.tfstate

複数バージョンの切り替え

  • バージョンによって動作しない機能があり、古いバージョンに切り替えたい時がある。
    • 0.7.1: terraformコマンドが1ファイルになった
    • 0.6.0: CentOS6.x ReleaseMedia?が起動しないのが直った?
    • 0.5.3: AWS EC2でCentOS6.x ReleaseMedia?が起動しない
    • 0.3.7: 1回目は成功するが、2回目は、AWS EC2セキュリティグループが壊れる
  • CentOS6.x 64bitの場合
    TERRAFORM_VER=0.8.5
    sudo mkdir -p /opt/terraform.${TERRAFORM_VER}
    sudo wget -O /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip
    sudo unzip -d /opt/terraform.${TERRAFORM_VER}/ /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip
    
    # v0.7.x以上の場合
    sudo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 70
    
    # v0.6.x以下の場合
    echo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 60 \\ > /tmp/install-terraform.sh
    find /opt/terraform.${TERRAFORM_VER}/ -name 'terraform-*' -type f -printf ' --slave /usr/local/bin/%f %f %p \\\n' >> /tmp/install-terraform.sh
    sudo bash /tmp/install-terraform.sh
  • バージョンの切り替え
    alternatives --display terraform
    sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform
    
    # 削除
    sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform

不具合

  • v0.6.16: v0.6.15と同様にawsリソースを作り直す不具合有り
  • v0.6.15: awsリソースを全部作り直そうとする不具合がある。destroy, apply, refresh, planで確認
  • v0.6.14: heroku_app SSL証明書更新でクラッシュした
  • v0.4.2: awsのroot_block_deviceでvolume_size等を変更しようとすると「logicalType cannot be modified on root device」のエラーが出る。v0.3.7へダウングレードした。
  • v0.4.2: aws_instance の *_block_device が毎回違うように表示される
  • v0.4.2: aws_security_group の 自己参照、他セキュリティグループIDの指定がうまくいかない
  • v0.4.2: --target=リソース名で、ハイフン(-)に対応していない。 例:--target=aws_route53_record.web-01 はNG、web_01ならOK

インストール

  • CentOS6.x /usr/local/binにインストールする
    wget -O terraform_0.3.7_linux_amd64.zip https://dl.bintray.com/mitchellh/terraform/terraform_0.3.7_linux_amd64.zip
    sudo unzip terraform_0.3.7_linux_amd64.zip -d /usr/local/bin/
    
    terraform version
    Terraform v0.3.7

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-09-15 (土) 07:31:38 (5d)