Una guida Docker per Java

1. Panoramica

In questo articolo, diamo uno sguardo a un'altra API specifica per piattaforma ben consolidata: Java API Client per Docker .

In tutto l'articolo, comprendiamo il modo in cui connettersi a un daemon Docker in esecuzione e il tipo di funzionalità importanti che l'API offre agli sviluppatori Java.

2. Dipendenza da Maven

Innanzitutto, dobbiamo aggiungere la dipendenza principale nel nostro file pom.xml :

 com.github.docker-java docker-java 3.0.14 

Al momento della stesura dell'articolo, l'ultima versione dell'API è 3.0.14 . Ogni versione può essere visualizzata dalla pagina della versione di GitHub o dal repository Maven.

3. Utilizzo del client Docker

DockerClient è dove possiamo stabilire una connessione tra un motore / daemon Docker e la nostra applicazione.

Per impostazione predefinita, il daemon Docker può essere accessibile solo dal file unix: ///var/run/docker.sock . Possiamo comunicare localmente con il motore Docker in ascolto sul socket Unix se non diversamente configurato .

Qui, ci applichiamo alla classe DockerClientBuilder per creare una connessione accettando le impostazioni predefinite:

DockerClient dockerClient = DockerClientBuilder.getInstance().build();

Allo stesso modo, possiamo aprire una connessione in due passaggi:

DefaultDockerClientConfig.Builder config = DefaultDockerClientConfig.createDefaultConfigBuilder(); DockerClient dockerClient = DockerClientBuilder .getInstance(config) .build();

Poiché i motori possono fare affidamento su altre caratteristiche, il client è configurabile anche con condizioni diverse.

Ad esempio, il builder accetta un URL del server, ovvero possiamo aggiornare il valore della connessione se il motore è disponibile sulla porta 2375 :

DockerClient dockerClient = DockerClientBuilder.getInstance("tcp://docker.baeldung.com:2375").build();

Nota che dobbiamo anteporre alla stringa di connessione unix: // o tcp: // a seconda del tipo di connessione.

Se facciamo un ulteriore passo avanti, possiamo ritrovarci con una configurazione più avanzata utilizzando la classe DefaultDockerClientConfig :

DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withRegistryEmail("[email protected]") .withRegistryPassword("baeldung") .withRegistryUsername("baeldung") .withDockerCertPath("/home/baeldung/.docker/certs") .withDockerConfig("/home/baeldung/.docker/") .withDockerTlsVerify("1") .withDockerHost("tcp://docker.baeldung.com:2376").build(); DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();

Allo stesso modo, possiamo eseguire lo stesso approccio utilizzando Proprietà :

Properties properties = new Properties(); properties.setProperty("registry.email", "[email protected]"); properties.setProperty("registry.password", "baeldung"); properties.setProperty("registry.username", "baaldung"); properties.setProperty("DOCKER_CERT_PATH", "/home/baeldung/.docker/certs"); properties.setProperty("DOCKER_CONFIG", "/home/baeldung/.docker/"); properties.setProperty("DOCKER_TLS_VERIFY", "1"); properties.setProperty("DOCKER_HOST", "tcp://docker.baeldung.com:2376"); DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withProperties(properties).build(); DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();

Un'altra scelta a meno che non configuriamo le impostazioni del motore nel codice sorgente è quella di impostare le variabili di ambiente corrispondenti in modo da poter considerare solo l'istanza predefinita di DockerClient nel progetto:

export DOCKER_CERT_PATH=/home/baeldung/.docker/certs export DOCKER_CONFIG=/home/baeldung/.docker/ export DOCKER_TLS_VERIFY=1 export DOCKER_HOST=tcp://docker.baeldung.com:2376

4. Gestione container

L'API ci consente una varietà di scelte sulla gestione dei container. Diamo un'occhiata a ciascuno di essi.

4.1. Elenca contenitori

Ora che abbiamo una connessione stabilita, possiamo elencare tutti i container in esecuzione situati sull'host Docker:

List containers = dockerClient.listContainersCmd().exec();

A condizione che mostrare i contenitori in esecuzione non soddisfi la necessità, possiamo utilizzare le opzioni offerte per interrogare i contenitori.

In questo caso, vengono visualizzati i contenitori con lo stato "uscito":

List containers = dockerClient.listContainersCmd() .withShowSize(true) .withShowAll(true) .withStatusFilter("exited").exec()

È un equivalente di:

$ docker ps -a -s -f status=exited # or $ docker container ls -a -s -f status=exited

4.2. Crea un contenitore

La creazione di un contenitore viene eseguita con il metodo createContainerCmd . Possiamo dichiarare dichiarazioni più complesse usando i metodi disponibili che iniziano con il prefisso " with" .

