Docker
Tags: docker, distributedsystems, devops
Published: 2023-05-24 21:34:54

O **Docker** é um software/ plataforma open source para construir, armazenar, distribuir e rodar contêineres (containers são ambientes virtualizados, isolados e voláteis), o qual permite a equipe emular ambientes de desenvolvimento e que empacota um todo sistema operacional, como também a sua aplicação para dentro de um container. 

Em 2013, foi criado o Docker, escrito na linguagem Go (Golang), por <a href="https://github.com/shykes">Solomon Hykes</a>, onde o Docker começou como um projeto interno, inicialmente desenvolvido pelo **dotCloud** engineers, essa empresa é uma **PaaS** (Plataform as a Service), uma empresa que você manda o código da sua aplicação feita em Java, PHP, Node ou Python etc e a empresa toma todo o cuidado possível para você hospedar sua aplicação. Um exemplo de empresas PaaS são as empresas:

- Heroku;
- AWS - Amazon Web Services;
- GCP - Google Cloud Plataform;
- AZ - Microsoft Azure;

<img src="https://user-images.githubusercontent.com/61624336/127539895-e0c3f3e9-3d78-40e4-9546-0feb39e233a2.png" height="177" align="right">

Quando o Docker nasceu, a dotCloud utilizava a **AWS - Amazon Web Services**, que é um serviço que provê VMs ou máquinas físicas, então ela queria hospedar na VM a aplicação dela, porém ela usou seu sistema de containers para subir sua aplicação, e assim nasceu o Docker, um intermediário entre um sistema operacional e seus containers contendo aplicações em uma mesma máquina.

De desktop a cloud, o Docker é considerado padrão e a ferramenta mais popular: é  usado por milhões de desenvolvedores para construir e compartilhar aplicações conteinerizadas. Para termos uma ideia de volume, 30% das empresas usavam Docker em ambientes AWS, em 2019. 

Embora seja uma das ferramentas de conteinerização mais usadas e conhecidas, o Docker não está sozinho. Há outros fornecedores no ecossistema: ConteinerD, CoreOS, Canonical, LXC Linux Conteiners, CRIO-D, OpenShift e Mesos Containerizer. 

Além da empresa dotCloud ser focada em manter o Docker, ela também liberou o Docker para Open Source, exatamente isso, o Docker possui também código-livre para desenvolvimento!

- https://www.github.com/docker
- https://renatogroffe.medium.com/docker-dicas-e-truques-na-utiliza%C3%A7%C3%A3o-de-containers-parte-1-ef3f981fbcf5

Está tudo lá, as bibliotecas, as contribuições para o desenvolvimento de tecnologias do Docker.

## Vantagens de um container Docker
<div align="center"><img src="https://miro.medium.com/max/500/1*lu5prM28fxIlbHmjPT8VIg.jpeg" height="177"></div><br \>

Por não possuir um sistema operacional, o container é muito mais leve e não possui o custo de manter múltiplos sistemas operacionais, já que só teremos um sistema operacional, que será dividido entre os containers.

Além disso, por ser mais leve, o container é muito rápido de subir, subindo em questão de segundos. Logo, o container é uma solução para suprir o problema de múltiplas máquinas virtuais em um hardware físico, já que com o container, nós dividimos o sistema operacional entre as nossas aplicações.

Volátil, ou seja, sobe e mata um container, é da natureza do container sumir e subir rapidamente.

## Necessidade do uso de containers Docker
Mas por que precisamos dos containers, não podemos simplesmente instalar as aplicações no nosso próprio sistema operacional? Até por que já fazemos isso, já que no nosso sistema operacional temos um editor de texto, terminal, navegador, etc.

No caso das nossas aplicações, essa abordagem pode ter alguns problemas. Por exemplo, se dois aplicativos precisarem utilizar a mesma porta de rede? Precisaremos de algo para isolar uma aplicação da outra. E se uma aplicação consumir toda a CPU, a ponto de prejudicar o funcionamento das outras aplicações? Isso acontece se não limitarmos a aplicação. Outro problema que pode ocorrer é cada aplicação precisar de uma versão específica de uma linguagem, por exemplo, uma aplicação precisa do Java 7 e outra do Java 8. Além disso, uma aplicação pode acabar congelando todo o sistema. Por isso é bom ter essa separação das aplicações, isolar uma da outra, e isso pode ser feito com os containers.

Com os containers, conseguimos limitar o consumo de CPU das aplicações, melhorando o controle sobre o uso de cada recurso do nosso sistema (CPU, rede, etc). Também temos uma facilidade maior em trabalhar com versões específicas de linguagens/bibliotecas, além de ter uma agilidade maior na hora de criar e subir containers, já que eles são mais leves que as máquinas virtuais.

Vamos agora ver as diversas tecnologias presentes no Docker e como são incríveis as possibilidades de se trabalhar em cada uma delas!

## O ecossistema/ambiente do container Docker
<div align="center"><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSJFwWA2jwjH5Zc_MrhbUpjUj-dsiHEeA5w4jWEjiiKIDEbxbB-ZrZjzP_Zg-Q3c0yPzGg&usqp=CAU" height="177"></div><br \> 

O **ambiente do container** é todo um ecossistema de desenvolvimento, ou seja, toda uma infraestrutura com:

<img src="https://user-images.githubusercontent.com/61624336/125382079-6460bf80-e36b-11eb-91e3-85f860b8bb84.png" height="177" align="right">

- Os **servidores web** em NGINX ou Apache; 
- **Banco de dados** como PostgreSQL, MySQL, MongoDB, Cassandra, etc; 
- Os **interpretadores de linguagem de programação** como Python, Node.js, PHP, GO, Ruby, etc; 
- As **bibliotecas** e **frameworks** utilizadas para desenvolvimento, como o React.js, Django, Spring, etc.;
- As **scripts e plugins** são arquivos de bash-script ou shell-script responsáveis por executar uma determinada ação no container, como: Automatizar alguma ação manual, Instalar programas adicionais, criar ou mudar configurações, preencher ambiente, etc.
- As **variáveis de ambiente** são arquivos responsáveis por guardar segredos relacionados a qualquer informação sigilosa.

Então, é basicamente tudo o que a gente instala no nosso sistema operacional (Windows, Mac OS ou Linux) para conseguir fazer o desenvolvimento de software e rodar uma aplicação nela.

Com o Docker é possível resolver aquele velho problema dos desenvolvedores: 

> Como assim!? Na minha máquina funciona, na sua não?

## Etapas e ferramentas/modos do Docker
<div align="center"><img src="https://user-images.githubusercontent.com/61624336/205305615-b13a5479-1e1d-4921-a24e-d5ae779bacc3.png" height="177"></div><br />

Além disso, existem algumas "etapas" com terminologias para a criação desses containers, pois afinal são uma evolução de VMs e não são instaladas por padrão na nossa máquina pessoal. São elas:

- **Image**: Pacote com todas as dependências para criar o nosso container (nossa aplicação rodando no container);
- **Dockerfile**: Arquivo de texto que contém todas as instruções para iniciar o processo de **_Build_** na nossa imagem do container; 
- **Build**: É uma ação que cria a imagem a partir do Dockerfile (lê as instruções e cria uma imagem);
- **Container**: Feito a imagem pelo Build é instanciado o container (execução de uma aplicação, ou processo ou um serviço);
- **Volumes**: Armazena dados em disco, ou seja, se o container morrer é o volume que salva os dados do container (os dados do container não serão perdidos dentro do volume);
- **Tags**: Ajuda no versionamento das nossas imagens;
- **Multi-stage Build**: Múltiplos estágios de Build onde usamos uma imagem para compilar a nossa aplicação, e essa chama outra imagem (Roy na Aplicação);
- **Repository**: Nada mais é do que uma coleção de imagens, então é como se fosse uma caixa cheia de imagens;
- **Registry**: É um serviço que provê acesso do Docker ao repositório, como o **Docker Hub** (repositório remoto de imagens de containers públicas e privadas);
- **Compose**: É uma metadata que você pode executar múltiplos containers com um simples comando.

### Docker Engine
<img src="https://user-images.githubusercontent.com/61624336/205306089-70ce69b3-23d2-446c-8b30-3b8a2ae6ae6c.png" height="177" align="right">

