Перейти к основному содержимому

Руководство по Docker для Java

· 9 мин. чтения

Задача: Сумма двух

Дано массив целых чисел и целая сумма. Нужно найти индексы двух чисел, сумма которых равна заданной ...

ANDROMEDA

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 :

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

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

Если мы сделаем еще один шаг, то сможем получить более продвинутую конфигурацию с использованием класса DefaultDockerClientConfig :

DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withRegistryEmail("info@foreach.com")
.withRegistryPassword("foreach")
.withRegistryUsername("foreach")
.withDockerCertPath("/home/foreach/.docker/certs")
.withDockerConfig("/home/foreach/.docker/")
.withDockerTlsVerify("1")
.withDockerHost("tcp://docker.foreach.com:2376").build();

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

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

Properties properties = new Properties();
properties.setProperty("registry.email", "info@foreach.com");
properties.setProperty("registry.password", "foreach");
properties.setProperty("registry.username", "baaldung");
properties.setProperty("DOCKER_CERT_PATH", "/home/foreach/.docker/certs");
properties.setProperty("DOCKER_CONFIG", "/home/foreach/.docker/");
properties.setProperty("DOCKER_TLS_VERIFY", "1");
properties.setProperty("DOCKER_HOST", "tcp://docker.foreach.com:2376");

DefaultDockerClientConfig config
= DefaultDockerClientConfig.createDefaultConfigBuilder()
.withProperties(properties).build();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Предположим, что у нас есть команда создания докера , определяющая зависящий от хоста контейнер MongoDB, прослушивающий внутренний порт 27017: ``

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

Мы можем программно загрузить тот же контейнер вместе с его конфигурациями:

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

4.3. Запустить, остановить и убить контейнер

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

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

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

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

4.4. Осмотрите контейнер

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

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

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

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

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

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

String snapshotId = dockerClient.commitCmd("3464bb547f88")
.withAuthor("ForEach <info@foreach.com>")
.withEnv("SNAPSHOT_YEAR=2018")
.withMessage("add git support")
.withCmd("git", "version")
.withRepository("alpine")
.withTag("3.6.git").exec();

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

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

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

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

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

Если имеет место только отображение висячих изображений, необходимо рассмотреть метод withDanglingFilter :

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

5.2. Создайте образ

Давайте сосредоточимся на способе создания изображения с помощью API. Метод buildImageCmd создает образы Docker из Dockerfile . В нашем проекте у нас уже есть один Dockerfile, который дает образ 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"]

Новый образ будет собран без использования кеша, и перед началом процесса сборки в любом случае движок Docker попытается загрузить более новую версию alpine:3.6 . Если все пойдет хорошо, мы должны в итоге увидеть образ с заданным именем, 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. Осмотрите изображение

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

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

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

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

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

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

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

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

5.5. Нажмите изображение

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

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

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

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

5.6. Вытяните изображение

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

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

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

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

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

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

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

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

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

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

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

6. Управление громкостью

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

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

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

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

6.2. Осмотр тома

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

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

6.3. Создать том

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

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

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

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

6.4. Удалить том

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7.3. Осмотрите сеть

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

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

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

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

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

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

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

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