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 .