Supponiamo di avere un comando docker create che definisce un contenitore MongoDB dipendente dall'host in ascolto internamente sulla porta 27017:

$ docker create --name mongo \ --hostname=baeldung \ -e MONGO_LATEST_VERSION=3.6 \ -p 9999:27017 \ -v /Users/baeldung/mongo/data/db:/data/db \ mongo:3.6 --bind_ip_all

Siamo in grado di eseguire il bootstrap dello stesso contenitore insieme alle sue configurazioni in modo programmatico:

CreateContainerResponse container = dockerClient.createContainerCmd("mongo:3.6") .withCmd("--bind_ip_all") .withName("mongo") .withHostName("baeldung") .withEnv("MONGO_LATEST_VERSION=3.6") .withPortBindings(PortBinding.parse("9999:27017")) .withBinds(Bind.parse("/Users/baeldung/mongo/data/db:/data/db")).exec();

4.3. Avvia, interrompi e uccidi un container

Una volta creato il contenitore, possiamo avviarlo, arrestarlo e ucciderlo rispettivamente per nome o ID:

dockerClient.startContainerCmd(container.getId()).exec(); dockerClient.stopContainerCmd(container.getId()).exec(); dockerClient.killContainerCmd(container.getId()).exec();

4.4. Ispeziona un contenitore

Il metodo inspectContainerCmd accetta un argomento String che indica il nome o l'ID di un contenitore. Utilizzando questo metodo, possiamo osservare direttamente i metadati di un contenitore:

InspectContainerResponse container = dockerClient.inspectContainerCmd(container.getId()).exec();

4.5. Snapshot di un contenitore

Simile al comando docker commit , possiamo creare una nuova immagine utilizzando il metodo commitCmd .

Nel nostro esempio, lo scenario è che in precedenza abbiamo eseguito un container alpine: 3.6 il cui id è "3464bb547f88" e abbiamo installato git su di esso.

Ora, vogliamo creare una nuova istantanea dell'immagine dal contenitore:

String snapshotId = dockerClient.commitCmd("3464bb547f88") .withAuthor("Baeldung <[email protected]>") .withEnv("SNAPSHOT_YEAR=2018") .withMessage("add git support") .withCmd("git", "version") .withRepository("alpine") .withTag("3.6.git").exec();

Poiché la nostra nuova immagine in bundle con git rimane sull'host, possiamo cercarla sull'host Docker:

$ docker image ls alpine --format "table {{.Repository}} {{.Tag}}" REPOSITORY TAG alpine 3.6.git

5. Gestione delle immagini

Ci sono alcuni comandi applicabili che ci vengono forniti per gestire le operazioni sulle immagini.

5.1. Elenca immagini

Per elencare tutte le immagini disponibili incluse le immagini pendenti sull'host Docker, dobbiamo applicare al metodo listImagesCmd :

List images = dockerClient.listImagesCmd().exec();

Se abbiamo due immagini sul nostro Docker Host, dovremmo ottenere gli oggetti Immagine di esse in fase di esecuzione . Le immagini che cerchiamo sono:

$ docker image ls --format "table {{.Repository}} {{.Tag}}" REPOSITORY TAG alpine 3.6 mongo 3.6

Accanto a questo, per vedere le immagini intermedie, dobbiamo richiederlo esplicitamente:

List images = dockerClient.listImagesCmd() .withShowAll(true).exec();

Se è solo la visualizzazione delle immagini penzolanti, deve essere considerato il metodo withDanglingFilter :

List images = dockerClient.listImagesCmd() .withDanglingFilter(true).exec();

5.2. Build an Image

Let's focus on the way of building an image using the API. The buildImageCmd method builds Docker images from a Dockerfile. In our project, we already have one Dockerfile which gives an Alpine image with git installed:

FROM alpine:3.6 RUN apk --update add git openssh && \ rm -rf /var/lib/apt/lists/* && \ rm /var/cache/apk/* ENTRYPOINT ["git"] CMD ["--help"]

The new image will be built without using cache and before starting the building process, in any case, Docker engine will attempt to pull the newer version of alpine:3.6. If everything goes well, we should eventually see the image with the given name,alpine:git:

String imageId = dockerClient.buildImageCmd() .withDockerfile(new File("path/to/Dockerfile")) .withPull(true) .withNoCache(true) .withTag("alpine:git") .exec(new BuildImageResultCallback()) .awaitImageId();

5.3. Inspect an Image

We can inspect the low-level information about an image thanks to the inspectImageCmd method:

InspectImageResponse image = dockerClient.inspectImageCmd("161714540c41").exec();

5.4. Tag an Image

Adding a tag to our image is quite simple using the dockertag command, so the API is no exception. We can carry out the same intention with the tagImageCmd method as well. To tag a Docker image with id 161714540c41 into the baeldung/alpine repository with git:

String imageId = "161714540c41"; String repository = "baeldung/alpine"; String tag = "git"; dockerClient.tagImageCmd(imageId, repository, tag).exec();

We would list the newly created image, and there it is:

$ docker image ls --format "table {{.Repository}} {{.Tag}}" REPOSITORY TAG baeldung/alpine git

5.5. Push an Image

Before sending out an image to a registry service, the docker client must be configured to cooperate with the service because working with registries need to be authenticated in advance.

Since we assume that the client was configured with Docker Hub, we can push the baeldung/alpine image to the baeldung DockerHub account:

dockerClient.pushImageCmd("baeldung/alpine") .withTag("git") .exec(new PushImageResultCallback()) .awaitCompletion(90, TimeUnit.SECONDS);

We must abide by the duration of the process. In the example, we are waiting 90 seconds.

5.6. Pull an Image

To download images from registry services, we make use of the pullImageCmd method. In addition, if the image being pulled from a private registry, the client must know our credential otherwise the process ends up with a failure. Same as the pulling an image, we specify a callback along with a fixed period to pull an image:

dockerClient.pullImageCmd("baeldung/alpine") .withTag("git") .exec(new PullImageResultCallback()) .awaitCompletion(30, TimeUnit.SECONDS);

To check out whether the mentioned image exists on the Docker host after pulling it:

$ docker images baeldung/alpine --format "table {{.Repository}} {{.Tag}}" REPOSITORY TAG baeldung/alpine git

5.7. Remove an Image

Another simple function among the rest is the removeImageCmd method. We can remove an image with its short or long ID:

dockerClient.removeImageCmd("beaccc8687ae").exec();

5.8. Search in Registry

To search an image from Docker Hub, the client comes with the searchImagesCmd method taking a String value which indicates a term. Here, we explore images related to a name containing ‘Java' in Docker Hub:

List items = dockerClient.searchImagesCmd("Java").exec();

The output returns first 25 related images in a list of SearchItem objects.

6. Volume Management

If Java projects need to interact with Docker for volumes, we should also take into account this section. Briefly, we look at the fundamental techniques of volumes provided by the Docker Java API.

6.1. List Volumes

All of the available volumes including named and unnamed are listed with:

ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec(); List volumes = volumesResponse.getVolumes();

6.2. Inspect a Volume

The inspectVolumeCmd method is the form to show the detailed information of a volume. We inspect the volume by specifying its short id:

InspectVolumeResponse volume = dockerClient.inspectVolumeCmd("0220b87330af5").exec();

6.3. Create a Volume

The API serves two different options to create a volume. The non-arg createVolumeCmd method creates a volume where the name is given by Docker:

CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();

Rather than using the default behavior, the helper method called withName lets us set a name to a volume:

CreateVolumeResponse namedVolume = dockerClient.createVolumeCmd().withName("myNamedVolume").exec();

6.4. Remove a Volume

We can intuitively delete a volume from the Docker host using the removeVolumeCmd method. What is important to note that we cannot delete a volume if it is in use from a container. We remove the volume, myNamedVolume, from the volume list:

dockerClient.removeVolumeCmd("myNamedVolume").exec();

7. Network Management

Our last section is about managing network tasks with the API.

7.1. List Networks

We can display the list of network units with one of the conventional API methods starting with list:

List networks = dockerClient.listNetworksCmd().exec();

7.2. Create a Network

The equivalent of the docker network create command is conducted with the createNetworkCmd method. If we have a thirty party or a custom network driver, the withDriver method can accept them besides the built-in drivers. In our case, let's create a bridge network whose name is baeldung:

CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd() .withName("baeldung") .withDriver("bridge").exec();

Furthermore, creating a network unit with the default settings doesn't solve the problem, we can apply for other helper methods to construct an advanced network. Thus, to override the default subnetwork with a custom value:

CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd() .withName("baeldung") .withIpam(new Ipam() .withConfig(new Config() .withSubnet("172.36.0.0/16") .withIpRange("172.36.5.0/24"))) .withDriver("bridge").exec();

The same command we can run with the docker command is:

$ docker network create \ --subnet=172.36.0.0/16 \ --ip-range=172.36.5.0/24 \ baeldung

7.3. Inspect a Network

Displaying the low-level details of a network is also covered in the API:

Network network = dockerClient.inspectNetworkCmd().withNetworkId("baeldung").exec();

7.4. Remove a Network

We can safely remove a network unit with its name or id using the removeNetworkCmd method:

dockerClient.removeNetworkCmd("baeldung").exec();

8. Conclusion

In this extensive tutorial, we explored the various diverse functionality of the Java Docker API Client, along with several implementation approaches for deployment and management scenarios.

Tutti gli esempi illustrati in questo articolo possono essere trovati su GitHub.