|
|
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?](#docker)
|
|
|
- [O que é Contêinerização](#conteinerizacao)
|
|
|
- [Como criar um contêiner](#criarconteiner)
|
|
|
- [Construindo e executando um contêiner](#executando)
|
|
|
- [Executando imagens prontas](#imagensprontas)
|
|
|
- [Docker desktop](#desktop)
|
|
|
|
|
|
## <a name="docker"> O que é Docker? </a>
|
|
|
|
|
|
O Docker, essencialmente, é uma ferramenta de criação de máquinas virtuais.<br>
|
|
|
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.
|
|
|
|
|
|
## <a name="conteinerizacao"> 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**.<br>
|
|
|
Essas imagens guardam todas as informações da máquina, como sistema operacional, softwares baixados, entre outras coisas.<br>
|
|
|
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**.<br>
|
|
|
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**.<br>
|
|
|
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**.<br>
|
|
|
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!
|
|
|
|
|
|
|
|
|
## <a name="criarconteiner"> 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**.<br>
|
|
|
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](https://hub.docker.com) (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**.<br>
|
|
|
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 ad 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**.<br>
|
|
|
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**.<br>
|
|
|
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
|
|
|
|
|
|
Poderíamos escrever o Dockerfile facilmente desta maneira:
|
|
|
|
|
|
``` Dockerfile
|
|
|
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](https://hub.docker.com/_/node);
|
|
|
- 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!
|
|
|
|
|
|
|
|
|
## <a name="executando"> 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](https://www.docker.com/products/docker-desktop/), 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 arquivo `Dockerfile`;
|
|
|
- `-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:<br>
|
|
|
`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.<br>
|
|
|
Caso quiséssemos ter o diretório `my-app` como partida, poderíamos rodar:<br>
|
|
|
`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. <br>
|
|
|
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.<br>
|
|
|
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:<br>
|
|
|
`sudo docker run --name my_database -d -p 8080:3000 db`
|
|
|
|
|
|
Este comando irá:
|
|
|
- Buscar pela imagem `db`.<br>
|
|
|
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 porta `8080` da máquina que o está executando.<br>
|
|
|
No comando `-p`, a porta da esquerda do caractere `:` é a porta da máquina local, e da direita, do contêiner.<br>
|
|
|
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**!<br>
|
|
|
Nele é possível realizar todo o fluxo indicado, porém através de uma interface amigável ao usuário.
|
|
|
|
|
|
## <a name="imagensprontas"> Executando imagens prontas
|
|
|
|
|
|
É válido ressaltar também que estas funcionalidades não se limitam apenas aos **Dockerfiles**.<br>
|
|
|
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!
|
|
|
|
|
|
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](https://hub.docker.com/_/mysql):
|
|
|
- `docker pull mysql`
|
|
|
- `docker run -p 3306:3306 db`
|
|
|
|
|
|
Fará com que o **mysql** seja executado na porta 3306!
|
|
|
|
|
|
## <a name="desktop"> Docker Desktop </a>
|
|
|
O [Docker Desktop](https://docs.docker.com/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.
|
|
|
|