Docker: Desmistificando a conteinerização de aplicações

Renicius Pagotto Fostaini
10 min readApr 8, 2020

--

E ai pessoal, no tema de hoje vamos aprender um pouco sobre docker e como ele vem ajudando os profissionais de TI na construção de aplicações.

Porém, antes de nos aprofundarmos no mundo Docker, vamos entender alguns outros conceitos essenciais.

Virtualização

A virtualização cria um ambiente de computação simulado ou virtual em vez de um ambiente físico. A virtualização costuma incluir versões de hardware, sistemas operacionais, dispositivos de armazenamento e outros, todas geradas por computador. Isso permite que as organizações particionem um único computador físico ou servidor em diversas máquinas virtuais. Cada máquina virtual pode interagir de forma independente e executar diferentes sistemas operacionais ou aplicativos ao mesmo tempo em que todas compartilham os recursos de um único computador host.

Ao criar múltiplos recursos de um único computador ou servidor, a virtualização aprimora a escalabilidade e as cargas de trabalho enquanto utiliza menos servidores, gasta menos energia e requer menos custos de infraestrutura e manutenção

Na imagem acima, temos a camada mais inferior composta pelo hardware, em seguida temos a camada responsável pela virtualização, um pouco mais acima temos vários sistemas operacionais virtualizados e suas respectivas aplicações.

Vale ressaltar, quando virtualizamos um Sistema Operacional, ele é emulado por completo, ficando disponível todos os seus recursos.

Alguns dos beneficios da virtualização é permitir o compartilhamento dos recurso de hardware de um equipamento, possibilitando a criação de diversos ambientes virtuais e isolados.

Agora que entendemos um pouco sobre virtualização e sua importância, vamos adentrar no mundo Docker.

Docker — Conceitos Básicos

Vamos agora falar de dois conceitos de extrema importância dentro do mundo docker.

Imagem: Representa a image base do Sistema Operacional que utilizaremos para a criação dos container e que contêm as dependencias necessárias para a execução da aplicação.

Container: É construído com base em uma imagem, sendo executado em um ambiente isolado dentro no nosso servidor.

Docker Hub

O Docker Hub, é um serviço que permite ao desenvolvedor criar repositórios de imagens docker semelhante ao GitHub. Ou seja, é possível enviar, armazenar e utilizar imagens docker públicas e privadas.

Para mais informações: https://hub.docker.com

Docker

Docker é uma plataforma Open Source escrita em Go, uma linguagem de alto desempenho desenvolvida pela Google, que facilita a criação e administração de ambientes isolados, ou seja, ajuda desenvolvedores e administradores em criar, testar e implantar aplicações.

Com o Docker, podemos colocar as aplicações em um container que possui todos os recursos necessários para que ela funcione, nos ajudandando a eliminar aquele velho problema onde o desenvolvedor cria uma aplicação em sua máquina, ou em uma máquina virtual, mas no momento da publicação ela não se comporta da maneira esperada.

Na imagem abaixo, temos um comparativo entre o funcionamento da virtualização e do docker.

Conforme podemos ver na imagem acima, possuímos em ambas as tecnologias, tanto a camada de hardware (Disco Rigido, Memória RAM, etc) como o Sistema Operacional que gerencia esse hardware, porém, temos algumas diferenças nas camadas superiores.

Perceba que quando utilizamos a técnica da virtualização, os Sistemas Operacionais são virtualizados por completo, ou seja, todas as funcionalidades dele estarão disponíveis para uso. No entanto, ao usar containers, não realizamos nenhum tipo de virtualização e apenas aproveitamos os recursos fornecidos pelo Kernel do Sistema Operacional original do equipamento, evitando assim virtualizar recursos desnecessários para rodar nossa aplicação.

Em outras palavras, não precisamos de um Sistema Operacional completo, que fornece UI e vários outros recursos que são desnecessários para rodar uma aplicação. Precisamos apenas de recursos essenciais do Kernel para que nossa aplicação seja compilada e executada.

Containers

É um ambiente isolado, seguro e protegido em que a aplicação é executada e possui todas as dependências necessárias.

Para contextualizar melhor, imagine um navio cargueiro com vários containers dentro, caso um container se danifique não afetará os outros, pois estão isolados e protegidos.

A imagem abaixo exemplifica melhor o seu funcionamento.

Por que utilizar?

Vamos abordar agora algumas vantagens muito interessantes quando adotamos o docker:

Ambientes Semelhantes

A transformação da aplicação em uma imagem docker permite que ela seja instanciada como container em diferentes ambientes. Essa característica garante a sua utilização, por exemplo, tanto no notebook do desenvolvedor quando no servidor de produção.

Aplicação Empacotada

As imagens do docker possibilitam o empacotamento da aplicação e as suas dependências, o que simplifica o processo de distribuição por não ser exigida ampla documentação sobre a configuração da infraestrutura com a finalidade de execução. Para isso, basta disponibilizar o repositório e permitir o acesso para o usuário.

Modularidade

Permite ao desevolvedor realize manutenções em determinadas funcionalidades que estão isoladas evitando assim de parar a aplicação por completo.​