As tecnologias de containers para prover ferramentas modernas para deployar e rodar aplicações.  

<div align="center"><img src="https://user-images.githubusercontent.com/61624336/127540946-fdaf8dbc-cadd-4278-91e4-835d9f06d3ff.png" height="177"></div><br \>

### Docker Compose
<img src="https://openwhisk.apache.org/images/deployments/logo-docker-compose-text.svg" height="177" align="right">

O **Docker Compose** é um modo do Docker onde podemos escalar e diminuir o número de containers necessário para nossa aplicação rodar com melhor aproveitamento, ou seja, é um jeito simples de definir múltiplos containers, o que é ideal para uma **stack** de tecnologia (.NET, Spring, LAMP, LEMP, MEAN, MERN, MENV, RoR, FReMP, etc), utilizar um balanceador de carga, banco de dados e até adotar a arquitetura de micro serviços.

No entanto, para aplicações e arquiteturas mais robustas é um pouco limitado em utilidades, se comparado ao modo Docker Swarm.

### Docker Swarm
<img src="https://www.pinclipart.com/picdir/big/83-834158_docker-swarm-logo-clipart.png" height="177" align="right">

O **Docker Swarm** é um modo do Docker com uma ferramenta para orquestrar múltiplos docker engines para trabalharem juntos em um cluster com dezenas ou centenas de containers. As versões atuais do Docker incluem o modo swarm para gerenciar nativamente um cluster de Docker Engines chamado **swarm** (enxame). Use a CLI do Docker para criar um swarm, implantar serviços de aplicativo em um swarm e gerenciar o comportamento do swarm.

O modo Docker Swarm é integrado ao Docker Engine. E possui algumas utilidades:

- **Gerenciamento de cluster integrado ao Docker Engine**: Use a CLI do Docker Engine para criar um enxame de Docker Engines onde você pode implantar serviços de aplicativos. Você não precisa de software de orquestração adicional para criar ou gerenciar um enxame.

- **Design descentralizado**: em vez de lidar com a diferenciação entre as funções do nó no momento da implantação, o Docker Engine lida com qualquer especialização no tempo de execução. Você pode implantar os dois tipos de nós, gerentes e trabalhadores, usando o Docker Engine. Isso significa que você pode criar um enxame inteiro a partir de uma única imagem de disco.

- **Modelo de serviço declarativo**: o Docker Engine usa uma abordagem declarativa para permitir que você defina o estado desejado dos vários serviços em sua pilha de aplicativos. Por exemplo, você pode descrever um aplicativo composto por um serviço de front-end da Web com serviços de enfileiramento de mensagens e um back-end de banco de dados.

- **Dimensionamento**: Para cada serviço, você pode declarar o número de tarefas que deseja executar. Quando você aumenta ou diminui, o gerenciador de enxame se adapta automaticamente adicionando ou removendo tarefas para manter o estado desejado.

- **Reconciliação do estado desejado**: O nó do gerenciador de swarm monitora constantemente o estado do cluster e reconcilia quaisquer diferenças entre o estado real e o estado desejado expresso. Por exemplo, se você configurar um serviço para executar 10 réplicas de um contêiner e uma máquina de trabalho que hospeda duas dessas réplicas falhar, o gerente criará duas novas réplicas para substituir as réplicas que falharam. O gerenciador de swarm atribui as novas réplicas aos trabalhadores que estão em execução e disponíveis.

- **Rede multi-host**: você pode especificar uma rede de sobreposição para seus serviços. O gerenciador de swarm atribui automaticamente endereços aos contêineres na rede de sobreposição quando inicializa ou atualiza o aplicativo.

- **Descoberta de serviço**: os nós do Swarm Manager atribuem a cada serviço no Swarm um nome DNS exclusivo e equilibram a carga dos contêineres em execução. Você pode consultar todos os contêineres em execução no enxame por meio de um servidor DNS incorporado no enxame.

- **Balanceamento de carga**: você pode expor as portas para serviços a um balanceador de carga externo. Internamente, o enxame permite especificar como distribuir contêineres de serviço entre os nós.

- **Seguro por padrão**: cada nó no enxame impõe autenticação e criptografia mútuas TLS para proteger as comunicações entre ele e todos os outros nós. Você tem a opção de usar certificados raiz autoassinados ou certificados de uma CA raiz personalizada.

- **Atualizações contínuas**: no momento da implementação, você pode aplicar atualizações de serviço aos nós de forma incremental. O gerenciador de enxame permite controlar o atraso entre a implantação do serviço em diferentes conjuntos de nós. Se algo der errado, você pode reverter para uma versão anterior do serviço.

### Docker Machine <img src="https://img.shields.io/badge/tool-deprecated-red">
<img src="https://www.mundodocker.com.br/wp-content/uploads/2016/06/machine.png" height="177" align="right">

Uma ferramenta que nos permite instalar e gerenciar o Docker Engine em hosts virtuais, com comandos `docker-machine`. 

Você pode usar o Machine para criar hosts do Docker em sua caixa local do Mac ou Windows, na rede da empresa, no data center ou em provedores de nuvem como Azure, AWS ou Digital Ocean.

> O Docker encerrou o desenvolvimento ativo e o suporte para o Docker Machine. Um método principal para escalonamento automático do GitLab runner em instâncias de máquina virtual de nuvem pública é o Runner configurado com o executor do Docker Machine.

### Docker Hub [![Docker HUB](https://img.shields.io/badge/-Docker_Hub-ffffff?style=flat&logo=Docker&logoColor=2496ED)](https://hub.docker.com/u/isaacalves7)

Um repositório com mais de 250 mil imagens diferentes para os nossos containers. Para instalar, você precisa ter os seguintes requisitos:

- Um sistema de 64 bits;
- Se você utiliza o Windows 10, você precisar utilizar a versão Windows 10 PRO;
- Habilitar a virtualização do Windows;

### Play with Docker
<a href="https://labs.play-with-docker.com/" title="Download Docker Desktop"><img src="https://galaxyproject.github.io/training-material/topics/admin/images/docker_whale.png" height="177" align="right"></a>

- https://labs.play-with-docker.com/

Esse é um ambiente totalmente grátis para testar a todos os recursos do Docker pela web, o que facilita muito no aprendizado para iniciantes! Com práticas e exercícios.

Você verá que cada instância é um nó com seu endereço de IP e tem tempo para escalar seus ambientes antes que eles sejam removidos.

> Em exemplos posteriores, vamos utilizar o modo Docker Swarm com Flask e Hashicorp Consul como malha de serviço!

## Instalação do Docker

**Linux**

```sh
sudo apt update
sudo apt install -y docker.io
sudo systemctl enable docker --now
docker
```

**Windows**

<img src="https://s3.amazonaws.com/caelum-online-public/646-docker/01/imagens/virtualizacao-habilitada.png" height="277" align="right">

- Habilitar a Virtualização de CPU na BIOS (programa da placa-mãe) do seu computador
- Baixar o pacote de atualização do kernel do Linux (WSL 2)

https://docs.microsoft.com/pt-br/windows/wsl/install-win10#step-4---download-the-linux-kernel-update-package

