مطبات Terraform

صورة

سنسلط الضوء على بعض العثرات ، بما في ذلك تلك المتعلقة بالحلقات ، إذا كانت البيانات ، وتقنيات النشر ، بالإضافة إلى المشكلات العامة التي تتعلق بـ Terraform ككل:

  • المعلمات العدد و for_each لها قيود ؛
  • قيود نشر وقت التوقف عن العمل صفر
  • حتى خطة جيدة قد تفشل.
  • يمكن أن يكون لإعادة الحيل حيله ؛
  • الاتساق المؤجل يتفق ... مع التأجيل.

معلمات العد و for_each لها قيود


في الأمثلة الواردة في هذا الفصل ، يتم استخدام معلمة count وتعبير for_each بنشاط في الحلقات والمنطق الشرطي. إن أداءهم جيد ، لكن لديهم اثنين من القيود الهامة التي تحتاج إلى معرفتها.

  • في عدد و for_each ، لا يمكن الرجوع إلى أي متغيرات إخراج المورد.
  • لا يمكن استخدام count و for_each في تهيئة الوحدة النمطية.

لا يمكن أن يشير Count و for_each إلى أي متغيرات مخرجات لمورد


تخيل أنك بحاجة إلى نشر عدة خوادم EC2 ولسبب ما لا تريد استخدام ASG. قد يكون رمزك على هذا النحو:

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

سننظر فيها بدورها.

نظرًا لأنه تم تعيين قيمة ثابتة للمعلمة count ، سيعمل هذا الرمز بدون مشاكل: عند تشغيل الأمر application ، سيقوم بإنشاء ثلاثة خوادم EC2. ولكن إذا كنت تريد نشر خادم واحد في كل منطقة توفر (منطقة التوفر أو AZ) في منطقة AWS الحالية؟ يمكنك أن تجعل رمزك يحمّل قائمة المناطق من مصدر بيانات aws_availability_zones ثم يتنقل عبر كل منها وإنشاء خادم EC2 فيه باستخدام معلمة count والوصول إلى الصفيف حسب الفهرس:

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

سيعمل هذا الرمز أيضًا ، نظرًا لأن معلمة count يمكن أن تشير إلى مصادر البيانات دون أي مشاكل. ولكن ماذا يحدث إذا كان عدد الخوادم التي تحتاج إلى إنشائها يعتمد على إخراج بعض الموارد؟ لإثبات ذلك ، فإن أسهل طريقة هي أخذ مورد random_integer ، الذي ، كما قد تعتقد من الاسم ، إرجاع عدد صحيح عشوائي:

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

ينشئ هذا الرمز رقمًا عشوائيًا من 1 إلى 3. دعنا نرى ما يحدث إذا حاولنا استخدام ناتج النتيجة لهذا المورد في معلمة count الخاصة بمورد aws_instance:

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

إذا قمت بتنفيذ خطة terraform لهذا الرمز ، فستحصل على الخطأ التالي:

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 أن يتم احتساب العد و for_each خلال مرحلة التخطيط ، قبل إنشاء أو تعديل أي موارد. هذا يعني أن العد و for_each يمكن أن يشيروا إلى المعادلات الحرفية والمتغيرات ومصادر البيانات وحتى قوائم الموارد (بشرط تحديد طولها أثناء التخطيط) ، ولكن ليس إلى متغيرات المخرجات المحسوبة للمورد.

لا يمكن استخدام count و for_each في تهيئة الوحدة النمطية


في بعض الأحيان قد تميل إلى إضافة معلمة count إلى تكوين الوحدة النمطية:

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

     count = 3

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

يحاول هذا الرمز استخدام العد داخل الوحدة النمطية لإنشاء ثلاث نسخ من مورد نظام خادم الويب. أو ربما ترغب في جعل اتصال الوحدة اختياريًا اعتمادًا على بعض الحالة المنطقية ، وتعيين معلمة العدد الخاصة بها إلى 0. سيبدو هذا الرمز معقولًا تمامًا ، ولكن نتيجة لتنفيذ خطة 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.

لسوء الحظ ، في وقت إصدار Terraform 0.12.6 ، لم يكن استخدام count أو for_each في مورد الوحدة النمطية مدعومًا. وفقًا لملاحظات الإصدار Terraform 0.12 (http://bit.ly/3257bv4) ، تخطط HashiCorp لإضافة هذه الميزة في المستقبل ، لذا قد تكون متاحة بالفعل بناءً على وقت قراءة هذا الكتاب. لمعرفة ذلك على وجه اليقين ، تحقق من سجل تغيير Terraform هنا .


يعد استخدام كتلة create_before_destroy بالاشتراك مع ASG حلاً ممتازًا لتنظيم عمليات النشر بدون وقت تعطل ، باستثناء فارق بسيط واحد: لا يتم دعم قواعد القياس التلقائي. أو ، لكي أكون أكثر دقة ، يعيد هذا تعيين حجم ASG مرة أخرى إلى min_size في كل مرة يتم نشرها فيها ، مما قد يمثل مشكلة إذا استخدمت قواعد القياس التلقائي لزيادة عدد الخوادم قيد التشغيل.

على سبيل المثال ، تحتوي وحدة خادم الويب على زوج من الموارد aws_autoscaling_schedule ، والتي تزيد في الساعة 9 صباحًا عدد الخوادم في المجموعة من اثنين إلى عشرة. إذا قمت بالنشر ، على سبيل المثال ، في الساعة 11 صباحًا ، فلن يتم تشغيل ASG الجديد مع عشرة ، ولكن مع خادمين فقط ، وسيظل في هذه الحالة حتى الساعة 9 صباحًا في اليوم التالي.

يمكن التحايل على هذا القيد بعدة طرق.

  • قم بتغيير معلمة التكرار في aws_autoscaling_schedule من 0 9 * * * ("تبدأ الساعة 9 صباحًا") إلى شيء مثل 0-59 9-17 * * * ("تشغيل كل دقيقة من 9 صباحًا إلى 5 مساءً"). إذا كان ASG يحتوي بالفعل على عشرة خوادم ، فلن تؤدي إعادة تنفيذ قاعدة النطاق التلقائي هذه إلى تغيير أي شيء ، وهو ما نحتاجه. ولكن إذا تم نشر مجموعة ASG مؤخرًا ، فإن هذه القاعدة تضمن أن يصل عدد خوادمها في دقيقة واحدة إلى عشرة. هذا ليس نهجًا أنيقًا تمامًا ، ويمكن أن تتسبب القفزات الكبيرة من عشرة إلى خادمين والعكس بالعكس أيضًا في حدوث مشكلات للمستخدمين.
  • أنشئ نصًا برمجيًا معرفًا من قبل المستخدم يستخدم واجهة برمجة تطبيقات AWS لتحديد عدد الخوادم النشطة في ASG ، واسمه باستخدام مصدر بيانات خارجي (انظر قسم "مصدر بيانات خارجي" في الصفحة 249) ، وقم بتعيين معلمة القدرات المطلوبة لمجموعة ASG على القيمة التي أرجعها هذا النص البرمجي. وبالتالي ، فإن كل مثيل ASG جديد سيتم تشغيله دائمًا بنفس السعة التي يتمتع بها كود Terraform القديم ويعقد عملية الصيانة.

بالطبع ، من الناحية المثالية ، يجب أن يكون لـ Terraform دعمًا مدمجًا لعمليات النشر بدون أي وقت تعطل ، ولكن اعتبارًا من مايو 2019 ، لم يخطط فريق HashiCorp لإضافة هذه الوظيفة ( التفاصيل هنا ).

قد يتم تنفيذ الخطة الصحيحة دون جدوى.


في بعض الأحيان ، عندما تقوم بتنفيذ أمر plan ، تحصل على خطة نشر صحيحة تمامًا ، لكن الأمر application يقوم بإرجاع خطأ. على سبيل المثال ، حاول إضافة مورد aws_iam_user بنفس الاسم الذي استخدمته لمستخدم IAM الذي أنشأته سابقًا في الفصل 2:

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

الآن ، إذا قمت بتنفيذ أمر الخطة ، فسوف تعرض Terraform للوهلة الأولى خطة نشر معقولة جدًا:

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.

إذا قمت بتنفيذ الأمر Apply ، فستحصل على الخطأ التالي:

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

المشكلة بالطبع هي أن مستخدم IAM بهذا الاسم موجود بالفعل. ويمكن أن يحدث هذا ليس فقط مع مستخدمي IAM ، ولكن أيضًا مع أي مورد تقريبًا. من الممكن أن يقوم شخص ما بإنشاء هذا المورد يدويًا أو باستخدام سطر الأوامر ، ولكن مع ذلك ، قد تؤدي مطابقة المعرفات إلى حدوث تعارضات. هناك العديد من النكهات لهذا الخطأ الذي غالبًا ما يفاجئ مبتدئين Terraform.

النقطة الأساسية هي أن أمر خطة terraform يأخذ في الاعتبار الموارد المحددة في ملف حالة Terraform فقط. إذا تم إنشاء الموارد بطريقة أخرى (على سبيل المثال ، يدويًا ، عن طريق النقر على وحدة تحكم AWS) ، فلن تدخل إلى ملف الحالة ، وبالتالي ، لن تأخذها Terraform في الاعتبار عند تنفيذ أمر الخطة. ونتيجة لذلك ، ستفشل الخطة الصحيحة للوهلة الأولى.

يمكن تعلم درسين من هذا.

  • إذا كنت قد بدأت بالفعل في العمل مع Terraform ، فلا تستخدم أي شيء آخر. إذا تمت إدارة جزء من بنيتك الأساسية باستخدام Terraform ، فلا يمكنك تعديله يدويًا. خلاف ذلك ، فأنت لا تخاطر فقط بالحصول على أخطاء غريبة في Terraform ، ولكن أيضًا تبطل الفوائد العديدة لـ IaC ، حيث لن يعد الرمز تمثيلًا دقيقًا لبنيتك الأساسية.
  • - , 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