Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Konteynerler, bir uygulamayı tüm yazılım ve işletim sistemi bağımlılıklarıyla birlikte paketlemek ve ardından bunları farklı ortamlara dağıtmak için tercih edilen araç haline geldi.

Bu makalede, Spring Boot uygulamasını kapsayıcıya almanın farklı yolları ele alınmaktadır:

  • docker dosyası kullanarak docker görüntüsü oluşturma,
  • Cloud-Native Buildpack'i kullanarak kaynaktan bir OCI görüntüsü oluşturmak,
  • ve katmanlı araçları kullanarak JAR parçalarını farklı seviyelere ayırarak çalışma zamanında görüntü optimizasyonu.

 Kod örneği

Bu makaleye bir çalışma kodu örneği eşlik etmektedir GitHub'da .

Konteyner terminolojisi

Makale boyunca kullanılan kapsayıcı terminolojiyle başlayacağız:

  • Konteyner resmi: belirli bir formattaki dosya. Derleme aracını çalıştırarak uygulamamızı konteyner görüntüsüne dönüştürüyoruz.
  • konteyner: Kapsayıcı görüntüsünün yürütülebilir bir örneği.
  • Konteyner motoru: Konteynerin çalıştırılmasından sorumlu olan daemon işlemi.
  • Konteyner ana bilgisayarı: Konteyner motorunun çalıştığı ana makine.
  • Konteyner kaydı: Kapsayıcı görüntüsünü yayınlamak ve dağıtmak için kullanılan genel konum.
  • OCI standardıAçık Konteyner Girişimi (OCI) Linux Vakfı tarafından oluşturulan hafif, açık kaynaklı bir yönetim çerçevesidir. OCI Görüntü Spesifikasyonu, tüm konteyner motorlarının herhangi bir derleme aracı tarafından oluşturulan konteyner görüntülerini çalıştırabilmesini sağlamak amacıyla konteyner görüntü formatları ve çalışma süresi için endüstri standartlarını tanımlar.

Bir uygulamayı kapsayıcıya almak için uygulamamızı bir kapsayıcı görüntüsüne sarıyoruz ve bu görüntüyü genel kayıt defterinde yayınlıyoruz. Kapsayıcı çalışma zamanı bu görüntüyü kayıt defterinden alır, paketini açar ve içindeki uygulamayı çalıştırır.

Spring Boot'un 2.3 sürümü, OCI görüntüleri oluşturmak için eklentiler sağlar.

liman işçisi en sık kullanılan konteyner uygulamasıdır ve örneklerimizde Docker'ı kullanıyoruz, dolayısıyla bu makaledeki sonraki tüm konteyner referansları Docker'a atıfta bulunacaktır.

Geleneksel yöntemle konteyner görüntüsü oluşturma

Spring Boot uygulamaları için Docker görüntüleri oluşturmak, Docker dosyanıza birkaç talimat ekleyerek çok kolaydır.

Öncelikle yürütülebilir bir JAR oluşturuyoruz ve Dockerfile talimatlarının bir parçası olarak, gerekli özelleştirmeleri uyguladıktan sonra yürütülebilir JAR'ı temel JRE görüntüsünün üstüne kopyalıyoruz.

Spring uygulamamızı oluşturalım Bahar İlklendirme bağımlılıklarla weblombokи actuator. Ayrıca bir API sağlamak için bir dinlenme denetleyicisi de ekliyoruz. GETyöntem.

Docker dosyası oluşturma

Daha sonra bu uygulamayı ekleyerek bir konteynere yerleştiriyoruz. Dockerfile:

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

Dockerfile'ımız bir temel görüntü içeriyor: adoptopenjdkBunun üzerine JAR dosyamızı kopyalayıp portu açıyoruz, 8080istekleri dinleyecek.

Uygulama derlemesi

Öncelikle Maven veya Gradle kullanarak bir uygulama oluşturmanız gerekiyor. Burada Maven kullanıyoruz:

mvn clean package

Bu, uygulama için yürütülebilir bir JAR dosyası oluşturur. Bu yürütülebilir JAR'ı Docker motorunda çalıştırmak için Docker görüntüsüne dönüştürmemiz gerekiyor.

Kapsayıcı görüntüsü oluşturma

Daha sonra komutu çalıştırarak bu JAR çalıştırılabilir dosyasını Docker görüntüsüne yerleştiririz. docker builddaha önce oluşturulan Docker dosyasını içeren projenin kök dizininden:

docker build  -t usersignup:v1 .

Aşağıdaki komutla görselimizi listede görebiliriz:

docker images 

Yukarıdaki komutun çıktısı görselimizi içerir usersignuptemel görüntüyle birlikte, adoptopenjdkDocker dosyamızda belirtildi.

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

Bir kapsayıcı görüntüsünün içindeki katmanları görüntüleme

Görüntünün içindeki katman yığınına bakalım. Kullanacağız alet  dalmak, bu katmanları görüntülemek için:

dive usersignup:v1

Dalış komutunun çıktısının bir kısmı: 

Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Görüldüğü gibi uygulama katmanı görüntü boyutunun önemli bir kısmını oluşturmaktadır. Optimizasyonumuzun bir parçası olarak ilerleyen bölümlerde bu katmanın boyutunu azaltmak istiyoruz.

Buildpack ile konteyner görüntüsü oluşturma

Montaj paketleri (Derleme paketleri), kaynak kodundan kapsayıcı görüntüsü oluşturmak için çeşitli Hizmet Olarak Platform (PAAS) teklifleri tarafından kullanılan genel bir terimdir. 2011 yılında Heroku tarafından başlatıldı ve o zamandan beri Cloud Foundry, Google App Engine, Gitlab, Knative ve birkaç kişi tarafından benimsendi.

Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Cloud Build Paketlerinin Avantajı

Görüntü oluşturmak için Buildpack kullanmanın temel faydalarından biri, görüntü yapılandırma değişiklikleri merkezi olarak (oluşturucu) yönetilebilir ve oluşturucu kullanılarak tüm uygulamalara yayılabilir.

Derleme paketleri platforma yakından bağlıydı. Cloud-Native Buildpack'ler, görüntünün Docker motoru tarafından çalıştırılabilmesini sağlayan OCI görüntü formatını destekleyerek platformlar arasında standardizasyon sağlar.

Spring Boot Eklentisini Kullanmak

Spring Boot eklentisi, Buildpack'i kullanarak kaynaktan OCI görüntüleri oluşturur. Görüntüler kullanılarak oluşturulur bootBuildImagegörevler (Gradle) veya spring-boot:build-imagehedef (Maven) ve yerel Docker kurulumu.

Docker kayıt defterine göndermemiz gereken görüntünün adını, içinde adını belirterek özelleştirebiliriz. 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>

Yürütmek için Maven'i kullanalım build-imageuygulama oluşturmaya ve kapsayıcı görüntüsü oluşturmaya yönelik hedefler. Şu anda herhangi bir Docker dosyası kullanmıyoruz.

mvn spring-boot:build-image

Sonuç şöyle bir şey olacak:

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

Çıktıdan şunu görüyoruz paketo Cloud-Native buildpackÇalışan bir OCI görüntüsü oluşturmak için kullanılır. Daha önce olduğu gibi, şu komutu çalıştırarak Docker görüntüsü olarak listelenen görüntüyü görebiliriz:

docker images 

Sonuç:

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

Jib ile Container Image Oluşturma

Jib, kaynaktan konteyner görüntüsü oluşturmak için alternatif bir yöntem sağlayan, Google'ın sunduğu bir resim yazma eklentisidir.

özelleştirilebilir jib-maven-pluginpom.xml'de:

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

Daha sonra uygulamayı derlemek ve bir konteyner görüntüsü oluşturmak için Maven komutunu kullanarak Jib eklentisini çalıştırıyoruz. Daha önce olduğu gibi burada herhangi bir Docker dosyası kullanmıyoruz:

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

Yukarıdaki Maven komutunu çalıştırdıktan sonra aşağıdaki çıktıyı alıyoruz:

[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

Çıktı, kapsayıcı görüntüsünün oluşturulduğunu ve kayıt defterine yerleştirildiğini gösterir.

Optimize edilmiş görseller oluşturmaya yönelik motivasyonlar ve yöntemler

Optimize etmemizin iki ana nedeni var:

  • Proizvoditelnost: Bir konteyner düzenleme sisteminde, bir konteyner imajı, imaj kayıt defterinden konteyner motorunu çalıştıran ana bilgisayara çekilir. Bu sürece planlama denir. Kayıt defterinden büyük görüntülerin çekilmesi, konteyner düzenleme sistemlerinde uzun planlama sürelerine ve CI ardışık düzenlerinde uzun derleme sürelerine neden olur.
  • güvenlik: Büyük görseller aynı zamanda güvenlik açıkları için de geniş bir alana sahiptir.

Docker görüntüsü, her biri Docker dosyamızdaki bir ifadeyi temsil eden bir yığın katmandan oluşur. Her katman, alttaki katmandaki değişikliklerin deltasını temsil eder. Kayıt defterinden bir Docker görüntüsü çektiğimizde, katmanlar halinde çekilir ve ana bilgisayarda önbelleğe alınır.

Spring Boot'un kullanım alanları "şişman JAR" varsayılan paketleme formatı olarak. Şişman bir JAR'a baktığımızda uygulamanın tüm JAR'ın çok küçük bir parçası olduğunu görüyoruz. En çok değişen kısım burası. Geri kalanı Spring Framework bağımlılıklarından oluşur.

Optimizasyon formülü, uygulamayı Spring Framework bağımlılıklarından ayrı bir düzeyde izole etmeye odaklanmıştır.

Kalın JAR dosyasının büyük kısmını oluşturan bağımlılık katmanı yalnızca bir kez indirilir ve ana sistemde önbelleğe alınır.

Uygulama güncellemeleri ve kapsayıcı planlama sırasında uygulamanın yalnızca ince bir katmanı çekilir. bu şemada gösterildiği gibi:

Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Aşağıdaki bölümlerde, bir Spring Boot uygulaması için bu optimize edilmiş görüntülerin nasıl oluşturulacağına bakacağız.

Buildpack ile Spring Boot Uygulaması için Optimize Edilmiş Konteyner Görüntüsü Oluşturma

Spring Boot 2.3, kalın bir JAR dosyasının parçalarını ayrı katmanlara çıkararak katmanlamayı destekler. Katmanlama özelliği varsayılan olarak devre dışıdır ve Spring Boot Maven eklentisi kullanılarak açıkça etkinleştirilmesi gerekir:

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

İlerleyen bölümlerde öncelikle Buildpack ve ardından Docker ile konteyner imajımızı oluşturmak için bu konfigürasyonu kullanacağız.

Hadi koşalım build-imageBir konteyner görüntüsü oluşturmak için Maven hedefi:

mvn spring-boot:build-image

Ortaya çıkan görüntüdeki katmanları görmek için Dive'ı çalıştırırsak, uygulama katmanının (kırmızı daire içine alınmış) kalın JAR formatını kullanarak elde ettiğimize kıyasla kilobayt aralığında çok daha küçük olduğunu görebiliriz:

Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Docker Kullanarak Spring Boot Uygulaması için Optimize Edilmiş Konteyner Görüntüsü Oluşturma

Maven veya Gradle eklentisi kullanmak yerine Docker dosyasıyla katmanlı bir Docker JAR görüntüsü de oluşturabiliriz.

Docker'ı kullandığımızda katmanları çıkarmak ve bunları son görüntüye kopyalamak için iki ek adım daha uygulamamız gerekiyor.

Maven ile katmanlama etkinleştirilmiş şekilde derlendikten sonra ortaya çıkan JAR'ın içeriği şu şekilde görünecektir:

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

Çıktı, adlı ek bir JAR'ı gösterir. spring-boot-jarmode-layertoolsи layersfle.idxdosya. Bu ek JAR dosyası, sonraki bölümde açıklandığı gibi katmanlama yetenekleri sağlar.

Bireysel katmanlardaki bağımlılıkların çıkarılması

Katmanlı JAR'ımızdan katmanları görüntülemek ve çıkarmak için system özelliğini kullanırız -Djarmode=layertoolskoşmak spring-boot-jarmode-layertoolsUygulama yerine JAR:

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

Bu komutu çalıştırmak, mevcut komut seçeneklerini içeren bir çıktı üretir:

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

Çıktı komutları gösterir listextractи helpс helpvarsayılan ol. Komutu ile çalıştıralım listseçenek:

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

Katman olarak eklenebilecek bağımlılıkların bir listesini görüyoruz.

Varsayılan katmanlar:

Katman adı

Içerik

dependencies

sürümü SNAPSHOT içermeyen herhangi bir bağımlılık

spring-boot-loader

JAR Yükleyici Sınıfları

snapshot-dependencies

sürümü SNAPSHOT içeren herhangi bir bağımlılık

application

uygulama sınıfları ve kaynakları

Katmanlar şurada tanımlanır: layers.idxDosyayı Docker görüntüsüne eklenmeleri gereken sıraya göre düzenleyin. Bu katmanlar değişmedikleri için ilk getirmeden sonra ana bilgisayarda önbelleğe alınır. Ana bilgisayara yalnızca güncellenmiş uygulama katmanı indirilir; bu, küçültülmüş boyut nedeniyle daha hızlıdır .

Bağımlılıkların ayrı katmanlara ayıklandığı bir görüntü oluşturma

Nihai görüntüyü, adı verilen bir yöntemi kullanarak iki adımda oluşturacağız. çok aşamalı montaj . İlk adımda bağımlılıkları çıkaracağız ve ikinci adımda çıkarılan bağımlılıkları son görüntüye kopyalayacağız.

Çok aşamalı bir yapı için Docker dosyamızı değiştirelim:

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

Bu yapılandırmayı ayrı bir dosyaya kaydediyoruz - Dockerfile2.

Docker imajını şu komutu kullanarak oluşturuyoruz:

docker build -f Dockerfile2 -t usersignup:v1 .

Bu komutu yürüttükten sonra aşağıdaki çıktıyı alıyoruz:

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 imajının bir image ID ile oluşturulduğunu ve daha sonra etiketlendiğini görebiliriz.

Son olarak, oluşturulan Docker imajının içindeki katmanları kontrol etmek için önceki gibi Dive komutunu çalıştırıyoruz. Dalış komutuna girdi olarak bir görüntü kimliği veya etiketi sağlayabiliriz:

dive userssignup:v1

Çıktıdan da görebileceğiniz gibi uygulamayı içeren katman artık yalnızca 11 KB boyutundadır ve bağımlılıklar ayrı katmanlarda önbelleğe alınmıştır. 

Spring Boot Uygulaması için Optimize Edilmiş Docker Görüntüleri Oluşturma

Ayrı katmanlardaki dahili bağımlılıkları çıkarın

Özel bağımlılıklarımızdan herhangi birini uygulamayla birlikte paketlemek yerine ayrı bir katmana çıkararak uygulama katmanının boyutunu daha da azaltabiliriz. ymladlı benzer dosya 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/"

Bu dosyada layers.idxadında özel bir bağımlılık ekledik, io.myorgpaylaşılan depodan alınan kuruluş bağımlılıklarını içerir.

Aviator apk

Bu makalede, doğrudan kaynak koddan konteyner görüntüsü oluşturmak için Bulutta Yerel Yapı Paketlerini kullanmayı inceledik. Bu, her zamanki gibi bir kapsayıcı görüntüsü oluşturmak için Docker'ı kullanmanın bir alternatifidir: önce kalın bir yürütülebilir JAR dosyası oluşturun ve ardından Docker dosyasındaki talimatları belirterek bunu bir kapsayıcı görüntüsüne paketleyin.

Ayrıca, bağımlılıkları ana bilgisayarda önbelleğe alınan ayrı katmanlara çıkaran ve konteynerin yürütme motorlarına planlama zamanında ince bir uygulama katmanı yükleyen bir katmanlama özelliği ekleyerek konteynerimizi optimize etmeye de baktık.

Makalede kullanılan tüm kaynak kodlarını şu adreste bulabilirsiniz: Github .

Komut referansı

Hızlı bir referans olması açısından bu makalede kullandığımız komutların bir özetini burada bulabilirsiniz.

Bağlam temizleme:

docker system prune -a

Docker dosyası kullanarak konteyner görüntüsü oluşturma:

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

Kaynaktan kapsayıcı görüntüsü oluşturun (Dockerfile olmadan):

mvn spring-boot:build-image

Bağımlılık katmanlarını görüntüleyin. Uygulama jar dosyasını oluşturmadan önce, spring-boot-maven-eklentisinde katmanlama özelliğinin etkinleştirildiğinden emin olun:

java -Djarmode=layertools -jar application.jar list

Bağımlılık katmanlarını çıkarın. Uygulama jar dosyasını oluşturmadan önce, spring-boot-maven-eklentisinde katmanlama özelliğinin etkinleştirildiğinden emin olun:

 java -Djarmode=layertools -jar application.jar extract

Kapsayıcı görsellerinin listesini görüntüleyin

docker images

Konteyner görselinin sol iç kısmındaki görünüm (dalış aracının kurulu olduğundan emin olun):

dive <image ID or image tag>

Kaynak: habr.com