Docker: Conceitos Básicos e Comandos Iniciais (+ um jogo no final)
Published: 2023-05-25 01:13:08

Escrevo este artigo na intenção de que seja uma introdução ao Docker e a alguns conceitos relacionados a ele. Espero que ele seja útil para te apresentar a uma ferramenta tão importante.

Agora, imagine-se em uma equipe de desenvolvimento. Você desenvolve parte da aplicação em uma distro Linux, e os demais integrantes da equipe possuem diferentes sistemas operacionais do seu, como Windows ou MacOS. Como garantir que a aplicação vai rodar em cada Sistema Operacional? Como garantir que ela funcionará no ambiente de testes e em produção? Como evitar o cansativo trabalho de instalar todas as bibliotecas e dependências necessárias, além das configurações, toda vez que o ambiente mudar?

A resposta é relativamente simples: **containers.**

## **O que são containers?**

Um container é um processo ou conjunto de processos que são organizados de forma isolada no SO hospedeiro. São processos que o kernel do sistema operacional coloca em uma ‘caixa’, assim eles são isolados dos demais processos que estão sendo executados. Ele não possui acesso ao sistema de arquivos, à rede ou a outros containers. O kernel simula todo um ambiente que possui um sistema de arquivos próprio, além disso controla o quanto de recurso os processos do container podem consumir. Mas como isso é feito?

