سنبدأ بمصطلحات الحاوية المستخدمة في جميع أنحاء المقالة:
صورة الحاوية: ملف بتنسيق معين. نقوم بتحويل تطبيقنا إلى صورة حاوية عن طريق تشغيل أداة البناء.
حاوية: مثيل قابل للتنفيذ لصورة الحاوية.
محرك الحاوية: العملية الخفية المسؤولة عن تشغيل الحاوية.
مضيف الحاوية: الجهاز المضيف الذي يعمل عليه محرك الحاوية.
تسجيل الحاوية: الموقع العام المستخدم لنشر وتوزيع صورة الحاوية.
معيار OCI: مبادرة الحاويات المفتوحة (OCI) هو إطار إدارة خفيف الوزن ومفتوح المصدر شكلته مؤسسة Linux. تحدد مواصفات صور OCI معايير الصناعة لتنسيقات صور الحاوية ووقت التشغيل لضمان أن جميع محركات الحاوية يمكنها تشغيل صور الحاوية التي تم إنشاؤها بواسطة أي أداة بناء.
لتعبئة تطبيق ما ، نقوم بلف تطبيقنا في صورة حاوية ونشر تلك الصورة في السجل العام. يسترد وقت تشغيل الحاوية هذه الصورة من السجل ويفككها ويشغل التطبيق بداخلها.
يوفر الإصدار 2.3 من Spring Boot مكونات إضافية لبناء صور OCI.
عامل في حوض السفن هو تطبيق الحاوية الأكثر استخدامًا ، ونستخدم Docker في أمثلةنا ، لذلك ستشير جميع مراجع الحاوية اللاحقة في هذه المقالة إلى Docker.
بناء صورة حاوية بالطريقة التقليدية
يعد إنشاء صور Docker لتطبيقات Spring Boot أمرًا سهلاً للغاية عن طريق إضافة بعض الإرشادات إلى Dockerfile الخاص بك.
نقوم أولاً بإنشاء JAR قابل للتنفيذ ، وكجزء من تعليمات Dockerfile ، انسخ JAR القابل للتنفيذ أعلى صورة JRE الأساسية بعد تطبيق التخصيصات اللازمة.
لنقم بإنشاء تطبيق Spring الخاص بنا على بداية الربيع مع التبعيات web, lombokи actuator. نضيف أيضًا وحدة تحكم أخرى لتوفير واجهة برمجة تطبيقات بها GETطريقة.
إنشاء Dockerfile
ثم نضع هذا التطبيق في حاوية عن طريق الإضافة Dockerfile:
يحتوي ملف Dockerfile الخاص بنا على صورة أساسية ، من adoptopenjdk، فوقها نقوم بنسخ ملف JAR الخاص بنا ثم نفتح المنفذ ، 8080التي ستستمع للطلبات.
تجميع التطبيق
تحتاج أولاً إلى إنشاء تطبيق باستخدام Maven أو Gradle. نحن هنا نستخدم Maven:
mvn clean package
يؤدي هذا إلى إنشاء ملف JAR قابل للتنفيذ للتطبيق. نحتاج إلى تحويل JAR القابل للتنفيذ إلى صورة Docker للتشغيل على محرك Docker.
قم بإنشاء صورة حاوية
ثم نضع ملف JAR القابل للتنفيذ في صورة Docker عن طريق تشغيل الأمر docker buildمن الدليل الجذر للمشروع الذي يحتوي على Dockerfile الذي تم إنشاؤه مسبقًا:
docker build -t usersignup:v1 .
يمكننا رؤية صورتنا في القائمة بالأمر:
docker images
يتضمن إخراج الأمر أعلاه صورتنا usersignupمع الصورة الأساسية ، adoptopenjdkالمحدد في ملف Dockerfile الخاص بنا.
REPOSITORY TAG SIZE
usersignup v1 249MB
adoptopenjdk 11-jre-hotspot 229MB
اعرض الطبقات داخل صورة حاوية
دعونا نلقي نظرة على كومة الطبقات داخل الصورة. سوف نستخدم أداة يغوص، لعرض هذه الطبقات:
dive usersignup:v1
فيما يلي جزء من إخراج الأمر Dive:
كما نرى ، تشكل طبقة التطبيق جزءًا كبيرًا من حجم الصورة. نريد تقليل حجم هذه الطبقة في الأقسام التالية كجزء من تحسيننا.
بناء صورة حاوية باستخدام Buildpack
حزم التجميع (حزم البناء) هو مصطلح عام تستخدمه العديد من عروض النظام الأساسي كخدمة (PAAS) لإنشاء صورة حاوية من كود المصدر. تم إطلاقه بواسطة Heroku في عام 2011 وتم اعتماده منذ ذلك الحين بواسطة Cloud Foundry و Google App Engine و Gitlab و Knative وعدد قليل من الآخرين.
ميزة حزم الإنشاء السحابية
تتمثل إحدى الفوائد الرئيسية لاستخدام Buildpack في إنشاء الصور في ذلك يمكن إدارة تغييرات تكوين الصورة مركزيًا (منشئ) ونشرها على جميع التطبيقات باستخدام المنشئ.
كانت حزم البناء مرتبطة ارتباطًا وثيقًا بالمنصة. توفر حزم Build-Native للتوحيد القياسي عبر الأنظمة الأساسية من خلال دعم تنسيق صورة OCI ، مما يضمن إمكانية تشغيل الصورة بواسطة محرك Docker.
استخدام البرنامج المساعد Spring Boot
ينشئ المكون الإضافي Spring Boot صور OCI من المصدر باستخدام Buildpack. يتم إنشاء الصور باستخدام bootBuildImageالمهام (Gradle) أو spring-boot:build-imageالهدف (Maven) وتركيب Docker المحلي.
يمكننا تخصيص اسم الصورة التي نحتاج إلى دفعها إلى سجل Docker عن طريق تحديد الاسم في image tag:
من الإخراج ، نرى ذلك paketo Cloud-Native buildpackتستخدم لإنشاء صورة OCI عاملة. كما في السابق ، يمكننا رؤية الصورة مدرجة كصورة Docker عن طريق تشغيل الأمر:
بعد تنفيذ أمر Maven أعلاه ، نحصل على المخرجات التالية:
[INFO] Containerizing application to pratikdas/usersignup:v1...
.
.
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, io.pratik.users.UsersignupApplication]
[INFO]
[INFO] Built and pushed image as pratikdas/usersignup:v1
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete
يوضح الإخراج أنه تم إنشاء صورة الحاوية ووضعها في التسجيل.
الدوافع والأساليب لإنشاء صور محسنة
لدينا سببان رئيسيان للتحسين:
أداء: في نظام تزامن الحاوية ، يتم سحب صورة الحاوية من سجل الصور إلى المضيف الذي يقوم بتشغيل محرك الحاوية. هذه العملية تسمى التخطيط. يؤدي سحب الصور الكبيرة من السجل إلى أوقات جدولة طويلة في أنظمة تنظيم الحاويات وأوقات بناء طويلة في خطوط أنابيب CI.
أمن: تحتوي الصور الكبيرة أيضًا على مساحة كبيرة للثغرات الأمنية.
تتكون صورة Docker من كومة من الطبقات ، كل منها يمثل بيانًا في Dockerfile الخاص بنا. تمثل كل طبقة دلتا التغييرات في الطبقة الأساسية. عندما نسحب صورة Docker من التسجيل ، يتم سحبها في طبقات وتخزينها مؤقتًا على المضيف.
يستخدم Spring Boot "فات جار" في كتنسيق العبوة الافتراضي. عندما ننظر إلى JAR السمين ، نرى أن التطبيق جزء صغير جدًا من JAR بالكامل. هذا هو الجزء الذي يتغير أكثر. يتكون الباقي من تبعيات إطار الربيع.
تتمحور صيغة التحسين حول عزل التطبيق على مستوى منفصل عن تبعيات Spring Framework.
يتم تنزيل طبقة التبعية التي تشكل الجزء الأكبر من ملف JAR السميك مرة واحدة فقط وتخزينها مؤقتًا على النظام المضيف.
يتم سحب طبقة رقيقة فقط من التطبيق أثناء تحديثات التطبيق وجدولة الحاوية ، كما هو موضح في هذا الرسم البياني:
في الأقسام التالية ، سننظر في كيفية إنشاء هذه الصور المحسّنة لتطبيق Spring Boot.
بناء صورة حاوية محسّنة لتطبيق Spring Boot باستخدام Buildpack
يدعم Spring Boot 2.3 الطبقات عن طريق استخراج أجزاء من ملف JAR السميك إلى طبقات منفصلة. يتم تعطيل ميزة الطبقات بشكل افتراضي ويجب تمكينها بشكل صريح باستخدام المكون الإضافي Spring Boot Maven:
سنستخدم هذا التكوين لبناء صورة الحاوية الخاصة بنا أولاً باستخدام Buildpack ثم باستخدام Docker في الأقسام التالية.
هيا نركض build-imageالهدف المخضرم لإنشاء صورة حاوية:
mvn spring-boot:build-image
إذا قمنا بتشغيل Dive لرؤية الطبقات في الصورة الناتجة ، يمكننا أن نرى أن طبقة التطبيق (المحاطة بدائرة باللون الأحمر) أصغر بكثير في نطاق كيلوبايت مقارنة بما حصلنا عليه باستخدام تنسيق JAR السميك:
بناء صورة حاوية محسّنة لتطبيق Spring Boot باستخدام Docker
بدلاً من استخدام المكون الإضافي Maven أو Gradle ، يمكننا أيضًا إنشاء صورة Docker JAR ذات طبقات مع ملف Docker.
عندما نستخدم Docker ، نحتاج إلى اتخاذ خطوتين إضافيتين لاستخراج الطبقات ونسخها في الصورة النهائية.
ستبدو محتويات JAR الناتجة بعد البناء باستخدام Maven مع تمكين الطبقات كما يلي:
ينتج عن تشغيل هذا الأمر مخرجات تحتوي على خيارات الأمر المتاحة:
Usage:
java -Djarmode=layertools -jar usersignup-0.0.1-SNAPSHOT.jar
Available commands:
list List layers from the jar that can be extracted
extract Extracts layers from the jar for image creation
help Help about any command
يظهر الإخراج الأوامر list, extractи helpс helpيكون الافتراضي. لنقم بتشغيل الأمر بامتداد listخيار:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
يتم تعريف الطبقات في layers.idxالملف بالترتيب الذي يجب إضافته به إلى صورة Docker. يتم تخزين هذه الطبقات مؤقتًا على المضيف بعد الجلب الأول لأنها لا تتغير. يتم تنزيل طبقة التطبيق المحدثة فقط إلى المضيف ، وهو أسرع نظرًا لتقليل الحجم .
بناء صورة مع تبعيات مستخرجة في طبقات منفصلة
سنقوم ببناء الصورة النهائية في خطوتين باستخدام طريقة تسمى تجميع متعدد المراحل . في الخطوة الأولى سنقوم باستخراج التبعيات وفي الخطوة الثانية سننسخ التبعيات المستخرجة في النهاية.
دعنا نعدل ملف Dockerfile الخاص بنا لبناء متعدد المراحل:
# the first stage of our build will extract the layers
FROM adoptopenjdk:14-jre-hotspot as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract
# the second stage of our build will copy the extracted layers
FROM adoptopenjdk:14-jre-hotspot
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
نحفظ هذا التكوين في ملف منفصل - Dockerfile2.
نقوم ببناء صورة Docker باستخدام الأمر:
docker build -f Dockerfile2 -t usersignup:v1 .
بعد تنفيذ هذا الأمر نحصل على المخرجات التالية:
Sending build context to Docker daemon 20.41MB
Step 1/12 : FROM adoptopenjdk:14-jre-hotspot as builder
14-jre-hotspot: Pulling from library/adoptopenjdk
.
.
Successfully built a9ebf6970841
Successfully tagged userssignup:v1
يمكننا أن نرى أن صورة Docker تم إنشاؤها باستخدام معرف صورة ثم تم وضع علامة عليها.
أخيرًا ، نقوم بتشغيل أمر Dive كما كان من قبل للتحقق من الطبقات داخل صورة Docker التي تم إنشاؤها. يمكننا تقديم معرف صورة أو علامة كمدخلات لأمر الغوص:
dive userssignup:v1
كما ترى من الإخراج ، يبلغ حجم الطبقة التي تحتوي على التطبيق الآن 11 كيلو بايت فقط ويتم تخزين التبعيات مؤقتًا في طبقات منفصلة.
استخراج التبعيات الداخلية على طبقات منفصلة
يمكننا أيضًا تقليل حجم طبقة التطبيق عن طريق استخراج أي من التبعيات المخصصة لدينا في طبقة منفصلة بدلاً من تجميعها مع التطبيق عن طريق إعلانها في ymlملف مشابه اسمه layers.idx:
في هذا الملف layers.idxلقد أضفنا تبعية مخصصة تسمى ، io.myorgتحتوي على تبعيات المؤسسة التي تم استردادها من المستودع المشترك.
إنتاج
في هذه المقالة ، نظرنا في استخدام Cloud-Native Buildpacks لإنشاء صورة حاوية مباشرة من المصدر. هذا بديل لاستخدام Docker لإنشاء صورة حاوية بالطريقة المعتادة: أولاً ، يتم إنشاء ملف JAR سميك قابل للتنفيذ ثم يتم تجميعه في صورة حاوية عن طريق تحديد الإرشادات في Dockerfile.
نظرنا أيضًا في تحسين الحاوية الخاصة بنا من خلال تضمين ميزة الطبقات التي تستخرج التبعيات في طبقات منفصلة يتم تخزينها مؤقتًا على المضيف ويتم تحميل طبقة تطبيق رقيقة في وقت الجدولة في محركات تنفيذ الحاوية.
يمكنك العثور على جميع التعليمات البرمجية المصدر المستخدمة في المقالة في جيثب .
مرجع الأوامر
فيما يلي ملخص للأوامر التي استخدمناها في هذه المقالة كمرجع سريع.
مسح السياق:
docker system prune -a
بناء صورة حاوية باستخدام Dockerfile:
docker build -f <Docker file name> -t <tag> .
إنشاء صورة حاوية من المصدر (بدون Dockerfile):
mvn spring-boot:build-image
عرض طبقات التبعية. قبل إنشاء ملف jar للتطبيق ، تأكد من تمكين ميزة الطبقات في spring-boot-maven-plugin:
java -Djarmode=layertools -jar application.jar list
استخراج طبقات التبعية. قبل إنشاء ملف jar للتطبيق ، تأكد من تمكين ميزة الطبقات في spring-boot-maven-plugin: