Scaling Scala: كيفية Dockerize باستخدام Kubernetes
نشرت: 2022-03-11Kubernetes هو الطفل الجديد في الكتلة ، ويعد بالمساعدة في نشر التطبيقات في السحابة وتوسيع نطاقها بسرعة أكبر. اليوم ، عند التطوير لبنية الخدمات المصغرة ، من المعتاد جدًا اختيار Scala لإنشاء خوادم API.
إذا كان هناك تطبيق Scala في خططك وتريد توسيع نطاقه إلى سحابة ، فأنت في المكان الصحيح. في هذه المقالة ، سأعرض خطوة بخطوة كيفية استخدام تطبيق Scala العام وتنفيذ Kubernetes مع Docker لتشغيل مثيلات متعددة من التطبيق. ستكون النتيجة النهائية تطبيقًا واحدًا يتم نشره كمثيلات متعددة ، ويتم موازنة التحميل بواسطة Kubernetes.
سيتم تنفيذ كل هذا ببساطة عن طريق استيراد مجموعة أدوات Kubernetes في تطبيق Scala الخاص بك. يرجى ملاحظة أن المجموعة تخفي الكثير من التفاصيل المعقدة المتعلقة بالتثبيت والتكوين ، ولكنها صغيرة بما يكفي لتكون قابلة للقراءة وسهلة الفهم إذا كنت تريد تحليل ما تفعله. للتبسيط ، سننشر كل شيء على جهازك المحلي. ومع ذلك ، فإن نفس التكوين مناسب لنشر Kubernetes في العالم الحقيقي على السحابة.
ما هو Kubernetes؟
قبل الخوض في التفاصيل الدموية للتنفيذ ، دعنا نناقش ماهية Kubernetes ولماذا هي مهمة.
ربما سمعت بالفعل عن Docker. بمعنى ما ، إنها آلة افتراضية خفيفة الوزن.
لهذه الأسباب ، فهي بالفعل إحدى الأدوات الأكثر استخدامًا لنشر التطبيقات في السحب. تعد صورة Docker سهلة وسريعة الإنشاء وقابلة للنسخ ، وهي أسهل بكثير من الأجهزة الافتراضية التقليدية مثل VMWare أو VirtualBox أو XEN.
يكمل Kubernetes Docker ، مما يوفر بيئة كاملة لإدارة التطبيقات التي تم إرساؤها. باستخدام Kubernetes ، يمكنك بسهولة نشر مئات أو حتى آلاف تطبيقات Docker وتكوينها وتنظيمها وإدارتها ومراقبتها.
Kubernetes هي أداة مفتوحة المصدر طورتها Google وتم اعتمادها من قبل العديد من البائعين الآخرين. يتوفر Kubernetes في الأصل على النظام الأساسي السحابي لـ Google ، لكن البائعين الآخرين اعتمدوه في خدمات OpenShift السحابية الخاصة بهم أيضًا. يمكن العثور عليها في Amazon AWS و Microsoft Azure و RedHat OpenShift والمزيد من التقنيات السحابية. يمكننا القول أنه في وضع جيد ليصبح معيارًا لنشر التطبيقات السحابية.
المتطلبات الأساسية
الآن بعد أن غطينا الأساسيات ، دعنا نتحقق مما إذا كان لديك جميع البرامج المطلوبة مسبقًا مثبتة. بادئ ذي بدء ، أنت بحاجة إلى Docker. إذا كنت تستخدم نظام Windows أو Mac ، فأنت بحاجة إلى Docker Toolbox. إذا كنت تستخدم Linux ، فأنت بحاجة إلى تثبيت الحزمة المحددة التي توفرها التوزيع الخاص بك أو ببساطة اتباع الإرشادات الرسمية.
سنقوم بالترميز في Scala ، وهي لغة JVM. تحتاج ، بالطبع ، إلى تثبيت Java Development Kit وأداة scala SBT وإتاحتها في المسار العالمي. إذا كنت بالفعل مبرمجًا لـ Scala ، فمن المحتمل أن تكون لديك هذه الأدوات مثبتة بالفعل.
إذا كنت تستخدم Windows أو Mac ، فسيقوم Docker افتراضيًا بإنشاء جهاز افتراضي باسم default
بسعة 1 جيجابايت فقط من الذاكرة ، والتي يمكن أن تكون صغيرة جدًا لتشغيل Kubernetes. من واقع خبرتي ، واجهت مشكلات في الإعدادات الافتراضية. أوصي بفتح VirtualBox GUI ، وتحديد default
للجهاز الظاهري ، وتغيير الذاكرة إلى 2048 ميجابايت على الأقل.
تطبيق الكتلة
يمكن أن تنطبق التعليمات الواردة في هذا البرنامج التعليمي على أي تطبيق أو مشروع Scala. لكي تحتوي هذه المقالة على بعض "اللحوم" للعمل عليها ، اخترت مثالًا يستخدم كثيرًا لإظهار خدمة صغيرة بسيطة REST في Scala ، تسمى Akka HTTP. أوصيك بمحاولة تطبيق مجموعة المصدر على المثال المقترح قبل محاولة استخدامه في التطبيق الخاص بك. لقد اختبرت المجموعة مقابل التطبيق التجريبي ، لكن لا يمكنني ضمان عدم وجود تعارضات مع التعليمات البرمجية الخاصة بك.
لذلك أولاً ، نبدأ باستنساخ التطبيق التجريبي:
git clone https://github.com/theiterators/akka-http-microservice
بعد ذلك ، اختبر ما إذا كان كل شيء يعمل بشكل صحيح:
cd akka-http-microservice sbt run
بعد ذلك ، قم بالوصول إلى http://localhost:9000/ip/8.8.8.8
، وسترى شيئًا مثل في الصورة التالية:
إضافة Source Kit
الآن ، يمكننا إضافة مجموعة المصدر ببعض سحر Git:
git remote add ScalaGoodies https://github.com/sciabarra/ScalaGoodies git fetch --all git merge ScalaGoodies/kubernetes
مع ذلك ، لديك العرض التوضيحي بما في ذلك مجموعة المصدر ، وأنت مستعد للمحاولة. أو يمكنك حتى نسخ الرمز ولصقه من هناك في التطبيق الخاص بك.
بمجرد دمج الملفات أو نسخها في مشاريعك ، فأنت جاهز للبدء.
جارٍ بدء Kubernetes
بمجرد تنزيل المجموعة ، نحتاج إلى تنزيل برنامج kubectl
binary الضروري ، عن طريق تشغيل:
bin/install.sh
هذا المثبت ذكي بما يكفي (نأمل) لتنزيل kubectl
binary الصحيح لـ OSX أو Linux أو Windows ، اعتمادًا على نظامك. لاحظ أن المثبت عمل على الأنظمة التي أمتلكها. الرجاء الإبلاغ عن أي مشكلات ، حتى أتمكن من إصلاح المجموعة.
بمجرد تثبيت برنامج kubectl
الثنائي ، يمكنك بدء تشغيل Kubernetes بالكامل في Docker المحلي. فقط قم بتشغيل:
bin/start-local-kube.sh
عند تشغيله لأول مرة ، سيقوم هذا الأمر بتنزيل صور مكدس Kubernetes بالكامل ، والسجل المحلي المطلوب لتخزين صورك. قد يستغرق الأمر بعض الوقت ، لذا يرجى التحلي بالصبر. لاحظ أيضًا أنه يحتاج إلى وصول مباشر إلى الإنترنت. إذا كنت تعمل خلف وكيل ، فستكون هناك مشكلة لأن المجموعة لا تدعم الوكلاء. لحلها ، يجب عليك تكوين الأدوات مثل Docker و curl وما إلى ذلك لاستخدام الوكيل. إنه معقد بدرجة كافية لدرجة أنني أوصي بالحصول على وصول مؤقت غير مقيد.
بافتراض أنك تمكنت من تنزيل كل شيء بنجاح ، للتحقق مما إذا كان Kubernetes يعمل بشكل جيد ، يمكنك كتابة الأمر التالي:
bin/kubectl get nodes
الجواب المتوقع هو:
NAME STATUS AGE 127.0.0.1 Ready 2m
لاحظ أن العمر قد يختلف بالطبع. أيضًا ، نظرًا لأن بدء Kubernetes قد يستغرق بعض الوقت ، فقد تضطر إلى استدعاء الأمر عدة مرات قبل أن ترى الإجابة. إذا لم تحصل على أخطاء هنا ، فتهانينا ، لديك Kubernetes قيد التشغيل على جهازك المحلي.
إرساء تطبيق Scala الخاص بك
الآن بعد أن أصبح لديك Kubernetes قيد التشغيل ، يمكنك نشر تطبيقك فيه. في الأيام الخوالي ، قبل Docker ، كان عليك نشر خادم كامل لتشغيل تطبيقك. باستخدام Kubernetes ، كل ما عليك فعله لنشر تطبيقك هو:
- قم بإنشاء صورة Docker.
- ادفعه في سجل من حيث يمكن إطلاقه.
- قم بتشغيل المثيل باستخدام Kubernetes ، والذي سيأخذ الصورة من السجل.
لحسن الحظ ، يبدو الأمر أقل تعقيدًا ، خاصة إذا كنت تستخدم أداة إنشاء SBT كما يفعل الكثيرون.
في المجموعة ، قمت بتضمين ملفين يحتويان على جميع التعريفات اللازمة لإنشاء صورة قادرة على تشغيل تطبيقات Scala ، أو على الأقل ما هو مطلوب لتشغيل عرض Akka HTTP التجريبي. لا يمكنني ضمان أنه سيعمل مع أي تطبيقات Scala أخرى ، لكنها نقطة انطلاق جيدة ، ويجب أن تعمل مع العديد من التكوينات المختلفة. الملفات المطلوب البحث عنها لبناء صورة Docker هي:
docker.sbt project/docker.sbt
دعونا نلقي نظرة على ما بداخلهم. يحتوي ملف project/docker.sbt
على الأمر لاستيراد المكون الإضافي sbt-docker
:
addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0")
يدير هذا المكون الإضافي بناء صورة Docker باستخدام SBT نيابةً عنك. تعريف Docker موجود في ملف docker.sbt
ويبدو كالتالي:
imageNames in docker := Seq(ImageName("localhost:5000/akkahttp:latest")) dockerfile in docker := { val jarFile: File = sbt.Keys.`package`.in(Compile, packageBin).value val classpath = (managedClasspath in Compile).value val mainclass = mainClass.in(Compile, packageBin).value.getOrElse(sys.error("Expected exactly one main class")) val jarTarget = s"/app/${jarFile.getName}" val classpathString = classpath.files.map("/app/" + _.getName) .mkString(":") + ":" + jarTarget new Dockerfile { from("anapsix/alpine-java:8") add(classpath.files, "/app/") add(jarFile, jarTarget) entryPoint("java", "-cp", classpathString, mainclass) } }
لفهم معنى هذا الملف تمامًا ، يجب أن تعرف Docker جيدًا بما يكفي لفهم ملف التعريف هذا. ومع ذلك ، فإننا لا ندخل في تفاصيل ملف تعريف Docker ، لأنك لست بحاجة إلى فهمه تمامًا لبناء الصورة.

ستقوم SBT بتجميع كافة الملفات نيابة عنك.
لاحظ أن classpath يتم إنشاؤه تلقائيًا بواسطة الأمر التالي:
val classpath = (managedClasspath in Compile).value
بشكل عام ، من المعقد جدًا جمع كل ملفات JAR لتشغيل أحد التطبيقات. باستخدام SBT ، سيتم إنشاء ملف Docker مع add(classpath.files, "/app/")
. بهذه الطريقة ، تجمع SBT جميع ملفات JAR نيابة عنك وتقوم بإنشاء Dockerfile لتشغيل التطبيق الخاص بك.
تجمع الأوامر الأخرى الأجزاء المفقودة لإنشاء صورة Docker. سيتم إنشاء الصورة باستخدام APT للصورة الحالية لتشغيل برامج Java ( anapsix/alpine-java:8
، متاح على الإنترنت في Docker Hub). تعليمات أخرى تضيف الملفات الأخرى لتشغيل التطبيق الخاص بك. أخيرًا ، من خلال تحديد نقطة دخول ، يمكننا تشغيلها. لاحظ أيضًا أن الاسم يبدأ بـ localhost:5000
عن قصد ، لأن localhost:5000
هو المكان الذي قمت فيه بتثبيت التسجيل في البرنامج النصي start-kube-local.sh
.
بناء صورة Docker مع SBT
لإنشاء صورة Docker ، يمكنك تجاهل جميع تفاصيل ملف Docker. تحتاج فقط إلى كتابة:
sbt dockerBuildAndPush
سيقوم المكون الإضافي sbt-docker
ذلك بإنشاء صورة Docker لك ، وتنزيل جميع القطع الضرورية من الإنترنت ، ثم يتم دفعها إلى سجل Docker الذي بدأ من قبل ، مع تطبيق Kubernetes في localhost
المحلي. لذلك ، كل ما تحتاجه هو الانتظار قليلاً حتى يتم طهي صورتك وتجهيزها.
ملاحظة ، إذا واجهت مشاكل ، فإن أفضل ما يمكنك فعله هو إعادة تعيين كل شيء إلى حالة معروفة عن طريق تشغيل الأوامر التالية:
bin/stop-kube-local.sh bin/start-kube-local.sh
يجب أن توقف هذه الأوامر جميع الحاويات وتعيد تشغيلها بشكل صحيح حتى يصبح السجل جاهزًا لتلقي الصورة التي تم إنشاؤها ودفعها بواسطة sbt
.
بدء الخدمة في Kubernetes
الآن بعد أن تم حزم التطبيق في حاوية ودفعه في سجل ، أصبحنا جاهزين لاستخدامه. يستخدم Kubernetes سطر الأوامر وملفات التكوين لإدارة المجموعة. نظرًا لأن سطور الأوامر يمكن أن تصبح طويلة جدًا ، ويمكن أيضًا أن تكون قادرة على تكرار الخطوات ، فأنا أستخدم ملفات التهيئة هنا. جميع العينات في مجموعة المصدر موجودة في مجلد kube
.
خطوتنا التالية هي إطلاق نسخة واحدة من الصورة. تسمى الصورة قيد التشغيل ، في لغة Kubernetes ، جراب . لذلك دعونا ننشئ جرابًا عن طريق استدعاء الأمر التالي:
bin/kubectl create -f kube/akkahttp-pod.yml
يمكنك الآن فحص الموقف باستخدام الأمر:
bin/kubectl get pods
يجب أن ترى:
NAME READY STATUS RESTARTS AGE akkahttp 1/1 Running 0 33s k8s-etcd-127.0.0.1 1/1 Running 0 7d k8s-master-127.0.0.1 4/4 Running 0 7d k8s-proxy-127.0.0.1 1/1 Running 0 7d
يمكن أن تكون الحالة مختلفة في الواقع ، على سبيل المثال ، "ContainerCreating" ، وقد يستغرق الأمر بضع ثوانٍ قبل أن تصبح "قيد التشغيل". أيضًا ، يمكنك الحصول على حالة أخرى مثل "خطأ" ، على سبيل المثال ، إذا نسيت إنشاء الصورة من قبل.
يمكنك أيضًا التحقق مما إذا كان البود الخاص بك يعمل بالأمر:
bin/kubectl logs akkahttp
يجب أن ترى ناتجًا ينتهي بشيء مثل هذا:
[DEBUG] [05/30/2016 12:19:53.133] [default-akka.actor.default-dispatcher-5] [akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /0:0:0:0:0:0:0:0:9000
الآن لديك الخدمة وتشغيلها داخل الحاوية. ومع ذلك ، لا يمكن الوصول إلى الخدمة بعد. هذا السلوك جزء من تصميم Kubernetes. البود الخاص بك قيد التشغيل ، ولكن عليك كشفه بوضوح. خلاف ذلك ، من المفترض أن تكون الخدمة داخلية.
إنشاء خدمة
يعد إنشاء خدمة والتحقق من النتيجة أمرًا يتعلق بتنفيذ:
bin/kubectl create -f kube/akkahttp-service.yaml bin/kubectl get svc
يجب أن نرى شيئا من هذا القبيل:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE akkahttp-service 10.0.0.54 9000/TCP 44s kubernetes 10.0.0.1 <none> 443/TCP 3m
لاحظ أن المنفذ يمكن أن يكون مختلفًا. خصص Kubernetes منفذًا للخدمة وبدأ تشغيله. إذا كنت تستخدم Linux ، يمكنك فتح المتصفح مباشرة واكتب http://10.0.0.54:9000/ip/8.8.8.8
لرؤية النتيجة. إذا كنت تستخدم Windows أو Mac مع Docker Toolbox ، فإن عنوان IP محلي للجهاز الظاهري الذي يقوم بتشغيل Docker ، وللأسف لا يزال يتعذر الوصول إليه.
أريد أن أؤكد هنا أن هذه ليست مشكلة Kubernetes ، بل هي قيود على Docker Toolbox ، والتي تعتمد بدورها على القيود التي تفرضها الأجهزة الافتراضية مثل VirtualBox ، والتي تعمل كجهاز كمبيوتر داخل كمبيوتر آخر. للتغلب على هذا القيد ، نحتاج إلى إنشاء نفق. لتسهيل الأمور ، قمت بتضمين برنامج نصي آخر يفتح نفقًا على منفذ عشوائي للوصول إلى أي خدمة نشرناها. يمكنك كتابة الأمر التالي:
bin/forward-kube-local.sh akkahttp-service 9000
لاحظ أن النفق لن يعمل في الخلفية ، عليك إبقاء نافذة المحطة مفتوحة طالما كنت في حاجة إليها وإغلاقها عندما لا تحتاج إلى النفق بعد الآن. أثناء تشغيل النفق ، يمكنك فتح: http://localhost:9000/ip/8.8.8.8
والاطلاع أخيرًا على التطبيق قيد التشغيل في Kubernetes.
اللمسة الأخيرة: مقياس
حتى الآن وضعنا تطبيقنا "ببساطة" في Kubernetes. على الرغم من أنه إنجاز مثير ، إلا أنه لا يضيف قيمة كبيرة لنشرنا. لقد تم إنقاذنا من جهد التحميل والتثبيت على الخادم وتهيئة خادم وكيل له.
حيث تتألق Kubernetes في القياس. يمكنك نشر اثنتين أو عشرة أو مائة مثيل لتطبيقنا من خلال تغيير عدد النسخ المتماثلة في ملف التكوين فقط. لنفعلها اذا.
سنقوم بإيقاف الكبسولة الفردية والبدء في النشر بدلاً من ذلك. لذلك دعونا ننفذ الأوامر التالية:
bin/kubectl delete -f kube/akkahttp-pod.yml bin/kubectl create -f kube/akkahttp-deploy.yaml
بعد ذلك ، تحقق من الحالة. مرة أخرى ، يمكنك المحاولة عدة مرات لأن عملية النشر قد تستغرق بعض الوقت حتى يتم تنفيذها:
NAME READY STATUS RESTARTS AGE akkahttp-deployment-4229989632-mjp6u 1/1 Running 0 16s akkahttp-deployment-4229989632-s822x 1/1 Running 0 16s k8s-etcd-127.0.0.1 1/1 Running 0 6d k8s-master-127.0.0.1 4/4 Running 0 6d k8s-proxy-127.0.0.1 1/1 Running 0 6d
الآن لدينا قرصان ، وليس واحدًا. هذا لأنه في ملف التكوين الذي قدمته ، توجد replica: 2
، مع اسمين مختلفين تم إنشاؤهما بواسطة النظام. لن أخوض في تفاصيل ملفات التكوين ، لأن نطاق المقالة هو مجرد مقدمة لمبرمجي Scala للانطلاق في Kubernetes.
على أي حال ، هناك الآن نوعان من البودات النشطة. المثير للاهتمام هو أن الخدمة هي نفسها كما كانت من قبل. قمنا بتكوين الخدمة على موازنة التحميل بين جميع الكبسولات المسماة akkahttp
. هذا يعني أنه لا يتعين علينا إعادة نشر الخدمة ، ولكن يمكننا استبدال مثيل واحد بنسخة منسوخة.
يمكننا التحقق من ذلك عن طريق تشغيل الوكيل مرة أخرى (إذا كنت تستخدم Windows وقمت بإغلاقه):
bin/forward-kube-local.sh akkahttp-service 9000
بعد ذلك ، يمكننا محاولة فتح نافذتين طرفيتين ورؤية سجلات كل جراب. على سبيل المثال ، في النوع الأول:
bin/kubectl logs -f akkahttp-deployment-4229989632-mjp6u
وفي النوع الثاني:
bin/kubectl logs -f akkahttp-deployment-4229989632-s822x
بالطبع ، قم بتحرير سطر الأوامر وفقًا للقيم الموجودة في نظامك.
الآن ، حاول الوصول إلى الخدمة باستخدام مستعرضين مختلفين. يجب أن تتوقع أن يتم تقسيم الطلبات بين الخوادم المتعددة المتاحة ، كما في الصورة التالية:
خاتمة
اليوم نحن بالكاد خدش السطح. يوفر Kubernetes الكثير من الاحتمالات ، بما في ذلك القياس التلقائي وإعادة التشغيل وعمليات النشر الإضافية والأحجام. علاوة على ذلك ، فإن التطبيق الذي استخدمناه كمثال بسيط للغاية وعديم الحالة مع وجود حالات مختلفة لا تحتاج إلى معرفة بعضها البعض. في العالم الحقيقي ، تحتاج التطبيقات الموزعة إلى معرفة بعضها البعض ، وتحتاج إلى تغيير التكوينات وفقًا لتوافر الخوادم الأخرى. في الواقع ، يقدم etcd
مخزن مفاتيح موزعًا (إلخ) للسماح للتطبيقات المختلفة بالتواصل مع بعضها البعض عند نشر مثيلات جديدة. ومع ذلك ، فإن هذا المثال صغير بما يكفي ومبسط عن قصد لمساعدتك على المضي قدمًا ، مع التركيز على الوظائف الأساسية. إذا اتبعت البرنامج التعليمي ، فيجب أن تكون قادرًا على الحصول على بيئة عمل لتطبيق Scala الخاص بك على جهازك دون الخلط بين عدد كبير من التفاصيل والضياع في التعقيد.
- التطوير من أجل السحابة في السحابة: تطوير البيانات الضخمة باستخدام Docker في AWS
- K8s / Kubernetes: AWS مقابل GCP مقابل Azure
- مقارنة شبكة خدمة Kubernetes