Por meio de cgroups e namespaces. Control Groups ou cgroups são ferramentas do kernel do Linux que controlam o quanto de recurso um processo pode consumir, por exemplo, a quantidade de RAM, CPU, disco, realizar operações de I/O, etc… Afinal, se um processo pode consumir recurso indefinidamente, isso pode vir a atrapalhar o funcionamento do SO, tornando todas as operações muito lentas ou derrubando o sistema inteiro. Namespaces são responsáveis por segregar os processos. Lembra do filme Matrix, em que os humanos vivem uma realidade simulada por máquinas? Namespaces fazem o mesmo, simulam todo um ambiente alternativo para os processos, assim tudo que os processos executam não afetam o sistema principal, o que torna os containers seguros. Para se aprofundar mais nesses conceitos, recomento [este artigo do etcd](https://etcd.dev/2021/09/20/containers-o-que-sao-namespaces-e-cgroups/).

É comum confundir containers com máquinas virtuais, porém existem diferenças fundamentais entre as duas tecnologias. Um container usa o mesmo kernel do SO em que foi criado e é isso que torna o container leve e eficiente, ele é simplesmente um processo sendo executado dentro do SO hospedeiro. Tanto que containers de sistema Linux x64 só rodam em sistemas Linux x64, conatiners Windows x86 somente em sistemas Windows x86.

Máquinas virtuais servem para rodar outro sistema operacional, como MacOS, usando o mesmo hardware do SO hospedeiro. Para isso é necessário o hypervisor, um software que abstrai parte do hardware para a máquina virtual funcionar. Sendo assim, com uma máquina virtual você tem outro kernel, tem um SO inteiro para trabalhar, porém isso é muito mais custoso e difícil de escalar. 

Máquinas virtuais e containers são soluções para problemas diferentes. Enquanto containers são mais úteis para empacotar e executar aplicações, máquinas virtuais são úteis em casos que é necessário um SO inteiro.

![Diferença entre VM’s e Containers](https://caiodelgado.dev/content/images/2020/04/containervm.png)

Diferença entre VM’s e Containers

Para se aprofundar mais nestes conceitos e diferenças, recomendo este [vídeo do Fabio Akita sobre containers e VM's](https://www.youtube.com/watch?v=85k8se4Zo70).

## O que é o Docker?

Docker é um software que visa simplificar a criação e o gerenciamento de containers. Ele foi desenvolvido por Solomon Hykes e apresentado na PyCon de 2013 e foi um divisor de águas na manipulação de containers e imagens. Com ele é possível empacotar aplicações e todas suas dependências em imagens e executá-las em containers.

Empacotamos nossas aplicações em imagens, que são como scripts que contém tudo o que é necessário para a execução do container, como por exemplo: o código-fonte, bibliotecas, dependências, etc… Imagem é a matéria prima do container.

O Docker possui um hub de imagens, o [Docker Hub](https://hub.docker.com/), que é um repositório onde estão imagens oficiais das mais diferentes tecnologias como banco de dados, runtimes, So’s, etc… Nesse repositório podemos baixar imagens e após criar uma conta subir as nossas próprias. 

Docker ficou muito popular e muito útil pois simplificou e tornou eficiente a manipulação de containers e imagens. Docker não inventou essas tecnologias, elas já existiam no kernel do Linux. Seu grande mérito está em tornar o uso dessas tecnologias amigável e produtivo.

## Instalação do Docker e Comandos Básicos

Caso não tenha o Docker instalado: [Guia Instalação Oficial - Docker](https://docs.docker.com/get-docker/). Escolha a opção do seu SO e siga o passo a passo da instalação.

Vamos agora botar a mão na massa e desbravar o Docker. Para criar o seu primeiro container, digite o comando abaixo no terminal do Docker:

```bash
docker container run hello-world
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osqj9wy9f25irl2uztie.png)
O que acabamos de fazer é baixar a imagem hello-world e executá-la em um container. Porém há informações nesse output que vale a pena analisarmos para entendermos um pouco mais sobre o funcionamento do Docker.

Temos um descrição do procecedimento que o Docker seguiu após a execução do comando `run`. Primeiro o comando digitado no CLI foi enviado para Docker Daemon, que, por sua vez verificou se a imagem “hello-world” estava disponível localmente. Como não estava a imagem foi baixado do ***hub*** do Docker num processo semelhante ao ******git pull.****** Após baixar a imagem, o daemon criou um novo container, cujo o output é o que obtivemos no terminal.

Você pode verificar quais imagens você tem localmente com o comando abaixo:

```bash
docker images
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u4vapx6pppsd2vnkt5z.png)

Para verificar quais containers estão em execução:

```bash
docker container ls
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zrvyq34i0hshusg6ich.png)

Mas se foi criado um container a partir da imagem **************hello-world,************** por que ele não aparece na saída do comando acima? Isso é porque o container foi criado, executado e finalizado. Para listar containers que já foram finalizados, utilizamos o parâmetro `-a`.

```bash
docker container ls -a
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scz36596jm17jdf0ee8u.png)

Agora sim podemos ver todos os containers, tanto os que estão em execução como os que já foram finalizados. Aqui também há alguns detalhes que valem a pena serem observados.

Primeiro podemos ver que o container criado possui um ID, que é importante caso seja necessário realizar uma operação em um container específico. Podemos observar também o nome da imagem, além do comando que é executado no container logo quando é iniciado. Podemos ver quando o container foi criado e o seu status atual, a porta que ele está disponibilizando e por fim um nome, que é aleatório dado pelo Docker.

No output do comando `docker container run hello-world` há uma sugestão de um comando mais ambicioso. Vamos exexutá-lo? Além disso, vamos também nomear esse nosso novo container? 🤩

```bash
docker container run -it --name meu-container ubuntu bash
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcf9xtz7xxp3fca1hcf7.png)

Automagicamente temos um novo terminal diante de nós! Agora vamos entender o que fizemos: por meio do comando `run` pedimos a criação de um novo container, o parâmetro `-it` indica que queremos interagir com o container, já o `--name` nos permite passar um nome para o container (***************meu-container),*************** depois especificamos a imagem que queremos (**************ubuntu) e************** por fim damos o comando `bash` para entrarmos no terminal do container. E sim, agora estamos em um terminal do Ubuntu. Incrível, né!?

Execute o comando abaixo e veja por si mesmo que é de fato um Ubuntu:

```bash
cat /etc/os-release
```


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iglz7mblabvpvou37vgg.png)

Podemos criar um arquivo e escrever nele:


![print file content](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xasxoswfl1hdgrl8z8f.png)

Porém, quando finalizarmos esse container esse arquivo e qualquer outro que foi criado não persistirá no disco. Isso ocorre porque, como aprendemos no início desse artigo, esses containers são isolados do SO hospedeiro e esse isolamento inclui o sistema de arquivos.

Contudo, é possível permitir que o container tenha acesso ao sistema de arquivos, à rede, à outros containers… mas, isso é assunto para um próximo artigo, em que podemos aprender sobre mapeamento de portas, de volume, comunicação entre containers, entre outros assuntos. Até lá, para se aprofundar mais recomendo [Documentação Oficial do Docker](https://docs.docker.com/get-started/overview/) e também [este vídeo do canal Full Cycle](https://www.youtube.com/watch?v=EEvJsgiZprM&t=2736s), que é excelente e possui vários outros conteúdos. Vale a pena conferir.

Por fim, vamos sair do terminal do nosso container digitando `exit`  e apertando *****Enter*****, e agora vamos listar nossos containers com `docker ps -a` (um comando mais curto para listar os containers):


![output docker-cli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5yicy9dkneiq145egwb.png)

Agora vamos aprender a remover containers e a apagar imagens. Isso é útil pois, ainda que containers sejam leves eles consomem recursos de CPU e memória, muitos containers podem vir a afetar o desempenho da sua máquina. As imagens também são leves, porém o acúmulo de imagens desnecessárias ocupam armazenameto. Para remover um container específico, você pode usar o nome ou o ID do container. Vamos usar o nome que demos ao nosso

```bash
docker rm meu-container
```

Da mesma forma você pode remover imagens específicas por meio do ID ou nome, o que muda é que o comando é o `rmi` (remove image). Porém aqui vou ensinar um truque. Como possuímos mais de uma imagem vamos apagá-las numa tacada só:

```bash
docker rmi $(docker images -aq)
```

`$()` indica uma variável no shell. Neste caso o valor dessa variável é o retorno do comando `docker images -aq` que lista todas as imagens locais do Docker, o `rmi` será aplicado nessas imagens. O mesmo pode ser feito nos containers porém substituindo por `docker ps -aq` . O parâmetro `-f` pode ser usado após `rm` ou `rmi` para forçar a remoção dos containers em execução ou imagens que estejam servindo de base para containers em execução.

E chegamos ao fim desse artigo introdutório sobre Docker. Espero que tenha sido útil de algum jeito. Dúvidas, sugestões e feedbacks são muito bem-vindos 😄. Ah, e antes de ir quero deixar aqui uma dica divertida: execute o comando `docker run -it andrius/ascii-patrol` .

Até a próxima!