O Docker é uma ferramenta muito importante que se relaciona com o fluxo de integração contínua e o gitlab-ci de várias formas.
Neste guia, vamos passar pelos fundamentos e informações essenciais sobre a ferramenta.
Sumário
- O que é Docker?
- O que é Contêinerização
- Como criar um contêiner
- Construindo e executando um contêiner
- Executando imagens prontas
- Docker desktop
O que é Docker?
O Docker, essencialmente, é uma ferramenta de criação de máquinas virtuais.
Mas a parte principal dele é a capacidade de criar máquinas leves e de fácil reutilização, utilizando a técnica de contêinerização.
Isso faz com que o Docker seja a ferramenta principal para a administração de ambientes de aplicações complexas.
O que é Contêinerização?
O docker, essencialmente, cria máquinas virtuais, mas se difere muito das técnicas de virtualização comuns.
Isso por que ele utiliza a técnica de contêinerização.
Nesta técnica, as "especificações" de uma máquina são guardadas em imagens.
Essas imagens guardam todas as informações da máquina, como sistema operacional, softwares baixados, entre outras coisas.
Em outras palavras, pode-se dizer que as imagens guardam o estado de uma máquina virtual.
Quando essas imagens são executas, são gerados os contêineres.
Os contêineres são máquinas virtuais, porém muito mais leves. Eles contém apenas as informações necessárias que foram indicadas nas imagens.
Essencialmente, imagens e contêineres são criados para um propósito específico, e geralmente executam alguma coisa, como uma aplicação por exemplo, mas podem facilmente ser usados como uma máquina virtual.
O ponto principal a ressaltar aqui é que contêinerização não equivale totalmente a virtualização comum.
Para o Docker ser mais leve e mais rápido do que a virtualização, os contêineres gerados não possuem todas as capacidades de uma máquina completa.
Eles possuem sistema operacional, capacidade de lidar com arquivos, baixar softwares, rodar aplicações, mas são apenas uma partição de uma máquina comum, alguns recursos mais complexos não estarão disponíveis.
Sem falar que, essencialmente, contêineres são voláteis e temporários.
Isso significa que o desenvolvedor muito provavelmente irá apagá-los e re-executá-los com uma frequência alta, e tudo o que for feito nele será apagado.
Para resumir, idealmente contêineres devem ser utilizados como ambientes temporários para a execução ou desenvolvimento de uma aplicação!
Como criar um contêiner?
As imagens docker são criadas através de Dockerfiles, estes arquivos são como scripts que executam uma série de comandos para preparar um contêiner.
Como por exemplo fazer o download de dependências de software, preparar ambientes, executar aplicações dependentes, entre outras coisas.
A linguagem Docker
Os Dockerfiles são escritos uma linguagem própria do Docker e, essencialmente, são compostos por uma série de comandos que executam alguma coisa, como scripts.
Os comandos principais do docker são:
FROM
Qualquer imagem Docker deve ser feita com base em outra!
O comando FROM irá buscar a imagem base especificada no docker-hub (repositório de imagens oficial do Docker), e utilizá-la como ponto de partida para os comandos subsequentes.
COPY
O comando COPY basicamente copia o conteúdo de um diretório para outro, mas ele possui uma propriedade oculta muito importante.
Digamos que o desenvolvedor esteja escrevendo um Dockerfile em um diretório, e ele queira executar algum arquivo presente no mesmo diretório do Dockerfile.
Em teoria, os arquivos estariam no diretório /home/app
do contêiner, mas, como o desenvolvedor pegou como base uma imagem externa, estes arquivos não estarão automaticamente disponíveis!
Isto por que, como dito anteriormente, as imagens são estados de um contêiner e, o estado da imagem base utilizada não possui os arquivos que o desenvolvedor vai querer utilizar.
O comando COPY possui a habilidade de forçar a atualização do estado do contêiner.
Por isso, ao rodar este comando, o motor do docker irá atualizar os arquivos do contêiner, assim buscando os arquivos atuais.
WORKDIR
O comando WORKDIR é equivalente ao cd do terminal.
Ele irá alterar o diretório de atuação para o especificado.
RUN
O comando RUN vai executar algum comando no contêiner.
É importante ressaltar aqui que contêineres tem sistemas operacionais, como windows ou linux.
Estes comandos escritos no RUN deverão estar na linguagem daquele sistema operacional.
EXPOSE
Como dito anteriormente, o contêiner Docker essencialmente executa alguma coisa, como uma aplicação, por exemplo.
Esta aplicação que está rodando dentro do contêiner muito provavelmente será acessada de fora dele.
O comando EXPOSE irá expor uma ou mais portas do contêiner para fora dele, para que possa ser acessado do ambiente externo.
CMD
O comando CMD indica qual o comando que deverá ser executado ao rodar o contêiner.
Este comando não é obrigatório em todos os casos, mas é essencial quando estamos criando uma imagem que executa alguma coisa.
Este comando definirá qual o ponto de partida da imagem e, quando ela for executada, será utilizada toda a preparação feita nos passos anteriores para rodar aquele comando.
Exemplo
Para exemplificar, vamos pegar o seguinte caso de uso:
Uma imagem que executa uma aplicação nodejs na porta 3000, com árvore de arquivos:
- /
- my-app/
- index.js
- package.json
- Dockerfile
- my-app/
Poderíamos escrever o Dockerfile facilmente desta maneira:
FROM node
COPY / /
WORKDIR /home/app/my-app
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
- O comando FROM irá buscar a imagem base do node no docker-hub;
- O comando COPY irá atualizar a raiz de arquivos do contêiner;
- O comando WORKDIR irá navegar até o diretório da aplicação (
my-app
); - O comando RUN irá rodar a instalação do projeto node;
- O comando EXPOSE irá expor a porta 3000 do contêiner para o ambiente externo;
- O comando CMD irá indicar o comando a ser executado ao rodar o contêiner.
Ao construir e rodar o contêiner, será executada a aplicação!
Construindo e executando um contêiner
Após ter um Dockerfile
criado, o desenvolvedor vai precisar construir a imagem e executar o contêiner.
Para isso o desenvolvedor precisará ter o docker instalado na máquina, e utilizar estes comandos:
docker build
Irá ler o Dockerfile
, interpretá-lo, executá-lo, e transformá-lo em uma imagem.
Possui os seguintes parâmetros principais:
-
-f
: indica o arquivo a ser lido, por padrão procura pelo arquivoDockerfile
; -
-t
: indica a tag a ser dada para a imagem, a tag nada mais é do que o nome da imagem; -
--rm
: remove qualquer imagem posterior que tenha a mesma tag, para fins de otimização de espaço; -
"contexto"
: indica qual diretório de partida a ser executado a construção
Como no exemplo:
sudo docker build -f Dockerfile.db --rm -t database .
Este comando irá:
- Ler o arquivo
Dockerfile.db
; - Tagear a imagem como
database
; - Remover qualquer imagem com a mesma tag presente na máquina;
O ponto no final é o contexto, basicamente fala para ter o mesmo diretório em que o comando está sendo rodado como ponto de partida.
Caso quiséssemos ter o diretóriomy-app
como partida, poderíamos rodar:
sudo docker build -f Dockerfile.db --rm -t database ./my-app
docker run
Vai executar uma imagem em um contêiner.
Possui os seguintes parâmetros principais:
-
--name
: o nome do contêiner.
Caso não seja especificado, o docker dará um nome aleatório para ele; -
-d
: faz com que o contêiner seja rodado em segundo plano; -
-p
: utilizado para mapear portas do contêiner para o ambiente externo.
Por padrão, o Docker não suporta que o ambiente externo acesse processos rodando dentro da máquina, para isso, é preciso expor as portas do processos no Dockerfile, com o EXPOSE que foi mencionado anteriormente, e mapear a porta exposta ao ambiente externo usando o comando-p
; -
"tag da imagem"
: indicará qual a imagem a ser executada.
Como no exemplo:
sudo docker run --name my_database -d -p 8080:3000 db
Este comando irá:
- Buscar pela imagem
db
.
O Docker tentará primeiro buscar essa imagem na máquina local, se não encontrar, irá buscá-la no docker-hub; - Nomer o contêiner como
my_database
; - Rodar em segundo plano;
- Mapear a porta
3000
do contêiner para a porta8080
da máquina que o está executando.
No comando-p
, a porta da esquerda do caractere:
é a porta da máquina local, e da direita, do contêiner.
Não é possível mapear uma porta do contêiner que não está exposta, pra isso que serve o comando EXPOSE!
Se o desenvolvedor estiver executando o Docker via Docker Desktop, uma alternativa ao
docker run
via linha de comando é a interface do Docker Desktop!
Nele é possível realizar todo o fluxo indicado, porém através de uma interface amigável ao usuário.
Executando imagens prontas
É válido ressaltar também que estas funcionalidades não se limitam apenas aos Dockerfiles.
Os Dockerfiles apenas estão lá para gerar imagens conforme o desenvolvedor desejar, mas podemos também pegar imagens diretamente do docker-hub e rodá-las!
Como por exemplo, o laboratório GQS tem uma série de imagens prontas para vários casos de uso dos desenvolvedores!
Estas máquinas são públicas e podem ser utilizadas livremente pelos desenvolvedores do laboratório para auxiliar no desenvolvimento das aplicações.
Para isso o desenvolvedor precisa:
- Dar pull da imagem, utilizando o commando
docker pull
; - Rodar a imagem normalmente.
Como por exemplo, se o desenvolvedor quiser rodar a imagem mysql:
docker pull mysql
docker run -p 3306:3306 db
Fará com que o mysql seja executado na porta 3306!
Docker Desktop
O Docker Desktop é hoje a ferramenta padrão do Docker, nela é possível realizar tudo o que é feito na linha de comando, porém via uma interface amigável ao usuário.
Nela é possível rodar imagens, fazer uploads, verificar logs dos contêineres, entre outras coisas!
Por mais que o Docker via linha de comando ainda seja muito utilizado (nas pipelines de CI/CD por exemplo), o Docker Desktop é uma solução ótima para o desenvolvimento.