![Sem título](https://user-images.githubusercontent.com/61624336/127605495-c419872f-a163-4838-81e4-7f80cbea7ac2.png)

Seguimos o passo a passo do instalador para aceitar a licença, autorizamos o instalador e seguimos com a instalação. Ao clicar em Finish, precisamos encerrar a sessão do Windows e iniciá-la novamente. Ao fazer o login, precisamos habilitar o Hyper-V, clicando em Ok, para que o computador será reiniciado.

Quando o computador terminar a reinicialização, irá aparecer um ícone do Docker na barra inferior, à direita, ao lado do relógio. O Docker pode demorar um pouco para inicializar, mas quando a mensagem Docker is running for exibida, significa que ele foi instalado com sucesso e já podemos utilizá-lo.

## Exibir a versão do Docker
```sh
docker --version
```

## Exibir os todos os dados do Docker
```sh
docker version
```

Lembrando que você precisa estar executando o Docker na sua máquina.

# `Hello, World!` - Docker
```sh
docker run hello-world
```

Lembrando que quando aparece a seguinte mensagem no terminal `Unable to find image 'hello-world' locally`, significa que o docker não achou a imagem localmente, mas sim precisou buscar a imagem e baixar ela!

![Sem título](https://user-images.githubusercontent.com/61624336/127686224-6955ed13-8d38-4827-8848-85fdd7ddc88d.png)

## Imagens e Containers
<img src="https://linuxiac.com/wp-content/uploads/2021/06/what-is-docker-container.png" height="177" align="right">

Na aula anterior, para executar a imagem `hello-world`, executamos o comando `docker run hello-world`.

Quando executado esse comando, a primeira coisa que o Docker faz é verificar se temos a imagem `hello-world` no nosso computador, e caso não tenhamos, o Docker buscará e baixará essa imagem no <a href="https://hub.docker.com/">Docker Hub/Docker Store</a>.

<table>
  <tr>
    <td><img src="https://user-images.githubusercontent.com/61624336/127689988-66b3ce01-14ef-4843-8240-691da30a81ba.png"></td>
    <td><img src="https://user-images.githubusercontent.com/61624336/127694974-765bf63d-335d-4287-87ac-0b315102e343.png"></td>
  </tr>
</table>

A **imagem** é como se fosse uma receita de bolo, uma série de instruções que o Docker seguirá para criar *um container*, que irá conter as instruções da imagem, do `hello-world`. Criado o container, o Docker executa-o. Então, tudo isso é feito quando executamos o `docker run hello-world`.

Há milhares de imagens na Docker Store disponíveis para executarmos a nossa aplicação. Por exemplo, temos a imagem do Ubuntu:

# Rodar a imagem do Sistema Operacional Ubuntu

```sh
docker run ubuntu
```

É normal o nome do container ser seguida de nomes aleatórios, não tem problema nenhum! a imagem possui o nome escrita em azul para você identificar no Docker Desktop, assim como no terminal também informa.

## Listar todos os containers ativos no momento
```sh
docker ps
```

## Listar todos os containers que eu criei
```sh
docker ps -a
```

## Listar todos os containers que eu criei (somente os id's)
```sh
docker ps -q
```

## Executar um Olá, Mundo dentro do container do Ubuntu
```sh
docker run ubuntu echo "Ola Mundo"
```

## Interagir mais com o container do Ubuntu
```sh
docker run -it ubuntu
```

Dessa forma, ele retorna o terminal do Ubuntu:

<pre>root@9fea4151a9f8:/#</pre>

Se você rodar qualquer comando do Linux irá funcionar, porém não dentro do Sistema Operacional, mas sim dentro do container.

```sh
root@9fea4151a9f8:/# ls
```

Retorno:

<pre>bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var</pre>

Para sair é só `ctrl+D` ou `exit`.

## Startando o container
```sh
docker ps -a
docker start [id do container]
docker ps
```

Como também:

```sh
docker start -a -i [3 dígitos iniciais do id do container]
```

Agora vamos brincar, digite a série de comandos de entrada abaixo:

```sh
ls
touch hello-world.txt
ls
echo "Hello, World!" > hello-world.txt
cat hello-world.txt
```

Saída:

<pre>Hello, World!</pre>

## Startando o container de forma interativa
```sh
docker start -it [nome do container]
```
## Sai do container interativo, mas desejo voltar novamente
```sh
docker start -a -i [id-do-container]
```
Ou também você pode usar a seguinte forma:
```sh
docker start [nome container]
docker exec -it [nome do container] bash
```

## Parar o container

```sh
docker stop [id do container]
```

Existem formas mais eficientes de rodar ou parar o docker, pois a execução as vezes demora muito...Para saber mais, use:

```sh
docker stop --help
```

Então, com a lista de parâmetros, utilizaremos o parâmetro `-t` e com ele podemos, instantaneamente, executar o tempo de uma execução no docker, como:

```sh
docker stop -t 0 [3 dig. do id do container]
```

## Parar mais de um id
```sh
docker stop $(docker ps -q)
```
ou
```sh
docker stop -t 0 $(docker ps -q)
```

## Layered File System
<img src="https://s3.amazonaws.com/caelum-online-public/646-docker/02/imagens/estados-container.png" height="177" align="right">

Aqueles dois principais estados de um container, quando criamos um ou iniciamos, ele fica no estado de **running**, e quanto a sua execução encerra ou 
paramos, ele fica no estado de **stopped**:

## Limpar container
```sh
docker prune
```

## Limpar containers inativos
```sh
docker container prune
```

## Deletar ou Remover containers 
```sh
docker rm [id do container]
```

## Deletar todos os containers
```sh
docker container prune
```

## Remover ou Deletar as imagens do Docker
```sh
docker image prune
```

## Remover ou Deletar todas as imagens do Docker
```sh
docker system prune
```

## Remover ou deletar um Volume
```sh
docker volume prune
```

## Forçar remover um container
```sh
docker rm -f [id do container]
```

## Listando imagens 
```sh
docker images
```

## Removendo imagens
```sh
docker rmi [container]
```

## Ver logs de um container
```sh
docker logs [ID do container]
```

E do mesmo jeito que temos o comando `docker container` para mexermos com o container, temos o comando `docker images`, que nos exibe as imagens que temos na nossa máquina. Para remover uma imagem, utilizarmos o comando `docker rmi`, passando para ele o nome da imagem a ser removida, por exemplo:

## Rodar uma determinada versão da imagem
```sh
docker run ubuntu:14.04
```

## Camada de imagens 
<img src="https://s3.amazonaws.com/caelum-online-public/646-docker/02/imagens/camadas.png" height="177" align="right">

Quando baixamos a imagem do Ubuntu, reparamos que ela possui camadas, mas como elas funcionam? Toda imagem que baixamos é composta de uma ou mais *camadas*, e esse sistema tem o nome de **Layered File System**.

As pricincipais características das camadas, são:

- As camadas na imagem são de leitura apenas;
- Essas camadas podem ser reaproveitadas em outras imagens, acelerando assim o tempo de download;
- Toda imagem que baixamos é composta de uma ou mais camadas.

Essas camadas podem ser reaproveitadas em outras imagens. Por exemplo, já temos a imagem do Ubuntu, isso inclui as suas camadas, e agora queremos baixar a imagem do CentOS. Se o CentOS compartilha alguma camada que já tem na imagem do Ubuntu, o Docker é inteligente e só baixará as camadas diferentes, e não baixará novamente as camadas que já temos no nosso computador:

No caso da imagem acima, o Docker só baixará as duas primeiras camadas da imagem do CentOS, já que as duas últimas são as mesmas da imagem do Ubuntu, que já temos na nossa máquina. Assim poupamos tempo, já que precisamos de menos tempo para baixar uma imagem.

Uma outra vantagem é que as camadas de uma imagem são somente para leitura. Mas como então conseguimos criar arquivos na aula anterior? O que acontece é que não escrevemos na imagem, já que quando criamos um container, ele cria uma nova camada acima da imagem, e nessa camada podemos ler e escrever:

<img src="https://s3.amazonaws.com/caelum-online-public/646-docker/02/imagens/container-layer.png">

Então, quando criamos um container, ele é criado em cima de uma imagem já existente e nele nós conseguimos escrever. E com uma imagem base, podemos reaproveitá-la para diversos containers:

<img src="https://s3.amazonaws.com/caelum-online-public/646-docker/02/imagens/imagem-varios-containers.png">

Isso nos traz economia de espaço, já que não precisamos ter uma imagem por container.

## Praticando com o `docker run`
Já podemos fazer um container mais interessante, um pouco mais complexo. Então, vamos criar um container que segurará um site estático, para entendermos também como funciona a parte de redes do Docker. Para tal, vamos baixar a imagem `dockersamples/static-site`:

```sh
docker run dockersamples/static-site
```

<blockquote>As imagens não oficiais, ou seja, imagens de terceiros, precisam ser especificadas pelo username e pela imagem.</blockquote>

## Baixando a imagem apenas pelo docker, sem atrelar ao terminal
```sh
docker run -d dockersamples/static-site
```

Se você executar os dois comandos, criará dois containers diferentes, pois o Docker é inteligente no quesito de solicitações de imagem, ele sabe criar um id e ambientes diferentes!

## Acessando o site
```sh
docker run -d -P [nome da imagem]
```

A flag `-P` significa que fará com que o Docker atribua uma porta aleatória do mundo externo, que no caso é a nossa máquina, para poder se comunicar com o que está dentro do *container*.

## Abrindo uma porta externa para uma interna
Vamos ver como abrir portas e acessar o site com elas.

#### Descobrir a porta
```sh
docker ps
```

### Descobrir a porta de uma maneira mais legível
```
docker port [id do container]
```
Logo, ele retornará a porta pelo **protocolo TCP**:

<pre>
80/tcp -> 0.0.0.0:49155
80/tcp -> :::49155
443/tcp -> 0.0.0.0:49154
443/tcp -> :::49154
</pre>

Se você copiar o endereço:
```css
:49155
```

E ir para o browser digitando na aba do navegador:
```sh
localhost:49155
```

Você verá o preview do site estático!

![Sem título](https://user-images.githubusercontent.com/61624336/127750377-308786b3-8d0c-449a-8dde-b32a31b4b3a2.png)

## Nomeando um container
```sh
docker run -d -P --name [nome do container] [nome da imagem]
```

## Acessando os detalhes do container
```sh
dive [nome do container]
```

Dessa forma, ele irá nomear o container que possuia um nome aleatório, isso facilita nosso aprendizado e nossos projetos.

## Renomeando um container
```sh
docker rename [nome ou id do container] [novo nome]
```

## Mapeando portas do computador para a porta do servidor
```sh
docker run -d -p 12345:80 [nome da imagem]
```

Assim, ele muda o endereço da porta do localhost para `12345`.

![docker](https://user-images.githubusercontent.com/61624336/127752329-b51ec39d-f921-4f2b-90b3-5ccf105dc8c2.png)

## Definindo a variável autor
```sh
docker run -d -P -e AUTHOR="seu username" [imagem]
```

Dessa forma, ele apresentará você no site estático. Mas primeiro acesse o endereço ip que ele mudou!

```sh
docker ps
```

Dessa foma, coloque junto ao localhost.

![docker](https://user-images.githubusercontent.com/61624336/127752982-4696e9b2-8a44-4d49-aad5-5593ec9aa20c.png)

## Docker para DevOps
**DevOps** é um tópico bem amplo que envolve tanto aspectos culturais como técnicos, mas possui como principal objetivo aumentar a qualidade e eficiência da entrega de software. DevOps é uma metodologia que visa integrar os times de desenvolvimento com infraestrutura e o Docker está tendo um papel importante nessa tarefa.

Repare que com Docker os desenvolvedores não precisam se preocupar em configurar um ambiente de desenvolvimento específico de cada vez. Em vez disso, eles podem se concentrar na construção de um código de boa qualidade. Isso, obviamente, leva à aceleração nos esforços de desenvolvimento. O Docker facilita muito construir o ambiente: é rapido, simples e confiável.

No outro lado, para a equipe de operações de TI/Sysadmins, o Docker possibilita configurar ambientes que são exatamente como um servidor de produção e permite que qualquer pessoa trabalhe no mesmo projeto com exatamente as mesmas configurações, independentemente do ambiente de host local. As configurações são descritas em arquivos simples facilmente aplicáveis pelo desenvolvedor.

Com a padronização de um entregável Docker é possível que o desenvolvedor tenha um ambiente similar ao de produção na sua máquina sem todo o custo de configuração e o Sysadmin consiga lidar apenas com um tipo de entregável conseguindo, desta forma, dar atenção aos desafios de monitoramento e orquestração para que nada dê errado. Neste caso, o melhor para os dois.

## Salvando dados com volumes
<img src="https://user-images.githubusercontent.com/61624336/127754767-71296970-913c-4dca-8866-4e4be6c70ebc.png" height="177" align="right">

É da natureza dos containers a *volatilidade*, isto é, eles são criados e removidos rapidamente e facilmente, mas devemos ter um lugar para salvar os dados, e esse lugar são os **volumes**.

Quando escrevemos em um container, assim que ele for removido, os dados também serão. Mas podemos criar um local especial dentro dele, e especificamos que esse local será o nosso volume de dados.

Quando criamos um volume de dados, o que estamos fazendo é apontá-lo para uma pequena pasta no *Docker Host*. Então, quando criamos um volume, criamos uma pasta dentro do container, e o que escrevermos dentro dessa pasta na verdade estaremos escrevendo do Docker Host. Ou seja, fica salvo no computador onde a Docker Engine está rodando.

Isso faz com que não percamos os nossos dados, pois o container até pode ser removido, mas a pasta no Docker Host ficará intacta.

## Trabalhando com volumes
Sabendo disso, vamos ver como trabalhar com o **Docker Host**. No Terminal ou PowerShell (ou Docker Quickstart Terminal), criamos um container com o `docker run`, mas dessa vez utilizando a flag `-v` para criar um volume, seguido do nome do mesmo:
```sh
docker run -v "/var/www" ubuntu
```
No exemplo acima, criamos o volume `/var/www`, mas a que pasta no **Docker Host** ele faz referência? Para descobrir, podemos inspecionar o container, executando o comando docker inspect, passando o seu id para o mesmo:

## Inspecionando o container
```sh
docker inspect [id do container]
```

Ele retornará uma série de dados, mas o mais importante, no momento, é o `Mounts`:
<pre>
      "Mounts": [
            {
                "Type": "volume",
                "Name": "54a5fad5f6c046a4afad7854caf90089a40d9c9d60d3eb604982b6084b799cc2",
                "Source": "/var/lib/docker/volumes/54a5fad5f6c046a4afad7854caf90089a40d9c9d60d3eb604982b6084b799cc2/_data",
                "Destination": "/var/www",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
         ]
</pre>

Mesmo se eu remover o container, mas se tiver algo escrito no destination `/var/www` ele ainda vai existir, pois o que está escrito lá está ligada ao **Docker Host**, como se fosse um link. 

Portanto, os dados vão continuar ainda dentro do computador.

## Iniciando uma escrita no volume (montando ou linkando em /var/www pelo desktop)
```sh
docker run -it -v "C:\Users\Alura\Desktop:/var/www" ubuntu
```
Os `:` servem para separar os dois meios do container que vai rodar a imagem do ubuntu.

Funcionou, então:
<pre>root@abd0286c0083:/#</pre>

```sh
ls
cd var/www/
touch novo-arquivo.txt
echo "este arquivo foi criado dentro de um volume" > novo-arquivo.txt
cat novo-arquivo.txt
touch segundo-arquivo.txt
ls
```

## O que acontece se eu remover esse container?

Continuando o tópico acima, vamos sair do container e remover ele:

```sh
exit
docker ps -a
docker container prune
```

Aquela camada do container foi removida, porém os seus arquivos ainda permaneceram na sua Desktop!

## [Stand alone] Rodando código em apenas um container Docker
<img src="https://miro.medium.com/max/1600/1*_MtS4HqN2srTcrSyet61DQ.jpeg" height="177" align="right">

Já vimos que o que escrevemos no volume (pasta `/var/www` do container) aparece na pasta configurada da nossa máquina local, que no vídeo anterior foi o Desktop. Mas podemos pensar o contrário, ou seja, tudo o que escrevemos no Desktop será acessível na pasta `/var/www` do container.

Isso nos dá a possibilidade de implementar localmente um código de uma linguagem que não está instalada na nossa máquina, e colocá-lo para compilar e rodar dentro do container. Se o container possui **Node**, **Java**, **PHP**, seja qual for a linguagem, não precisamos tê-los instalados na nossa máquina, nosso ambiente de desenvolvimento pode ser dentro do container.

É isso que faremos, pegaremos um código nosso, que está na nossa máquina, e colocaremos para rodar dentro do container, utilizando essa técnica com volumes.

Para isso, vamos usar um exemplo escrito Node.js. Até podemos executar esse código na nossa máquina, mas temos que instalar o Node na versão certa em que o desenvolvedor implementou o código.

Agora, como fazemos para criar um container, que irá pegar e rodar esse código Node que está na nossa máquina? Vamos utilizar os volumes. Então, vamos começar a montar o comando.

Primeiramente, como vamos rodar um código em Node.js, precisamos utilizar a sua imagem:

```sh
docker run node
```

Ou

```sh
docker run -d -P --name running-node.js node
```

```sh
docker ps -a
docker ps -a -q
```

Além disso, precisamos criar um volume, que faça referência à pasta do código no nosso Desktop:

```sh
docker run -it -v "C:\Users\Dell\Desktop\volume-exemplo:/var/www" node
```

Mas para fazer esse servidor rodar, precisamos inserir o famoso NPM (Node Package Manager), com o comando `npm start`:

```sh
docker run -it -v "C:\Users\Dell\Desktop\volume-exemplo:/var/www" node npm start
```

Mas de acordo com a nossa aplicação, está programada para abrir a porta (endereço de ip) `:3000`:

```sh
docker run -p 8080:3000 -v "C:\Users\Dell\Desktop\volume-exemplo:/var/www" node npm start
```

Não funcionou, devido o nosso container não rodar na pasta `/var/www` e sim numa pasta aleatória! No caso do Ubuntu, ele rodou na pasta do boot do Ubuntu, então para ele rodar o comando do npm devemos especificar onde ele deve abrir o meu container!

Para isso passaremos mais uma flag, a `-w` que significa "working directory", então é só referenciar nosso diretório a ele:

## [Stand alone] Código completo do Docker + Node.js

```sh
docker run -d -p 8080:3000 -v "C:\Users\Dell\Desktop\volume-exemplo:/var/www" -w "/var/www" node npm start
```

Feito todos esses passos, o container irá funcionar no `localhost:8080`.

![docker](https://user-images.githubusercontent.com/61624336/127756743-e508fd13-0126-4549-a5a5-612e560368f5.png)

Eu posso modificar esse arquivo e atualizar a página localmente, que o servidor vai automaticamente atualizar. Portanto, eu tenho um ambiente de desenvolvimento dentro do meu desktop.

O container pode ser compartilhado entre a equipe e todos conteram a mesma versão dos programas e do ambiente.

Portanto, isso já resolve aquele velho erro de produção:

> Funcionou na minha máquina, na sua não? Como assim!? 🤔😕

> Então, se funcionou em uma máquina, funcionou em todas as máquinas! Esse é o grande benefício do Docker! 🐋 🐳 💙

## Interpolação de comando

```sh
docker run -d -p 8080:3000 -v "$(pwd):/var/www" -w "/var/www" node npm start
```

## [Stand alone] Dockerfile
<img src="https://user-images.githubusercontent.com/61624336/205774538-5e50423e-5ab9-43c7-b039-400838a55f43.png" height="177" align="right">

```sh
docker images
```

Já trabalhamos com a imagem do **ubuntu**, **hello-world**, **dockersamples/static-site** e por fim do **node**, mas até agora não criamos a nossa própria imagem, para podermos distribuir para as outras pessoas.

A **imagem** é como se fosse uma receita de bolo. Então, para criarmos a nossa própria imagem, temos que criar a nossa receita de bolo, o **Dockerfile** (arquivo Docker), ensinando o Docker a criar uma imagem a partir da nossa aplicação, para que ela seja utilizada em outros locais.

[![dockerfile](https://img.shields.io/badge/-dockerfile-cyan?style=social&logo=docker&logoColor=5050)](#) [![dockerfile](https://img.shields.io/badge/-node.dockerfile-cyan?style=social&logo=docker&logoColor=5050)](#)

O **Dockerfile** nada mais é do que um arquivo de texto, e ele possui o nome identificador, porém se alterarmos o nome com o identificador , como `node.dockerfile`, ele será reconhecido, mesmo assim. No caso de possuir muitos Dockerfile é realmente interessante modificar o nome pela tecnologia que você está utilizando.

Vamos agora, distribuir nosso arquivo para que os outros desenvolvedores possam utilizar, para isso utilizaremos os comandos do Dockerfile:

## [Stand alone] Montando imagem no Dockerfile
Você pode criar do zero ou pegar uma imagem base para isso! Vamos fazer nosso dockerfile se basear no node, então:

[![dockerfile](https://img.shields.io/badge/-dockerfile-cyan?style=social&logo=docker&logoColor=5050)](#)

```dockerfile
FROM node:latest
MAINTAINER isaacalves7 
ENV NODE_ENV=production
ENV PORT=3000
COPY . /var/www
WORKDIR /var/www
RUN npm install 
ENTRYPOINT npm start ["npm", "start"]
EXPOSE $PORT
```

- Identificamos com o `FROM` e depois imputamos a imagem e o sinal `:` servem para especificar a versão da imagem, no caso `latest` (última versão);
- Outra instrução que é comum colocarmos é quem cuida, quem criou a imagem, através do `MAINTAINER`;
- Variável de ambiente `ENV`, servem apenas para não gerar um log ao cliente;
- Com o `COPY` copiamos nosso diretório padrão para rodar o node;
- Com o `WORKDIR` assim que o container carregar o diretório, ele executará o mesmo com o `npm start`;
- Com o `RUN` instalamos nossas dependências necessárias. E assim, podemos deletar a pasta `node_modules`, pois o que vale é apenas o dockerfile;
- Com o `ENTRYPOINT`, cujo gerencia as dependências com base no *JSON*.

Feito a imagem, é hora de *buildar* essa imagem, para isso devemos ir ao terminal e inserir:
```sh
docker build -f Dockerfile -t isaacalves7/node-file .
```
Após rodar esse comando, o docker fará dele uma imagem. Portanto:
```sh
docker images
```
Dessa forma, poderá startar o comando dentro do repositório do arquivo:
```sh
docker run -d -p 8080:3000 isaacalves7/node-file
```
Podemos alterar a variável de ambiente:
```sh
docker run -d -p 1234:8080 isaacalves7/node-file
```

## [Stand alone] Subindo imagens no Docker Hub

Faça sua conta no <a href="https://hub.docker.com/">Docker Hub</a>, depois ensira os comandos no terminal:
```sh
docker login
docker push [nome da imagem]
```
No caso `docker login` é para se logar no Docker Hub, para ele reconhecer nosso ambiente e usuário. 

Depois, inserimos `docker push [nome da imagem]` para ele subir nossa imagem no Docker Hub, para outros usuários terem acesso!

## Somente baixar a imagem
```sh
docker pull [nome da imagem]
```

Link do meu perfil: [![Docker HUB](https://img.shields.io/badge/-Docker_Hub-ffffff?style=flat&logo=Docker&logoColor=2496ED)](https://hub.docker.com/u/isaacalves7)

# 🖧 Networking no Docker (Tipos de Redes)
<img src="https://user-images.githubusercontent.com/61624336/127785006-850089e0-c8e6-48f2-9a1f-776bffab2928.png" height="177" align="right">

Veremos como funciona a rede, e como fazemos para **interligar diversos containers** no Docker. Normalmente uma aplicação é composta por diversas partes, sejam elas o `load balancer/proxy`, a `aplicação em si`, `um banco de dados`, etc. Quando estamos trabalhando com containers, é bem comum separarmos cada uma dessas partes em um container específico, para cada container ficar com somente uma única responsabilidade.

Mas se temos uma parte da nossa aplicação em cada container, como podemos fazer para essas partes falarem entre elas? Pois para a nossa aplicação funcionar como um todo, os containers precisam trocar dados entre eles.

![tipos de redes no Docker](https://user-images.githubusercontent.com/61624336/143094304-926f55c6-2be2-416b-a254-3763c8ae0f17.png)

## Bridge network
![bridge-network-docker](https://user-images.githubusercontent.com/61624336/143094352-be8c806f-320f-42cc-bac5-a4bbcbe8cb4e.png)

## Host network
![host-network-docker](https://user-images.githubusercontent.com/61624336/143094409-0cb3775e-47b4-4e3a-86cb-c87ec56233fe.png)

## Overlay network
![overlay-network-docker](https://user-images.githubusercontent.com/61624336/143094454-7e0fe05a-3290-47b1-af88-d3ff49c01f47.png)

A boa notícia é que no Docker, por padrão, já existe uma *default network*, que no caso é a rede **bridge**. Isso significa que, quando criamos os nossos containers, por padrão eles funcionam na mesma rede:

<img src="https://user-images.githubusercontent.com/61624336/127784447-8a94e9a0-6b97-4313-9322-8241518242e1.png" height="270">

## [Stand alone] Verificando o default container com o Ubuntu
```sh
docker run -it ubuntu
```
## [Stand alone] Inspecionando a rede
```sh
docker inspect [id container]
```
<pre>
"Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "905b02e0985ba89a81e437141cfbc432e7aa82c73fd51b2979ef989c9f8ec2a5",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "DriverOpts": null
                }
</pre>

Logo, inserimos o comando:
```sh
hostname -i
ping 172.17.0.3
```
Depois disso, o comando não funcionará, pois a imagem do Ubuntu só possui o básico para o Ubuntu funcionar via container.

Para isso, precisamos utilizar os comandos do Linux, como o de instalação de pacotes:
```sh
apt-get update && apt-get install
```
Agora o nome do pacote:
```sh
apt-get update && apt-get install -y iputils-ping
```
O `iputils-ping` serve para fazer o ping dentro do container.
```sh
ping --help
ping 172.17.0.3
```

## Comunicação de containers
```sh
docker network create --driver bridge [nome da rede]
docker network ls
```

## Criando um novo container atrelada a rede
```sh
docker run -it --name meu-container-de-ubuntu --network [nome da rede] ubuntu
docker inspect [nome do container]
```

## Pegando dados de um banco
<img src="https://miro.medium.com/max/2560/1*I6ln5yd_pGKl93_iX-RkyQ.jpeg" height="107" align="right">

Para praticar o que vimos sobre redes no Docker, vamos criar uma pequena aplicação que se conectará ao banco de dados, utilizando tudo o que vimos no vídeo anterior.

O que vamos fazer é utilizar a aplicação alura-books, que irá pegar os dados de um banco de dados de livros e exibi-los em uma página web. É uma aplicação feita em **Node.js** e o banco de dados é o **MongoDB**.

## Pegando dados de um banco em um outro container
<img src="https://miro.medium.com/max/1316/1*W3hJmVyfUFvolUowVDlBkA.png" align="right" height="307">

Então, primeiramente vamos baixar essas duas imagens, a imagem <code><a href="https://s3.amazonaws.com/caelum-online-public/646-docker/05/projetos/alura-docker-cap05.zip">douglasq/alura-books</a></code> na versão cap05 e a imagem `mongo`:

```sh
docker pull douglasq/alura-books:cap05
docker pull mongo
docker images
```

Na imagem `douglasq/alura-books`, não há muito mistério. Ela possui o arquivo `server.js`, que carrega algumas dependências e módulos que são instalados no momento em que rodamos a imagem. Esse arquivo carrega também as configurações do banco, que diz onde o banco de dados estará em execução, no caso o seu host será **meu-mongo**, e o **database**, com nome de **alura-books**. Então, quando formos rodar o container de MongoDB, seu nome deverá ser **meu-mongo**. Além disso, o arquivo realiza a conexão com o banco, configura a porta que será utilizada (3000) e levanta o servidor.

[![server.js](https://img.shields.io/badge/-server.js-yellow?style=social&logo=JavaScript&logoColor=yellow)](#)

![code](https://user-images.githubusercontent.com/61624336/127849309-60f38f8e-93c8-4230-83e4-1e4d328f8471.png)

No Dockerfile da imagem, também não há mistério, é basicamente o que vimos no vídeo anterior. Por fim, temos as rotas, que são duas: a `rota /`, que carrega os livros e os exibe na página, e a rota `/seed`, que salva os livros no banco de dados.

[![alura-books.dockerfile](https://img.shields.io/badge/-alura--books.dockerfile-cyan?style=social&logo=docker&logoColor=5050)](#)

![code](https://user-images.githubusercontent.com/61624336/127850005-fd0bc7c5-3c11-4525-b277-93bdc777605a.png)

Caso queira, você pode baixar aqui o código da versão cap05 da imagem alura-books.

Visto isso, já podemos subir a imagem:

```sh
docker run -d -p 8080:3000 douglasq/alura-books:cap05
```

Ao acessar a página `http://localhost:8080/`, nenhum livro nos é exibido, pois além de não termos levantado o banco de dados, nós não salvamos nenhum dado nele. 

![docker](https://user-images.githubusercontent.com/61624336/127853997-4642628a-8723-4847-812c-5d054a27c26e.png)

```sh
docker rm -f 43
```

Então, vamos excluir esse container e subir o container do **MongoDB**, lembrando que o seu nome deve ser **meu-mongo**, e vamos colocá-lo na rede que criamos na sessão anterior:

```sh
docker run -d --name meu-mongo --network minha-rede mongo
```

Com o banco de dados rodando, podemos subir a aplicação do mesmo jeito que fizemos anteriormente, mas não podemos nos esquecer que **ele deve estar na mesma rede do banco de dados**, logo vamos configurar isso também:

```sh
docker run --network minha-rede -d -p 8080:3000 douglasq/alura-books:cap05
```
Verificando a rede:
```sh
docker network ls
docker inspect minha-rede
```
Possuimos então dois containers na mesma rede (`minha-rede`):
<pre>
"Containers": {
            "7dcce72922f00b3e3db3c9bc925c72cefb6495b0bb6e6d4a525629b62cc5a297": {
                "Name": "meu-mongo",
                "EndpointID": "89bb420463f092d057118137ce5b08d936194cae35db7f9886e3afcd8612042e",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "c68d8bbc9cdaea5fe75a3fce1e024eb6251f72e762c38b76670dd0ee7049d911": {
                "Name": "dreamy_wilbur",
                "EndpointID": "c3010c6d9c05af02d38b8816530677199d020b9c01d232f9fb86037375a3f5a4",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
</pre>

Agora, acessamos a página `http://localhost:8080/seed/` para salvar os livros no banco de dados. Após isso, acessamos a página `http://localhost:8080/` e vemos os dados livros são extraídos do banco e são exibidos na página. 

![docker](https://user-images.githubusercontent.com/61624336/127857583-b282f317-d6d2-4652-99a5-842758c00fc6.png)

![docker](https://user-images.githubusercontent.com/61624336/127858413-d5c2c647-57e7-4212-8914-f556260a2e40.png)

Para provar isso, podemos parar a execução do **meu-mongo** e atualizar a página, veremos que nenhum livro mais será exibido.
```
docker stop meu-mongo
docker ps
```
Então, esse foi um exemplo para praticar a comunicação entre containers, sempre lembrando que devemos colocá-los na mesma rede.

## Docker Swarm
<div align="center"><img src="https://blog.pridybailo.com/wp-content/uploads/sites/2/2015/01/swar-q.png" height="177"></div>

Ok, tudo bem até aqui. Agora vou ter vários serviços rodando usando o Docker. E para facilitar a criação desses containers já aprendemos usar o *Docker Compose* que sabe subir vários containers. O Docker Compose é a ferramenta ideal para coordenar a criação dos containers, no entanto para melhorar a escalabilidade e desempenho pode ser necessário criar muito mais containers para um serviço específico. 

<img src="https://user-images.githubusercontent.com/61624336/171045139-68a0b570-7649-47ac-bf39-65649c486d2a.gif" align="right" height="177"> 

Em outras palavras, agora gostaríamos de criar muitos containers aproveitando várias máquinas (virtuais ou físicas)! Ou seja, pode ser que um microsserviço fique rodando em 20 containers usando três máquinas físicas diferentes. Como podemos facilmente subir e parar esses containers? Repare que o Docker Compose não é para isso e por isso existe uma outra ferramenta que se chama **Docker Swarm**.

**Docker Swarm** facilita a criação e administração de um **cluster** de containers.

## Network: Bridge Mode
```sh
docker network create -d bridge petaBridge
docker network ls
docker run -d --net petaBridge --name db consul:latest
docker run -d --env "DB=db" --net petaBridge --name web -p 8000:5000 poroko/flask-demo-app
docker ps -a
docker exec -it web bash
bash$ ls
bash$ exit
docker exec -it web sh
```

## Network: Host Mode
```sh
docker run -d --net host --name db consul 
docker run -d --env "DB=localhost" --net host poroko/flask-demo-app
docker ps
netstat -nltp
```

<blockquote><b>WARNING</b>: Nesse modelo de rede, você não pode colocar outra aplicação da mesma linguagem na mesma porta, você precisa abrir outra porta!</blockquote>

<!-- ![blog](https://user-images.githubusercontent.com/61624336/171045202-dedec1f4-160f-4e6e-b923-2b07061e1da9.gif) -->

## [SWARM] Duas instâncias em rede bridge
<div align="center"><img src="https://user-images.githubusercontent.com/61624336/205681549-06914673-d715-4ba8-b834-d9d6274bebcc.png" height="177"></div><br \>

Faremos um Docker Swarm, onde uma das duas máquinas será o gerenciador, ou seja, o principal controlando os demais nós desse cluster (no caso, a máquina).

![Node2](https://img.shields.io/badge/192.168.0.8-node1-0095D5?&style=for-the-badge&logo=Docker&logoColor=white)

Container de front-end (Flask = chrch/docker-pets:1.0 = web) e rede Bridge (petsBridge). No caso, esse vai ser o container principal do Docker SWARM. 

Podemos escalonar o tráfego de rede com o Docker SWARM, criando assim o Load Balancer entre os dois containers (cada um em uma instância diferente).

```sh
docker network create -d bridge petsBridge
docker run -it --env "DB=db" --net petsBridge --name web -p 8000:5000 chrch/docker-pets:1.0
docker ps -a
docker restart [id do container]
ifconfig
docker swarm init --advertise-addr [IP do container]
> docker swarm join --token SWMTKN-1-1qrnyxbd5bja864nx27sdzpuwd52j60kdt1350ox42h45pkage-1jrrm0afvugxzdin34a5y6k1y 192.168.0.28:2377
docker node ls
hostname
docker network create -d overlay petsOverlay 
docker service create --network petsOverlay --name db consul
docker service create --network petsOverlay -p 8000:5000 -e "DB=db" --name web chrch/docker-pets:1.0
docker service ls
docker service scale web=3
```

![Node2](https://img.shields.io/badge/192.168.0.7-node2-0095D5?&style=for-the-badge&logo=Docker&logoColor=white)

Container de back-end (Consul = db) e rede Bridge (petsBridge). Esse será chamado através do token de acesso do docker swarm para se juntar com o outro container, ligados pelo mesmo modelo de rede.

```sh
docker network create -d bridge petsBridge
docker run -d --net petsBridge --name db consul
docker swarm join --token SWMTKN-1-1qrnyxbd5bja864nx27sdzpuwd52j60kdt1350ox42h45pkage-1jrrm0afvugxzdin34a5y6k1y 192.168.0.28:2377
docker logs [ID container]
netstat -nltp
```

## Armazenamento no Docker (Docker storage)
<img src="https://user-images.githubusercontent.com/61624336/143244782-08e93b38-b7b9-4424-8abc-06bb2c81a862.png" align="right" height="177">

Tanto o **bind mounts** quanto o **volume**, se o container for removido ou reiniciado, as informações serão persistidas em disco.

O **tmpfs mounts** é um disco temporário para guardar logs e caches da sua aplicação, o container for reiniciado ou removido esses arquivos serão deletados, ou seja, não foram para a camada de escrita do container. 

**Exemplo 1**: Criando volume

```sh
docker volume create meuPrimeiroVolume
docker volume ls
docker volume inspect meuPrimeiroVolume
docker run -d -p 80:80 --name container-volume --mount source=meuPrimeiroVolume,nginx:latest
```

## Limites no Docker
Precisamos ter limites de memória e CPU no Docker, se acaso a sua aplicação consumir muito recurso de memória ou CPU desnecessariamente, siga o exemplo abaixo:

```sh
docker run -d --memory 10m busybox sleep 3600
docker stats
docker run --cpus=".5" -d --rm programium/stress -c 8 -t 50s
```

![projeto-dio](https://user-images.githubusercontent.com/61624336/168445440-9feeb711-d1e3-48dd-9dbd-fab4ae4573c1.png)

## Docker Compose
<div align="center"><img src="https://openwhisk.apache.org/images/deployments/logo-docker-compose-text.svg" height="177"></div><br />

Agora vamos subir múltiplos containers! Mas como? Simples! Basta rodar os dois comandos ao mesmo tempo:

```sh
docker run -d --name meu-mongo --network rede-producao mongo
docker run -d -p 3000:3000 --name alura-books --network rede-producao douglasq/alura-books 
```
Contudo, essa forma de subir containers é um tanto falha, pois possue a maior probabilidade de:

- Esquecer de uma flag;
- Esquecer um parâmetro importante;
- Muito manual;
- Fácil de errar;
- Preciso garantir a ordem.

### [Compose] Funcionamento das aplicações em geral
Na vida real, sabemos que a aplicação é maior que somente dois containers, geralmente temos dois, três ou mais containers para segurar o tráfego da aplicação, distribuindo a carga. Além disso, temos que colocar todos esses containers para se comunicar com o **banco de dados** em um outro container, mas quanto **maior a aplicação**, devemos ter mais de um container para o banco também.

E claro, se temos três aplicações rodando, não podemos ter três endereços diferentes, então nesses casos utilizamos um **Load Balancer** em um outro container, para fazer a distribuição de carga quando tivermos muitos acessos. Ele recebe as requisições e distribui para uma das aplicações, e ele também é muito utilizado para servir os arquivos estáticos, como *imagens*, *arquivos CSS* e *JavaScript*. 

Assim, a nossa aplicação controla somente a *lógica, as regras de negócio*, com os *arquivos estáticos* ficando a cargo do **Load Balancer**:

![docker](https://user-images.githubusercontent.com/61624336/127864036-d3efbc43-67b0-4fc0-a89d-967fe2bc93f7.png)

Se formos seguir esse diagrama, teríamos que criar cinco containers na mão, e claro, cada container com configurações e flags diferentes, além de termos que nos preocupar com a ordem em que vamos subi-los. Ou seja, subir 5 flags na mão??? subir tudo isso de forma manual??? Que trabalhooooo....

<img src="https://lockmedown.com/wp-content/uploads/2017/01/docker-compose-logo.png" height="177" align="right">

[![dockerfile](https://img.shields.io/badge/-docker--compose.yaml-pink?style=social&logo=docker&logoColor=magenta)](#)

Então, para isso foi criada uma ferramenta no Docker chamada **Docker Compose**, que ele executa e auxilia na build ou criação de vários (múltiplos) containers simultaneamente. Ele funciona seguindo um arquivo de texto, chamado `docker-compose.yaml`.

Esse arquivo serve para dar um conjunto de passos ao Docker e com ele podemos controlar quais containers sobem primeiro, ou seja, funciona como um processo de BIOS ou um automatizador de containers.

O *Docker Compose* cria uma rede padrão. Também é possível criar uma nova rede usando o comando `docker network`.

> **OBS**: O **YAML** é semelhante ao **JSON**.

Seguindo o mesmo diagrama, vejamos nossa nova aplicação rodando atualizada para o uso do Docker Compose:

![docker](https://user-images.githubusercontent.com/61624336/127864036-d3efbc43-67b0-4fc0-a89d-967fe2bc93f7.png)

O código do projeto pode ser <a href="https://s3.amazonaws.com/caelum-online-public/646-docker/06/projetos/alura-docker-cap06.zip">baixado aqui</a>.

Para começarmos a entender como funciona o Docker Compose, primeiramente vamos entender como funciona a aplicação que utilizaremos como base. É uma aplicação bem semelhante à utilizada na sessão anterior, **com o mesmo servidor, rotas e banco de dados**. De novidade, é que agora precisamos criar o **NGINX**, que é mais um container que devemos subir.

Então, ao utilizamos a **imagem nginx**, ou criamos a nossa própria. Como vamos configurar o NGINX para algumas coisas específicas, como lidar com os *arquivos estáticos*, vamos criar a nossa própria imagem, por isso que na aplicação há o `nginx.dockerfile`:

![image](https://user-images.githubusercontent.com/61624336/127871319-51894a5f-2b71-4b40-8098-362bef3198ae.png)

[![dockerfile](https://img.shields.io/badge/-nginx.dockerfile-blue?style=social&logo=docker&logoColor=blue)](#)

```dockerfile
FROM nginx:latest
MAINTAINER isaacalves7
COPY /public /var/www/public
COPY /docker/config/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80 443
ENTRYPOINT ["nginx"]
# Parametros extras para o entrypoint
CMD ["-g", "daemon off;"]
```

Nesse arquivo, nós utilizamos a última versão disponível da imagem do nginx como base, e copiamos o conteúdo da pasta `public`, que contém os *arquivos estáticos*, e *um arquivo de configuração do NGINX para dentro do container*. Além disso, abrimos as portas `80` e `443` e executa o NGINX através do comando nginx, passando os parâmetros extras `-g` e `daemon off`.

Por fim, vamos ver um pouco sobre o *arquivo de configuração do NGINX*, para entendermos um pouco como o **load balancer** está funcionando.

No arquivo `nginx.conf`, dentro server, está a parte que trata de servir os *arquivos estáticos*. Na `porta 80`, no localhost, em `/var/www/public`, ele será responsável por servir as pastas `css`, `img` e `js`. E todo resto, que não for esses três locais, ele irá jogar para o `node_upstream`.

No **node_upstream**, é onde ficam as configurações para o NGINX redirecionar as conexões que ele receber para um dos três containers da nossa aplicação. O redirecionamento acontecerá de forma circular, ou seja, a **primeira conexão irá para o primeiro container**, a **segunda irá para o segundo container**, a **terceira irá para o terceiro container**, na **quarta, começa tudo de novo**, **e ela vai para o primeiro container e assim por diante**.

<blockquote>Isso já está tudo pronto, basta baixarmos o <a href="https://s3.amazonaws.com/caelum-online-public/646-docker/06/projetos/alura-docker-cap06.zip">código da imagem e da aplicação aqui</a>.</blockquote>

## Criando o `docker-compose.yaml`
> Tanto faz as duas extensões, as duas são a mesma linguagem!

[![dockerfile](https://img.shields.io/badge/-docker--compose.yaml-pink?style=social&logo=docker&logoColor=magenta)](#) [![dockerfile](https://img.shields.io/badge/-docker--compose.yml-pink?style=social&logo=docker&logoColor=magenta)](#) 

Para utilizar o Docker Compose, devemos criar o seu arquivo de configuração, o `docker-compose.yml`, na raiz do projeto. Em todo arquivo de Docker Compose, que é uma espécie de receita de bolo para construirmos as diferentes partes da nossa aplicação, a primeira coisa que colocamos nele é a versão do Docker Compose que estamos utilizando:

[![dockerfile](https://img.shields.io/badge/-docker--compose.yaml-pink?style=social&logo=docker&logoColor=magenta)](#)

```yaml
version: '3'
services:
    nginx:
        build:
            dockerfile: ./docker/nginx.dockerfile
            context: .
        image: douglasq/nginx
        container_name: nginx
        ports:
            - "80:80"
        networks: 
            - production-network
        depends_on: 
            - "node1"
            - "node2"
            - "node3"

    mongodb:
        image: mongo
        networks: 
            - production-network

    node1:
        build:
            dockerfile: ./docker/alura-books.dockerfile
            context: .
        image: douglasq/alura-books
        container_name: alura-books-1
        ports:
            - "3000"
        networks: 
            - production-network
        depends_on:
            - "mongodb"

    node2:
        build:
            dockerfile: ./docker/alura-books.dockerfile
            context: .
        image: douglasq/alura-books
        container_name: alura-books-2
        ports:
            - "3000"
        networks: 
            - production-network
        depends_on:
            - "mongodb"

    node3:
        build:
            dockerfile: ./docker/alura-books.dockerfile
            context: .
        image: douglasq/alura-books
        container_name: alura-books-3
        ports:
            - "3000"
        networks: 
            - production-network
        depends_on:
            - "mongodb"

networks: 
    production-network:
        driver: bridge
```

Estamos utilizando a versão 3 pois é a versão mais recente no momento da criação do treinamento. O YAML lembra um pouco o JSON, mas ao invés de utilizar as chaves para indentar o código, ele utiliza espaços.

Agora, começamos a descrever os nossos serviços, os nossos services:

No *YAML*, toda vez que colocamos o caractére `-` significa que disponibilizamos um array.

Um serviço é uma parte da nossa aplicação. Lembrando do nosso diagrama:

![docker](https://user-images.githubusercontent.com/61624336/127864036-d3efbc43-67b0-4fc0-a89d-967fe2bc93f7.png)

Temos **NGINX**, **três Node**, e o **MongoDB** como *serviços*. Logo, se queremos construir *cinco containers*, vamos construir *cinco serviços*, cada um deles com um nome específico.

Então, vamos começar construindo o *NGINX*, que terá o nome **nginx*.

Em cada serviço, devemos dizer como devemos construí-lo, como devemos fazer o seu build:

O serviço será construído através de um Dockerfile, então devemos passá-lo onde ele está. E também devemos passar um contexto, para dizermos a partir de onde o Dockerfile deve ser buscado. Como ele será buscado a partir da pasta atual, vamos utilizar o ponto:

Construída a imagem, devemos dar um nome para ela, por exemplo douglasq/nginx:

E quando o Docker Compose criar um container a partir dessa imagem, vamos dizer que o seu nome será nginx:

Sabemos também que o NGINX trabalha com duas portas, a 80 e a 443. Como não estamos trabalhando com HTTPS, vamos utilizar somente a porta 80, e no próprio arquivo, podemos dizer para qual porta da nossa máquina queremos mapear a porta 80 do container. Vamos mapear para a porta de mesmo número da nossa máquina:

No YAML, toda vez que colocamos um traço, significa que a propriedade pode receber mais de um item. Agora, para os containers conseguirem se comunicar, eles devem estar na mesma rede, então vamos configurar isso também. Primeiramente, devemos criar a rede, que não é um serviço, então vamos escrever do começo do arquivo, sem as tabulações:

O nome da rede será production-network e utilizará o driver bridge:

Com a rede criada, vamos utilizá-la no serviço:

Isso é para construir o serviço do NGINX, agora vamos construir o serviço do MongoDB, com o nome mongodb. Como ele será construído a partir da imagem mongo, não vamos utilizar nenhum Dockerfile, logo não utilizamos a propriedade build. Além disso, não podemos nos esquecer de colocá-lo na rede que criamos:

Falta agora criarmos os três serviços em que ficará a nossa aplicação, node1, node2 e node3. Para eles, será semelhante ao NGINX, com Dockerfile alura-books.dockerfile, contexto, rede production-network e porta 3000:

Com isso, a construção dos nossos serviços está finalizada.

#### Ordem dos serviços
Por último, quando subimos os containers na mão, temos uma ordem, primeiro devemos subir o mongodb, depois a nossa aplicação, ou seja, node1, node2 e node3 e após tudo isso subimos o nginx. Mas como que fazemos isso no docker-compose.yml?

Nós podemos dizer que os serviços da nossa aplicação dependem que um serviço suba antes deles, o serviço do mongodb.

Da mesma forma, dizemos que o serviço do nginx depende dos serviços node1, node2 e node3.

## [Docker Compose] Subindo os serviços
Com o `docker-compose.yml` pronto, podemos subir os serviços, mas antes devemos garantir que temos todas as imagens envolvidas neste arquivo na nossa máquina. Para isso, dentro da pasta do nosso projeto, executamos o seguinte comando:

#### Inicia o Docker Compose
```sh
docker-compose build
```
Depois de buildar os serviços, eles podem ser vistos em:
```sh
docker images
```
Após isso, podemos inserir o comando:

#### Starta todas as solicitações (build) do Docker Compose
```sh
docker-compose up
```
Ou
```sh
docker-compose up -d
```
#### Listando e verificando o docker-compose
```sh
docker-compose ps
```

#### Para e remove todos os containers do docker-compose
```sh
docker-compose down
```
#### Verificando que os containers estão se comunicando
```sh
docker exec -it alura-books-1 ping alura-books-2
docker exec -it alura-books-1 ping node2
```
  
#### Reinicializando containers
```sh
docker-compose restart
```

## Docker e Microsserviços
Trabalhar com uma arquitetura de microsserviços gera a necessidade de publicar o serviço de maneira rápida, leve, isolada e vimos que o Docker possui exatamente essas características! Com Docker e Docker Compose podemos criar um ambiente ideal para a publicação destes serviços.

O Docker é uma ótima opção para rodar os microsserviços pelo fato de isolar os containers. Essa utilização de containers para serviços individuais faz com que seja muito simples gerenciar e atualizar esses serviços, de maneira automatizada e rápida.