Perangkap terraform

Perangkap terraform
Mari kita serlahkan beberapa perangkap, termasuk yang berkaitan dengan gelung, pernyataan jika dan teknik penggunaan, serta isu yang lebih umum yang mempengaruhi Terraform secara umum:

  • kiraan dan untuk_setiap parameter mempunyai had;
  • hadkan penggunaan masa henti sifar;
  • rancangan yang baik pun boleh gagal;
  • pemfaktoran semula boleh mempunyai perangkapnya;
  • koheren tertunda adalah konsisten... dengan penangguhan.

Kiraan dan untuk_setiap parameter mempunyai had

Contoh dalam bab ini menggunakan secara meluas parameter kiraan dan ungkapan for_each dalam gelung dan logik bersyarat. Mereka berprestasi baik, tetapi mereka mempunyai dua batasan penting yang perlu anda ketahui.

  • Count dan for_each tidak boleh merujuk sebarang pembolehubah output sumber.
  • count dan for_each tidak boleh digunakan dalam konfigurasi modul.

count dan for_each tidak boleh merujuk sebarang pembolehubah output sumber

Bayangkan anda perlu menggunakan beberapa pelayan EC2 dan atas sebab tertentu anda tidak mahu menggunakan ASG. Kod anda mungkin seperti ini:

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

Mari kita lihat mereka satu persatu.

Memandangkan parameter kiraan ditetapkan kepada nilai statik, kod ini akan berfungsi tanpa masalah: apabila anda menjalankan perintah apply, ia akan mencipta tiga pelayan EC2. Tetapi bagaimana jika anda ingin menggunakan satu pelayan dalam setiap Zon Ketersediaan (AZ) dalam rantau AWS semasa anda? Anda boleh meminta kod anda memuatkan senarai zon daripada sumber data aws_availability_zones dan kemudian mengulang setiap satu dan mencipta pelayan EC2 di dalamnya menggunakan parameter kiraan dan akses indeks tatasusunan:

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" {}

Kod ini juga akan berfungsi dengan baik, kerana parameter kiraan boleh merujuk sumber data tanpa sebarang masalah. Tetapi apa yang berlaku jika bilangan pelayan yang anda perlu buat bergantung pada output beberapa sumber? Untuk menunjukkan ini, cara paling mudah ialah menggunakan sumber random_integer, yang, seperti namanya, mengembalikan integer rawak:

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

Kod ini menjana nombor rawak antara 1 dan 3. Mari lihat apa yang berlaku jika kita cuba menggunakan output sumber ini dalam parameter kiraan sumber aws_instance:

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

Если Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ для этого ΠΊΠΎΠ΄Π° terraform plan, получится ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ ошибка:

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 memerlukan kiraan dan untuk_setiap dikira semasa fasa perancangan, sebelum sebarang sumber dicipta atau diubah suai. Ini bermakna kiraan dan untuk_setiap boleh merujuk kepada literal, pembolehubah, sumber data dan juga senarai sumber (selagi panjangnya boleh ditentukan pada masa penjadualan), tetapi bukan kepada pembolehubah output sumber yang dikira.

count dan for_each tidak boleh digunakan dalam konfigurasi modul

Suatu hari nanti anda mungkin tergoda untuk menambah parameter kiraan pada konfigurasi modul anda:

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

     count = 3

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

Kod ini cuba menggunakan kiraan di dalam modul untuk mencipta tiga salinan sumber kluster pelayan web. Atau anda mungkin mahu menjadikan penyambungan modul sebagai pilihan berdasarkan beberapa syarat Boolean dengan menetapkan parameter kiraannya kepada 0. Ini mungkin kelihatan seperti kod yang munasabah, tetapi anda akan mendapat ralat ini apabila menjalankan pelan terraform:

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.

