Docker e Containers
Muitos desenvolvedores sabem o quanto o processo de virtualização costuma ser demorado, tornando ineficiente o desenvolvimento e manutenção de softwares. Porém, com o avanço da tecnologia, é interessante que você compreenda um conceito diferente de virtualização e, para isso, você deve entender o que é Docker.
O Docker foi criado com o objetivo de facilitar o desenvolvimento, acelerar a implantação e a execução de aplicações em ambientes isolados. Ele foi desenhado especialmente para disponibilizar aplicações da forma mais rápida possível, apoiado por metodologias ágeis e pelo modelo DevOps.
Com o Docker você consegue melhorar a forma de como você distribui os desenvolvedores (squads independentes) e também as aplicações que eles criam (microsserviços independentes).
Com ele você também elimina o famoso problema de “dependency hell” (mas na minha máquina funciona!), pois o Docker empacota sua aplicação com todas as suas dependências necessárias, simplificando muito o processo de implantação e com um custo e tempo muito menor que máquinas virtuais.
Afinal, o que é Docker?
Docker é uma tecnologia de virtualização (diferente de um sistema de virtualização tradicional) que possibilita o empacotamento de uma aplicação ou ambiente inteiro dentro de um contêiner, e então, o ambiente inteiro torna-se portável para qualquer outro host com docker instalado.
Dessa forma, não importa o ambiente em que você esteja rodando, o Docker garante que a aplicação rodará sempre da mesma maneira, pois o contêiner além de conter a sua aplicação, também contém tudo o que você precisa para que o seu sistema funcione (SO, bibliotecas, softwares, arquivos, etc).
Docker também pode ser definido como uma engine de administração de contêineres, ele é uma plataforma open-source escrita na linguagem GO e que utiliza por trás os serviços do LXC (Linux Contêiner).
Como o Docker funciona?
O Docker aproveita da infraestrutura do kernel do seu sistema operacional para não precisar instalar outro SO completo. Ao invés disso ele utiliza uma engine (Docker Engine, como na imagem abaixo) para abstrair as chamadas de SO das suas aplicações e utilizar as libraries e binários já existentes.
Para criar e executar os seus containers, o Docker cria processos (como os programas normais) mantendo isolado em cada um deles as dependências da sua aplicação, garantindo que estejam instaladas nela apenas as bibliotecas (libs) necessárias para fazer a sua aplicação funcionar.
Desta forma não é necessário que você instale todo um sistema operacional novo em cima do seu já existente, para que você tenha todos os recursos do mesmo. Basta reutilizar os recursos que o seu próprio SO já utiliza.
Como o Docker utiliza os recursos da máquina host, ou seja, o kernel da máquina host é compartilhado com os contêineres, o contêiner utiliza muito menos memória e acaba sendo muito mais rápido que uma VM.
Algumas características do Docker
O docker consegue ter a maior parte dos benefícios das máquinas virtuais (isolamento do sistema, elasticidade de recursos de CPU e memória, etc) e ainda com a vantagem de utilizar muito menos recurso que uma VM, sendo muito mais rápido ao iniciar e executar.
- É possível iniciar um contêiner em menos de 1 segundo, pois não existe um S.O. em cada contêiner.
- A quantidade de recurso de memória que um contêiner necessita é muito menor do que uma máquina virtual necessita.
- É possível você ter 20 a 30 contêineres executando em um servidor, mas é inviável você ter a mesma quantidade de VMs rodando numa mesma CPU.
Ele fornece uma camada adicional de abstração para que você possa administrar seus contêineres através de ferramentas o Docker Client (client de linha de comando), API Rest e do Docker File (arquivos descritores).
O docker empacota software com vários níveis de isolamento: limites de memória, CPU, limite de rede, isolamento do file system dentro do contêiner, limites de uso de I/O, etc.
E o que são Contêineres?
Contêiner é o nome dado para a segregação de processos no mesmo kernel, de forma que o processo seja isolado o máximo possível de todo o resto do ambiente.
Os contêineres docker empacotam componentes de software em um sistema de arquivos completo, que contem tudo necessário para a sua execução: código, runtime, ferramentas de sistema (qualquer coisa que possa ser instalada em um servidor), etc. Isso garante que o software sempre irá executar da mesma forma, independente do seu ambiente.
Por característica, o contêiner é um ambiente leve e portátil, no qual aplicações são executadas, onde ele encapsula todos os binários (código) e bibliotecas necessárias para execução de uma aplicação ou um software.
O que são Imagens Docker?
A Imagem é um modelo de um sistema de arquivos (somente leitura) usado para criar contêiner. Um contêiner se baseia numa imagem para ser criado.
As imagens são criadas através de um processo de build, com um descritor (DockerFile) com um passo a passo e os detalhes para a geração da imagem.
O armazenamento das imagens pode ser feito local, utilizando o Registry ou utilizando o Docker Hub (Cloud), onde elas são versionadas e com Tags.
As imagens são compostas por uma ou mais camadas (layers), onde uma camada representa uma ou mais mudanças no sistemas de arquivos.
- Uma camada é também chamada de Imagem Intermediária.
- Qualquer alteração numa imagem é gerada uma nova camada (layer).
- O objetivo da estratégia de dividir uma imagem em camadas é o reuso das camadas em outras imagens (compartilhar camadas entre imagens).
- A junção de todas as camadas formam a Imagem.
- Quando o contêiner inicia, ele cria uma nova camada e ela é a única que pode ser alterada.
Arquitetura do Docker
O sistema do Docker é dividido basicamente em três partes: Docker Daemon e Server (com containers e imagens), Docker Registry e Docker Client.
- Docker Daemon, Server ou Engine: Camada de administração, recebe comandos e os executa. Quando recebe a solicitação de criação de um contêiner, baixa a imagem do Registry, monta todas as camadas (layers), faz o build da imagem e gera um novo contêiner.
- Docker Registry: Repositório de imagens docker, podendo ser local ou cloud (Docker Hub). Para uso corporativo o ideal é instalar o Registry.
- Docker Client: Formas de interagir com o Docker Daemon (linha de comando, API ou Interface Gráfica).
Tipos de Rede do Docker
O que o Docker chama de rede, na verdade é uma abstração criada para facilitar o gerenciamento da comunicação de dados entre containers e os nós externos ao ambiente Docker.
Quando você cria um novo container, você precisa definir qual a rede (network) que esse container vai pertencer, definindo a sua comunicação.
- None Network: Os contêineres são isolados, não possuem acesso entre si e nem ao mundo exterior. Se o processamento do container não depende de conexão de rede, esse modelo é o mais indicado por ser o mais seguro. A única forma de acesso a esse tipo de container é via terminal, pois não existe interface de rede criada dentro do container.
- Bridge Network: É o modo padrão, cria uma camada de isolamento da rede da máquina host com a rede de cada um dos contêineres. É uma ponte gerenciada pelo Docker, você configura as redes da forma que quiser para os contêineres. Para cada rede bridge criada, ele cria uma subnet independente (diferente faixas de IPs), porem, você pode adicionar um container a mais de uma rede, caso haja necessidade de comunicação.
- Host Network: Você não tem uma bridge entre as interfaces de rede da máquina host com as interfaces de rede dos contêineres. Nesse modo o contêiner vai utilizar diretamente as interfaces de rede da máquina host, com um nível de proteção mais baixo (por não existir uma camada de bridge), porem, você tem mais velocidade de comunicação.
O arquivo Dockerfile
A principal preocupação que você deve ter ao querer utilizar o Docker é como você vai empacotar a sua aplicação para poder distribuir. Para isso, você precisa listar quais dependências a sua aplicação precisa, quais softwares precisam ser instalados, qual sistema operacional você vai utilizar (Ubuntu, CentOs, Redhat), quais pacotes precisam ser copiados, qual o language runtime que será utilizado (Java, PHP, Python), como você vai conectar nos seus serviços e bases de dados (como irá injetar configurações no container - variáveis de ambiente ou arquivos de configuração), etc.
O Docker, através do DockerFile, padronizou como você empacota sua aplicação e resolve todos esses problemas acima de uma forma simples.
O Dockerfile é um arquivo texto com o passo a passo (instruções e comandos) para a geração de uma imagem que é utilizado no momento de build. Esses passos que existem no Dockerfile, seriam os passos que você executaria manualmente, basicamente o Docker executa uma “receita de bolo” e no final da execução, ele encapsula cada layer gerada para dentro da imagem.
Um exemplo de um arquivo Dockerfile (formato YAML):
O arquivo Docker Compose
Você já entendeu que para distribuir sua aplicação em um container, você precisa criar um DockerFile, porem, é muito comum você querer segregar sua aplicação em diferentes camadas (containers), por exemplo: banco de dados, frontend e backend.
Agora imagine o trabalho que seria executar algumas dezenas de containers manualmente na linha de comando, um por um e todos seus parâmetros necessários, suas configurações de rede, volumes, etc. Para facilitar esse gerenciamento de múltiplos containers existe o Docker Compose.
Docker compose é uma ferramenta para definição e execução de múltiplos containers Docker (também chamado de orquestrador de containers). Com ele é possível configurar todos os parâmetros necessários para executar cada container a partir de um arquivo de definição. Dentro desse arquivo, definimos cada container como serviço, tal como portas expostas, variáveis de ambiente e afins.
Um exemplo de um arquivo docker-compose.yml: