Jebakan Terraform

gambar

Kami akan menyoroti beberapa perangkap, termasuk yang terkait dengan loop, jika pernyataan, dan teknik penyebaran, serta masalah yang lebih umum yang menyangkut Terraform secara keseluruhan:

  • parameter count dan for_each memiliki keterbatasan;
  • Batasan penyebaran downtime nol
  • bahkan rencana yang bagus bisa gagal;
  • refactoring dapat memiliki triknya;
  • konsistensi yang ditangguhkan konsisten ... dengan penangguhan.

Parameter count dan for_each memiliki keterbatasan


Dalam contoh dalam bab ini, parameter count dan ekspresi for_each secara aktif digunakan dalam loop dan logika kondisional. Mereka berkinerja baik, tetapi mereka memiliki dua batasan penting yang perlu Anda ketahui.

  • Dalam hitungan dan for_each, tidak ada variabel keluaran sumber daya yang dapat dirujuk.
  • count dan for_each tidak dapat digunakan dalam konfigurasi modul.

Hitungan dan for_each tidak dapat merujuk variabel output apa pun dari sumber daya


Bayangkan Anda perlu menggunakan beberapa server EC2 dan untuk beberapa alasan Anda tidak ingin menggunakan ASG. Kode Anda mungkin seperti ini:

