سحابة Terraform AWS: إدارة البنية التحتية السليمة

نشرت: 2022-03-11

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

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

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

إدخال Terraform

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

Terraform عبارة عن طبقة تجريدية ، نعم ، والتجريدات متسربة ، أوافق. لكن في النهاية ، نحن نعمل على حل المشكلات وإدارة الأفكار المجردة. يهدف هذا إلى تزويدنا بمزيد من العقلانية في مهامنا اليومية.

الهدف

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

ستركز الأمثلة جميعها على مزود سحابة واحد: Amazon Web Services (AWS). هذه مجرد سحابة لدي أكثر خبرة بها ولكن يجب أن تنطبق جميع المعلومات على السحب الأخرى أيضًا.

سأختم ببعض الملاحظات التي كنت أتمنى لو كنت أعرفها عندما بدأت: بعض القواعد اللغوية ، والمراوغات ، والحالات التي لن يكون فيها Terraform هو الأداة المفضلة لدي.

لن أركز على التفاصيل الدقيقة لبناء الجملة. يمكنك التعجيل بذلك بسرعة من خلال قراءة الوثائق والأدلة الرائعة التي توفرها HashiCorp لـ Terraform. أود التركيز على الأشياء التي قد لا تكون واضحة من البداية والتي كنت أتمنى لو كنت أعرفها قبل أن أبدأ العمل مع Terraform. نأمل أن يوجهك هذا إلى الاتجاه الصحيح ويسمح لك بالنظر إلى Terraform كأداة لاستخدامها في المشاريع المستقبلية.

إدارة البنية التحتية صعبة

هناك عدة خطوات متضمنة في إعداد بيئة لتطبيق ما في السحابة. ما لم تقم بتدوينها جميعًا كقوائم مراجعة تفصيلية ومتابعتها عن كثب ، فسترتكب أخطاء طوال الوقت ؛ نحن بشر، بعد كل شيء. هذه الخطوات يصعب مشاركتها. تحتاج إلى توثيق الكثير من الإجراءات اليدوية ويمكن أن تصبح المستندات قديمة بسرعة. الآن اضرب كل ذلك في العدد الإجمالي للبيئات لتطبيق واحد: dev و test / qa و stage و prod . تحتاج أيضًا إلى التفكير في الأمان لكل منهم.

ألا تحتوي كل أداة على واجهة مستخدم يمكنني استخدامها وأنسى التعقيد؟

لديهم واجهات مستخدم — وحدة الإدارة في AWS هي مثال رئيسي. لكن هذه الأدوات تفعل الكثير تحت الغطاء. نقرة واحدة على واجهة المستخدم قد تستدعي بالفعل سلسلة من التغييرات التي يصعب فهمها. لا توجد عادة طريقة للتراجع عما فعلته في واجهة المستخدم (غالبًا ما تكون المطالبات المعتادة "هل أنت متأكد؟" غير كافية). علاوة على ذلك ، من الجيد دائمًا أن يكون لديك زوجان آخران من العيون للتحقق من التغييرات ، ولكن عندما نستخدم واجهات المستخدم ، سنحتاج إلى الجلوس مع هذا الشخص معًا أو التحقق من التغييرات بعد إجرائها ، وهو أمر أكثر من مجرد تدقيق. مراجعة. كل مزود خدمة سحابية لديه واجهة مستخدم خاصة به تحتاج إلى إتقانها. تم تصميم واجهات المستخدم لتكون سهلة الاستخدام (وليست بالضرورة بسيطة!) وبالتالي فهي عرضة لمقاربات وهمية مثل "هذا مجرد تعديل صغير" أو إصلاح سريع للإنتاج سوف تنساه في غضون 48 ساعة. من الصعب جدًا أتمتة مثل هذا النقر اليدوي.

ماذا عن أدوات CLI؟

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

التزامن مقابل إدارة التكوين

سأحدد بعض فئات الأدوات والممارسات المستخدمة لإدارة البنية التحتية. هذه هي إدارة التنسيق والتهيئة. يمكنك التفكير فيها تقريبًا كنماذج برمجة توضيحية وضرورية (فكر في Prolog مقابل Python). لكل منها مزاياها وعيوبها ولكن من الأفضل أن تعرفها جميعًا وتطبق أفضل أداة لوظيفة معينة. أيضًا ، عادةً ما يتضمن التزامن مستوى تجريد أعلى من إدارة التكوين.

تنسيق

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

هذا هو المكان الذي يناسب Terraform. يمكنك استخدامه لإجراء جزء من البنية التحتية لتكنولوجيا المعلومات: فأنت تخبره بما يتم نشره ، ويربطه Terraform معًا ويقوم بإجراء جميع استدعاءات API الضرورية. هناك أدوات مماثلة في هذا الفضاء ؛ واحدة من أكثرها شهرة هي AWS Cloud Formation. إنه يدعم الاسترداد والتراجع في حالة وجود أخطاء أفضل من Terraform ، ولكن أيضًا ، في رأيي ، منحنى تعليمي أكثر حدة. كما أنه ليس حياديًا بالسحابة: فهو يعمل فقط مع AWS.

إدارة التكوين

الجانب التكميلي لهذه الممارسات هو نهج إدارة التكوين. في هذا النموذج ، تحدد الخطوات الدقيقة التي يجب أن تقوم بها الأداة للوصول إلى التكوين المطلوب. قد تكون الخطوات نفسها صغيرة وتدعم أنظمة تشغيل متعددة ، ولكن عليك التفكير بنشاط في ترتيب تنفيذها. ليس لديهم وعي بالحالة الحالية والمناطق المحيطة (إلا إذا قمت ببرمجتها على هذا النحو) ، وعلى هذا النحو ، سوف ينفذون بشكل أعمى أي خطوات تعطيهم إياهم. قد يؤدي ذلك إلى مشكلة تُعرف باسم انحراف التكوين ، حيث تتزامن مواردك ببطء مع ما كان من المفترض أن تمثله في البداية ، خاصةً إذا أجريت بعض التغييرات اليدوية عليها. إنها رائعة في إدارة وتوفير الخدمات في الحالات الفردية. أمثلة على الأدوات التي تتفوق في سير العمل هذا هي Chef و Puppet و Ansible و Salt.

يفرض التنظيم نهجًا للبنية التحتية الخاصة بك حيث تعامل مواردك على أنها ماشية ، وليس كحيوانات أليفة. بدلاً من "رعاية" كل VPS يدويًا ، يمكنك استبدالها بنسخة مطابقة عند حدوث خطأ ما. لا أعني أنك ببساطة لا تهتم وتعيد تشغيل الشيء على أمل الأفضل.

Meme of the IT Crowd TV Show مع شعارها الأيقوني: هل حاولت إيقاف تشغيله وتشغيله مرة أخرى؟

بدلاً من ذلك ، يجب عليك التحقيق في المشكلة وإصلاحها في التعليمات البرمجية ثم نشرها.

يمكن استخدام Ansible (وأدوات CM الأخرى) لإدارة البنية التحتية لـ AWS ، ولكن هذا قد يتطلب الكثير من العمل ويكون أكثر عرضة للخطأ ، خاصةً عندما تتغير البنية التحتية كثيرًا وتزداد تعقيدًا.

أحد الأشياء المهمة التي يجب تذكرها هو أن مناهج إدارة التنسيق والتهيئة لا تتعارض مع بعضها البعض. إنها متوافقة. من المقبول تمامًا أن يكون لديك مجموعة من مثيلات EC2 (VPS) في مجموعة AutoScaling تُدار بواسطة Terraform ولكن يتم تشغيل AWS Application Image (AMI) ، وهي لقطة للقرص ، تم إعدادها بخطوات ضرورية ، على سبيل المثال ، Ansible . حتى أن Terraform لديه مفهوم "الموفرين" الذي يسمح لك بتشغيل أدوات التزويد الخارجية بمجرد بدء تشغيل الجهاز.

تقوم وثائق Terraform بعمل رائع لشرح هذا الأمر بشكل أكبر ومساعدتك على وضع Terraform في النظام البيئي بأكمله.

ما هو Terraform؟

إنها أداة مفتوحة المصدر ، أنشأتها HashiCorp والتي تسمح لك بتدوين البنية التحتية الخاصة بك كملفات تكوين تعريفية يتم إصدارها ومشاركتها ويمكن مراجعتها.

