Executando Seu Primeiro Contêiner

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Neste laboratório, você executará seu primeiro contêiner Docker.

Contêineres são apenas um processo (ou um grupo de processos) executando em isolamento. O isolamento é alcançado por meio de namespaces Linux, grupos de controle (cgroups), seccomp e SELinux. Observe que os namespaces Linux e os grupos de controle são integrados ao kernel Linux! Além do próprio kernel Linux, não há nada de especial sobre contêineres.

O que torna os contêineres úteis são as ferramentas que os cercam. Para estes laboratórios, usaremos o Docker, que tem sido uma ferramenta amplamente adotada para usar contêineres para construir aplicações. O Docker fornece aos desenvolvedores e operadores uma interface amigável para construir, enviar e executar contêineres em qualquer ambiente com um mecanismo Docker. Como o cliente Docker requer um mecanismo Docker, uma alternativa é usar o Podman, que é um mecanismo de contêiner sem daemon para desenvolver, gerenciar e executar contêineres OCI e é capaz de executar contêineres como root ou no modo rootless. Por essas razões, recomendamos o Podman, mas devido à adoção, este laboratório ainda usa o Docker.

Na primeira parte deste laboratório, executaremos nosso primeiro contêiner e aprenderemos como inspecioná-lo. Poderemos testemunhar o isolamento de namespace que adquirimos do kernel Linux.

Depois de executarmos nosso primeiro contêiner, mergulharemos em outros usos de contêineres. Você pode encontrar muitos exemplos disso na Docker Store, e executaremos vários tipos diferentes de contêineres no mesmo host. Isso nos permitirá ver o benefício do isolamento - onde podemos executar vários contêineres no mesmo host sem conflitos.

Usaremos alguns comandos Docker neste laboratório. Para obter a documentação completa sobre os comandos disponíveis, consulte a documentação oficial.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 90%. Recebeu uma taxa de avaliações positivas de 92% dos estudantes.

Começando

Abra um terminal na VM do LabEx e execute docker -h, que mostrará a página de ajuda para a CLI do Docker.

$ docker -h
Flag shorthand -h has been deprecated, please use --help

Usage: docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

...

Management Commands:
builder Manage builds
config Manage Docker configs
container Manage containers
engine Manage the docker engine
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes

A linha de comando do Docker pode ser usada para gerenciar vários recursos do Docker Engine. Neste laboratório, focaremos principalmente no comando container.

Instale o podman na sua VM do LabEx.

sudo apt-get update
sudo apt-get install podman -y

Se o podman estiver instalado, você pode executar o comando alternativo para comparação.

sudo podman -h

Você pode, adicionalmente, revisar a versão da sua instalação do Docker com docker version

docker version

Client:
Version: 20.10.21
...

Server:
Engine:
Version: 20.10.21
...

Observe que o Docker instala tanto um Client quanto um Server: Docker Engine. Por exemplo, se você executar o mesmo comando para podman, verá apenas uma versão da CLI, porque o podman é executado sem daemon e depende de um runtime de contêiner compatível com OCI (runc, crun, runv etc.) para interagir com o sistema operacional para criar os contêineres em execução.

sudo podman version --events-backend=none
Version: 3.4.4
API Version: 3.4.4
Go Version: go1.17.3
Built: Thu Jan 1 08:00:00 1970
OS/Arch: linux/amd64

Execute Seu Primeiro Contêiner

Vamos usar a CLI do Docker para executar nosso primeiro contêiner.

Abra um terminal na VM do LabEx.

Execute o comando.

docker container run -t ubuntu top

Use o comando docker container run para executar um contêiner com a imagem ubuntu usando o comando top. As flags -t alocam um pseudo-TTY, que precisamos para que o top funcione corretamente.

$ docker container run -it ubuntu top
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
aafe6b5e13de: Pull complete
0a2b43a72660: Pull complete
18bdd1e546d2: Pull complete
8198342c3e05: Pull complete
f56970a44fd4: Pull complete
Digest: sha256:f3a61450ae43896c4332bda5e78b453f4a93179045f20c8181043b26b5e79028
Status: Downloaded newer image for ubuntu:latest

O comando docker run resultará primeiro em um docker pull para baixar a imagem ubuntu para o seu host. Depois de baixada, ele iniciará o contêiner. A saída para o contêiner em execução deve ser semelhante a esta:

top - 20:32:46 up 3 days, 17:40,  0 users,  load average: 0.00, 0.01, 0.00
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2046768 total,   173308 free,   117248 used,  1756212 buff/cache
KiB Swap:  1048572 total,  1048572 free,        0 used.  1548356 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
      1 root      20   0   36636   3072   2640 R   0.3  0.2   0:00.04 top