resource "aws_instance" "example_1" {
   count             = 3
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Kami akan mempertimbangkan mereka pada gilirannya.

Karena parameter hitung diberi nilai statis, kode ini akan berfungsi tanpa masalah: ketika Anda menjalankan perintah yang berlaku, itu akan membuat tiga server EC2. Tetapi jika Anda ingin menggunakan satu server di setiap zona ketersediaan (Zona Ketersediaan atau AZ) dalam wilayah AWS saat ini? Anda dapat meminta kode memuat daftar zona dari sumber data aws_available_zones dan kemudian menelusuri masing-masing zona dan membuat server EC2 di dalamnya menggunakan parameter jumlah dan mengakses array dengan indeks:

resource "aws_instance" "example_2" {
   count                   = length(data.aws_availability_zones.all.names)
   availability_zone   = data.aws_availability_zones.all.names[count.index]
   ami                     = "ami-0c55b159cbfafe1f0"
   instance_type       = "t2.micro"
}

data "aws_availability_zones" "all" {}

Kode ini juga akan berfungsi dengan baik, karena parameter hitung dapat merujuk sumber data tanpa masalah. Tetapi apa yang terjadi jika jumlah server yang Anda butuhkan tergantung pada output dari beberapa sumber daya? Untuk menunjukkan ini, cara termudah adalah dengan mengambil sumber random_integer, yang, seperti yang Anda duga dari namanya, mengembalikan bilangan bulat acak:

resource "random_integer" "num_instances" {
  min = 1
  max = 3
}

Kode ini menghasilkan angka acak dari 1 hingga 3. Mari kita lihat apa yang terjadi jika kita mencoba menggunakan hasil keluaran dari sumber ini dalam parameter hitungan sumber daya aws_instance:

resource "aws_instance" "example_3" {
   count             = random_integer.num_instances.result
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Jika Anda menjalankan rencana terraform untuk kode ini, Anda mendapatkan kesalahan berikut:

Error: Invalid count argument

   on main.tf line 30, in resource "aws_instance" "example_3":
   30: count = random_integer.num_instances.result

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

Terraform mengharuskan penghitungan dan for_each dihitung selama fase perencanaan, sebelum sumber daya apa pun dibuat atau dimodifikasi. Ini berarti bahwa count dan for_each dapat merujuk ke literal, variabel, sumber data, dan bahkan daftar sumber daya (asalkan panjangnya dapat ditentukan selama perencanaan), tetapi tidak untuk menghitung variabel output dari sumber daya.

count dan for_each tidak dapat digunakan dalam konfigurasi modul


Terkadang Anda tergoda untuk menambahkan parameter hitungan ke konfigurasi modul:

module "count_example" {
     source = "../../../../modules/services/webserver-cluster"

     count = 3

     cluster_name = "terraform-up-and-running-example"
     server_port = 8080
     instance_type = "t2.micro"
}

Kode ini mencoba menggunakan hitungan di dalam modul untuk membuat tiga salinan sumber daya webserver-cluster. Atau, mungkin Anda ingin membuat koneksi modul opsional tergantung pada beberapa kondisi Boolean, menetapkan parameter hitungannya ke 0. Kode ini akan terlihat cukup masuk akal, tetapi sebagai hasil dari menjalankan rencana terraform Anda akan mendapatkan kesalahan berikut:

Error: Reserved argument name in module block

   on main.tf line 13, in module "count_example":
   13: count = 3

The name "count" is reserved for use in a future version of Terraform.

Sayangnya, pada saat Terraform 0.12.6 dirilis, penggunaan count atau for_each dalam sumber daya modul tidak didukung. Menurut catatan rilis Terraform 0.12 (http://bit.ly/3257bv4), HashiCorp berencana untuk menambahkan fitur ini di masa mendatang, jadi tergantung pada saat Anda membaca buku ini, mungkin sudah tersedia. Untuk mengetahui dengan pasti, lihat log perubahan Terraform di sini .


Menggunakan blok create_before_destroy dalam kombinasi dengan ASG adalah solusi yang sangat baik untuk mengatur penyebaran dengan nol downtime, kecuali untuk satu nuansa: aturan autoscaling tidak didukung. Atau, lebih tepatnya, ini me-reset ukuran ASG kembali ke min_size setiap kali dikerahkan, yang bisa menjadi masalah jika Anda menggunakan aturan autoscaling untuk meningkatkan jumlah server yang berjalan.

Sebagai contoh, modul webserver-cluster berisi sepasang sumber daya aws_autoscaling_schedule, yang pada jam 9 pagi menambah jumlah server di cluster dari dua menjadi sepuluh. Jika Anda menggunakan, katakanlah, pada jam 11 pagi, ASG baru tidak akan boot dengan sepuluh, tetapi dengan hanya dua server, dan akan tetap dalam kondisi ini hingga jam 9 pagi hari berikutnya.

Keterbatasan ini dapat dielakkan dengan beberapa cara.

  • Ubah parameter perulangan di aws_autoscaling_schedule dari 0 9 * * * ("run at 9am") menjadi sesuatu seperti 0-59 9-17 * * * ("run setiap menit dari jam 9 pagi sampai jam 5 sore"). Jika ASG sudah memiliki sepuluh server, menjalankan kembali aturan autoscale ini tidak akan mengubah apa pun, yang kami butuhkan. Tetapi jika grup ASG baru-baru ini digunakan, aturan ini memastikan bahwa dalam satu menit jumlah servernya mencapai sepuluh. Ini bukan pendekatan yang sepenuhnya elegan, dan lompatan besar dari sepuluh ke dua server dan sebaliknya juga dapat menyebabkan masalah bagi pengguna.
  • Buat skrip yang ditentukan pengguna yang menggunakan AWS API untuk menentukan jumlah server aktif di ASG, sebut saja menggunakan sumber data eksternal (lihat bagian "Sumber data eksternal" pada halaman 249), dan setel parameter kapasitas_ yang diinginkan dari grup ASG ke nilai yang dikembalikan oleh skrip ini. Dengan demikian, setiap instance ASG baru akan selalu berjalan dengan kapasitas yang sama dengan kode Terraform lama dan mempersulit pemeliharaannya.

Tentu saja, idealnya, Terraform harus memiliki dukungan built-in untuk penyebaran dengan downtime nol, tetapi pada Mei 2019, tim HashiCorp tidak berencana untuk menambahkan fungsi ini ( detailnya ada di sini ).

Rencana yang benar mungkin tidak berhasil dilaksanakan.


Terkadang saat Anda menjalankan perintah paket, Anda mendapatkan paket penerapan yang benar, tetapi perintah yang berlaku mengembalikan kesalahan. Misalnya, coba tambahkan sumber aws_iam_user dengan nama yang sama yang Anda gunakan untuk pengguna IAM yang Anda buat sebelumnya di bab 2:

resource "aws_iam_user" "existing_user" {
   #       IAM,
   #      terraform import
   name = "yevgeniy.brikman"
}

Sekarang, jika Anda menjalankan perintah rencana, Terraform akan menampilkan sekilas rencana penyebaran yang sangat masuk akal:

Terraform will perform the following actions:

   # aws_iam_user.existing_user will be created
   + resource "aws_iam_user" "existing_user" {
         + arn                  = (known after apply)
         + force_destroy   = false
         + id                    = (known after apply)
         + name               = "yevgeniy.brikman"
         + path                 = "/"
         + unique_id         = (known after apply)
      }

Plan: 1 to add, 0 to change, 0 to destroy.

Jika Anda menjalankan perintah yang berlaku, Anda mendapatkan kesalahan berikut:

Error: Error creating IAM User yevgeniy.brikman: EntityAlreadyExists:
User with name yevgeniy.brikman already exists.

   on main.tf line 10, in resource "aws_iam_user" "existing_user":
   10: resource "aws_iam_user" "existing_user" {

Masalahnya, tentu saja, adalah bahwa pengguna IAM dengan nama itu sudah ada. Dan ini bisa terjadi tidak hanya dengan pengguna IAM, tetapi juga dengan hampir semua sumber daya. Ada kemungkinan bahwa seseorang membuat sumber ini secara manual atau menggunakan baris perintah, tetapi meskipun demikian, pengidentifikasi yang cocok akan menyebabkan konflik. Ada banyak rasa kesalahan ini yang mengejutkan pemula Terraform.

Poin kuncinya adalah bahwa perintah rencana terraform hanya mempertimbangkan sumber daya yang ditentukan dalam file status Terraform. Jika sumber daya dibuat dengan cara lain (misalnya, secara manual, dengan mengklik pada konsol AWS), mereka tidak akan masuk ke file status dan, oleh karena itu, Terraform tidak akan memperhitungkannya ketika menjalankan perintah rencana. Akibatnya, rencana yang benar pada pandangan pertama akan gagal.

Dua pelajaran dapat dipelajari dari ini.

  • Jika Anda sudah mulai bekerja dengan Terraform, jangan gunakan yang lain. Jika sebagian infrastruktur Anda dikelola menggunakan Terraform, Anda tidak dapat lagi memodifikasinya secara manual. Jika tidak, Anda tidak hanya menjalankan risiko mendapatkan kesalahan Terraform yang aneh, tetapi juga meniadakan banyak manfaat IaC, karena kode tidak lagi menjadi representasi akurat dari infrastruktur Anda.
  • - , import. Terraform , terraform import. Terraform , . import . . , : _. ( aws_iam_user.existing_user). β€” , . , ID aws_iam_user (, yevgeniy.brikman), ID aws_instance EC2 ( i-190e22e5). , , .

    import, aws_iam_user, Terraform IAM 2 (, yevgeniy.brikman ):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform API AWS, IAM aws_iam_user.existing_user Terraform. plan Terraform , IAM , .

    , , , Terraform, . , Terraforming (http://terraforming.dtan4.net/), AWS .


    β€” , , . , , . β€” , . , Terraform IaC, , Β« Β» , .

    , β€” . IDE . , , Terraform , .

    , webserver-cluster cluster_name:

    variable "cluster_name" {
       description = "The name to use for all the cluster resources"
       type          = string
    }

    , foo. bar. , - .

    , webserver-cluster cluster_name , name ALB:

    resource "aws_lb" "example" {
       name                    = var.cluster_name
       load_balancer_type = "application"
       subnets = data.aws_subnet_ids.default.ids
       security_groups      = [aws_security_group.alb.id]
    }

    name - , Terraform . ALB, -. , , , .

    , , Terraform. aws_security_group webserver-cluster:

    resource "aws_security_group" "instance" {
      # (...)
    }

    instance. , ( ) cluster_instance:

    resource "aws_security_group" "cluster_instance" {
       # (...)
    }

    ? : .

    Terraform ID . , iam_user IAM AWS, aws_instance β€” ID AWS EC2. (, instance cluster_instance, aws_security_group), Terraform , . , Terraform , .

    , .

    • plan. . , Terraform , , , .
    • , . , , . , create_before_destroy. , : apply, apply .
    • . , (, aws_security_group instance cluster_instance), , Terraform. β€” terraform state. terraform state mv, :

      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE β€” , , NEW_REFERENCE β€” , . , aws_security_group instance cluster_instance :

      $ terraform state mv \
         aws_security_group.instance \
         aws_security_group.cluster_instance

      Terraform, , aws_security_group.instance, aws_security_group.cluster_instance. terraform plan , , .

    • . . , Terraform . , , . plan create_before_destroy.

    …


    API , AWS, . , , . , ; , , API-.

    , , API- AWS EC2. API «» (201 Created) , . , , AWS , , . , , , (404 Not Found). , EC2 AWS, , .

    API , . , AWS SDK , Terraform 6813 (https://github.com/hashicorp/terraform/issues/6813):

    $ terraform apply
    aws_subnet.private-persistence.2: InvalidSubnetID.NotFound:
    The subnet ID 'subnet-xxxxxxx' does not exist

    , (, ) - ( ID ), Terraform . ( 6813) , , Terraform . , . terraform apply , .

    Β«Terraform: Β».

All Articles