Go + Docker
Dockerizando uma API — Artigo 3
Postman collection, código do artigo, diff com o artigo 2.
Sumário: Desenvolvimento de APIs em Go
Próximo Artigo: Go + Repository + Command-Query Separation
Artigo Anterior: Go + Project Layout — Organizando a Casa
Introdução
Nos dias de hoje é praticamente impossível se falar em desenvolvimento de APIs sem falar de Docker. Essa plataforma permite empacotar, distribuir e executar aplicativos de forma isolada em contêineres. Esses contêineres são unidades leves e portáveis que encapsulam todos os recursos necessários para que um aplicativo seja executado, incluindo o código, bibliotecas, dependências e configurações.
A importância do Docker nos dias de hoje reside no fato de que ele simplifica e acelera o processo de desenvolvimento, implantação e dimensionamento de aplicativos. Ele resolve problemas comuns, como inconsistências de ambiente, dependências de software e conflitos entre aplicativos, permitindo que os desenvolvedores criem aplicativos em um ambiente consistente e previsível. Além disso, o Docker facilita a implantação de aplicativos em diferentes plataformas, desde servidores locais até ambientes de nuvem, proporcionando flexibilidade e portabilidade.
Como software é feito para resolver problemas do mundo real, temos que deixar nossa API disponível para que consiga ser utilizada. Docker é parte crucial para isso nos dias de hoje.
Dockerizando a API
Todo o trabalho deste artigo resume-se a essas simples 12 linhas do Dockerfile, e elas são 12 linhas mesmo utilizando multi-stage build.
Da linha 1 até a linha 5 estamos é realizado o estágio de build do código Go, realizando as operações em sequência:
- Define que irá utilizar a imagem golang:1.20
- Define o diretório de trabalho dentro da imagem como /app
- Move tudo do diretório atual para dentro de /app
- Realiza o build e gera um executável denominado api
Depois da linha 7 até a linha 12 é realizado o estágio de copiar o executável para um imagem scratch e fornecer uma forma dele ser executado de fora. Esse trabalho é feito realizando as seguintes operações:
- Define que irá utilizar a imagem scratch
- Define o diretório de trabalho dentro da imagem como /app
- Copia o executável para dentro da imagem
- Expõe a porta 8081 para fora da imagem
- Define o ponto de entrada como um comando que roda o executável copiado ./api
O multi-stage build é importante para reduzir a quantidade de recursos utilizados em produção. Isso ocorre porque a quantidade de recursos (bibliotecas e ferramentas) necessários para realizar o build do Go é muito maior do que a quantidade de recursos necessários para executar a aplicação. Por exemplo, a imagem golang:1.20 pode facilmente ultrapassar 700 MB, enquanto a imagem scratch (definida pelo Docker como a menor imagem possível para executar uma aplicação) tem um tamanho final de apenas 8 MB, nesse caso.
Buildando e Rodando a API com Docker
O comando utilizado para realizar o build da imagem é o seguinte:
docker build -t product-api .
O trecho “-t product-api” indica que vamos definir uma tag para a imagem de product-api, isso é importante para facilitar a execução da API depois dentro do docker.
O comando utilizado para rodar a API é o seguinte:
docker run -p 8081:8081 product-api
No comando é utilizado a tag definida anteriormente de product-api e é definido a exposição da porta 8081 que será mapeada também para a porta 8081 de dentro do container. E assim é feito a dockerização de uma API Go.
Conclusões e Próximas Etapas
Neste artigo, vimos como dockerizar uma API em Go é um processo simples. No entanto, é importante ressaltar que a simplicidade do exemplo até o momento facilitou esse processo. Em cenários mais complexos, nos quais trabalhamos com arquivos estáticos ou consumimos outros serviços, teríamos que lidar com mais elementos no Dockerfile. Também vale ressaltar, que as imagens docker para serem utilizadas em produção devem passar por um processo de Hardening, para garantir a segurança da aplicação.
No próximo artigo, vamos começar a preparar o código para integrar com um banco de dados de verdade. Para isso vamos falar de dois padrões bem conhecidos no mundo de desenvolvimento de software: repositórios e injeção de dependência.