Programe em uma nova linguagem com mínimo setup
No tempo antes do tempo
Quem nunca colocou a mão em caixas de disquetes ou CDs de instalação para deixar a máquina pronta para escrever a primeira linha de código não vai entender o quanto a Internet e a disponibilidade de boas conexões facilitam a vida e permitem ter os profissionais trabalhando no código o mais rápido possível ao invés de perder dias de trabalho até ter as suas ferramentas prontas e afiadas.
Dias atrás tive a necessidade de fazer alguns testes com Java para atender a um cliente. O meu dia a dia é .NET e eu não tenho o software necessário na minha máquina para desenvolver em Java e, além disso eu não tinha muito tempo para entrar na documentação e descobrir como fazer o setup. Não sei se é simples ou complexo, só não queria ter de lidar com mais essa variável.
Eu precisava de algo mais ágil.
Entram os containers
De todas as tecnologias que se popularizaram recentemente a de containers talvez seja uma das mais interessantes não apenas pela possibilidade de distribuir e executar aplicações com suas dependências de forma isolada e reproduzível, mas também por permitir empacotar e distribuir tempo. Ao usar containers eu faço uso do tempo investido em configurar e deixar pronto para uso o software que roda nesse container.
Ficou claro que para eu ganhar tempo na instalação do software o caminho indicado seria reutilizar o tempo de alguém através de um container pronto para compilar e executar código Java.
Uma máquina virtual pronta também seria uma opção, porém containers são muito mais leves para baixar e executar além de possuírem um modelo de repositórios (ex. Docker Hub) com centenas de containers que podem ser utilizados como base.
Entra o Visual Studio Code
O Visual Studio Code tem sido uma das minhas principais ferramentas de trabalho para vários cenários, como escrever este artigo por exemplo. Além do excelente desempenho, suporte a múltiplas linguagens, múltiplas plataformas e facilidade de uso, o Visual Studio Code tem um rico ecossistema de extensões que permitem personalizar o editor para as mais diversas tarefas.
Quando um mais um é sempre mais que dois
A Microsoft tem um conjunto de extensões que permitem edição remota com o Visual Studio Code: Remote - Containers, Remote - SSH e Remote - WSL. Essas extensões podem ser instaladas individualmente ou através do pacote Remote Development.
Elas trabalham executando um servidor do Visual Studio Code no computador remoto que é controlado pelo Visual Studio Code na máquina do programador como se estivesse trabalhando localmente. As extensões oferecem acesso não apenas ao sistema de arquivos, mas também ao shell permitindo compilar, executar e depurar código.
Essa soma de containers, Visual Studio Code e extensões é o que nos permite configurar ambientes prontos para desenvolver em minutos ou até mesmo segundos ao invés de horas que seriam gastas com setup de linguagens e ferramentas.
Parece bom. O que eu preciso?
Caso ainda não tenha instalado:
- Docker Desktop, instala as ferramentas e o runtime de containers
- Visual Studio Code, ambiente integrado de desenvolvimento
- Remote - Containers, extensão para conectar o Visual Studio Code aos containers
O Docker Desktop prepara a máquina com tudo o que é necessário para desenvolver e executar aplicações baseadas em containers, basta baixar e instalar.
Instale o Visual Studio Code e procure na barra do lado esquerdo por Extensions (Ctrl+Shift+X) e na caixa de pesquisa busque por Remote. Instale a extensão Remote - Containers ou o pacote completo de extensões para desenvolvimento remoto (Remote Development).
Isso completa o setup e agora começa a diversão.
No Visual Studio Code temos a paleta de comandos (Command Palette) que pode ser acessada pela combinação de teclas Ctrl+Shift+P ou F1. Nessa paleta conseguimos acessar todos os comandos disponíveis no Visual Studio Code e extensões instaladas, nem todos os comandos têm teclas de atalho ou itens de menu associados.
Tecle Ctrl+Shift+P, digite remote containers e veja os comandos da extensão:
Para a nossa primeira experiência selecione Remote-Containers: Try a Development Container Sample… e em seguida selecione uma das linguagens disponíveis no meu caso eu selecionei Java que era a minha necessidade no momento, escolha à vontade:
Aguarde por um momento enquanto o container é baixado e iniciado, comigo foi menos de 1 minuto, se quiser clique em logs para acompanhar o processo e voilá (pronuncia-se vu-a-lá, é francês eu acho), temos um ambiente pronto para programar em Java e com um projeto de exemplo criado, Maven e extensões do Visual Studio Code para Java instaladas. Tecle Ctrl+F5 e veja a mensagem “Hello Remote World!” no terminal.
A partir desse ponto temos um ambiente pronto para desenvolver em Java e podemos criar ou clonar um projeto e seguir desenvolvendo.
Note no canto inferior esquerdo do Visual Studio Code o indicador de conexão remota com o tipo de conexão e o nome da configuração utilizada:
Um click nesse indicador abre a paleta de comandos com as opções da extensão. Note que a última opção é para encerrar a conexão remota.
Um ponto interessante é que as edições feitas em containers também aparecem na lista de recentes do Visual Studio Code permitindo retomar rapidamente o trabalho e é possível conectar a um container em execução usando o comando Remote-Containers: Attach to Running Container….
Clonar um repositório dentro do container
Quando existir um repositório no qual se deseja trabalhar a opção mais rápida é clonar o repositório dentro de um container. Na paleta de comandos procure pela opção Remote-Containers: Clone Repository in Container Volume….
A extensão permite buscar um repositório no GitHub ou informar a URL de um repositório Git qualquer, em seguida será solicitado que se escolha uma configuração e se deseja adicionar outras funcionalidades ao container.
O repositório será clonado em um volume e aberto no Visual Studio Code após o container ser iniciado.
Usar uma pasta local
Digamos que o código não veio de um repositório, mas está em uma pasta local no computador, nesse caso na paleta de comandos basta selecionar Remote-Containers: Open Folder in Container… o processo é bastante similar ao de clonar um repositório com a diferença de que será solicitado que se informe uma pasta local e essa pasta será mapeada como volume no container.
Configurações
Os containers de desenvolvimento podem ser configurados através do arquivo devcontainer.json colocado na pasta .devcontainer.
O arquivo de configuração é gerado automaticamente a partir da seleção de opções ao clonar um repositório ou abrir pasta local e pode ser criado através do comando Remote-Containers: Add Development Container Configuration Files…. Os comentários trazem boas informações sobre o que pode ser configurado através desse arquivo.
Esse é um exemplo do arquivo de configuração:
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/Visual Studio Code-dev-containers/tree/v0.241.1/containers/dotnet
{
"name": "C# (.NET)",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update 'VARIANT' to pick a .NET Core version: 3.1, 6.0
// Append -bullseye or -focal to pin to an OS version.
"VARIANT": "6.0-bullseye",
// Options
"NODE_VERSION": "lts/*"
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to Visual Studio Code.
"Visual Studio Code": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-dotnettools.csharp"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [5000, 5001],
// [Optional] To reuse of your local HTTPS dev cert:
//
// 1. Export it locally using this command:
// * Windows PowerShell:
// dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere"
// * macOS/Linux terminal:
// dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere"
//
// 2. Uncomment these 'remoteEnv' lines:
// "remoteEnv": {
// "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere",
// "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/Visual Studio Code/.aspnet/https/aspnetapp.pfx",
// },
//
// 3. Do one of the following depending on your scenario:
// * When using GitHub Codespaces and/or Remote - Containers:
// 1. Start the container
// 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer
// 3. Open a terminal in Visual Studio Code and run "mkdir -p /home/Visual Studio Code/.aspnet/https && mv aspnetapp.pfx /home/Visual Studio Code/.aspnet/https"
//
// * If only using Remote - Containers with a local container, uncomment this line instead:
// "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/Visual Studio Code/.aspnet/https,type=bind" ],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "dotnet restore",
// Comment out to connect as root instead. More info: https://aka.ms/Visual Studio Code-remote/containers/non-root.
"remoteUser": "Visual Studio Code"
}
Note no topo do arquivo que é possível definir um nome para a configuração (aparecerá no canto inferior esquerdo do Visual Studio Code) e as instruções de build que apontam para um arquivo Dockerfile também na pasta .devcontainer.
O arquivo Dockerfile configura a imagem que será utilizada pelo container e demais instruções para construir a imagem local, note que a variável VARIANT definida no arquivo devcontainer.json é usada no label da imagem:
# See here for image contents: https://github.com/microsoft/Visual Studio Code-dev-containers/tree/v0.241.1/containers/dotnet/.devcontainer/base.Dockerfile
# [Choice] .NET version: 6.0, 3.1, 6.0-bullseye, 3.1-bullseye, 6.0-focal, 3.1-focal
ARG VARIANT="6.0-bullseye-slim"
FROM mcr.microsoft.com/Visual Studio Code/devcontainers/dotnet:0-${VARIANT}
# [Choice] Node.js version: none, lts/*, 18, 16, 14
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su Visual Studio Code -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su Visual Studio Code -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
Para utilizar qualquer outra imagem basta alterar o FROM nesse arquivo. Por exemplo se quiser utilizar uma imagem do .NET 7.0 para experimentar as novas funcionalidades basta alterar para:
...
FROM mcr.microsoft.com/dotnet/nightly/aspnet:7.0
...
Redirecionamento de portas
Containers são ambientes isolados e portanto para acessar aplicações dentro de containers usando um browser ou outra aplicação é necessário redirecionar as portas da aplicação.
Ao executar aplicações que escutam portas TCP, exemplo aplicações Web, as portas são mapeadas automaticamente de forma temporária ou mapeadas de forma permanente através da opção forwardPorts no arquivo devcontainer.json:
...
"forwardPorts": [5000, 5001],
...
Portas redirecionadas podem ser acessadas no browser usando-se localhost e a porta.
Extensões
As nossas extensões preferidas no Visual Studio Code não são mapeadas por padrão nos containers de desenvolvimento e podem ser instaladas manualmente usando o menu Extensions ou através da opção extensions no arquivo devcontainer.json:
...
"extensions": [
"ms-dotnettools.csharp"
]
...
Entra a nuvem
O GitHub leva esse conceito ainda mais longe através do serviço Codespaces que oferece a possibilidade de codificar usando o Visual Studio Code com containers hospedados em máquinas virtuais na nuvem e que podem inclusive serem acessadas através de um browser.
Os Codespaces são acessados e gerenciados diretamente a partir da interface do GitHub e oferecem máquinas virtuais com 2,4,8,16 ou 32 núcleos e 4,8,16,32 ou 64GB de RAM com inicialização rápida. São uma solução ideal para correção rápida de problemas quando não estamos com a nossa máquina a mão, quando se precisa rapidamente validar um pull request ou até mesmo para situações em que necessitamos de mais poder computacional.
Em resumo
O desenvolvimento usando containers pode ser extremamente produtivo pela possibilidade de oferecer ambientes prontos para uso que combinados com o GIT permitem as pessoas atuar quase que imediatamente nos projetos poupando horas de esforço de setup.
Usar ambientes pré-configurados aumenta a velocidade do desenvolvimento ao permitir que todos os programadores trabalhem em ambientes padronizados evitando que se perca tempo depurando erros decorrentes de dependências configuradas incorretamente.
E tão facilmente quanto são provisionados podem ser descartados após cumprirem a sua função.
Container é tempo empacotado.