Como criar um cluster de Raspberry Pi 4 com Docker Swarm

O Raspberry Pi 4 é um microcontrolador com uma boa capacidade de processamento. Possui uma CPU ARM v8 64 bits (Quad core Cortex-A72), uma interface Gigabit Ethernet, uma porta USB 3.0 e duas portas HDMI. Ele pode ser configurado com até 8 GB de memória. Com armazenamento microSD é possível instalar sistemas operacionais Linux, tal como Ubuntu 18 Core ou Ubuntu 20 e softwares de servidores.

Podemos instalar o Docker neste hardware e criar um cluster com 2 ou mais Raspberry Pi para executar cargas de trabalho de análise de dados em larga escala. A vantagem desta configuração é o baixo custo do hardware. Um cartão microSD pode fornecer desempenho de leitura de 80 MB/s e com a interface gigabit ligada num switch adequado podemos obter 100 MB/s de taxa de transferência. Considerando um custo de 100 dólares por kit Raspberry Pi 4 de 8GB e 30 dólares pelo arranjo mostrado na figura abaixo, temos um custo aproximado de 780 dólares por um cluster de 6 computadores totalizando uma memória de 48 GB e 192 GB de armazenamento de dados. 

Isto é suficiente para várias aplicações e provas de conceito de  IoT, AI, WEB, micro-serviços rodando de forma distribuída sobre Docker numa solução escalável horizontalmente.

Vale ressaltar que o próprio Raspberry Pi possui interface GPIO e portas para câmera, audio stéreo e vídeo composto, permitindo aquisição e processamentos diversos, incluindo processamento de imagens usando aprendizado de máquina. 

Instalação do Sistema Operacional

Na página de Download https://www.raspberrypi.org/downloads/ você pode baixar o Raspberry Pi Imager que está disponível para Ubuntu, MacOS e Windows. Este programa, após instalado no seu computador, oferecerá um diálogo onde você pode escolher [1] a versão do Sistema Operacional que quer copiar para o cartão microSD. Escolha [2] o microSD onde será gravada a imagem do Sistema Operacional. Com estas duas informações preenchidas o botão [3] ficará habilitado para iniciar a gravação. 

É importante escolher uma versão de 64 bits tal como Ubuntu Core 18

As instruções para instalar o Ubuntu Core 18 podem ser vistas neste link:

https://ubuntu.com/download/raspberry-pi-core. Veja abaixo um resumo 

Antes de fazer o boot no seu Raspberry Pi com o microSD gerado faça o seguinte no seu computador usado para gerar a imagem:

1. Crie um par de chaves RSA usando o comando ssh-keygen. No meu caso usei opção -C para adicionar um comentário para facilitar o gerenciamento de chaves SSH na minha conta do ubuntu.com. Veja abaixo o comando executado:

ssh-keygen -t rsa -b 4096 -C "id_rsa_r-pi-01 AT admin@automacao.info"

Quando solicitado informe o nome do arquivo, como por exemplo: ~/.ssh/id_rsa_r-pi-01. É importante escolher um nome adequado para não sobrescrever chaves já existentes em ~/.ssh.

2. Copie a chave pública gerada para a área de transferência do computador. No caso do MacOS você pode usar pbcopy como mostrado abaixo: 

pbcopy < ~/.ssh/id_rsa_r-pi-01.pub

3. Crie uma conta em https://ubuntu.com e depois adicione a chave pública gerada na etapa 1 acima, na sua lista de chaves. Use a URL abaixo e faça a cópia à partir da área de transferência (Copy/Paste) :  

https://login.ubuntu.com/ssh-keys

OBS: Esta chave pública será copiada para o seu Raspberry Pi para a área de usuário no arquivo ~/.ssh/authorized_keys  onde  ~/ no meu caso é /home/r-pi-01/, ou seja, o mecanismo que o programa de setup do Ubuntu Core 18 64 bits para permitir o acesso remoto ao Raspberry Pi é criar o arquivo ~/.ssh/authorized_keys  com a chave pública associada a uma chave privada que você tem acesso pois criou o par anteriormente.

Agora já é possível fazer o boot do Raspberry Pi à partir do microSD gerado. Vamos então ao passo a passo.

1. Conecte os cabos: i) teclado na USB; ii) rede ethernet no RJ45; iii)  monitor na micro HDMI; iv) alimentação na fonte adequada.

2. Quando for solicitado use as credenciais de sua conta citada na etapa 3, anteriormente, e informe usuário e senha.

3. O Raspberry responde que copiou a chave SSH da sua conta Ubuntu (Public SSH keys were added to the device for remote access) e abre uma tela informando:

  1. O endereço IP associado a sua interface ethernet 
  2. As impressões digitais (fingerprints) de sua chave RSA
  3. Instruções de login remoto usando o comando ssh com IPV4

Veja exemplo de saída (no meu caso) abaixo:

Ubuntu Core 18 on 192.168.1.120 (tty1)
The host key fingerprints are:
    RSA     SHA256:PaSE+kKJLKSDREKJeiKLKJeJ3JKLKJEEKJ    ECDSA   SHA256:wIKSDFLKsdlkfsjDFSdfujsdf83kljSDdD    ED25519 SHA256:7JSDFSDkdsS3KJKSDsdflkfSDFLJKDSs39
To login:
    ssh r-pi-01@192.168.1.120
Personalize your account at https://login.ubuntu.com.

Volte ao seu computador e execute os 3 comandos abaixo:

eval `ssh-agent -s` && ssh-add -D && ssh-add ~/.ssh/id_rsa_r-pi-01

Estes comandos registram sua chave privada criada para efeitos de autenticação automática (sem pedido de senha) no seu Raspberry Pi.

Use as instruções de login remoto fornecidas pelo se Raspberry Pi para logar-se remotamente. No meu caso use o comando abaixo:

ssh r-pi-01@192.168.1.120

Em seguida você pode definir uma senha para seu usuário no Raspberry Pi para usar também sem configuração de chaves SSH.   

sudo passwd $USER

Informe a nova senha e anote para uso posterior.

Para ver detalhes da versão instalada execute:

cat /etc/os-release ; uname -a  

