No post anterior eu mostrei a imagem do Docker que criei para um agente de build do VSTS, tornando trivial criar agentes de build, e escalá-los. É importante ler aquele antes de ler esse. Nesse post, vou levar esse agente de build um pouco mais adiante, mostrando como ele pode usar o host no qual ele roda para criar outras imagens, fazer o deploy de contêineres, etc, ou seja, qualquer operação que você queira realizar, usando Docker.

Entendendo ALM com contêineres

Quando trabalhamos com contêineres, temos que lembrar que todo o ciclo de vida da aplicação conteinerizada vai ser feito também dentro de um contêiner. Desde o desenvolvimento já usando contêineres, até a produção, passando pelos diversos estágios intermediários (teste, homologação, etc), a aplicação sempre é utilizada dentro de um contêiner. E está aí um dos grandes benefícios do Docker: a garantia do ambiente. Se rodar na máquina do desenvolvedor, ela vai, por definição, rodar em qualquer outro ambiente. Isso facilita tudo.

No entanto, a maneira com que pensamos nos artefatos que representam a aplicação muda completamente. Numa aplicação não conteinerizada, os artefatos resultantes de uma build são os binários, recursos (assets) diversos como imagens, arquivos JavaScript, etc. Em uma aplicação conteinerizada o artefato é somente a imagem, gerada durante o processo de build, e que já contém todos os artefatos que estariam na aplicação tradicional, mais alguns scripts para trabalhar com o Docker. A mesma imagem, após o build inicial, será a base para os contêineres que rodarão em cada um dos ambientes, até chegar em produção.

Isso garante maior confiança no processo de validação. Não há possibilidade de um deploy falhar em produção, por exemplo, se ele funcionou em homologação.

Nesse cenário, é importante que a imagem fique armazenada em um local que possa ser facilmente acessado, e o local padrão é o Docker Hub, um registry do Docker, que é gratuito para repositórios públicos. Um repositório no Docker Hub tem um nome – por exemplo, “ubuntu” – e pode armazenar várias imagens diferentes, com diferentes tags. Isso significa que, no fim do processo de build a imagem deve ser empurrada para o Docker Hub. Isso é fundamental se você tem mais de um agente de build, rodando em mais de um host de Docker, já que, nesse caso, a imagem poderia estar em outro Docker host. O Docker Hub resolve esse problema e se torna nosso repositório canônico de imagens.

O agente de build de Docker

O principal desafio de um build que vai trabalhar com imagens e contêineres Docker é que você precisa de um Docker host para criar suas imagens, ter somente o client do Docker não funciona, é o host que faz o processo de montagem da imagem. Isso significa que um Linux é necessário. O agente de build poderia ser essa máquina, isso é um cenário totalmente viável. No caso do agente de build que montei, isso fica ainda mais fácil, já que ele é, por si só, um contêiner. Precisamos apenas fazer com que ele fale com o Docker host no qual ele roda.

Fazer isso é razoavelmente simples. Basta montar o socket do host no contêiner. O arquivo que representa o socket do Docker fica em “/var/run/docker.sock”, e, se montado em um contêiner no mesmo local, qualquer processo no contêiner vai conseguir falar com o Docker host. Foi isso que fiz.

Além disso, o agente de build vai precisar conversar com o Docker Hub, e isso vai demandar que ele esteja logado.

Trabalhando com o Docker em um agente de build

O mesmo repositório giggio/vsts-agent que existe no Docker hub agora possui uma tag “docker”. Essa imagem, montada a partir da outra que discuti no post anterior, contém o docker client e o socket do Docker montados, e pode, portanto, trabalhar com imagens, contêineres, etc. Ela também vai exigir um usuário e senha para o Docker Hub.

Para testar que essa imagem consegue se conectar no Docker host, você pode rodar:

docker run -ti  --rm --volume=/var/run/docker.sock:/var/run/docker.sock giggio/vsts-agent:docker /bin/bash

E dentro do contêiner rodar:

docker ps

E verá o mesmo contêiner rodando (clique nas imagens para ampliar)

Chamando o docker de dentro de um contêiner

Ou se quiser pegar um atalho, rode diretamente:

docker run -ti  --rm --name temp --volume=/var/run/docker.sock:/var/run/docker.sock giggio/vsts-agent:docker docker ps

Note que agora coloquei o nome “temp” pro contêiner, e ele aparece no output:

Executando "docker ps" diretamente pelo contêiner

Para rodar o agente de fato, você vai precisar configurar as variáveis de ambiente para login no Docker Hub. São duas: “DOCKER_USERNAME” e “DOCKER_PASSWORD”, assim como foi feito com as outras variáveis no post anterior. Rode no Windows:

docker run --name vsts-agent -d -ti -e VS_TENANT=$env:VS_TENANT -e AGENT_PAT=$env:AGENT_PAT -e DOCKER_USERNAME=$env:DOCKER_USERNAME -e DOCKER_PASSWORD=$env:DOCKER_PASSWORD --restart=always --volume=/var/run/docker.sock:/var/run/docker.sock giggio/vsts-agent:docker

No bash, rode:

docker run --name vsts-agent -d -ti -e VS_TENANT=$VS_TENANT -e AGENT_PAT=$AGENT_PAT -e DOCKER_USERNAME=$DOCKER_USERNAME -e DOCKER_PASSWORD=$DOCKER_PASSWORD --restart=always --volume=/var/run/docker.sock:/var/run/docker.sock giggio/vsts-agent:docker

A saída deve ser assim:

Resultado do comando docker-run

Ocultei o nome do usuário do Docker (bloco em azul).

Note o login no Docker Hub com sucesso (primeira seta), o agente adicionado no VSTS (segunda seta), e o agente no ar, esperando conexões (terceira seta).

O agente de build será registrado. Os números de versão de cada componente do Docker, e do .NET CLI, ficarão visíveis na tela de configuração do agente (no VSTS, navegue para Configurações – a engrenagem no canto superior direito, e então vá para a aba Agent pools):

Agente de build registrado

Subindo e escalando o agente com docker-compose

Você também pode rodar via docker-compose, como mostrei no post anterior. Use um arquivo docker-compose.yml assim:

version: '2'
services:
  agent:
    image: giggio/vsts-agent:docker
    environment:
      AGENT_PAT:
      AGENT_POOL:
      VS_TENANT:
      DOCKER_USERNAME:
      DOCKER_PASSWORD:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    stdin_open: true
    tty: true
restart: always

Para rodar, basta executar:

docker-compose up –d

Para escalar, basta usar as mesmas técnicas do post anterior

Concluindo

Agora, além de um agente de build rodando num contêiner, temos ele também executando comandos Docker.

No próximo post vou dar um exemplo de como seria montada a imagem e depois feito o release, usando alguns scripts bash.