Pesquisa de site

Como atualizar para o Terraform 0.12


Atualizar seu ambiente para o Terraform 0.12 não é para os fracos de coração, mas este tutorial tornará isso menos doloroso.

Ao começar a usar o Terraform, usei o Terraform versão 0.11. Como alguns leitores apontaram, esta versão carece de alguns recursos significativos, portanto, neste artigo, explicarei como atualizar o Terraform para a versão 0.12, mais rica em recursos.

Você pode se surpreender ao saber que muitas pessoas ainda usam o Terraform 0.11, incluindo grandes organizações e usuários teimosos que gostam de usar tecnologia testada e comprovada. A conversão do Terraform 0,11 para 0,12 pode ser muito complexa. Se você deseja aproveitar as vantagens dos recursos mais recentes, mas acha que não está pronto para atualizar, este artigo deve ajudar a facilitar a conversão.

Neste artigo, usarei o código do meu artigo de "primeiros passos" e a ferramenta tfenv, que permite usar diferentes versões do Terraform na mesma máquina. Também presumirei que você esteja familiarizado com as duas versões do Terraform (a que você está usando e a que está migrando) e como usar o comando terraform em geral.

Mudanças de código necessárias para a versão 12

Algumas coisas funcionam no Terraform versão 0.11 e não funcionam na versão 0.12. Aqui está um exemplo de código que usei em meu artigo anterior:

resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = "my-first-terraform-namespace"
  }
}

Como apontou um leitor, este bloco de código não funciona na versão 0.12 devido a mudanças na forma como o Terraform mais recente funciona. Este é um exemplo simples e fica mais complicado. Aqui estão alguns exemplos de quanto as coisas podem precisar mudar em um ambiente de produção.

O novo Terraform precisa de sintaxe e estrutura ligeiramente diferentes. Para começar, adicione um arquivo vars.tf:

variable "namespace" {
  type = "string"
  default = "helloworld"
}

variable "cluster" {
  type = "string"
  default = "minikube"
}

Em seguida, altere partes do arquivo main.tf para incorporar o novo arquivo vars.tf. O provedor Kubernetes agora se parece com isto:

provider "kubernetes" {
  config_context_cluster   = "${var.cluster}"
}

Aqui está o recurso de namespace revisado:

resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = "${var.namespace}"
  }
}

Finalmente, o provedor Helm alterado:

provider "helm" {
  kubernetes {
	config_context_cluster   = "${var.cluster}"
  }
}

Cuidado! Essas pequenas mudanças serão importantes ao alternar entre versões.

Implantar antes da conversão

Antes de prosseguir, configure o tfenv para usar a versão correta do Terraform:

jess@Athena:~/terraform_doc$ tfenv list
  0.12.29
  0.11.15-oci
jess@Athena:~/terraform_doc$ tfenv use 0.11.15-oci
Switching default version to v0.11.15-oci
Switching completed
jess@Athena:~/terraform_doc$ terraform --version
Terraform v0.11.15-oci
+ provider.helm v1.2.2
+ provider.kubernetes v1.11.2

Your version of Terraform is out of date! The latest version
is 0.xx.y. You can update by downloading from www.terraform.io/downloads.html

Se você (como eu) fez outro trabalho com o Terraform depois de usar o código em meu artigo anterior do Terraform, será necessário reimplantar tudo. Configure o cluster Minikube para esta implantação do Terraform iniciando um cluster novo e limpo:

$ minikube delete
?  Deleting "minikube" in kvm2 ...
?  Removed all traces of the "minikube" cluster.
jess@Athena:~/terraform_doc$ minikube start
?  minikube v1.14.0 on Ubuntu 18.04

Após a configuração do cluster, você poderá implantar seu código Terraform modificado. Comece com o comando init:

$ terraform init

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.helm: version = "~> 1.2"
* provider.kubernetes: version = "~> 1.11"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work. [...]

Em seguida, execute seu plano:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace) [...]

Terraform will perform the following actions:

  + helm_release.local
  	id:                      	<computed>
  	atomic:                  	"false"
  	chart:                   	"./buildachart"
  	cleanup_on_fail:         	"false"
  	create_namespace:        	"false"
  	dependency_update:       	"false"
  	disable_crd_hooks:       	"false"
  	disable_openapi_validation:  "false"
  	[...]
  + kubernetes_namespace.1-minikube-namespace
  	id:                      	<computed>
  	metadata.#:              	"1"
  	[...]

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Por fim, aplique o Terraform:

