Um Guia do Docker para Java

Um Guia do Docker para Java

1. Visão geral

Neste artigo, damos uma olhada em outra API específica de plataforma bem estabelecida -Java API Client for Docker.

Ao longo do artigo, compreendemos como se conectar com um daemon Docker em execução e que tipo de funcionalidade importante a API oferece aos desenvolvedores Java.

2. Dependência do Maven

Primeiro, precisamos adicionar a dependência principal em nosso arquivopom.xml:


    com.github.docker-java
    docker-java
    3.0.14

No momento da redação do artigo,the latest version of the API is 3.0.14. Cada liberação pode ser visualizada emthe GitHub release page ou emthe Maven repository.

3. Usando o Docker Client

DockerClient é onde podemos estabelecer uma conexão entre um mecanismo / daemon Docker e nosso aplicativo.

Por padrão, o daemon Docker só pode ser acessado no arquivounix:///var/run/docker.sock. Podemos nos comunicar localmente comthe Docker engine listening on the Unix socket unless otherwise configured.

Aqui, aplicamos à classeDockerClientBuilder para criar uma conexão aceitando as configurações padrão:

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

Da mesma forma, podemos abrir uma conexão em duas etapas:

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

Como os motores podem confiar em outras características, o cliente também é configurável com diferentes condições.

Por exemplo, o construtor aceita um URL de servidor, ou seja,we can update the connection value if the engine is available on port 2375:

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

Observe que precisamos precederthe connection string with unix:// or tcp:// depending on the connection type.

Se formos um passo adiante, podemos acabar com uma configuração mais avançada usando a classeDefaultDockerClientConfig:

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

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

Da mesma forma, podemos realizar a mesma abordagem usandoProperties:

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

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

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

Outra opção, a menos que definamos as configurações do mecanismo no código-fonte, é definir as variáveis ​​de ambiente correspondentes para que possamos considerar apenas a instanciação padrão deDockerClient no projeto:

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

4. Gerenciamento de contêineres

A API nos permite uma variedade de opções sobre gerenciamento de contêineres. Vamos dar uma olhada em cada um deles.

4.1. Recipientes de lista

Agora que temos uma conexão estabelecida, podemos listar todos os contêineres em execução localizados no host do Docker:

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

Desde que mostrar os contêineres em execução não atraia a necessidade, podemos usar as opções oferecidas para consultar os contêineres.

Nesse caso, exibimos contêineres com o status "encerrado":

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

É o equivalente a:

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

4.2. Criar um contêiner

A criação de um contêiner é realizada com o métodocreateContainerCmd. Podemos declarar uma declaração mais complexausingthe available methods starting with the “with” prefix.

Vamos supor que temos um comandodockercreate definindo um contêiner MongoDB dependente do host escutando internamente na porta 27017:

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

Podemos inicializar o mesmo contêiner junto com suas configurações de maneira programática:

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

4.3. Iniciar, parar e matar um contêiner

Depois de criar o contêiner, podemos iniciar, parar e matá-lo por nome ou ID, respectivamente:

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

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

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

4.4. Inspecionar um contêiner

O métodoinspectContainerCmd leva um argumentoString que indica o nome ou id de um contêiner. Usando este método, podemos observar os metadados de um contêiner diretamente:

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

4.5. Instantâneo de um contêiner

Semelhante ao comandodocker commit, podemos criar uma nova imagem usando o métodocommitCmd.

Em nosso exemplo, o cenário é, we previously run an alpine:3.6 container whose id is “3464bb547f88” and installed git on top of it.

Agora, queremos criar um novo instantâneo de imagem a partir do contêiner:

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

Como nossa nova imagem agrupada comgit permanece no host, podemos pesquisá-la no host Docker:

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

5. Gestão de Imagens

Existem alguns comandos aplicáveis ​​que são dados para gerenciar operações de imagem.

5.1. Listar imagens

Para listar todas as imagens disponíveis, incluindo imagens pendentes no host Docker, precisamos aplicar ao métodolistImagesCmd:

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

Se tivermos duas imagens em nosso Docker Host,we should obtain the Image objects of them at run-time. As imagens que procuramos são:

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

Além disso, para ver as imagens intermediárias, precisamos solicitá-lo explicitamente:

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

Se apenas exibir as imagens pendentes for o caso, o métodowithDanglingFilter deve ser considerado:

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

5.2. Construir uma imagem

Vamos nos concentrar na maneira de construir uma imagem usando a API. The buildImageCmd method builds Docker images from a Dockerfile. Em nosso projeto, já temos um Dockerfile que fornece uma imagem Alpine com o git instalado:

FROM alpine:3.6

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

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

A nova imagem será construída sem usar cache e antes de iniciar o processo de construção, em qualquer caso, o mecanismo Docker tentará extrair a versão mais recente dealpine: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. Inspecione uma imagem

Podemos inspecionar as informações de baixo nível sobre uma imagem graças ao métodoinspectImageCmd:

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

5.4. Marque uma imagem

Adicionar uma tag à nossa imagem é bastante simples usando o comandodockertag, então a API não é exceção. Podemos realizar a mesma intenção com o métodotagImageCmd também. To tag a Docker image with id 161714540c41 into the example/alpine repository with git:

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

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

Listaríamos a imagem recém-criada e aqui está:

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

5.5. Empurre uma imagem

Antes de enviar uma imagem para um serviço de registro, o cliente docker deve ser configurado para cooperar com o serviço, pois o trabalho com registros precisa ser autenticado com antecedência.

Como supomos que o cliente foi configurado com Docker Hub, podemos enviar a imagemexample/alpine para a conta DockerHub de exemplo:

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

We must abide by the duration of the process. No exemplo,we are waiting 90 seconds.

5.6. Puxe uma imagem

Para baixar imagens de serviços de registro, usamos o métodopullImageCmd. Além disso, se a imagem estiver sendo retirada de um registro particular, o cliente deverá conhecer nossa credencial, caso contrário, o processo terminará com uma falha. Same as the pulling an image, we specify a callback along with a fixed period to pull an image:

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

Para verificar se a imagem mencionada existe no host do Docker depois de removê-la:

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

5.7. Remover uma imagem

Outra função simples entre as demais é o métodoremoveImageCmd. Podemos remover uma imagem com seu ID curto ou longo:

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

5.8. Pesquisa no Registro

Para pesquisar uma imagem do Docker Hub, o cliente vem com o métodosearchImagesCmd obtendo um valor String que indica um termo. Aqui, exploramos imagens relacionadas a um nome contendo ‘Java' no Docker Hub:

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

A saída retorna as primeiras 25 imagens relacionadas em uma lista de objetosSearchItem.

6. Gestão de Volume

Se os projetos Java precisarem interagir com o Docker para volumes, também devemos levar em consideração esta seção. Briefly, we look at the fundamental techniques of volumes provided by the Docker Java API.

6.1. Listar Volumes

Todos os volumes disponíveis, incluindo nomeados e não nomeados, são listados com:

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

6.2. Inspecione um Volume

O métodoinspectVolumeCmd é a forma de mostrar as informações detalhadas de um volume. Inspecionamos o volume especificando seu ID curto:

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

6.3. Crie um Volume

A API serve duas opções diferentes para criar um volume. O método não argcreateVolumeCmd cria um volume em que o nome é fornecido pelo Docker:

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

Em vez de usar o comportamento padrão, o método auxiliar chamadowithName nos permite definir um nome para um volume:

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

6.4. Remover um Volume

Podemos excluir intuitivamente um volume do host Docker usando o métodoremoveVolumeCmd. What is important to note that we cannot delete a volume if it is in use from a container. Removemos o volumemyNamedVolume da lista de volumes:

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

7. Gerenciamento de rede

Nossa última seção é sobre como gerenciar tarefas de rede com a API.

7.1. Listar redes

Podemos exibir a lista de unidades de rede com um dos métodos convencionais de API começando comlist:

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

7.2. Crie uma rede

O equivalente do comandodocker network create é conduzido com o métodocreateNetworkCmd. Se tivermos um driver de rede de terceiros ou personalizado, o métodowithDriver pode aceitá-los além dos drivers integrados. Em nosso caso, vamos criar uma rede de ponte cujo nome éexample:

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

Além disso, criar uma unidade de rede com as configurações padrão não resolve o problema, podemos aplicar outros métodos auxiliares para construir uma rede avançada. Assim,to override the default subnetwork with a custom value:

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

O mesmo comando que podemos executar com o comandodocker é:

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

7.3. Inspecione uma rede

A exibição dos detalhes de baixo nível de uma rede também é abordada na API:

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

7.4. Remover uma rede

Podemos remover com segurança uma unidade de rede com seu nome ou id usando o métodoremoveNetworkCmd:

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

8. Conclusão

Neste tutorial extenso, exploramos as várias funcionalidades diversas doJava Docker API Client, junto com várias abordagens de implementação para cenários de implantação e gerenciamento.

Todos os exemplos ilustrados neste artigo podem ser encontradosover on GitHub.