Esta versão do Ubuntu não vem com apt instalado e devemos usar o snap.  Podemos instalar o  htop assim:

sudo snap install htop 

E depois usá-lo.  Num Raspberry Pi com 4GB vemos algo assim:

Veja agora onde o snap instala os aplicativos:

which htop

/snap/bin/htop

Ou seja, todos os aplicativos instalados via snap ficam em área protegida /snap.

Podemos instalar o Firewall do Ubuntu Core 18.04 com o snap:

snap install ufw ; sudo ufw allow 22 # Muito importante liberar a porta 22 do SSH !

Instalação do Docker

Podemos pesquisar a documentação oficial em https://docs.ubuntu.com/core/en/platforms.  No caso de instalar o Docker podemos pesquisar o repositório de aplicativos assim:

snap find docker | grep canonical 

que exibirá:

 docker      19.03.11       canonical*    -   Docker container runtime
 etcd        3.4.5          canonical*    -   Resilient key-value store by CoreOS

Assim podemos instalar o Docker executando:

sudo snap install docker  &amp;&amp; sudo usermod -aG docker $USER &amp;&amp; groups $USER

Com o Docker instalado podemos obter, por exemplo, o OpenJDK 11 64 bits para processadores ARM executando o comando:

docker pull adoptopenjdk/openjdk11:jdk-11.0.8_10-slim

A tag jdk-11.0.8_10-slim ocupa apenas 150 MB (comprimida) e é adequada ao RaspberryPi. Para testar a imagem podemos usar:

docker run -it --rm --name java11 adoptopenjdk/openjdk11:jdk-11.0.8_10-slim bash

Dentro do contêiner é possível executar o jshell que é um REPL Java e testar o ambiente.

System.out.println("Olá mundo. Timestamp:" 
    + new java.sql.Timestamp(System.currentTimeMillis()))

Criando o Cluster com Swarm

Podemos em seguida criar um cluster de hosts gerenciado pelo Swarm que é nativo no Docker. Para tal precisamos saber o endereço externo da interface de rede usada pelo Docker. Já sabemos o endereço mas podemos executar um script awk para filtrar a lista de interfaces de rede  executando:

EXTERNAL_IP=`ip addr show | grep 'inet ' | grep -v ' lo$\| docker0$' \
            | awk '{ split($2,t,"/"); print t[1] }'`
echo ${EXTERNAL_IP}

que exibe no meu caso:

192.168.1.120

Este é o endereço já conhecido e o script awk foi colocado aí para efeitos de automação, caso seja necessário. Para criar um cluster executamos o comando abaixo no primeiro node (Raspberry Pi corrente):

sudo docker swarm init --advertise-addr ${EXTERNAL_IP}

que exibirá algo assim:

Swarm initialized: current node (e02ra6e1n0coj5rt6j3tqwnad) is now a manager. 

To add a worker to this swarm, run the following command: 

docker swarm join --token \
SWMTKN-1-5e3lcfszfbvwxpzqbdf6l1yged6pzovctmlze0070bsfv41r98-f59jvnvcesyb5rxd4dm7irlov \
192.168.1.120:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and
follow the instructions.

Observe que o Swarm é inicializado e configura o Raspberry Pi atual como master e fornece o comando para agregar novos hosts ao Cluster.

O próximo passo agora é repetir a instalação e configuração do segundo Raspberry Pi que envolve todas as etapas descritas acima nesta seção. Quando estivermos com o sistema rodando no Ubuntu Core 18 com o Docker instalado podemos executar neste novo host, o comando sugerido pelo swarm (em azul, acima).

docker swarm join --token \
  SWMTKN-1-5e3lcfszfbvwxpzqbdf6l1yged6pzovctmlze0070bsfv41r98-f59jvnvcesyb5rxd4dm7irlov \
  192.168.1.120:237

O  docker responde com o seguinte:

This node joined a swarm as a worker.

Pronto, temos agora um Cluster com 2 Nodes. Este processo pode ser repetido para quantos Raspberry Pi forem necessários no Cluster.

O comando docker info pode ser executado em todos os hosts onde instalamos o Docker e fizemos Join ao Cluster. Todos eles mostrarão uma seção Swarm com status active. e também mostrará o número de nodes existentes no Cluster, no momento aparece a linha Nodes: 2 sob a seção Swarm.

É importante mencionar que o comando mostrado acima poderá ser executado também em outras máquinas da rede que tenham o Docker instalado. Basta que seja uma versão atual com o Swarm integrado. É bastante saudável, entretanto, que as versões do Docker sejam as mesmas, mesmo que em sistemas operacionais distintos (Ubuntu, CentOS, MacOS, etc.).

Para ter uma visão geral das informações com destaque para o número de nodes podemos executar:

INFO=`sudo docker info 2> /dev/null` ; echo $INFO | grep "Nodes: \|: "

que retorna algo parecido com isto abaixo:

Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 19.03.11 Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: active NodeID: e02ra6e1n0coj5rt6j3tqwnad Is Manager: true ClusterID: l7341897urhhwv1d0hyuq2rjh Managers: 1 Nodes: 2 Default Address Pool: 10.0.0.0/8 SubnetSize: 24 Data Path Port: 4789 Orchestratio: Task History Retention Limit: 5 Raft: Snapshot Interval: 10000 Number of Old Snapshots to Retain: 0 Heartbeat Tick: 1 Election Tick: 10 Dispatcher: Heartbeat Period: 5 seconds CA Configuration: Expiry Duration: 3 months Force Rotate: 0 Autolock Managers: false Root Rotation In Progress: false Node Address: 192.168.1.120 Manager Addresses: 192.168.1.120:2377 Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429 runc version: init version: fec3683 Security Options: apparmor seccomp Profile: default Kernel Version: 5.3.0-1028-raspi2 Operating System:Ubuntu Core 16 OSType: linux Architecture: aarch64 CPUs: 4 Total Memory: 3.703GiB Name: localhost ID: A5IA:FQN7:N675:6F5V:E34I:UIHO:K4YY:UJIZ:35CU:XZBE:U4GR:I7SU Docker Root Dir: /var/snap/docker/common/var-lib-docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false

O comando  sudo docker node ls mostra os Nodes existentes no Cluster, seus Hostnames, seus estados e versões do Docker instaladas.  Já o comando docker node inspec mostra mais detalhes.