$ terraform apply --auto-approve
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
kubernetes_namespace.1-minikube-namespace: Creating...
  metadata.#:              	"" => "1"
  metadata.0.generation:   	"" => "<computed>"
  [...]
helm_release.local: Creating...
  atomic:                 	"" => "false"
  chart:                  	"" => "./buildachart"
  cleanup_on_fail:        	"" => "false"
  create_namespace:       	"" => "false"
 [...]
  version:                	"" => "0.1.0"
  wait:                   	"" => "true"
kubernetes_namespace.1-minikube-namespace: Creation complete after 1s (ID: helloworld)
helm_release.local: Still creating... (10s elapsed)
helm_release.local: Creation complete after 13s (ID: buildachart)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Agora você tem um backup local do estado do Terraform:

$ ls -lrt

drwxr-xr-x 6 jess jess 4096 May 16 14:15 buildachart
-rw-r--r-- 1 jess jess  363 Oct 24 13:06 main.tf
-rw-rw-r-- 1 jess jess  132 Oct 24 13:17 vars.tf
-rw-rw-r-- 1 jess jess 3897 Oct 24 13:20 terraform.tfstate.backup
-rw-rw-r-- 1 jess jess 3821 Oct 24 13:21 terraform.tfstate

Converter do Terraform 0,11 para 0,12

Depois que tudo for implantado usando o Terraform 0.11, você deverá trabalhar no processo de conversão sem danificar o que tem em seu novo cluster de produção. Primeiro, altere a versão do Terraform para 0.12 usando tfenv:

$ tfenv list
  0.12.29
* 0.11.15-oci (set by /home/jess/.tfenv/version)
jess@Athena:~/terraform_doc$ tfenv use 0.12.29
Switching default version to v0.12.29
Switching completed
jess@Athena:~/terraform_doc$ terraform --version
Terraform v0.12.29
+ provider.helm v1.2.2
+ provider.kubernetes v1.11.2

Agora que está no Terraform 0.12, você está pronto para converter. Felizmente, o Terraform possui um comando integrado para isso, conforme mostrado neste trecho da lista de comandos do Terraform:

All other commands:
	0.12upgrade    	Rewrites pre-0.12 module source code for v0.12

Teste o comando upgrade para ver o que será reescrito e digite yes para fazer a atualização:

$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12.
[...]
Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

The configuration files were upgraded successfully. Use your version control
system to review the proposed changes, make any necessary adjustments, and
then commit.

Parece que tudo correu bem. Pode ter reescrito algumas coisas, mas isso não será responsável por tudo o que precisa ser alterado nos arquivos do Terraform. No entanto, ele faz um ótimo trabalho ao fazer anotações nos arquivos. Aqui estão as alterações feitas no arquivo vars.tf:

variable "namespace" {
  type	= string
  default = "helloworld"
}

variable "cluster" {
  type	= string
  default = "minikube"
}

O type foi alterado para remover aspas ao redor da palavra string (as aspas nos arquivos Terraform mudaram com o tempo). A seguir, observe o arquivo main.tf:

provider "kubernetes" {
  config_context_cluster = var.cluster
}

# TF-UPGRADE-TODO: In Terraform v0.11 and earlier, it was possible to begin a
# resource name with a number, but it is no longer possible in Terraform v0.12.
#
# Rename the resource and run `terraform state mv` to apply the rename in the
# state. Detailed information on the `state move` command can be found in the
# documentation online: https://www.terraform.io/docs/commands/state/mv.html
resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = var.namespace
  }
}

provider "helm" {
  kubernetes {
	config_context_cluster = var.cluster
  }
}

resource "helm_release" "local" {
  name  = "buildachart"
  chart = "./buildachart"
}

As aspas também mudaram aqui e as variáveis não são mais agrupadas em caracteres $ {}. A seção maior é uma grande nota TODO que o comando de conversão colocado no código para mostrar o que precisa ser alterado no nome do recurso para ser aceitável na versão 0.12. Melhor ainda, explica como corrigir esse problema e o comando que você precisa executar. As outras mudanças importantes são o novo arquivo versions.tf e um novo arquivo de backup:

$ ls -lrt
drwxr-xr-x 6 jess jess 4096 May 16 14:15 buildachart
-rw-rw-r-- 1 jess jess 3897 Oct 24 13:20 terraform.tfstate.backup
-rw-r--r-- 1 jess jess   46 Oct 24 13:28 versions.tf
-rw-rw-r-- 1 jess jess  140 Oct 24 13:55 vars.tf
-rw-r--r-- 1 jess jess  369 Oct 24 13:56 main.tf
-rw-rw-r-- 1 jess jess 3821 Oct 24 13:56 terraform.tfstate.1603562212.backup
-rw-rw-r-- 1 jess jess 3827 Oct 24 13:56 terraform.tfstate