Padronização e Replicação

As imagens do docker são construídas por meio de arquivos de definição, o que assegura o seguimento de um determinado padrão e eleva a confiança na replicação. Dessa maneira, fica muito mais viável escalar a estrutura.

Versões do Docker

Atualmente, o Docker é divido em dois produtos, Community Edition (CE) e Enterprise Edition (EE).

Como os nomes sugerem, o Community é gratuito e voltado a comunidade, enquanto o Enterprise é recomendado para uso empresarial.

Criando uma conta

A primeira coisa que precisamos fazer é criar uma conta no Docker Hub, ela é necessária para utilizar o Docker em nossa máquina assim como gerenciar nossas imagens.

Instalação

O Docker possui uma versão desktop que em sua instalação, adiciona também a CLI. Para fazer o download, clique aqui.

Após finalizar a instalação, o Docker começa a ser executado em sua máquina. Podemos ver seu status e acessar sua funcionalidades por meio do ícone que fica disponível toda vez que o Docker estiver em execução.

Comandos

Vamos agora apresentar alguns comandos essencias e mais utilizados.

OBS: Recomendo a utilização do Powershell ou Windows Terminal

docker --version

É responsável por exibir as informações da versão do docker instalado em seu equipamento.

docker info

Mostra todas as informações do Docker, como quantidade de containers criados, quantidade de imagens, porcentagem de CPU utilizada, entre outras informaçãoes.

docker images

É responsável por listar todas as imagens baixadas localmente, apresentando as seguintes informações de cada container:

  • REPOSITORY: Nome da imagem
  • TAG: Versão definida da imagem
  • IMAGE ID: Id que o Docker atribuiu a imagem
  • CREATED: Data de criação da imagem
  • SIZE: Tamanho em disco dessa imagem

docker pull nome-imagem

Comando utilizado para baixar localmente uma imagem do docker hub ou outro servidor de imagens.

Ex: docker pull ubuntu

Esse comando definido acima baixará uma imagem ubuntu no docker pronta para ser executada em um container.

docker push user/nome-imagem

Responsável por fazer um upload de imagem no seu repositório

docker rmi imagem-id

Responsável por excluir localmente uma imagem com base em seu identificador (ID)

docker run [OPTIONS] IMAGE [COMMAND][ARG...]

Com esse comando, criamos um container baseado em uma imagem e conseguimos definir algumas configurações para sua execução passando alguns argumentos.

docker run -d nome-imagem

É utilizado para criar um container em que seu processo será executado em background, com isso o nosso terminal não fica travado no contexto do processo do container.

docker run --name nome-desejado nome-imagem

Utilizado para criar um container onde é possibilitado definir um nome para o mesmo.

docker run -p [porta-do-seu-windows]:[porta-container][nome-imagem]

Com esse comando, criamos um container onde conseguimos especificar a porta em que ele será executado.

Em outras palavras, quando definimos 8000:80 como porta, significa que o processo do seu container será executado na porta 8000 do seu SO e toda requisição que chegar a essa porta, será redirecionada para a porta 80 que é a porta em que nossa aplicação esta sendo executada dentro o nosso container.

docker ps

Comando responsável por listar todos os containers em execução e fornecendo algumas informações importantes como:

  • CONTAINER ID: Identificador atribuído ao container em sua criação
  • IMAGE: Imagem base utilizada para a criação do container
  • COMMAND: Comando utilizado para a execução do container
  • CREATED: Data de criação do container
  • STATUS: Informa se o container esta parado ou em execução
  • PORTS: Portas atribuidas entre o SO e o container
  • NAMES: Nome atrbuído ao container

docker ps -a

Comando responsável por listar todos os containers parados e em execução e exibe as mesmas informações explicadas no comando acima.

docker start container-id

Comando responsável por inicializar a execução de um container onde precisamos especificar o container que desejamos executar através do seu identificador.

docker stop container-id

Comando responsável por parar a execução de um determinado container onde precisamos passar o identificador do container que desejamos pausar.

docker rm container-id

Comando responsável por deletar um container, onde devemos passar o identificador do container desejado.

Lembrando que o docker não possibilita deletar um container em execução, para isso precisamos parar sua execução e depois sim efetuar sua exclusão ou ainda, podemos utilizar a flag -f ao final do comando onde força a exclusão do container mesmo estando em execução.

Ex: docker rm container-id -f

docker stats container-id

Comando responsável por exibir algumas informações como:

  • CONTAINER ID: Identificador do container
  • CPU %: Porcentagem do uso de CPU
  • MEM USAGE / LIMIT: Memória utilizada/Limite de memória definido para o container
  • MEM: Porcentagem do uso de memória
  • NET I/O: Entrada e saída de banda
  • BLOCK I/O: Outros processos de I/O

docker inspect container-id

Comando responsável por exibir uma série de informações sobre o container. Para mais informações, aqui esta a documentação completa.

Dockerfile

O Dockerfile nada mais é do que um arquivo de instruções escrito com uma sintaxe simples no formato “YML” ou “YAML” com o intuito de criar nossas próprias imagens. Em outras palavras, ele serve como a receita para construir um container, permitindo definir um ambiente personalizado.

