«1. Обзор

В этой статье мы рассмотрим еще один хорошо зарекомендовавший себя API для конкретных платформ — Java API Client for Docker.

На протяжении всей статьи мы понимаем, как подключиться к работающему демону Docker и какие важные функции API предлагает разработчикам Java.

2. Зависимость Maven

Во-первых, нам нужно добавить основную зависимость в наш файл pom.xml:

<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java</artifactId>
    <version>3.0.14</version>
</dependency>

На момент написания статьи последняя версия API — 3.0.14. . Каждый выпуск можно просмотреть либо на странице выпуска GitHub, либо в репозитории Maven.

3. Использование клиента Docker

DockerClient — это место, где мы можем установить соединение между движком/демоном Docker и нашим приложением.

По умолчанию демон Docker доступен только в файле unix:///var/run/docker.sock. Мы можем локально взаимодействовать с механизмом Docker, прослушивающим сокет Unix, если не настроено иное.

Здесь мы обращаемся к классу DockerClientBuilder для создания соединения, принимая настройки по умолчанию:

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

Точно так же мы можем открыть соединение в два этапа: характеристики, клиент также настраивается с различными условиями.

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

Например, сборщик принимает URL-адрес сервера, то есть мы можем обновить значение подключения, если движок доступен на порту 2375:

Обратите внимание, что нам нужно добавить unix к строке подключения:/ / или tcp:// в зависимости от типа подключения.

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

Если мы сделаем еще один шаг вперед, то сможем получить более продвинутую конфигурацию, используя класс 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();

Другой вариант если мы не настроим параметры движка в исходном коде, необходимо установить соответствующие переменные среды, чтобы мы могли рассматривать только экземпляр DockerClient по умолчанию в проекте:

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

4. Управление контейнерами

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

API позволяет нам вариантов управления контейнерами. Давайте посмотрим на каждый из них.

4.1. Список контейнеров


Теперь, когда у нас есть установленное соединение, мы можем вывести список всех запущенных контейнеров, расположенных на хосте Docker:

При условии, что отображение запущенных контейнеров не требует необходимости, мы можем использовать предлагаемые варианты запроса контейнеров.

List<Container> containers = dockerClient.listContainersCmd().exec();

В этом случае мы отображаем контейнеры со статусом «выход»:

Это эквивалент:

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

4.2. Создание контейнера

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

Создание контейнера выполняется с помощью метода createContainerCmd. Мы можем объявить более сложное объявление, используя доступные методы, начиная с префикса «with».

Давайте предположим, что у нас есть команда docker create, определяющая зависимый от хоста контейнер MongoDB, прослушивающий внутренний порт 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

~ ~~ 4.3. Запуск, остановка и уничтожение контейнера

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.4. Проверка контейнера

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

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

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

Метод inspectContainerCmd принимает строковый аргумент, который указывает имя или идентификатор контейнера. Используя этот метод, мы можем напрямую наблюдать за метаданными контейнера:

4.5. Снимок контейнера

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

Подобно команде фиксации docker, мы можем создать новый образ, используя метод commitCmd.

В нашем примере мы запускаем контейнер alpine:3.6 с идентификатором «3464bb547f88» и устанавливаем поверх него git.

Теперь мы хотим создать новый снимок образа из контейнера:

Поскольку наш новый образ, связанный с git, остается на хосте, мы можем найти его на хосте Docker:

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

5. Управление изображениями

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

Есть несколько применимых команд, которые нам даются для управления операциями с изображениями.

5.1. Список изображений

Чтобы вывести список всех доступных образов, включая висячие изображения на хосте Docker, нам нужно применить метод listImagesCmd:

«

List<Image> images = dockerClient.listImagesCmd().exec();

«Если у нас есть два образа на нашем хосте Docker, мы должны получить их объекты Image во время выполнения. Мы ищем следующие изображения:


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

Далее, чтобы увидеть промежуточные изображения, нам нужно запросить их явно:

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

учитывать:

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

5.2. Создание образа

Давайте сосредоточимся на способе создания образа с помощью API. Метод buildImageCmd создает образы Docker из Dockerfile. В нашем проекте у нас уже есть один Dockerfile, который дает образ Alpine с установленным git: более новая версия альпийского: 3.6. Если все пойдет хорошо, в итоге мы должны увидеть образ с заданным именем, alpine:git:

FROM alpine:3.6

RUN apk --update add git openssh && \
  rm -rf /var/lib/apt/lists/* && \
  rm /var/cache/apk/*

ENTRYPOINT ["git"]
CMD ["--help"]

5.3. Проверка изображения

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

Мы можем проверить низкоуровневую информацию об изображении благодаря методу inspectImageCmd:

5.4. Пометить изображение

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

Добавить тег к нашему изображению довольно просто с помощью команды docker tag, поэтому API не является исключением. Мы можем реализовать то же намерение с помощью метода tagImageCmd. Чтобы пометить образ Docker с идентификатором 161714540c41 в репозиторий baeldung/alpine с помощью git:

Мы перечислили бы только что созданный образ, и вот он:

String imageId = "161714540c41";
String repository = "baeldung/alpine";
String tag = "git";

dockerClient.tagImageCmd(imageId, repository, tag).exec();

5.5. Отправка образа

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

Перед отправкой образа в службу реестра клиент Docker должен быть настроен для взаимодействия со службой, поскольку для работы с реестрами необходимо заранее пройти аутентификацию.

Поскольку мы предполагаем, что клиент был настроен с помощью Docker Hub, мы можем отправить образ baeldung/alpine в учетную запись baeldung DockerHub:

Мы должны соблюдать продолжительность процесса. В примере мы ждем 90 секунд.

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

5.6. Извлечь образ

Чтобы загрузить образы из служб реестра, мы используем метод pullImageCmd. Кроме того, если образ извлекается из частного реестра, клиент должен знать наши учетные данные, иначе процесс завершится сбоем. Как и при извлечении образа, мы указываем обратный вызов вместе с фиксированным периодом для извлечения образа:

Чтобы проверить, существует ли упомянутый образ на хосте Docker после его извлечения:

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

5.7 . Удалить изображение

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

Еще одна простая функция среди остальных — это метод removeImageCmd. Мы можем удалить изображение с его коротким или длинным идентификатором:

5.8. Поиск в реестре

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

Для поиска изображения в Docker Hub клиент использует метод searchImagesCmd, принимающий строковое значение, указывающее термин. Здесь мы исследуем изображения, связанные с именем, содержащим «Java», в Docker Hub:

Выходные данные возвращают первые 25 связанных изображений в списке объектов SearchItem.

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

6. Управление томами

Если Java-проектам необходимо взаимодействовать с Docker для томов, мы также должны принять во внимание этот раздел. Вкратце мы рассмотрим основные методы работы с томами, предоставляемые Docker Java API.

6.1. Список томов

Все доступные тома, включая именованные и безымянные, перечислены с:

6.2. Проверка тома

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

Метод inspectVolumeCmd — это форма для отображения подробной информации о томе. Инспектируем том, указав его короткий id:

6.3. Создание тома

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

API предлагает два разных варианта создания тома. Метод createVolumeCmd без аргументов создает том, имя которого задается Docker:

Вместо того, чтобы использовать поведение по умолчанию, вспомогательный метод withName позволяет нам задать имя для тома:

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

6.4. Удаление тома

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

Мы можем интуитивно удалить том с хоста Docker, используя метод removeVolumeCmd. Важно отметить, что мы не можем удалить том, если он используется из контейнера. Мы удаляем том myNamedVolume из списка томов:

7. Управление сетью

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

Наш последний раздел посвящен управлению сетевыми задачами с помощью API.

7.1. Список сетей

«Мы можем отобразить список сетевых устройств одним из обычных методов API, начиная со списка:

7.2. Создать сеть

List<Network> networks = dockerClient.listNetworksCmd().exec();

Эквивалент команды создания сети docker выполняется с помощью метода createNetworkCmd. Если у нас есть сторонний или собственный сетевой драйвер, метод withDriver может принять их помимо встроенных драйверов. В нашем случае давайте создадим мостовую сеть с именем baeldung:

Кроме того, создание сетевого узла с настройками по умолчанию не решает проблему, мы можем применить другие вспомогательные методы для построения расширенной сети. Таким образом, чтобы переопределить подсеть по умолчанию с помощью пользовательского значения:

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

Та же самая команда, которую мы можем запустить с помощью команды docker:

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

7.3. Проверка сети

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

Отображение низкоуровневых сведений о сети также рассматривается в API:

7.4. Удаление сети

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

Мы можем безопасно удалить сетевой блок с его именем или идентификатором, используя метод removeNetworkCmd:

8. Заключение

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

В этом подробном руководстве мы рассмотрели различные разнообразные функции Java Docker. Клиент API, а также несколько подходов к реализации сценариев развертывания и управления.

Все примеры, проиллюстрированные в этой статье, можно найти на GitHub.

«