Salve galera!

Neste artigo, vamos demonstrar a implementação de uma aplicação ASP.NET Core na nuvem, utilizando Docker, Web App on Containers e Azure Container Registry.

Vamos iniciar já com alguma otimização de nossa imagem Docker, mas este assunto vou abordar em mais detalhes em outro artigo. Também vamos utilizar como base o projeto simples em ASP.NET Core, já pronto apenas focarmos na construção da imagem docker.

Introdução

Hoje em dia, temos alguns problemas na publicação de aplicações entre ambientes diferentes. Temos um erro no ambiente X, mas o desenvolvedor diz que “ah, na minha máquina funciona”. Como podemos padronizar isso de forma a evitar esses problemas?

O Docker veio para fornecer um ambiente padronizado para publicação de aplicações, assim como reduzir os custos de escalabilidade da aplicação, isso devido ao seu modelo de “virtualização”, onde utiliza alguns recursos da máquina host.

Ao contrário de VMs comuns onde para cada VM, temos um novo core em uso, um novo SO instalado, ocupando mais espaço e memória. Um container docker utiliza apenas os recursos de processador, memória e disco necessários para sua execução, tendo apenas um SO instalado compartilhando recursos com os diversos containers em execução.

Temos hoje, inclusive, Windows Containers, onde temos imagens menores do Windows, sem interface gráfica, apenas o ambiente necessário para execução de aplicações, rodando em modelo de containers, economizando memória, disco, etc.

A documetação da Microsoft provê, além de informações sobre Windows Containers, uma boa base conceitual sobre containers em geral. Recomendo a leitura:

Sobre contêineres do Windows
Saiba mais sobre contêineres do Windows.

E no Azure, temos recursos que facilitam a utilização de containers Docker para nossas aplicações, como veremos neste artigo. Nosso foco será migrar uma aplicação existente para o Docker e disponibilizá-la no Azure através do Web App for Containers de forma simples e rápida.

Preparando o ambiente na nuvem

Antes de seguirmos com a construção de nossa imagem Docker, precisamos preparar alguns itens no Azure. Vamos criar um repositório privado no Azure para armazenar a imagem Docker que vamos construir. Estando logado no portal do Azure, vamos clicar no menu Create a resource, opção Containers e escolhemos criar um Container Registry.

Criando um novo recurso do tipo Container Registry no Azure.

Na tela de configurações de nosso registry, vamos preencher algumas opções como o nome de nosso registry. Ele será utilizado na URL de acesso e deve ser único.

Selecionamos a assinatura do Azure no campo Subscription.

No campo resource group, podemos criar um novo ou utilizar um existente. Caso esteja criando um novo, forneça um nome único em sua conta para ele.

Eu costumo utilizar a localização como Brazil South, por questoes de ping e acesso mais rápido.

Habilitamos o Admin user, para usar o nome do registro e uma senha gerada automaticamente como credenciais, permitindo o uso diretamente no bash ou prompt a sua escolha.

Escolhemos o SKU como Basic, sendo o plano inicial e de menor custo.

Selecione a opção Pin to Dashboard, para facilitar o acesso ao Container Registry. Estando tudo ok com a validação, clicamos em Create e aguardamos a criação do recurso.

Configurações do Container Registry a ser criado.

Após isto, anote a url que foi gerada para nosso Container Registry, pois vamos utilizá-la na hora de criar a tag da imagem Docker e publicar a mesma. No meu caso ficou como gbbigardi.azurecr.io.

Construindo a imagem de nossa aplicação

Vamos clonar a aplicação do github para podermos trabalhar nela, com o comando a seguir.

gustavobigardi/demo-aspnet-core-docker
Repositório demo para artigo sobre ASP.NET Core + Docker - gustavobigardi/demo-aspnet-core-docker

Em seguida, vamos abrir nossa aplicação no Visual Studio Code. Pode ser utilizado o Visual Studio no Windows ou mesmo o Visual Studio for Mac. Utilizo o VS Code pois gosto mais dele para trabalhar no Mac, mas fique a vontade de utilizar o editor a sua escolha.

Projeto carregado no VS Code, com o arquivo Dockerfile que vamos criar.

Dentro do diretório da solution, vamos criar um novo arquivo chamado Dockerfile. Neste arquivo iremos adicionar as instruções para construção de nossa imagem. Vamos adicionar o conteúdo abaixo no arquivo.

Vamos analisar o conteúdo deste arquivo e entender o que estamos fazendo.

Na primeira linha, estamos definindo que vamos utilizar como base, a imagem do .NET Core 2.1, sendo a imagem de SDK, com as ferramentas para build e publicação da aplicação. Nos demais comandos, fazemos restore das dependências e o build / publish de nosso projeto.

Se finalizássemos por aí, teríamos uma imagem pronta para utilizar, mas por ser a imagem de SDK, temos muitas ferramentas para build, cache de depenências e outros arquivos que tornariam nossa imagem muito grande. Não precisamos de tudo isso para executar nossa aplicação na nuvem.

Para resolver esse problema, vamos utilizar um recurso do Docker chamado multi-stage build. Na linha 14 de nosso arquivo, adicionamos um novo comando FROM, onde indicamos a imagem de runtime do ASP.NET Core, que contém apenas o necessário para execução da aplicação. A partir deste ponto, a imagem do SDK não será mais utilizada na imagem final que será produzida no build. Por conta disso, qualquer arquivo que precisamos da imagem anterior, deve ser copiado. Fazemos isso nas linhas 15 e 16, onde copiamos apenas o conteúdo gerado pelo publish do CLI do .NET Core. Note que indicamos a origem dos arquivos com o parâmetro from, onde usamos o alias dado para a imagem de SDK na primeira linha do arquivo.

E finalmente na linha 17, indicamos que o CLI do .NET Core deve execuar nossa aplicação, informando qual a DLL principal dela.

Vamos construir nossa imagem, utilizando o comando abaixo.

docker build -t demoaspnetdocker .

Com isso temos uma imagem otimizada para poder publicar na nuvem. Veja abaixo a diferença de tamanho do build, utilizando apenas a imagem de SDK e o multi-stage build com a imagem de runtime, respectivamente.

Geradas duas imagens, com SDK e Runtime, nota-se a diferença de 1.75GB para 258MB, respectivamente.

A imagem de runtime ficou com 271MB, contra 1.95GB da imagem de SDK. Ao utilizar o segundo FROM indicando a imagem de runtime, ela foi utilizada como base para a imagem final de nossa aplicação, contendo apenas o necessário, nada além disso e economizando espaço na nuvem, afinal, pagamos por espaço utilizado.


Versionando e enviando nossa imagem para a nuvem

Antes de enviar nossa imagem para a nuvem, precisamos criar uma versão, ou tag, da mesma. Para isto, após realizar o build da imagem, que agora encontra-se no repositório local do Docker, vamos gerar uma tag dela, com o comando abaixo.

docker tag demoaspnetdocker:latest gbbigardi.azurecr.io/demoaspnetdocker

Após gerar a tag, vamos subir a mesma para nosso ambiente no Azure, com o comando abaixo.

docker login gbbigardi.azurecr.io -u USUÁRIO -p SENHA

O usuário e senha, podem ser obtidos no Container Registry que criamos no Azure, acessando o item Access Keys, e copiando o Username e qualquer um dos valores dos campos Password ou Password2, como na imagem abaixo.

Configurações de login de nosso Container Registry.

Em seguida, vamos subir a tag criada para o Container Registry, com o comando abaixo.

docker push gbbigardi.azurecr.io/demoaspnetdocker

Lembrando que, em todos os comandos, sempre troque a URL com a do Container Registry que criou em sua conta do Azure. Caso contrário terá erro pois este Registry que usei no artigo já foi removido.

Ao acessar o Container Registry no Azure e navegarmos na opção Repositories, vemos que um novo repository para nossa aplicação foi criado, e clicando nele, podemos ver as versões disponíveis. Até o momento, temos apenas a versão latest.

Imagem docker carregada no Registro, com a tag que criamos, latest.

Publicando nossa aplicação com nosso Container Registry

Agora que temos a imagem de nossa aplicação pronta no Container Registry, vamos criar um recurso do tipo Web App para publicar a aplicação. Mas vamos utilizar o Web App for Containers, que já é um recurso preparado para executar containers Docker. Criaremos um novo recurso através do menu Create a resource, opção Containers, item Web App for Containers.

Menu do portal Azure com a opção para criar um Web App usando containers Docker.

Na tela de configurações de nosso Web App, vamos preencher os campos conforme a imagem abaixo.

Configurações básicas do Web App.

App name: Vamos dar um nome a nosso Web App. Ele deve ser único, pois será utilizado na URL padrão com sufixo .azurewebsites.net.

Subscription: Identificamos qual assinatura do Azure vamos utilizar para este recurso.

Resource Group: Como já criamos um grupo junto com nosso Container Registry, vamos selecionar a opção Use existing, e escolher o grupo criado no drop down que será exibido.

App Service plan/Location: Nesta opção, podemos escolher qual a camada de serviços e localização que vamos utilizar. Cada opção gera um custo diferente.

Em meu caso, criei um novo Service Plan através da opção Create New, e escolhi a opção B1 Basic em Pricing tier, e localização como Brazil South. Devemos dar um nome a nosso plano, sendo que não pode conter espaços.

Criando um novo service plan no Brasil para nosso Web App, utilizando a camada B1 Basic.

Após isto, precisamos configurar o container que será utilizado em nosso Web App.

Tela de configuração do Web App para selecionarmos a imagem Docker criada.

Podemos utilizar um container simples, como no caso deste artigo, ou mesmo uma composição de vários containers, através do Docker Composer, ou mesmo utilizar o Kubernetes para isto. Estas duas últimas opções, apesar de bem estáveis na minha opnião, ainda são recursos em fase de Preview no Azure.

Vamos selecionar a aba Single Container. Nela podemos utilizar um container armazenado em um Container Registry privado do Azure, no DockerHub ou um Container Registry privado que esteja em outra plataforma, como AWS, Google ou mesmo um servidor privado.

Vamos selecionar a opção Azure Container Registry. Com isso teremos as seguintes opções para configurar.

Registry: Serão apresentados todos os recursos de Container Registry criados nesta assinatura do Azure. Vamos escolher o que criamos, no meu caso, o gbbigardi.

Image: Vamos escolher qual imagem docker vamos utilizar em nosso container. Como nosso registry tem apenas uma imagem, vamos selecionar ela, sendo a demoaspnetdocker.

Tag: Nesta opção, podemos escolher uma versão, ou tag, específica de nossa imagem. Como temos apenas uma versão, e quero que sempre que uma nova versão seja publicada o Web App seja atualizado, vamos escolher a tag latest.

Com tudo configurado, salvamos e na tela de configurações do Web App, marcamos a opção Pin to dashboard e clicamos em Create. Agora é só aguardar o Azure criar o Web App e subir o container Docker com a imagem de nossa aplicação.

Assim que estiver finalizado, podemos navegar para a url de nossa aplicação, em meu caso, demoblazor.azurewebsites.net, e ver nossa aplicação em execução, como nas imagens a seguir.

Aplicação em execução no Azure, utilizando o Web App for Containers com Docker.

Concluindo

O uso de serviços como Azure Container Registry e Azure Web App on Linux não está disponívei apenas para ASP.NET Core e outras tecnologias da Microsoft. Podemos utilizar para PHP, Java, GoLang, NodeJS, Python…

Tudo isso é possível graças ao Docker, onde a maioria das tecnologias que tem suporte a Linux podem ser executadas sem a necessidade de adaptações.

Em meus próximos artigos desta série, vamos focar mais no Docker e melhores práticas em seu uso, assim como também iremos adicionar a construção e publicação de nossas imagens Docker ao nosso processo de CI / CD através do Visual Studio Team Services.

Espero que tenham gostado, não deixem de comentar e deixar seu feedback.

Até a próxima!