top é um utilitário linux que imprime os processos em um sistema e os ordena por consumo de recursos. Observe que há apenas um único processo nesta saída: é o próprio processo top. Não vemos outros processos do nosso host nesta lista por causa do isolamento do namespace PID.

Contêineres usam namespaces Linux para fornecer isolamento de recursos do sistema de outros contêineres ou do host. O namespace PID fornece isolamento para IDs de processo. Se você executar top dentro do contêiner, notará que ele mostra os processos dentro do namespace PID do contêiner, que é muito diferente do que você pode ver se executasse top no host.

Embora estejamos usando a imagem ubuntu, é importante notar que nosso contêiner não tem seu próprio kernel. Ele usa o kernel do host e a imagem ubuntu é usada apenas para fornecer o sistema de arquivos e as ferramentas disponíveis em um sistema ubuntu.

Inspecione o contêiner com docker container exec

O comando docker container exec é uma maneira de "entrar" nos namespaces de um contêiner em execução com um novo processo.

Abra um novo terminal. selecione Terminal > New Terminal.

No novo terminal, use o comando docker container ls para obter o ID do contêiner em execução que você acabou de criar.

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b3ad2a23fab3 ubuntu "top" 29 minutes ago Up 29 minutes goofy_nobel

Em seguida, use esse ID para executar bash dentro desse contêiner usando o comando docker container exec. Como estamos usando bash e queremos interagir com este contêiner do nosso terminal, use as flags -it para executar no modo interativo enquanto aloca um psuedo-terminal.

$ docker container exec -it ID < CONTAINER > bash
root@b3ad2a23fab3:/#

E Voilá! Acabamos de usar o comando docker container exec para "entrar" nos namespaces do nosso contêiner com nosso processo bash. Usar docker container exec com bash é um padrão comum para inspecionar um contêiner Docker.

Observe a mudança no prefixo do seu terminal. Ex. root@b3ad2a23fab3:/. Esta é uma indicação de que estamos executando bash "dentro" do nosso contêiner.

Observação: Isso não é o mesmo que fazer ssh em um host separado ou em uma VM. Não precisamos de um servidor ssh para nos conectar com um processo bash. Lembre-se de que os contêineres usam recursos de nível de kernel para obter isolamento e que os contêineres são executados em cima do kernel. Nosso contêiner é apenas um grupo de processos em execução em isolamento no mesmo host, e podemos usar docker container exec para entrar nesse isolamento com o processo bash. Após executar docker container exec, o grupo de processos em execução em isolamento (ou seja, nosso contêiner) inclui top e bash.

Do mesmo terminal, execute ps -ef para inspecionar os processos em execução.

root@b3ad2a23fab3:/## ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 20:34 ? 00:00:00 top
root 17 0 0 21:06 ? 00:00:00 bash
root 27 17 0 21:14 ? 00:00:00 ps -ef

Você deve ver apenas o processo top, o processo bash e nosso processo ps.

Para comparação, saia do contêiner e execute ps -ef ou top no host. Esses comandos funcionarão no linux ou mac. Para Windows, você pode inspecionar os processos em execução usando tasklist.

root@b3ad2a23fab3:/## exit
exit
$ ps -ef
## Lots of processes!

Análise Técnica Detalhada PID é apenas um dos namespaces Linux que fornece aos contêineres isolamento de recursos do sistema. Outros namespaces Linux incluem:

  • MNT - Montar e desmontar diretórios sem afetar outros namespaces
  • NET - Contêineres têm sua própria pilha de rede
  • IPC - Mecanismos de comunicação entre processos isolados, como filas de mensagens.
  • User - Visão isolada dos usuários no sistema
  • UTC - Definir nome de host e nome de domínio por contêiner

Esses namespaces juntos fornecem o isolamento para contêineres que lhes permite executar juntos com segurança e sem conflito com outros contêineres em execução no mesmo sistema. Em seguida, demonstraremos diferentes usos de contêineres e o benefício do isolamento ao executarmos vários contêineres no mesmo host.

Observação: Namespaces são um recurso do kernel linux. Mas o Docker permite que você execute contêineres no Windows e Mac... como isso funciona? O segredo é que embutido no produto Docker ou no mecanismo Docker está um subsistema linux. O Docker abriu o código-fonte desse subsistema linux para um novo projeto: LinuxKit. Ser capaz de executar contêineres em muitas plataformas diferentes é uma vantagem de usar as ferramentas Docker com contêineres.

