-
mvrp21 authored
Signed-off-by:
Marcus V. <mvrp21@inf.ufpr.br>
mvrp21 authoredSigned-off-by:
Marcus V. <mvrp21@inf.ufpr.br>
Fundamentos do Docker
Assume-se que quem ler essa documentação é familiarizado com os conceitos básicos de Docker1.
Antes de começar a executar comandos é preciso ter Docker instalado no sistema. Há documentação oficial2 sobre instalação.
:::{note} Esse guia/tutorial é um tipo de 'comece a mexer com docker rápido'. Ele não substitui e nem tenta substituir pesquisa mais aprofundada sobre Docker para quem tem interesse em aprender essa tecnologia.
A motivação para criar esse guia foi o fato de alguns projetos do C3SL
utilizarem Docker mas quem realmente tinha algum conhecimento era uma única
pessoa, e por conta disso já ocorreram algumas instâncias de precisar mexer
no Dockerfile
'pra ontem' mas ninguém ter o conhecimento básico para fazer
isso, pois quem sabia saiu do projeto (por exemplo). Esse guia visa no mínimo
atenuar esse problema.
:::
Pré-requisitos
A ideia para esse guia era ter o mínimo de pré-requisitos possível, para que quem precisar aprender a mexer com Docker no próprio projeto pudesse começar rapidamente. Entretanto é impossível negar que alguns conhecimentos específicos facilitariam o acompanhamento desse guia.
Segue a lista de conhecimentos que são interessantes ter:
- Sistemas Operacionais: Em particular virtualização e sistemas de arquivos, facilitam a compreensão do que é Docker e volumes, respectivamente. Entender processos pode ajudar também.
- Redes de Computadores: O guia assume que o leitor sabe o que é 'porta', e tem uma ideia básica de comunicação em rede. Compreensão do modelo TCP/IP é ideal.
-
Shell: Em certos momentos variáveis de ambiente são mencionadas, além disso
o
Dockerfile
se assemelha muito a um script.sh
. - NodeJS: Esse não é necessário, mas não faz mal. Como é comum utilizarem NodeJS nos projetos do C3SL um exemplo abaixo utiliza essa tecnologia com Docker. Não é necessário saber Node, mas saber exatamente o que o código faz apenas olhando para ele pode facilitar a compreensão do que está acontecendo.
- Git: Em alguns momentos é feita analogia entre GitHub e DockerHub, não é necessário conhecer Git, apenas a analogia que não será compreendida.
Terminologia básica
Algumas palavras vão se repetir frequentemente nesse guia:
- 'host': É a máquina que vai executar o contêiner Docker.
- 'porta': Definição de porta TCP/UDP.
- 'output': Saída padrão da execução de algum programa (neste caso de contêineres Docker).
Trabalhando com contêineres
Utilizando imagens pré-construídas
Vamos iniciar com prática: pegar uma imagem que já foi construída por alguém e executar um contêiner baseado nela. Para este guia foi escolhida a imagem do NodeJS3.
Execute o comando docker run node
. A saída deve ser similar à seguinte:
root@devops:~# docker run node
Unable to find image 'node:latest' locally
latest: Pulling from library/node
7bb465c29149: Pull complete
2b9b41aaa3c5: Pull complete
49b40be4436e: Pull complete
c558fac597f8: Pull complete
449619e06fe3: Pull complete
91b364bb66eb: Pull complete
4974a440c623: Pull complete
43ff5adaa0a2: Pull complete
Digest: sha256:104b26b5d34f9907f1f1e5e51fd9e557845f1a354f07ee9f28814dd9574a6154
Status: Downloaded newer image for node:latest
root@devops:~#
Analisando o comando e o output:
- Foi requisitado um contêiner em execução utilizando a imagem do Node.
- O sistema não tinha nenhuma imagem do Node, então ele procurou no registry padrão: o DockerHub4.
- Cada linha que termina com 'Pull complete' é uma camada da imagem baixada. Veremos mais sobre camadas de imagens depois.
- Após isso um novo contêiner foi criado com base nessa imagem e colocado em execução.
Há mais um detalhe: a imagem que foi baixada é node:latest
. Esse 'latest' é
uma tag, um identificador de versão da imagem. No caso da imagem do NodeJS as
tags da imagem são as próprias versões do Node, como 18.19
ou 21.7
.
Sempre que a tag não for especificada a versão mais recente será escolhida.
Como um exemplo vamos rodar docker run node:18.19
, especificando que queremos
a versão 18.19
do NodeJS:
root@devops:~# docker run node:18.19
Unable to find image 'node:18.19' locally
18.19: Pulling from library/node
7bb465c29149: Already exists
2b9b41aaa3c5: Already exists
49b40be4436e: Already exists
c558fac597f8: Already exists
449619e06fe3: Already exists
f52e55fee245: Pull complete
b51b5379e841: Pull complete
806ff1e3aade: Pull complete
Digest: sha256:aa329c613f0067755c0787d2a3a9802c7d95eecdb927d62b910ec1d28689882f
Status: Downloaded newer image for node:18.19
root@devops:~#
Note que as primeiras camadas da imagem aparecem como 'Already exists', isso
pois node:18.19
é uma versão mais antiga de node:latest
, logo a base para
ambos é a mesma, mas a versão nova muda algumas coisas.
Veremos essa questão de tags e 'camadas' de imagens mais à frente.
Verificando contêineres no sistema
O comando docker ps
lista os contêineres em execução no sistema.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Mas onde estão os contêineres que executamos?
Simples, eles executaram. Uma vez que o processo principal de um contêiner finaliza ele também encerra a sua execução.
Para ver contêineres parados no sistema precisamos passar uma flag extra para
o comando docker ps
: --all
.
Em sua forma reduzida: docker ps -a
.
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db1fae1ac24f node:18.19 "docker-entrypoint.s…" 4 seconds ago Exited (0) 2 seconds ago objective_williamson
f0c5f39f1c91 node "docker-entrypoint.s…" 9 seconds ago Exited (0) 8 seconds ago keen_shannon
root@devops:~#
Vamos analisar esse output:
- Cada contêiner tem um id único, assim como um nome único.
- Como não fornecemos um nome para os contêineres o Docker escolheu um nome para cada um deles.
- O comando executado foi um tal
docker-entrypoint.s...
. Esse é um arquivo de entrypoint, que veremos mais à frente. Por enquanto veja esse comando apenas como um script que incia tudo o que o contêiner precisa.
:::{hint}
Para mais opções do comando docker ps
basta executar docker ps --help
.
:::
Executando um contêiner em modo interativo
Legal, mas e se quisermos interagir com o contêiner? Para rodar um contêiner
interativo basta passar as flags --interactive
e --tty
para o comando.
Na forma reduzida, nosso comando vira: docker run -it node
.
Basicamente, sempre que o usuário for interagir com o contêiner as flags são
-it
, e se for apenas a shell que interage com o contêiner usa-se -i
.
root@devops:~# docker run -it node
Welcome to Node.js v21.7.0.
Type ".help" for more information.
> console.log('Hello, World!');
Hello, World!
undefined
>
Para sair da shell interativa do Node basta teclar Ctrl+D
.
:::{hint}
Para mais opções do comando docker run
basta executar docker run --help
.
:::
Removendo contêineres
Se você rodar docker ps -a
de novo perceberá que o número de contêineres
parados aumentou. Para não ficar com esse lixo parado basta executar o
comando docker rm <identificador-do-container>
.
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5452a05b6ab7 node:18.19 "docker-entrypoint.s…" 6 seconds ago Exited (0) 2 seconds ago pedantic_varahamihira
00b6d2a0de3d node "docker-entrypoint.s…" About a minute ago Exited (0) About a minute ago objective_edison
root@devops:~# docker rm pedantic_varahamihira
pedantic_varahamihira
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
00b6d2a0de3d node "docker-entrypoint.s…" About a minute ago Exited (0) About a minute ago objective_edison
root@devops:~# docker rm 00b6d2a0de3d
00b6d2a0de3d
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@devops:~#
Note que tanto o identificador do nome quando do id do contêiner servem.
:::{tip} Além de que tanto faz se o comando recebe o ID ou o NOME do contêiner, se o comando receber apenas o prefixo do identificador, supondo que esse prefixo não se repete o efeito é o mesmo.
Veja um exemplo em que se faz isso utilizando o ID:
root@devops:~/node-test# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80d9d7b2dab1 node "docker-entrypoint.s…" 2 seconds ago Exited (0) 2 seconds ago awesome_roentgen
ec38d6715864 node "docker-entrypoint.s…" 32 seconds ago Exited (0) 32 seconds ago jolly_gates
root@devops:~/node-test# docker rm 8
8
root@devops:~/node-test# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec38d6715864 node "docker-entrypoint.s…" 41 seconds ago Exited (0) 40 seconds ago jolly_gates
root@devops:~/node-test#
:::
Removendo contêineres automaticamente
Para remover automaticamente um contêiner que você executou é só passar a
flag --rm
para o comando docker run
.
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@devops:~# docker run --rm node
root@devops:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@devops:~#
Lidando com imagens
Como comentado anteriormente, imagens Docker são construídas por camadas.
Essencialmente 'cada camada é uma linha de um script'. O que ocorre sempre que um contêiner é criado a partir de uma imagem é que ele pega o contêiner original, aplica as configurações definidas nas camadas da imagem em ordem, e
De certa forma o efeito seria o mesmo se você
:::{note}
Como curiosidade: execute o comando docker image inspect node
.
Não veremos isso em detalhes, mas se você pesquisar pelos termos docker inspect
você encontrará diversos recursos sobre inspeção de imagens e
contêineres. Isso é extremamente útil para análise do que está realmente
sendo executado no sistema, e talvez ajude a compreender Docker em baixo
nível.
:::
É possível ver quais imagens estão no seu sistema com o comando docker image ls
. E do mesmo modo que contêineres: para remover uma imagem basta executar
docker image rm <imagem>
, com a tag caso necessário.