[Da última vez](https://community.intersystems.com/post/deploying-intersystems-iris-solution-gcp-kubernetes-cluster-gke-using-circleci), lançamos uma aplicação IRIS no Google Cloud usando seu serviço GKE. E, embora criar um cluster manualmente (ou por meio do [gcloud](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-cluster)) seja fácil, a [abordagem de Infraestrutura como Código (IaC)](https://martinfowler.com/bliki/InfrastructureAsCode.html) moderna recomenda que a descrição do cluster Kubernetes também seja armazenada no repositório como código. Como escrever este código é determinado pela ferramenta que é usada para IaC.
No caso do Google Cloud, existem várias opções, entre elas o Deployment Manager e o Terraform. As opiniões estão divididas quanto o que é melhor: se você quiser saber mais, leia este tópico no Reddit Opiniões sobre Terraform vs. Deployment Manager? e o artigo no Medium Comparando o GCP Deployment Manager e o Terraform.
Para este artigo, escolheremos o Terraform, já que ele está menos vinculado a um fornecedor específico e você pode usar seu IaC com diferentes provedores em nuvem.
Suporemos que você leu o artigo anterior e já tem uma conta do Google, e que criou um projeto chamado “Desenvolvimento”, como no artigo anterior. Neste artigo, seu ID é mostrado como
Lembre-se de que o Google não é gratuito, embora tenha um nível gratuito. Certifique-se de controlar suas despesas.
Também presumiremos que você já bifurcou o repositório original. Chamaremos essa bifurcação (fork) de “my-objectscript-rest-docker-template” e nos referiremos ao seu diretório raiz como
Todos os exemplos de código são armazenados neste repositório para simplificar a cópia e a colagem.
O diagrama a seguir descreve todo o processo de implantação em uma imagem:
Então, vamos instalar a versão mais recente do Terraform no momento desta postagem:
A versão é importante aqui, pois muitos exemplos na Internet usam versões anteriores, e a 0.12 trouxe muitas mudanças.
Queremos que o Terraform execute certas ações (use certas APIs) em nossa conta do GCP. Para ativar isso, crie uma conta de serviço com o nome 'terraform' e ative a API do Kubernetes Engine. Não se preocupe sobre como vamos conseguir isso — basta continuar lendo e suas perguntas serão respondidas.
Vamos tentar um exemplo com o utilitário gcloud, embora também possamos usar o console web.
Usaremos alguns comandos diferentes nos exemplos a seguir. Consulte os tópicos da documentação a seguir para obter mais detalhes sobre esses comandos e recursos.
Agora vamos analisar o exemplo.
Como trabalhamos com o gcloud no artigo anterior, não discutiremos todos os detalhes de configuração aqui. Para este exemplo, execute os seguintes comandos:
Agora, vamos adicionar alguns papéis à conta de serviço do terraform além de “Administrador do Kubernetes Engine” (container.admin). Essas funções serão úteis para nós no futuro.
$ gcloud projects add-iam-policy-binding
--member serviceAccount:terraform@
--role roles/iam.serviceAccountUser
$ gcloud projects add-iam-policy-binding
--member serviceAccount:terraform@
--role roles/compute.viewer
$ gcloud projects add-iam-policy-binding
--member serviceAccount:terraform@
--role roles/storage.admin
$ gcloud iam service-accounts keys create account.json \
--iam-account terraform@
Observe que a última entrada cria o seu arquivo account.json. Certifique-se de manter este arquivo em segredo.
A seguir, vamos descrever o cluster GKE na linguagem HCL do Terraform. Observe que usamos vários placeholders aqui; substitua-os por seus valores:
Placeholder | Significado | Exemplo |
|
ID do projeto do GCP | possible-symbol-254507 |
|
Armazenamento para estado/bloqueio do Terraform - deve ser único | circleci-gke-terraform-demo |
|
Região onde os recursos serão criados | europe-west1 |
|
Zona onde os recursos serão criados | europe-west1-b |
|
Nome do cluster GKE | dev-cluster |
|
Nome do pool de nós de trabalho do GKE | dev-cluster-node-pool |
Aqui está a configuração HCL para o cluster na prática:
provider "google" {
credentials = file("account.json")
project = "
region = "
}
resource "google_container_cluster" "gke-cluster" {
name = "
location = "
remove_default_node_pool = true
# No cluster regional (localização é região, não zona)
# este é um número de nós por zona
initial_node_count = 1
}
resource "google_container_node_pool" "preemptible_node_pool" {
name = "
location = "
cluster = google_container_cluster.gke-cluster.name
# No cluster regional (localização é região, não zona)
# este é um número de nós por zona
node_count = 1
node_config {
preemptible = true
machine_type = "n1-standard-1"
oauth_scopes = [
"storage-ro",
"logging-write",
"monitoring"
]
}
}
Para garantir que o código HCL esteja no formato adequado, o Terraform fornece um comando de formatação útil que você pode usar:
O fragmento de código (snippet) mostrado acima indica que os recursos criados serão fornecidos pelo Google e os próprios recursos são google_container_cluster e google_container_node_pool, que designamos como preemptivos para economia de custos. Também optamos por criar nosso próprio pool em vez de usar o padrão.
Vamos nos concentrar brevemente na seguinte configuração:
O Terraform grava tudo o que é feito no arquivo de status e usa esse arquivo para outro trabalho. Para um compartilhamento conveniente, é melhor armazenar este arquivo em algum lugar remoto. Um lugar típico é um Google Bucket.
Vamos criar este bucket. Use o nome do seu bucket em vez do placeholder
Boa resposta:
A resposta ocupado "Busy" significa que você deve escolher outro nome:
Também vamos habilitar o controle de versão, como o Terraform recomenda.
$ gsutil versioning get gs://
gs://
$ gsutil versioning set on gs://
$ gsutil versioning get gs://
gs://
O Terraform é modular e precisa adicionar um plugin de provedor do Google para criar algo no GCP. Usamos o seguinte comando para fazer isso:
Vejamos o que o Terraform fará para criar um cluster do GKE:
A saída do comando inclui detalhes do plano. Se você não tem objeções, vamos implementar este plano:
A propósito, para excluir os recursos criados pelo Terraform, execute este comando a partir do
Vamos deixar o cluster como está por um tempo e seguir em frente. Mas primeiro observe que não queremos colocar tudo no repositório, então vamos adicionar vários arquivos às exceções:
Usando Helm
No artigo anterior, armazenamos os manifestos do Kubernetes como arquivos YAML no
Desta vez, tentaremos uma abordagem diferente: usando o gerenciador de pacotes Helm do Kubernetes, que foi atualizado recentemente para a versão 3. Use a versão 3 ou posterior porque a versão 2 tinha problemas de segurança do lado do Kubernetes (veja Executando o Helm na produção: melhores práticas de Segurança para mais detalhes). Primeiro, empacotaremos os manifestos Kubernetes de nosso diretório k8s/ em um pacote Helm, que é conhecido como chart. Um chart Helm instalado no Kubernetes é chamado de release. Em uma configuração mínima, um chart consistirá em vários arquivos:
Seu propósito está bem descrito no site oficial. As práticas recomendadas para criar seus próprios charts são descritas no Guia de Melhores Práticas do Chart na documentação do Helm.
Esta é a aparência do conteúdo de nossos arquivos:
{{- define "iris-rest.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Cria o nome e a versão do chart conforme usado pelo rótulo do chart.
*/}}
{{- define "iris-rest.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
replicaCount: 1
strategy: |
type: Recreate
image:
repository: eu.gcr.io/iris-rest
tag: v1
webPort:
name: web
value: 52773
service:
enabled: true
name: iris-rest
type: LoadBalancer
loadBalancerIP: ""
ports:
web:
port: 52773
targetPort: 52773
protocol: TCP
Para criar os charts do Helm, instale o cliente Helm e o utilitário de linha de comando kubectl.
Crie um namespace chamado iris. Seria bom se isso fosse criado durante a implantação, mas até agora não é o caso.
Primeiro, adicione credenciais para o cluster criado pelo Terraform ao kube-config:
Confirme (sem iniciar uma implantação real) se o Helm criará o seguinte no Kubernetes:
A saída — os manifestos do Kubernetes — foi omitida por causa do espaço aqui. Se tudo estiver certo, vamos implantar:
Vemos que o Helm implantou nossa aplicação, mas como ainda não criamos a imagem Docker eu.gcr.io/iris-rest:v1, o Kubernetes não pode extraí-la (ImagePullBackOff):
Vamos terminar com isso por agora:
O Lado do CircleCI
Agora que experimentamos o Terraform e o cliente Helm, vamos colocá-los em uso durante o processo de implantação no lado do CircleCI.
orbs:
gcp-gcr: circleci/gcp-gcr@0.6.1
jobs:
terraform:
docker:
# A versão da imagem do Terraform deve ser a mesma de quando
# você executa o terraform antes da máquina local
- image: hashicorp/terraform:0.12.17
steps:
- checkout
- run:
name: Create Service Account key file from environment variable
working_directory: terraform
command: echo ${TF_SERVICE_ACCOUNT_KEY} > account.json
- run:
name: Show Terraform version
command: terraform version
- run:
name: Download required Terraform plugins
working_directory: terraform
command: terraform init
- run:
name: Validate Terraform configuration
working_directory: terraform
command: terraform validate
- run:
name: Create Terraform plan
working_directory: terraform
command: terraform plan -out /tmp/tf.plan
- run:
name: Run Terraform plan
working_directory: terraform
command: terraform apply /tmp/tf.plan
k8s_deploy:
docker:
- image: kiwigrid/gcloud-kubectl-helm:3.0.1-272.0.0-218
steps:
- checkout
- run:
name: Authorize gcloud on GKE
working_directory: helm
command: |
echo ${GCLOUD_SERVICE_KEY} > gcloud-service-key.json
gcloud auth activate-service-account --key-file=gcloud-service-key.json
gcloud container clusters get-credentials ${GKE_CLUSTER_NAME} --zone ${GOOGLE_COMPUTE_ZONE} --project ${GOOGLE_PROJECT_ID}
- run:
name: Wait a little until k8s worker nodes up
command: sleep 30 # It’s a place for improvement
- run:
name: Create IRIS namespace if it doesn't exist
command: kubectl get ns iris || kubectl create ns iris
- run:
name: Run Helm release deployment
working_directory: helm
command: |
helm upgrade iris-rest \
--install \
. \
--namespace iris \
--wait \
--timeout 300s \
--atomic \
--set image.repository=eu.gcr.io/${GOOGLE_PROJECT_ID}/iris-rest \
--set image.tag=${CIRCLE_SHA1}
- run:
name: Check Helm release status
command: helm list --all-namespaces --all
- run:
name: Check Kubernetes resources status
command: |
kubectl -n iris get pods
echo
kubectl -n iris get services
workflows:
main:
jobs:
- terraform
- gcp-gcr/build-and-push-image:
dockerfile: Dockerfile
gcloud-service-key: GCLOUD_SERVICE_KEY
google-compute-zone: GOOGLE_COMPUTE_ZONE
google-project-id: GOOGLE_PROJECT_ID
registry-url: eu.gcr.io
image: iris-rest
path: .
tag: ${CIRCLE_SHA1}
- k8s_deploy:
requires:
- terraform
- gcp-gcr/build-and-push-image
Você precisará adicionar várias variáveis de ambiente ao seu projeto no lado do CircleCI:
O GCLOUD\_SERVICE\_KEY é a chave da conta de serviço CircleCI e o TF\_SERVICE\_ACCOUNT_KEY é a chave da conta de serviço Terraform. Lembre-se de que a chave da conta de serviço é todo o conteúdo do arquivo _account.json_. A seguir, vamos enviar nossas alterações para um repositório:
O painel da IU do CircleCI deve mostrar que tudo está bem:
Terraform é uma ferramenta idempotente e se o cluster GKE estiver presente, o trabalho "terraform" não fará nada. Se o cluster não existir, ele será criado antes da implantação do Kubernetes.
Por fim, vamos verificar a disponibilidade de IRIS:
$ kubectl -n iris get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
Iris-rest LoadBalancer 10.23.249.42 34.76.130.11 52773:31603/TCP 53s
$ curl -XPOST -H "Content-Type: application/json" -u _system:SYS 34.76.130.11:52773/person/ -d '{"Name":"John Dou"}'
$ curl -XGET -u _system:SYS 34.76.130.11:52773/person/all
[{"Name":"John Dou"},]
Conclusão
Terraform e Helm são ferramentas DevOps padrão e devem ser perfeitamente integrados à implantação do IRIS.
Eles exigem algum aprendizado, mas depois de alguma prática, eles podem realmente economizar seu tempo e esforço.