Além de executar contêineres linux no Windows usando um subsistema linux, contêineres nativos do Windows agora são possíveis devido à criação de primitivas de contêiner no sistema operacional Windows. Contêineres nativos do Windows podem ser executados no Windows 10 ou Windows Server 2016 ou posterior.

Observação: se você executar este exercício em um terminal em contêiner e executar o comando ps -ef no terminal, ainda verá um conjunto limitado de processos após sair do comando exec. Você pode tentar executar o comando ps -ef em um terminal em sua máquina local para ver todos os processos.

Limpe o contêiner executando os processos top digitando: <ctrl>-c, liste todos os contêineres e remova os contêineres por seu ID.

docker ps -a

docker rm <CONTAINER ID>

Executar Vários Contêineres

Explorar o Docker Hub

O Docker Hub é o registro central público para imagens Docker, que contém imagens da comunidade e oficiais.

Ao pesquisar imagens, você encontrará filtros para imagens "Docker Certified", "Verified Publisher" e "Official Images". Selecione o filtro "Docker Certified" para encontrar imagens que são consideradas prontas para empresas e são testadas com o produto Docker Enterprise Edition. É importante evitar o uso de conteúdo não verificado da Docker Store ao desenvolver suas próprias imagens que se destinam a serem implantadas no ambiente de produção. Essas imagens não verificadas podem conter vulnerabilidades de segurança ou possivelmente até mesmo software malicioso.

Na etapa 2 deste laboratório, iniciaremos alguns contêineres usando algumas imagens verificadas do Docker Hub: servidor web nginx e banco de dados mongo.

Executar um Servidor Nginx

Vamos executar um contêiner usando a imagem Nginx oficial do Docker Hub.

docker container run --detach --publish 8080:80 --name nginx nginx

Estamos usando algumas novas flags aqui. A flag --detach executará este contêiner em segundo plano. A flag publish publica a porta 80 no contêiner (a porta padrão para nginx), através da porta 8080 em nosso host. Lembre-se de que o namespace NET dá aos processos do contêiner sua própria pilha de rede. A flag --publish é um recurso que nos permite expor a rede através do contêiner para o host.

Como você sabe que a porta 80 é a porta padrão para nginx? Porque ela está listada na documentação no Docker Hub. Em geral, a documentação para as imagens verificadas é muito boa, e você vai querer consultá-las ao executar contêineres usando essas imagens.

Também estamos especificando a flag --name, que nomeia o contêiner. Cada contêiner tem um nome, se você não especificar um, o Docker atribuirá um aleatoriamente para você. Especificar seu próprio nome facilita a execução de comandos subsequentes em seu contêiner, pois você pode referenciar o nome em vez do ID do contêiner. Por exemplo: docker container inspect nginx em vez de docker container inspect 5e1.

Como esta é a primeira vez que você está executando o contêiner nginx, ele baixará a imagem nginx da Docker Store. Contêineres subsequentes criados a partir da imagem Nginx usarão a imagem existente localizada em seu host.

Nginx é um servidor web leve. Você pode acessar o servidor nginx na aba Web 8080 da VM do LabEx. Alterne-a e atualize a página para ver a saída do nginx.

step 2 nginx

Executar um Servidor de Banco de Dados mongo

Agora, execute um servidor mongoDB. Usaremos a imagem mongoDB oficial do Docker Hub. Em vez de usar a tag latest (que é a padrão se nenhuma tag for especificada), usaremos uma versão específica da imagem mongo: 4.4.

docker container run --detach --publish 8081:27017 --name mongo mongo:4.4

Novamente, como esta é a primeira vez que estamos executando um contêiner mongo, baixaremos a imagem mongo da Docker Store. Estamos usando a flag --publish para expor a porta 27017 mongo em nosso host. Temos que usar uma porta diferente de 8080 para o mapeamento do host, pois essa porta já está exposta em nosso host. Novamente, consulte os documentos oficiais no Docker Hub para obter mais detalhes sobre como usar a imagem mongo.

Veja a saída do mongoDB usando 0.0.0.0:8081 no navegador da Web. Você deve ver uma mensagem que retornará um aviso do MongoDB.

MongoDB server output warning

Verifique seus contêineres em execução com docker container ls

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6777df89fea nginx "nginx -g 'daemon ..." Less than a second ago Up 2 seconds 0.0.0.0:8080- nginx > 80/tcp
ead80a0db505 mongo "docker-entrypoint..." 17 seconds ago Up 19 seconds 0.0.0.0:8081- mongo > 27017/tcp
af549dccd5cf ubuntu "top" 5 minutes ago Up 5 minutes priceless_kepler

