Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Bekas telah menjadi cara pilihan untuk membungkus aplikasi dengan semua kebergantungan perisian dan sistem pengendaliannya dan kemudian menghantarnya ke persekitaran yang berbeza.

Artikel ini merangkumi cara yang berbeza untuk menyimpan aplikasi Spring Boot:

  • mencipta imej Docker menggunakan fail Docker,
  • mencipta imej OCI daripada sumber menggunakan Cloud-Native Buildpack,
  • dan pengoptimuman imej masa jalan dengan mengasingkan bahagian JAR ke dalam lapisan berbeza menggunakan alat berbilang peringkat.

 Contoh kod

Artikel ini disertakan dengan contoh kod kerja pada GitHub .

Istilah bekas

Kami akan bermula dengan terminologi bekas yang digunakan dalam artikel:

  • Imej bekas: fail dalam format tertentu. Kami akan menukar aplikasi kami kepada imej bekas dengan menjalankan alat binaan.
  • bekas: Contoh boleh laku bagi imej bekas.
  • Enjin kontena: Proses daemon yang bertanggungjawab untuk menjalankan bekas.
  • Hos kontena: Komputer hos di mana enjin kontena berfungsi.
  • Pendaftaran kontena: Lokasi umum yang digunakan untuk menerbitkan dan mengedarkan imej kontena.
  • piawaian OCIInisiatif Kontena Terbuka (OCI) ialah struktur tadbir urus terbuka yang ringan yang dibentuk dalam Yayasan Linux. Spesifikasi Imej OCI mentakrifkan piawaian industri untuk imej kontena dan format masa jalan untuk memastikan semua enjin kontena boleh menjalankan imej kontena yang dicipta oleh sebarang alat binaan.

Untuk menyimpan aplikasi, kami membungkus aplikasi kami dalam imej kontena dan menerbitkan imej itu ke pendaftaran kongsi. Masa jalan kontena mendapatkan semula imej ini daripada pendaftaran, membongkarnya dan menjalankan aplikasi di dalamnya.

Versi 2.3 Spring Boot menyediakan pemalam untuk mencipta imej OCI.

buruh pelabuhan ialah pelaksanaan kontena yang paling biasa digunakan, dan kami menggunakan Docker dalam contoh kami, jadi semua rujukan kontena berikutnya dalam artikel ini akan merujuk kepada Docker.

Membina imej kontena dengan cara tradisional

Mencipta imej Docker untuk aplikasi Spring Boot adalah sangat mudah dengan menambahkan beberapa arahan pada fail Docker.

Kami mula-mula mencipta fail JAR boleh laku dan, sebagai sebahagian daripada arahan fail Docker, salin fail JAR boleh laku di atas imej JRE asas selepas menggunakan tetapan yang diperlukan.

Mari buat aplikasi Spring kami pada Spring Initializr dengan tanggungan weblombokΠΈ actuator. Kami juga menambah pengawal rehat untuk menyediakan API GETkaedah.

Mencipta fail Docker

Kami kemudiannya menyimpan aplikasi ini dengan menambah Dockerfile:

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

Fail Docker kami mengandungi imej asas daripada adoptopenjdk, di atasnya kami menyalin fail JAR kami dan kemudian membuka port, 8080yang akan mendengar permintaan.

Membina aplikasi

Mula-mula anda perlu membuat aplikasi menggunakan Maven atau Gradle. Di sini kami menggunakan Maven:

mvn clean package

Ini mencipta fail JAR boleh laku untuk aplikasi. Kita perlu menukar JAR boleh laku ini kepada imej Docker untuk dijalankan pada enjin Docker.

Mencipta imej bekas

Kami kemudian meletakkan fail JAR boleh laku ini ke dalam imej Docker dengan menjalankan arahan docker builddari direktori akar projek yang mengandungi Dockerfile yang dibuat sebelum ini:

docker build  -t usersignup:v1 .

Kita boleh melihat imej kami dalam senarai menggunakan arahan:

docker images 

Output arahan di atas termasuk imej kami usersignupbersama-sama dengan imej asas, adoptopenjdkdinyatakan dalam fail Docker kami.

REPOSITORY          TAG                 SIZE
usersignup          v1                  249MB
adoptopenjdk        11-jre-hotspot      229MB

Lihat lapisan dalam imej bekas

Mari lihat timbunan lapisan di dalam imej. Kami akan gunakan alat  menyelam untuk melihat lapisan ini:

dive usersignup:v1

Berikut adalah sebahagian daripada output daripada arahan Dive: 

Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Seperti yang kita dapat lihat, lapisan aplikasi membentuk sebahagian besar saiz imej. Kami ingin mengurangkan saiz lapisan ini dalam bahagian berikut sebagai sebahagian daripada pengoptimuman kami.

Mencipta imej bekas menggunakan Buildpack

Pakej pemasangan (Pek binaan) ialah istilah umum yang digunakan oleh pelbagai tawaran Platform sebagai Perkhidmatan (PAAS) untuk mencipta imej kontena daripada kod sumber. Ia telah dilancarkan oleh Heroku pada 2011 dan sejak itu telah diterima pakai oleh Cloud Foundry, Google App Engine, Gitlab, Knative dan beberapa yang lain.

Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Kelebihan pakej binaan awan

Salah satu faedah utama menggunakan Buildpack untuk mencipta imej ialah itu Perubahan konfigurasi imej boleh diurus secara berpusat (pembina) dan disebarkan kepada semua aplikasi menggunakan pembina.

Pakej binaan digandingkan rapat dengan platform. Cloud-Native Buildpacks menyediakan penyeragaman merentas platform dengan menyokong format imej OCI, yang memastikan imej boleh dijalankan oleh enjin Docker.

Menggunakan pemalam Spring Boot

Pemalam Spring Boot membina imej OCI daripada sumber menggunakan Buildpack. Imej dicipta menggunakan bootBuildImagetugasan (Gradle) atau spring-boot:build-imagesasaran (Maven) dan pemasangan Docker tempatan.

Kami boleh menyesuaikan nama imej yang diperlukan untuk menolak ke pendaftaran Docker dengan menyatakan nama dalam 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>

Mari gunakan Maven untuk melakukannya build-imagematlamat untuk mencipta aplikasi dan mencipta imej bekas. Kami tidak menggunakan sebarang Dockerfiles pada masa ini.

mvn spring-boot:build-image

Hasilnya akan menjadi seperti ini:

[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'

Dari output kita lihat itu paketo Cloud-Native buildpackdigunakan untuk mencipta imej OCI yang berfungsi. Seperti sebelum ini, kita boleh melihat imej disenaraikan sebagai imej Docker dengan menjalankan arahan:

docker images 

Kesimpulan:

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

Mencipta imej bekas menggunakan Jib

Jib ialah pemalam penciptaan imej daripada Google yang menyediakan kaedah alternatif untuk mencipta imej bekas daripada kod sumber.

Mengkonfigurasi jib-maven-plugindalam pom.xml:

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

Seterusnya, kami menjalankan pemalam Jib menggunakan arahan Maven untuk membina aplikasi dan mencipta imej bekas. Seperti sebelum ini, kami tidak menggunakan sebarang fail Docker di sini:

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

Selepas melaksanakan arahan Maven di atas, kami mendapat output berikut:

[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

Output menunjukkan bahawa imej kontena telah dibuat dan diletakkan dalam pendaftaran.

Motivasi dan teknik untuk mencipta imej yang dioptimumkan

Kami mempunyai dua sebab utama untuk pengoptimuman:

  • Produktiviti: Dalam sistem orkestrasi kontena, imej kontena diambil daripada pendaftaran imej kepada hos yang menjalankan enjin kontena. Proses ini dipanggil perancangan. Menarik imej besar daripada registri menghasilkan masa penjadualan yang panjang dalam sistem orkestrasi kontena dan masa binaan yang lama dalam saluran paip CI.
  • keselamatan: Imej yang lebih besar juga mempunyai kawasan yang lebih besar untuk kelemahan.

Imej Docker terdiri daripada timbunan lapisan, setiap satunya mewakili arahan dalam Fail Docker kami. Setiap lapisan mewakili delta perubahan dalam lapisan asas. Apabila kita menarik imej Docker daripada registri, ia ditarik dalam lapisan dan dicache pada hos.

Penggunaan Spring Boot "JAR gemuk" dalam sebagai format pembungkusan lalai. Apabila kita melihat JAR yang tebal, kita melihat bahawa aplikasi itu membentuk sebahagian kecil daripada keseluruhan JAR. Ini adalah bahagian yang paling kerap berubah. Selebihnya terdiri daripada kebergantungan Rangka Kerja Spring.

Formula pengoptimuman berpusat di sekitar mengasingkan aplikasi pada tahap yang berasingan daripada kebergantungan Rangka Kerja Spring.

Lapisan kebergantungan, yang membentuk sebahagian besar fail JAR tebal, dimuat turun sekali sahaja dan dicache pada sistem hos.

Hanya lapisan nipis aplikasi ditarik semasa kemas kini aplikasi dan penjadualan kontena. seperti yang ditunjukkan dalam rajah ini:

Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Dalam bahagian berikut, kita akan melihat cara mencipta imej yang dioptimumkan ini untuk aplikasi Spring Boot.

Mencipta Imej Bekas Dioptimumkan untuk Aplikasi But Spring Menggunakan Buildpack

Spring Boot 2.3 menyokong pelapisan dengan mengekstrak bahagian fail JAR tebal ke dalam lapisan berasingan. Ciri lapisan dilumpuhkan secara lalai dan mesti didayakan secara eksplisit menggunakan pemalam Spring Boot Maven:

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

Kami akan menggunakan konfigurasi ini untuk membina imej kontena kami terlebih dahulu dengan Buildpack dan kemudian dengan Docker dalam bahagian berikut.

Jom lancarkan build-imageSasaran maven untuk mencipta imej bekas:

mvn spring-boot:build-image

Jika kita menjalankan Dive untuk melihat lapisan dalam imej yang terhasil, kita dapat melihat bahawa lapisan aplikasi (digariskan dalam warna merah) adalah jauh lebih kecil dalam julat kilobait berbanding dengan apa yang kita dapat menggunakan format JAR tebal:

Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Mencipta Imej Kontena Dioptimumkan untuk Aplikasi But Spring Menggunakan Docker

Daripada menggunakan pemalam Maven atau Gradle, kami juga boleh mencipta imej Docker JAR berlapis dengan fail Docker.

Apabila kami menggunakan Docker, kami perlu melakukan dua langkah tambahan untuk mengekstrak lapisan dan menyalinnya ke dalam imej akhir.

Kandungan JAR yang terhasil selepas membina menggunakan Maven dengan lapisan diaktifkan akan kelihatan seperti ini:

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

Output menunjukkan JAR tambahan bernama spring-boot-jarmode-layertoolsΠΈ layersfle.idxfail. Fail JAR tambahan ini menyediakan keupayaan pemprosesan berlapis, seperti yang diterangkan dalam bahagian seterusnya.

Mengeluarkan kebergantungan pada lapisan individu

Untuk melihat dan mengekstrak lapisan daripada JAR berlapis kami, kami menggunakan sifat sistem -Djarmode=layertoolsuntuk permulaan spring-boot-jarmode-layertoolsJAR bukannya aplikasi:

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

Menjalankan arahan ini menghasilkan output yang mengandungi pilihan arahan yang tersedia:

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

Output menunjukkan arahan listextractΠΈ helpс helpmenjadi lalai. Mari jalankan arahan dengan listpilihan:

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

Kami melihat senarai kebergantungan yang boleh ditambah sebagai lapisan.

Lapisan lalai:

Nama lapisan

Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΠ΅

dependencies

sebarang pergantungan yang versinya tidak mengandungi SNAPSHOT

spring-boot-loader

Kelas Pemuat JAR

snapshot-dependencies

sebarang pergantungan yang versinya mengandungi SNAPSHOT

application

kelas aplikasi dan sumber

Lapisan ditakrifkan dalam layers.idxfail dalam susunan ia harus ditambahkan pada imej Docker. Lapisan ini dicache dalam hos selepas pengambilan pertama kerana ia tidak berubah. Hanya lapisan aplikasi yang dikemas kini dimuat turun ke hos, yang lebih pantas disebabkan saiz yang dikurangkan .

Membina imej dengan kebergantungan yang diekstrak ke dalam lapisan berasingan

Kami akan membina imej akhir dalam dua peringkat menggunakan kaedah yang dipanggil perhimpunan pelbagai peringkat . Dalam langkah pertama kami akan mengekstrak kebergantungan dan pada langkah kedua kami akan menyalin kebergantungan yang diekstrak ke dalam imej akhir.

Mari ubah suai Dockerfile kami untuk binaan berbilang peringkat:

# 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"]

Kami menyimpan konfigurasi ini dalam fail berasingan - Dockerfile2.

Kami membina imej Docker menggunakan arahan:

docker build -f Dockerfile2 -t usersignup:v1 .

Selepas menjalankan arahan ini kami mendapat output berikut:

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

Kita dapat melihat bahawa imej Docker dicipta dengan ID imej dan kemudian ditandakan.

Akhir sekali, kami menjalankan perintah Dive seperti sebelum ini untuk memeriksa lapisan dalam imej Docker yang dihasilkan. Kami boleh menyediakan ID imej atau tag sebagai input kepada arahan Dive:

dive userssignup:v1

Seperti yang anda lihat dalam output, lapisan yang mengandungi aplikasi kini hanya 11 KB, dan kebergantungan dicache dalam lapisan berasingan. 

Mencipta Imej Docker Dioptimumkan untuk Aplikasi Spring Boot

Mengekstrak kebergantungan dalaman pada lapisan individu

Kami boleh mengurangkan lagi saiz peringkat aplikasi dengan mengekstrak mana-mana kebergantungan tersuai kami ke dalam peringkat berasingan dan bukannya membungkusnya dengan aplikasi dengan mengisytiharkannya dalam ymlfail serupa bernama 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/"

Dalam fail ini layers.idxkami telah menambah pergantungan tersuai bernama, io.myorgmengandungi kebergantungan organisasi yang diambil daripada repositori kongsi.

Output

Dalam artikel ini, kami melihat menggunakan Cloud-Native Buildpacks untuk membina imej bekas terus daripada kod sumber. Ini adalah alternatif untuk menggunakan Docker untuk mencipta imej bekas dengan cara biasa: mula-mula mencipta fail JAR boleh laku tebal dan kemudian membungkusnya ke dalam imej bekas dengan menyatakan arahan dalam fail Docker.

Kami juga melihat untuk mengoptimumkan bekas kami dengan mendayakan ciri lapisan yang menarik kebergantungan ke lapisan berasingan yang dicache pada hos dan lapisan nipis aplikasi dimuatkan pada masa penjadualan dalam enjin pelaksanaan kontena.

Anda boleh menemui semua kod sumber yang digunakan dalam artikel di Github .

Rujukan arahan

Berikut ialah senarai ringkas arahan yang kami gunakan dalam artikel ini.

Pembersihan konteks:

docker system prune -a

Mencipta imej bekas menggunakan fail Docker:

docker build -f <Docker file name> -t <tag> .

Kami membina imej kontena daripada kod sumber (tanpa Dockerfile):

mvn spring-boot:build-image

Lihat lapisan pergantungan. Sebelum membina fail JAR aplikasi, pastikan ciri lapisan didayakan dalam spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Mengeluarkan lapisan pergantungan. Sebelum membina fail JAR aplikasi, pastikan ciri lapisan didayakan dalam spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Lihat senarai imej bekas

docker images

Lihat di sebelah kiri dalam imej bekas (pastikan alat menyelam dipasang):

dive <image ID or image tag>

Sumber: www.habr.com