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.
📚 **Esta entrada faz parte da série _Guia de Docker_**, dividida em dois capítulos que são lidos em ordem:
> * 👉 **Parte 1: Contêineres, imagens e aplicações**
* Parte 2: Docker Compose e tópicos avançados
Contêineres
Olá mundo
Executar o primeiro contêiner do tipo Hello World com o comando docker run hello-world
InputPython!docker run hello-worldCopied
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world85e32844: Pull complete 457kB/2.457kBBDigest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1cStatus: Downloaded newer image for hello-world:latestHello 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 theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare 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, a primeira mensagem já não aparecerá, na qual se indica que está sendo feito o download
InputPython!docker run hello-worldCopied
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 theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare 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, executar docker ps
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Como vemos, não há nenhum contêiner aberto. No entanto, se executarmos docker ps -a (all), vemos que sim aparecem
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1efb51bbbf38 hello-world "/hello" 10 seconds ago Exited (0) 9 seconds ago strange_thompson5f5705e7603e hello-world "/hello" 15 seconds ago Exited (0) 14 seconds ago laughing_jang
Vemos que aparecem dois contêineres chamados hello-world, que são os dois que executámos anteriormente. Portanto, cada vez que executamos o comando run, o Docker cria um novo contêiner, não executa um que já exista.
Se quisermos ter mais informações de um dos dois contêineres, podemos executar docker inspect <id>, onde <id> corresponde ao ID do contêiner que foi mostrado na lista anterior
InputPython!docker inspect 1efb51bbbf38Copied
[{"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 a mesma informação que com o ID
Vou executar novamente docker ps -a para ver a lista novamente
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1efb51bbbf38 hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago strange_thompson5f5705e7603e hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago laughing_jang
E agora executo docker inspect <name> para ver as informações do contêiner
InputPython!docker inspect strange_thompsonCopied
[{"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 mostra apenas os contêineres que estão em execução, enquanto docker ps -a mostra todos os contêineres, os que estão em execução e os que estão desligados
Podemos criar um contêiner atribuindo a ele um nome por meio do comando docker run --name <name> hello-world
InputPython!docker run --name hello_world hello-worldCopied
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 theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare 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 cômodo para nós, já que 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 os nomes dos contêineres se repitam. Assim, se quisermos renomear o contêiner, podemos usar o comando docker rename <old name> <new name>
InputPython!docker rename hello_world hello_world2Copied
Temos agora um monte de contêineres iguais. Então, se quisermos apagar algum, temos que usar o comando docker rm <id> ou docker rm <name>
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf432c9c2ca21 hello-world "/hello" 9 seconds ago Exited (0) 8 seconds ago hello_world21efb51bbbf38 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago strange_thompson5f5705e7603e hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago laughing_jang
InputPython!docker rm hello_world2Copied
hello_world2
Se voltarmos a ver a lista de contêineres, o contêiner hello_world2 já não estará
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1efb51bbbf38 hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago strange_thompson5f5705e7603e hello-world "/hello" 5 minutes ago Exited (0) 5 minutes ago laughing_jang
Se quisermos apagar todos os contêineres, podemos fazê-lo um por um, mas, como isso é muito trabalhoso, podemos apagar todos por meio do comando docker container prune. Este comando elimina apenas os contêineres que estiverem parados
InputPython!docker container pruneCopied
WARNING! This will remove all stopped containers.Are you sure you want to continue? [y/N] y
O Docker pergunta se você tem certeza, e se você disser que sim, ele apaga todos. Se agora listar todos os contêineres, não aparece nenhum
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
O modo interativo
Vamos a executar um Ubuntu por meio do comando docker run ubuntu
InputPython!docker run ubuntuCopied
Unable to find image 'ubuntu:latest' locallylatest: Pulling from library/ubuntuDigest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833c8785644c1[1AStatus: Downloaded newer image for ubuntu:latest
Como vemos, agora demorou mais para baixar. Se listarmos os contêineres usando o comando docker ps, vemos que o contêiner que acabamos de criar não aparece, ou seja, não está em execução.
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Listamos agora todos os contêineres
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESda16b3a85178 ubuntu "bash" 4 seconds ago Exited (0) 3 seconds ago hardcore_kare
Vemos que o estado do contêiner é Exited (0)
Se olharmos para o comando do contentor, aparece bash e, juntamente com o estado Exited (0), indica-nos que o Ubuntu foi iniciado, executou o seu *bash*, terminou a execução e devolveu um 0. Isto acontece porque ao Bash do Ubuntu não lhe foi dito nada para fazer. Para resolver isso, agora vamos executar o contentor através do comando docker run -it ubuntu; com it, o que lhe estamos a indicar é que o queremos executar em modo interativo
InputPython!docker run -it ubuntuCopied
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
InputPython!root@5b633e9d838f:/# cat /etc/lsb-releaseCopied
DISTRIB_ID=UbuntuDISTRIB_RELEASE=22.04DISTRIB_CODENAME=jammyDISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"
Se abrirmos outro terminal e vermos a lista de contêineres, agora sim aparecerá o contêiner executando Ubuntu
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhani
Vemos o contêiner com Ubuntu e, em seu estado, podemos ver UP
Se agora vemos a lista de todos os contêineres, veremos que aparecem os dois contêineres com Ubuntu, o primeiro desligado e o segundo o que está em execução
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES5b633e9d838f ubuntu "bash" 3 minutes ago Up 3 minutes funny_mirzakhanida16b3a85178 ubuntu "bash" 3 minutes ago Exited (0) 3 minutes ago hardcore_kare
Se voltarmos ao terminal onde tínhamos o Ubuntu sendo executado dentro de um Docker, se digitarmos exit sairemos do Ubuntu.
InputPython!root@5b633e9d838f:/# exitCopied
exit
Se executarmos docker ps, o contêiner já não aparece
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Mas se eu executar docker ps -a, ele aparece. Isso quer dizer que o contêiner foi desligado
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES5b633e9d838f ubuntu "bash" 4 minutes ago Exited (0) 27 seconds ago funny_mirzakhanida16b3a85178 ubuntu "bash" 4 minutes ago Exited (0) 4 minutes ago hardcore_kare
Isso acontece porque, ao digitar exit, na verdade estamos escrevendo isso no console do bash do Ubuntu, o que significa que estamos encerrando o processo bash do Ubuntu.
Ciclo de vida de um contêiner
No 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 somente quando o processo principal termina o contêiner é desligado
Portanto, se quisermos executar um contêiner que não desligue quando um processo terminar, devemos fazer com que seu processo principal não termine. Neste caso, que o bash não finalize
Se quisermos executar um contêiner com Ubuntu, mas que não termine quando o processo do Bash finalizar, podemos fazer da seguinte maneira
InputPython!docker run --name alwaysup -d ubuntu tail -f /dev/nullCopied
ce4d60427dcd4b326d15aa832b816c209761d6b4e067a016bb75bf9366c37054
O que fazemos é primeiro dar-lhe o nome alwaysup, em segundo lugar passar-lhe a opção -d (detach) para que o contêiner seja executado em segundo plano e, por último, dizemos-lhe 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 devolverá a ID do contêiner, mas não estaremos dentro do Ubuntu como acontecia antes
Se agora vemos a lista de contêineres que estão em execução, aparece o contêiner que acabamos de criar
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 18 seconds ago Up 17 seconds alwaysup
Como já temos um contêiner em execução o tempo todo, podemos nos conectar a ele por meio do comando exec. Indicamos o nome ou o ID do contêiner e passamos o processo que queremos que seja executado. Além disso, passamos a opção -it para indicar que ele seja interativo
InputPython!docker exec -it alwaysup bashCopied
root@ce4d60427dcd:/#
Agora voltamos a estar dentro do Ubuntu. Se executarmos o comando ps -aux, podemos ver uma lista dos processos que estão sendo executados dentro do Ubuntu.
InputPython!ps -auxCopied
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.0 2820 1048 ? Ss 13:04 0:00 tail -f /dev/nullroot 7 0.0 0.0 4628 3796 pts/0 Ss 13:04 0:00 bashroot 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 estar sempre ligado enquanto o processo tail -f /dev/null continuar em execução
Se sairmos do contêiner com o comando exit e executarmos o comando docker ps, vemos que o contêiner continua ligado
InputPython!exitCopied
exit
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd 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>
InputPython!docker stop alwaysupCopied
alwaysup
Se agora voltarmos a listar os contêineres em execução, o contêiner com Ubuntu já não aparece.
InputPython!docker psCopied
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
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 14 minutes ago Exited (137) About a minute ago alwaysup5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhanida16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare
Também podemos pausar um contêiner usando o comando docker pause <name>
InputPython!docker run --name alwaysup -d ubuntu tail -f /dev/nullCopied
8282eaf9dc3604fa94df206b2062287409cc92cbcd203f1a018742b5c171c9e4
Agora pausamos
InputPython!docker pause alwaysupCopied
alwaysup
Se voltarmos a ver todos os contêineres, vemos que o contêiner com Ubuntu está pausado
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8282eaf9dc36 ubuntu "tail -f /dev/null" 41 seconds ago Up 41 seconds (Paused) alwaysup5b633e9d838f ubuntu "bash" 19 minutes ago Exited (0) 15 minutes ago funny_mirzakhanida16b3a85178 ubuntu "bash" 20 minutes ago Exited (0) 20 minutes ago hardcore_kare
Contentores de uso único
Se ao executar um contêiner, adicionamos a opção --rm, esse contêiner será excluído quando terminar de ser executado.
InputPython!docker run --rm --name autoremove ubuntu:latestCopied
Se agora vemos quais contêineres temos
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Vemos que o contêiner que acabamos de criar não está presente
Expor contêineres ao mundo exterior
Vamos criar um novo contêiner com um servidor
InputPython!docker run -d --name proxy nginxCopied
Unable to find image 'nginx:latest' locallylatest: Pulling from library/nginxf1ad4ce1: Pulling fs layerb079d0f8: Pulling fs layer5fbbebc6: Pulling fs layerffdd25f4: Pulling fs layer32c8fba2: Pulling fs layer24b8ba39: Pull complete 393kB/1.393kBB[5ADigest: sha256:2888a97f7c7d498bbcc47ede1ad0f6ced07d72dfd181071dde051863f1f79d7bStatus: Downloaded newer image for nginx:latest1a530e04f14be082811b72ea8b6ea5a95dad3037301ee8a1351a0108ff8d3b30
Isso cria um servidor, vamos listar novamente os contêineres que estão em execução
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1a530e04f14b 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 por meio de http://localhost:80 não conseguimos conectar. Isso acontece porque cada contêiner tem sua própria interface de rede. Ou seja, o servidor está escutando 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
InputPython!docker stop proxyCopied
proxy
Se listarmos os contêineres, não aparece em execução
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Nós o apagamos para criá-lo novamente
InputPython!docker rm proxyCopied
proxy
Se listarmos todos os contêineres, eles já não estão presentes
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 19 minutes ago Exited (137) 5 minutes ago alwaysup5b633e9d838f ubuntu "bash" 24 minutes ago Exited (0) 20 minutes ago funny_mirzakhanida16b3a85178 ubuntu "bash" 24 minutes ago Exited (0) 24 minutes ago hardcore_kare
Para recriar o contêiner com o servidor e poder vê-lo a partir do host, temos de usar a opção -p (publish), indicando em primeiro lugar a porta na qual queremos vê-lo no host e, a seguir, a porta do contêiner, ou seja, -p <ip host>:<ip contenedor>
InputPython!docker run -d --name proxy -p 8080:80 nginxCopied
c199235e42f76a30266f6e1af972e0a59811806eb3d3a9afdd873f6fa1785eae
Listamos os contêineres
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESc199235e42f7 nginx "/docker-entrypoint.…" 22 seconds ago Up 21 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp proxy
Vemos que a porta do contêiner é 0.0.0.0:8080->80/tcp. Se agora abrirmos um navegador e introduzirmos 0.0.0.0:8080, poderemos acessar o servidor do contêiner
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, por meio do comando docker logs <name> posso ver os registros do contêiner
InputPython!docker logs proxyCopied
/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.sh10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf10-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 up2022/09/13 13:24:06 [notice] 1#1: using the "epoll" event method2022/09/13 13:24:06 [notice] 1#1: nginx/1.23.12022/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-generic2022/09/13 13:24:06 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:10485762022/09/13 13:24:06 [notice] 1#1: start worker processes2022/09/13 13:24:06 [notice] 1#1: start worker process 312022/09/13 13:24:06 [notice] 1#1: start worker process 322022/09/13 13:24:06 [notice] 1#1: start worker process 332022/09/13 13:24:06 [notice] 1#1: start worker process 342022/09/13 13:24:06 [notice] 1#1: start worker process 352022/09/13 13:24:06 [notice] 1#1: start worker process 362022/09/13 13:24:06 [notice] 1#1: start worker process 372022/09/13 13:24:06 [notice] 1#1: start worker process 382022/09/13 13:24:06 [notice] 1#1: start worker process 392022/09/13 13:24:06 [notice] 1#1: start worker process 402022/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'×ü[ïºåÇè÷&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 requisições que foram feitas ao servidor. Mas, se quero ver os logs em tempo real, com docker logs -f <name> posso fazer isso
InputPython!docker logs -f proxyCopied
Agora posso ver os logs em tempo real. Para sair, introduzir CTRL+C
Como pode chegar um momento em que haja muitos logs, se você só quiser os últimos logs, por meio da opção --tail <num> você pode ver os últimos <num> logs. Se eu adicionar a opção -f estaremos vendo sempre os últimos <num> logs
InputPython!docker logs --tail 10 proxyCopied
2022/09/13 13:24:06 [notice] 1#1: start worker process 412022/09/13 13:24:06 [notice] 1#1: start worker process 42172.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'×ü[ïºåÇè÷&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 também adicionarmos a opção -t, podemos ver a data e a hora de cada log; dessa maneira, se tivermos tido um problema, podemos saber em que momento ocorreu.
InputPython!docker logs --tail -t 10 proxyCopied
2022-09-13T13:24:06.573362728Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 412022-09-13T13:24:06.651127107Z 2022/09/13 13:24:06 [notice] 1#1: start worker process 422022-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'×ü[ïºåÇè÷&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 removemos o contêiner
InputPython!docker rm -f proxyCopied
proxy
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 27 minutes ago funny_mirzakhanida16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare
Dados no Docker
Montagens bind
Vamos ver os contêineres que temos parados
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 26 minutes ago Exited (137) 13 minutes ago alwaysup5b633e9d838f ubuntu "bash" 31 minutes ago Exited (0) 28 minutes ago funny_mirzakhanida16b3a85178 ubuntu "bash" 32 minutes ago Exited (0) 32 minutes ago hardcore_kare
Vamos eliminar os dois do Ubuntu em que o comando principal é o Bash e vamos deixar o que deixamos como não operação
InputPython!docker rm funny_mirzakhaniCopied
funny_mirzakhani
InputPython!docker rm hardcore_kareCopied
hardcore_kare
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESce4d60427dcd ubuntu "tail -f /dev/null" 27 minutes ago Exited (137) 14 minutes ago alwaysup
Vamos voltar a executar o contêiner do Ubuntu que deixamos, isso é feito por meio do comando start
InputPython!docker start alwaysupCopied
alwaysup
Nos metemos outra vez dentro do
InputPython!docker exec -it alwaysup bashCopied
root@ce4d60427dcd:/#
No contêiner, posso criar uma nova pasta chamada dockerfolder
InputPython!mkdir dockerfolderCopied
Se listarmos os arquivos, a nova pasta aparecerá
InputPython!lsCopied
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
InputPython!exitCopied
exit
E apagamos isso
InputPython!docker rm -f alwaysupCopied
alwaysup
Se listarmos todos os contêineres, o último que criamos já não aparece.
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Vamos a voltar a fazer tudo, mas primeiro vamos criar uma pasta no host na qual compartilharemos os dados com o contêiner
InputPython!mkdir dockerHostFolderCopied
Vemos que dentro da pasta não há nada
InputPython!ls dockerHostFolderCopied
Agora obtemos nosso caminho absoluto
InputPython!pwdCopied
/home/wallabot/Documentos/web/portafolio/posts
Voltamos a criar 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>
InputPython!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/nullCopied
4ede4512c293bdcc155e9c8e874dfb4a28e5163f4d5c7ddda24ad2863f28921b
Entramos no contêiner, listamos os arquivos e já aparece a pasta que tínhamos criado
InputPython!docker exec -it alwaysup bashCopied
root@4ede4512c293:/#
InputPythonroot@4ede4512c293:/# lsCopied
bin dev etc lib lib64 media opt root sbin sys usrboot 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
InputPythonroot@4ede4512c293:/# cd dockerContainerFolderCopied
InputPythonroot@4ede4512c293:/dockerContainerFolder# touch bindFile.txtCopied
InputPythonroot@4ede4512c293:/dockerContainerFolder# exitCopied
exit
Vamos ver o que há dentro da pasta compartilhada
InputPython!ls dockerHostFolderCopied
bindFile.txt
Mas ainda mais, se apagarmos o contêiner, o arquivo continua lá
InputPython!docker rm -f alwaysupCopied
alwaysup
InputPython!ls dockerHostFolderCopied
bindFile.txt
Se eu recriar um contêiner compartilhando as pastas, todos os arquivos estarão no contêiner
InputPython!docker run -d --name alwaysup -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/nullCopied
6c021d37ea29d8b23fe5cd4968baa446085ae1756682f65340288b4c851c362d
InputPython!docker exec -it alwaysup bashCopied
root@6c021d37ea29:/#
InputPython!root@6c021d37ea29:/# ls dockerContainerFolder/Copied
bindFile.txt:/#
Eliminamos o contêiner
InputPython!docker rm -f alwaysupCopied
alwaysup
InputPython!docker ps -aCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Volumes
Os volumes foram criados como uma evolução dos bind mounts para oferecer mais segurança. Podemos listar todos os volumes do Docker por meio de docker volume ls
InputPython!docker volume lsCopied
DRIVER VOLUME NAME
Vamos criar um novo volume para o contêiner do Ubuntu, para isso usamos o comando docker volume create <volume name>
InputPython!docker volume create ubuntuVolumeCopied
ubuntuVolume
Se voltarmos a listar os volumes, aparecerá o que acabamos de criar
InputPython!docker volume lsCopied
DRIVER VOLUME NAMElocal ubuntuVolume
No entanto, ele não aparece como uma pasta no sistema de arquivos do host. Com ls -d */ listamos todas as pastas
InputPython!ls -d */Copied
dockerHostFolder/ __pycache__/
Vamos a voltar a criar um contêiner, mas agora vamos criá-lo com o volume que acabamos de criar com a opção --mount, indicando o volume de origem por meio de src=<volume name> (se o volume não existisse, o docker o criaria), a seguir o destino separado por uma ,, dst=<container path>, ou seja --mount src=<volume name>,dst=<container path>
InputPython!docker run -d --name alwaysup --mount src=ubuntuVolume,dst=/dockerVolumeFolder ubuntu tail -f /dev/nullCopied
42cdcddf4e46dc298a87b0570115e0b2fc900cb4c6db5eea22a61409b8cb271d
Uma vez criado, podemos ver os volumes do contêiner usando o comando inspect e filtrando por '{{.Mounts}}'
$ docker inspect --format '{{.Mounts}}' alwaysup
[
{
volume ubuntuVolume /var/lib/docker/volumes/ubuntuVolume/_data /dockerVolumeFolder local z true
}
]Vemos que o volume se chama ubuntuVolume e também podemos ver o caminho onde ele 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 ele 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.txtO arquivo está criado
Inserir e extrair arquivos de um contêiner
Primeiro vamos criar um arquivo que queremos copiar para dentro de um contêiner
InputPython!touch dockerHostFolder/text.txtCopied
Entramos no contêiner
$ docker exec -it alwaysup bash
root@42cdcddf4e46:/#Criamos uma nova pasta onde vamos copiar o arquivo e saímos
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 varroot@42cdcddf4e46:/# exit
exitCopiamos 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>
InputPython!docker cp dockerHostFolder/text.txt alwaysup:/folderToCopyCopied
Voltamos a entrar no contêiner e verificamos se o arquivo está lá
$ docker exec -it alwaysup bash
root@42cdcddf4e46:/# ls folderToCopy/text.txtSaímos do contêiner
/# exit
exitAgora vamos a extrair o arquivo do contêiner e vamos 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>:<docker file path> <host file path>
InputPython!docker cp alwaysup:/folderToCopy/text.txt dockerHostFolder/fileExtract.txtCopied
Vemos que está no host
InputPython!ls dockerHostFolderCopied
bindFile.txt fileExtract.txt text.txt
Embora o contêiner esteja parado, também é possível copiar arquivos
Por fim, removemos o contêiner
InputPython!docker rm -f alwaysupCopied
alwaysup
Imagens
Conceitos fundamentais
As imagens são os arquivos ("templates") 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, na primeira vez aparecia uma mensagem dizendo que não tínhamos a imagem e que ela seria baixada. No Docker Hub existem inúmeras imagens com todos os tipos de máquinas, mas para um ambiente de desenvolvimento muito específico podemos criar nosso próprio template 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 guardadas no nosso computador através do comando docker image ls
InputPython!docker image lsCopied
REPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 2d389e545974 8 hours ago 142MBubuntu latest 2dc39ba059dc 11 days ago 77.8MBhello-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 ser baixada do que o resto
Outra coluna que podemos ver é a de TAG, isso indica a versão da imagem. Em todas aparece latest, isso quer dizer que é a última. Ou seja, no momento de baixá-la, nós baixamos a última versão que há no Docker Hub. Isso em um ambiente de desenvolvimento não é ideal, porque nós podemos baixar uma imagem do Ubuntu e, se não especificarmos a versão, é baixada a última, por exemplo a 20.04. Mas depois de um tempo alguém pode querer desenvolver contigo e baixar essa imagem, mas, ao não especificar a versão, será baixada novamente a última, que no caso pode ser a 22.04. Isso pode dar origem a problemas e a que coisas que para uma das pessoas funcione e para a outra não
Podemos ver todas as imagens que existem no Docker Hub acessando https://hub.docker.com/. Lá você poderá procurar a imagem que melhor se adapte ao projeto que você queira fazer. Se navegarmos até a imagem do Ubuntu, por exemplo, podemos ver as versões (tags) das imagens.
Vamos 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 fará o download 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
InputPython!docker pull ubuntu:20.04Copied
20.04: Pulling from library/ubuntuDigest: sha256:35ab2bf57814e9ff49e365efd5a5935b6915eede5c7f8581e9e1b85e0eecbe16[1AStatus: Downloaded newer image for ubuntu:20.04docker.io/library/ubuntu:20.04
Se voltarmos a listar as imagens, vemos que agora temos duas imagens do Ubuntu, uma com a tag 20.04 e outra com a tag latest
InputPython!docker image lsCopied
REPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 2d389e545974 8 hours ago 142MBubuntu latest 2dc39ba059dc 11 days ago 77.8MBubuntu 20.04 a0ce5a295b63 11 days ago 72.8MBhello-world latest feb5d9fea6a5 11 months ago 13.3kB
Criar imagens por meio de Dockerfile
Criamos um diretório no host chamado dockerImages para trabalhar nele
InputPython!mkdir dockerImagesCopied
Criamos um arquivo Dockerfile com o qual criaremos uma imagem
InputPython!touch dockerImages/DockerfileCopied
Abrimos o arquivo criado com nosso editor preferido e escrevemos o seguinte:
FROM ubuntu:latestIsso diz ao Docker para criar a imagem com base na imagem latest do Ubuntu
A seguir, escrevemos um comando que será executado em tempo de compilação
RUN touch /test.txtIsso quer dizer 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.txtCompilamos o Dockerfile mediante o comando build, com a opção -t podemos atribuir-lhe um tag. Por último, é preciso indicar o caminho do contexto de build; mais adiante explicaremos isso
InputPython!docker build -t ubuntu:test ./dockerImagesCopied
Sending build context to Docker daemon 2.048kBStep 1/2 : FROM ubuntu:latest---> 2dc39ba059dcStep 2/2 : RUN touch /test.txt---> Using cache---> a78cf3ea16d8Successfully built a78cf3ea16d8Successfully tagged ubuntu:testUse 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Como vemos, ele é compilado em 2 etapas, cada uma tem um id, cada um desses ids são camadas da imagem, isso também veremos mais adiante
Voltamos a ver as imagens que temos guardadas no nosso computador e aparece a que acabámos de criar
InputPython!docker image lsCopied
REPOSITORY TAG IMAGE ID CREATED SIZEubuntu test a78cf3ea16d8 8 minutes ago 77.8MBnginx latest 2d389e545974 8 hours ago 142MBubuntu latest 2dc39ba059dc 11 days ago 77.8MBubuntu 20.04 a0ce5a295b63 11 days ago 72.8MBhello-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 em tempo de 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á tem esse arquivo. Ele não é criado quando o contêiner é iniciado
Saímos do contêiner
root@b57b9d4eedeb:/# exit
exitComo já temos uma imagem, poderíamos enviá-la para o Docker Hub, mas vamos listar as imagens novamente antes disso.
InputPython!docker image lsCopied
REPOSITORY TAG IMAGE ID CREATED SIZEubuntu test a78cf3ea16d8 20 minutes ago 77.8MBnginx latest 2d389e545974 8 hours ago 142MBubuntu latest 2dc39ba059dc 11 days ago 77.8MBubuntu 20.04 a0ce5a295b63 11 days ago 72.8MBhello-world latest feb5d9fea6a5 11 months ago 13.3kB
Se virmos, ele 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, por isso no Docker Hub precisamos criar uma conta para poder enviar a imagem para o nosso repositório. No meu caso, meu repositório se chama maximofn, então altero o repositório da imagem por meio do comando tag, indicando a imagem da qual queremos mudar o repositório e o novo repositório. No novo repositório, costuma-se indicar o nome do repositório seguido do tipo de imagem e da tag, no meu caso maximofn/ubuntu:test
InputPython!docker tag ubuntu:test maximofn/ubuntu:testCopied
Se agora voltarmos a listar as imagens
InputPython!docker image lsCopied
REPOSITORY TAG IMAGE ID CREATED SIZEubuntu test a78cf3ea16d8 24 minutes ago 77.8MBmaximofn/ubuntu test a78cf3ea16d8 24 minutes ago 77.8MBnginx latest 2d389e545974 8 hours ago 142MBubuntu latest 2dc39ba059dc 11 days ago 77.8MBubuntu 20.04 a0ce5a295b63 11 days ago 72.8MBhello-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 baixar imagens do Docker Hub. Se você não tiver um Docker ID, acesse https://hub.docker.com para criar um.
Nome de usuário: maximofn
Senha:
Login realizado com sucessoAgora podemos enviar a imagem por meio do comando push
InputPython!docker push maximofn/ubuntu:testCopied
The push refers to repository [docker.io/maximofn/ubuntu]06994357: Preparing06994357: Pushed from library/ubuntu test: digest: sha256:318d83fc3c35ff930d695b0dc1c5ad1b0ea54e1ec6e3478b8ca85c05fd793c4e size: 735
Subiu apenas a primeira camada; a segunda, como a usei a partir da imagem do Ubuntu, o que faz é colocar um ponteiro para essa imagem para não ter camadas enviadas mais de uma vez
É preciso ter em conta que este repositório é público, pelo que não deves enviar imagens com dados sensíveis. Além disso, se uma imagem não tiver uso em 6 meses, será apagada
O sistema de camadas
Através do comando history podemos ver as camadas de uma imagem. Se virmos as camadas da imagem que acabamos de criar, usamos docker history ubuntu:test
InputPython!docker history ubuntu:testCopied
IMAGE CREATED CREATED BY SIZE COMMENTa78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B<missing> 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 disso diz que foi criada há 3 minutos. No entanto, o restante 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.txtNo final, o Dockerfile fica assim:
FROM ubuntu:latest
RUN touch /test.txtRUN rm /test.txtSe recompilarmos, veremos o que acontece
InputPython!docker build -t ubuntu:test ./dockerImagesCopied
Sending build context to Docker daemon 2.048kBStep 1/3 : FROM ubuntu:latest---> 2dc39ba059dcStep 2/3 : RUN touch /test.txt---> Using cache---> a78cf3ea16d8Step 3/3 : RUN rm /test.txt---> Running in c2e6887f2025Removing intermediate container c2e6887f2025---> 313243a9b573Successfully built 313243a9b573Successfully tagged ubuntu:testUse 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Como vemos, há uma camada a mais com a nova linha que adicionamos. Se voltarmos a ver as camadas da imagem com history
InputPython!docker history ubuntu:testCopied
IMAGE CREATED CREATED BY SIZE COMMENT313243a9b573 About a minute ago /bin/sh -c rm /test.txt 0Ba78cf3ea16d8 3 minutes ago /bin/sh -c touch /test.txt 0B2dc39ba059dc 12 days ago /bin/sh -c #(nop) CMD ["bash"] 0B<missing> 12 days ago /bin/sh -c #(nop) ADD file:a7268f82a86219801… 77.8MB
Vemos que as primeiras camadas são iguais às anteriores e ele adicionou uma nova camada com o novo comando
Pesquisa no Docker Hub
Não é necessário acessar a página do Docker Hub para procurar imagens; isso pode ser feito pelo terminal. Para isso, usamos o comando docker search <image name>
InputPython!docker search ubuntuCopied
NAME DESCRIPTION STARS OFFICIAL AUTOMATEDubuntu 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 & we… 98ubuntu/squid Squid is a caching proxy for the Web. Long-t… 66ubuntu/cortex Cortex provides storage for Prometheus. Long… 4ubuntu/apache2 Apache, a secure & extensible open-source HT… 60ubuntu/kafka Apache Kafka, a distributed event streaming … 35ubuntu/mysql MySQL open source fast, stable, multi-thread… 53ubuntu/bind9 BIND 9 is a very flexible, full-featured DNS… 62ubuntu/prometheus Prometheus is a systems and service monitori… 51ubuntu/zookeeper ZooKeeper maintains configuration informatio… 12ubuntu/postgres PostgreSQL is an open source object-relation… 31ubuntu/redis Redis, an open source key-value store. Long-… 19ubuntu/grafana Grafana, a feature rich metrics dashboard & … 9ubuntu/memcached Memcached, in-memory keyvalue store for smal… 5ubuntu/dotnet-aspnet Chiselled Ubuntu runtime image for ASP.NET a… 11ubuntu/dotnet-deps Chiselled Ubuntu for self-contained .NET & A… 11ubuntu/prometheus-alertmanager Alertmanager handles client alerts from Prom… 9ubuntu/dotnet-runtime Chiselled Ubuntu runtime image for .NET apps… 10ubuntu/cassandra Cassandra, an open source NoSQL distributed … 2ubuntu/telegraf Telegraf collects, processes, aggregates & w… 4
Uso do Docker para criar aplicativos
Exposição de portas
Anteriormente vimos como podíamos vincular uma porta de um contêiner a uma porta do computador (-p 8080:80). Mas, para que isso seja possível, ao criar a imagem é necessário expor a porta; isso é feito adicionando ao Dockerfile a linha EXPOSE <port>, no caso anterior
EXPOSE 80Usar imagens como base que já tenham portas expostas
Reuso do cache de camadas ao compilar
Ao compilarmos uma imagem, se alguma das camadas que definimos já tiver sido compilada antes, o Docker detecta isso e as utiliza, não voltando a compilá-las. Se voltarmos a compilar a imagem que definimos no Dockerfile, agora levará muito pouco tempo, porque todas as camadas já estão compiladas e o Docker não as recompila.
InputPython!docker build -t ubuntu:test ./dockerImagesCopied
Sending build context to Docker daemon 2.048kBStep 1/3 : FROM ubuntu:latest---> 2dc39ba059dcStep 2/3 : RUN touch /test.txt---> Using cache---> a78cf3ea16d8Step 3/3 : RUN rm /test.txt---> Using cache---> 313243a9b573Successfully built 313243a9b573Successfully tagged ubuntu:testUse '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 este é um caderno Jupyter, ao executar as células, ele fornece informações sobre o tempo que levam para serem executadas. Na vez anterior que compilei a imagem, demorou 1,4 segundos, enquanto agora demorou 0,5 segundos.
Mas se agora eu alterar o Dockerfile, e na primeira linha, onde dizia que nos baseávamos na última versão do Ubuntu e mudamos para a versão 20.04
FROM ubuntu:20.04No final, o Dockerfile fica assim:
FROM ubuntu:20.04
RUN touch /test.txtRUN rm /test.txtSe recompilarmos, vai demorar muito mais
InputPython!docker build -t ubuntu:test ./dockerImagesCopied
Sending build context to Docker daemon 2.048kBStep 1/3 : FROM ubuntu:20.04---> a0ce5a295b63Step 2/3 : RUN touch /test.txt---> Running in a40fe8df2c0dRemoving intermediate container a40fe8df2c0d---> 0bb9b452c11fStep 3/3 : RUN rm /test.txt---> Running in 2e14919f3685Removing intermediate container 2e14919f3685---> fdc248fa833bSuccessfully built fdc248fa833bSuccessfully tagged ubuntu:testUse 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Demorou 1,9 segundos e já não aparece o texto Using cache
Ao alterar a primeira camada, o Docker recompila todas as camadas. Isso pode ser um problema porque, ao desenvolver código, pode ocorrer o seguinte caso
- Desenvolvemos o código no nosso computador
- Ao construir a imagem, copiamos todo o código do nosso computador para o contêiner
- Depois pedimos à imagem que instale 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 tenha que ser recompilada, já que uma camada anterior foi alterada
Para resolver isso, a ideia seria que, na hora de criar a imagem, primeiro peçamos que as bibliotecas sejam instaladas e depois que o código do nosso computador seja copiado para o contêiner. Assim, cada vez que mudarmos o código e voltarmos a compilar a imagem, só será recompilada a camada em que o código é copiado, de modo que a compilação será mais rápida
Poderás pensar que é melhor compartilhar uma pasta entre o host e o contêiner (bind mount) onde teremos o código e assim não é preciso recompilar a imagem toda vez que alterarmos o código. E a resposta é que isso é verdade, só coloquei 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, recompila-se o número mínimo de camadas
Escrever corretamente um Dockerfile
Como vimos, o Docker não recompila camadas de um Dockerfile se já as compilou antes, então as carrega do cache. Vamos ver qual é a forma correta de escrever um Dockerfile para aproveitarmos isso.
Vamos a partir deste Dockerfile para ir comentando possíveis correções
FROM ubuntu
COPY ./sourceCode /sourceCode
RUN apt-get update
RUN apt-get install -y python3 sshCMD ["python3", "/sourceCode/sourceApp/app.py"]Como se pode ver, 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
Copie o código antes da execução
Como dissemos antes, se primeiro copiarmos o código e depois instalarmos o Python, toda vez que fizermos uma alteração no código e compilarmos a imagem, ela será compilada por completo. Mas, se copiarmos o código depois de instalar o Python, toda vez que mudarmos o código e compilarmos a imagem, ela só compilará a partir da cópia do código e não voltará a instalar o Python. Portanto, o Dockerfile deveria passar a ser assim
FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3 sshCOPY ./sourceCode /sourceCode
CMD ["python3", "/sourceCode/sourceApp/app.py"]Copiar apenas o código necessário
Estamos copiando a pasta com todo o código, mas talvez dentro tenhamos código de que não precisamos, por isso devemos copiar apenas o código de que realmente precisemos para a aplicação; dessa maneira, a imagem ocupará menos memória. Assim, o Dockerfile ficaria assim
FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3 sshCOPY ./sourceCode/sourceApp /sourceCode/sourceApp
CMD ["python3", "/sourceCode/sourceApp/app.py"]Atualizar repositórios e instalar Python na mesma linha
Estamos atualizando os repositórios em uma linha e, em outra, instalando python3.
FROM 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 ssh
Instalámos SSH na imagem para poder depurar caso seja necessário, mas isso faz com que a imagem ocupe mais memória. Caso seja necessário depurar, deveríamos entrar no contentor, instalar SSH e, em seguida, depurar. Por isso, removemos a instalação do SSH
FROM 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, fazendo com que a imagem ocupe mais espaço. Então, para evitar isso, adicionamos à instalação --no-install-recommends
FROM 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 atualizados
Atualizamos a lista de repositórios e instalamos o python, mas, uma vez feito isso, já não precisamos da lista de repositórios atualizados, porque o único efeito será fazer com que a imagem ocupe mais espaço; por isso, removemo-los depois de instalar o python e na mesma linha.
FROM 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 Python
Tudo o que fizemos de atualizar a lista de pacotes e instalar Python não é necessário, já que já existem imagens de Python sobre Ubuntu, que seguramente também seguiram boas práticas, que até o fizeram melhor do que nós e que foi escaneada em busca de vulnerabilidades pelo Docker Hub. Por isso removemos tudo isso e partimos de uma imagem de Python
DE python
COPY ./sourceCode/sourceApp /sourceCode/sourceAppCMD ["python3", "/sourceCode/sourceApp/app.py"]Especificar a imagem do Python
Ao não especificar a imagem do Python, está sendo baixada a mais recente, mas, dependendo de quando o contêiner for construído, pode-se baixar uma ou outra. Portanto, é necessário adicionar o tag com a versão do Python desejada
FROM python:3.9.18
COPY ./sourceCode/sourceApp /sourceCode/sourceAppCMD ["python3", "/sourceCode/sourceApp/app.py"]Escolher uma tag pequena
Escolhemos a tag 3.9.18, mas essa versão do Python tem uma série de bibliotecas que talvez não precisemos; por isso, podemos usar as versões 3.9.18-slim, que têm muito menos bibliotecas instaladas, ou a versão 3.9.18-alpine, que é uma versão do Python sobre Alpine e não sobre Ubuntu. Alpine é uma distribuição Linux muito leve, que tem muito poucos pacotes instalados e que costuma ser muito usada em contêineres Docker para ocuparem muito pouco espaço
A imagem 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"]Indicar o workspace
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"]Indicar o workspace
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
Tínhamos criado uma pasta chamada dockerHostFolder na qual tínhamos compartilhado arquivos entre o host e um contêiner. Dentro, além disso, deveriam haver três arquivos
InputPython!ls dockerHostFolderCopied
bindFile.txt fileExtract.txt text.txt
Vamos aproveitar o arquivo text.txt para ver isso. Vamos ver o que há dentro de text.txt
InputPython!cat dockerHostFolder/text.txtCopied
Não há saída, o arquivo está vazio. Vamos criar novamente um contêiner do Ubuntu compartilhando a pasta dockerHostFolder
InputPython!docker run --name alwaysup -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/dockerContainerFolder ubuntu tail -f /dev/nullCopied
24adbded61f507cdf7f192eb5e246e43ee3ffafc9944b7c57918eb2d547dff19
Vemos que o contêiner está em execução
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES24adbded61f5 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 virmos o que há dentro do arquivo no contêiner, veremos o mesmo texto
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
Olá mundoAgora editamos o arquivo no contêiner e saímos do contêiner
root@24adbded61f5:/# echo hola contenedor > dockerContainerFolder/text.txt
root@24adbded61f5:/# cat dockerContainerFolder/text.txt
olá contêiner
root@24adbded61f5:/# exit
exitSe olharmos para o arquivo no host, veremos o texto que escrevemos no contêiner
InputPython!cat dockerHostFolder/text.txtCopied
hola contenedor
Apagamos o contêiner
InputPython!docker rm -f alwaysupCopied
alwaysup
Conectar contêineres por rede
No caso de quisermos ter vários contêineres em execução e quisermos que eles se comuniquem, podemos fazer com que se comuniquem por rede. O Docker nos dá a possibilidade de fazer isso por meio de suas redes virtuais
Vamos ver quais redes o Docker tem por meio do comando docker network ls
InputPython!docker network lsCopied
NETWORK ID NAME DRIVER SCOPEde6e8b7b737e bridge bridge localda1f5f6fccc0 host host locald3b0d93993c0 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 já
- 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
InputPython!docker network create --attachable myNetworkCopied
2f6f3ddbfa8642e9f6819aa0965c16339e9e910be7bcf56ebb718fcac324cc27
Podemos inspecioná-la mediante o comando docker network inspect <name>
InputPython!docker network inspect myNetworkCopied
[{"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 precisamos criar dois contêineres para que possam se comunicar.
Vamos a criar um novo contêiner, que chamaremos container1, com uma pasta compartilhada e que em seu interior se chamará folder1
InputPython!docker run --name container1 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder1 ubuntu tail -f /dev/nullCopied
a5fca8ba1e4ff0a67002f8f1b8cc3cd43185373c2a7e295546f774059ad8dd1a
Agora criamos outro contêiner, chamado container2, com outra pasta compartilhada, mas que se chame folder2
InputPython!docker run --name container2 -d -v ~/Documentos/web/portafolio/posts/dockerHostFolder:/folder2 ubuntu tail -f /dev/nullCopied
6c8dc18315488ef686f7548516c19b3d716728dd8a173cdb889ec0dd082232f9
Vemos os contêineres em execução e vemos que estão os dois
InputPython!docker psCopied
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES6c8dc1831548 ubuntu "tail -f /dev/null" 3 seconds ago Up 2 seconds container2a5fca8ba1e4f 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>
InputPython!docker network connect myNetwork container1Copied
InputPython!docker network connect myNetwork container2Copied
Para verificar se foram conectados corretamente, podemos inspecionar a rede, mas filtrando pelos contêineres conectados
$ docker network inspect --format '{{.Containers}}' myNetwork
map
[
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 contêiner container1 tem o IP 172.18.0.2 e o contêiner 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
...
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, a partir do contêiner container1, fazemos um ping para o 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 time=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 time=0.056 ms
64 bytes de 172.18.0.3: icmp_seq=4 ttl=64 time=0.060 ms
^C
--- estatísticas de ping de 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 msE, a partir do contêiner container2, fazemos um ping para o 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 de 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 msMas há uma coisa melhor que o Docker nos permite fazer: 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, a partir do contêiner container1, fazemos um ping para o 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 from 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 time=0.053 ms
^C
--- estatísticas de 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 msComo vemos, o Docker sabe que o IP do contêiner container2 é 172.18.0.3
E do contêiner container2 fazemos um ping para o 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 tiempo=0.051 ms
64 bytes from 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 de ping do container1 ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3057ms
rtt min/média/máx/desvio = 0.051/0.054/0.058/0.003 msComo vemos, o Docker sabe que o IP do contêiner container1 é 172.18.0.2
Saímos dos contêineres e os apagamos
InputPython!docker rm -f container1 container2Copied
container1container2
Também apagamos a rede que criamos
InputPython!docker network rm myNetworkCopied
myNetwork
Uso de GPUs
Para poder usar as GPUs do host dentro dos contêineres Docker, é necessário realizar os passos descritos na página de instalação do Nvidia container toolkit
Configurar o repositório e a chave GPG
Temos que configurar o repositório do nvidia container toolkit e a chave GPG; para isso executamos o seguinte comando no console
distribution=$(. /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.listInstalação do nvidia container toolkit
Uma vez que atualizamos o repositório e a chave, atualizamos os repositórios por meio do comando
sudo apt updateE instalamos nvidia container toolkit
sudo apt install -y nvidia-docker2Reinicialização do Docker
Uma vez terminemos, temos que reiniciar o daemon do Docker mediante
sudo systemctl restart dockerUso de GPUs
Agora que configuramos o Docker para poder usar as GPUs do host dentro dos contêineres, podemos testá-lo usando a opção --gpus all. Se houver mais de uma GPU e quiser usar apenas 1, isso deve ser especificado, mas por enquanto aqui só explicamos como usar todas
Criamos um contêiner que não vai ser executado em segundo plano, mas sim o que ele vai fazer é executar o comando nvidia-smi para que possamos ver se ele tem acesso às GPUs
InputPython!docker run --name container_gpus --gpus all ubuntu nvidia-smiCopied
Unable to find image 'ubuntu:latest' locallylatest: Pulling from library/ubuntu6a12be2b: Pull complete .54MB/29.54MBBDigest: sha256:aabed3296a3d45cede1dc866a24476c4d7e093aa806263c27ddaadbdce3c1054Status: Downloaded newer image for ubuntu:latestMon 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
InputPython!doker rm container_gpusCopied
---
➡️ **Continue na Parte 2: Docker Compose e temas avançados**, onde você orquestrará vários contêineres ao mesmo tempo e aprofundará em Docker.