Tutorial de Containers Docker

Objetivo deste tutorial: Conhecer na prática os comandos e as tarefas básicas do Docker, ilustrando os principais conceitos relacionados a containers de aplicação. São fornecidos links para tutoriais e documentação mais avançada para aprofundamento. Para entender mais sobre a teoria de máquinas virtuais e containers clique aqui.

Os containers Docker (www.docker.com) podem ser executados no Linux, no MacOS e mesmo no Windows. Em arquiteturas x86, ou ARM. Seja em um sistema bare metal, seja em um sistema virtualizado. Em tempo, optei por manter o termo container no original em inglês, ao invés de contêiner.

Para as instruções de instalação do Docker no seu sistema acesse https://docs.docker.com/engine/install/. Usuários de Windows e Mac podem usar a versão Desktop. Usuários da distribuição Linux Ubuntu podem instalar facilmente utilizando o comando sudo snap install docker. No Linux a maior parte dos comandos requer permissão de superusuário, portanto será necessário utilizar sudo antes dos comandos se estiver no Linux! Seu esquecimento resultará em uma mensagem de permissão negada. Basta executar o comando novamente precedido pelo sudo.

Também é possível executar este tutorial online no Play With Docker (https://labs.play-with-docker.com/). É um ambiente de testes completo fornecido pela Docker, requerendo apenas um navegador e o cadastro no site.

Familiarização com containers

Para verificar sua instalação do Docker execute:

docker --version

E para ter certeza que tudo funciona corretamente vamos executar (run) nosso primeiro container:

docker run hello-world

Aproveite para verificar as interessantes informações e links fornecidos. Um deles sugere que você tente "algo mais ambicioso" com o comando:

docker run -it ubuntu bash

Você ingressará em um terminal como root, dentro do container Ubuntu recém criado. Mas há alguns detalhes adicionais. As opções -i e -t (-it), na prática, permitem que você interaja em um terminal. Foi solicitada a execução de um container ubuntu, mais especificamente de seu interpretador de comandos, o bash. Teste alguns comandos dentro deste container! Você notará que há algumas limitações. Agora vamos a mais alguns testes. Enquanto está utilizando o container do Ubuntu abra outro terminal no seu sistema host e execute o comando docker ps. Observe as informações fornecidas.

Agora saia do container normalmente usando o comando exit e execute o comando docker ps novamente. Notou a diferença?

É possível executar um único processo dentro desse container do linux Ubuntu substituindo o "bash" diretamente pelo comando específico, como no exemplo:

docker run -it ubuntu ls

Note que o container é criado, o processo é executado e o container é encerrado. Observe também como depois da primeira execução em que foi necessário obter a imagem do container, o que demorou um pouco mais, a execução do container agora é praticamente instantânea. O container pode existir apenas durante a execução do processo desejado. Ele é efêmero! Para verificar ainda melhor a efemeridade dos containers execute um container com um processo que não se encerra sozinho, como o top:

docker run -it ubuntu top

Agora, em outro terminal verifique com docker ps, encerre o processo do top (tecla "q") e execute de novo o docker ps. Agora teste docker ps -a e veja o histórico completo.

Já que estamos lidando com comandos ps e top. No docker a opção ps lista os containers, mas é possível verificar os processos em execução dentro de um container. com a opção top do docker. Execute novamente docker run -it ubuntu top e em outro terminal docker ps e depois docker top nome_ou_ID_do_container. Se necessário pode acrescentar parâmetros normais do comando "ps" do Linux (aux, alx, etc.).

Também é possível interromper e iniciar containers com as opções stop e start, bem como removê-lo com a opção rm. Tente docker stop nome_ou_ID_do_container, docker start nome_ou_ID_do_container e docker rm nome_ou_ID_do_container (em um container já parado, ou adicione a opção -f). Apenas digitar os caracteres iniciais do ID do container é suficiente. Para remover automaticamente um container após a execução pode-se usar a opção --rm no comando docker run.

Teste executar um container, interrompê-lo, reiniciá-lo, removê-lo de maneira forçada, etc. De fato, remova todas os containers criados até aqui neste tutorial antes de prosseguir.

A propósito, a qualquer momento em que precise de ajuda, ou simplesmente recordar as opções disponíveis, é possível usar a opção --help para um comando específico do docker, como docker run --help. Para a ajuda geral basta usar docker --help, ou simplesmente docker sem nenhuma opção.

Para ver as imagens de containers disponíveis em seu sistema use docker image ls, ou simplesmente docker images.

Nas opções anteriores, quando solicitamos a execução de um container cuja imagem não estava disponível, ela foi baixada automaticamente e a imagem salva no disco local. É a partir da imagem que o container é criado e executado, como se fosse uma instância da imagem. Pense como uma espécie de imagem de disco para a instalação de um sistema operacional em uma máquina bare metal, ou virtual, mas já configurada e pronta para execução, no caso dos containers.

Ao listar as imagens é possível notar que, embora os containers criados anteriormente tenham sido removidos, as imagens utilizadas para criá-los permanecem no sistema.

O Docker Hub (hub.docker.com) é a plataforma oficial de imagens Docker, ou registro de imagens. Seu uso é gratuito. Para publicar imagens é necessário criar uma conta e efetuar o login. Para baixar imagens públicas nem isso é necessário. Para apenas baixar uma imagem do Docker Hub sem criar o container use docker pull nome_da_imagem. P.ex.: docker pull centos. Para remover uma imagem de container use docker image rm nome_da_imagem ou docker rmi nome_da_imagem.

Teste outros containers prontos, disponíveis no Docker Hub, para perceber as inúmeras possibilidades de executar, testar, desenvolver e distribuir software usando containers. Algumas sugestões...

Para testar uma distribuição diferente use docker run -it centos, para aproveitar a imagem recém baixada, ou uma das diversas outras disponíveis no Docker Hub. Pode ser uma boa ideia atribuir um nome ao seu container usando a opção --name, por exemplo docker run --name meu_centos -it centos.

Para tentar um servidor web, como o NGINX use:

docker run -p 8080:80 -d nginx

Neste último comando a flag -d corresponde a "detach", o que faz com que o container seja executado em background (segundo plano). A flag -p mapeia uma porta exposta no container para o host. No caso a porta 80 do container (serviço web, protocolo http) é exposta na porta 8080 do host. Esse valor 8080 foi escolhido, poderia ter sido mapeada na própria porta 80, ou em outra porta. Agora é só abrir um navegador e acessar http://localhost:8080. Para verificar as portas sendo usadas por um container pode-se usar docker port nome_ou_ID_do_container. Se não souber as portas a serem usadas pode usar -P (maiúsculo) para expor todas as portas do container para porta aleatórias do sistema host, por exemplo: docker run -P -d nginx

Este container está executando em segundo plano, será necessário usar o comando stop para encerrá-lo. Mas antes disso, saiba que é possível executar um comando dentro do container com docker exec nome_ou_ID_do_container comando, por exemplo docker exec nome_ou_ID_do_container cat /etc/*release. E este comando poder ser o próprio interpretador de comandos devidamente anexado de modo interativo ao terminal (opção -it) usando docker exec -it nome_ou_ID_do_container bash

Também é possível verificar os logs gerados pelo container com docker logs nome_ou_ID_do_container. Verifique que há uma opção para monitorar constantemente a saída de logs.

Criar imagens e executar seus próprios containers + Docker Hub + Azure

Agora que estamos familiarizados com Docker e o conceito de containers é possível testar a criação, ou construção, de uma imagem Docker. É o que será necessário fazer para transformar sua aplicação em um container, ou "conteinerizá-la". Vamos aproveitar um exemplo disponível no GitHub da Docker. Obtenha os arquivos necessários e acesse o diretório executando:

git clone https://github.com/dockersamples/node-bulletin-board

Caso não tenha o git instalado e não queira instalá-lo, pode simplesmente fazer o download da aplicação em https://github.com/dockersamples/node-bulletin-board e descompactá-la. Acesse o diretório da aplicação com:

cd node-bulletin-board/bulletin-board-app

As imagens são criadas a partir de uma espécie de arquivo de instruções chamado de Dockerfile. Elas são compostas partindo de uma base à qual são adicionadas camadas que podem ser programas instalados, configurações, arquivos acrescentados, etc. Tudo definido nesse Dockerfile. Essas camadas podem ser manipuladas de maneira independente viabilizando que apenas a parte modificada da imagem do container precise ser atualizada para a criação de uma nova imagem, ou uma nova versão de uma imagem.

Nesse diretório de aplicação há um Dockerfile (o nome do arquivo é com "D" maiúsculo mesmo). Seu conteúdo é o seguinte (traduzido de https://docs.docker.com/get-started/part2/#sample-dockerfile):

# FROM seleciona a imagem base e a respectiva tag

# que pode ser "latest", "current", ou uma versão específica.

# Neste caso é a imagem do node.

FROM node:current-slim


# WORKDIR determina o diretório de trabalho.

WORKDIR /usr/src/app


# COPY copia um arquivo do sistema host para a imagem sendo criada.

COPY package.json .


# RUN executa um comando no sistema de arquivos da imagem (durante a criação).

RUN npm install


# EXPOSE expõe a porta 8080 do container.

EXPOSE 8080


# CMD executa o comando dentro do container (na execução). Há apenas um por container...

CMD [ "npm", "start" ]


# Copia o restante do código da aplicação para o sistema de arquivos da imagem.

COPY . .

A documentação do Dockerfile com todas as opções possíveis está disponível em inglês em https://docs.docker.com/engine/reference/builder/.

Para construir (build) a imagem execute o comando a seguir, sem esquecer do ponto no final, uma referência ao diretório corrente. O parâmetro tag determina o nome da imagem e sua identificação, nesse caso a versão 1.0.

docker build --tag meuprimeiroapp:1.0 .

Verifique as saídas fornecidas pela comando. Note as várias etapas realizadas a partir das definições fornecidas pelo Dockerfile. São as "camadas" comentadas anteriormente. No final deve haver uma mensagem de sucesso. Para executar um container a partir dessa imagem use o comando seguinte e verifique o resultado em seu navegador (http://localhost):

docker run -p 80:8080 -d --name meuapp meuprimeiroapp:1.0

Depois de testar a aplicação pode-se remover o container. Aproveite para checar as imagens disponíveis.

Essa imagem pode ser publicada no Docker Hub (hub.docker.com). Será necessário criar uma conta gratuita caso não possua. Devidamente "logado" no site do Docker Hub, crie um repositório, por exemplo "meuprimeiroapp". Se não for direcionado para a página do repositório será possível encontrá-lo em "repositories" no menu. O site já informa como publicar uma imagem para o repositório. Será algo como:

docker push nomedeusuariodocker/meuprimeiroapp:tagname

As imagens dos repositórios oficiais, mantidas pela Docker e outras empresas, podem ser obtidas simplesmente pelo nome (hello-world, ubuntu, centos, nginx, etc.). As demais são obtidas nesse padrão nomedeusuario/nomedaimagem:versao.

Será necessário gerar uma nova imagem usando sua DockerID (nome de usuário) e versão na tag. Ela ficará no formato indicado na linha anterior (nomedeusuariodocker/meuprimeiroapp:tagname). Para fazer isso execute:

docker tag meuprimeiroapp:1.0 nomedeusuariodocker/meuprimeiroapp:1.0

Para publicar a imagem no Docker Hub ajuste o comando sugerido no site do Docker Hub:

docker push nomedeusuariodocker/meuprimeiroapp:1.0

Se necessário o login no Docker Hub pode ser feito a partir da própria linha de comando utilizando:

docker login

Caso o repositório esteja como público, agora qualquer pessoa poderá criar um container baseado nessa imagem com:

docker run -p 80:8080 -d --name meuapp nomedeusuariodocker/meuprimeiroapp:1.0

Altere algo nessa aplicação exemplo, pode ser simplesmente o título no arquivo HTML, ou a cor, apenas para facilitar a identificar visualmente a modificação. Então gere uma nova imagem mudando a tag, para 1.1 por exemplo. Atualize o repositório no Docker Hub e gere um novo container a partir da nova versão da imagem.

Também é possível implementar facilmente esse container diretamente como uma aplicação em nuvem na Microsoft Azure. Para isso é necessário possuir uma conta com uma assinatura válida. Em tempo, caso seja estudante de um curso de Engenharia, ou TIC, é bem provável que sua instituição de ensino possua uma parceria acadêmica com acesso gratuito e sem a necessidade de cartão de crédito.

Dispondo da assinatura, acesse o portal do Azure (https://portal.azure.com) e faça o login. Clique em "+ Criar um recurso" e procure por "Aplicativo Web". Selecione a assinatura, um grupo de recursos (na dúvida crie um novo) e o nome da instância, que também será o endereço web da aplicação. Em publicar selecione "Contêiner do Docker" e sistema operacional "Linux". A opção "Região" normalmente é ajustada conforme sua assinatura. Eu utilizei uma conta no Brasil e portanto minha região é "Brazil South". No "Plano do Serviço de Aplicativo" já deve ter sido sugerido um plano correspondendo ao tamanho "Gratuito F1" no item seguinte (SKU e tamanho). Se não, é possível criar um novo plano e selecionar a opção gratuita. A imagem seguinte mostra essas configurações:

Clique em "Avançar: Docker". Aqui configuraremos a imagem já publicada em nosso repositório do Docker Hub. Selecione "Contêiner Único" em Opções e "Docker Hub" em Origem da Imagem. Nas Opções do Docker Hub escolha se seu repositório é Público, ou Privado, em Tipo de Acesso. Em Imagem e marca (tag), insira seu nome de usuário, nome do container e versão, exatamente como no comando docker run, ou seja, no formato nomedeusuariodocker/meuprimeiroapp:1.0. A imagem seguinte ilustra isso:

Depois clique em "Revisar + criar". Se tudo estiver correto após a validação, surgirá a opção "Criar". Ao clicar nela iniciará a implantação e surgirá a mensagem de "Ir para o recurso" quando o processo estiver concluído. Agora é possível verificar o monitoramento do container e acessar o aplicativo online (pode demorar um pouco pra carregar inicialmente):

Confira o monitoramento e explore as opções disponíveis no menu lateral. Após realizar seus testes, talvez seja uma boa ideia excluir o recurso, de modo a liberar o nome de aplicação e endereço utilizado, além de recursos da sua conta para novas aplicações.

É muito comum implementar o registro de containers diretamente no provedor de nuvem. Isso corresponde basicamente a implementar um Docker Hub privativo.


Aplicações multi-container com Docker Compose + Azure + Duas linhas sobre Swarm e Kubernetes

Essencialmente, cada container Docker executa uma aplicação. Mas muitas vezes é necessário executar e criar uma aplicação mais complexa, com vários programas. Seja uma aplicação que precisa ser executada junto com um banco de dados, um ambiente de múltiplas aplicações, ou um contexto de microsserviços. Será necessário trabalharmos multi-container. Para isso existe o Docker Compose.

O Docker Compose utiliza um arquivo YAML (Compose File) para definir os serviços que constituem a aplicação. A documentação do Docker Compose pode ser lida em inglês em https://docs.docker.com/compose/. Para entender o funcionamento do Docker Compose vamos aproveitar novamente um exemplo da documentação oficial do Docker e que está disponível em https://docs.docker.com/compose/wordpress/.

Crie um diretório vazio e acesse-o. Por exemplo "meuapp". No Linux o comando para a criação de um diretório é mkdir e para acessá-lo é cd. Assim temos mkdir meuapp e cd meuapp.

Agora é necessário criar um arquivo com o nome docker-compose.yml. A extensão também pode ser ".yaml". Edite o arquivo com o conteúdo a seguir:

version: '3.3'


services:

db:

image: mysql:5.7

volumes:

- db_data:/var/lib/mysql

restart: always

environment:

MYSQL_ROOT_PASSWORD: somewordpress

MYSQL_DATABASE: wordpress

MYSQL_USER: wordpress

MYSQL_PASSWORD: wordpress


wordpress:

depends_on:

- db

image: wordpress:latest

ports:

- "8080:80"

restart: always

environment:

WORDPRESS_DB_HOST: db:3306

WORDPRESS_DB_USER: wordpress

WORDPRESS_DB_PASSWORD: wordpress

WORDPRESS_DB_NAME: wordpress

volumes:

db_data: {}

Este arquivo descreve a composição de um ambiente formado por dois serviços: um banco de dados MySQL e o WordPress, o mais popular sistema de gerenciamento de conteúdo da web, mas que depende de um banco de dados para funcionar. O arquivo finaliza acrescentando um volume, isto é, um espaço para armazenamento persistente dos dados gerenciado pelo Docker. Grande parte das aplicações irá requerer o armazenamento persistente dos dados e portanto, volumes. Na descrição de cada serviço há uma série de parâmetros e configurações explicitando variáveis de ambiente, dependências, portas expostas (8080 no exemplo), etc. É possível especificar um Dockerfile para a geração de uma imagem personalizada dentro do ambiente também, bem como ajustar as configurações de rede. Para verificar todas as opções disponíveis para o compose file consulte a documentação oficial em https://docs.docker.com/compose/compose-file/.

Agora para efetivamente compor o ambiente usaremos o comando docker-compose up dentro do diretório criado para a aplicação, sem esquecer da opção -d para executar em segundo plano:

docker-compose up -d

Abra o navegador e acesse http://localhost:8080 e termine a configuração do WordPress no navegador conforme as instruções da própria página.

Verifique algumas informações sobre o ambiente configurado e em execução, tais como os containers criados, os processos em execução em cada container, as imagens do Wordpress e do MySQL acrescentadas, os logs de cada container e o log geral. Este último corresponde ao comando docker-compose logs, enquanto os demais já foram vistos e a esta altura constituem um bom exercício de fixação!

Para interromper o ambiente criado, mas preservando os dados armazenados persistentemente no volume criado, utilize:

docker-compose down

Execute novamente docker-compose up -d e acesse o WordPress criado no navegador. Como o volume teve os dados persistidos, o sistema encontra-se já instalado, com o site em execução.

Desta vez, para interromper os containers e a configuração de rede que foi criada de modo automático e transparente para que esses containers pudessem se comunicar e, também, remover o volume criado, utilize:

docker-compose down -v

Também podemos testar essa aplicação na nuvem Microsoft Azure. Siga os mesmos passos apresentados no item anterior "Criar imagens e executar seus próprios containers + Docker Hub + Azure" para criar o aplicativo web e preencher os dados básicos. Clique em "Avançar: Docker". Aqui que haverá uma ligeira mudança. Desta feita será feito o carregamento do arquivo de composição. O Azure se encarregará de buscar as imagens no Docker Hub, no registro de containers do próprio Azure, ou em um registro privado e configurar tudo!

Assim, selecione "Docker Compose" em Opções e "Docker Hub" em Origem da Imagem. Nas Opções do Docker Hub escolha se seu repositório é Público, ou Privado, em Tipo de Acesso. Em Arquivo de Configuração clique para carregar o arquivo "docker-compose.yml" criado anteriormente, conforme mostrado a seguir:

Depois clique em "Revisar + criar". Se tudo estiver correto após a validação, surgirá a opção "Criar". Ao clicar nela iniciará a implantação e surgirá a mensagem de "Ir para o recurso" quando o processo estiver concluído. Agora é possível verificar o monitoramento da nossa aplicação e acessar o WordPress online (pode demorar um pouco pra carregar inicialmente):

Confira o monitoramento e explore as opções disponíveis no menu lateral. Novamente, após realizar seus testes, talvez seja uma boa ideia excluir o recurso, de modo a liberar o nome de aplicação e endereço utilizado, além de recursos da sua conta para novas aplicações.

De certa forma o Docker Compose é uma espécie de orquestrador para uma máquina, fazendo uma analogia com os orquestradores normalmente utilizados em um ambiente de produção num data center, ou em nuvem. Nesses caso os diversos serviços poderão ser executados em máquinas distintas e muitas vezes cada container precisará de múltiplas instâncias que podem ser criadas e destruídas dinamicamente para oferecer escalabilidade. Para cumprir com essa orquestração "em produção" é que existem o Docker Swarm, da própria Docker, e Kubernetes (https://kubernetes.io/pt/), ou simplesmente "K8s", desenvolvida inicialmente pela Google e que se tornou o grande padrão de mercado para orquestração de containers.


Próximos passos

Agora que você já sabe os primeiros passos e está com os pés bem firmados, aproveite esses links para ampliar seus conhecimentos (todos em inglês):


Na documentação oficial há também um material introdutório (todos em inglês):


Voltar para Máquinas Virtuais e Containers, Computação em Nuvem, ou Especial Nanocurso de Linux.