Exemplo: sudo docker node inspect --pretty localhost

O comando docker service create pode ser usado no Cluster para iniciar contêineres Docker que irão se replicar pelos nodes existentes. Considere um exemplo com o HTTP Server do Python3 do Ubuntu rodando como serviço em um Cluster de 4 Raspberry Pi. Um deles funcionará como master e os outros 3 como workers.

Cluster de Raspberry Pi numa arquitetura orientada a serviços rodando sobre Docker Swarm

Antes liberamos a porta 80 no Raspberry Pi usando os comandos:

sudo ufw allow 80 ; sudo ufw enable ; sudo ufw status 

É preciso também atualizar a disponibilidade do Node master. No primeiro Raspberry Pi criado, que funcionará como node master, execute o seguinte:

NODE_ID=`docker info | grep "NodeID: " | awk '{ print $2 }'`
echo  $NODE_ID # Para verificar a execução correta do AWK
sudo docker node update --availability drain $NODE_ID

Agora criamos o serviço HTTP Server do Python3 disponível, por exemplo no Ubuntu 18.

sudo docker service create --name http-server -d -p 80:8000 \
     ubuntu:18.04 python3 -m http.server 8000

Agora podemos listar os serviços.

sudo docker service ls

E verificar o estado deles.

sudo docker service ps http-server

Ao finalizar a utilização dos serviços no Cluster podemos removê-los

sudo docker service rm http-server

O docker swarm é apenas um dentre os diversos orquestradores multi-host para contêineres Docker.  O padrão de fato para computação em nuvem é o Kubernetes. Então podemos verificar o suporte no Ubuntu Core 18 64 bits para este componente.  Executando o comando snap find kubernetes  verificamos que não existe um snap pronto com Kubernetes no Ubuntu Core 18. Na verdade esta versão do sistema é focada em IoT e sistemas embarcados e por isso deve ser muito enxuta. Para a maioria das aplicações, mesmo em Cluster, o Docker Swarm atende as necessidades de infraestrutura neste tipo de hardware. Sendo assim Kubernetes no Raspberry Pi fica pra próxima.

Conclusão

