Docker

Docker Docker

Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.

Contentoreslink image 125

Olá mundolink image 126

Executar o primeiro contêiner do tipo Hello World com o comando docker run hello-world

	
!docker run hello-world
Copy
	
Unable to find image 'hello-world:latest' locally
	
latest: Pulling from library/hello-world
85e32844: Pull complete 457kB/2.457kBBDigest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

Como não temos o contêiner salvo localmente, o Docker o baixa do Docker Hub. Se agora voltarmos a executar o contêiner, já não aparecerá a primeira mensagem, na qual indica que está sendo baixado.

	
!docker run hello-world
Copy
	
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

Para ver os contêineres que estão em execução, execute docker ps

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Como vemos não há nenhum contêiner aberto. Mas, se executarmos docker ps -a (all), veremos que sim aparecem.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 10 seconds ago Exited (0) 9 seconds ago strange_thompson
5f5705e7603e hello-world "/hello" 15 seconds ago Exited (0) 14 seconds ago laughing_jang

Vemos que aparecem dois contentores chamados hello-world que são os dois que executamos anteriormente. Portanto, cada vez que executamos o comando run, o Docker cria um novo contentor, não executa um que já exista.

Se quisermos ter mais informações sobre um dos dois contêineres, podemos executar docker inspect <id>, onde <id> corresponde à ID do contêiner que foi exibida na lista anterior.

	
!docker inspect 1efb51bbbf38
Copy
	
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

Como lembrar IDs é complicado para nós, o Docker atribui nomes aos contêineres para facilitar nossa vida. Assim, na lista anterior, a última coluna corresponde ao nome que o Docker atribuiu a cada contêiner, de modo que se agora executarmos docker inspect <name> obteremos as mesmas informações que com a ID.

Volto a executar docker ps -a para voltar a ver a lista

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago laughing_jang

E agora eu executo docker inspect <name> para ver a informação do contêiner.

	
!docker inspect strange_thompson
Copy
	
[
{
"Id": "1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e",
"Created": "2023-09-04T03:59:17.795499354Z",
"Path": "/hello",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-04T03:59:18.406663026Z",
"FinishedAt": "2023-09-04T03:59:18.406181184Z"
},
"Image": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d",
"ResolvConfPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hostname",
"HostsPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/hosts",
"LogPath": "/var/lib/docker/containers/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e/1efb51bbbf38917affd1b5871db8e658ebfe0b2efa5ead17545680b7866f682e-json.log",
"Name": "/strange_thompson",
...
}
}
}
]

Mas por que com docker ps não vemos nenhum contêiner e com docker ps -a sim. Isso acontece porque docker ps só mostra os contêineres que estão em execução, enquanto docker ps -a mostra todos os contêineres, tanto os que estão em execução quanto os que estão desligados.

Podemos criar um contêiner atribuindo a ele um nome através do comando docker run --name <name> hello-world

	
!docker run --name hello_world hello-world
Copy
	
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

Isso será mais confortável para nós, pois poderemos controlar nós mesmos os nomes dos contêineres.

Se agora quisermos criar outro contêiner com o mesmo nome, não poderemos, porque o Docker não permite que se dupliquem os nomes dos contêineres. Desse modo, se quisermos renomear o contêiner, podemos usar o comando docker rename <old name> <new name>

	
!docker rename hello_world hello_world2
Copy

Agora temos um monte de contêineres idênticos. Então, se quisermos remover algum, temos que usar o comando docker rm <id> ou docker rm <name>

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f432c9c2ca21 hello-world "/hello" 9 seconds ago Exited (0) 8 seconds ago hello_world2
1efb51bbbf38 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago laughing_jang
	
!docker rm hello_world2
Copy
	
hello_world2

Se voltarmos a ver a lista de contêineres, o contêiner hello_world2 já não estará mais lá.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efb51bbbf38 hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago strange_thompson
5f5705e7603e hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago laughing_jang

Se quisermos apagar todos os contêineres, podemos fazer isso um a um, mas como é muito trabalhoso, podemos apagar todos usando o comando docker container prune. Este comando remove apenas os contêineres que estejam parados.

	
!docker container prune
Copy
	
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y

Docker pergunta se você tem certeza, e se você disser que sim, ele apaga todos. Se agora listar todos os contêineres, nenhum aparecerá.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

O modo interativolink image 127

