Olá pessoal, tudo bem?

Continuando o assunto “Docker”, irei demonstrar neste post como fazer o deploy de uma aplicação, detalhando a estrutura do Dockerfile e das imagens do Docker.

Aplicação do exemplo

Para este exemplo, utilizarei uma aplicação bem simples em NodeJS. Será um servidor que ficará ouvindo a porta 3000, e irá responder “Hello World!” para qualquer requisição.

Instalando o Docker

O primeiro passo, é instalar o Docker no host. Eu instalei no Ubuntu 15.10, porém é possível instalar em outras distribuições do Linux também.

Para instalar no Windows ou no OS X, é necessário instalar o Docker Toolbox. Nele está incluso o Docker-Machine, que cria uma micro VM Linux para suportar o Docker.

No próprio site do Docker, existe uma excelente documentação sobre este procedimento.

Dockerfile

O Dockerfile é o arquivo que deverá ser criado para podermos gerar a nossa imagem. Ele não possui extensão,  no nome deve ter a letra “D” maiúscula, e as demais letras não possuem restrições. Neste exemplo, o arquivo é semelhante ao apresentado no post anterior, salvo a versão da imagem do NodeJS que utilizei.

Dockerfile2

Para criar uma nova imagem do Docker, é necessário indicar quem será a imagem base para aquela que será criada utilizando a instrução FROM (linha 1). Esta imagem será obtida do repositório padrão do Docker, o DockerHub. Para este exemplo, utilizei uma imagem do nodejs 6.1.

A instrução RUN é utilizada para executar comandos que serão aplicados à uma camada da imagem (mais detalhes abaixo). Na linha 3, utilizo ele para executar o comando mkdir e assim criar um novo diretório onde será depositada a aplicação.

Em seguida, na linha 4, utilizo a instrução WORKDIR para alterar o diretório padrão de outras instruções que virão a seguir no Dockerfile, como o RUN, o CMD e o COPY.

Na linha 6, eu copio o arquivo package.json para dentro da imagem utilizando a instrução COPY, e em seguida, na linha 7, eu executo novamente a instrução RUN para instalar as dependências do nodejs.

Após a instalação das dependências, executo novamente a instrução COPY para copiar o restante da aplicação (linha 9) para dentro da imagem (em seguida explicarei o porquê da separação entre a instalação das dependências e a cópia da aplicação).

Na linha 11, eu executo a instrução EXPOSE para informar a engine do Docker que utilizarei a porta 3000. Essa instrução não torna esta porta acessível ao host, ela estará disponível apenas dentro do ambiente do Docker.

Por último, executo a instrução CMD (linha 13) para indicar que, quando o container for iniciado, será executado o comando “npm start”, que será o primeiro processo dentro do container.

Construindo a imagem

Agora podemos construir a imagem executando o comando:

“docker build -t myimage .”,

onde “-t” é o nome da imagem e “.”  é o diretório atual (onde está o Dockerfile).

Alguns de vocês devem estar se perguntando, “Por que não copiar a aplicação inteira para dentro da imagem em apenas uma instrução?”, eu poderia ter escrito o Dockerfile da seguinte forma:

Docker

Então, uma das características de uma imagem que justifica esta abordagem, é que a imagem não é formada por um arquivo, e sim vários arquivos. Cada um deles, representa uma camada dentro da imagem, e cada camada é adicionada a cada instrução executada dentro do Dockerfile.

Em resumo, cada instrução executa os seguintes passos:

  • Obtém a imagem gerada através da instrução anterior;
  • Cria um container;
  • Executa o instrução dentro do container;
  • Salva o estado do container em uma nova camada da imagem.

Atualizamos dependências com muito menos frequência do que os fontes da aplicação em si, então é uma boa prática separarmos estas duas operações em camadas, além disso garantimos um pouco mais de performance no build, já que podemos usufruir do mecanismo de cache de camadas das imagens do Docker (ele utiliza o cache se a camada não for alterada).

TEste

Criando um container

Por fim, podemos criar um container utilizando o seguinte comando:

“docker run -d -p 8080:3000 –name testcontainer myimage

Basicamente, o que estou dizendo é para:

  • Executar em background/detached (-d);
  • Redirecionar a porta 8080 do host para a 3000 exposta no Docker (-p). Sem este argumento não é possível acessar a aplicação através do Host;
  • Dar o nome “testcontainer” para o container (–name);
  • E utilizar a imagem “myimage”.

Para verificar se o container está executando basta utilizar o comando “docker ps”, e acessar “http://localhost:8080” para ver a aplicação em execução:

DOckerps

Podem ser criados quantos containers forem necessários através da mesma imagem.

Por fim…

Era isso pessoal!

Uma das coisas que acho excelente no Docker é que consigo levar uma imagem para diversos ambientes diferentes sem maiores problemas, pois a aplicação está totalmente encapsulada dentro desta imagem, e os containers gerados a partir dela serão idênticos em qualquer lugar.

Estou gostando muito de aprender Docker, e espero que desperte interesse em vocês também, pois com certeza o futuro de containers será muito promissor.

O projeto exemplo deste post esta disponível no GitHub.

Um grande abraço!