نشر Jenkins المستمر بدون توقف مع Terraform على AWS
نشرت: 2022-03-11في عالم الإنترنت اليوم حيث يحتاج كل شيء حرفيًا إلى العمل على مدار الساعة طوال أيام الأسبوع ، تعد الموثوقية أمرًا أساسيًا. يُترجم هذا إلى ما يقرب من الصفر وقت تعطل لمواقع الويب الخاصة بك ، أو تفادي صفحة الخطأ المخيفة "لم يتم العثور على: 404" ، أو غيرها من اضطرابات الخدمة أثناء طرح أحدث إصدار.
لنفترض أنك أنشأت تطبيقًا جديدًا لعميلك ، أو ربما لنفسك ، وتمكنت من الحصول على قاعدة مستخدمين جيدة تحب تطبيقك. لقد جمعت تعليقات من المستخدمين لديك ، وتذهب إلى مطوريك وتطلب منهم إنشاء ميزات جديدة وجعل التطبيق جاهزًا للنشر. مع هذا الاستعداد ، يمكنك إما إيقاف التطبيق بأكمله ونشر الإصدار الجديد أو إنشاء خط أنابيب لنشر CI / CD بدون توقف والذي من شأنه أن يقوم بكل العمل الشاق المتمثل في دفع إصدار جديد للمستخدمين دون تدخل يدوي.
في هذه المقالة ، سنتحدث بالضبط عن الأخير ، كيف يمكننا الحصول على خط أنابيب نشر مستمر لتطبيق ويب ثلاثي المستويات مُدمج في Node.js على سحابة AWS باستخدام Terraform كمنسق للبنية التحتية. سنستخدم Jenkins لجزء النشر المستمر و Bitbucket لاستضافة قاعدة الرموز الخاصة بنا.
مستودع الكود
سنستخدم تطبيق ويب تجريبيًا من ثلاثة مستويات يمكنك العثور على الكود هنا.
يحتوي الريبو على رمز لكل من الويب وطبقة API. إنه تطبيق بسيط حيث تستدعي وحدة الويب إحدى نقاط النهاية في طبقة API التي تجلب داخليًا معلومات حول الوقت الحالي من قاعدة البيانات وتعود إلى طبقة الويب.
هيكل الريبو كالتالي:
- API: رمز طبقة API
- الويب: رمز لطبقة الويب
- Terraform: كود لتنسيق البنية التحتية باستخدام Terraform
- Jenkins: رمز منظم البنية التحتية لخادم Jenkins المستخدم لخط أنابيب CI / CD.
الآن بعد أن فهمنا ما نحتاج إلى نشره ، دعنا نناقش الأشياء التي يتعين علينا القيام بها لنشر هذا التطبيق على AWS ثم سنتحدث عن كيفية إنشاء هذا الجزء من خط أنابيب CI / CD.
صور الخبز
نظرًا لأننا نستخدم Terraform لمنظم البنية التحتية ، فمن المنطقي أن يكون لديك صور مُعدة مسبقًا لكل طبقة أو تطبيق تريد نشره. ومن أجل ذلك ، سنستخدم منتجًا آخر من Hashicorp - وهو Packer.
Packer هي أداة مفتوحة المصدر تساعد في إنشاء Amazon Machine Image أو AMI ، والتي سيتم استخدامها للنشر على AWS. يمكن استخدامه لإنشاء صور لمنصات مختلفة مثل EC2 و VirtualBox و VMware وغيرها.
فيما يلي مقتطف من كيفية استخدام ملف تهيئة Packer ( terraform/packer-ami-api.json
) لإنشاء AMI لطبقة API.
{ "builders": [{ "type": "amazon-ebs", "region": "eu-west-1", "source_ami": "ami-844e0bf7", "instance_type": "t2.micro", "ssh_username": "ubuntu", "ami_name": "api-instance {{timestamp}}" }], "provisioners": [ { "type": "shell", "inline": ["mkdir api", "sudo apt-get update", "sudo apt-get -y install npm nodejs-legacy"], "pause_before": "10s" }, { "type": "file", "source" : "../api/", "destination" : "api" }, { "type": "shell", "inline": ["cd api", "npm install"], "pause_before": "10s" } ] }
وتحتاج إلى تشغيل الأمر التالي لإنشاء AMI:
packer build -machine-readable packer-ami-api.json
سنقوم بتشغيل هذا الأمر من Jenkins build لاحقًا في هذه المقالة. بطريقة مماثلة ، سنستخدم ملف تهيئة Packer ( terraform/packer-ami-web.json
) لطبقة الويب أيضًا.
دعنا ننتقل إلى ملف تهيئة Packer أعلاه ونفهم ما الذي يحاول القيام به.
- كما ذكرنا سابقًا ، يمكن استخدام Packer لإنشاء صور للعديد من الأنظمة الأساسية ، وبما أننا ننشر تطبيقنا على AWS ، فسنستخدم أداة الإنشاء "amazon-ebs" ، لأنها أسهل أداة إنشاء يمكن البدء بها.
- يأخذ الجزء الثاني من التكوين قائمة بالموفرين التي تشبه إلى حد كبير البرامج النصية أو كتل التعليمات البرمجية التي يمكنك استخدامها لتكوين صورتك.
- تقوم الخطوة 1 بتشغيل مزود shell لإنشاء مجلد API وتثبيت Node.js على الصورة باستخدام الخاصية
inline
، وهي مجموعة من الأوامر التي تريد تشغيلها. - تقوم الخطوة 2 بتشغيل مزود الملفات لنسخ كود المصدر الخاص بنا من مجلد API إلى المثيل.
- تقوم الخطوة 3 مرة أخرى بتشغيل موفر الصدفة ولكن هذه المرة تستخدم خاصية البرنامج النصي لتحديد ملف (terraform / scripts / install_api_software.sh) مع الأوامر التي يجب تشغيلها.
- تقوم الخطوة 4 بنسخ ملف التكوين إلى المثيل المطلوب لـ Cloudwatch ، والذي تم تثبيته في الخطوة التالية.
- تقوم الخطوة 5 بتشغيل مزود shell لتثبيت وكيل AWS Cloudwatch. سيكون الإدخال إلى هذا الأمر هو ملف التكوين المنسوخ في الخطوة السابقة. سنتحدث عن Cloudwatch بالتفصيل لاحقًا في المقالة.
- تقوم الخطوة 1 بتشغيل مزود shell لإنشاء مجلد API وتثبيت Node.js على الصورة باستخدام الخاصية
لذلك ، في جوهرها ، يحتوي تكوين Packer على معلومات حول المنشئ الذي تريده ثم مجموعة من المورّدين التي يمكنك تحديدها بأي ترتيب بناءً على الطريقة التي تريد تكوين صورتك بها.
إعداد نشر جينكينز المستمر
بعد ذلك ، سننظر في إعداد خادم Jenkins والذي سيتم استخدامه لخط أنابيب CI / CD الخاص بنا. سنستخدم Terraform و AWS لإعداد هذا أيضًا.
كود Terraform لإعداد Jenkins موجود داخل مجلد jenkins/setup
. لنستعرض بعض الأشياء المثيرة للاهتمام حول هذا الإعداد.
- بيانات اعتماد AWS: يمكنك إما توفير معرف مفتاح الوصول إلى AWS ومفتاح الوصول السري لمزود
instance.tf
AWS (example.tf) أو يمكنك إعطاء موقع ملف بيانات الاعتماد إلى ملفshared_credentials_file
في مزود AWS. - دور IAM: نظرًا لأننا سنقوم بتشغيل Packer و Terraform من خادم Jenkins ، فسوف يصلون إلى خدمات S3 و EC2 و RDS و IAM وموازنة التحميل وخدمات القياس التلقائي على AWS. لذلك إما أن نقدم بيانات الاعتماد الخاصة بنا على Jenkins for Packer & Terraform للوصول إلى هذه الخدمات أو يمكننا إنشاء ملف تعريف IAM (
iam.tf
) ، والذي يمكننا باستخدامه إنشاء مثيل Jenkins. - حالة Terraform: يجب أن يحافظ Terraform على حالة البنية التحتية في مكان ما في ملف ، وباستخدام S3 (
backend.tf
) ، يمكنك فقط الاحتفاظ بها هناك ، حتى تتمكن من التعاون مع زملاء العمل الآخرين ، ويمكن لأي شخص التغيير والنشر منذ الحالة يتم الاحتفاظ بها في مكان بعيد. - زوج المفاتيح العام / الخاص: ستحتاج إلى تحميل المفتاح العام لزوج المفاتيح الخاص بك جنبًا إلى جنب مع المثيل بحيث يمكنك إرساله إلى مثيل Jenkins بمجرد تشغيله. لقد حددنا مورد
aws_key_pair
(key.tf
) الذي تحدد فيه موقع مفتاحك العام باستخدام متغيرات Terraform.
خطوات إعداد Jenkins:
الخطوة 1: للحفاظ على حالة Terraform البعيدة ، ستحتاج إلى إنشاء دلو يدويًا في S3 يمكن استخدامه بواسطة Terraform. ستكون هذه هي الخطوة الوحيدة التي يتم إجراؤها خارج Terraform. تأكد من تشغيل AWS configure
قبل تشغيل الأمر أدناه لتحديد بيانات اعتماد AWS الخاصة بك.
aws s3api create-bucket --bucket node-aws-jenkins-terraform --region eu-west-1 --create-bucket-configuration LocationConstraint=eu-west-1
الخطوة 2: تشغيل terraform init
. سيؤدي هذا إلى تهيئة الحالة وتكوينها ليتم تخزينها على S3 وتنزيل المكون الإضافي لموفر AWS.
الخطوة 3: تشغيل terraform apply
. سيؤدي هذا إلى التحقق من جميع كود Terraform وإنشاء خطة وإظهار عدد الموارد التي سيتم إنشاؤها بعد انتهاء هذه الخطوة.
الخطوة 4: اكتب yes
، ثم ستبدأ الخطوة السابقة في إنشاء جميع الموارد. بعد انتهاء الأمر ، ستحصل على عنوان IP العام لخادم Jenkins.
الخطوة 5: Ssh في خادم Jenkins ، باستخدام مفتاحك الخاص. ubuntu
هو اسم المستخدم الافتراضي للمثيلات المدعومة من AWS EBS. استخدم عنوان IP الذي تم إرجاعه بواسطة أمر terraform apply
.
ssh -i mykey [email protected]
الخطوة 6: ابدأ واجهة مستخدم ويب Jenkins بالانتقال إلى http://34.245.4.73:8080
. يمكن العثور على كلمة المرور في /var/lib/jenkins/secrets/initialAdminPassword
.
الخطوة 7: اختر "تثبيت الإضافات المقترحة" وأنشئ مستخدمًا إداريًا لـ Jenkins.
إعداد خط أنابيب CI بين Jenkins و Bitbucket
- لهذا ، نحتاج إلى تثبيت المكون الإضافي Bitbucket في Jenkins. انتقل إلى Manage Jenkins → Manage Plugins ومن المكونات الإضافية المتاحة ، قم بتثبيت المكون الإضافي Bitbucket.
- في جانب Bitbucket repo ، انتقل إلى Settings → Webhooks ، وأضف خطاف ويب جديدًا. سيرسل هذا الخطاف جميع التغييرات في المستودع إلى Jenkins وسيؤدي ذلك إلى تشغيل خطوط الأنابيب.
جينكينز بايبلاين لخبز / بناء الصور
- ستكون الخطوة التالية هي إنشاء خطوط أنابيب في جينكينز.
- سيكون خط الأنابيب الأول عبارة عن مشروع Freestyle والذي سيتم استخدامه لبناء AMI للتطبيق باستخدام Packer.
- تحتاج إلى تحديد بيانات الاعتماد وعنوان URL لمستودع Bitbucket الخاص بك.
- حدد بناء المشغل.
- أضف خطوتين للبناء ، واحدة لبناء AMI لوحدة التطبيق والأخرى لبناء AMI لوحدة الويب.
- بمجرد الانتهاء من ذلك ، يمكنك حفظ مشروع Jenkins والآن ، عندما تدفع أي شيء إلى مستودع Bitbucket الخاص بك ، فسيؤدي ذلك إلى إنشاء بنية جديدة في Jenkins والتي من شأنها إنشاء AMI ودفع ملف Terraform الذي يحتوي على رقم AMI لتلك الصورة إلى دلو S3 الذي يمكنك رؤيته من آخر سطرين في خطوة الإنشاء.
echo 'variable "WEB_INSTANCE_AMI" { default = "'${AMI_ID_WEB}'" }' > amivar_web.tf aws s3 cp amivar_web.tf s3://node-aws-jenkins-terraform/amivar_web.tf
جينكينز بايبلاين لتشغيل سيناريو تيرافورم
الآن بعد أن أصبح لدينا AMIs لوحدات API والويب ، سنقوم بتشغيل بنية لتشغيل كود Terraform لإعداد التطبيق بالكامل ثم الانتقال من خلال المكونات في كود Terraform مما يجعل خط الأنابيب هذا ينشر التغييرات بدون توقف الخدمة.

