Una guía de Docker para Java

1. Información general

En este artículo, echamos un vistazo a otra API específica de plataforma bien establecida: Java API Client para Docker .

A lo largo del artículo, comprendemos la forma de cómo conectarse con un demonio Docker en ejecución y qué tipo de funcionalidad importante ofrece la API a los desarrolladores de Java.

2. Dependencia de Maven

Primero, necesitamos agregar la dependencia principal en nuestro archivo pom.xml :

 com.github.docker-java docker-java 3.0.14 

En el momento de redactar el artículo, la última versión de la API es 3.0.14 . Cada lanzamiento se puede ver desde la página de lanzamiento de GitHub o desde el repositorio de Maven.

3. Uso del cliente Docker

DockerClient es donde podemos establecer una conexión entre un motor / demonio Docker y nuestra aplicación.

De forma predeterminada, solo se puede acceder al demonio de Docker en el archivo unix: ///var/run/docker.sock . Podemos comunicarnos localmente con el motor Docker que escucha en el socket Unix a menos que se configure lo contrario .

Aquí, aplicamos a la clase DockerClientBuilder para crear una conexión aceptando la configuración predeterminada:

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

Del mismo modo, podemos abrir una conexión en dos pasos:

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

Dado que los motores pueden depender de otras características, el cliente también es configurable con diferentes condiciones.

Por ejemplo, el constructor acepta una URL de servidor, es decir, podemos actualizar el valor de conexión si el motor está disponible en el puerto 2375 :

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

Tenga en cuenta que debemos anteponer la cadena de conexión con unix: // o tcp: // según el tipo de conexión.

Si damos un paso más, podemos terminar con una configuración más avanzada usando la clase 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();

Asimismo, podemos realizar el mismo planteamiento utilizando Propiedades :

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();

Otra opción, a menos que configuremos la configuración del motor en el código fuente, es establecer las variables de entorno correspondientes para que solo podamos considerar la instanciación predeterminada de DockerClient en el proyecto:

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. Gestión de contenedores

La API nos permite una variedad de opciones sobre la gestión de contenedores. Veamos cada uno de ellos.

4.1. Lista de contenedores

Ahora que tenemos una conexión establecida, podemos enumerar todos los contenedores en ejecución ubicados en el host de Docker:

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

Siempre que mostrar los contenedores en ejecución no satisfaga la necesidad, podemos hacer uso de las opciones ofrecidas para consultar los contenedores.

En este caso, mostramos contenedores con el estado "salido":

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

Es un equivalente de:

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

4.2. Crear un contenedor

La creación de un contenedor se sirve con el método createContainerCmd . Podemos declarar declaraciones más complejas usando los métodos disponibles comenzando con el prefijo " con" .

Supongamos que tenemos un comando docker create que define un contenedor MongoDB dependiente del host que escucha internamente en el puerto 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

Podemos arrancar el mismo contenedor junto con sus configuraciones de manera programática:

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. Iniciar, detener y matar un contenedor

Una vez que creamos el contenedor, podemos iniciarlo, detenerlo y matarlo por nombre o id respectivamente:

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

4.4. Inspeccionar un contenedor

El método inspectContainerCmd toma un argumento String que indica el nombre o id de un contenedor. Usando este método, podemos observar los metadatos de un contenedor directamente:

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

4.5. Instantánea de un contenedor

De forma similar al comando docker commit , podemos crear una nueva imagen usando el método commitCmd .

En nuestro ejemplo, el escenario es , previamente ejecutamos un contenedor alpine: 3.6 cuya identificación es "3464bb547f88" e instalamos git encima.

Ahora, queremos crear una nueva instantánea de imagen desde el contenedor:

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();

Dado que nuestra nueva imagen incluida con git permanece en el host, podemos buscarla en el host de Docker:

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

5. Gestión de imágenes

Hay algunos comandos aplicables que se nos dan para administrar las operaciones de imágenes.

5.1. Lista de imágenes

Para enumerar todas las imágenes disponibles, incluidas las imágenes colgantes en el host de Docker, debemos aplicar al método listImagesCmd :

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

Si tenemos dos imágenes en nuestro Docker Host, deberíamos obtener los objetos Image de ellas en tiempo de ejecución . Las imágenes que buscamos son:

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

Junto a esto, para ver las imágenes intermedias, necesitamos solicitarlo explícitamente:

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

Si solo se muestran las imágenes colgantes, se debe considerar el método 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.

Todos los ejemplos ilustrados en este artículo se pueden encontrar en GitHub.