Spring Boot And Docker-3 Build Image With Docker File
Başlangıç
Merhabalar, yazı dizimize kaldığımız yerden devam ediyoruz. Bu yazıda Dockerfile
oluşturmayı inceleyeceğiz. Hatırlayacağınız üzere aşağıdaki adımları gerçekleştirmiştik.
- Spring Boot Rest API oluşturduk.
- Bu Rest Api'yi JAR olarak paketledik.
- JDK içeren Docker Image'i indirdik.
- İndirdiğimiz Image'i kullanarak Container'ı ayağa kaldırdık.
- JAR dosyamızı Container içerisine kopyaladık.
- Son olarak Container'ımızın başlarken JAR dosyamızı çalıştırmasını ayarladık.
Bu saydığımız tüm adımları terminal yardımıyla teker teker yazarak yaptık.Bu işlemleri otomatikleştirmek için Dockerfile
oluşturulur.
Dockerfile Oluşturma
Şimdi gelin hep beraber ilk Dockerfile
dosyamızı oluşturalım.Proje dizinimizde Dockerfile
isminde bir dosya oluşturup içerisine aşağıdaki kodları yazalım.
FROM openjdk:8-jdk-alpine
ADD target/SpringBootRestHello-0.0.1-SNAPSHOT.jar SpringBootRestHello-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["sh","-c","java -jar /SpringBootRestHello-0.0.1-SNAPSHOT.jar"]
Evet yukarıda gördüğünüz kodları açıklayacak olursak.
FROM openjdk:8-jdk-alpine
ifadesi ile OpenJdk Image'ini çekiyoruz.
ADD target/SpringBootRestHello-0.0.1-SNAPSHOT.jar SpringBootRestHello-0.0.1-SNAPSHOT.jar
ifadesi ile target dizinindeki JAR dosyasını Container içerisine aynı isimle kopyalar.
ENTRYPOINT ["sh","-c","java -jar /SpringBootRestHello-0.0.1-SNAPSHOT.jar"]
ifadesi ile de Container'ımıza başlangıçta ilgili JAR'ı çalıştırmasını söylüyoruz.
Dockerfile'ı Kullanma
Evet şimdi gelin hep beraber oluşturduğumuz Dockerfile'ı nasıl kullanacağımıza bakalım.
Proje dizinimizin içindeyken aşağıdaki komutu çalıştırıyoruz.
docker build -t rest-api:with-dockerfile .
-t
parametresi tag anlamına gelir build işleminin sonucunda oluşacak olana Image'in isim:tag bilgisini yazarız.
Komutun sonundaki nokta ilgili Dockerfile'ın şu anki dizinde olduğunu belirtir. Komut çalıştığında aşağıdaki gibi bir çıktı oluşur.
Evet bu işlem sonucunda JAR'ımızı içeren Docker Image'ımız oluştu, isterseniz,
docker images
komutu yardımıyla kontrol edebilriz.
Yukarıdaki çıktıda da gördüğünüz üzere en üstte rest-api
isminde ve with-dockerfile
etiketine sahip bir Image oluştu.
Şimdi sıra bu Image'i kullanarak Container'ımızı ayağa kaldırmaya geldi.Bu işlemi gerçekleştirmek için aşağıdaki komutu kullanırız.
docker run -p 8080:8080 rest-ap:with-dockerfile
Evet yukarıdaki çıktıdan da gördüğünüz gibi Container'ımız ayağa kalktı ve başlangıç scripti olarak ayarladığımız JAR dosyasını çalıştırma işlemini başarılı bir şekilde yaptı. Sonuç olarak tarayıcımızda localhost:8080/hello
ya da localhost:8080/date
endpointlerine gidersek uygulamamızın başarılı bir şekilde çalıştığınız görürüz.
Eğer Dockerfile içerisinde EXPOSE 8080
diye bir ifade kullanırsak docker run komutu içerisinde -p 8080
diye bir ifade kullanmadan uygulamamız 8080 portunda çalışmaya başlar.
Spotify Maven Plug-in ile Image Oluşturma
Bu Maven plugin'i kullanarak JAR oluşturma aşamasında aynı zamanda ilgili Dockerfile'ımızdan Image oluşturabiliriz. Bu plugin'i kullanmak için pom. xml dosyasında <plugins></plugins>
etiketleri arasına aşağıdaki kodlar eklenir.
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>${project.name}</repository>
<tag>${project.version}</tag>
<skipDockerInfo>true</skipDockerInfo>
</configuration>
</plugin>
Bu plugini ekledikten sonra
mvn package -DskipTests
komutunu çalıştırırsak aşağıdaki gibi bir çıktı ile karşılaşırız.
Evet görüldüğü üzere kodlarımız derlendi ve paketlendi sonra Image'imiz oluşturuldu ve içerisine JAR'ımız kopyalandı. Oluşan Image çalıştırılmak için hazır.
docker run spring-boot-rest-hello:0.0.1-SNAPSHOt
komutu ile Image'imizi çalıştırırız. localhost:8080/hello
endpointine tarayıcımızdan gidersek Container'ımızın çalıştığını görürüz.
Daha generic ve reusable bir Dockerfile oluşturmak için aşağıdaki düzenlemeleri yapabiliriz.
FROM openjdk:8-jdk-alpine
ADD target/*.jar app.jar
ENTRYPOINT ["sh","-c","java -jar /app.jar"]
Cached Dependency - No Fat Jar
Şimdiye kadar anlattığımız kısımlarda JAR'ımız çok fazla bağımlılığa sahip olmadığı için boyutu da çok büyük olmadı fakat başka projelerde çok fazla bağımlılık olabilir buda JAR boyutunu oldukça büyük hale getirir bunun önüne geçmek ve build aşamasında her bağımlılığın tekrar indirilmesini önlemek için Maven Dependency Plugin
kullanılır. Bu plugin'i kullanmak için Spotify plugin'i kaldırıp ya da comment yapabiliriz.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
Bu plugin'i pom. xml'e ekledikten sonra mvn package
komutunu çalıştırdığımızda aşağıdaki gibi bir durum oluşur. Oluşan JAR dosyası çözülür sonuçta olarak
target/classes
target/dependency
Klasörleri oluşur, classes altında derlenmiş .class
uzantılı sınıflarımız bulunur.
dependecy altında ise aşağıdaki yapıda projenin bağımlılıkları bulunur.
Evet projemiz derlendi ve bağımlılıkları oluşturuldu artık Image'imizi oluşturabiliriz bunun için Dockerfile
içerisinde bazı değişiklikler yapmamız gerekiyor.
FROM openjdk:8-jdk-alpine
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com/fsoftdev/blog/SpringBootRestHello/SpringBootRestHelloApplication"]
Evet kısaca bahsedecek olursak oluşan bağımlılıkları Image içine kopyalıyoruz ve başlangıç scripti olarak Application sınıfımızın çalıştırılmasını ayarlıyoruz.
docker build -t rest-api:no-fat-jar .
komutunu çalıştırdığımızda aşağıda gördüğünüz gibi Image'imiz oluşur.
Son olarak oluşan Image'ımızı çalıştırıp localhost'tan kontrollerimizi yapıyoruz.
docker run -p 8080:8080 rest-api:no-fat-jar
Google JIB Plug-in ile Image Oluşturma
Bu plugin'i kullandığımız zaman Dockerfile'a ihtiyacımız olmaz. Çünkü Dockerfile içerisindeki ayarları plugin'e config olarak vereceğiz.
pom. xml dosyasına aşağıdaki plugin kodlarını ekleyerek bu plugin'i kullanmaya başlayabiliriz.
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.6.1</version>
<configuration>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
</executions>
</plugin>
pom. xml'i kaydedip mvn clean package -DskipTests
komutunu çalıştırdığımızda Image'imiz oluşur burada ilginç olan durum bu plugin'in birçok şeyi bizden herhangi bir config alamdan halletmesidir. JDK için distroless java
diye bir base image indiriyor, daha sonra bizim Application sınıfımızı kendisi algılıyor. Eğer bu configleri özelleştirmek istiyorsak <configuration></configuration>
etiketleri arasına aşağıdaki gibi bir config yazabiliriz. Eğer config yazmayacaksak eklenti Image adını artifactId kısmından alacaktır ve eğer docker image name standartlarına göre gir artifact idmiz yoksa hata oluşacaktır. Yani artifact id küçük harf sayı ve sembollerden oluşabilir.
<configuration>
<from>
<image>openjdk:alpine</image>
</from>
<to>
<image>in28min/${project.name}</image>
<tags>
<tag>${project.version}</tag>
<tag>latest</tag>
</tags>
</to>
<container>
<jvmFlags>
<jvmFlag>-Xms512m</jvmFlag>
</jvmFlags>
<mainClass>com/fsoftdev/blog/SpringBootRestHello/SpringBootRestHelloApplication</mainClass>
<ports>
<port>8080</port>
</ports>
</container>
</configuration>
Image'imiz oluştuktan sonra docker run komutu ile çalıştırabiliriz.
Bu yazınında sonuna gelmiş bulunmaktayız bir sonraki yazıda Spring Boot Web uygulamasını nasıl containerize edeceğimizi öğreneceğiz.