Você deve ver que tem um contêiner de servidor web Nginx e um contêiner MongoDB em execução em seu host. Observe que não configuramos esses contêineres para se comunicarem entre si.

Você pode ver os nomes "nginx" e "mongo" que demos aos nossos contêineres e o nome aleatório (no meu caso "priceless_kepler") que foi gerado para o contêiner ubuntu. Você também pode ver os mapeamentos de porta que especificamos com a flag --publish. Para obter mais informações sobre esses contêineres em execução, você pode usar o comando docker container inspect [container id.

Uma coisa que você pode notar é que o contêiner mongo está executando o comando docker-entrypoint. Este é o nome do executável que é executado quando o contêiner é iniciado. A imagem mongo requer alguma configuração prévia antes de iniciar o processo do banco de dados. Você pode ver exatamente o que o script faz olhando para ele no github. Normalmente, você pode encontrar o link para a fonte do github na página de descrição da imagem no site da Docker Store.

Os contêineres são autossuficientes e isolados, o que significa que podemos evitar possíveis conflitos entre contêineres com diferentes dependências de sistema ou tempo de execução. Por exemplo: implantar um aplicativo que usa Java 7 e outro aplicativo que usa Java 8 no mesmo host. Ou executar vários contêineres nginx que todos têm a porta 80 como suas portas de escuta padrão (se expostas no host usando a flag --publish, as portas selecionadas para o host precisarão ser exclusivas). Os benefícios do isolamento são possíveis por causa dos Namespaces Linux.

Observação: Você não precisou instalar nada em seu host (além do Docker) para executar esses processos! Cada contêiner inclui as dependências de que precisa dentro do contêiner, então você não precisa instalar nada em seu host diretamente.

Executar vários contêineres no mesmo host nos dá a capacidade de utilizar totalmente os recursos (cpu, memória, etc.) disponíveis em um único host. Isso pode resultar em enormes economias de custos para uma empresa.

Embora a execução de imagens diretamente do Docker Hub possa ser útil às vezes, é mais útil criar imagens personalizadas e consultar as imagens oficiais como ponto de partida para essas imagens. Mergulharemos na construção de nossas próprias imagens personalizadas no Lab 2.

Limpeza

Completar este laboratório resulta em um monte de contêineres em execução em seu host. Vamos limpá-los.

Primeiro, obtenha uma lista dos contêineres em execução usando docker container ls.

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6777df89fea nginx "nginx -g 'daemon ..." 3 minutes ago Up 3 minutes 0.0.0.0:8080- nginx > 80/tcp
ead80a0db505 mongo "docker-entrypoint..." 3 minutes ago Up 3 minutes 0.0.0.0:8081- mongo > 27017/tcp
af549dccd5cf ubuntu "top" 8 minutes ago Up 8 minutes priceless_kepler

Em seguida, execute docker container stop [container id] para cada contêiner na lista. Você também pode usar os nomes dos contêineres que você especificou antes.

$ docker container stop d67 ead af5
d67
ead
af5

Observação: Você só precisa referenciar dígitos suficientes do ID para serem exclusivos. Três dígitos são quase sempre suficientes.

Remova os contêineres parados

docker system prune é um comando muito útil para limpar seu sistema. Ele removerá quaisquer contêineres parados, volumes e redes não utilizados e imagens pendentes.

$ docker system prune
WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images
Are you sure you want to continue? [y/N] y
Deleted Containers:
7872fd96ea4695795c41150a06067d605f69702dbcb9ce49492c9029f0e1b44b
60abd5ee65b1e2732ddc02b971a86e22de1c1c446dab165462a08b037ef7835c
31617fdd8e5f584c51ce182757e24a1c9620257027665c20be75aa3ab6591740

Total reclaimed space: 12B

Resumo

Neste laboratório, você criou seus primeiros contêineres Ubuntu, Nginx e MongoDB.

Principais Conclusões

  • Os contêineres são compostos por namespaces Linux e grupos de controle que fornecem isolamento de outros contêineres e do host.
  • Devido às propriedades de isolamento dos contêineres, você pode agendar muitos contêineres em um único host sem se preocupar com dependências conflitantes. Isso facilita a execução de vários contêineres em um único host: utilizando totalmente os recursos alocados para esse host e, em última análise, economizando algum dinheiro com os custos do servidor.
  • Evite usar conteúdo não verificado da Docker Store ao desenvolver suas próprias imagens, pois essas imagens podem conter vulnerabilidades de segurança ou possivelmente até mesmo software malicioso.
  • Os contêineres incluem tudo o que precisam para executar os processos dentro deles, portanto, não há necessidade de instalar dependências adicionais diretamente em seu host.