Multi-Stage Builds

É um método de organizar um arquivo Dockerfile para minimizar o tamanho do container final, melhorar o desempenho do tempo de execução, permitir uma melhor organização dos comandos e arquivos do Docker e fornecer um método padronizado de execução de ações de compilação.

O multi-stage build (compilação de vários estágios) é feita criando seções diferentes de um Dockerfile, cada uma referenciando uma imagem base diferente. Isso permite que cada estágio, execute sua responsabilidade atribuída, copiando arquivos entre containeres ou executando diferentes pipelines.

Entendendo o dockerfile

Vamos entender um pouco mais da estrutura do dockerfile agora e para isso vou utilizar o próprio template padrão que a Microsoft disponibiliza ao criar um projeto com o Docker.

Link do projeto: https://github.com/reniciuspagotto/blazor-docker-sample

Após o download, procure pelo arquivo Dockerfile na raiz do projeto e abra com o editor de texto de sua preferência.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

Nesse primeiro comando, estamos especificando a imagem que vamos utilizar para este primeiro estagio da nossa build e para isso utilizamos a palavra FROM seguida do nome da imagem.

E perceba que no final temos o AS, que significa que podemos nomear cada estágio de nossa build e recuperar informações desse estágio em um momento que se fizer necessário.

OBS: Cada FROM executado do dockerfile é um novo estágio da construção do nosso container totalmente isolado dos estágios anteriores.

WORKDIR /app

Especificamos o diretório dentro no nosso container que vamos usar para o contexto da aplicação, ou seja, onde ficará o código fonte.

EXPOSE 80

Nessa instrução, definimos a porta em que o container que contêm nossa aplicação será executado.

Aqui finalizamos nossa primeira etapa do build, onde escolhemos a imagem que será utilizada para rodar a aplicação, o diretório em que estará todos os binários e a porta que será executada.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build

Nessa segunda etapa do nosso build é onde realizamos o build da aplicação e para isso, definimos novamente uma imagem base que contém o sdk do .NET Core instalado e pronto para executar nosso build.

WORKDIR /src

Especificamos o diretório dentro no nosso container que vamos usar para o contexto da aplicação, ou seja, onde ficará o código fonte.

COPY [“BlazorAppExampleDocker/BlazorAppExampleDocker.csproj”, “BlazorAppExampleDocker/”]

Nessa etapa, toda a aplicação é copiada para nosso container no caminho especificado “BlazorAppExampleDocker”.

OBS: Lembre que estamos trabalhando no diretório src, portanto o caminho que nossa aplicação foi copiada é src/BlazorAppExampleDocker

RUN dotnet restore “BlazorAppExampleDocker/BlazorAppExampleDocker.csproj”

Nessa etapa estamos restaurando todas as dependências necessárias para realizar o build de nossa aplicação.

COPY . .

Nessa instrução estamos copiando os arquivos da raiz (‘.’) da nossa aplicação para a raiz (‘.’) da imagem.

WORKDIR “/src/BlazorAppExampleDocker”

Especificamos novamente o diretório que representa o contexto no nosso container.

RUN dotnet build “BlazorAppExampleDocker.csproj” -c Release -o /app/build

Nessa instrução realizamos o build da nossa aplicação, note que passamos alguns argumentos em noss comando:

  • -c : Define a configuração da compilação. O padrão para a maioria dos projetos é Debug, mas você pode substituir as definições de configuração da compilação no seu projeto. Nesse caso definimos o modo Release
  • -o: Diretório no qual colocar os binários criados. Nesse caso definimos o caminho /app/build

Nesse ponto finalizamos a segundo estágio do build da nossa imagem

FROM build AS publish

Nesse novo estágio iniciamos o processo de publicar nossa aplicação e para isso utilizamos o estágio anterior para conseguir acessar nossos binários que foram compilados.

RUN dotnet publish “BlazorAppExampleDocker.csproj” -c Release -o /app/publish

Nessa instrução realizamos a publicação da nossa aplicação em modo Release e especificamos o diretório em que as DLLs serão copiadas.

Finalizamos o terceiro estágio do nosso build que é responsável por apenas publicar nossa aplicação.

FROM base AS final

Aqui iniciamos nosso estágio final e para isso utilizamos o primeiro estágio onde configuramos a imagem que será utilizado para execução da aplicação.

WORKDIR /app

Especificamos o diretório dentro no nosso container que vamos usar para o contexto da aplicação, ou seja, onde ficará o código fonte.

COPY - - from=publish /app/publish .

Nessa instrução, vamos copiar toda a aplicação que realizamos a publicação no estágio anterior e copiar para o diretório em que estamos trabalhamos e que foi definido na instrução anterior.

ENTRYPOINT [“dotnet”, “BlazorAppExampleDocker.dll”]

Nessa instrução definimos o comando e o binário (DLL) que utilizaremos para rodar nossa aplicação.

Com isso, finalizamos nosso último estágio e agora temos uma imagem com nossa aplicação embutida nela e pronta para ser executada.

Referências

--

--