קונטיינרים הפכו לאמצעי המועדף לאריזת אפליקציה עם כל התלות בתוכנה ובמערכת ההפעלה שלה ולאחר מכן להעביר אותן לסביבות שונות.
מאמר זה מכסה דרכים שונות להכיל יישום Spring Boot במכולה:
- בניית תמונת docker באמצעות dockerfile,
- בניית תמונת OCI ממקור באמצעות Cloud-Native Buildpack,
- ואופטימיזציה של תמונה בזמן ריצה על ידי הפרדת חלקי JAR לרמות שונות באמצעות כלים שכבות.
דוגמה לקוד
מאמר זה מלווה בדוגמה של קוד עבודה .
טרמינולוגיה של מיכל
נתחיל בטרמינולוגיית המכולה המשמשת לאורך המאמר:
- תמונת מיכל: קובץ בפורמט מסוים. אנו ממירים את האפליקציה שלנו לתמונת מכיל על ידי הפעלת כלי הבנייה.
- מכולה: מופע בר הפעלה של תמונת המכולה.
- מנוע מיכל: תהליך הדמון שאחראי להפעלת הקונטיינר.
- מארח מיכל: המכונה המארח שבה פועל מנוע המכולה.
- רישום מיכל: המיקום הכללי המשמש לפרסום והפצה של תמונת המכולה.
- תקן OCI: - הוא מבנה ניהולי קל ופתוח שנוצר במסגרת של Linux יסוד. מפרט התמונות של OCI מגדיר סטנדרטים בתעשייה עבור פורמטים וזמני ריצה של תמונות קונטיינרים כדי להבטיח שכל מנועי הקונטיינרים יוכלו להריץ תמונות קונטיינרים שנוצרו על ידי כל כלי בנייה.
כדי לאכלס אפליקציה, אנו עוטפים את האפליקציה שלנו בתמונת מכיל ומפרסמים את התמונה הזו לרישום הציבורי. זמן הריצה של מיכל מאחזר תמונה זו מהרישום, פורק אותה ומריץ את היישום בתוכה.
גרסה 2.3 של Spring Boot מספקת תוספים לבניית תמונות OCI.
הוא המימוש הנפוץ ביותר של קונטיינר, ואנו משתמשים ב-Docker בדוגמאות שלנו, כך שכל הפניות הקונטיינר הבאות במאמר זה יתייחסו ל-Docker.
בניית תמונת מיכל בדרך המסורתית
בניית תמונות Docker עבור יישומי Spring Boot היא קלה מאוד על ידי הוספת כמה הוראות ל- Dockerfile שלך.
אנו יוצרים תחילה JAR בר הפעלה וכחלק מהוראות Dockerfile, מעתיקים את ה-JAR להפעלה על גבי תמונת JRE הבסיסית לאחר החלת ההתאמות הנחוצות.
בואו ניצור את אפליקציית האביב שלנו ב- עם תלות web, lombokи actuator. אנחנו גם מוסיפים בקר מנוחה כדי לספק API עם 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הנה חלק מהפלט של פקודת הצלילה:

כפי שאנו יכולים לראות, שכבת היישום מהווה חלק ניכר מגודל התמונה. אנו רוצים להקטין את גודל השכבה הזו בסעיפים הבאים כחלק מהאופטימיזציה שלנו.
בניית תמונת מיכל עם Buildpack
() הוא מונח כללי המשמש הצעות שונות של Platform as a Service (PAAS) ליצירת תמונת מיכל מקוד המקור. הוא הושק על ידי Heroku בשנת 2011 ומאז אומץ על ידי Cloud Foundry, Google App Engine, Gitlab, Knative ועוד כמה.

היתרון של חבילות בניית ענן
אחד היתרונות העיקריים של השימוש ב-Buildpack לבניית תמונות הוא זה ניתן לנהל שינויים בתצורת תמונה באופן מרכזי (בונה) ולהפיץ אותם לכל היישומים המשתמשים בבונה.
חבילות הבנייה היו קשורות קשר הדוק לפלטפורמה. Cloud-Native Buildpacks מספקים סטנדרטיזציה בין פלטפורמות על ידי תמיכה בפורמט התמונה OCI, מה שמבטיח שניתן להפעיל את התמונה על ידי מנוע Docker.
שימוש בתוסף Spring Boot
התוסף Spring Boot בונה תמונות OCI ממקור באמצעות Buildpack. תמונות נוצרות באמצעות bootBuildImageמשימות (Gradle) או spring-boot:build-imageיעד (Maven) והתקנה מקומית של Docker.
אנו יכולים להתאים אישית את שם התמונה שאנו צריכים לדחוף ל-Docker Registry על ידי ציון השם 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 הוא תוסף ליצירת תמונות מגוגל המספק שיטה חלופית ליצירת תמונת מיכל מהמקור.
הגדרת תצורה 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.
נוסחת האופטימיזציה מתמקדת בבידוד האפליקציה ברמה נפרדת מהתלות של Spring Framework.
שכבת התלות שמהווה את החלק הארי של קובץ ה-JAR העבה יורדת פעם אחת בלבד ונשמרת במטמון במערכת המארחת.
רק שכבה דקה של האפליקציה נמשכת במהלך עדכוני אפליקציה ותזמון מיכל, כפי שמוצג בתרשים זה:

בסעיפים הבאים, נבחן כיצד ליצור תמונות אופטימליות אלה עבור יישום 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 העבה:

בניית תמונת מיכל אופטימלית עבור יישום 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הפלט מציג את הפקודות list, extractи helpс helpלהיות ברירת המחדל. בוא נריץ את הפקודה עם listאוֹפְּצִיָה:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar listdependencies
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 נוצרת עם מזהה תמונה ולאחר מכן מתויגת.
לבסוף, אנו מפעילים את פקודת הצלילה כמו קודם כדי לבדוק את השכבות בתוך תמונת Docker שנוצרה. אנו יכולים לספק מזהה תמונה או תג כקלט לפקודת הצלילה:
dive userssignup:v1כפי שניתן לראות מהפלט, השכבה המכילה את האפליקציה היא כעת רק 11 KB והתלות נשמרות בשכבות נפרדות.

חילוץ תלות פנימית בשכבות נפרדות
אנו יכולים להקטין עוד יותר את גודל שכבת היישום על ידי חילוץ כל אחת מהתלות המותאמות אישית שלנו לשכבה נפרדת במקום לארוז אותן עם היישום על ידי הצהרתן ב 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הצג שכבות תלות. לפני בניית קובץ צנצנת היישום, ודא שתכונת השכבות מופעלת בתוסף spring-boot-maven-:
java -Djarmode=layertools -jar application.jar listחלץ שכבות תלות. לפני בניית קובץ צנצנת היישום, ודא שתכונת השכבות מופעלת בתוסף spring-boot-maven-:
java -Djarmode=layertools -jar application.jar extractהצגת רשימה של תמונות מיכל
docker imagesהצג משמאל בתוך תמונת המיכל (ודא שכלי הצלילה מותקן):
dive <image ID or image tag>מקור: www.habr.com
