Aplikasi Dockerizing Spring Boot

1. Gambaran keseluruhan

Dalam artikel ini, kami akan memfokuskan pada cara merapatkan Aplikasi Spring Boot untuk menjalankannya dalam persekitaran terpencil, alias bekas .

Selanjutnya, kami akan menunjukkan cara membuat komposisi bekas, yang saling bergantung antara satu sama lain dan dihubungkan antara satu sama lain dalam rangkaian peribadi maya. Kami juga akan melihat bagaimana ia dapat dikendalikan bersama dengan satu perintah.

Mari kita mulakan dengan membuat gambar asas ringan dan berkemampuan Java, menjalankan Alpine Linux .

2. Sokongan Buildpacks di Spring Boot 2.3

Spring Boot 2.3 menambah sokongan untuk buildpacks . Ringkasnya, bukannya membuat Dockerfile kita sendiri dan membinanya menggunakan sesuatu seperti build docker , yang perlu kita buat ialah mengeluarkan arahan berikut:

$ ./mvnw spring-boot:build-image

Atau di Gradle:

$ ./gradlew bootBuildImage

Motivasi utama di sebalik buildpacks adalah untuk mencipta pengalaman penyebaran yang sama seperti yang disediakan oleh beberapa perkhidmatan awan terkenal seperti Heroku atau Cloud Foundry untuk sementara waktu . Kami hanya menjalankan matlamat build-image dan platform itu sendiri mengurus membina dan menggunakan artifak.

Lebih-lebih lagi, ia dapat membantu kita mengubah cara kita membina imej Docker dengan lebih berkesan. Daripada menerapkan perubahan yang sama pada banyak Dockerfiles dalam projek yang berlainan, yang harus kita lakukan adalah mengubah atau menyesuaikan pembangun gambar buildpacks.

Selain kemudahan penggunaan dan pengalaman keseluruhan pembangun yang lebih baik, ia juga boleh menjadi lebih efisien . Sebagai contoh, pendekatan buildpacks akan membuat gambar Docker berlapis dan menggunakan versi fail Jar yang meletup.

3. Imej Pangkalan Biasa

Kami akan menggunakan format fail binaan Docker sendiri: Dockerfile .

A Dockerfile pada dasarnya, fail linewise kelompok, yang mengandungi arahan untuk membina imej. Tidak semestinya sangat mustahak untuk memasukkan perintah ini ke dalam fail, kerana kami juga dapat meneruskannya ke baris perintah - fail lebih mudah.

Oleh itu, mari tulis Dockerfile pertama kami :

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/
  • DARI : Kata kunci DARI , menyuruh Docker menggunakan gambar yang diberi dengan teg sebagai asas-asas. Sekiranya gambar ini tidak ada di perpustakaan tempatan, carian dalam talian di DockerHub , atau pada pendaftaran jauh yang dikonfigurasi lain, dilakukan
  • MAINTAINER : MAINTAINER biasanya alamat e-mel, yang mengenal pasti pengarang gambar
  • RUN : Dengan perintah RUN , kami menjalankan baris perintah shell dalam sistem sasaran. Di sini kami menggunakan apk pengurus pakej Alpine Linux untuk memasang OpenJDK Java 8
  • COPY : Perintah terakhir memberitahu Docker untuk MENGHASIL beberapa fail dari sistem fail tempatan, khususnya subfolder ke direktori build, ke dalam gambar di jalan tertentu

KEPERLUAN: Untuk berjaya menjalankan tutorial, anda harus memuat turun Fail Dasar Jurisdiksi Kekuatan Tidak Terbatas Java Cryptography Extension (JCE) dari Oracle . Cukup ekstrak arkib yang dimuat turun ke folder tempatan bernama 'file'.

Untuk akhirnya membina gambar dan menyimpannya di perpustakaan tempatan, kita harus menjalankan:

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

PEMBERITAHUAN: Pilihan –tag akan memberikan namanya pada gambar dan –rm = true akan membuang gambar perantaraan setelah berjaya dibina. Karakter terakhir dalam perintah shell ini adalah titik, bertindak sebagai argumen build-direktori.