- أنشأنا مشروعًا مجانيًا آخر لـ Jenkins ،
nodejs-terraform
، والذي سيشغل كود Terraform لنشر التطبيق. - سنقوم أولاً بإنشاء بيانات اعتماد من نوع "نص سري" في مجال بيانات الاعتماد العالمية ، والتي سيتم استخدامها كمدخل إلى البرنامج النصي Terraform. نظرًا لأننا لا نريد ترميز كلمة المرور الخاصة بخدمة RDS داخل Terraform و Git ، فإننا نمرر هذه الخاصية باستخدام بيانات اعتماد Jenkins.
- تحتاج إلى تحديد بيانات الاعتماد وعنوان URL مشابهًا للمشروع الآخر.
- في قسم مشغل البناء ، سنربط هذا المشروع بالمشروع الآخر بطريقة بحيث يبدأ هذا المشروع عند انتهاء المشروع السابق.
- ثم يمكننا تكوين بيانات الاعتماد التي أضفناها مسبقًا إلى المشروع باستخدام الروابط ، لذلك فهي متوفرة في خطوة الإنشاء.
- نحن الآن جاهزون لإضافة خطوة بناء ، والتي ستقوم بتنزيل ملفات نصية Terraform (
amivar_api.tf
وamivar_web.tf
) التي تم تحميلها إلى S3 بواسطة المشروع السابق ثم تشغيل كود Terraform لبناء التطبيق بالكامل على AWS.
إذا تم تكوين كل شيء بشكل صحيح ، الآن إذا قمت بدفع أي رمز إلى مستودع Bitbucket الخاص بك ، فيجب أن يؤدي ذلك إلى تشغيل مشروع Jenkins الأول متبوعًا بالمشروع الثاني ويجب أن يتم نشر تطبيقك على AWS.
تهيئة Terraform Zero Downtime لـ AWS
الآن دعنا نناقش ما هو موجود في كود Terraform الذي يجعل خط الأنابيب هذا ينشر الكود بدون أي توقف.
أول شيء هو أن Terraform يوفر كتل تكوين دورة الحياة هذه للموارد التي لديك من خلالها خيار create_before_destroy
كعلامة تعني حرفياً أن Terraform يجب أن ينشئ موردًا جديدًا من نفس النوع قبل تدمير المورد الحالي.
نحن الآن نستغل هذه الميزة في موارد aws_autoscaling_group
و aws_launch_configuration
. لذا فإن aws_launch_configuration
يهيئ نوع مثيل EC2 الذي يجب توفيره وكيفية تثبيت البرنامج على هذا المثال ، ويوفر مورد aws_autoscaling_group
مجموعة قياس تلقائي لـ AWS.
من المثير للاهتمام هنا أن جميع الموارد في Terraform يجب أن يكون لها اسم فريد وتركيبة نوع. لذلك ما لم يكن لديك اسم مختلف لـ aws_autoscaling_group
الجديد و aws_launch_configuration
، فلن يكون من الممكن تدمير الاسم الحالي.
يعالج Terraform هذا القيد من خلال توفير خاصية name_prefix
لمورد aws_launch_configuration
. بمجرد تحديد هذه الخاصية ، سيضيف Terraform لاحقة فريدة لجميع موارد aws_launch_configuration
ومن ثم يمكنك استخدام هذا الاسم الفريد لإنشاء مورد aws_autoscaling_group
.
يمكنك التحقق من الكود لكل ما سبق في terraform/autoscaling-api.tf
resource "aws_launch_configuration" "api-launchconfig" { name_prefix = "api-launchconfig-" image_ instance_type = "t2.micro" security_groups = ["${aws_security_group.api-instance.id}"] user_data = "${data.template_file.api-shell-script.rendered}" iam_instance_profile = "${aws_iam_instance_profile.CloudWatchAgentServerRole-instanceprofile.name}" connection { user = "${var.INSTANCE_USERNAME}" private_key = "${file("${var.PATH_TO_PRIVATE_KEY}")}" } lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "api-autoscaling" { name = "${aws_launch_configuration.api-launchconfig.name}-asg" vpc_zone_identifier = ["${aws_subnet.main-public-1.id}"] launch_configuration = "${aws_launch_configuration.api-launchconfig.name}" min_size = 2 max_size = 2 health_check_grace_period = 300 health_check_type = "ELB" load_balancers = ["${aws_elb.api-elb.name}"] force_delete = true lifecycle { create_before_destroy = true } tag { key = "Name" value = "api ec2 instance" propagate_at_launch = true } }
والتحدي الثاني المتمثل في عدم نشر أي فترة تعطل هو التأكد من أن النشر الجديد جاهز لبدء تلقي الطلب. لا يكفي مجرد نشر مثيل EC2 جديد وبدء تشغيله في بعض المواقف.
لحل هذه المشكلة ، يحتوي aws_launch_configuration
على خاصية user_data
تدعم خاصية AWS autoscaling user_data
الأصلية التي يمكنك من خلالها تمرير أي برنامج نصي ترغب في تشغيله عند بدء تشغيل مثيلات جديدة كجزء من مجموعة القياس التلقائي. في مثالنا ، نقوم بتفصيل سجل خادم التطبيق وننتظر ظهور رسالة بدء التشغيل. يمكنك أيضًا التحقق من خادم HTTP ومعرفة وقت تشغيله.
until tail /var/log/syslog | grep 'node ./bin/www' > /dev/null; do sleep 5; done
إلى جانب ذلك ، يمكنك أيضًا تمكين فحص ELB على مستوى مورد aws_autoscaling_group
، والذي سيضمن إضافة المثيل الجديد لاجتياز اختبار ELB قبل أن يدمر Terraform المثيلات القديمة. هكذا يبدو فحص ELB لطبقة API ؛ يتحقق من نقطة نهاية /api/status
لإرجاع النجاح.
resource "aws_elb" "api-elb" { name = "api-elb" subnets = ["${aws_subnet.main-public-1.id}"] security_groups = ["${aws_security_group.elb-securitygroup.id}"] listener { instance_port = "${var.API_PORT}" instance_protocol = "http" lb_port = 80 lb_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 target = "HTTP:${var.API_PORT}/api/status" interval = 30 } cross_zone_load_balancing = true connection_draining = true connection_draining_timeout = 400 tags { Name = "my-elb" } }
الملخص والخطوات التالية
إذن هذا يقودنا إلى نهاية هذا المقال ؛ نأمل ، الآن ، إما أنك قمت بالفعل بنشر التطبيق وتشغيله باستخدام خط أنابيب CI / CD بدون توقف باستخدام نشر Jenkins وأفضل ممارسات Terraform أو أنك تشعر براحة أكبر في استكشاف هذه المنطقة وجعل عمليات النشر الخاصة بك تحتاج إلى تدخل يدوي بسيط مثل المستطاع.
في هذه المقالة ، تسمى استراتيجية النشر المستخدمة نشر Blue-Green حيث لدينا تثبيت حالي (أزرق) يتلقى حركة مرور مباشرة أثناء نشر واختبار الإصدار الجديد (الأخضر) ثم نستبدلها بمجرد إصدار الإصدار الجديد كل شيء جاهز. بصرف النظر عن هذه الإستراتيجية ، هناك طرق أخرى لبدء نشر التطبيق الخاص بك ، والذي تم شرحه بشكل جيد في هذه المقالة ، مقدمة عن استراتيجيات النشر. أصبح تكييف استراتيجية أخرى الآن أمرًا بسيطًا مثل تكوين خط أنابيب Jenkins الخاص بك.
أيضًا ، في هذه المقالة ، افترضت أن جميع التغييرات الجديدة في API والويب وطبقات البيانات متوافقة لذلك لا داعي للقلق بشأن الإصدار الجديد الذي يتحدث إلى إصدار أقدم. لكن في الواقع ، قد لا يكون هذا هو الحال دائمًا. لحل هذه المشكلة ، أثناء تصميم الإصدار / الميزات الجديدة ، فكر دائمًا في طبقة التوافق مع الإصدارات السابقة وإلا ستحتاج إلى تعديل عمليات النشر الخاصة بك للتعامل مع هذا الموقف أيضًا.
يعد اختبار التكامل شيئًا مفقودًا من خط أنابيب النشر هذا أيضًا. نظرًا لأنك لا تريد إصدار أي شيء للمستخدم النهائي دون أن يتم اختباره ، فمن المؤكد أنه شيء يجب مراعاته عندما يحين الوقت لتطبيق هذه الاستراتيجيات على مشاريعك الخاصة.
إذا كنت مهتمًا بمعرفة المزيد حول كيفية عمل Terraform وكيف يمكنك النشر إلى AWS باستخدام التكنولوجيا ، فإنني أوصي بـ Terraform AWS Cloud: Sane Infrastructure Management حيث يشرح زميلك Toptaler Radoslaw Szalski Terraform ثم يوضح لك الخطوات اللازمة لتكوين نظام متعدد - إعداد Terraform للبيئة والإنتاج لفريق