يجب أن يدق اسم HashiCorp الجرس — كما أنهم يصنعون Nomad و Vault و Packer و Vagrant و Consul. إذا كنت قد استخدمت أيًا من هذه الأدوات ، فأنت تعرف بالفعل جودة التوثيق ، والمجتمع النابض بالحياة ، والفائدة التي يمكنك توقعها من حلولهم.

البنية التحتية كرمز

Terraform هو منصة حيادية ؛ يمكنك استخدامه لإدارة الخوادم المعدنية أو الخوادم السحابية مثل AWS و Google Cloud Platform و OpenStack و Azure. في لغة Terraform ، يطلق عليهم مقدمو الخدمات ، يمكنك التعرف على المقياس من خلال قراءة قائمة كاملة بمقدمي الخدمات المدعومين. يمكن استخدام عدة موفرين في نفس الوقت ، على سبيل المثال عندما يقوم الموفر "أ" بتكوين أجهزة ظاهرية نيابة عنك ، ولكن الموفر "ب" يقوم بتكوين وتفويض سجلات DNS.

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

الأجزاء الأساسية لإعداد Terraform

  1. ثنائي Terraform نفسه ، الذي يتعين عليك تثبيته
  2. ملفات التعليمات البرمجية المصدر ، أي التكوين الخاص بك
  3. الحالة (سواء كانت محلية أو بعيدة) التي تمثل الموارد التي يديرها Terraform (المزيد عن ذلك لاحقًا)

كتابة تكوين Terraform

تكتب كود تكوين Terraform في ملفات *.tf باستخدام لغة HCL. هناك خيار لاستخدام تنسيق JSON ( *.tf.json ) ، لكنه يستهدف الآلات والتوليد التلقائي بدلاً من البشر. أوصي بأن تتمسك بـ HCL. لن أتعمق في بناء جملة لغة HCL ؛ تقوم المستندات الرسمية بعمل رائع لوصف كيفية كتابة HCL وكيفية استخدام المتغيرات والاستقراءات. سأذكر فقط الحد الأدنى المطلوب لفهم الأمثلة.

داخل ملفات Terraform ، أنت تتعامل في الغالب مع الموارد ومصادر البيانات . تمثل الموارد مكونات البنية الأساسية الخاصة بك ، على سبيل المثال ، مثيل AWS EC2 ، أو مثيل RDS ، أو سجل Route53 DNS ، أو قاعدة في مجموعة أمان. إنها تسمح لك بتوفيرها وتغييرها داخل بنية السحابة.

بافتراض أنك قمت بإعداد Terraform ، إذا قمت بإصدار terraform apply ، فسيؤدي الكود أدناه إلى إنشاء مثيل EC2 يعمل بكامل طاقته (يتم عرض خصائص معينة فقط):

 resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }

من ناحية أخرى ، هناك مصادر بيانات تسمح لك بقراءة البيانات حول مكونات معينة دون تغييرها. هل تريد الحصول على معرف AWS (ARN) لشهادة صادرة عن ACM؟ أنت تستخدم مصدر بيانات. الفرق هو أن مصادر البيانات مسبوقة بـ data_ عند الرجوع إليها في ملفات التكوين.

 data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }

تشير المراجع أعلاه إلى شهادة ACM SSL صادرة يمكن استخدامها مع AWS ALBs. قبل أن تفعل كل ذلك ، تحتاج إلى إعداد بيئتك.

هيكل المجلد

يتم فصل بيئات Terraform (وحالاتها) بواسطة الدلائل. يقوم Terraform بتحميل جميع ملفات *.tf في دليل إلى مساحة اسم واحدة ، لذلك لا يهم الترتيب. أوصي بهيكل الدليل التالي:

 /terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
  1. default_variables.tf - حدد جميع متغيرات المستوى الأعلى وقيمها الافتراضية اختياريًا. يمكن إعادة استخدامها في كل بيئة (دليل متداخل) مع روابط رمزية.
  2. /stage/ - دليل يحتوي على التكوين لبيئة منفصلة بالكامل (هنا يسمى stage ، ولكن يمكن أن يكون أي شيء). أي تغييرات يتم إجراؤها داخل هذا المجلد مستقلة تمامًا عن البيئات الأخرى (env - مثل prod ) وهو شيء تريده لتجنب العبث مع بيئة الإنتاج بالتغييرات التي تم إجراؤها على stage !
  3. terraform.tfvars - تحديد القيم المتغيرة. تتشابه ملفات .tfvars مع ملفات .env من حيث أنها تحتوي على أزواج key=val للمتغيرات المحددة. على سبيل المثال ، يحدد هذا profile AWS و AWS key_name و AWS key_path الذي أستخدمه. يمكن تجاهله في Git.
  4. default_variables.tf - هذا رابط رمزي للملف (2) ، والذي يسمح لنا بمشاركة المتغيرات المستقلة عن البيئة دون تكرار أنفسنا.
  5. terraform.tf - هذا هو التكوين الرئيسي لكل بيئة ؛ يحتوي على كتلة terraform {} التي تكوّن النهاية الخلفية. أنا أيضا أقوم بتكوين مقدمي هنا.
  6. env_variables.tf - يحتوي هذا الملف على متغيرات خاصة بالبيئة. أقوم بتمييز جميع الموارد بـ Env=<env_name> في AWS ، لذلك عادةً ما يحدد هذا الملف متغيرًا واحدًا فقط: env .

بالطبع ، هذه ليست الطريقة الوحيدة لتنظيم بيئتك. هذا مجرد شيء نجح معي من خلال تمكين الفصل الواضح بين الاهتمامات.

التكوين الخلفي

لقد ذكرت بالفعل ولاية Terraform. هذا جزء أساسي من سير عمل Terraform. قد تتساءل عما إذا كانت الدولة مطلوبة بالفعل. ألا تستطيع Terraform فقط الاستعلام عن AWS API طوال الوقت للحصول على الحالة الفعلية للبنية التحتية؟ حسنًا ، إذا فكرت في الأمر ، يحتاج Terraform إلى الحفاظ على تعيين بين ما يديره في ملفات التكوين التعريفي وما تتوافق معه هذه الملفات بالفعل (في بيئة مزود السحابة). لاحظ أنه أثناء كتابة ملفات تكوين Terraform ، لا تهتم بمعرفات ، على سبيل المثال ، مثيلات EC2 الفردية أو ARNs التي سيتم إنشاؤها لمجموعات الأمان التي تنشرها. داخليًا ، ومع ذلك ، يحتاج Terraform إلى معرفة أن كتلة موارد معينة تمثل موردًا ملموسًا مع ID / ARN. هذا مطلوب لاكتشاف التغييرات. علاوة على ذلك ، تُستخدم الحالة لتتبع التبعيات بين الموارد (أيضًا شيء لا يتعين عليك التفكير فيه عادةً!). تُستخدم لإنشاء رسم بياني يمكن (عادةً) موازنته وتنفيذه. كما هو الحال دائمًا ، أوصي بقراءة الوثائق الممتازة حول حالة Terraform والغرض منه.

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

بشكل افتراضي ، يخزن Terraform الحالة في ملف على القرص ، يقع في دليل العمل الحالي (لكل بيئة) كملف terraform.tfstate . هذا جيد إذا كنت تعلم أنك ستكون المطور الوحيد في الوظيفة أو كنت تتعلم وتجرب فقط Terraform. من الناحية الفنية ، يمكنك جعلها تعمل في فريق لأنه يمكنك إلزام الدولة بمستودع VCS. ولكن بعد ذلك ، ستحتاج إلى التأكد من أن الجميع يعمل دائمًا على أحدث إصدار من الحالة وأن لا أحد يقوم بتغييرات في نفس الوقت! هذا صداع كبير بشكل عام وأنا أنصح بشدة بعدم استخدامه. بالإضافة إلى ذلك ، إذا انضم شخص ما إلى عملية التطوير الفردي ، فلا يزال يتعين عليك تكوين مكان بديل للحالة.

لحسن الحظ ، هذه مشكلة مع حلول جيدة مدمجة في Terraform: ما يسمى بالحالة البعيدة . لكي تعمل الحالة البعيدة ، تحتاج إلى تكوين النهاية الخلفية باستخدام أحد موفري النهاية الخلفية المتاحين. سيعتمد المثال الخلفي التالي على AWS S3 و AWS DynamoDB (قاعدة بيانات AWS NoSQL). يمكنك استخدام S3 فقط ولكن بعد ذلك تفقد آلية قفل الحالة والتحقق من التناسق (غير مستحسن). إذا كنت قد استخدمت الولاية المحلية فقط سابقًا ، فسيوفر لك تكوين خلفية خلفية عن بُعد خيارًا لترحيل حالتك في المرة الأولى ، حتى لا تفقد أي شيء. يمكنك قراءة المزيد حول التكوين الخلفي هنا.

لسوء الحظ ، هناك مشكلة الدجاج والبيض: يجب إنشاء دلو S3 وجدول DynamoDB يدويًا. لا يمكن لـ Terraform إنشائها تلقائيًا نظرًا لعدم وجود حالة بعد! حسنًا ، هناك بعض الحلول مثل https://github.com/gruntwork-io/terragrunt التي تعمل على أتمتة ذلك باستخدام AWS CLI ، لكنني لا أريد الخروج عن الموضوع الرئيسي لمنشور المدونة هذا.

الأشياء المهمة التي يجب معرفتها حول تكوين الواجهة الخلفية S3 و DynamoDB هي:

  1. قم بتمكين تعيين الإصدار في حاوية S3 لتكون في مأمن من الخطأ البشري وقانون مورفي.
  2. يحتوي جدول DynamoDB على حد لمعدل القراءة والكتابة (يسمى السعة). إذا قمت بإجراء الكثير من التغييرات على الحالة البعيدة ، فتأكد من تمكين DynamoDB AutoScaling لهذا الجدول أو تكوين حدود R / W عالية بما يكفي. بخلاف ذلك ، سيحصل Terraform على أخطاء HTTP 400 من AWS API عند تنفيذ الكثير من الاستدعاءات.

لتلخيص كل ذلك ، يمكن وضع التكوين الخلفي التالي في terraform.tf لتكوين الحالة البعيدة على S3 و DynamoDB.

 terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }

هذا كثير للاستيعاب في وقت واحد ، لكن تذكر أنك تفعل هذا مرة واحدة لكل شخص ثم يمكنك نسيانه. إذا كنت بحاجة إلى مزيد من التحكم في قفل الحالة ، فهناك HashiCorp Terraform Enterprise ، لكنني لن أغطيها هنا.

الموفرون

من أجل أن تكون هذه النهاية الخلفية قابلة للوصول وأن تكون قادرًا على التواصل مع مزود الخدمة السحابية لدينا على الإطلاق ، نحتاج إلى تكوين ما يسمى بالمزود . يمكن وضع الكتلة التالية في ملف terraform.tf (لكل بيئة):

 provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }

يتم تخزين متغيرات profile region في ملف terraform.tfvars ، والتي قد يتم تجاهلها. يشير متغير profile التعريف إلى ملف تعريف مسمى يحتفظ ببيانات اعتماد الأمان لسحابة AWS باستخدام ملف بيانات الاعتماد القياسي. لاحظ أنني أقوم أيضًا بتعيين بعض قيود الإصدار. لا تريد أن يقوم Terraform بترقية المكونات الإضافية للمزود الخاص بك دون علمك بكل تهيئة خلفية. خاصة بالنظر إلى وجود الإصدار 2.x من موفر AWS الذي يتطلب ترقية دقيقة.

تهيئة النهاية الخلفية

يتطلب كل تكوين للجهة الخلفية خطوة تهيئة في البداية وفي كل مرة يتم إجراء تغيير عليها. تعمل التهيئة أيضًا على تهيئة وحدات Terraform (المزيد عن تلك لاحقًا) ، لذلك عند إضافة تلك الوحدات ، تحتاج إلى إعادة تشغيل خطوة init أيضًا. هذه العملية آمنة للتشغيل عدة مرات. لاحظ أنه أثناء تهيئة النهاية الخلفية ، لا يمكن قراءة جميع المتغيرات بواسطة Terraform لتكوين الحالة ، ولا ينبغي أن تكون لأسباب أمنية (على سبيل المثال ، المفاتيح السرية). للتغلب على هذا ، وفي حالتنا ، استخدم ملف تعريف AWS مختلفًا عن الملف الافتراضي ، يمكنك استخدام الخيار -backend-config مع قبول أزواج المتغيرات k=v . هذا يسمى التكوين الجزئي.

 terraform init -backend-config=profile=<aws_profile_name>