Esta foi uma breve introdução ao orquestrador multi host nativo do Docker que portanto pode rodar normalmente mesmo em sistemas Linux embarcados como é o caso do Ubuntu Core 18. Não foi a intenção esgotar as funcionalidades do Docker Swarm neste documento mas ele poderá servir de ponto de partida para desenvolvimento de soluções criativas, escaláveis e com bom desempenho. Também foi abordado o processo de instalação do Ubuntu Core 18.04 64 bits no Raspberry Pi 4 de forma que fica muito fácil criar um cluster on-premise com este tipo de hardware de baixo custo. Tal solução permite desenvolver soluções de IoT com computação distribuída usando micro serviços. Uma dessas soluções é o SOMA – Sistema Orientado à Gestão de Ativos de engenharia. Este sistema foi instalado pelo CEPEL (http://cepel.br) na Usina de Itaipú, usando servidores Windows Server, para monitorar toda a planta de geração de energia, mas poderá ser instalado em pequenas plantas industriais, em um cluster como este descrito, pois é uma solução Java EE e portanto compatível com a arquitetura ARM 64.

SCM na na GCP usando Cloud Source Repositories

A GCP (Google Cloud Platform) fornece serviço de gestão de mudança (SCM) em software (análogo ao GitHub) via o serviço Cloud Source Repositories. Neste serviço podemos hospedar repositórios privados de código associados aos nossos projetos com a vantagem de poder criar pipelines de CI/CD usando uma infraestrutura já pronta para o uso. Existe uma cota de uso que é gratuita e atende a maioria das pequenas empresas e algumas startups, o que é uma grande vantagem. O fato de não ser necessário a preocupação com a gestão da infra-estrutura é uma grande vantagem para pequenos negócios.

A seguir descrevo o processo de criação de repositório, habilitação de chave SSH e criação de gatilho (trigger) para processo de CI/CD com geração de imagem Docker da aplicação, toda a vez que houver um commit no branch MASTER. 

Criação de repositório de fontes na GCP

Antes de criar o repositório é necessário ter uma conta registrada na GCP. Quem não possui tal conta pode criar uma gratuitamente e ganhar um crédito de US$ 300,00 para usar durante 12 meses. Para obter detalhes sobre os custos acesse a a pagina https://cloud.google.com/free 

Na figura acima, como já estou conectado aparece à direita na parte superior (1) o combo de escolha de idioma, o link para a console do GCP e o meu avatar. O (2) botão para para acesso a console também é mostrado na figura.   

Existem produtos classificados como “Sempre gratuitos” e outros que ao se usar consomem do seu crédito existente, ou seja, a GCP usa um modelo de cobrança de serviço pré-pago. Basta rolar a página para ver as condições. 

Para quem ainda não tem a conta, pode iniciar o trial e seu crédito é depositado na conta imediatamente permitindo criar uma máquina virtual, um contêiner ou qualquer outro tipo de recurso na Cloud.

Para criar repositório GIT no Cloud Source Repositories  é necessário primeiro criar um projeto qualquer na console do GCP. Depois basta acessar o link https://source.cloud.google.com/ e criar o repositório .

Veja figura acima : (1) botão para criar o repositório e (2) opção de visualização de todos os repositórios existentes associados a sua conta no GCP.

Ao clicar em Add repository você verá algo como mostrado na figura abaixo e basta escolher (1) Create new repository e depois (2) Continue.

Uma nova tela abrirá como mostrado na figura abaixo. Você deverá preencher o  (1) Repository name. Em relação  ao Project (2) você poderá escolher algum projeto já criado, ou mesmo usar o botão (3) Create Project. Ao final basta clicar em (4) Create.

Ao criar o projeto abre-se uma tela para escolha entre duas opções:

  1. Atualizar o repositório com fontes já existentes localmente na área de trabalho
  2. Executar o comando git clone para a área local de trabalho.

Escolha a mais adequada a situação em questão. Acima aparece a figura no caso da opção 1 que é a mais comum, Ou seja (1) Push code from a local Git repository. Em seguida, em (2) selecionamos SSH Authentication como método de autenticação e podemos então copiar o comando (3) para adicionar ao repositório local o endereço remoto correto do repositório criado. Em (4) aparece o comando necessário para atualizar o repositório remoto com os artefatos modificados localmente. 

No Workflow do GIT antes de fazer o push é necessário fazer add e commit, mas não aparece na tela por ser o Workflow padrão, que pode envolver outros comandos como git tag, git log, etc. Também é necessário configurar globalmente user e e-mail, caso ainda não tenha sido feito.

echo $USER
git config --global user.email $USER@automacao.info # Substitua o domínio pelo seu
git config --global user.name $USER

Com o projeto criado podemos lista na visão (1) All repositories como visto em (2). Observe que existe uma estrela a esquerda que permite escolher alguns repositórios como preferidos para acesso rápido. Isto é útil quando temos dezenas de repositórios.

Neste momento já é possível acessá-lo na Console do GCP usando a URL:  https://console.cloud.google.com/. Isso é feito via Cloud Shell cujo botão de ativação aparece no canto superior direito como mostra a figura abaixo. Passando o mouse sobre (1) você verá a descrição (2) Activate Cloud Shell. Basta clicar no ícone para abrir a Shell Bash com todas as credenciais necessárias para uso dos serviços na GCP, inclusive o acesso aos repositórios de códigos fonte do Cloud Source Repositories.

Isso já é suficiente para executarmos várias tarefas, porém as vezes é necessário acessar tais repositórios de fonte à partir de uma Console do seu sistema Operacional na sua máquina remota de desenvolvimento, teste ou homologação. Por exemplo, este é o caso quando você deseja usar IDEs tais como IntelliJ IDEA, VS Code, PyCharm, Eclipse, etc. Estas IDEs podem trazer grandes ganhos de produtividade para a equipe, e neste caso precisamos clonar o repositório em máquinas remotas de forma segura usando SSH. Veja a seguir a descrição deste processo.

Habilitando chave SSH para acesso remoto

Em toda a máquina Linux ou MacOS já temos um diretório padrão $HOME/.ssh que contém, dentre outras coisas, os pares de chaves RSA do usuário logado no momento. Em geral já existe neste diretório os arquivos : id_rsa e id_rsa.pub que são respectivamente a chave privada e a chave pública gerada no momento da criação do usuário pelos administradores de sistema. No caso do Windows é aconselhável usar o WLS, cuja instalação é mostrada neste guia: https://docs.microsoft.com/pt-br/windows/wsl/install-win10 

Podemos habilitar este par de chaves para acesso aos repositórios Git do Cloud Source Repositories. Para isso fazemos:

  1. Adicionamos a chave pública  a lista de chaves permitidas no Cloud Source Repositories.
  2. Registramos no daemon do SSH a chave privada, na máquina cliente.

Para a primeira etapa, veja figura abaixo onde: escolhemos em (1) a elipse vertical e depois a opção (2) Manage SSH Keys que nos abre uma página para adicionar e remover Chaves públicas autorizadas.

Para criar usamos o botão  Register SSH Key que abre um diálogo onde nomeamos a chave no campo Key name. Neste ponto acessamos  a máquina local e copiamos para a área de transferência do sistema operacional o arquivo id_rsa.pub e colamos na pagina WEB no campo Key, como mostrado abaixo:

Agora basta clicar no botão Register para confirmar.

Para a segunda etapa que é registrar no daemon do SSH a chave privada, na máquina cliente devemos executar os seguintes comandos:

eval `ssh-agent -s` &amp;&amp; ssh-add -D &amp;&amp; ssh-add ~/.ssh/id_rsa

Observe que localmente usamos a chave privada em vez da chave pública usada na etapa anterior. A chave privada não pode ser compartilhada pois cria uma grande brecha na segurança dos sistemas.

Criação de trigger para processo de CI/CD

Para criar o Trigger (gatilho) necessário ao processo de Integração Contínua (continuous integration) ou Entrega Contínua (continuous delivery) precisamos de uma aplicação exemplo. 

Criei uma aplicação muito simples chamada CitiesRestApp para testar o trigger da GCP. Desta forma precisamos clonar este repositório localmente e depois adicionar no repositório criado no Cloud Source Repositories. Assim, num terminal local executamos:

git clone git@github.com:joao-parana/cities-app.git CitiesRestApp
cd CitiesRestApp
# Criamos algumas váriáveis no ambiente para estruturar o script
GCP_USER=admin@automacao.info # atualize com seu usuário@domínio
CSR_ADDRESS="ssh://${GCP_USER}@source.developers.google.com:2022"
GCP_PROJECT=$(gcloud info --format='value(config.project)') 
CSR_REPO=CitiesRestApp
echo google ${CSR_ADDRESS}/p/${GCP_PROJECT}/r/${CSR_REPO} 
# Adicionamos a referencia para o remote no repositório local
git remote add google ${CSR_ADDRESS}/p/${GCP_PROJECT}/r/${CSR_REPO}
# Atualizando https://source.cloud.google.com/ com CitiesRestApp
git push --all google

OBS: Você deverá substituir os valores das variáveis GCP_USER, GCP_PROJECT e CSR_REPO pelos valores adequados a sua realidade. 

Agora os fontes já estão no repositório do GCP, onde podemos ver os três painéis principais como mostrado na figura abaixo

  1. Painel de navegação pelos artefatos (arquivos e pastas)
  2. Painel principal onde aparece o README.md renderizado
  3. Painel com a história de commits no repositório

Agora que temos a aplicação no Cloud Source Repositories podemos criar efetivamente o  Build automatizado. Um dos requisitos é a criação de um bucket  no  Cloud Storage que neste caso  chamaremos ${CSR_REPO}-${PROJECT_ID} e para isso executamos na Cloud Shell:

export PROJECT_ID=$(gcloud info --format='value(config.project)')
echo ${PROJECT_ID}
# O nome do Bucket deve ser lowercase
CSR_REPO_LOWERCASE=`echo ${CSR_REPO} | tr '[:upper:]' '[:lower:]'`
BUCKET_NAME=gs://${CSR_REPO_LOWERCASE}-${PROJECT_ID}
echo $BUCKET_NAME
# Criando o Bucket
gsutil mb -l us-east1 $BUCKET_NAME
# Alterando a ACL para acesso de READ a todo mundo
gsutil defacl set public-read $BUCKET_NAME
# Listando o Bucket 
gsutil ls -L gs:// | grep "gs://"

Com o Bucket criado e os fontes hospedados na GCP podemos criar o Trigger.

Entre na Console do GCP e procure o grupo de ferramentas (1) Tools e depois a opção (2) Cloud Build e clique em (3) Triggers. Veja na figura abaixo:

Veja na figura abaixo como criar o Trigger. Escolha um nome tal como cities-rest-app e uma descrição relevante. Event deve ser mantido como Push to a branch e para Build configuration / File type escolha Dockerfile  e também defina o caminho como (3) : /docker e escolha o  nome do Dockerfile como o valor padrão (4). Como nome da imagem Docker gerada pelo trigger escolha: 

gcr.io/$PROJECT_ID/<resultado de $CSR_REPO_LOWERCASE>:$COMMIT_SHA

que no meu caso foi (5) :

gcr.io/$PROJECT_ID/citiesrestapp:$COMMIT_SHA

As variáveis $PROJECT_ID e $COMMIT_SHA são suportadas no Cloud Build e isto é muito útil quando queremos criar scripts de CI/CD flexíveis.  

Observe que é apresentado (6) o comando que será executado para criar a imagem que é bastante útil para verificar se tudo está OK.  

cd docker 
docker build \
   -t gcr.io/$PROJECT_ID/citiesrestapp:$COMMIT_SHA \
   -f Dockerfile \
   .

Por último basta (7)  clicar em CREATE.  Veja abaixo o resultado da trigger criada.  No próximo commit na Branch Master o Trigger será executado e uma nova imagem Docker estará disponível no GCP em gcr.io cuja TAG será definida pelo ID do commit : $COMMIT_SHA.  

Assim poderemos automatizar a entrega contínua da aplicação.

Conclusão

Neste artigo foi mostrado como criar um repositório de código fonte gerenciado pelo GIT na GCP, como habilitar uma chave SSH para acesso à partir de sua máquina de desenvolvimento para editar e testar o código e como criar um Trigger para gerar uma imagem Docker de uma versão atualizada do software toda vez que for feito um commit no branch Master. Estas instruções demonstram a facilidade de implementar um pipeline de CI/CD na GCP.

Espero que tenham gostado. Deixem suas dúvidas e seus comentários abaixo e divulguem caso ache útil este conteúdo.

O Docker e a Virtualização Enxuta

Atualmente os sistemas de software afetam praticamente todos os aspectos de nossas vidas. No entanto  erros em sistemas de computação podem produzir prejuízos assustadores, tal como explodir uma nave espacial, levar uma empresa multimilionária a falência ou expor a imagem de uma gigante e líder na área de video-games. Todos estes exemplos são reais e a todo momento vemos noticias de outras catástrofes ocorridas por erros de software.

Qual o motivo para ocorreram estas coisas ? No raiz do problema está a negligência com a complexidade envolvida nos processos de desenvolvimento e implantação de sistemas. Um sistema de software bem escrito deve ser robusto, eficiente, confiável e flexível. Para conseguir isso devemos ter o controle de todo o Ciclo de Vida da Aplicação, desde a concepção, baseada nos requisitos do negócio, passando pelo desenvolvimento, testes, homologação, produção e gerenciamento de mudanças.

Ok, mas este é um artigo sobre Docker então por que falar disso tudo ? Ora, por que o Docker vai facilitar nossa vida em várias etapas desse ciclo de vida da aplicação. Neste artigo vou descrever o que é o Docker, pois este é o primeiro passo para poder usa-lo de forma eficiente para Gerenciar todos os Ciclos de Vida de nossas Aplicações. Acompanhe!

Gerenciamento do Ciclo de Vida da Aplicação

A manutenção de ambientes de Desenvolvimento, Homologação e Produção tem grande impacto no Gerenciamento do Ciclo de Vida da Aplicação (ALM – Application Lifecycle Management), devido a grande complexidade das aplicações atuais nas sua maioria rodando em ambiente distribuído. Para mitigar os riscos neste gerenciamento e manter os altos níveis de qualidade desejados pelos usuários dos sistemas é necessário que usemos algumas das ferramentas disponíveis para os setores de Tecnologia da Informação que permitam alto nível de produtividade.

Considere como exemplo um sistema com com apenas 5 clientes (Window, MacOS, Ubuntu, iOS e Android). Como temos três ambientes diferentes para cada cliente (teste, homologação e produção) devemos gerenciar 15 ambientes, cada um com seus ativos de software, banco de dados, scripts de teste, etc. Torna-se impossível usar maquinas reais nesta tarefa pois teríamos uma pilha de 15 máquinas que consumiria muitos recursos e muito espaço físico. Para resolver este problema é necessário compartilhar os recursos de hardware usando algum tipo de virtualização.

Virtualização

Podemos optar por usar Maquinas Virtuais para facilitar o gerenciamento e provisionamento de serviços. Neste caso entra em cena a ferramenta Vagrant que facilita a criação dessas máquinas virtuais e o provisionamento de recursos de forma programática. Isto é possível usando ferramentas como Puppet, Salt, Chief ou Ansible.

A criação e manutenção de uma máquina virtual (Virtual Box, Hyper-V ou VMWare) demanda grande quantidade de tempo e, muitas vezes revela-se uma tarefa complexa. Além do fato dessas máquinas virtuais consumirem uma quantidade imensa de espaço em disco.

Melhorando o modelo

Devido ao crescimento da demanda por Maquinas Virtuais e grande dificuldade na operação desse ambiente surgiu a necessidade de melhorar esse modelo. Os pesquisadores desta área perceberam que não havia a necessidade de recriar um SO completo e que bastava reusar os recursos de um mesmo SO num espaço de run-time independente chamado Virtual Environment. Lançando mão de uma funcionalidade nativa do Kernel Linux, para facilitar a criação e gestão destes ambientes virtuais leves, eles conseguiram ótimos resultados. Assim surgiu o LxC e mais recentemente o Software Docker que em pouco tempo roubou a cena quando se fala em automatizar a infraestrutura de software e hardware.

O Vagrant e o Docker se complementam num ambiente de Gerenciamento de infraestrutura de TI mais sofisticado onde podemos usar Ansible para automatizar, de forma global, toda a infraestrutura de virtualização.

Detalhando

A seguir vamos detalhar um pouco mais o Docker que é uma ferramenta extremamente útil na gestão de múltiplos ambientes para Gerenciamento do Ciclo de Vida do Software.

Docker é uma plataforma aberta usada por desenvolvedores e administradores de sistemas para construir, entregar e rodar aplicações distribuídas. É composto pelo Docker Engine, que é uma ferramenta leve de execução e empacotamento, e pelo Docker Hub, um serviço em nuvem responsável pelo compartilhamento de aplicações e automação de fluxos de trabalho. Ele permite que aplicações sejam rapidamente montadas e elimina as diferenças entre os ambientes de desenvolvimento, testes e produção.

O Docker não é uma ferramenta de virtualização de máquinas, ele é um ambiente de virtualização de Linux, construído sobre os LinuX Contêineres (LxC), que utiliza as funcionalidades cgroups e namespacing do Kernel do Linux (e é escrito na linguagem Go) para criar e rodar ambientes Linux Virtuais isolados em um único host. Em 2015 a equipe do Docker em parceria com 34 outras empresas fundaram a Open Container Initiative para padronizar contêineres e a forma de executá-los independente de sistema operacional. Com isso foi possível que a Microsoft criasse também o seu Kernel de Windows como base para contêineres.

O Virtual Environment – VE roda diretamente sobre o kernel já existente (do host) e apenas cria um contêiner onde serão executados seus aplicativos, onde até é possível recriar outra configuração de SO, já que este será apenas outro aplicativo rodando sobre o kernel. Vale observar que o Docker compartilha o kernel do Linux entre os ambientes e portanto seus contêineres não terão completo isolamento como acontece numa maquina real ou numa maquina virtual completa rodando sob Virtual Box, Hyper-V ou VMWare.

Acontece porém que o isolamento que ele proporciona atende plenamente as necessidades dos Sistemas de Informação em ambientes controlados, com a vantagem de executar extremamente rápido. Por exemplo, podemos iniciar um aplicativo num contêiner à partir do Host em apenas alguns milisegundos de latência. Esta mesma aplicação iniciando numa maquina virtual pode demorar vários minutos.

O Docker usa o AuFS (advanced multi layered unification filesystem) como sistema de arquivos, assim ele reutiliza a imagem do SO e as imagens relativas a cada um dos serviços instalados entre vários de seus contêineres em execução economizando recursos valiosos do computador host. O Docker pode usar opcionalmente o OverlayFS que oferece as mesmas vantagens e tem melhor desempenho.

O Docker permite Implantação portátil através de máquinas, pois podemos usar o Docker para criar um objeto único que contém todas as suas aplicações empacotadas o qual chamamos Imagem Docker. Este objeto pode ser transferido e rapidamente instalado em qualquer outro host Linux com Docker habilitado, incluindo maquinas reais ou virtuais rodando Linux, Windows ou MAC OSX.

O Docker permite Reutilização de componentes pois podemos “empilhar” pacotes já criados cada um com sua funcionalidade específica. Por exemplo, se você precisa criar várias máquinas que todos necessitam de banco de dados Oracle e WebServer Apache, podemos criar uma imagem base contendo o Oracle e outra contendo o Apache, e na sequência, construir e criar novos contêineres que utilizam estas imagens base já criadas.

O Docker pode usar imagens disponíveis em bibliotecas compartilhadas. Existe um registro público, já mencionado, onde podemos encontrar milhares de imagens já prontas (https://hub.docker.com/explore/).

Principais Funcionalidades do Docker

Contêineres facilmente portáveis: você pode criar uma imagem de toda a configuração e aplicativos instalados em seu contêiner, transferir e instalar em um outro host desde que tenha um Docker previamente instalado.

Versionamento: Docker permite que você versione as alterações de um contêiner de uma forma muito semelhante ao feito pelo GIT ou SVN. Isto permite portanto verificar as diferenças entre versões, fazer commit de novas versões e fazer rollback de uma dada versão.

Reutilização de componentes: as imagens criadas podem ser reutilizadas, como por exemplo, se diversas de suas aplicações utilizam uma stack com Java 11, Tomcat 9 e Oracle 18 você poderá criar uma uma imagem base contendo estes itens com sua instalação e configuração. Desta maneira esta imagem poderá ser reutilizada em diversos contêineres diferentes. Podemos construir imagens Docker usando um arquivo texto chamado Dockerfile e o comando de montagem docker build. O Dockerfile usa uma DSL – Domain Specific Language – simples com poucas instruções, porém suficientes para construir imagens Docker. A abordagem de criação via Dockerfile pode ser repetida quantas vezes for necessário, em quantos ambientes seja desejado além de permitir gerenciamento de versão no GIT/SVN das definições da infraestrutura. Com esta DSL o Docker consegue levar a Metodologia de Orientação a Objetos para o provisionamento da Infraestrutura e passamos a ter funcionalidades de Programação de Infraestrutura que é o sonho de qualquer profissional de operações de TI.

Compartilhamento: o Docker Hub já possui milhares de imagens de contêineres com as mais diversas aplicações instaladas e configuradas e desta maneira você pode rapidamente criar sua aplicação com uma base desenvolvida por outra pessoa, ou ainda criar sua base e compartilhá-la.

CLI – Command Line Interface e API – Application Program Interface: Permite criação de Programas e Scripts que interagem com o Docker para provisionar serviços nos Contêineres.

Automatização de Implantação dentro dos Contêineres: Usando os provisionadores que por sua vez usam a API do Docker, podemos automatizar a implantação dos ambientes de software.

Licença Open Source: Licenciado como Apache License, Version 2.0 mantém os códigos fonte disponíveis para facilitar o desenvolvimento colaborativo.

Evita Dependency Hell: Um dos maiores problemas em múltiplos ambientes com os quais os desenvolvedores de software convivem diariamente é o gerenciamento de dependências. O Docker evita problemas neste gerenciamento por permitir versionamento de cada componente adicionado na imagem.

Demanda Poucos Recursos de Hardware: Exige poucos recursos de processos, memória e espaço em disco. Você pode rodar facilmente o Docker e os seus contêineres em computadores tão pequenos quanto um Raspberry Pi.

Performance inigualável: é possível baixar, por exemplo, uma imagem Fedora do repositório público na Internet em menos de um minuto e executar um comando simples num contêiner criado com esta imagem, à partir do computador Host, em menos de um segundo. Imagens baseadas em Alpine Linux são ainda menores e mais rápidas.

Ligação entre Contêineres: Conectar contêineres via mapeamentos de porta TCP/IP não é a única forma de disponibilizar recursos entre eles. Um contêiner Docker pode se conectar a um outro via um sistema de ligação e enviar informações de um para o outro de forma eficiente e segura. Quando os contêineres estão ligados, a informação sobre o contêiner origem pode ser enviada para um contêiner destino. Isso permite que o contêiner destino possa acessar dados selecionados do contêiner origem. Como exemplo, podemos então criar um contêiner chamado web (destino) e ligá-lo a um contêiner chamado db (origem) usando um alias para o link chamado dblink, por exemplo. Para permitir a ligação o Docker cria um túnel seguro entre o contêiner origem e o contêiner destino, eliminando a necessidade de expor qualquer porta TCP/IP externamente a estes dois contêineres. Isto é feito com Redes Docker que são redes programadas por software compatíveis com Ethernet/TCP-IP.

Orquestração com Docker Compose ou Kubernetes

Docker Compose é uma ferramenta para definir e rodar aplicações complexas via Docker. Com o Docker Compose, podemos definir uma aplicação multi-contêiner num único arquivo de script, e em seguida iniciar a aplicação e tudo da qual ela depende para sua execução. O Docker Compose é ótimo para usar em ambientes de desenvolvimento, homologação e Integração Contínua e permite implementar Arquitetura de Micro Serviço de forma simples.

Quando precisamos escalar nossa infraestrutura horizontalmente em um Cluster podemos usar o Docker Swarm que é um orquestrado multi-host fornecido nativamente pelo Docker e funciona até em hardwares minúsculos como o Raspberry Pi. A vantagem do Docker Swarm é a simplicidade.

Quando pensamos em Computação em nuvem em larga escala precisamos de soluções mais escaláveis para orquestração e neste caso é necessário usar Kubernetes que é o padrão da indústria de TI atualmente. Por ser projeto de código aberto o Kubernetes é suportado em todos os provedores de CaaS (Containers as a Service), tais como Azure, Google Cloud Plataform e AWS. Veja este post para detalhes das ofertas de serviços atuais de nuvem

Virtualização Enxuta

Por tudo isso que foi exposto aqui podemos dizer que o Docker permite levar para a Infraestrutura de TI esta Técnica de Virtualização Enxuta que nos proporciona o benefício da eliminação de desperdício da mesma forma que é preconizada pelas Metodologias Ágeis para Desenvolvimento de Software (SCRUM/XP) e de Produtos e Negócios (Lean Startup). Assim Docker, Kubernetes e Cloud Computing são os parceiros ideais das metodologias ágeis de gestão e desenvolvimento de produtos e de negócios. Ferramentas essenciais para as Start-ups de qualquer natureza.

IaaS, CaaS, Pass, SaaS ou FaaS. Qual a melhor opção ?

As abstrações são os blocos de construção fundamentais da computação. Quando criamos uma aplicação Web, não escrevemos código de máquina ou construímos pacotes TCP/IP. Nós usamos linguagens de alto nível tal como Java ou Javascript para enviar e receber mensagens HTTPS. Na infraestrutura computacional usamos também as abstrações. Em 1972 já se tinha sistema operacional baseado em máquina virtual, que é uma abstração da computador real. Hoje em dia toda a infraestrutura da Internet, dos Data Centers e da Nuvem computacional pública (Amazon AWS, Azure, Google Cloud, DigitalOcean, etc) são construídas sobre abstrações. 

Os termos IaaS, CaaS, PasS, SaaS e FaaS referem-se a tipos de serviços de computação fornecidos na nuvem com modelo de pagamento flexível dependendo da utilização. Estes tipos diferem fundamentalmente em seu nível de abstração de recursos computacionais. As três últimas letras dos acrônimos significam As A Service, ou seja, Como Serviço e a primeira letra significa o nível de abstração desejado: I para infraestrutura, C para Contêiner, P para plataforma, S para Software e F para Função. E qual a importância de conhecer a diferença entre estes modelos de serviço na nuvem ? Conhecer ajuda a escolher a melhor opção para solucionar um dado problema, já que existem vantagens e desvantagens em cada uma dessas abordagens, e é necessário a avaliação da relação custo/benefício para cada projeto.

Modelos de Serviços Nuvem

A figura acima mostra em azul tudo que é responsabilidade do provedor de serviço de nuvem e em cinza tudo que é responsabilidade do cliente em termos de gestão. Em verde está o ponto de entrada do cliente. No caso da Infraestrutura como serviço (primeira coluna à esquerda) o administrador da infraestrutura por parte do cliente escolhe e instala o sistema operacional (em verde na figura) e portanto terá responsabilidade de gerir todos os recursos que porventura venha instalar no Sistema Operacional escolhido, mas a Virtualização e o Hardware (Discos, Rede, CPU, etc) são gerenciados pelo provedor de nuvem. Num cenário de aplicação Serverless, por exemplo, podemos ter a configuração mostrada na figura onde a aplicação usa Functions sobre um ambiente de Runtime Serverless rodando sobre contêineres Docker no Sistema Operacional escolhido. O custo de Gestão de Infraestrutura neste caso é o mais alto dentre todos os modelos escolhidos mas a contrapartida é o controle total por parte do cliente de todos os recursos que aparecem em cinza. 

IAAS

A opção IaaS pode ser boa para pequenos projetos onde seja necessário um maior controle sobre o processo e que não necessite de balanceamento de carga das cargas de trabalho (workload). Porém no caso do cliente precisar de um cluster na nuvem numa aplicação de BigData esta opção adiciona custo de gestão do orquestrador de contêineres (Kubernetes, por exemplo) e passa a ser mais vantajoso escolher o modelo CaaS, fornecido pelos principais players de Nuvem. Considerando este último exemplo com aplicação BigData quando usamos o modelo IaaS, o cliente do serviço de nuvem deve instalar e configurar o Docker e o orquestrador de contêineres Kubernetes em todos os hosts. Isto não é um trabalho fácil e pode requerer muitas horas de trabalho árduo, dependendendo do Sistema Operacional escolhido, e da versões do Docker e Kubernetes. Além disso existe um custo de manutenção da plataforma (CAPEX), que não é desprezível. 

CaaS

Por outro lado (no modelo CaaS), num serviço gerenciado de contêineres como o GKE (Google Kubernetes Engine) ou AKS (Azure Kubernetes Service) é possível criar um cluster gerenciado e elástico com pouquíssimos comandos, e à partir daí todo o gerenciamento fica por conta do provedor de nuvem. 

Todos os profissionais de TI adoram contêineres pois oferecem muita liberdade no trabalho do dia-a-dia, permitindo empacotar um aplicativo com todas as suas dependências em um package fácil-de-mover (easy-to-move) facilitando o trabalho dos desenvolvedores e operadores. A diminuição de custos de gerenciamento da plataforma é espantosa e agrada a direção das empresas. 

Veja abaixo um exemplo de criação de Cluster elástico na nuvem da Google gerenciado com Kubernetes. Este exemplo demonstra a simplicidade da solução e como é fácil iniciar um projeto no modelo CaaS.

Kubernetes na GCP

PaaS

Na terceira coluna da figura temos o PaaS, que difere do CaaS por fornecer, além dos contêineres, o ambiente de Runtime gerenciado. Obviamente perde-se flexibilidade pois a aplicação deve ser escrita para aquele runtime específico, como por exemplo o AWS Elastic Beanstalk, Heroku ou GAE (Google App Engine).  O GAE por exemplo impõem algumas restrições sobre as tecnologias e linguagens de programação usadas na aplicação para que possa rodar na nuvem. A grande vantagem desta abordagem é a elasticidade do serviço. O gerenciamento exige menos recursos do cliente quando comparado ao modelo CaaS, ou seja perde-se flexibilidade mas economiza-se com a gestão da infra-estrutura.

SaaS

O modelo SaaS não exige nenhum esforço do cliente da nuvem, basta apontar o browser para a URL e usar. Os exemplos mais conhecidos são Google Drive, Google Docs, Office 365, Dropbox e Salesforce. A responsabilidade pelo gerenciamento é toda do provedor de nuvem e o único inconveniente são as limitações do modelo de domínio. O cliente não tem acesso às fontes dos aplicativos e ficam limitados a uso de plugins para adicionar funcionalidade. Neste caso pode ser necessário promover uma mudança cultural na empresa e modificações nos processos internos para se adequar às funcionalidades disponíveis no serviço SaaS. Em muitas situações isto é possível e ocorre uma diminuição considerável de custos para as empresas. O Salesforce, por exemplo, fornece serviço de gerenciamento de relacionamento com o cliente (CRM) além de um conjunto complementar de aplicativos corporativos focados no atendimento ao cliente, automação de marketing, etc. num licenciamento flexível por usuário cadastrado. Isto permite que pequenas e médias empresas usem a plataforma sem precisar licenciar um caríssimo software de CRM e arcar com a gestão da infraestrutura relacionada. Entretanto certos negócios não podem usar este modelo pois o custo de adaptação de seus processos às limitações impostas pelo software não compensa a economia com Infraestrutura de TI.

FaaS

O último e mais recente modelo de serviço de nuvem é o FaaS (Functions as a Service). As funções como serviço são o nível mais alto de abstração disponíveis no momento, mas pode ser que surjão no futuro modelos ainda mais abstratos. As arquiteturas FaaS e Serverless (sem servidor) ainda são um conceito relativamente novo para muitos desenvolvedores e setores de TI. À medida que as plataformas amadurecem e a adoção aumenta, os desenvolvedores criam funções de código (aberto ou fechado) que outros desenvolvedores poderão usar em seus aplicativos. Essas funções pré-criadas usarão os serviços FaaS existentes e abstraem o código dos desenvolvedores.

FaaS não é a mesma coisa que computação serverless, na verdade FaaS é mais um tipo de  computação serverless. O que caracteriza o modelo FaaS é o fato de a unidade de deploy na nuvem ser a Function e além disso as functions podem ser invocadas por eventos. Outros serviços de computação serverless conhecidos são: serverless HTTP applicationsserverless containers que podem ser implementadas nas nuvens Azure ou Google Cloud Platform.

Conclusão

Neste artigo definimos os termos IaaS, CaaS, PasS, SaaS e FaaS que se referem aos tipos de serviços de computação fornecidos pelos provedores de nuvem. Eles implementam níveis diferentes de abstrações de recursos computacionais e certamente cada aplicação da empresa, que esteja sendo migrado do deploy on-premise para a nuvem publica, pode se encaixar em um desses modelos descritos, considerando os requisitos de escalabilidade, elasticidade, confiabilidade, disponibilidade e segurança da informação definidos para o projeto de migração.

Ou seja, o Azure, AWS e Google Cloud Platform oferecem estas opções de computação e hospedagem, ou tipos de serviços de computação que incluem principalmente:

  • trabalhar em um ambiente sem servidor (FaaS);
  • usar uma plataforma de aplicativos gerenciados (PaaS);
  • aproveitar as tecnologias de contêiner para ter mais flexibilidade (CaaS);
  • criar a própria infraestrutura na nuvem para ter mais controle e flexibilidade (IaaS).

A escolha correta depende do fato de que de um lado, boa parte das responsabilidades pelo gerenciamento de recursos seja sua e, de outro, o provedor de nuvem é quem cuide da maioria delas. Isso implica que uma escolha errada para aumentar e muito os custos envolvidos se considerarmos, é claro, o custo total de propriedade (TCO).

Para comentar este post clique aqui e faça scroll para o fim do artigo. Nós apreciamos seu comentário e isso nos ajuda a melhorar o conteúdo no site