بناء صور Docker محسّنة لتطبيق Spring Boot

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

تتناول هذه المقالة طرقًا مختلفة لتعبئة تطبيق Spring Boot في حاوية:

  • بناء صورة عامل ميناء باستخدام ملف عامل ميناء ،
  • بناء صورة OCI من المصدر باستخدام Cloud-Native Buildpack ،
  • وتحسين الصورة في وقت التشغيل عن طريق فصل أجزاء JAR إلى مستويات مختلفة باستخدام أدوات ذات طبقات.

 مثال رمز

هذه المقالة مصحوبة بمثال رمز العمل على جيثب .

مصطلحات الحاويات

سنبدأ بمصطلحات الحاوية المستخدمة في جميع أنحاء المقالة:

  • صورة الحاوية: ملف بتنسيق معين. نقوم بتحويل تطبيقنا إلى صورة حاوية عن طريق تشغيل أداة البناء.
  • حاوية: مثيل قابل للتنفيذ لصورة الحاوية.
  • محرك الحاوية: العملية الخفية المسؤولة عن تشغيل الحاوية.
  • مضيف الحاوية: الجهاز المضيف الذي يعمل عليه محرك الحاوية.
  • تسجيل الحاوية: الموقع العام المستخدم لنشر وتوزيع صورة الحاوية.
  • معيار OCIمبادرة الحاويات المفتوحة (OCI) هو إطار إدارة خفيف الوزن ومفتوح المصدر شكلته مؤسسة Linux. تحدد مواصفات صور OCI معايير الصناعة لتنسيقات صور الحاوية ووقت التشغيل لضمان أن جميع محركات الحاوية يمكنها تشغيل صور الحاوية التي تم إنشاؤها بواسطة أي أداة بناء.

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

يوفر الإصدار 2.3 من Spring Boot مكونات إضافية لبناء صور OCI.

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

بناء صورة حاوية بالطريقة التقليدية

يعد إنشاء صور Docker لتطبيقات Spring Boot أمرًا سهلاً للغاية عن طريق إضافة بعض الإرشادات إلى Dockerfile الخاص بك.

نقوم أولاً بإنشاء JAR قابل للتنفيذ ، وكجزء من تعليمات Dockerfile ، انسخ JAR القابل للتنفيذ أعلى صورة JRE الأساسية بعد تطبيق التخصيصات اللازمة.

لنقم بإنشاء تطبيق Spring الخاص بنا على بداية الربيع مع التبعيات weblombokи actuator. نضيف أيضًا وحدة تحكم أخرى لتوفير واجهة برمجة تطبيقات بها GETطريقة.

إنشاء Dockerfile

ثم نضع هذا التطبيق في حاوية عن طريق الإضافة Dockerfile:

FROM adoptopenjdk:11-jre-hotspot
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/application.jar"]

يحتوي ملف 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: 

بناء صور Docker محسّنة لتطبيق Spring Boot

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

بناء صورة حاوية باستخدام Buildpack

حزم التجميع (حزم البناء) هو مصطلح عام تستخدمه العديد من عروض النظام الأساسي كخدمة (PAAS) لإنشاء صورة حاوية من كود المصدر. تم إطلاقه بواسطة Heroku في عام 2011 وتم اعتماده منذ ذلك الحين بواسطة Cloud Foundry و Google App Engine و Gitlab و Knative وعدد قليل من الآخرين.

بناء صور Docker محسّنة لتطبيق Spring Boot

ميزة حزم الإنشاء السحابية

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

كانت حزم البناء مرتبطة ارتباطًا وثيقًا بالمنصة. توفر حزم Build-Native للتوحيد القياسي عبر الأنظمة الأساسية من خلال دعم تنسيق صورة OCI ، مما يضمن إمكانية تشغيل الصورة بواسطة محرك Docker.

استخدام البرنامج المساعد Spring Boot

ينشئ المكون الإضافي Spring Boot صور OCI من المصدر باستخدام Buildpack. يتم إنشاء الصور باستخدام bootBuildImageالمهام (Gradle) أو spring-boot:build-imageالهدف (Maven) وتركيب Docker المحلي.

يمكننا تخصيص اسم الصورة التي نحتاج إلى دفعها إلى سجل Docker عن طريق تحديد الاسم في image tag:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <name>docker.io/pratikdas/${project.artifactId}:v1</name>
    </image>
  </configuration>
</plugin>

دعنا نستخدم Maven للتنفيذ build-imageأهدافًا لإنشاء تطبيق وإنشاء صورة حاوية. لا نستخدم حاليًا أي ملفات Dockerfiles.

mvn spring-boot:build-image

ستكون النتيجة شيئًا كالتالي:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Building image 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [creator]     Adding label 'org.springframework.boot.version'
.. [creator]     *** Images (c311fe74ec73):
.. [creator]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] Successfully built image 'docker.io/pratikdas/usersignup:v1'

من الإخراج ، نرى ذلك paketo Cloud-Native buildpackتستخدم لإنشاء صورة OCI عاملة. كما في السابق ، يمكننا رؤية الصورة مدرجة كصورة Docker عن طريق تشغيل الأمر:

docker images 

الخلاصة:

REPOSITORY                             SIZE
paketobuildpacks/run                  84.3MB
gcr.io/paketo-buildpacks/builder      652MB
pratikdas/usersignup                  257MB

إنشاء صورة حاوية باستخدام Jib

Jib هو مكون إضافي لتأليف الصور من Google يوفر طريقة بديلة لإنشاء صورة حاوية من المصدر.

التكوين jib-maven-pluginفي pom.xml:

      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>

بعد ذلك ، نقوم بتشغيل المكون الإضافي Jib باستخدام الأمر Maven لبناء التطبيق وإنشاء صورة الحاوية. كما في السابق ، لا نستخدم أي ملفات Dockerfiles هنا:

mvn compile jib:build -Dimage=<docker registry name>/usersignup:v1

بعد تنفيذ أمر 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 السميك مرة واحدة فقط وتخزينها مؤقتًا على النظام المضيف.

يتم سحب طبقة رقيقة فقط من التطبيق أثناء تحديثات التطبيق وجدولة الحاوية ، كما هو موضح في هذا الرسم البياني:

بناء صور Docker محسّنة لتطبيق Spring Boot

في الأقسام التالية ، سننظر في كيفية إنشاء هذه الصور المحسّنة لتطبيق Spring Boot.

بناء صورة حاوية محسّنة لتطبيق Spring Boot باستخدام Buildpack

يدعم Spring Boot 2.3 الطبقات عن طريق استخراج أجزاء من ملف JAR السميك إلى طبقات منفصلة. يتم تعطيل ميزة الطبقات بشكل افتراضي ويجب تمكينها بشكل صريح باستخدام المكون الإضافي Spring Boot Maven:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layers>
      <enabled>true</enabled>
    </layers>
  </configuration> 
</plugin>

سنستخدم هذا التكوين لبناء صورة الحاوية الخاصة بنا أولاً باستخدام Buildpack ثم باستخدام Docker في الأقسام التالية.

هيا نركض build-imageالهدف المخضرم لإنشاء صورة حاوية:

mvn spring-boot:build-image

إذا قمنا بتشغيل Dive لرؤية الطبقات في الصورة الناتجة ، يمكننا أن نرى أن طبقة التطبيق (المحاطة بدائرة باللون الأحمر) أصغر بكثير في نطاق كيلوبايت مقارنة بما حصلنا عليه باستخدام تنسيق JAR السميك:

بناء صور Docker محسّنة لتطبيق Spring Boot

بناء صورة حاوية محسّنة لتطبيق Spring Boot باستخدام Docker

بدلاً من استخدام المكون الإضافي Maven أو Gradle ، يمكننا أيضًا إنشاء صورة Docker JAR ذات طبقات مع ملف Docker.

عندما نستخدم Docker ، نحتاج إلى اتخاذ خطوتين إضافيتين لاستخراج الطبقات ونسخها في الصورة النهائية.

ستبدو محتويات JAR الناتجة بعد البناء باستخدام Maven مع تمكين الطبقات كما يلي:

META-INF/
.
BOOT-INF/lib/
.
BOOT-INF/lib/spring-boot-jarmode-layertools-2.3.3.RELEASE.jar
BOOT-INF/classpath.idx
BOOT-INF/layers.idx

يُظهر الإخراج اسم JAR إضافي spring-boot-jarmode-layertoolsи layersfle.idxملف. يوفر ملف JAR الإضافي هذا إمكانيات الطبقات ، كما هو موضح في القسم التالي.

استخراج التبعيات في طبقات منفصلة

لعرض واستخراج الطبقات من JAR ذي الطبقات ، نستخدم خاصية النظام -Djarmode=layertoolsللبداية spring-boot-jarmode-layertoolsJAR بدلاً من التطبيق:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar

ينتج عن تشغيل هذا الأمر مخرجات تحتوي على خيارات الأمر المتاحة:

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

يظهر الإخراج الأوامر listextractи helpс helpيكون الافتراضي. لنقم بتشغيل الأمر بامتداد listخيار:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
dependencies
spring-boot-loader
snapshot-dependencies
application

نرى قائمة من التبعيات التي يمكن إضافتها كطبقات.

الطبقات افتراضيًا:

اسم الطبقة

محتوى

dependencies

أي تبعية لا يحتوي إصدارها على SNAPSHOT

spring-boot-loader

فئات محمل JAR

snapshot-dependencies

أي تبعية تحتوي نسختها على SNAPSHOT

application

فئات التطبيق والموارد

يتم تعريف الطبقات في 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 كيلو بايت فقط ويتم تخزين التبعيات مؤقتًا في طبقات منفصلة. 

بناء صور Docker محسّنة لتطبيق Spring Boot

استخراج التبعيات الداخلية على طبقات منفصلة

يمكننا أيضًا تقليل حجم طبقة التطبيق عن طريق استخراج أي من التبعيات المخصصة لدينا في طبقة منفصلة بدلاً من تجميعها مع التطبيق عن طريق إعلانها في ymlملف مشابه اسمه layers.idx:

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "custom-dependencies":
  - "io/myorg/"
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"

في هذا الملف 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:

 java -Djarmode=layertools -jar application.jar extract

عرض قائمة صور الحاوية

docker images

عرض على اليسار داخل صورة الحاوية (تأكد من تثبيت أداة الغوص):

dive <image ID or image tag>

المصدر: www.habr.com