Melakukan Docker pada Aplikasi Spring Boot

1. Ikhtisar

Pada artikel ini, kami akan fokus pada cara melakukan docker pada Aplikasi Spring Boot untuk menjalankannya di lingkungan yang terisolasi, alias wadah .

Selanjutnya, kami akan menunjukkan cara membuat komposisi wadah, yang bergantung satu sama lain dan ditautkan satu sama lain dalam jaringan pribadi virtual. Kami juga akan melihat bagaimana mereka dapat dikelola bersama dengan satu perintah.

Mari kita mulai dengan membuat gambar dasar yang ringan dan berkemampuan Java, menjalankan Alpine Linux .

2. Dukungan Buildpacks di Spring Boot 2.3

Spring Boot 2.3 menambahkan dukungan untuk buildpack . Sederhananya, alih-alih membuat Dockerfile kita sendiri dan membangunnya menggunakan sesuatu seperti build docker , yang harus kita lakukan adalah mengeluarkan perintah berikut:

$ ./mvnw spring-boot:build-image

Atau di Gradle:

$ ./gradlew bootBuildImage

Motivasi utama di balik buildpack adalah untuk menciptakan pengalaman penerapan yang sama dengan yang disediakan beberapa layanan cloud terkenal seperti Heroku atau Cloud Foundry untuk sementara waktu . Kami baru saja menjalankan tujuan membangun-gambar dan platform itu sendiri yang menangani pembuatan dan penerapan artefak.

Selain itu, ini dapat membantu kami mengubah cara kami membuat gambar Docker dengan lebih efektif. Alih-alih menerapkan perubahan yang sama ke banyak Dockerfile dalam proyek yang berbeda, yang harus kita lakukan adalah mengubah atau menyesuaikan pembuat gambar buildpacks.

Selain kemudahan penggunaan dan pengalaman pengembang yang lebih baik secara keseluruhan, ini juga bisa lebih efisien . Misalnya, pendekatan buildpacks akan membuat gambar Docker berlapis dan menggunakan versi file Jar yang di-exploding.

3. Gambar Dasar Umum

Kami akan menggunakan format file build milik Docker : a Dockerfile .

Sebuah Dockerfile pada prinsipnya, file linewise batch, yang berisi perintah untuk membangun sebuah citra. Tidak mutlak perlu untuk memasukkan perintah ini ke dalam file, karena kami dapat meneruskannya ke baris perintah, juga - file akan lebih nyaman.

Jadi, mari kita tulis Dockerfile pertama kita :

FROM alpine:edge MAINTAINER baeldung.com RUN apk add --no-cache openjdk8 COPY files/UnlimitedJCEPolicyJDK8/* \ /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/
  • FROM : Kata kunci FROM , memberi tahu Docker untuk menggunakan gambar tertentu dengan tagnya sebagai basis build. Jika image ini tidak ada di perpustakaan lokal, pencarian online di DockerHub , atau di registri jarak jauh lain yang dikonfigurasi, dilakukan
  • MAINTAINER : A MAINTAINER biasanya berupa alamat email, mengidentifikasi pembuat gambar
  • RUN : Dengan perintah RUN , kami menjalankan baris perintah shell dalam sistem target. Di sini kami menggunakan apk manajer paket Alpine Linux untuk menginstal Java 8 OpenJDK
  • SALIN : Perintah terakhir memberi tahu Docker untuk MENYALIN beberapa file dari sistem file lokal, khususnya subfolder ke direktori build, ke dalam gambar di jalur tertentu

PERSYARATAN: Untuk menjalankan tutorial dengan sukses, Anda harus mengunduh File Kebijakan Yurisdiksi Kekuatan Tanpa Batas Java Cryptography Extension (JCE) dari Oracle . Cukup ekstrak arsip yang diunduh ke dalam folder lokal bernama 'files'.

Untuk akhirnya membangun image dan menyimpannya di perpustakaan lokal, kita harus menjalankan:

docker build --tag=alpine-java:base --rm=true .

PEMBERITAHUAN: The -Tag pilihan akan memberikan gambar nama dan -rm = true akan menghapus gambar menengah setelah telah dibangun berhasil. Karakter terakhir dalam perintah shell ini adalah titik, yang bertindak sebagai argumen direktori build.

4. Membuat Aplikasi Boot Musim Semi Standalone

Sebagai contoh untuk aplikasi yang dapat kita galangankan, kita akan mengambil spring-cloud-config / server dari tutorial konfigurasi cloud musim semi. Sebagai langkah persiapan, kita harus merakit file jar yang dapat dijalankan dan menyalinnya ke direktori build Docker :

tutorials $> cd spring-cloud-config/server server $> mvn package spring-boot:repackage server $> cp target/server-0.0.1-SNAPSHOT.jar \ ../../spring-boot-docker/files/config-server.jar server $> cd ../../spring-boot-docker

Sekarang kita akan membuat Dockerfile bernama Dockerfile.server dengan konten berikut:

FROM alpine-java:base MAINTAINER baeldung.com COPY files/spring-cloud-config-server.jar /opt/spring-cloud/lib/ COPY files/spring-cloud-config-server-entrypoint.sh /opt/spring-cloud/bin/ ENV SPRING_APPLICATION_JSON= \ '{"spring": {"cloud": {"config": {"server": \ {"git": {"uri": "/var/lib/spring-cloud/config-repo", \ "clone-on-start": true}}}}}}' ENTRYPOINT ["/usr/bin/java"] CMD ["-jar", "/opt/spring-cloud/lib/spring-cloud-config-server.jar"] VOLUME /var/lib/spring-cloud/config-repo EXPOSE 8888
  • DARI : Sebagai dasar untuk gambar kita kita akan mengambil Java -memungkinkan Alpine Linux , dibuat di bagian sebelumnya
  • SALIN : Kami membiarkan Docker menyalin file jar kami ke dalam gambar
  • ENV : Perintah ini memungkinkan kita menentukan beberapa variabel lingkungan, yang akan diterapkan oleh aplikasi yang berjalan di penampung. Di sini kami mendefinisikan konfigurasi Aplikasi Spring Boot yang disesuaikan , untuk diserahkan ke jar-executable nanti
  • ENTRYPOINT / CMD : Ini akan menjadi eksekusi untuk memulai ketika kontainer sedang boot. Kita harus mendefinisikannya sebagai JSON-Array , karena kita akan menggunakan ENTRYPOINT yang dikombinasikan dengan CMD untuk beberapa argumen aplikasi.
  • VOLUME : Karena container kami akan berjalan di lingkungan yang terisolasi, tanpa akses jaringan langsung, kami harus menentukan mountpoint-placeholder untuk repositori konfigurasi kami
  • EXPOSE : Di sini kami memberi tahu Docker , di port mana aplikasi kami terdaftar. Porta ini akan dipublikasikan ke host, saat container sedang di-boot

Untuk membuat image dari Dockerfile kita, kita harus menjalankan 'docker build' , seperti sebelumnya:

$> docker build --file=Dockerfile.server \ --tag=config-server:latest --rm=true .

Tapi sebelum kita akan menjalankan container dari image kita, kita harus membuat volume untuk mount:

$> docker volume create --name=spring-cloud-config-repo

PEMBERITAHUAN: Meskipun penampung tidak dapat diubah, jika tidak dikomit ke gambar setelah aplikasi ditutup, data yang disimpan dalam volume akan tetap ada di beberapa penampung.

Akhirnya, kami dapat menjalankan container dari gambar kami:

$> docker run --name=config-server --publish=8888:8888 \ --volume=spring-cloud-config-repo:/var/lib/spring-cloud/config-repo \ config-server:latest
  • Pertama, kita harus –menamai container kita. Jika tidak, seseorang akan dipilih secara otomatis
  • Kemudian, kita harus - mempublikasikan porta yang terekspos (lihat Dockerfile ) ke port pada host kita. Nilainya diberikan dalam bentuk 'host-port: container-port' . Jika hanya port-kontainer yang diberikan, port-host yang dipilih secara acak akan digunakan. Jika kita mengabaikan opsi ini, wadah akan sepenuhnya terisolasi
  • The -volume pilihan memberikan akses ke salah satu direktori pada host (bila digunakan dengan path absolut) atau dibuat sebelumnya Docker volume (bila digunakan dengan volume nama ). Jalur setelah titik dua menentukan titik pemasangan di dalam wadah
  • Sebagai argumen kita harus memberi tahu Docker , image mana yang akan digunakan. Di sini kita harus memberikan nama gambar dari langkah ' docker build ' sebelumnya
  • Beberapa opsi yang lebih berguna:
    • -it - mengaktifkan mode interaktif dan mengalokasikan pseudo-tty
    • -d - lepas dari kontainer setelah booting

Jika kita menjalankan penampung dalam mode terpisah, kita dapat memeriksa detailnya, menghentikannya dan menghapusnya dengan perintah berikut:

$> docker inspect config-server $> docker stop config-server $> docker rm config-server

5. Membuat Aplikasi yang Bergantung pada Docker dalam Komposit

Perintah Docker dan Dockerfiles sangat cocok untuk membuat wadah individu. Namun jika Anda ingin beroperasi pada jaringan aplikasi yang terisolasi , pengelolaan kontainer dengan cepat menjadi berantakan.

Untuk mengatasinya, Docker menyediakan alat bernama Docker Compose . Ini hadir dengan file build sendiri dalam format YAML dan lebih cocok dalam mengelola banyak container. Misalnya, ia dapat memulai atau menghentikan gabungan layanan dalam satu perintah, atau menggabungkan keluaran pencatatan beberapa layanan menjadi satu pseudo-tty .

Mari buat contoh dua aplikasi yang berjalan di wadah Docker berbeda. Mereka akan berkomunikasi satu sama lain dan disajikan sebagai "satu unit" ke sistem host. Kita akan membangun dan menyalin contoh spring-cloud-config / client yang dijelaskan dalam tutorial konfigurasi cloud musim semi ke folder file kita , seperti yang telah kita lakukan sebelumnya dengan config-server .

Ini akan menjadi docker-compose.yml kami :

version: '2' services: config-server: container_name: config-server build: context: . dockerfile: Dockerfile.server image: config-server:latest expose: - 8888 networks: - spring-cloud-network volumes: - spring-cloud-config-repo:/var/lib/spring-cloud/config-repo logging: driver: json-file config-client: container_name: config-client build: context: . dockerfile: Dockerfile.client image: config-client:latest entrypoint: /opt/spring-cloud/bin/config-client-entrypoint.sh environment: SPRING_APPLICATION_JSON: \ '{"spring": {"cloud": \ {"config": {"uri": "//config-server:8888"}}}}' expose: - 8080 ports: - 8080:8080 networks: - spring-cloud-network links: - config-server:config-server depends_on: - config-server logging: driver: json-file networks: spring-cloud-network: driver: bridge volumes: spring-cloud-config-repo: external: true
  • versi : Menentukan versi format mana yang harus digunakan. Ini adalah bidang wajib. Di sini kami menggunakan versi yang lebih baru, sedangkan format legacy adalah '1'
  • services : Setiap objek dalam kunci ini mendefinisikan layanan , alias wadah. Bagian ini wajib diisi
    • build: If given, docker-compose is able to build an image from a Dockerfile
      • context: If given, it specifies the build-directory, where the Dockerfile is looked-up
      • dockerfile: If given, it sets an alternate name for a Dockerfile
    • image: Tells Docker which name it should give to the image when build-features are used. Otherwise, it is searching for this image in the library or remote-registry
    • networks: This is the identifier of the named networks to use. A given name-value must be listed in the networks section
    • volumes: This identifies the named volumes to use and the mountpoints to mount the volumes to, separated by a colon. Likewise in networks section, a volume-name must be defined in separate volumes section
    • links: This will create an internal network link between this service and the listed service. This service will be able to connect to the listed service, whereby the part before the colon specifies a service-name from the services section and the part after the colon specifies the hostname at which the service is listening on an exposed port
    • depends_on: This tells Docker to start a service only, if the listed services have started successfully. NOTICE: This works only at container level! For a workaround to start the dependent application first, see config-client-entrypoint.sh
    • logging: Here we are using the ‘json-file' driver, which is the default one. Alternatively ‘syslog' with a given address option or ‘none' can be used
  • networks: In this section we're specifying the networks available to our services. In this example, we let docker-compose create a named network of type ‘bridge' for us. If the option external is set to true, it will use an existing one with the given name
  • volumes: This is very similar to the networks section

Before we continue, we will check our build-file for syntax-errors:

$> docker-compose config

This will be our Dockerfile.client to build the config-client image from. It differs from the Dockerfile.server in that we additionally install OpenBSD netcat (which is needed in the next step) and make the entrypoint executable:

FROM alpine-java:base MAINTAINER baeldung.com RUN apk --no-cache add netcat-openbsd COPY files/config-client.jar /opt/spring-cloud/lib/ COPY files/config-client-entrypoint.sh /opt/spring-cloud/bin/ RUN chmod 755 /opt/spring-cloud/bin/config-client-entrypoint.sh

And this will be the customized entrypoint for our config-client service. Here we use netcat in a loop to check whether our config-server is ready. You have to notice, that we can reach our config-server by its link-name, instead of an IP address:

#!/bin/sh while ! nc -z config-server 8888 ; do echo "Waiting for upcoming Config Server" sleep 2 done java -jar /opt/spring-cloud/lib/config-client.jar

Finally, we can build our images, create the defined containers, and start it in one command:

$> docker-compose up --build

To stop the containers, remove it from Docker and remove the connected networks and volumes from it, we can use the opposite command:

$> docker-compose down

A nice feature of docker-compose is the ability to scale services. For example, we can tell Docker to run one container for the config-server and three containers for the config-client.

But for this to work properly, we have to remove the container_name from our docker-compose.yml, for letting Docker choose one, and we have to change the exposed port configuration, to avoid clashes.

Setelah itu, kami dapat meningkatkan layanan kami seperti:

$> docker-compose build $> docker-compose up -d $> docker-compose scale config-server=1 config-client=3

6. Kesimpulan

Seperti yang telah kita lihat, kita sekarang dapat membuat image Docker kustom , menjalankan Aplikasi Spring Boot sebagai container Docker , dan membuat container dependen dengan docker-compose .

Untuk membaca lebih lanjut tentang file build, kami merujuk ke referensi resmi Dockerfile dan referensi docker-compose.yml .

Seperti biasa, kode sumber untuk tutorial ini dapat ditemukan di Github .