Aprenda Docker

  • Página Inicial
  • Contato!
  • Tudo sobre Docker Parte 1!
  • Tudo sobre Docker Parte 2!
  • Tudo sobre Docker Parte 3!
  • Tudo sobre Docker Parte 1

    Entenda o Docker Agora

    Muitas vezes, quando criamos um programa e ele funciona direitinho na nossa máquina, ele acaba dando problemas ao portar para outros sistemas. Alguns problemas comuns são versões de sistemas, interpretadores (como Node e Python), entre outras. É para isso que o Docker existe.

    Assim como um navio, que contém containers (caixas de aço para guardar coisas como ouro, alimentos, eletrônicos, etc.) para facilitar o transporte desses produtos, o Docker funciona de forma semelhante. Colocamos tudo que precisamos dentro de um container para que um programa rode perfeitamente como planejado (como por exemplo, uma versão do Python específica).

    Diferente de uma máquina virtual, que a gente emula um sistema operacional completo dentro de outro sistema, e que por isso acaba sendo mais pesado, os containers do Docker apenas colocam o que o programa precisa dentro dele, e por isso, é bem mais leve.

    Resumindo, o Docker padroniza o ambiente de desenvolvimento e produção, garantindo que a aplicação rode em qualquer lugar.

    Instalação Completa e Sem Erros

    Para usar o Docker no Windows, siga as instruções para instalar o Linux no WSL do Windows e a imagem do Ubuntu nele: https://learn.microsoft.com/pt-br/windows/wsl/install

    No caso do Linux, o Docker roda nativamente. Para isso, digite esses comandos na sequência:

    
    curl -fsSL https://get.docker.com -o get-docker.sh
    
    sudo sh get-docker.sh 
    
    sudo usermod -aG docker $USER
    
    

    Depois, baixe e instale o Docker Desktop aqui: https://docs.docker.com/desktop/

    Na linha de comando, digite docker --version pra se tudo está instalado corretamente, visualizando a versão do mesmo. Ao abrir o Docker Desktop no Windows, vá em Settings, Resources e WSL Integration e marque as opções Enable Integration with my Default WSL Distro e Ubuntu.

    Abra o Docker Desktop e digite na linha de comando o comando docker run hello-world, ele não vai encontrar o container no computador e vai baixar o container com a imagem indicada (Hello World), que poderá ser visualizado pelo Docker Desktop. O comando run cria um novo container a partir de uma imagem. Caso rode o mesmo comando pela segunda vez, ele já vai identificar a imagem já encontrada, e criará um segundo container com a mesma imagem.

    PS: Caso precise atualizar o WSL, digite no prompt de comando do Windows o comando wsl --update como administrador.

    Conheça o Poder do docker run

    Vamos primeiramente, excluir os containers criados, e depois a imagem, pelo Docker Desktop mesmo, pra começarmos do zero. Abra a linha de comando e rode novamente o comando docker run hello-world.

    Note que, ao executar o comando, primeiro ele exibirá que não conseguiu encontrar a última versão do hello-world localmente, então, a partir daí, ele vai obter essa imagem do Docker Hub e baixará no nosso computador, e mostrará a notificação de download da mesma. O hello-world, tudo que ele faz, é exibir uma mensagem de boas-vindas, que também é exibida. Ao baixar a imagem, ele automaticamente criará um container com essa imagem. Lembrando que o comando run obterá a imagem, caso rode o comando novamente, ele encontrará a imagem no nosso computador e criará um novo container com ela.

    PS: Não confunda, o run no comando docker é pra obter imagens e criar containers, não exatamente rodar a imagem.

    Lembrando que, caso você tente deletar uma imagem que esteja sendo utilizada por algum container, ele dará erro e não deixará excluir a imagem. Para isso devemos primeiramente excluir todas os containers que utilizam essa imagem primeiro.

    Vamos baixar a imagem do Ubuntu e criar um container digitando docker run ubuntu, ele fará o mesmo processo anterior, mas não executará igual ao hello-world. Ele baixará uma versão simplificada do Ubuntu.

    Para usar o Docker no modo interativo, podemos digitar o comando docker run -it ubuntu bash, ele rodará o terminal (Bash) do Ubuntu baixado. Nesse caso, a imagem do Ubuntu estará sendo executada, e deveremos usar o comandos do Linux, e não do Windows, no caso. Ele criará um novo container com a mesma imagem também.

    PS: Quando um container está sendo executado, ele ficará com a bolinha verde e com o símbolo de stop no Docker Desktop.

    Digite esses comandos do Linux, para exemplificar o uso de comandos no mesmo:

    
    ls
    
    touch arquivo.txt
    
    ls
    
    apt update
    
    apt install nano -y
    
    nano arquivo.txt
    
    # Digite algo no arquivo pelo Nano, depois dê Ctrl O e Ctrl X
    
    cat arquivo.txt
    
    exit
    
    

    O comando acima, criará um arquivo de texto dentro da imagem do nosso Linux, esse arquivo não estará no nosso sistema padrão (no caso, o Windows).

    Digite o comando docker run -it ubuntu bash, como ele criará um novo container, não encontraremos o arquivo criado, pois ele estará num outro container criado anteriormente, que tem um nome específico (geralmente uns nomes meio doidos criados pelo próprio Docker).

    PS: Podemos baixar a imagem sem criar container com docker pull ubuntu, e podemos procurar por uma imagem com o comando docker search ubuntu, substituindo o ubuntu pela imagem desejada.

    A Teoria Fundamental (Imagem vs Container)

    Ao rodar docker run -it ubuntu bash, ele criará um novo container com a imagem anteriormente baixada pro Docker, e por isso, não encontraremos os arquivos criados no container anterior. Isso é porque o comando run sempre cria um novo container de uma imagem especificada.

    Numa analogia com uma linguagem orientada a objetos, a imagem é como se fosse uma classe de uma linguagem orientada a objetos, ou seja, é como se fosse o molde de alguma coisa, sendo apenas um modelo. O container seria como um objeto, uma instância de uma classe, ao rodar run, é como se ele fosse o new, ou seja, criará um novo objeto (no caso, container), que será independente dos outros, mesmo sendo oriundos da mesma classe (no caso, imagem).

    Em outras palavras, a imagem é somente-leitura, e o container é criado a partir de uma imagem. Ao deletar um container, a imagem não é afetada. Uma imagem só pode ser deletada caso nenhum container a esteja utilizando.

    Existem formas de persistir os dados em um container, com os volumes, que veremos mais pra frente.

    Aprenda a Domar seus Containers Agora

    Mesmo usando o exit, os containers continuam no Docker. Para listar os containers, usamos o comando docker ps, mas este só mostra os containers que estão rodando. Para visualizar todos os containers independente do status, digite docker ps -a.

    Lembrando que, mesmo parados, os containers ocupam espaço no computador, e ao usar o run, ele criará um novo container.

    Para criar um container com um nome específico, usaremos o comando docker run -it --name nomedocontainer ubuntu, para criar um container com o nome que você quiser, com a imagem do Ubuntu baixada. Ele inclusive, já entrará no Ubuntu e abrirá o Bash dele, aí digitaremos esses comandos:

    
    touch cursodocker.txt
    
    ls
    
    exit
    
    

    Execute o comando docker ps -a novamente, ele mostrará o container que acabou de ser criado junto com os outros.

    Para voltar a trabalhar num container criado anteriormente (ou seja, ressuscitar um container), usaremos o comando docker start nomedocontainer, mas este mostrará só o nome dele e executará em segundo plano. Para mostrar o container rodando, digite docker ps, e ao rodar docker ps -a veremos que ele estará rodando. Podemos olhar no Docker Desktop também.

    Para entrar num container que está rodando, precisaremos nos anexar nele. Após digitar docker ps, digitaremos docker attach nomedocontainer para entrar nele novamente. Podemos dar um ls pra ver que o arquivo criado anteriormente continua lá, provando que as alterações são salvas nele. Podemos dar exit pra sair do Ubuntu e automaticamente o container será parado.

    PS: Só poderemos nos anexar a um container que esteja rodando. Para iniciar e anexar a um container de uma vez só, digite o comando docker start -ai nomedocontainer. Ele já entrará automaticamente.

    Para parar um container que esteja rodando, use o comando docker stop nomedocontainer. Caso não consiga parar o container e ele não parar, usaremos o comando kill, com o comando docker kill nomedocontainer. Use o kill apenas em emergências, ele seria o equivalente a tirar um container da tomada.

    Resumindo:

    Para renomear um container, principalmente um daqueles com nomes padrões doidos, use o comando docker rename nomeatual novonome.

    Limpe o Lixo do Docker e Ative o Modo Fantasma

    Um container parado não consome processador nem RAM, mas ocupa espaço em disco, por isso é importante limpar os containers e imagens não utilizados.

    Liste os containers em execução com docker ps e depois liste todos eles com docker ps -a.

    Para remover um container, podemos usar o comando rm, podemos usar o nome ou a ID do container, na sintaxe docker rm nomeouiddocontainer. Ele retornará o nome do container e o removerá. No caso de ID, nem precisamos colocar a ID inteira, apenas os primeiros caracteres, caso se refiram a um container só. Lembrando que só podemos remover container que estão parados.

    Para forçar a remoção de um container, use o parâmetro -f, na sintaxe docker rm -f nomeouiddocontainer. Isso só pode ser usado em emergências.

    Para remover todos os container que estão parados, digite o comando docker container prune. Ele mostrará os containers excluídos e o espaço recuperado em disco.

    Note que, ao excluir os containers, a imagem estará intacta, para deletar a imagem pela linha de comando, primeiramente liste as imagens com o comando docker images, e pra remover uma imagem específica, digite o comando docker rmi nomeouiddaimagem. Para remover todas as imagens não utilizadas por nenhum container, use o comando docker image prune.

    Até agora, pra rodarmos os containers, usamos o modo interativo, no qual, ao fechar o prompt/terminal, o container morre. Em casos de, por exemplo, servidores web ou de banco de dados, ele deve continuar funcionando em segundo plano mesmo fechando a linha de comando.

    Primeiramente, baixe a imagem do Nginx e já crie um container com um comando só, digitando docker run -it --name servidor nginx. Nesse caso, ele rodará no modo interativo, portanto, ao fechar a linha de comando, ele parará de executar o container.

    Para que ele execute o container independente de fechar a linha de comando, vamos remover o container anteriormente criado com docker rm servidor, e criar ele novamente no modo detach, com o comando docker run -d --name servidor nginx. Nesse caso poderemos fechar a linha de comando e ele continuará executando o container criado, para ter certeza, rode docker ps novamente.

    Para verificar os logs do servidor, digite o comando docker logs servidor. Para ele mostrar em tempo real, use o comando docker logs -f servidor. Mas ele não ficará preso ao terminal/prompt, portanto poderemos fechá-lo.

    Como Entrar no Container e Monitorar Tudo

    Para listar os containers em execução, digite docker ps, e para listar todos, docker ps -a.

    Para criar um container e executar ele no modo detach, digitamos docker run -d --name servidor nginx caso esse servidor não tenha sido criado anteriormente.

    Para rodar o container, digite o comando docker start servidor. Ele não se utiliza com o parâmetro -d porque ele já inicia no modo detach por padrão, por isso podemos fechar o prompt que ele continuará executando.

    O container é tipo uma caixa preta, ou seja, ele é fechado, e não podemos acessar ele pelo sistema padrão. Não dá pra usar o docker run para executar o terminal porque ele vai criar um novo container com esse comando. Para executar comandos em um container que já existe e esteja rodando, use o comando docker exec -it nomedocontainer comando, como por exemplo, docker exec -it servidor bash pra abrir o Bash dele. Ele deve ser utilizado com um container que esteja rodando.

    PS: As imagens Linux do Docker geralmente são minúsculas e simples, portanto, ele pode não ter o bash, nesse caso, basta substituir por sh.

    Para acessar o servidor web do Nginx, digite esses comandos:

    
    ls
    
    cd /usr/share/nginx/html
    
    ls
    
    cat index.html
    
    exit
    
    

    Ao sair com o exit, nesse caso, ele continuará rodando o container.

    Podemos usar outros comandos no lugar do bash, só que ele executará o comando e retornará a saída, por exemplo, docker exec servidor ls ou docker exec servidor ls /usr/share/nginx/html.

    Um servidor Nginx, ao receber visitas, gera logs. O Docker captura tudo que o processo joga na saída padrão, e joga nos logs. Para ver esses logs, basta digitar docker logs nomedocontainer. Os logs também podem ser vistos pelo Docker Desktop, clicando no container desejado, assim como o exec pode ser executado diretamente por ele.

    Voltando pra linha de comando, para acompanhar os logs em tempo real, usamos o parâmetro -f, no caso, docker logs -f servidor, e para sair use Ctrl C. Para testar, abra um novo prompt e digite o comando docker exec servidor curl localhost pra ele gerar um log. O comando curl é usado para transferir dados para um servidor, usado muito pra testar conexão com um servidor, como um navegador sem interface gráfica.

    Mapeamento de Portas (Port Binding)

    Mesmo se um servidor em um container estiver rodando e a porta (no caso, a 80) estiver aberta, ela não estará acessível fora do container porque o Docker cria um muro em torno do container, por segurança. Para podermos acessar essa porta do container, precisaremos criar um túnel pra isso.

    Só que pra abrir uma porta de um container, nós temos que fazer isso ao criar ele, portanto não é possível abrir as portas de um container já existente, mesmo se ele estiver parado. Para isso, digite esses comandos:

    
    docker ps
    
    docker stop servidor
    
    docker rm servidor
    
    docker ps -a
    
    

    Para criar um novo container com mapeamento de portas, usaremos o parâmetro -p, e teremos que informar os dispositos que vamos mapear, o da esquerda é o host (nosso computador, que é o que manda), e o da direita é o container. Por padrão, o Nginx roda na porta 80. Para criar um novo container no modo detach, use o comando docker run -d -p 8080:80 --name servidor nginx. Pode ser exibido um aviso do firewall. Aí, no seu computador, podemos rodar no navegador o link http://localhost:8080. Ele abrirá a página que está dentro do container. Dê um docker ps para ver o servidor rodando e as portas mapeadas.

    PS: Pode dar erro caso a porta 8080 já esteja em uso. Nesse caso, basta mudar a porta do host, como por exemplo, 8081 ao invés da 8080.

    Podemos rodar vários container no mesmo host, mas em portas diferentes, as portas dos containers podem ser as mesmas por serem independentes, apenas a porta do host deve ser diferente. Para exemplificar, digite o comando pra criar um segundo container do mesmo tipo, com outra porta no host, docker run -d -p 8081:80 --name servidor2 nginx.

    Com isso, podemos ter vários serviços rodando na mesma máquina, cada um em sua porta e seu container.

    PS: Por padrão, ele usa o TCP, caso precise do UDP, coloque algo como 8080:80/udp.

    O Segredo das Versões (Tags) no Docker

    As imagens que baixamos do Docker vem do Docker Hub, que funciona de forma mais ou menos parecida com o GitHub. O site é esse: https://hub.docker.com/

    Lá temos imagens de vários desenvolvedores, mas o recomendado é baixar as imagens dos desenvolvedores oficiais, que geralmente tem um indicador disso. Podemos pesquisar, por exemplo, a do Python, e ir em tags: https://hub.docker.com/_/python/tags

    Ao baixar uma imagem no Docker, por padrão ele assume a tag latest, ou seja, baixa a última lançada (mais recente). Mas isso pode dar o risco do sistema quebrar, caso a versão seja atualizada posteriormente. O ideal é a gente amarrar as imagens em uma versão específica.

    Vamos supor que queremos uma versão do Python como a 3.8, vamos pesquisar ela na página da imagem especificada. Para baixar a imagem, sem criar nenhum container, usamos o comando docker pull nomedaimagem, no caso, especificando a tag, docker pull python:3.8. Só que esse comando vai baixar a versão inteira, que dará uma imagem bem grande por baixar uma imagem com um Linux completo, que podemos verificar usando o comando docker images.

    No caso, o que importa é o Python, e assim podemos procurar uma versão alpine disponível no mesmo Docker Hub, por exemplo, docker pull python:3.13-alpine3.23, que baixará uma imagem bem menor, só com o que é necessário. Nesse caso, nós especificamos a versão pra não vir qualquer uma (no caso, a última com o rótulo latest). Baixe novamente a versão 3.8 com o alpine usando docker pull python:3.8-alpine3.20. Para remover a imagem grande, digite o comando docker rmi python:3.8.

    Como cada imagem e cada container são independentes entre si, elas não interferem uma na outra, e nem no sistema principal.

    Para criar um container com uma das imagens baixadas, usamos o comando, no modo interativo, docker run -it --name python python:3.13-alpine3.23 python, pra ele criar o container e já executar o Python dele. Para criar com a outra imagem, abra outro prompt e use o comando docker run -it --name python38 python:3.8-alpine3.20 python, da mesma forma. Pra sair do Python, use o comando de saída dele (exit()).

    Aprenda a Empacotar seu Projeto/Site/App em uma Imagem Docker

    Podemos empacotar nossos projetos em uma imagem, para isso usaremos um arquivo chamado Dockerfile, o Dockerfile é como uma receita, onde nós descrevemos os ingredientes e o Docker cozinha (faz o build) e o resultado é uma imagem pronta.

    Vamos pegar, por exemplo, um arquivo index.html só pra exemplificar um projeto nosso a ser empacotado.

    Primeiramente, crie num editor de texto um arquivo com o nome Dockerfile, com a primeira maiúscula e sem extensão, e coloque ele na mesma pasta do projeto, com esse código aqui:

    
    FROM nginx:1.28.2
    
    COPY index.html /usr/share/nginx/html/index.html
    
    

    Lembrando que temos que indicar o nome do arquivo a ser copiado com o novo nome, que pode ser o mesmo ou outro.

    Para copiar todo o conteúdo de uma pasta (no caso, a pasta onde ele está), use o ponto, nessa sintaxe:

    
    COPY . /usr/share/nginx/html
    
    

    Só que nesse caso, ele copiará tudo que estará nele, incluindo pastas ocultas, como as de Git, variáveis ambiente, e outros. Para ignorar esses arquivos, usaremos um arquivo chamado .dockerignore (não esqueça do ponto), para listar os arquivos a serem ignorados. Ele funciona de forma parecida com o Git Ignore. Esse é um exemplo de Docker Ignore, onde ele ignorará arquivos e pastas especificados:

    
    .git
    Dockerfile
    README.md
    README.txt
    docker-compose.yml
    
    

    Para construir o projeto, usaremos o comando docker build -t minhaimagem:1.0 . (não esqueça do ponto).

    PS: Caso você reconstrua um projeto já construído, ele só construirá caso tenha algo alterado, de forma mais ou menos parecida com os commits do Git.

    Para ver se a imagem foi criada, rode o comando docker images. Para criar um container no modo detach e com o mapeamento de portas, digite docker run -d -p 8080:80 --name meusite minhaimagem:1.0, ele rodará o container em segundo plano e poderemos acessar no navegador via http://localhost:8080/.

    Crie um novo container com essa mesma imagem, usando o comando docker run -d -p 8090:80 --name meusite2 minhaimagem:1.0, e acesse pela http://localhost:8090/ da mesma forma.

    Os dois containers funcionam de forma independente, podemos parar um e deixar o outro rodando, e um não interferirá no outro.

    Com isso, podemos mandar nosso projeto com tudo configurado, no caso do site, a outra pessoa nem precisaria instalar servidor web para visualizar o site, pois tudo estaria incluído na imagem e no container.

    Aprenda a Profissionalizar suas Imagens

    Podemos melhorar nosso Dockerfile. Nós informaremos uma pasta para transferir nossos arquivos, pois por padrão ele manda pra raiz do Linux. Caso a pasta não exista, ela será criada.

    Crie um novo Dockerfile e um arquivo de texto qualquer. Usaremos a diretiva WORKDIR para entrar em uma pasta. E podemos usar a diretiva RUN para rodar comandos do terminal do Linux e instalar o Vim no Ubuntu (não confundir com o run usado no comando do Docker). É necessário instalar algumas coisas pelo Linux do Docker ser bem simples e pelado. Podemos liberar a porta 80 com o comando EXPOSE também. Veja como ele ficaria:

    
    FROM ubuntu:24.04
    
    WORKDIR /app
    
    RUN apt update && apt install -y curl vim
    
    COPY app.txt .
    
    EXPOSE 80
    
    

    Aí, na pasta onde estão os arquivos, basta rodar o comando no prompt docker build -t meuubuntu:1.0 . (não esqueça do ponto) pra criar a imagem.

    Depois de criar a imagem, rode o comando docker images para ver se ela realmente foi criada, e crie um container com o comando docker run -it --name exemplocontainer meuubuntu:1.0 bash para criar o container no modo interativo e entrar no terminal. Ele já entrará na pasta /app criada. Digite esses comandos:

    
    ls
    
    curl --version
    
    vim --version
    
    cat app.txt
    
    

    Lembrando que o RUN do Dockerfile roda no build, o resultado final fica na imagem, já o CMD roda no start, ou seja, pra iniciar o aplicativo.

    CMD ou ENTRYPOINT? Entenda Quando Usar no seu Dockerfile

    Crie um Dockerfile novo com esse código aqui:

    
    FROM ubuntu:latest
    
    CMD ["echo", "Olá, sou o comando CMD!"]
    
    

    O comando CMD executará um comando na imagem do Ubuntu criado. Crie a imagem com o comando docker build -t teste-cmd . (não esqueça do ponto).

    Após criar a imagem, vamos criar o container com ela, com o comando docker run teste-cmd, ele criará o container e já executará o echo do Dockerfile.

    O comando CMD é mais flexível, ele permite que a gente faça mudanças no comando, ou seja, caso a gente passe outra coisa no comando de criação do container, ele substituirá o comando do Dockerfile. Para ver isso, digite no prompt docker run teste-cmd echo "Mudei Tudo!", ele apenas exibirá essa mensagem.

    Agora, altere a linha do Dockerfile que está o CMD, e coloque essa linha no lugar:

    
    ENTRYPOINT ["echo", "Olá, sou o comando ENTRYPOINT!"]
    
    

    Agora crie uma nova imagem com o comando docker build -t teste-entry . (não esqueça do ponto).

    Após criar a imagem, crie um container com ela usando o comando docker run teste-entry. Ele, ao criar, executará o echo do Dockerfile.

    Só que ao rodar o echo na criação do container, ele adicionará o conteúdo ao definido no Dockerfile, por exemplo, docker run teste-entry echo "Mudei Tudo!". Ele adicionará o texto echo "Mudei Tudo!" e não reconhecerá esse outro echo como comando.

    Em outras palavras, o CMD é uma sugestão, ele roda o comando, mas caso o usuário digite outro comando, ele é substituído. O ENTRYPOINT é uma ordem, portanto é executado obrigatoriamente e nada que o usuário digite muda o mesmo, ele é fixo.

    Dependendo do caso, é melhor usar um ou outro.

    Vamos supor um container pra usar o Ping, por exemplo, deixe o Dockerfile assim:

    
    FROM ubuntu:latest
    
    RUN apt update && apt install -y iputils-ping
    
    ENTRYPOINT ["ping", "-c", "4"]
    
    CMD ["google.com"]
    
    

    No caso acima, o ping e seus parâmetros serão executados obrigatoriamente, já o Google é pingado por padrão, mas podemos passar outro servidor como parâmetro.

    Digite o comando docker build -t meuping . (não esqueça do ponto) pra criar a imagem.

    Depois de criada a imagem, crie um container com o comando docker run meuping e ele criará o container e fará o Ping no Google. Ao rodar docker run meuping youtube.com ele executará o Ping no Youtube ao invés do Google.

    Lembrando que, para um controle melhor dos comandos do CMD e ENTRYPOINT, use sempre entre aspas e dentro de colchetes, separados por vírgulas.