Terraform - czyli tfstate w akcji i nonszalancki delete post

Zarządzanie infrastrukturą w chmurze to wyzwanie. Możemy to robić ręcznie lub przy pomocy narzędzi. Dzisiaj skupmy się jednak na tym drugim rozwiązaniu, które pozwala nam wprowadzić do projektu podejście "infrastruktury jako kodu" (eng. Infrastructure as a Code). Tymczasem pozwól, że opowiem Ci moją historię, jak utraciłem możliwość używania tego podejścia przez "nonszalancki delete".

Terraform

Terraform - to narzędzie do zapisywania, planowania i uruchamiania infrastruktury jako kodu. Choć nie brzmi to zachęcająco, to ze stuprocentową pewnością mogę powiedzieć, że w każdym projekcie powinniśmy stosować takie podejście.

Sama składnia Terraforma wygląda następująco:

provider "aws" {
  access_key = "ACCESS_KEY_HERE"
  secret_key = "SECRET_KEY_HERE"
  region     = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-2757f631"
  instance_type = "t2.micro"
}

Gdzie przy pomocy zasobów (eng. resource) możemy zarządzać naszą infrastrukturą. Więcej o pracy z Terraformem dowiesz się z dokumentacji.

Stan infrastruktury i cała historia

Teraz pozwól, że przejdę do clue mojej historii. Komputery nie są autonomiczne i nie potrafią same myśleć. Oczywiście potrafią wykonywać nasze polecenia w mniejszym lub większym stopniu. Podobnie działa Terraform.

Pomimo, że cały zapis naszych zasobów AWSowych jest w plikach *.tf to Terraform musi utworzyć plik stanu, w którym przechowuje informacje o mapowaniu zasobów w kodzie na odpowiednie zasoby w chmurze. Taki stan przechowywany jest w pliku z rozszerzeniem *.tfstate. Można byłoby powiedzieć, że to podobne rozwiązanie do composer.lock, package.lock.json, Gemfile.lock itd.

Po usunięciu powyższych plików zachowanie menadżerów jest podobne. I nie bądź zdziwiony, to samo dzieje się z Terraformem. Wystarczy, że usuniesz plik, który zawiera informacje o stanie naszej infrastruktury i Terraform będzie próbował tworzyć ją od nowa.

Ludzie dzielą się na tych co robią kopie zapasowe i na tych, którzy będą je robić.

W moim przypadku plik *.tfstate przechowywany był na buckecie S3 (bez wersjonowania). Wydawałoby się, że jest tam bezpieczny, ale nic mniej mylnego. Nawet tam go dopadłem i usunąłem ;-)

Konsternacja, niezrozumienie, co ja zrobiłem, że nie działa?

Szybko doszedłem gdzie popełniłem błąd i po chwili googlowania wiedziałem, że pliku nie odzyskam. Rozpoczęła się walka Terraformem, jak odzyskać środowisko, na którym stoją serwery CI, jenkins i inne narzędzia deweloperskie.

Całe szczęście Terraform ma w sobie funkcje, które pozwalają przypinać kod do infrastruktury.

terraform state rm - # usuwa stan danego zasobu
terraform import # importuje zasób

Dwie powyższe komendy były dla mnie kluczowe. Ale od czego zacząłem?

  1. Właczyłem wersjonowanie na kubełku (S3 bucket), w którym trzymam pliki *.tfstate
  2. Zrobiłem analizę tego co nie się nie mapuje. Dzięki komendzie terraform plan dostałem listę wszystkich zasobów z modułów, które muszę zmapować.
  3. Przygotowałem sobie mapping listę
  4. Napisałem skrypt bashowy, który zaimportuje je automatycznie
  5. Następnie odpaliłem terraform refresh && terraform apply - aby terraform tam gdzie nie mógł automatycznie, to ręcznie utworzył asocjacje
  6. Usunąłem zbędne zasoby np. RouteTables - które pozostały puste po nowych asocjacjach
#!/usr/bin/env bash

function reimport() {
    key=$1
    value=$2
    state=$(terraform state show "$key" | grep id | head -n 1 | awk '{ print $3 }' )
    if ! [ "$state" = "$value" ]; then
        echo -e "Removing state for ${key}: ${value}"
        terraform state rm "$key"
        echo -e "Importing state for ${key}: ${value}"
        terraform import "$key" "$value"
    else
        echo "Resource ${key}:${value} already imported"
    fi
}

reimport module.vpc.aws_vpc.main[0] vpc-XXXXXX
reimport module.vpc.aws_internet_gateway.main[0] igw-XXXXXX

reimport module.vpc.aws_subnet.private[0] subnet-XXXXXX
reimport module.vpc.aws_subnet.public[0] subnet-XXXXXX

reimport module.vpc.aws_route_table.public rtb-XXXXXX
reimport module.vpc.aws_route_table.private[0] rtb-XXXXXX

Niestety u mnie liczba zasobów była dość spora, dlatego sam proces odtworzenia zajął mi około 8 godzin roboczych.

Linki

  • https://www.terraform.io/docs/
  • https://www.terraform.io/docs/commands/state/index.html
  • https://www.terraform.io/docs/backends/types
  • http://someguys.blog/2017-04-26-recovering-terraform-state/

Kategorie: cloud

Tagi: cloud, aws, terraform, hashicorp