تشترك ملفات Terraform في نطاق مقيد بدليل معين. هذا يعني أن المجلد الفرعي غير متصل مباشرة برمز الدليل الأصلي. ومع ذلك ، فهي تحدد وحدة نمطية تسمح بإعادة استخدام الكود وإدارة التعقيد والمشاركة.

سير العمل

يكون سير العمل العام عند العمل باستخدام كود Terraform كما يلي:

  1. اكتب التكوين للبنية التحتية الخاصة بك.
  2. تعرف على التغييرات الفعلية التي سيتم إجراؤها ( terraform plan ).
  3. اختياريًا ، قم بتنفيذ التغييرات الدقيقة التي رأيتها في الخطوة 2 ( terraform apply ).
  4. اذهب 1

خطة Terraform

سيقدم لك أمر plan Terraform قائمة بالتغييرات التي سيتم إجراؤها apply البنية التحتية الخاصة بك عند إصدار الأمر application. من الآمن إصدار plan عدة مرات ، لأنها في حد ذاتها لا تغير أي شيء.

كيف تقرأ الخطة

يمكن التعرف بسهولة على الكائنات في Terraform (الموارد ومصادر البيانات) من خلال أسمائها المؤهلة بالكامل.

  1. في حالة الموارد ، قد يبدو المعرف بالشكل التالي: <resource_type>.<resource_name> —eg ، aws_ecs_service.this .
  2. في حالة الموارد داخل الوحدات ، لدينا اسم وحدة إضافية: module.<module_name>.<resource_type>.<resource_name> —eg ، module.my_service_ecs_service_task.aws_ecs_service.this .
  3. مصادر البيانات (داخل وخارج الوحدة): (module.<module_name>).data.<resource_type>.<resource_name> --eg، module.my_service_ecs_service_task.data.aws_ecs_task_definition.this .

نوع المورد خاص بمزود معين وعادةً ما يتضمن اسمه ( aws_… ). يمكن العثور على القائمة الكاملة للموارد المتاحة لـ AWS في المستندات.

هناك خمسة إجراءات ستظهرها الخطة لموارد معينة:

  1. [+] إضافة - سيتم إنشاء مورد جديد.
  2. [-] تدمير - سيتم تدمير المورد بالكامل.
  3. [~] التعديل في المكان - سيتم تعديل المورد وسيتم تغيير واحد أو أكثر من المعلمات. هذا آمن بشكل عام. سيُظهر لك Terraform المعلمات التي سيتم تعديلها وكيف (إن أمكن).
  4. [- / +] - ستتم إزالة المورد ثم إعادة إنشائه بمعلمات جديدة. يحدث هذا إذا تم إجراء تغيير على معلمة لا يمكن تغييرها في مكانها. سيُظهر لك Terraform التغييرات التي تفرض إعادة إنشاء مورد مع التعليق التالي باللون الأحمر: (forces new resource) . من المحتمل أن يكون هذا خطيرًا ، نظرًا لوجود فترة لن يكون فيها المورد موجودًا على الإطلاق. يمكنه أيضًا كسر التبعيات الأخرى المتصلة. أوصي بالتغلب على مثل هذه التغييرات ما لم تكن تعرف العواقب أو لا تهتم بالتوقف عن العمل.
  5. [<=] - ستتم قراءة مصدر البيانات. هذه عملية للقراءة فقط.

Terraform AWS - نموذج "خطة"

أعلاه هو مثال على الخطة. التغييرات التي أجريتها كانت:

  • تم تغيير نوع instance_type الأساس الأول
  • تمت إضافة نسخة أساسية ثانية
  • تم تغيير اسم مجموعة الأمان

لاحظ أن التغيير الأخير هو قاعدة مجموعة الأمان التي اكتشفت تلقائيًا تغييرًا في اسم المجموعة الرئيسية. في رأيي ، الخطة بأكملها سهلة القراءة للغاية. تظهر بعض قيم المعلمات على أنها <computed> - وهذا لا يعني أنه سيتم تغييرها ، ولكن لا يمكن استرجاعها وتقديمها في هذه المرحلة (مثل اسم SG الذي لم يتم إنشاؤه بعد). تذكر أنه سيتم عرض الموارد التي تم تغييرها فقط أثناء أمر plan . أي شيء تم حذفه لن يتم لمسه.

عادة ، تقبل أوامر Terraform معلمات إضافية. أهم معلمة لأمر الخطة هو الخيار -out ، والذي سيحفظ خطتك على القرص. يمكن بعد ذلك تنفيذ هذه الخطة المحفوظة (تمامًا كما تم حفظها) بواسطة الأمر "تطبيق". هذا مهم جدًا ، لأنه بخلاف ذلك قد يكون هناك تغيير ، apply سبيل المثال ، من قبل الزملاء بين إصدار plan وإصدار الطلب. مثال:

  1. إصدار plan والتحقق من أنها تبدو جيدة.
  2. شخص ما يغير الحالة دون علمك.
  3. تم apply المشكلة و- عفوًا ، لقد فعلت شيئًا بخلاف ما كان مخططًا له!

لحسن الحظ ، تم تحسين سير العمل هذا في Terraform v0.11.0. منذ هذا الإصدار ، يقدم لك الأمر " apply " تلقائيًا خطة يتعين عليك الموافقة عليها بعد ذلك (عن طريق كتابة " yes " صراحة). الميزة هي أنه إذا قمت بتطبيق هذه الخطة ، فسيتم تنفيذها تمامًا كما هو مقدم.

خيار آخر مفيد هو -destroy ، والذي سيُظهر لك الخطة التي ستؤدي إلى تدمير جميع الموارد التي يديرها Terraform. يمكنك أيضًا استهداف موارد محددة للتدمير باستخدام الخيار -target .

تطبيق Terraform

عندما نطبق خطة معينة ، يخرج Terraform ويضع قفلًا على دولتنا لضمان التفرد. ثم يشرع في تغيير الموارد ، وفي النهاية ، يدفع بحالة محدثة. شيء واحد يجب ملاحظته هو أن بعض الموارد تستغرق وقتًا أطول للانتهاء من غيرها. على سبيل المثال ، قد يستغرق إنشاء مثيل AWS RDS أكثر من 12 دقيقة ، وستنتظر Terraform حتى ينتهي هذا. من الواضح أن Terraform ذكي بما يكفي لعدم منع أي عملية أخرى بهذا. يقوم بإنشاء رسم بياني موجه للتغييرات المطلوبة ، وفي حالة عدم وجود ترابطات ، يستخدم التوازي لتسريع التنفيذ.

استيراد الموارد

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

لجعل عملية التقديم أسهل قليلاً ، يشتمل Terraform على أمر import . يتم استخدامه لإدخال الموارد الخارجية الموجودة بالفعل في حالة Terraform والسماح لها بإدارة تلك الموارد. لسوء الحظ ، لا يستطيع Terraform إنشاء تكوينات تلقائية لتلك الوحدات المستوردة ، على الأقل وقت كتابة هذا التقرير. توجد خطط لهذه الوظيفة ، ولكن في الوقت الحالي ، تحتاج إلى كتابة تعريف المورد أولاً في Terraform ثم استيراد هذا المورد لإخبار Terraform ببدء إدارته. بمجرد الاستيراد إلى الحالة (وأثناء التخطيط للتنفيذ) ، سترى جميع الاختلافات بين ما كتبته في ملفات .tf وما هو موجود بالفعل في السحابة. بهذه الطريقة ، يمكنك تعديل التكوين بشكل أكبر. من الناحية المثالية ، يجب ألا تظهر أي تغييرات ، مما يعني أن تكوين Terraform يعكس ما هو موجود بالفعل على السحابة 1: 1.

الوحدات

تعد الوحدات النمطية جزءًا أساسيًا من تكوين Terraform ، وأوصي باحتضانها واستخدامها بشكل متكرر. أنها توفر لك طريقة لإعادة استخدام مكونات معينة. من الأمثلة على ذلك مجموعة AWS ECS ، والتي تُستخدم لتشغيل حاويات Docker. لكي تعمل مثل هذه المجموعة ، تحتاج إلى تكوين العديد من الموارد المنفصلة: المجموعة نفسها ، وتهيئة التشغيل التي ستدير مثيلات EC2 المنفصلة ، ومستودعات الحاويات للصور ، ومجموعة وسياسة القياس التلقائي ، وما إلى ذلك. تحتاج عادةً إلى مجموعات منفصلة لبيئات و / أو تطبيقات منفصلة.

تتمثل إحدى طرق التغلب على ذلك في نسخ التكوين ولصقه ، ولكن من الواضح أن هذا حل قصير النظر. قد ترتكب أخطاء أثناء القيام بأبسط التحديثات.

تسمح لك الوحدات النمطية بتغليف كل تلك الموارد المنفصلة ضمن مجموعة تكوين واحدة (تسمى الوحدة النمطية). تحدد الوحدات المدخلات والمخرجات وهي الواجهات التي تتواصل من خلالها مع "العالم الخارجي" (أو رمز الاستدعاء). علاوة على ذلك ، يمكن أن تتداخل الوحدات النمطية داخل وحدات نمطية أخرى ، مما يسمح لك بتدوير البيئات المنفصلة بأكملها بسرعة.

الوحدات المحلية والبعيدة

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

هذا ، مع ذلك ، له حدود. من الصعب نسخ هذه الوحدات ومشاركتها. يعد تعيين الإصدار أمرًا مهمًا لأنك قد ترغب في استخدام الإصدار 1.0.0 من وحدة ECS الخاصة بك في الإنتاج ولكنك ترغب في تجربة الإصدار 1.1.0 في بيئة التدريج. إذا تم تخزين الوحدة جنبًا إلى جنب مع التعليمات البرمجية الخاصة بك ، فسوف ينعكس كل تغيير في رمز الوحدة النمطية في كل بيئة (بمجرد تشغيل apply ) وهو أمر غير مرغوب فيه عادةً.

تتمثل إحدى الطرق السهلة في تعيين الوحدات النمطية في وضعها جميعًا في مستودع منفصل ، على سبيل المثال ، وحدات الشركة / وحدات التخزين. بعد ذلك ، عند الرجوع إلى تلك الوحدات داخل تكوين Terraform ، يمكنك استخدام رابط VCS كمصدر:

 module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }

هنا أشير إلى v1.1.0 من الوحدة الخاصة بي (مسار محدد) والتي يمكنني اختبارها بشكل مستقل عن الإصدارات الأخرى من نفس الوحدة في بيئات مختلفة.

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

باستخدام الوحدات

يمكن الرجوع إلى الوحدات النمطية بسهولة في بيئات Terraform من خلال تحديد كتلة وحدة خاصة. فيما يلي مثال على مثل هذه الكتلة لوحدة ECS الافتراضية:

 module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }

تم تحديد جميع الخيارات التي تم تمريرها (باستثناء بعض الخيارات العالمية مثل source ) داخل تكوين هذه الوحدة كمدخلات (متغيرات).

تسجيل الوحدات

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

في السابق ، كان عليك إما كتابة الوحدات النمطية الخاصة بك من البداية (والتعلم من أخطائك) أو استخدام الوحدات المنشورة على GitHub وأماكن أخرى ، دون أي ضمانات بشأن سلوكهم (بصرف النظر عن قراءة الكود!)

مشاركة البيانات بين البيئات

من الناحية المثالية ، يجب أن تكون البيئات منفصلة تمامًا ، حتى باستخدام حسابات AWS مختلفة. في الواقع ، هناك حالات قد تستخدم فيها بيئة Terraform بعض المعلومات في بيئة أخرى. This is especially true if you are gradually converting your architecture to use Terraform. One example might be that you have a global env that provides certain resources to other envs.

Let's say env global shares data with stage . For this to work, you can define outputs at the main level of the environment like so:

 output "vpc_id" { value = "${module.network.vpc_id}" }

Then, in the stage environment, you define a datasource that points to the remote state of global :

 data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }

Now, you can use this datasource as any other and access all the values that were defined in global 's outputs:

 vpc_

Words of Caution

Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.

Where to Report Issues and Monitor Changes

First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.

This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.

Can't Cheat Your Way out of Cloud Knowledge

You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.

Prefer Separate Resources That Are Explicitly linked

There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).

Best-practice example, with explicitly linked resources:

 resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }

Terraform plan Doesn't Always Detect Issues and Conflicts

I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.

Syntax Quirks

There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.

When State Update Fails

It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .

Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock with the lock ID to take it down manually.

Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.

Not Everything Is Fun to Manage Through Terraform

There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!

Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.

When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.

خاتمة

Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.

I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.

I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.

It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.

Related: Terraform vs. CloudFormation: The Definitive Guide