4. Dockerize Aplikasi Spring Spring Berdiri

Sebagai contoh untuk aplikasi yang dapat kita dok, kita akan mengambil spring-cloud-config / server dari tutorial konfigurasi cloud spring. Sebagai langkah persediaan, kita harus menyusun fail balang yang dapat dijalankan dan menyalinnya ke direktori build Docker kami :

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 kandungan 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 asas untuk gambar kami, kami akan menggunakan Java Alpine Linux yang diaktifkan , yang dibuat di bahagian sebelumnya
  • SALINAN : Kami membiarkan Docker menyalin fail balang kami ke dalam gambar
  • ENV : Perintah ini membolehkan kita menentukan beberapa pemboleh ubah persekitaran, yang akan dipatuhi oleh aplikasi yang berjalan di dalam wadah. Di sini kami menentukan konfigurasi Spring Boot Application yang disesuaikan , untuk diserahkan kepada jar yang boleh dilaksanakan kemudian
  • ENTRYPOINT/CMD: This will be the executable to start when the container is booting. We must define them as JSON-Array, because we will use an ENTRYPOINT in combination with a CMD for some application arguments
  • VOLUME: Because our container will be running in an isolated environment, with no direct network access, we have to define a mountpoint-placeholder for our configuration repository
  • EXPOSE: Here we are telling Docker, on which port our application is listing. This port will be published to the host, when the container is booting

To create an image from our Dockerfile, we have to run ‘docker build', like before:

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

But before we're going to run a container from our image, we have to create a volume for mounting:

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

NOTICE: While a container is immutable, when not committed to an image after application exits, data stored in a volume will be persistent over several containers.

Finally, we are able to run the container from our image:

$> docker run --name=config-server --publish=8888:8888 \ --volume=spring-cloud-config-repo:/var/lib/spring-cloud/config-repo \ config-server:latest
  • First, we have to –name our container. If not, one will be automatically chosen
  • Then, we must –publish our exposed port (see Dockerfile) to a port on our host. The value is given in the form ‘host-port:container-port'. If only a container-port is given, a randomly chosen host-port will be used. If we leave this option out, the container will be completely isolated
  • The –volume option gives access to either a directory on the host (when used with an absolute path) or a previously created Docker volume (when used with a volume-name). The path after the colon specifies the mountpoint within the container
  • As argument we have to tell Docker, which image to be used. Here we have to give the image-name from the previously ‘docker build‘ step
  • Some more useful options:
    • -it – enable interactive mode and allocate a pseudo-tty
    • -d – detach from the container after booting

If we run the container in detached mode, we can inspect its details, stop it and remove it with the following commands:

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

5. Dockerize Dependent Applications in a Composite

Docker commands and Dockerfiles are particularly suitable for creating individual containers. But if you want to operate on a network of isolated applications, the container management quickly becomes cluttered.

To solve that, Docker provides a tool named Docker Compose. This comes with an own build-file in YAML format and is better suited in managing multiple containers. For example, it is able to start or stop a composite of services in one command, or merges the logging output of multiple services together into one pseudo-tty.

Let's build an example of two applications running in different Docker containers. They will communicate with each other and be presented as a “single unit” to the host system. We will build and copy the spring-cloud-config/client example described in the spring cloud configuration tutorial to our files folder, like we have done before with the config-server.

This will be our docker-compose.yml:

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
  • version: Specifies which format version should be used. This is a mandatory field. Here we use the newer version, whereas the legacy format is ‘1'
  • services: Each object in this key defines a service, a.k.a container. This section is mandatory
    • 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.

Selepas itu, kami dapat meningkatkan perkhidmatan seperti berikut:

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

6. Kesimpulannya

Seperti yang telah kita lihat, kita sekarang dapat membina gambar Docker tersuai , menjalankan Aplikasi Boot Musim Semi sebagai wadah Docker , dan membuat kontena yang bergantung dengan komposisi docker .

Untuk membaca lebih lanjut mengenai build-files, kami merujuk kepada rujukan Dockerfile rasmi dan rujukan docker-compose.yml .

Seperti biasa, kod sumber untuk tutorial ini boleh didapati di Github .