Vamos a executar um Ubuntu por meio do comando docker run ubuntu

	
!docker run ubuntu
Copy
	
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
Digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1[1A
Status: Downloaded newer image for ubuntu:latest

Como vemos, agora demorou mais para baixar. Se listarmos os contêineres através do comando docker ps, veremos que não aparece o contêiner que acabamos de criar, ou seja, ele não está em execução.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Listamos agora todos os contêineres

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da16b3a85178 ubuntu "bash" 4 seconds ago Exited (0) 3 seconds ago hardcore_kare

Vemos que o estado do contêiner é Exited (0)

Se observa que o comando do contêiner é bash e junto ao estado Exited (0) indica que foi iniciado o Ubuntu, executou seu *bash*, terminou a execução e retornou 0. Isso acontece porque não foi especificada nenhuma tarefa para o bash do Ubuntu. Para resolver isso, agora vamos executar o contêiner com o comando docker run -it ubuntu, onde it indica que queremos executá-lo em modo interativo.

	
!docker run -it ubuntu
Copy
	
root@5b633e9d838f:/#

Agora vemos que estamos dentro do bash do Ubuntu. Se executarmos o comando cat /etc/lsb-release podemos ver a distribuição do Ubuntu.

	
!root@5b633e9d838f:/# cat /etc/lsb-release
Copy
	
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"

Se abrirmos outra terminal e vermos a lista de contêineres, agora sim aparecerá o contêiner rodando Ubuntu

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani

Vemos o contêiner com Ubuntu e em seu estado podemos ver UP

Se vemos agora a lista de todos os contentores, veremos que aparecem os dois contentores com Ubuntu, o primeiro desligado e o segundo que está em execução.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani
da16b3a85178 ubuntu "bash" 3 minutes ago Exited (0) 3 minutes ago hardcore_kare

Se voltarmos à terminal onde tínhamos o Ubuntu rodando dentro de um docker, se escrevermos exit sairemos do Ubuntu.

	
!root@5b633e9d838f:/# exit
Copy
	
exit

Se executarmos docker ps o contêiner já não aparece

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Mas se eu executar docker ps -a ele aparece. Isso significa que o contêiner foi desligado.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b633e9d838f ubuntu "bash" 4 minutes ago Exited (0) 27 seconds ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 4 minutes ago Exited (0) 4 minutes ago hardcore_kare

Isso ocorre porque ao escrever exit, na verdade estamos escrevendo no console do bash do Ubuntu, o que significa que estamos encerrando o processo bash do Ubuntu.

Ciclo de vida de um contêinerlink image 128

Em Docker, quando o processo principal de um contêiner termina, o contêiner é desligado. Dentro de um contêiner podem ser executados vários processos, mas apenas quando o processo principal termina, o contêiner é desligado.

Portanto, se quisermos executar um contêiner que não pare quando um processo terminar, devemos fazer com que seu processo principal não termine. Neste caso, que o bash não termine.

Se quisermos executar um contêiner com Ubuntu, mas que não finalize quando o processo do Bash terminar, podemos fazer da seguinte maneira

	
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copy
	
ce4d60427dcd4b326d15aa832b816c209761d6b4e067a016bb75bf9366c37054

O que fazemos é primeiro dar o nome alwaysup, em segundo lugar passar a opção -d (detach) para que o contêiner seja executado em segundo plano e por fim dizer o processo principal que queremos que seja executado no contêiner, que neste caso é tail -f /dev/null que equivale a um comando nop

Isso nos retornará o ID do contêiner, mas não estaremos dentro do Ubuntu como acontecia antes.

Se agora vermos a lista de contêineres que estão em execução, aparecerá o contêiner que acabamos de criar.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 18 seconds ago Up 17 seconds alwaysup

Como já temos um contêiner rodando sempre, podemos nos conectar a ele através do comando exec. Dizemos o nome ou a ID do contêiner e passamos o processo que queremos que seja executado. Além disso, passamos a opção -it para dizer que deve ser interativo.

	
!docker exec -it alwaysup bash
Copy
	
root@ce4d60427dcd:/#

Agora estamos de volta no Ubuntu. Se executarmos o comando ps -aux, podemos ver uma lista dos processos que estão sendo executados dentro do Ubuntu.

	
!ps -aux
Copy
	
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2820 1048 ? Ss 13:04 0:00 tail -f /dev/null
root 7 0.0 0.0 4628 3796 pts/0 Ss 13:04 0:00 bash
root 15 0.0 0.0 7060 1556 pts/0 R+ 13:05 0:00 ps -aux

Vemos apenas três processos, o ps -aux, o bash e o tail -f /dev/null

Este contêiner vai ficar sempre ligado enquanto o processo tail -f /dev/null estiver em execução.

Se sairmos do contêiner com o comando exit e executarmos o comando docker ps veremos que o contêiner continua ligado.

	
!exit
Copy
	
exit
	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 2 minutes ago Up 2 minutes alwaysup

Para poder finalizar o processo e poder desligar o contêiner, devemos usar o comando docker stop <name>

	
!docker stop alwaysup
Copy
	
alwaysup

Se agora listarmos os contêineres em execução, o contêiner com Ubuntu já não aparecerá.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

E se listarmos todos os contêineres, aparece o contêiner com Ubuntu, e seu estado Exited

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 14 minutes ago Exited (137) About a minute ago alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

Também podemos pausar um contêiner por meio do comando docker pause <name>

	
!docker run --name alwaysup -d ubuntu tail -f /dev/null
Copy
	
8282eaf9dc3604fa94df206b2062287409cc92cbcd203f1a018742b5c171c9e4

Agora nós pausamos

	
!docker pause alwaysup
Copy
	
alwaysup

Se voltarmos a ver todos os contêineres, vemos que o contêiner com Ubuntu está pausado.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8282eaf9dc36 ubuntu "tail -f /dev/null" 41 seconds ago Up 41 seconds (Paused) alwaysup
5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare

Contentores de uso únicolink image 129

Se a hora de executar um contêiner, colocarmos a opção --rm, esse contêiner vai ser apagado quando terminar de ser executado.

	
!docker run --rm --name autoremove ubuntu:latest
Copy

Se agora vemos quais contêineres temos

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Vemos que não está o contêiner que acabamos de criar

Expor containers ao mundo externolink image 130

Vamos a criar um novo contêiner com um servidor

	
!docker run -d --name proxy nginx
Copy
	
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
f1ad4ce1: Pulling fs layer
b079d0f8: Pulling fs layer
5fbbebc6: Pulling fs layer
ffdd25f4: Pulling fs layer
32c8fba2: Pulling fs layer
24b8ba39: Pull complete 393kB/1.393kBB[5ADigest: sha256:2888a97f7c7d498bbcc47ede1ad0f6ced07d72dfd181071dde051863f1f79d7b
Status: Downloaded newer image for nginx:latest
1a530e04f14be082811b72ea8b6ea5a95dad3037301ee8a1351a0108ff8d3b30

Isso cria um servidor, vamos listar novamente os contêineres que estão em execução.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a530e04f14b nginx "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp proxy

Agora aparece uma nova coluna com a porta, e nos diz que o servidor que acabamos de criar está na porta 80 sob o protocolo tcp.

Se abrirmos um navegador e tentarmos nos conectar ao servidor através de http://localhost:80, não conseguiremos conectar. Isso acontece porque cada contêiner tem sua própria interface de rede. Ou seja, o servidor está ouvindo na porta 80 do contêiner, mas nós estamos tentando conectar à porta 80 do host.

Paramos o contêiner para relançá-lo de outra forma

	
!docker stop proxy
Copy
	
proxy

Se listarmos os contêineres, não aparece em execução

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

O apagamos para recriá-lo

	
!docker rm proxy
Copy
	
proxy

Se listarmos todos os contêineres, já não está

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 19 minutes ago Exited (137) 5 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 24 minutes ago Exited (0) 20 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 24 minutes ago Exited (0) 24 minutes ago hardcore_kare

Para recriar o contêiner com o servidor e poder vê-lo do host, temos que usar a opção -p (publish), indicando em primeiro lugar a porta na qual queremos vê-la no host e em seguida a porta do contêiner, ou seja, -p <ip host>:<ip contêiner>

	
!docker run -d --name proxy -p 8080:80 nginx
Copy
	
c199235e42f76a30266f6e1af972e0a59811806eb3d3a9afdd873f6fa1785eae

Listamos os contêineres

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c199235e42f7 nginx "/docker-entrypoint.…" 22 seconds ago Up 21 seconds 0.0.0.0:8080-&gt;80/tcp, :::8080-&gt;80/tcp proxy

Vemos que o porto do contentor é 0.0.0.0:8080->80/tcp. Se agora formos a um navegador e introduzirmos 0.0.0.0:8080 poderemos aceder ao servidor do contentor

Ao listar os contêineres, na coluna PORTS indica 0.0.0.0:8080->80/tcp, o que nos ajuda a ver a relação de portas

Para ver os logs do contêiner, através do comando docker logs <name> posso ver os registros do contêiner

	
!docker logs proxy
Copy
	
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/09/13 13:24:06 [notice] 1#1: using the "epoll" event method
2022/09/13 13:24:06 [notice] 1#1: nginx/1.23.1
2022/09/13 13:24:06 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/09/13 13:24:06 [notice] 1#1: OS: Linux 5.15.0-46-generic
2022/09/13 13:24:06 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/09/13 13:24:06 [notice] 1#1: start worker processes
2022/09/13 13:24:06 [notice] 1#1: start worker process 31
2022/09/13 13:24:06 [notice] 1#1: start worker process 32
2022/09/13 13:24:06 [notice] 1#1: start worker process 33
2022/09/13 13:24:06 [notice] 1#1: start worker process 34
2022/09/13 13:24:06 [notice] 1#1: start worker process 35
2022/09/13 13:24:06 [notice] 1#1: start worker process 36
2022/09/13 13:24:06 [notice] 1#1: start worker process 37
2022/09/13 13:24:06 [notice] 1#1: start worker process 38
2022/09/13 13:24:06 [notice] 1#1: start worker process 39
2022/09/13 13:24:06 [notice] 1#1: start worker process 40
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
...
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Agora posso ver todas as solicitações que foram feitas ao servidor. Mas se eu quiser ver os logs em tempo real, usando docker logs -f <name> posso fazer isso.

	
!docker logs -f proxy
Copy

Agora posso ver os logs em tempo real. Para sair, pressione CTRL+C

Como pode chegar um momento em que haja muitos logs, se você quiser apenas os últimos logs, através da opção --tail <num> você pode ver os últimos <num> logs. Se adicionar a opção -f estaremos vendo sempre os últimos <num> logs.

	
!docker logs --tail 10 proxy
Copy
	
2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022/09/13 13:24:06 [notice] 1#1: start worker process 42
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üE޶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Se adicionarmos a opção -t, podemos ver a data e hora de cada log. Dessa forma, se tivermos um problema, podemos saber em que momento ele ocorreu.

	
!docker logs --tail -t 10 proxy
Copy
	
2022-09-13T13:24:06.573362728Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 41
2022-09-13T13:24:06.651127107Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 42
2022-09-13T13:24:16.651160189Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "üE޶ EgóɚœÊì§y#3’•ÜQïê$¿# ƒ÷-,s!rê|®ß¡LZª4y³t«ÀÎ_¸çÿ'φ êêÀ+À/À,À0̨̩ÀÀœ/5“ŠŠ localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:16.116817914Z 172.17.0.1 - - [13/Sep/2022:13:24:16 +0000] "ü}©Dr{Œ;z‚­¼ŠzÂxßšæl?§àDoK‘'g»µ %»ýق?ۀ³TöcJ÷åÂÒ¼¢£ë½=R¼ƒ‰… ÊÊÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:24:39.117398081Z 172.17.0.1 - - [13/Sep/2022:13:24:39 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:24:39.117412408Z 2022/09/13 13:24:40 [error] 34#34: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "0.0.0.0:8080", referrer: "http://0.0.0.0:8080/"
2022-09-13T13:24:40.117419389Z 172.17.0.1 - - [13/Sep/2022:13:24:40 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://0.0.0.0:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
2022-09-13T13:25:00.117434249Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "üâV$Zqi'×ü[€ïºåÇè÷&amp;3nSëÉì‘ÂØÑ‰ž¾ Ç?áúaΐ˜uã/ØRfOHì’+“\»±¿Òm°9 úúÀ+À/À,À0̨̩ÀÀœ/5“šš localhostÿ" 400 157 "-" "-" "-"
2022-09-13T13:25:00.223560881Z 172.17.0.1 - - [13/Sep/2022:13:25:00 +0000] "ü)šbCÙmñ†ëd"ÏÄE‡#~LÁ„µ‘k˜«lî[0 ÐÒ`…Æ‹…R‹‡êq{Pòû⨝IôtH™~Ê1-|Ž êêÀ+À/À,À0̨̩ÀÀœ/5“" 400 157 "-" "-" "-"
2022-09-13T13:26:25.223596738Z 172.17.0.1 - - [13/Sep/2022:13:26:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

Paramos e apagamos o contêiner

	
!docker rm -f proxy
Copy
	
proxy
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 27 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Dados no Dockerlink image 131

Montagens bindlink image 132

Vamos ver os contêineres que temos parados

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup
5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 28 minutes ago funny_mirzakhani
da16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare

Vamos a apagar os dois do Ubuntu nos quais seu comando principal é o Bash e vamos deixar o que ficou como não operação.

	
!docker rm funny_mirzakhani
Copy
	
funny_mirzakhani
	
!docker rm hardcore_kare
Copy
	
hardcore_kare
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce4d60427dcd ubuntu "tail -f /dev/null" 27 minutes ago Exited (137) 14 minutes ago alwaysup

Vamos a voltar a executar o contêiner do Ubuntu que deixamos, isso é feito através do comando start

	
!docker start alwaysup
Copy
	
alwaysup

Nós entramos novamente dentro de el

	
!docker exec -it alwaysup bash
Copy
	
root@ce4d60427dcd:/#

No contêiner, posso criar uma nova pasta chamada dockerfolder

	
!mkdir dockerfolder
Copy

Se listarmos os arquivos, aparecerá a nova pasta

	
!ls
Copy
	
bin boot dev dockerfolder etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var

Se sairmos do contêiner

	
!exit
Copy
	
exit

E o apagamos

	
!docker rm -f alwaysup
Copy
	
alwaysup

Se listarmos todos os contêineres, o último que criamos já não aparece.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Vamos a fazer tudo de novo, mas primeiro vamos criar uma pasta no host na qual compartilharemos os dados com o contêiner

	
!mkdir dockerHostFolder
Copy

Vemos que dentro da pasta não há nada

	
!ls dockerHostFolder
Copy

Agora obtemos nosso caminho absoluto

	
!pwd
Copy
	
/home/wallabot/Documentos/web/portafolio/posts

Recriamos o contêiner, mas adicionando a opção -v (bind mount). Em seguida, adiciona-se o caminho absoluto da pasta do host e o caminho absoluto da pasta no contêiner, -v <host path>:<container path>.

	
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
4ede4512c293bdcc155e9c8e874dfb4a28e5163f4d5c7ddda24ad2863f28921b

Entreamos no contentor, listamos os arquivos e já aparece a pasta que havíamos criado

	
!docker exec -it alwaysup bash
Copy
	
root@4ede4512c293:/#
	
root@4ede4512c293:/# ls
Copy
	
bin dev etc lib lib64 media opt root sbin sys usr
boot dockerContainerFolder home lib32 libx32 mnt proc run srv tmp var

Vamos ao diretório do contêiner que compartilhamos, criamos um arquivo e saímos do contêiner

	
root@4ede4512c293:/# cd dockerContainerFolder
Copy
	
root@4ede4512c293:/dockerContainerFolder# touch bindFile.txt
Copy
	
root@4ede4512c293:/dockerContainerFolder# exit
Copy
	
exit

Vamos ver o que tem dentro da pasta compartida

	
!ls dockerHostFolder
Copy
	
bindFile.txt

Mas ainda, se apagarmos o contêiner, o arquivo continua lá.

	
!docker rm -f alwaysup
Copy
	
alwaysup
	
!ls dockerHostFolder
Copy
	
bindFile.txt

Se eu criar novamente um contêiner compartilhando as pastas, todos os arquivos estarão no contêiner.

	
!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
6c021d37ea29d8b23fe5cd4968baa446085ae1756682f65340288b4c851c362d
	
!docker exec -it alwaysup bash
Copy
	
root@6c021d37ea29:/#
	
!root@6c021d37ea29:/# ls dockerContainerFolder/
Copy
	
bindFile.txt:/#

Eliminamos o contêiner

	
!docker rm -f alwaysup
Copy
	
alwaysup
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Volumeslink image 133

Os volumes foram criados como uma evolução dos bind mounts para fornecer mais segurança. Podemos listar todos os volumes do Docker usando docker volume ls.

	
!docker volume ls
Copy
	
DRIVER VOLUME NAME

Vamos a criar um novo volume para o contêiner do Ubuntu, para isso usamos o comando docker volume create <nome_do_volume>

	
!docker volume create ubuntuVolume
Copy
	
ubuntuVolume

Se listarmos os volumes novamente, aparecerá o que acabamos de criar.

	
!docker volume ls
Copy
	
DRIVER VOLUME NAME
local ubuntuVolume

No entanto, não aparece como uma pasta no sistema de arquivos do host. Com ls -d */ listamos todas as pastas

	
!ls -d */
Copy
	
dockerHostFolder/ __pycache__/

Vamos a voltar a criar um contêiner, mas agora o criamos com o volume que acabamos de criar com a opção --mount, indicando o volume fonte mediante src=<nome do volume> (se o volume não existisse, docker o criaria), a seguir o destino separado por uma ,, dst=<caminho do contêiner>, ou seja --mount src=<nome do volume>,dst=<caminho do contêiner>

	
!docker run -d --name alwaysup --mount src=ubuntuVolume,dst=/dockerVolumeFolder ubuntu tail -f /dev/null
Copy
	
42cdcddf4e46dc298a87b0570115e0b2fc900cb4c6db5eea22a61409b8cb271d

Uma vez criado, podemos ver os volumes do contêiner usando o comando inspect e filtrando por '{{.Mounts}}'

$ docker inspect --format '{{.Mountns}}' alwaysup
[
{
volume ubuntuVolume /var/lib/docker/volumes/ubuntuVolume/_data /dockerVolumeFolder local z true
}
]

Vemos que o volume se chama ubuntuVolume e além disso podemos ver o caminho onde está armazenado, neste caso em /var/lib/docker/volumes/ubuntuVolume/_data. Fazemos o mesmo que antes, entramos no contêiner, criamos um arquivo no caminho do volume, saímos e vemos no host se foi criado.

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/# touch dockerVolumeFolder/volumeFile.txt
root@42cdcddf4e46:/# exit
$ sudo ls /var/lib/docker/volumes/ubuntuVolume/_data
volumeFile.txt

O arquivo está criado.

Inserir e extrair arquivos de um contêinerlink image 134

Primeiro vamos criar um arquivo que queremos copiar dentro de um contêiner

	
!touch dockerHostFolder/text.txt
Copy

Entramos no contêiner

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/#

Criamos uma nova pasta onde vamos copiar o arquivo e sair.

root@42cdcddf4e46:/# mkdir folderToCopy
root@42cdcddf4e46:/# ls
bin boot dev dockerVolumeFolder etc folderToCopy home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@42cdcddf4e46:/# exit
sair

Copiamos dentro do contêiner o arquivo por meio do comando cp, indicando o arquivo que **quero** copiar, o contêiner onde queremos copiá-lo e o caminho dentro do contêiner, docker cp <file> <container>:<container path>

	
!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopy
Copy

Voltamos a entrar no contêiner e verificamos se o arquivo está lá.

$ docker exec -it alwaysup bash
root@42cdcddf4e46:/# ls folderToCopy/
Parece que hay un malentendido. No has proporcionado el contenido de `text.txt` para que pueda traducirlo. Por favor, comparte el texto markdown que deseas traducir al portugués.

Sair do contêiner

/# sair
saída

Agora vamos extrair o arquivo do contêiner e salvá-lo no host com outro nome. Para isso, usamos novamente o comando cp, mas indicando agora o contêiner, o caminho do arquivo no contêiner e o caminho e nome que queremos que o arquivo tenha no host, docker cp <container>:<caminho_do_arquivo_no_docker> <caminho_do_arquivo_no_host>

	
!docker cp alwaysup:/folderToCopy/text.txt dockerHostFolder/fileExtract.txt
Copy

Vemos que está no host

	
!ls dockerHostFolder
Copy
	
bindFile.txt fileExtract.txt text.txt

Embora o contêiner esteja parado, também é possível copiar arquivos.

Por último, apagamos o contêiner

	
!docker rm -f alwaysup
Copy
	
alwaysup

Imagenslink image 135

Conceitos fundamentaislink image 136

As imagens são os arquivos ("modelos") com toda a configuração para criar um contêiner. Cada vez que criamos um contêiner, ele é criado a partir de uma imagem. Quando criávamos contêineres novos, a primeira vez aparecia uma mensagem dizendo que não tínhamos a imagem e que iria baixá-la. No Docker Hub existem muitas imagens com todo tipo de máquinas, mas para um ambiente de desenvolvimento muito específico podemos criar nosso próprio modelo para passá-lo a alguém e trabalhar em um contêiner com a mesma configuração que o nosso.

Podemos ver todas as imagens que temos salvas no nosso computador através do comando docker image ls

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Podemos ver os tamanhos, e podemos ver como a de nginx ocupa muito e por isso demorou mais para baixar do que as outras.

Outra coluna que podemos ver é a de TAG, isso indica a versão da imagem. Em todas está escrito latest, o que significa que é a mais recente. Isso significa que, no momento do download, baixamos a última versão disponível no Docker Hub. Isso não é ideal em um ambiente de desenvolvimento, pois podemos baixar uma imagem do Ubuntu sem especificar a versão, por exemplo, a 20.04. Mas depois de algum tempo, alguém pode querer desenvolver com você e baixar essa imagem, mas como a versão não foi especificada, será baixada novamente a mais recente, que no caso pode ser a 22.04. Isso pode levar a problemas e fazer com que algo funcione para uma pessoa e não para a outra.

Podemos ver todas as imagens que há no Docker Hub indo a https://hub.docker.com/. Lá você poderá buscar a imagem que melhor se adapte ao projeto que quiser fazer. Se navegarmos até a imagem do Ubuntu, por exemplo, podemos ver as versões (tags) das imagens.

Vamos a baixar, **mas não executar** uma imagem. Para isso usamos o comando docker pull <hub> <image name>:<tag>. Se não indicarmos o hub, ele será baixado do docker hub por padrão, mas podemos indicar outro, por exemplo um privado da nossa organização. Também, se não indicarmos o tag, por padrão baixará a última versão.

	
!docker pull ubuntu:20.04
Copy
	
20.04: Pulling from library/ubuntu
Digest: sha256:35ab2bf57814e9ff49e365efd5a5935b6915eede5c7f8581e9e1b85e0eecbe16[1A
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04

Se voltarmos a listar as imagens, veremos que agora temos duas imagens do Ubuntu, uma com o tag 20.04 e outra com o tag latest

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Criar imagens através de Dockerfile

Criamos um diretório no host chamado dockerImages para trabalhar nele.

	
!mkdir dockerImages
Copy

Criamos um arquivo Dockerfile com o qual criaremos uma imagem

	
!touch dockerImages/Dockerfile
Copy

Abrimos o arquivo criado com nosso editor preferido e escrevemos o seguinte:

FROM ubuntu:latest

Isso diz ao Docker para criar a imagem a partir da imagem latest do Ubuntu.

A seguir, escrevemos um comando que será executado em tempo de compilação

RUN touch /test.txt

Isso significa que quando o Dockerfile for compilado, esse comando será executado, mas não quando o contêiner da imagem for executado.

No final o Dockerfile fica assim:

FROM ubuntu:latest
      RUN touch /test.txt

Compilamos o Dockerfile através do comando build, com a opção -t podemos dar-lhe um tag. Por último, é necessário indicar a rota do contexto de build, mais tarde explicaremos isso.

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/2 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Successfully built a78cf3ea16d8
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Como podemos ver, a compilação ocorre em 2 etapas, cada uma possui uma id, cada uma dessas ids são camadas da imagem, isso também será abordado posteriormente.

Voltamos a ver as imagens que temos salvias no nosso computador e aparece a que acabamos de criar

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 8 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Executamos o contêiner a partir da imagem que acabamos de criar

$ docker run -it ubuntu:test
root@b57b9d4eedeb:/#

Entramos no bash do contêiner. Como dissemos, o comando RUN é executado durante a compilação da imagem, portanto, o arquivo que pedimos para ser criado deve estar em nosso contêiner.

root@b57b9d4eedeb:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys test.txt tmp usr var

É importante entender que esse arquivo foi criado quando a imagem foi construída, ou seja, a imagem do contêiner já possui esse arquivo. Ele não é criado quando o contêiner é iniciado.

Saimos do contêiner

root@b57b9d4eedeb:/# exit
saída

Já que já temos uma imagem, poderíamos enviá-la para o hub do Docker, mas vamos listar as imagens novamente antes disso.

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 20 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Se vê, está nos dizendo que a imagem que acabamos de criar pertence ao repositório do Ubuntu, mas nós não temos acesso ao repositório do Ubuntu, então no Docker Hub precisamos criar uma conta para poder enviar a imagem ao nosso repositório. No meu caso, meu repositório se chama maximofn, então altero o repositório da imagem usando o comando tag, indicando a imagem que queremos mudar de repositório e o novo repositório. No novo repositório, geralmente indica-se o nome do repositório seguido do tipo de imagem e o tag, no meu caso maximofn/ubuntu:test

	
!docker tag ubuntu:test maximofn/ubuntu:test
Copy

Se agora listarmos as imagens novamente

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
maximofn/ubuntu test a78cf3ea16d8 24 minutes ago 77.8MB
nginx latest 2d389e545974 8 hours ago 142MB
ubuntu latest 2dc39ba059dc 11 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 11 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Agora devemos fazer login no Docker Hub para poder enviar a imagem, para isso usamos o comando login

$ docker login
Faça login com seu Docker ID para enviar e receber imagens do Docker Hub. Se você não tiver um Docker ID, vá até https://hub.docker.com para criar um.
Nome de usuário: maximofn
Senha:

Login bem-sucedido

Agora podemos fazer o upload da imagem através do comando push

	
!docker push maximofn/ubuntu:test
Copy
	
The push refers to repository [docker.io/maximofn/ubuntu]
06994357: Preparing
06994357: Pushed from library/ubuntu test: digest: sha256:318d83fc3c35ff930d695b0dc1c5ad1b0ea54e1ec6e3478b8ca85c05fd793c4e size: 735

Ele apenas carregou a primeira camada, a segunda, como usei a partir da imagem do Ubuntu, o que faz é colocar um ponteiro para essa imagem para não ter camadas carregadas mais de uma vez.

Deve-se ter em conta que este repositório é público, portanto não deve enviar imagens com dados sensíveis. Além disso, se uma imagem não tiver uso em 6 meses, ela será excluída.

O sistema de camadaslink image 137

Com o comando history podemos ver as camadas de uma imagem. Se formos ver as camadas da imagem que acabamos de criar, usamos docker history ubuntu:test

	
!docker history ubuntu:test
Copy
	
IMAGE CREATED CREATED BY SIZE COMMENT
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
&lt;missing&gt; 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

Vemos que a primeira camada tem o comando que introduzimos no Dockerfile, além de dizer que foi criada há 3 minutos. No entanto, o resto das camadas foram criadas há 12 dias, e são as camadas da imagem do Ubuntu na qual nos baseamos.

Ao Dockerfile que criamos anteriormente adicionamos a linha

RUN rm /test.txt

No final o Dockerfile fica assim:

FROM ubuntu:latest
      RUN touch /test.txt
      RUN rm /test.txt

Se recompilarmos, veremos o que acontece

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---&gt; Running in c2e6887f2025
Removing intermediate container c2e6887f2025
---&gt; 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Como podemos ver, há uma camada adicional com a nova linha que adicionamos. Se voltarmos a ver as camadas da imagem com history

	
!docker history ubuntu:test
Copy
	
IMAGE CREATED CREATED BY SIZE COMMENT
313243a9b573 About a minute ago /bin/sh -c rm /test.txt 0B
a78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B
2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
&lt;missing&gt; 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB

Vemos que as primeiras camadas são iguais às de antes e adicionou uma nova camada com o novo comando

Pesquisa no Docker Hublink image 138

Não é necessário entrar na página do Docker Hub para procurar imagens, isso pode ser feito diretamente da terminal. Para isso, usamos o comando docker search <nome da imagem>

	
!docker search ubuntu
Copy
	
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 16425 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 297 [OK]
open-liberty Open Liberty multi-architecture images based… 62 [OK]
neurodebian NeuroDebian provides neuroscience research s… 104 [OK]
ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 52 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 115 [OK]
ubuntu/nginx Nginx, a high-performance reverse proxy &amp; we… 98
ubuntu/squid Squid is a caching proxy for the Web. Long-t… 66
ubuntu/cortex Cortex provides storage for Prometheus. Long… 4
ubuntu/apache2 Apache, a secure &amp; extensible open-source HT… 60
ubuntu/kafka Apache Kafka, a distributed event streaming … 35
ubuntu/mysql MySQL open source fast, stable, multi-thread… 53
ubuntu/bind9 BIND 9 is a very flexible, full-featured DNS… 62
ubuntu/prometheus Prometheus is a systems and service monitori… 51
ubuntu/zookeeper ZooKeeper maintains configuration informatio… 12
ubuntu/postgres PostgreSQL is an open source object-relation… 31
ubuntu/redis Redis, an open source key-value store. Long-… 19
ubuntu/grafana Grafana, a feature rich metrics dashboard &amp; … 9
ubuntu/memcached Memcached, in-memory keyvalue store for smal… 5
ubuntu/dotnet-aspnet Chiselled Ubuntu runtime image for ASP.NET a… 11
ubuntu/dotnet-deps Chiselled Ubuntu for self-contained .NET &amp; A… 11
ubuntu/prometheus-alertmanager Alertmanager handles client alerts from Prom… 9
ubuntu/dotnet-runtime Chiselled Ubuntu runtime image for .NET apps… 10
ubuntu/cassandra Cassandra, an open source NoSQL distributed … 2
ubuntu/telegraf Telegraf collects, processes, aggregates &amp; w… 4

Uso do Docker para criar aplicaçõeslink image 139

Exposição de portaslink image 140

Antes vimos como podíamos vincular um porta de um contêiner a uma porta do computador (-p 8080:80). Mas para que isso seja possível, na hora de criar a imagem é preciso expor a porta, isso se faz adicionando ao Dockerfile a linha EXPOSE <port>, no caso anterior

EXPOSE 80

Ou usar imagens como base que já tenham portas expostas

Reutilização do cache de camadas ao compilarlink image 141

Quando compilamos uma imagem, se alguma das camadas que definimos já foram compiladas antes, o Docker detecta e as usa, não as recompila. Se recompilarmos a imagem que definimos no Dockerfile, agora levará muito pouco tempo, pois todas as camadas já estão compiladas e o Docker não as recompila.

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:latest
---&gt; 2dc39ba059dc
Step 2/3 : RUN touch /test.txt
---&gt; Using cache
---&gt; a78cf3ea16d8
Step 3/3 : RUN rm /test.txt
---&gt; Using cache
---&gt; 313243a9b573
Successfully built 313243a9b573
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Na segunda e terceira camada aparece o texto Using cache

Como isso é um caderno Jupyter, ao executar as células ele fornece a informação do tempo que elas levam para serem executadas. Da última vez que compilei a imagem, demorou 1,4 segundos, enquanto agora levou 0,5 segundos.

Mas se agora eu mudar o Dockerfile, e na primeira linha, onde dizia que estávamos baseados na versão mais recente do Ubuntu, mudamos para a versão 20.04

FROM ubuntu:20.04

No final o Dockerfile fica assim:

FROM ubuntu:20.04
      RUN touch /test.txt
      RUN rm /test.txt

Se recompilarmos, levará muito mais tempo.

	
!docker build -t ubuntu:test ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---&gt; a0ce5a295b63
Step 2/3 : RUN touch /test.txt
---&gt; Running in a40fe8df2c0d
Removing intermediate container a40fe8df2c0d
---&gt; 0bb9b452c11f
Step 3/3 : RUN rm /test.txt
---&gt; Running in 2e14919f3685
Removing intermediate container 2e14919f3685
---&gt; fdc248fa833b
Successfully built fdc248fa833b
Successfully tagged ubuntu:test
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Demorou 1,9 segundos e o texto Using cache não aparece mais.

Ao alterar a primeira camada, o Docker recompila todas as camadas. Isso pode ser um problema porque, durante o desenvolvimento de código, pode ocorrer o seguinte caso

  • Desenvolvemos o código em nosso computador
  • Ao construir a imagem, copiamos todo o código do nosso computador para o contêiner
  • Em seguida, pedimos à imagem para instalar as bibliotecas necessárias

Isso pode fazer com que ao alterar qualquer parte do código, ao ter que recompilar a imagem, a camada na qual as bibliotecas são instaladas precise ser recompilada novamente, pois uma camada anterior foi alterada.

Para resolver isso, a ideia seria que na hora de criar a imagem, primeiro peçamos que se instalem as bibliotecas e depois que se copie o código do nosso computador para o contêiner. Assim, cada vez que alterarmos o código e recompilarmos a imagem, apenas será recompilada a camada em que se copia o código, portanto, a compilação será mais rápida

Você pode pensar que é melhor compartilhar uma pasta entre o host e o contêiner (bind mount) onde teremos o código e assim não será necessário recompilar a imagem toda vez que alterarmos o código. E a resposta é que é verdade, coloquei apenas este exemplo porque é muito fácil de entender, mas é para ilustrar que na hora de criar imagens é preciso pensar bem de forma que, se for necessário recompilá-la, recompile o menor número possível de camadas.

Escrever corretamente um Dockerfilelink image 142

Como vimos, o Docker não recompila as camadas de um Dockerfile se já as tiver compilado anteriormente, portanto, as carrega do cache. Vamos ver como deve ser a forma correta de escrever um Dockerfile para aproveitar isso.

Vamos a partir deste Dockerfile para ir comentando possíveis correções

DE ubuntu
      COPY ./sourceCode /sourceCode
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Como pode ser visto, parte-se de uma imagem do Ubuntu, copia-se a pasta com o código, atualizam-se os repositórios, instala-se o Python, instala-se também o SSH e executa-se a aplicação.

Copiar o código antes da execuçãolink image 143

Como dissem antes, se primeiro copiarmos o código e depois instalarmos o Python, cada vez que fizermos uma mudança no código e compilarmos a imagem, ela será compilada inteira, mas se copiarmos o código após instalar o Python, cada vez que alterarmos o código e compilarmos a imagem, apenas compilará a partir da cópia do código e não reinstalará o Python, por isso o Dockerfile deveria ficar assim

DE ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      COPY ./sourceCode /sourceCode
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Copiar apenas o código necessáriolink image 144

Estamos copiando a pasta com todo o código, mas talvez dentro temos código que não precisamos, por isso é necessário copiar apenas o código que realmente precisamos para a aplicação, desta forma a imagem ocupará menos memória. De modo que o Dockerfile ficaria assim

DE ubuntu
      RUN apt-get update
      RUN apt-get install -y python3 ssh
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Atualizar repositórios e instalar python na mesma linhalink image 145

Estamos atualizando os repositórios em uma linha e em outra instalando python3.

DE ubuntu
      RUN apt-get update && apt-get install -y python3 ssh
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Não instalar sshlink image 146

Havíamos instalado ssh na imagem para poder depurar em caso de precisar, mas isso faz com que a imagem ocupe mais memória. Em caso de precisarmos depurar, deveríamos entrar no contêiner, instalar ssh e, em seguida, depurar. Por isso, removemos a instalação do ssh.

DE ubuntu
      RUN apt-get update && apt-get install -y python3
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Usar --no-install-recommends

Quando instalamos algo no Ubuntu, ele instala pacotes recomendados, mas que não precisamos, por isso a imagem ocupa mais espaço. Então, para evitá-lo, adicionamos à instalação --no-install-recommends

DE ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Apagar lista de repositórios atualizadoslink image 147

Nós atualizamos a lista de repositórios e instalamos o python, mas uma vez feito, já não precisamos da lista de repositórios atualizados, pois tudo o que farão é aumentar o tamanho da imagem, então os removemos após instalar o python e na mesma linha.

DE ubuntu
      RUN apt-get update && apt-get install -y python3 --no-install-recommends && rm -rf /var/lib/apt/lists/*
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Usar uma imagem de Pythonlink image 148

Tudo o que fizemos para atualizar a lista de pacotes e instalar o Python não é necessário, já que existem imagens do Python baseadas no Ubuntu, que com certeza seguiram boas práticas e podem até ter feito melhor do que nós, além de ter sido escaneada em busca de vulnerabilidades pelo Docker Hub. Portanto, removemos tudo isso e partimos de uma imagem do Python.

DE python
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Especificar a imagem do Pythonlink image 149

Ao não especificar a imagem do Python, está sendo baixada a última, mas dependendo de quando você construir o contêiner, pode ser baixada uma ou outra, por isso é necessário adicionar a tag com a versão do Python desejada.

FROM python:3.9.18
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Escolher uma tag pequenalink image 150

Escolhemos a tag 3.9.18, mas essa versão do Python tem um monte de bibliotecas que provavelmente não precisamos, então podemos usar as versões 3.9.18-slim que têm muitas menos bibliotecas instaladas, ou a versão 3.9.18-alpine que é uma versão do Python baseada no Alpine e não no Ubuntu. O Alpine é uma distribuição Linux muito leve que tem muito poucos pacotes instalados e que costuma ser usada bastante em contêineres Docker para ocupar muito pouco espaço.

A imagem de python 3.9.18 ocupa 997 MB, a 3.9.18-slim ocupa 126 MB e a 3.9.18-alpine ocupa 47,8 MB

FROM python:3.9.18-alpine
      COPY ./sourceCode/sourceApp /sourceCode/sourceApp
      CMD ["python3", "/sourceCode/sourceApp/app.py"]

Indique o workspacelink image 151

Em vez de indicar o caminho da imagem /sourceCode/sourceApp, estabelecemos que esse caminho seja o workspace da imagem. Assim, quando copiarmos o código ou executarmos a aplicação, não é necessário indicar o caminho.

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Indique o workspacelink image 152

Em vez de indicar o caminho da imagem /sourceCode/sourceApp, estabelecemos que esse caminho seja o workspace da imagem. Assim, quando copiarmos o código ou executarmos a aplicação, não é necessário indicar o caminho.

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Código compartilhado em uma pasta bind mount

Havíamos criado uma pasta chamada dockerHostFolder na qual havíamos compartilhado arquivos entre o host e um contêiner. Dentro, além disso, deveriam haver três arquivos.

	
!ls dockerHostFolder
Copy
	
bindFile.txt fileExtract.txt text.txt

Vamos aproveitar o arquivo text.txt para ver isso. Vamos ver o que há dentro de text.txt

	
!cat dockerHostFolder/text.txt
Copy

Não há saída, o arquivo está vazio. Vamos criar novamente um contêiner do Ubuntu compartilhando a pasta dockerHostFolder.

	
!docker run --name alwaysup -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/null
Copy
	
24adbded61f507cdf7f192eb5e246e43ee3ffafc9944b7c57918eb2d547dff19

Vemos que o contêiner está rodando

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
24adbded61f5 ubuntu "tail -f /dev/null" 16 seconds ago Up 15 seconds alwaysup

Entramos no contêiner, vemos que está text.txt e que está vazio

$ docker exec -it alwaysup bash
root@24adbded61f5:/# ls dockerContainerFolder/
bindFile.txt fileExtract.txt text.txt
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
root@24adbded61f5:/#

Agora abrimos no host o arquivo text.txt com o editor de textos que quisermos, escrevemos Hola mundo e salvamos. Se agora vemos o que há dentro do arquivo no contêiner, veremos o mesmo texto.

root@24adbded61f5:/# cat dockerContainerFolder/text.txt
Olá mundo

Agora editamos o arquivo no contêiner e saímos do contêiner

root@24adbded61f5:/# echo olá contêiner > dockerContainerFolder/text.txt
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
olá contêiner
root@24adbded61f5:/# exit
saída

Se olharmos para o arquivo no host, veremos o texto que escrevemos no contêiner

	
!cat dockerHostFolder/text.txt
Copy
	
hola contenedor

Apagamos o contêiner

	
!docker rm -f alwaysup
Copy
	
alwaysup

Conectar contentores por redelink image 153

Em caso de quisermos ter vários contêineres rodando e querermos que eles se comuniquem, podemos fazer com que se comuniquem por rede. O Docker nos dá a possibilidade de fazer isso através de suas redes virtuais.

Vamos ver quais redes o Docker tem através do comando docker network ls

	
!docker network ls
Copy
	
NETWORK ID NAME DRIVER SCOPE
de6e8b7b737e bridge bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

Vemos que por padrão o Docker tem três redes

  • bridge: Está por retrocompatibilidade com versões anteriores, mas não deveríamos usá-la mais
  • host: É a rede do host
  • none: Esta é a opção que devemos usar se quisermos que um contêiner não tenha acesso à internet

Podemos criar novas redes às quais outros contêineres possam se conectar, para isso usamos o comando docker network create <name>, para que outros contêineres possam se conectar também devemos adicionar a opção --attachable

	
!docker network create --attachable myNetwork
Copy
	
2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27

Podemos inspecioná-la através do comando docker network inspect <name>

	
!docker network inspect myNetwork
Copy
	
[
{
"Name": "myNetwork",
"Id": "2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27",
"Created": "2022-09-14T15:20:08.539830161+02:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

Agora temos que criar dois contêineres para que eles possam se comunicar.

Vamos a criar um novo contêiner, que chamaremos de container1, com uma pasta compartilhada e que dentro dele se chamará folder1

	
!docker run --name container1 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder1 ubuntu tail -f /dev/null
Copy
	
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a

Agora criamos outro contêiner, chamado container2, com outra pasta compartilhada, mas que se chame folder2

	
!docker run --name container2 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder2 ubuntu tail -f /dev/null
Copy
	
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9

Vemos os contêineres rodando e vemos que estão os dois

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c8dc1831548 ubuntu "tail -f /dev/null" 3 seconds ago Up 2 seconds container2
a5fca8ba1e4f ubuntu "tail -f /dev/null" 4 seconds ago Up 3 seconds container1

Agora temos que conectar os contêineres à rede, para isso usamos o comando docker network connect <network name> <container name>

	
!docker network connect myNetwork container1
Copy
	
!docker network connect myNetwork container2
Copy

Para verificar que foram conectados corretamente, podemos inspecionar a rede, mas filtrando pelos contêineres conectados.

$ docker network inspect --format '{{.Containers}}' myNetwork
mapa
[
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9:
{
container2
f828d211e894f7a5a992ce41a2a0def8e2424e9737fb4e1485fc09cc2d607b69
02:42:ac:12:00:03
172.18.0.3/16
}
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a:
{
container1
cff762e6286ebc169804b2a675bbff904102de796751d367c18d4b490c994c45
02:42:ac:12:00:02
172.18.0.2/16
}
]

Como podemos ver o contentor container1 tem o IP 172.18.0.2 e o contentor container2 tem o IP 172.18.0.3

Entramos no contêiner container1 e instalamos ping

$ docker exec -it container1 bash
root@a5fca8ba1e4f:/# apt update
...
root@a5fca8ba1e4f:/# apt install iputils-ping
Claro, por favor proporciona el texto markdown que deseas que traduzca al portugés.
root@a5fca8ba1e4f:/#

Entramos no contêiner container2 e instalamos ping

$ docker exec -it container2 bash
root@a5fca8ba1e4f:/# apt update
...
root@a5fca8ba1e4f:/# apt install iputils-ping
...
root@a5fca8ba1e4f:/#

Agora do contêiner container1 fazemos um ping ao IP 172.18.0.3, que pertence ao contêiner container2

root@a5fca8ba1e4f:/# ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes de dados.
64 bytes de 172.18.0.3: icmp_seq=1 ttl=64 tempo=0.115 ms
64 bytes de 172.18.0.3: icmp_seq=2 ttl=64 time=0.049 ms
64 bytes de 172.18.0.3: icmp_seq=3 ttl=64 tempo=0.056 ms
64 bytes de 172.18.0.3: icmp_seq=4 ttl=64 tempo=0,060 ms
^C
--- estatísticas do ping para 172.18.0.3 ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3068ms
rtt min/avg/max/mdev = 0,049/0,070/0,115/0,026 ms

E do contêiner container2 fazemos um ping ao IP 172.18.0.2, que pertence ao contêiner container1

root@6c8dc1831548:/# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes de dados.
64 bytes de 172.18.0.2: icmp_seq=1 ttl=64 time=0.076 ms
64 bytes de 172.18.0.2: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes de 172.18.0.2: icmp_seq=3 ttl=64 time=0.049 ms
64 bytes de 172.18.0.2: icmp_seq=4 ttl=64 time=0.051 ms
^C
--- estatísticas de ping para 172.18.0.2 ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3074ms
rtt min/avg/max/mdev = 0,045/0,055/0,076/0,012 ms

Mas há uma coisa melhor que nos permite fazer com o Docker: se eu não souber o IP do contêiner ao qual quero me conectar, em vez de escrever seu IP, posso escrever seu nome.

Agora, do contêiner container1, fazemos um ping ao IP de container2.

root@a5fca8ba1e4f:/# ping container2
PING container2 (172.18.0.3) 56(84) bytes de dados.
64 bytes de container2.myNetwork (172.18.0.3): icmp_seq=1 ttl=64 time=0.048 ms
64 bytes de container2.myNetwork (172.18.0.3): icmp_seq=2 ttl=64 time=0.050 ms
64 bytes de container2.myNetwork (172.18.0.3): icmp_seq=3 ttl=64 time=0,052 ms
64 bytes de container2.myNetwork (172.18.0.3): icmp_seq=4 ttl=64 tempo=0,053 ms
^C
--- estatísticas do ping do container2 ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3071ms
rtt min/avg/max/mdev = 0,048/0,050/0,053/0,002 ms

Como vemos, o Docker sabe que o IP do contêiner container2 é a 172.18.0.3

E do contêiner container2 fazemos um ping ao IP de container1

root@6c8dc1831548:/# ping container1
PING container1 (172.18.0.2) 56(84) bytes de dados.
64 bytes de container1.myNetwork (172.18.0.2): icmp_seq=1 ttl=64 tempo=0.051 ms
64 bytes de container1.myNetwork (172.18.0.2): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes de container1.myNetwork (172.18.0.2): icmp_seq=3 ttl=64 time=0,052 ms
64 bytes de container1.myNetwork (172.18.0.2): icmp_seq=4 ttl=64 time=0,056 ms
^C
--- estatísticas do container1 ping ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3057ms
rtt min/avg/max/mdev = 0,051/0,054/0,058/0,003 ms

Como vemos, o Docker sabe que o IP do contêiner container1 é a 172.18.0.2

Saimos dos contêineres e os apagamos

	
!docker rm -f container1 container2
Copy
	
container1
container2

Apagamos também a rede que criamos

	
!docker network rm myNetwork
Copy
	
myNetwork

Uso de GPUslink image 154

Para poder usar as GPUs do host dentro dos contêineres Docker, é necessário seguir os passos descritos na página de instalação do Nvidia container toolkit.

Configurar o repositório e a chave GPGlink image 155

Temos que configurar o repositório do nvidia container toolkit e a chave GPG. Para isso, execute o seguinte comando no terminal:

distribuição=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

Instalação do nvidia container toolkit

Uma vez atualizamos o repositório e a chave, atualizamos os repositórios por meio do comando

sudo apt update

E instalamos nvidia container toolkit

sudo apt install -y nvidia-docker2

Reinício do Dockerlink image 156

Uma vez que tenhamos terminado, temos que reiniciar o demônio do Docker através de

sudo systemctl restart docker

Uso de GPUslink image 157

Agora que configuramos o Docker para poder usar as GPUs do host dentro dos contêineres, podemos testá-lo através da opção --gpus all. Se houver mais de uma GPU e quiser usar apenas 1, seria necessário especificá-la, mas por enquanto aqui explicamos apenas como usar todas.

Criamos um contêiner que não será executado em segundo plano, mas sim irá executar o comando nvidia-smi para que possamos verificar se ele tem acesso às GPUs.

	
!docker run --name container_gpus --gpus all ubuntu nvidia-smi
Copy
	
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6a12be2b: Pull complete .54MB/29.54MBBDigest: sha256:aabed3296a3d45cede1dc866a24476c4d7e093aa806263c27ddaadbdce3c1054
Status: Downloaded newer image for ubuntu:latest
Mon Sep 4 07:10:36 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.39.01 Driver Version: 510.39.01 CUDA Version: 11.6 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 Quadro T1000 On | 00000000:01:00.0 Off | N/A |
| N/A 44C P0 15W / N/A | 9MiB / 4096MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 2545 G 4MiB |
| 0 N/A N/A 3421 G 4MiB |
+-----------------------------------------------------------------------------+

Apagamos o contêiner

	
!doker rm container_gpus
Copy

Docker composelink image 158

Docker compose vs docker-composelink image 159

docker-compose foi uma ferramenta criada para ajudar no mantenimiento de imagens e contêineres, e era necessário instalá-la separadamente do Docker. No entanto, o Docker incorporou essa funcionalidade em suas últimas versões, e agora não é mais necessário instalá-la separadamente. Para usá-la, em vez de usar o comando docker-compose, você deve usar o comando docker compose. Em muitos lugares, você encontrará informações usando docker-compose, mas ao instalar o Docker, o docker compose já virá instalado, portanto tudo que podia ser feito com docker-compose é compatível com docker compose.

Docker composelink image 160

Docker Compose é uma ferramenta do Docker que faz tudo o que vimos até agora, mas nos economizando tempo e esforço. Editando um arquivo .yml, podemos dizer ao Docker Compose para criar todos os contêineres que quisermos.

Para usar uma vez não haverá muita diferença entre escrever todos os comandos que vimos antes ou escrever o arquivo .yml, mas quando quiser ter a mesma configuração de contêineres funcionando novamente, basta chamar o arquivo .yml e ele recriará toda a configuração.

Vamos a criar uma pasta onde armazenaremos os arquivos do Docker Compose

	
!mkdir dockerComposeFiles
Copy

Criamos o arquivo .yml dentro

	
!touch dockerComposeFiles/docker-compose.yml
Copy

Um arquivo Docker Compose deve começar com a versão

versão: "<v.v>"

No momento de escrever isso, a última versão é a 3.8 então escrevemos essa

*docker-compose.yml*:

versão: "3.8"

A seguir estão indicados os serviços, que são os contentores. Em cada serviço tem de especificar a imagem e, além disso, podem ser adicionados outros parâmetros como portas, variáveis de ambiente, etc.

serviços:
      container1:
      image: ubuntu
          
      container2:
      image: ubuntu

O docker-compose.yml ficaria assim:

versão: "3.8"
      
      serviços:
      container1:
      imagem: ubuntu
          
      container2:
      image: ubuntu

Uma vez que temos criado o arquivo, em seu caminho, podemos executar tudo através do comando docker compose up, mas adicionando a opção -d faremos com que ele rode em segundo plano.

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠿ Network dockercomposefiles_default Created 0.1s
⠋ Container dockercomposefiles-container2-1 Creating 0.0s
⠋ Container dockercomposefiles-container1-1 Creating 0.0s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠙ Container dockercomposefiles-container2-1 Creating 0.1s
⠙ Container dockercomposefiles-container1-1 Creating 0.1s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.5s
[+] Running 2/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.6s
[+] Running 3/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Started 0.7s

Se podemos observar, ele criou dois contentores dockercomposefiles-container1-1 e dockercomposefiles-container2-1 e a rede que os conecta dockercomposefiles_default

Vamos a apagar os dois contêineres

	
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copy
	
dockercomposefiles-container1-1
dockercomposefiles-container2-1

E apagamos a rede que foi criada

	
!docker network rm dockercomposefiles_default
Copy
	
dockercomposefiles_default

Vamos tentar fazer o que fizemos anteriormente com o que sabemos até agora. Vamos criar uma nova imagem que venha com ping instalado.

*Arquivo Dockerfile*:

FROM ubuntu:20.04
      RUN apt update
      RUN apt install iputils-ping -y

E a compilamos

	
!docker build -t ubuntu:ping ./dockerImages
Copy
	
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---&gt; a0ce5a295b63
Step 2/3 : RUN apt update
---&gt; Running in 3bd5278d39b4
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
Get:3 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [898 kB]
Get:4 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:5 http://archive.ubuntu.com/ubuntu focal-backports InRelease [108 kB]
Get:6 http://archive.ubuntu.com/ubuntu focal/universe amd64 Packages [11.3 MB]
Get:7 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [2133 kB]
Get:8 http://security.ubuntu.com/ubuntu focal-security/multiverse amd64 Packages [27.5 kB]
Get:9 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [1501 kB]
Get:10 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages [1275 kB]
Get:11 http://archive.ubuntu.com/ubuntu focal/restricted amd64 Packages [33.4 kB]
Get:12 http://archive.ubuntu.com/ubuntu focal/multiverse amd64 Packages [177 kB]
Get:13 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [2594 kB]
Get:14 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 Packages [1613 kB]
Get:15 http://archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 Packages [30.2 kB]
Get:16 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [1200 kB]
Get:17 http://archive.ubuntu.com/ubuntu focal-backports/universe amd64 Packages [27.4 kB]
...
Successfully built c3d32aa9de02
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Verificamos que foi criado

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test a78cf3ea16d8 25 hours ago 77.8MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Mudamos o tag

	
!docker tag ubuntu:ping maximofn/ubuntu:ping
Copy
	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test c3d32aa9de02 About a minute ago 112MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Editamos o arquivo Docker Compose para que utilize as imagens que acabamos de criar

*docker-compose.yml*:

versão: "3.8"
      
      serviços:
      container1:
      image: maximofn/ubuntu:ping
      
      container2:
      image: maximofn/ubuntu:ping

E além disso, dizemos para executar uma não operação

O docker-compose.yml ficaria assim:

versão: "3.8"
      
      serviços:
      container1:
      imagem: ubuntu
      command: tail -f /dev/null
      
      container2:
      imagem: ubuntu
      command: tail -f /dev/null

O levantamos

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠇ Container dockercomposefiles-container2-1 Recreate 0.9s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠏ Container dockercomposefiles-container2-1 Recreate 1.0s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 1.1s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠙ Container dockercomposefiles-container2-1 Recreate 1.2s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠹ Container dockercomposefiles-container2-1 Recreate 1.3s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠸ Container dockercomposefiles-container2-1 Recreate 1.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
...
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s

Vemos os contêineres que estão rodando

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
935939e5a75d maximofn/ubuntu:ping "tail -f /dev/null" 15 seconds ago Up 13 seconds dockercomposefiles-container2-1
f9138d7064dd maximofn/ubuntu:ping "tail -f /dev/null" 25 seconds ago Up 13 seconds dockercomposefiles-container1-1

Os dois contêineres estão em execução, agora vamos entrar em um deles e tentar fazer ping no outro.

$ docker exec -it dockercomposefiles-container1-1 bash
root@f9138d7064dd:/# ping dockercomposefiles-container2-1
PING dockercomposefiles-container2-1 (172.21.0.3) 56(84) bytes de dados.
64 bytes de dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=1 ttl=64 tempo=0,110 ms
64 bytes de dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes de dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=3 ttl=64 time=0.049 ms
64 bytes de dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=4 ttl=64 tempo=0,075 ms
^C
--- estatísticas do ping do container2-1 do dockercomposefiles ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3068ms
rtt min/avg/max/mdev = 0,049/0,070/0,110/0,025 ms

Como vemos, podemos fazer ping, criamos a imagem com ping instalado. Além disso, no docker-compose fizemos com que seja executada uma operação nula para que os contêineres continuem em execução.

Apagamos os dois contêineres e a rede que criamos

	
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copy
	
dockercomposefiles-container1-1
dockercomposefiles-container2-1
	
!docker network rm dockercomposefiles_default
Copy
	
dockercomposefiles_default

Como o Docker Compose nomeia os contêinereslink image 161

Se nós observarmos, os contêineres criados pelo Docker se chamam dockercomposefiles-container1-1 e dockercomposefiles-container2-1. Isso ocorre porque a pasta em que está o arquivo do Docker Compose está dentro de uma pasta chamada dockerComposeFiles, por isso a primeira parte do nome dos contêineres é dockercomposefiles, seguida pelo nome do serviço que demos no arquivo do Docker Compose (container1 e container2) e, por fim, um número para poder criar mais se necessário.

O mesmo ocorre com o nome da rede que foi criada dockercomposefiles_default

Logs no docker composelink image 162

Vamos agora a mudar o arquivo Docker Compose, nas linhas em que tínhamos command: tail -f /dev/null, vamos colocar command: ping 0.0.0.0

E além disso, dizemos para ele executar uma não operação.

O docker-compose.yml ficaria assim:

versão: "3.8"
      
      serviços:
      container1:
      imagem: ubuntu
      comando: ping 0.0.0.0
      
      container2:
      imagem: ubuntu
      comando: ping 0.0.0.0

Isso fazemos para que cada contêiner esteja enviando o ping constantemente, assim simulamos alguns logs

Se executarmos novamente o docker compose

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container1-1 Recreate 0.2s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container1-1 Recreate 0.3s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container1-1 Recreate 0.4s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container1-1 Recreate 0.5s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container1-1 Recreate 0.6s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container1-1 Recreate 0.7s
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container1-1 Recreate 0.8s
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
[+] Running 0/2
...
⠿ Container dockercomposefiles-container1-1 Starting 11.0s
⠿ Container dockercomposefiles-container2-1 Started 11.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 11.1s
⠿ Container dockercomposefiles-container2-1 Started 11.0s

Agora podemos ver os logs dos dois contêineres através do comando docker compose logs

	
!cd dockerComposeFiles && docker compose logs
Copy
	
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=214 ttl=64 time=0.015 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=215 ttl=64 time=0.021 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=216 ttl=64 time=0.020 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=217 ttl=64 time=0.049 ms

Como vemos, podemos ver os logs dos dois contêineres, mas no caso de querer ver apenas os de um, podemos especificar o **nome do serviço**

	
!cd dockerComposeFiles && docker compose logs container1
Copy
	
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.023 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.022 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.024 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.034 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=332 ttl=64 time=0.027 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=333 ttl=64 time=0.030 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=334 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=335 ttl=64 time=0.036 ms
	
!cd dockerComposeFiles && docker compose logs container2
Copy
	
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=23 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=24 ttl=64 time=0.037 ms
...
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=340 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=341 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=342 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=343 ttl=64 time=0.036 ms

Se quisermos ver os logs continuamente, podemos adicionar a opção -f: docker compose logs -f <service name>

Se eu criei um docker compose com mais de dois serviços, quando se quiser ver os logs de vários serviços basta adicionar mais nomes ao comando, docker compose logs <nome serviço 1> <nome serviço 2> ...

Exec serviçoslink image 163

Como vimos, através do comando exec podemos entrar em um contêiner indicando o nome do contêiner, o comando que se deseja executar e a opção -it. Com Docker Compose isso é mais simples, pois apenas é necessário o nome do serviço e o comando, mas não é necessária a opção -it, já que o Docker Compose assume essa opção por padrão.

$ docker compose exec container1 bash
root@a7cf282fe66c:/#

Parando docker composelink image 164

Quando terminamos de trabalhar, com um único comando (stop), o Docker Compose para tudo, não é necessário parar um a um cada contêiner.

	
!cd dockerComposeFiles && docker compose stop
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠿ Container dockercomposefiles-container1-1 Stopped 10.4s

Como se pode ver, o Docker Compose parou os dois contêineres, mas não os excluiu, nem excluiu a rede.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e6c1dd9adb2 maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container2-1
a7cf282fe66c maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container1-1
	
!docker network ls
Copy
	
NETWORK ID NAME DRIVER SCOPE
13cc632147f3 bridge bridge local
d4a2f718cd83 dockercomposefiles_default bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

Docker compose como ferramenta de desenvolvimentolink image 165

Assim como vimos antes, para poder desenvolver, o ideal seria compartilhar a pasta que tem o código com o serviço. Isso com docker compose se faz adicionando a etiqueta volumes ao arquivo docker compose. Primeiro temos que adicionar a rota da pasta onde está o código no host e depois a rota no contêiner.

*docker-compose.yml*:

versão: "3.8"
      
      serviços:
      container1:
      image: ubuntu
      comando: ping 0.0.0.0
      volumes:
      - ../dockerHostFolder/:/dockerContainerFolder
      
      container2:
      image: ubuntu
      comando: ping 0.0.0.0

Como se pode ver, o caminho da pasta do host coloquei relativo

Se subirmos o Docker Compose

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠿ Container dockercomposefiles-container2-1 Created 0.0s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Started 0.6s

Se entrarmos no contêiner, podemos ver o que há dentro do arquivo text.txt

$ docker compose exec container1 bash
root@c8aae9d619d3:/# ls dockerContainerFolder/
bindFile.txt fileExtract.txt text.txt
root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt
olá contêiner

Se agora o abrirmos no host, escrevemos hola host e voltamos a ver no contêiner

root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt
olá host

E agora ao contrário, se o modificarmos no contêiner

root@c8aae9d619d3:/# echo olá compose > dockerContainerFolder/text.txt
root@c8aae9d619d3:/# exit
sair

Se o vemos do host, devemos obter hola compose

	
!cat dockerHostFolder/text.txt
Copy
	
hola compose

Exposição de portas no docker composelink image 166

Também podemos configurar as portas no arquivo do Docker Compose, através da etiqueta ports, indicando a porta do host e em seguida o IP do serviço

portos:
      - <porta do host>:<porta do serviço>

Docker compose em equipe - docker overridelink image 167

Se formos um grupo de pessoas desenvolvendo com Docker e Docker Compose, é provável que muitas pessoas estejam alterando o arquivo Docker Compose, o que pode fazer com que não se sincronizem bem e haja conflitos.

Para resolver isso, o Docker oferece uma ferramenta chamada Docker Override. Dessa forma, pode haver um arquivo Docker Compose base e cada um pode modificá-lo por meio do Docker Override.

Para fazer isso, agora temos que criar um arquivo chamado docker-compose.override.yml que será o que poderemos editar

	
!touch dockerComposeFiles/docker-compose.override.yml
Copy

Se agora tentarmos subir o Docker Compose, vamos receber um erro.

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
Top-level object must be a mapping

E isso porque o Docker Compose detectou que há um arquivo chamado docker-compose.override.yml e que está vazio, então vamos editá-lo. O arquivo docker-compose.override.yml serve para editar o arquivo docker-compose.yml, então, se quisermos fazer uma alteração no serviço container2 para adicionar um volume, escreveríamos assim o arquivo docker-compose.override.yml

*docker-compose.override.yml*:

versão: "3.8"
      
      serviços:
      container2:
      volumes:
      - ../dockerHostFolder/:/dockerOverrideFolder

Note que a pasta compartida no serviço foi chamada de dockerOverrideFolder, então vamos iniciar o docker compose e verificar se vemos essa pasta no contêiner container2.

	
!cd dockerComposeFiles && docker compose up -d
Copy
	
[+] Running 1/0
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Starting 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Started 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s

Vemos que levou 10 segundos para iniciar o serviço container2, isso se deve ao fato de estar aplicando as alterações.

$ docker compose exec container2 bash
root@d8777a4e611a:/# ls dockerOverrideFolder/
bindFile.txt fileExtract.txt text.txt
root@d8777a4e611a:/# cat dockerOverrideFolder/text.txt
olá compose
root@d8777a4e611a:/# exit
saída

Descemos o Compose e apagamos os contêineres e a rede criada

	
!cd dockerComposeFiles && docker compose down
Copy
	
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
⠸ Container dockercomposefiles-container2-1 Stopping 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removing 10.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠋ Network dockercomposefiles_default Removing 0.1s
[+] Running 3/3
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠿ Network dockercomposefiles_default Removed 0.2s

Neste caso, apenas com down o Docker Compose parou e removeu tudo, pois, como podemos ver nos contêineres e na rede, está escrito Removed

Docker compose reiniciarlink image 168

Ao escrever um docker compose, podemos adicionar a tag restart para que, se o contêiner cair, ele seja reiniciado automaticamente.

restart: sempre

Desta forma, se o contêiner cair, ele será reiniciado automaticamente. Se quisermos que ele seja reiniciado apenas um número de vezes, podemos adicionar a opção on-failure.

restart: on-failure:<número>

Agora o contêiner será reiniciado um número de vezes, mas se cair mais vezes, não será reiniciado. Se quisermos que seja reiniciado sempre, podemos adicionar a opção unless-stopped.

restart: unless-stopped

Agora o contêiner será reiniciado sempre, a menos que seja parado manualmente.

Docker avançadolink image 169

Gerenciar ambiente de trabalholink image 170

Remoção de contêineres desligadoslink image 171

Depois de um tempo desenvolvendo, podemos ter vários contêineres desligados, mas armazenados no computador. Isso acaba ocupando memória, então com docker container prune podemos eliminar todos os que estão parados.

	
!docker run ubuntu
Copy
	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
effcee24f54a ubuntu "bash" 37 seconds ago Exited (0) 36 seconds ago musing_rosalind
$ docker container prune
AVISO! Isso removerá todos os contêineres parados.
Tem certeza de que deseja continuar? [s/N] s
Contêineres Excluídos:
effcee24f54aab22e34fdea2465b3b7af132d8c627e5432ba3e915a370876977

Espaço total recuperado: 0B

Neste caso, economizamos 0 bytes, mas no deixar contêineres desligados após muito desenvolvimento com certeza resultará em maior economia de memória.

Exclusão de todos os contêinereslink image 172

Em caso de ter contêineres em execução, podemos eliminar todos os contêineres por meio de outro comando

O comando docker ps -q nos retorna o ID de todos os contêineres, portanto, com o comando docker rm -f $(docker ps -aq) pararemos e apagaremos todos.

	
!docker run -d ubuntu tail -f /dev/null
Copy
	
c22516186ef7e3561fb1ad0d508a914857dbc61274a218f297c4d80b1fc33863
	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c22516186ef7 ubuntu "tail -f /dev/null" About a minute ago Up About a minute agitated_knuth
	
!docker rm -f $(docker ps -aq)
Copy
	
c22516186ef7
	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Exclusão de tudolink image 173

Como vimos, o Docker também cria redes, imagens, volumes, etc, então com o comando docker system prune podemos apagar todos os contêineres parados, todas as redes que não estejam sendo usadas por pelo menos um contêiner, as imagens duplicadas, e o que estiver duplicado no cache de compilação.

$ docker system prune
AVISO! Isso removerá:
- todos os contêineres parados
- todas as redes não utilizadas por pelo menos um contêiner
- todas as imagens pendentes
- todos os caches de construção pendentes

Tem certeza de que deseja continuar? [s/N] s
Espaço total recuperado: 0B

Assim como antes, não se economizou muito espaço, mas após muito tempo desenvolvendo, a economia será considerável.

Uso de recursos do host por parte dos contêinereslink image 174

Por exemplo, ao criar um contêiner, podemos limitar a RAM que o host pode usar através da opção --memory

	
!docker run -d --memory 1g ubuntu tail -f /dev/null
Copy
	
d84888eafe531831ef8915d2270422365adec02678122bf59580e2da782e6972

Mas com docker ps não temos acesso aos recursos que o contêiner está consumindo.

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d84888eafe53 ubuntu "tail -f /dev/null" 35 seconds ago Up 34 seconds musing_ritchie

Para isso temos o comando docker stats

$ docker stats
ID DO CONTAINER NOME CPU % USO DE MEMÓRIA / LIMITE MEM % REDE I/O BLOCO I/O PIDS
d84888eafe53 musing_ritchie 0,00% 540KiB / 1GiB 0,05% 5,62kB / 0B 0B / 0B 1

Isso é muito útil se quisermos simular um ambiente com um limite de RAM

Parando contêineres corretamente: SHELL vs EXEClink image 175

Como explicamos, quando atribuímos um processo a um contêiner, quando esse processo termina, o contêiner para, mas às vezes podemos nos deparar com problemas com isso. Vamos criar uma nova pasta chamada Dockerfile_loop

	
!mkdir Dockerfile_loop
Copy

Agora vamos a criar um arquivo chamado loop.sh dentro de Dockerfile_loop

	
!touch Dockerfile_loop/loop.sh
Copy

E vamos a escrever o seguinte dentro de loop.sh

#!/usr/bin/env bash
      trap "exit 0" SIGTERM
      while true; do :; done

Se eu executar este script no host, ele será executado até que eu insira CTRL+C

./loop
^C

Por favor, forneça o texto em markdown que você gostaria que eu traduzisse para o português.

Agora vamos a criar um arquivo Dockerfile dentro de Dockerfile_loop

	
!touch Dockerfile_loop/Dockerfile
Copy

*Arquivo Dockerfile*:

DE ubuntu:trusty
      COPY ["loop.sh", "/"]
      CMD /loop.sh

Vamos a criar uma imagem baseada no Ubuntu que copia o script para dentro e o executa, e o script é executado até receber o sinal SIGTERM do sistema operacional. Compilamos a imagem

	
!docker build -t ubuntu:loop ./Dockerfile_loop
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---&gt; 89f2bbd25a88
Step 3/3 : CMD /loop.sh
---&gt; Running in ff52569c35fd
Removing intermediate container ff52569c35fd
---&gt; feb091e4efa3
Successfully built feb091e4efa3
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Executamos o contêiner

docker run -d --name looper ubuntu:loop bash
	
!docker run -d --name looper ubuntu:loop
Copy
	
8a28f8cc9892213c4e0603dfdde320edf52c091b82c60510083549a391cd6645

Verificamos e vemos que o contêiner está rodando

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 4 seconds ago Up 3 seconds looper

Tentamos parar o contêiner com docker stop looper. O Docker stop tenta parar o contêiner enviando-lhe o sinal SIGTERM.

	
%%time
!docker stop looper
Copy
	
looper
CPU times: user 89.2 ms, sys: 21.7 ms, total: 111 ms
Wall time: 10.6 s

Isso levou cerca de 10 segundos para parar, quando deveria ser imediato. Isso aconteceu porque stop enviou o sinal SIGTERM para parar o contêiner, mas como ele não parava, depois de um tempo enviou um SIGKILL para forçar a parada. Vamos ver o que acontece se listarmos os contêineres.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 23 seconds ago Exited (137) 2 seconds ago looper

Podemos ver que o sinal de Exited é 137, isso equivale a SIGKILL, ou seja, o Docker teve que forçar o desligamento.

Vamos a apagar o contêiner e a executá-lo novamente

	
!docker rm looper
Copy
	
looper
	
!docker run -d --name looper ubuntu:loop
Copy
	
84bc37f944d270be5f84a952968db2b8cf5372c61146d29383468198ceed18fd

Se agora tentarmos parar o contêiner com docker kill looper

	
%%time
!docker kill looper
Copy
	
looper
CPU times: user 9.1 ms, sys: 857 µs, total: 9.96 ms
Wall time: 545 ms

Vemos que o tempo são cerca de 500 ms, ou seja, o Docker o parou em um momento enviando a ordem SIGKILL. Porque kill não envia SIGTERM e se em um tempo não foi parado o contêiner, ele envia SIGKILL, o que faz é enviar SIGKILL desde o início.

Se vemos os contêineres, vemos que o sinal de saída é o mesmo, 137

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84bc37f944d2 ubuntu:loop "/bin/sh -c /loop.sh" 6 seconds ago Exited (137) 2 seconds ago looper

Esta não é a maneira correta de desligar um contêiner, pois quando quisermos desligá-lo, deveríamos fazer isso através do sinal SIGTERM, para que ele possa terminar de processar o que estivesse fazendo e então se desligue.

Se apagarmos o contêiner e o reexecutarmos

	
!docker rm looper
Copy
	
looper
	
!docker run -d --name looper ubuntu:loop
Copy
	
b9d9f370cc0de7569eb09d0a85cd67e8ea6babc0754a517ccba5c5057f5cc50e

Se agora olharmos para os processos que estão sendo executados dentro do contêiner

	
!docker exec looper ps -ef
Copy
	
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:05 ? 00:00:00 /bin/sh -c /loop.sh
root 7 1 93 14:05 ? 00:00:02 bash /loop.sh
root 8 0 0 14:05 ? 00:00:00 ps -ef

Na verdade o processo principal, o 1, não é /loop.sh mas sim /bin/sh -c /loop.sh, ou seja, é um processo filho do shell. Portanto, quando a sinal SIGTERM chegava ao shell, este não a encaminhava para seus processos filhos, por isso ela não chegava a loop.sh

Para que isso não aconteça, é necessário alterar o Dockerfile para o seguinte:

*Arquivo Dockerfile*:

FROM ubuntu:trusty
      COPY ["loop.sh", "/"]
      CMD ["/loop.sh"]    # antes era CMD /loop.sh

Esta forma se chama exec form, enquanto a anterior se chama shell form. Na forma anterior, o processo é executado como um filho do shell, enquanto na forma exec form executa diretamente o processo que indicarmos. Então, apagamos o contêiner, recompilamos e reiniciamos o contêiner com a imagem.

	
!docker rm -f looper
Copy
	
looper
	
!docker build -t ubuntu:loop ./Dockerfile_loop
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---&gt; Using cache
---&gt; 89f2bbd25a88
Step 3/3 : CMD ["/loop.sh"]
---&gt; Running in 6b8d92fcd57c
Removing intermediate container 6b8d92fcd57c
---&gt; 35a7bb2b1892
Successfully built 35a7bb2b1892
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
!docker run -d --name looper ubuntu:loop
Copy
	
850ae70c071426850b28428ac60dcbf875c6d35d9b7cc66c17cf391a23392965

Agora vejo os processos dentro do contêiner.

	
!docker exec looper ps -ef
Copy
	
UID PID PPID C STIME TTY TIME CMD
root 1 0 88 14:14 ? 00:00:02 bash /loop.sh
root 7 0 0 14:14 ? 00:00:00 ps -ef

Agora vejo que o processo principal, o 1, é /loop.sh

Se agora eu tentar parar o contêiner

	
%%time
!docker stop looper
Copy
	
looper
CPU times: user 989 µs, sys: 7.55 ms, total: 8.54 ms
Wall time: 529 ms

Vemos que demora mais. Vejamos o código com o qual parou.

	
!docker ps -a
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850ae70c0714 ubuntu:loop "/loop.sh" About a minute ago Exited (0) 33 seconds ago looper

Contentores executáveislink image 176

Se quisermos um binário que rode como um executável, no dockerfile é preciso especificar o comando em ENTRYPOINT e os parâmetros do comando em CMD, vamos ver isso.

Vamos a criar uma nova pasta onde guardaremos o Dockerfile

	
!mkdir dockerfile_ping
Copy

Agora criamos um Dockerfile dentro

	
!touch dockerfile_ping/Dockerfile
Copy

Escrevemos dentro do Dockerfile o seguinte

FROM ubuntu:trusty
      ENTRYPOINT [ "/bin/ping", "-c", "3" ]
      CMD [ "localhost" ]

Compilamos a imagem

	
!docker build -t ubuntu:ping ./dockerfile_ping
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : ENTRYPOINT [ "/bin/ping", "-c", "3" ]
---&gt; Using cache
---&gt; 1cebcfb542b1
Step 3/3 : CMD [ "localhost" ]
---&gt; Using cache
---&gt; 04ddc3de52a2
Successfully built 04ddc3de52a2
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Se agora executarmos a imagem sem passar um parâmetro, o contêiner fará um ping a si mesmo

	
!docker run --name ping_localhost ubuntu:ping
Copy
	
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.054 ms
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2027ms
rtt min/avg/max/mdev = 0.041/0.051/0.058/0.007 ms

Mas se passarmos um parâmetro agora, ele fará ping no endereço que dissermos

	
!docker run --name ping_google ubuntu:ping google.com
Copy
	
PING google.com (216.58.209.78) 56(84) bytes of data.
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=1 ttl=111 time=3.93 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=2 ttl=111 time=6.80 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=3 ttl=111 time=6.92 ms
--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 3.930/5.886/6.920/1.383 ms

Apagamos os contêineres

	
!docker rm ping_localhost ping_google
Copy
	
ping_localhost
ping_google

O contexto de build

Vamos a criar uma pasta chamada dockerfile_contexto

	
!mkdir dokerfile_contexto
Copy

Agora criamos nela dois arquivos: um test.txt e o Dockerfile

	
!touch dokerfile_contexto/Dockerfile dokerfile_contexto/text.txt
Copy

Modificamos o Dockerfile e colocamos o seguinte

FROM ubuntu:trusty
      COPY [".", "/"]

Isso vai fazer com que tudo o que está na pasta onde se encontra o Dockerfile seja copiado para dentro da imagem. Compilamos a imagem.

	
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copy
	
Sending build context to Docker daemon 2.56kB
Step 1/2 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/2 : COPY [".", "/"]
---&gt; 3ab79fdce389
Successfully built 3ab79fdce389
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Vamos ver o que tem dentro do contêiner

	
!docker run --name ls ubuntu:contexto ls
Copy
	
Dockerfile
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
text.txt
tmp
usr
var

Como vemos está o arquivo text.txt. Mas é possível que dentro da pasta que está no mesmo diretório que o Dockerfile haja arquivos ou pastas que não queremos que sejam copiados para a imagem, por qualquer razão, então assim como no git temos o .gitignore, no docker temos o .dockerignore, onde colocamos os arquivos ou pastas que não queremos que sejam levados em conta na hora de compilar.

Então criamos um arquivo .dockerignore

	
!touch dokerfile_contexto/.dockerignore
Copy

E dentro adicionamos o text.txt, e de passo o Dockerfile que não precisamos dentro da imagem

*.dockerignore*:

Dockerfile
      Parece que hay un malentendido. El archivo `text.txt` no foi fornecido. Por favor, forneça o conteúdo do arquivo em formato markdown para que eu possa traduzi-lo.

Apagamos o contêiner que tínhamos criado, recompilamos e vemos o que há dentro do contêiner

	
!docker rm ls
Copy
	
ls
	
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copy
	
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/2 : COPY [".", "/"]
---&gt; 7a6689546da4
Successfully built 7a6689546da4
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
!docker run --name ls ubuntu:contexto ls
Copy
	
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

Vemos que agora não estão nem Dockerfile nem text.txt. Apagamos o contentor.

	
!docker rm ls
Copy
	
ls

Construção em múltiplas etapaslink image 177

No final de um desenvolvimento, não queremos que todo o código esteja na imagem que vai ser enviada para produção.

Podemos dividir o dockerfile em dois, por exemplo, o developer.Dockerfile e o production.Dockerfile, onde no desenvolvimento haverá mais coisas do que na produção. Na hora de compilá-los, através da opção -f escolhemos o dockerfile que queremos usar

docker build -t <tag> -f developer.Dockerfile
docker build -t <tag> -f production.Dockerfile

Mas para não ter que criar dois arquivos Dockerfile, o Docker criou os multi stage builds. Com um único Dockerfile vamos resolver o problema.

Criamos a pasta onde vamos salvar o Dockerfile

	
!mkdir docker_multi_stage
Copy

E dentro criamos o arquivo Dockerfile

	
!cd docker_multi_stage && touch Dockerfile
Copy

Editamos o arquivo, inserindo o seguinte

# Etapa 1: Gerar o executável com Python baseado em Alpine
      FROM python:3.9-alpine como build-stage
      WORKDIR /app
      # Instalar dependências para PyInstaller
      RUN apk add --no-cache gcc musl-dev libc-dev
      # Gerar hello.py
      RUN echo 'print("Olá do Alpine!")' > hello.py
      # Instalar PyInstaller
      RUN pip install pyinstaller
      # Usar PyInstaller para criar um executável independente
      RUN pyinstaller --onefile hello.py
      
      # Etapa 2: Executar o executável em uma imagem do Alpine
      FROM alpine:latest
      WORKDIR /app
      # Copiar o executável da etapa de build
      COPY --from=build-stage /app/dist/hello .
      # Comando padrão para executar o executável
      CMD ["./hello"]

Como se pode ver, o Dockerfile está dividido em duas partes. De um lado, trabalhamos sobre a imagem python:3.9-alpine, que é chamada de build-stage. Do outro lado, trabalhamos sobre a imagem alpine:latest, que é uma imagem de Linux muito leve e é utilizada muito em produção.

O compilamos

	
!docker build -t maximofn/multistagebuild:latest ./docker_multi_stage
Copy
	
[+] Building 0.0s (0/2) docker:default
	
[+] Building 0.2s (4/6) docker:default
=&gt; [internal] load build definition from Dockerfile 0.0s
=&gt; =&gt; transferring dockerfile: 722B 0.0s
=&gt; [internal] load .dockerignore 0.0s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:latest 0.1s
=&gt; [internal] load metadata for docker.io/library/python:3.9-alpine 0.1s
...
=&gt; CACHED [stage-1 3/3] COPY --from=build-stage /app/dist/hello . 0.0s
=&gt; exporting to image 0.0s
=&gt; =&gt; exporting layers 0.0s
=&gt; =&gt; writing image sha256:7fb090d1495d00e892118b6bc3c03400b63a435fd4703 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multistagebuild:latest 0.0s

Se agora olharmos para as imagens que temos

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 8 minutes ago 13.6MB

Vamos baixar a imagem do Python para ver quanto ela pesa

	
!docker pull python:3.9-alpine
Copy
	
3.9-alpine: Pulling from library/python
a8db6415: Already exists
d5e70e42: Already exists
3fe96417: Already exists
aa4dddbb: Already exists
518be9f7: Already exists Digest: sha256:6e508b43604ff9a81907ec17405c9ad5c13664e45a5affa2206af128818c7486
Status: Downloaded newer image for python:3.9-alpine
docker.io/library/python:3.9-alpine
	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 9 minutes ago 13.6MB
python 3.9-alpine 6946662f018b 9 days ago 47.8MB

Podemos ver que enquanto nossa imagem pesa apenas 13,6 MB, a de Python com a qual foi construído o aplicativo pesa 47,8 MB. Portanto, podemos tirar duas conclusões: com a primeira imagem, a de Python, foi construído o aplicativo, gerou-se o executável e esse executável é o que usamos na segunda imagem, a de Alpine. Além disso, podemos ver que, embora a primeira imagem usada seja a de Python, ela não é baixada no nosso sistema, já que tivemos que baixá-la nós mesmos.

Então só resta testá-lo.

	
!docker run --rm --name multi_stage_build maximofn/multistagebuild
Copy
	
Hello from Alpine!

Funciona!

Builds multi arquiteturalink image 178

Suponhamos que queremos fazer uma imagem que possa ser executada em um computador e em uma Raspberry. O computador provavelmente terá um micro com arquitetura AMD64, enquanto a Raspberry tem um micro com arquitetura ARM. Portanto, não podemos criar a mesma imagem para os dois. Ou seja, quando criamos uma imagem, a criamos com um Dockerfile que geralmente começa assim

DE ...

Portanto, o Dockerfile da imagem do computador poderia começar assim

FROM ubuntu:latest

Enquanto o da Raspberry poderia começar assim

FROM arm64v8/ubuntu:latest

Teríamos que criar dois arquivos Dockerfile, compilá-los e no computador usar uma imagem e na raspberry usar outra.

Para evitar ter que ver a arquitetura do computador e ver qual imagem temos que usar, o Docker cria os manifest, que como seu nome indica é um manifesto que indica, com base na arquitetura de micro que tenhamos, usa uma imagem ou outra.

Então vamos ver como fazer isso

Em primeiro lugar, criamos uma pasta onde vamos criar nossos arquivos Dockerfile

	
!mkdir docker_multi_arch
Copy

Agora criamos os dois Dockerfiles

	
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
Copy

Escrevemos o Dockerfile para AMD64

	
!cd docker_multi_arch && echo "FROM ubuntu:20.04" &gt;&gt; Dockerfile_amd64 && echo "CMD echo 'Hello from amd64'" &gt;&gt; Dockerfile_amd64
Copy
	
!cd docker_multi_arch && echo "FROM arm64v8/ubuntu:latest" &gt;&gt; Dockerfile_arm && echo "CMD echo 'Hello from ARM'" &gt;&gt; Dockerfile_arm
Copy

Agora combinamos as duas imagens

	
!cd docker_multi_arch && docker build -t maximofn/multiarch:arm -f Dockerfile_arm .
Copy
	
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.1s
[+] Building 0.3s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.2s
[+] Building 0.5s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.4s
[+] Building 0.6s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.5s
...
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load build definition from Dockerfile_arm 0.0s
=&gt; =&gt; transferring dockerfile: 94B 0.0s
=&gt; [internal] load metadata for docker.io/arm64v8/ubuntu:latest 1.8s
=&gt; [auth] arm64v8/ubuntu:pull token for registry-1.docker.io 0.0s
=&gt; CACHED [1/1] FROM docker.io/arm64v8/ubuntu:latest@sha256:94d12db896d0 0.0s
=&gt; exporting to image 0.0s
=&gt; =&gt; exporting layers 0.0s
=&gt; =&gt; writing image sha256:a9732c1988756dc8e836fd96e5c9512e349c97ea5af46 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multiarch:arm 0.0s

Vamos ver se temos as duas imagens compiladas

	
!docker image ls
Copy
	
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multiarch arm a9732c198875 4 weeks ago 69.2MB
maximofn/multiarch amd64 5b612c83025f 6 weeks ago 72.8MB

Vemos que compilamos as duas imagens. Para poder criar um manifesto, primeiro temos que enviar as imagens para o Docker Hub, então as enviamos.

	
!docker push maximofn/multiarch:amd64
Copy
	
The push refers to repository [docker.io/maximofn/multiarch]
82bdeb5f: Mounted from library/ubuntu amd64: digest: sha256:30e820f2a11a24ad4d8fb624ae485f7c1bcc299e8cfc72c88adce1acd0447e1d size: 529
	
!docker push maximofn/multiarch:arm
Copy
	
The push refers to repository [docker.io/maximofn/multiarch]
	
eda53374: Layer already exists arm: digest: sha256:6ec5a0752d49d3805061314147761bf25b5ff7430ce143adf34b70d4eda15fb8 size: 529

Se eu for ao meu Docker Hub posso ver que minha imagem maximofn/multiarch tem os tags amd64 e arm

docker_multi_arch_tags

Agora vamos a criar o manifest baseado nestas duas imagens

	
!docker manifest create maximofn/multiarch:latest maximofn/multiarch:amd64 maximofn/multiarch:arm
Copy
	
Created manifest list docker.io/maximofn/multiarch:latest

Uma vez criado, temos que indicar as arquiteturas das CPUs a que corresponde cada uma.

	
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:amd64 --os linux --arch amd64
Copy
	
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:arm64 --os linux --arch arm64
Copy
	
manifest for image maximofn/multiarch:arm64 does not exist in maximofn/multiarch:latest

Uma vez criado e anotado, podemos enviar o manifest para o Docker Hub

	
!docker manifest push maximofn/multiarch:latest
Copy
	
sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304

Se eu olhar novamente os tags da minha imagem maximofn/multiarch, também vejo o de latest

docker_multi_arch_tags_manifest

Agora, tanto se eu quiser usar minha imagem de uma máquina com CPU AMD64 ou CPU ARM ao fazer FROM maximofn/multiarch:latest, o Docker verificará a arquitetura da CPU e baixará o tag amd64 ou o tag arm. Vamos ver isso, se do meu computador eu executar a imagem obtenho

	
!docker run maximofn/multiarch:latest
Copy
	
Unable to find image 'maximofn/multiarch:latest' locally
	
latest: Pulling from maximofn/multiarch
Digest: sha256:7cef0de10f7fa2b3b0dca0fbf398d1f48af17a0bbc5b9beca701d7c427c9fd84
Status: Downloaded newer image for maximofn/multiarch:latest
Hello from amd64

Como não a tem, baixa-a

Se eu me conectar agora via ssh a uma Raspberry Pi e tentar o mesmo, obtenho

raspberrry@raspberrypi:~ $ docker run maximofn/multiarch:latest
Impossível encontrar a imagem 'maximofn/multiarch:latest' localmente
latest: Pulling from maximofn/multiarch
Digesto: sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304
Status: Baixada a imagem mais recente para maximofn/multiarch:latest
Olá do ARM

Aparece Hello from ARM pois o Raspberry Pi possui um microprocessador com arquitetura ARM

Como pode ser visto, cada máquina baixou a imagem de que precisava.

Escrita correta de Dockerfiles avançadoslink image 179

Já vimos a maneira de escrever Dockerfiles corretamente, mas há uma coisa a mais que podemos fazer agora que conhecemos o multi-stage build e é criar um contêiner para criar o executável e outro menor para executá-lo.

Chegamos à conclusão de que um bom Dockerfile poderia ser este

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Vamos a criar agora um executável em um contêiner builder e em outro menor o executamos

FROM python:3.9.18-alpine como builder
      WORKDIR /sourceCode/sourceApp
      RUN apk add --no-cache gcc musl-dev libc-dev && pip install pyinstaller
      COPY ./sourceCode/sourceApp .
      RUN pyinstaller --onefile app.py
      
      DE alpine:3.18.3
      WORKDIR /sourceCode/sourceApp
      COPY --from=builder /sourceCode/sourceApp/dist/app .
      CMD ["./app"]

Criamos o código Python no caminho necessário

	
!mkdir multistagebuild/sourceCode
!mkdir multistagebuild/sourceCode/sourceApp
!touch multistagebuild/sourceCode/sourceApp/app.py
!echo 'print("Hello from Alpine!")' &gt; multistagebuild/sourceCode/sourceApp/app.py
Copy

Agora compilando a imagem

	
!docker build -t maximofn/multistagebuild:alpine-3.18.3 ./multistagebuild
Copy
	
[+] Building 0.0s (0/0) docker:default
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (3/5) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.1s
=&gt; [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.1s
=&gt; [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.3s (3/5) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.2s
=&gt; [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.2s
=&gt; [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.5s (4/6) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.4s
...
=&gt; exporting to image 0.1s
=&gt; =&gt; exporting layers 0.1s
=&gt; =&gt; writing image sha256:8a22819145c6fee17e138e818610ccf46d7e13c786825 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multistagebuild:alpine-3.18.3 0.0s

A executamos

	
!docker run --rm --name multi_stage_build maximofn/multistagebuild:alpine-3.18.3
Copy
	
Hello from Alpine!

A imagem maximofn/multistagebuild:alpine-3.18.3 só pesa 13,6 MB

Diferença entre RUN, CMD e ENTRYPOINTlink image 180

EXECUTARlink image 181

O comando RUN é o mais simples, ele apenas executa um comando no momento da compilação da imagem. Por exemplo, se quisermos instalar um pacote na imagem, fazemos isso através do RUN.

Portanto, é importante, RUN é executado no momento da compilação da imagem, não quando o contêiner é executado.

CMDlink image 182

O comando CMD é o comando que é executado quando o contêiner é iniciado. Por exemplo, se quisermos que o contêiner execute um comando ao ser iniciado, fazemos isso através do CMD. Por exemplo, se tivermos uma aplicação Python em um contêiner, com CMD podemos indicar que, quando o contêiner for iniciado, ele execute a aplicação Python.

Desta forma, quando o contêiner for iniciado, a aplicação Python será executada. Isso significa que, se fizermos docker run <image>, a aplicação Python será executada. No entanto, CMD nos permite sobrescrever o comando que é executado ao iniciar o contêiner, por exemplo, se fizermos docker run <image> bash, o bash será executado em vez da aplicação Python.

ENTRYPOINTlink image 183

O comando ENTRYPOINT é similar ao comando CMD, mas com uma diferença: o ENTRYPOINT não está destinado a ser sobrescrito. Isso significa que, se tivermos uma aplicação Python em um contêiner, podemos usar ENTRYPOINT para indicar que, quando o contêiner for executado, ele execute a aplicação Python. No entanto, se fizermos docker run <image> bash, será executada a aplicação Python, não o bash.

Um uso muito comum de ENTRYPOINT é quando queremos que o contêiner seja um executável, por exemplo, se quisermos que o contêiner seja um executável de uma versão do Python que não temos em nosso host, porque, por exemplo, queremos testar a nova versão do Python que foi lançada podemos fazer

FROM python:3.9.18-alpine
      ENTRYPOINT ["python3"]

Desta forma, quando o contêiner for iniciado, o Python será executado. Isso significa que, se fizermos docker run <image>, o Python será executado. Mas ENTRYPOINT nos permite sobrescrever o comando que é executado ao iniciar o contêiner, por exemplo, se fizermos docker run <image> myapp.py, será executado python3 myapp.py dentro do contêiner. Assim podemos testar nossa aplicação Python na nova versão do Python.

Mudanças em um contêinerlink image 184

Com docker diff podemos ver as diferenças entre o contêiner e a imagem, o que é o mesmo que a diferença no contêiner desde sua criação até agora.

Vamos a executar um contêiner e dentro criamos um arquivo

	
!docker run --rm -it --name ubuntu-20.04 ubuntu:20.04 bash
Copy
	
root@895a19aef124:/# touch file.txt

Agora podemos ver a diferença

	
!docker diff ubuntu-20.04
Copy
	
C /root
A /root/.bash_history
A /file.txt

A significa que foi adicionado, C significa que foi alterado e D significa que foi excluído.

Docker no Dockerlink image 185

Suponhamos que temos contêineres que precisam iniciar ou desligar outros contêineres. Isso é feito da seguinte maneira

Dado que no Linux tudo é um arquivo e o host se comunica com o Docker por meio de um socket. Portanto, para o Linux, esse socket é um arquivo. Então, se montarmos esse socket como um arquivo no contêiner, ele poderá se comunicar com o Docker.

Primeiro vamos a montar um contêiner com Ubuntu

	
!docker run -d --name ubuntu ubuntu:latest tail -f /dev/null
Copy
	
144091e4a3325c9068064ff438f8865b40f944af5ce649c7156ca55a3453e423

Vamos a montar o contêiner que vai poder se comunicar com o Docker montando a pasta /var/run/docker.sock

$ docker run -it --rm --name main -v /var/run/docker.sock:/var/run/docker.sock docker:19.03.12
/ #

Entramos em um contêiner, e se executarmos docker ps dentro dele

# docker ps
ID DO CONTÊINER IMAGEM COMANDO CRIADO STATUS PORTAS NOMES
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" 3 segundos atrás Up 2 segundos main
144091e4a332 ubuntu:latest "tail -f /dev/null" 19 segundos atrás Up 18 segundos ubuntu

Como podemos ver, dentro do Docker podemos ver os contêineres do host

Podemos executar um novo contêiner

# docker run -d --name ubuntu_from_main ubuntu:latest tail -f /dev/null
362654a72bb0fb047c13968707a6f16b87fed7ce051eb5c1a146b15828589a1a
/ #

E se voltarmos a ver os contêineres

# docker ps
ID DO CONTAINER IMAGEM COMANDO CRIADO STATUS PORTAS NOMES
362654a72bb0 ubuntu:latest "tail -f /dev/null" 3 segundos atrás Up 3 segundos ubuntu_from_main
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" Há um minuto Up Há um minuto main
144091e4a332 ubuntu:latest "tail -f /dev/null" 2 minutos atrás Up Há cerca de um minuto ubuntu

Mas se agora executarmos uma nova terminal do host, veremos o contêiner criado a partir de dentro do contêiner

	
!docker ps
Copy
	
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
362654a72bb0 ubuntu:latest "tail -f /dev/null" About a minute ago Up About a minute ubuntu_from_main
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes main
144091e4a332 ubuntu:latest "tail -f /dev/null" 3 minutes ago Up 3 minutes ubuntu

Tudo o que fizermos a partir do contêiner main será refletido no host

Isso tem a vantagem de podermos instalar programas em um contêiner que tem acesso ao host para não ter que instalá-los no host. Por exemplo, dive é uma ferramenta para explorar contêineres, mas se você não quiser instalá-la no host, pode instalá-la em um contêiner com acesso ao host, assim, a partir desse contêiner main, você pode explorar o resto dos contêineres sem ter que instalá-la no host.

Continuar lendo

Últimos posts -->

Você viu esses projetos?

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversacional para cozinheiros de hotéis e restaurantes. Um cozinheiro, gerente de cozinha ou serviço de quarto de um hotel ou restaurante pode falar com o chatbot para obter informações sobre receitas e menus. Mas também implementa agentes, com os quais pode editar ou criar novas receitas ou menus

Naviground

Naviground Naviground

Subtify

Subtify Subtify
Python
Whisper
Spaces

Gerador de legendas para vídeos no idioma que você desejar. Além disso, coloca uma legenda de cor diferente para cada pessoa

Ver todos os projetos -->

Quer aplicar IA no seu projeto? Entre em contato!

Quer melhorar com essas dicas?

Últimos tips -->

Use isso localmente

Os espaços do Hugging Face nos permitem executar modelos com demos muito simples, mas e se a demo quebrar? Ou se o usuário a deletar? Por isso, criei contêineres docker com alguns espaços interessantes, para poder usá-los localmente, aconteça o que acontecer. Na verdade, se você clicar em qualquer botão de visualização de projeto, ele pode levá-lo a um espaço que não funciona.

Flow edit

Flow edit Flow edit

Edite imagens com este modelo de Flow. Baseado em SD3 ou FLUX, você pode editar qualquer imagem e gerar novas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos os contêineres -->

Quer aplicar IA no seu projeto? Entre em contato!

Você quer treinar seu modelo com esses datasets?

short-jokes-dataset

Dataset com piadas em inglês

opus100

Dataset com traduções de inglês para espanhol

netflix_titles

Dataset com filmes e séries da Netflix

Ver mais datasets -->