Malangnya, pada Terraform 0.12.6, penggunaan count atau for_each dalam sumber modul tidak disokong. Menurut nota keluaran Terraform 0.12 (http://bit.ly/3257bv4), HashiCorp merancang untuk menambah keupayaan ini pada masa hadapan, jadi bergantung pada masa anda membaca buku ini, ia mungkin sudah tersedia. Untuk mengetahui dengan pasti, baca log perubahan Terraform di sini.

Had Penggunaan Masa Henti Sifar

Menggunakan blok create_before_destroy dalam kombinasi dengan ASG ialah penyelesaian yang bagus untuk membuat atur letak masa sifar, kecuali satu kaveat: peraturan autoscaling tidak disokong. Atau lebih tepatnya, ini menetapkan semula saiz ASG kembali kepada min_size pada setiap penggunaan, yang boleh menjadi masalah jika anda menggunakan peraturan autoscaling untuk meningkatkan bilangan pelayan yang dijalankan.

Sebagai contoh, modul kluster pelayan web mengandungi sepasang sumber aws_autoscaling_schedule, yang pada pukul 9 pagi meningkatkan bilangan pelayan dalam kluster daripada dua kepada sepuluh. Jika anda menggunakan pada, katakan, 11 pagi, ASG baharu akan but dengan hanya dua pelayan berbanding sepuluh dan kekal seperti itu sehingga 9 pagi keesokan harinya.

Had ini boleh dielakkan dalam beberapa cara.

  • Tukar parameter ulangan dalam aws_autoscaling_schedule daripada 0 9 * * * (β€œlari pada 9 pagi”) kepada sesuatu seperti 0-59 9-17 * * * (β€œlari setiap minit dari 9 pagi hingga 5 petang”). Jika ASG sudah mempunyai sepuluh pelayan, menjalankan peraturan autoscaling ini sekali lagi tidak akan mengubah apa-apa, itulah yang kita mahukan. Tetapi jika ASG baru-baru ini digunakan, peraturan ini akan memastikan bahawa dalam masa maksimum satu minit bilangan pelayannya akan mencapai sepuluh. Ini bukan pendekatan yang sepenuhnya elegan, dan lompatan besar dari sepuluh kepada dua pelayan dan belakang juga boleh menyebabkan masalah kepada pengguna.
  • Buat skrip tersuai yang menggunakan API AWS untuk menentukan bilangan pelayan aktif dalam ASG, panggilnya menggunakan sumber data luaran (lihat "Sumber Data Luaran" di halaman 249), dan tetapkan parameter kapasiti_kehendak ASG kepada nilai yang dikembalikan oleh skrip. Dengan cara ini, setiap tika ASG baharu akan sentiasa berjalan pada kapasiti yang sama seperti kod Terraform sedia ada dan menjadikannya lebih sukar untuk diselenggara.

Sudah tentu, Terraform idealnya mempunyai sokongan terbina dalam untuk penggunaan masa sifar, tetapi setakat Mei 2019, pasukan HashiCorp tidak bercadang untuk menambah fungsi ini (butiran - di sini).

Pelan yang betul mungkin tidak berjaya dilaksanakan

Kadangkala arahan pelan menghasilkan pelan penggunaan yang betul-betul betul, tetapi arahan apply mengembalikan ralat. Cuba, sebagai contoh, tambahkan sumber aws_iam_user dengan nama yang sama yang anda gunakan untuk pengguna IAM yang anda buat sebelum ini dalam Bab 2:

resource "aws_iam_user" "existing_user" {
   # ΠŸΠΎΠ΄ΡΡ‚Π°Π²ΡŒΡ‚Π΅ сюда имя ΡƒΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ IAM,
   # Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² использовании ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ terraform import
   name = "yevgeniy.brikman"
}

Sekarang, jika anda menjalankan arahan pelan, Terraform akan mengeluarkan pelan penggunaan yang kelihatan munasabah:

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 apply, anda akan mendapat ralat 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, sudah tentu, pengguna IAM dengan nama itu sudah wujud. Dan ini boleh berlaku bukan sahaja kepada pengguna IAM, tetapi kepada hampir mana-mana sumber. Ada kemungkinan seseorang mencipta sumber ini secara manual atau menggunakan baris arahan, tetapi dalam apa jua cara, padanan ID membawa kepada konflik. Terdapat banyak variasi ralat ini yang sering mengejutkan pendatang baru ke Terraform.

Perkara utama ialah arahan pelan terraform hanya mengambil kira sumber yang dinyatakan dalam fail keadaan Terraform. Jika sumber dicipta dengan cara lain (contohnya, secara manual dengan mengklik dalam konsol AWS), ia tidak akan berakhir dalam fail keadaan dan oleh itu Terraform tidak akan mengambil kiranya apabila melaksanakan arahan pelan. Akibatnya, rancangan yang kelihatan betul pada pandangan pertama akan menjadi tidak berjaya.

Terdapat dua pengajaran yang boleh diambil daripada ini.

  • Jika anda sudah mula bekerja dengan Terraform, jangan gunakan apa-apa lagi. Jika sebahagian daripada infrastruktur anda diurus menggunakan Terraform, anda tidak boleh mengubah suai secara manual lagi. Jika tidak, anda bukan sahaja berisiko terhadap ralat Terraform yang pelik, tetapi anda juga menafikan banyak faedah IaC kerana kod itu tidak lagi menjadi perwakilan tepat bagi infrastruktur anda.
  • Jika anda sudah mempunyai beberapa infrastruktur, gunakan arahan import. Jika anda mula menggunakan Terraform dengan infrastruktur sedia ada, anda boleh menambahkannya pada fail keadaan menggunakan perintah import terraform. Dengan cara ini Terraform akan mengetahui infrastruktur yang perlu diuruskan. Perintah import mengambil dua hujah. Yang pertama ialah alamat sumber dalam fail konfigurasi anda. Sintaks di sini adalah sama seperti pautan sumber: _. (seperti aws_iam_user.existing_user). Argumen kedua ialah ID sumber yang akan diimport. Katakan ID sumber aws_iam_user ialah nama pengguna (contohnya, yevgeniy.brikman), dan ID sumber aws_instance ialah ID pelayan EC2 (seperti i-190e22e5). Cara mengimport sumber biasanya ditunjukkan dalam dokumentasi di bahagian bawah halamannya.

    Di bawah ialah perintah import yang menyegerakkan sumber aws_iam_user yang anda tambahkan pada konfigurasi Terraform anda bersama-sama dengan pengguna IAM dalam Bab 2 (sudah tentu menggantikan nama anda dengan yevgeniy.brikman):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform akan memanggil API AWS untuk mencari pengguna IAM anda dan membuat perkaitan fail keadaan antaranya dan sumber aws_iam_user.existing_user dalam konfigurasi Terraform anda. Mulai sekarang, apabila anda menjalankan arahan pelan, Terraform akan mengetahui bahawa pengguna IAM sudah wujud dan tidak akan cuba menciptanya lagi.

    Perlu diingat bahawa jika anda sudah mempunyai banyak sumber yang ingin anda import ke dalam Terraform, menulis kod secara manual dan mengimport setiap satu pada satu masa boleh menyusahkan. Oleh itu, adalah wajar melihat alat seperti Terraforming (http://terraforming.dtan4.net/), yang boleh mengimport kod dan keadaan secara automatik daripada akaun AWS anda.

    Pemfaktoran semula boleh mempunyai perangkapnya

    Pemfaktoran semula ialah amalan biasa dalam pengaturcaraan di mana anda menukar struktur dalaman kod sambil membiarkan tingkah laku luaran tidak berubah. Ini adalah untuk menjadikan kod lebih jelas, kemas dan lebih mudah diselenggara. Pemfaktoran semula adalah teknik yang sangat diperlukan yang harus digunakan dengan kerap. Tetapi apabila ia berkaitan dengan Terraform atau mana-mana alat IaC lain, anda perlu berhati-hati tentang apa yang anda maksudkan dengan "tingkah laku luaran" sekeping kod, jika tidak masalah yang tidak dijangka akan timbul.

    Sebagai contoh, jenis pemfaktoran semula yang biasa adalah menggantikan nama pembolehubah atau fungsi dengan yang lebih mudah difahami. Banyak IDE mempunyai sokongan terbina dalam untuk pemfaktoran semula dan boleh menamakan semula pembolehubah dan fungsi secara automatik sepanjang projek. Dalam bahasa pengaturcaraan tujuan umum, ini adalah prosedur remeh yang mungkin anda tidak fikirkan, tetapi dalam Terraform anda perlu berhati-hati dengan ini, jika tidak, anda mungkin mengalami gangguan.

    Sebagai contoh, modul kluster pelayan web mempunyai kluster_name pembolehubah input:

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

    Bayangkan anda mula menggunakan modul ini untuk menggunakan perkhidmatan mikro yang dipanggil foo. Kemudian, anda ingin menamakan semula perkhidmatan anda kepada bar. Perubahan ini mungkin kelihatan remeh, tetapi sebenarnya ia boleh menyebabkan gangguan perkhidmatan.

    Hakikatnya ialah modul kluster pelayan web menggunakan pembolehubah cluster_name dalam beberapa sumber, termasuk parameter nama dua kumpulan keselamatan dan 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]
    }

    Jika anda menukar parameter nama pada sumber, Terraform akan memadamkan versi lama sumber itu dan mencipta yang baharu sebagai gantinya. Tetapi jika sumber itu ialah ALB, antara memadamkannya dan memuat turun versi baharu, anda tidak akan mempunyai mekanisme untuk mengubah hala trafik ke pelayan web anda. Begitu juga, jika kumpulan keselamatan dipadamkan, pelayan anda akan mula menolak sebarang trafik rangkaian sehingga kumpulan baharu dibuat.

    Satu lagi jenis pemfaktoran semula yang mungkin anda minati ialah menukar ID Terraform. Mari kita ambil sumber aws_security_group dalam modul kluster pelayan web sebagai contoh:

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

    Pengecam sumber ini dipanggil instance. Bayangkan bahawa semasa pemfaktoran semula anda memutuskan untuk menukarnya kepada nama cluster_instance yang lebih mudah difahami (pada pendapat anda):

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

    Apakah yang akan berlaku pada akhirnya? Betul: gangguan.

    Terraform mengaitkan setiap ID sumber dengan ID pembekal awan. Contohnya, iam_user dikaitkan dengan ID pengguna AWS IAM dan aws_instance dikaitkan dengan ID pelayan AWS EC2. Jika anda menukar ID sumber (katakan daripada instance kepada cluster_instance, seperti halnya dengan aws_security_group), kepada Terraform ia akan kelihatan seolah-olah anda memadamkan sumber lama dan menambah yang baharu. Jika anda menggunakan perubahan ini, Terraform akan memadamkan kumpulan keselamatan lama dan mencipta kumpulan baharu, sementara pelayan anda mula menolak sebarang trafik rangkaian.

    Berikut ialah empat pengajaran utama yang perlu anda ambil daripada perbincangan ini.

    • Sentiasa gunakan arahan pelan. Ia boleh mendedahkan semua halangan ini. Semak outputnya dengan teliti dan perhatikan situasi di mana Terraform merancang untuk memadamkan sumber yang kemungkinan besar tidak harus dipadamkan.
    • Buat sebelum anda memadam. Jika anda ingin menggantikan sumber, fikirkan dengan teliti sama ada anda perlu membuat penggantian sebelum memadamkan sumber asal. Jika jawapannya ya, create_before_destroy boleh membantu. Hasil yang sama boleh dicapai secara manual dengan melakukan dua langkah: mula-mula tambah sumber baharu pada konfigurasi dan jalankan arahan guna, kemudian alih keluar sumber lama daripada konfigurasi dan gunakan arahan guna semula.
    • Menukar pengecam memerlukan perubahan keadaan. Jika anda ingin menukar ID yang dikaitkan dengan sumber (contohnya, menamakan semula aws_security_group daripada instance kepada cluster_instance) tanpa memadamkan sumber dan mencipta versi baharunya, anda mesti mengemas kini fail keadaan Terraform dengan sewajarnya. Jangan sekali-kali melakukan ini secara manual - sebaliknya gunakan arahan keadaan terraform. Apabila menamakan semula pengecam, anda harus menjalankan perintah mv keadaan terraform, yang mempunyai sintaks berikut:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE ialah ungkapan yang merujuk kepada sumber dalam bentuk semasanya dan NEW_REFERENCE ialah tempat anda mahu mengalihkannya. Sebagai contoh, apabila menamakan semula kumpulan aws_security_group daripada instance kepada cluster_instance, anda perlu menjalankan arahan berikut:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Ini memberitahu Terraform bahawa keadaan yang sebelum ini dikaitkan dengan aws_security_group.instance kini harus dikaitkan dengan aws_security_group.cluster_instance. Jika selepas menamakan semula dan menjalankan pelan terraform arahan ini tidak menunjukkan sebarang perubahan, maka anda melakukan semuanya dengan betul.

    • Beberapa tetapan tidak boleh diubah. Parameter banyak sumber tidak boleh diubah. Jika anda cuba mengubahnya, Terraform akan memadamkan sumber lama dan mencipta yang baharu sebagai gantinya. Setiap halaman sumber biasanya akan menunjukkan perkara yang berlaku apabila anda menukar tetapan tertentu, jadi pastikan anda menyemak dokumentasi. Sentiasa gunakan arahan pelan dan pertimbangkan untuk menggunakan strategi create_before_destroy.

    Konsistensi tertunda adalah konsisten... dengan penangguhan

    Sesetengah API penyedia awan, seperti AWS, tidak segerak dan telah melambatkan konsistensi. Asynchrony bermakna antara muka boleh mengembalikan respons dengan segera tanpa menunggu tindakan yang diminta selesai. Konsistensi tertunda bermakna perubahan mungkin mengambil masa untuk disebarkan ke seluruh sistem; semasa ini berlaku, respons anda mungkin tidak konsisten dan bergantung pada replika sumber data yang bertindak balas kepada panggilan API anda.

    Bayangkan, sebagai contoh, anda membuat panggilan API ke AWS memintanya membuat pelayan EC2. API akan mengembalikan respons "berjaya" (201 Created) hampir serta-merta, tanpa menunggu pelayan itu sendiri dibuat. Jika anda cuba menyambung kepadanya dengan segera, ia hampir pasti akan gagal kerana pada ketika itu AWS masih memulakan sumber atau, sebagai alternatif, pelayan masih belum but. Selain itu, jika anda membuat panggilan lain untuk mendapatkan maklumat tentang pelayan ini, anda mungkin menerima ralat (404 Tidak Ditemui). Masalahnya ialah maklumat tentang pelayan EC2 ini masih boleh disebarkan ke seluruh AWS sebelum ia tersedia di mana-mana, anda perlu menunggu beberapa saat.

    Setiap kali anda menggunakan API tak segerak dengan konsistensi malas, anda mesti mencuba semula permintaan anda secara berkala sehingga tindakan selesai dan disebarkan melalui sistem. Malangnya, AWS SDK tidak menyediakan sebarang alat yang baik untuk ini, dan projek Terraform pernah mengalami banyak pepijat seperti 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

    Dalam erti kata lain, anda mencipta sumber (seperti subnet) dan kemudian cuba mendapatkan beberapa maklumat mengenainya (seperti ID subnet yang baru dibuat), dan Terraform tidak menemuinya. Kebanyakan pepijat ini (termasuk 6813) telah diperbaiki, tetapi ia masih muncul dari semasa ke semasa, terutamanya apabila Terraform menambah sokongan untuk jenis sumber baharu. Ini menjengkelkan, tetapi dalam kebanyakan kes tidak menyebabkan sebarang bahaya. Apabila anda menjalankan terraform apply semula, segala-galanya akan berfungsi, kerana pada masa ini maklumat itu akan tersebar ke seluruh sistem.

    Petikan ini dibentangkan dari buku oleh Evgeniy Brikman "Terraform: infrastruktur pada tahap kod".

Sumber: www.habr.com

Tambah komen