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  && sudo usermod -aG docker $USER && 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` && ssh-add -D && 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.