Para atualizar seu cluster de produção, comece com um nome diferente que seja mais adequado para esta versão:

resource "kubernetes_namespace" "upgrade-minikube-namespace" {
  metadata {
	name = var.namespace
  }
}

Suavizando as arestas

Após essa alteração, você deve executar o comando terraform state mv (conforme referenciado na grande nota TODO). Mas primeiro, execute state list para ver com o que você está trabalhando:

$ terraform state list
helm_release.local
Kubernetes_namespace.1-minikube-namespace

O namespace ainda está definido no estado como 1-minikube-namespace, e esse é o estado que você precisa mover. Isso pode ser feito garantindo que você tenha os nomes de recursos novos e antigos e, em seguida, executando o comando terraform state mv. Mas antes disso, você deve voltar para 0.11, usando tfenv para fazer essas alterações, porque isso foi implantado usando Terraform 0.11, e 0.12 não reconhece os números no início do nome do recurso (como diz o TODO). Você terá que reverter todas as alterações de código feitas pela atualização do Terraform, exceto a alteração do nome do recurso:

main.tf

provider "kubernetes" {
  config_context_cluster   = "${var.cluster}"
}

resource "kubernetes_namespace" "upgrade-minikube-namespace" {
  metadata {
	name = "${var.namespace}"
  }
}

provider "helm" {
  kubernetes {
	config_context_cluster   = "${var.cluster}"
  }
}
resource "helm_release" "local" {
  name   	= "buildachart"
  chart  	= "./buildachart"
}

Vars.tf

variable "namespace" {
  type	= "string"
  default = "helloworld"
}

variable "cluster" {
  type	= "string"
  default = "minikube"
}

Assim que as alterações estiverem em vigor, mude o tfenv de volta para a versão 0.11 e execute o comando state mv:

$ tfenv use 0.11.15-oci
Switching default version to v0.11.15-oci
Switching completed
jess@Athena:~/terraform_doc$ terraform state mv 'kubernetes_namespace.1-minikube-namespace' 'kubernetes_namespace.upgrade-minikube-namespace'
Moved kubernetes_namespace.1-minikube-namespace to kubernetes_namespace.upgrade-minikube-namespace

Etapas finais

Após a conclusão, altere o tfenv de volta para a versão 0.12 e remova o arquivo versions.tf para garantir que a conversão seja concluída. Se você não remover este arquivo, receberá uma mensagem de erro:

$ terraform 0.12upgrade
Error: Module already upgraded

  on versions.tf line 3, in terraform:
   3:   required_version = ">= 0.12"

The module in directory . has a version constraint that suggests it has
already been upgraded for v0.12. If this is incorrect, either remove this
constraint or override this heuristic with the -force argument. Upgrading a
module that was already upgraded may change the meaning of that module.

Remova o arquivo e execute o comando:

$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12. [...]

Would you like to upgrade the module in the current directory?
  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

Teste a conversão com outra implantação

Teste sua nova conversão executando novamente os comandos init, plan e apply (omiti partes da saída nesses trechos):

$ terraform init

Initializing the backend...
Initializing provider plugins...
[...]
Terraform has been successfully initialized!

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

helm_release.local: Refreshing state... [id=buildachart]
kubernetes_namespace.upgrade-minikube-namespace: Refreshing state... [id=helloworld]

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date

$ terraform apply
helm_release.local: Refreshing state... [id=buildachart]
kubernetes_namespace.upgrade-minikube-namespace: Refreshing state... [id=helloworld]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Como isso mostra, depois que tudo foi pré-configurado e o estado mudou e reaplicou durante a conversão, nenhuma outra alteração foi feita porque a infraestrutura está instalada.

Pensamentos finais

É difícil fazer atualizações de código e aplicativos, especialmente em um ambiente de produção ativo. Isso é verdade ao converter do Terraform 0,11 para 0,12. Fiz isso em grande escala e envolveu um planejamento extensivo ao longo de um período de duas semanas.

Se você for fazer isso em seu ambiente de produção, certifique-se de:

  • Comece removendo quaisquer recursos ou módulos com números prefixados.
  • Mova o estado antes de executar a atualização.
  • Mantenha seus arquivos atualizados do Terraform em um repositório bifurcado por segurança.

Espero que este artigo ajude você a avançar com mais rapidez e facilidade do que eu.

Artigos relacionados: