Przejdź do treści
Automatyzacja i DevOps · · ~18 min czytania

Terraform od podstaw: bezpieczny lab, plan, state i backend

Jak bezpiecznie przejść od pierwszego zasobu w labie do powtarzalnej pracy ze stanem Terraform w zespole.

Autor: Zespół LinuxLab

Terraform opisuje infrastrukturę w plikach tekstowych i przez API doprowadza ją do oczekiwanego stanu. Ten przewodnik zaczyna się od bezpiecznego labu: Terraform tworzy tylko jeden plik w nowym katalogu tymczasowym na komputerze administratora. Dzięki temu można zobaczyć cały cykl init → plan → apply → destroy, nie używając tokenu API, serwera ani środowiska produkcyjnego.

Kiedy Terraform ma sens, a kiedy nie?

Terraform jest narzędziem do infrastruktury jako kodu (IaC, Infrastructure as Code). Administrator zapisuje w plikach .tf, jaki stan ma osiągnąć infrastruktura, a Terraform oblicza różnicę i wywołuje API wybranej platformy. Typowe obiekty to maszyny wirtualne (VM), sieci, rekordy DNS, reguły firewalla, wolumeny i konta usługowe.

Sięgnij po Terraform, gdy zasoby mają być powtarzalne, przeglądane w Git i odtwarzane w więcej niż jednym środowisku. Nie jest to najlepsze narzędzie do jednorazowej korekty na serwerze ani do konfiguracji pakietów i usług wewnątrz już uruchomionej VM. Tę drugą część zwykle realizuje Ansible.

Dobry przypadek użycia

Zespół utrzymuje po trzy VM dla dev, stage i prod. Kod opisuje ich rozmiar, sieć i DNS, a każda zmiana ma plan do przeglądu.

Przypadek, który wymaga innego narzędzia

Na istniejącym serwerze trzeba poprawić plik nginx.conf lub zaktualizować pakiet. Terraform może utworzyć VM, ale konfigurację systemu lepiej wdrożyć playbookiem Ansible.

Co oznaczają provider, resource, state, plan, apply i destroy?

Te pojęcia pojawiają się przed pierwszym poleceniem. Ich rozróżnienie pozwala ocenić, co Terraform rzeczywiście zmieni.

Pojęcie Znaczenie w praktyce
Konfiguracja HCLKod w plikach .tf. HCL (HashiCorp Configuration Language) opisuje oczekiwany wynik, a nie ręczną sekwencję kliknięć.
ProviderWtyczka komunikująca Terraform z konkretnym API lub lokalnym systemem. Provider Hetzner tworzy zasoby w Hetzner Cloud; provider local tworzy plik na komputerze, na którym uruchomiono Terraform.
ResourceZasób zarządzany przez Terraform. Ma adres, na przykład local_file.status, oraz pełny cykl życia: utworzenie, zmianę i usunięcie.
StateStan, zwykle plik terraform.tfstate, łączy adres zasobu z rzeczywistym obiektem i jego atrybutami. Nie jest to kopia całej infrastruktury.
PlanPropozycja różnicy między konfiguracją, stanem i odczytem providera. Polecenie terraform plan samo nie tworzy ani nie usuwa zasobów.
ApplyWykonanie zatwierdzonego planu. To na tym etapie Terraform wywołuje API i zmienia zasoby.
DestroyPlanowane usunięcie zasobów zapisanych w stanie. Nie jest uniwersalnym przyciskiem cofania, ponieważ usunięte dane mogą nie mieć kopii.

W konfiguracji można także użyć bloku data. Data source odczytuje istniejący obiekt, którym Terraform nie zarządza, na przykład istniejącą sieć lub obraz systemu. Nie tworzy go ani nie usuwa.

Co przygotować przed pierwszym uruchomieniem?

Do poniższego labu potrzebny jest tylko komputer administratora z dostępem do Internetu. Nie potrzeba konta w chmurze, uprawnień root ani tokenu API.

  1. Zainstaluj Terraform zgodnie z oficjalną instrukcją HashiCorp i sprawdź, czy program jest dostępny w PATH.
  2. Uruchom lab w nowym katalogu tymczasowym. Nie dodawaj jego plików do repozytorium z produkcyjną konfiguracją.
  3. Przed pracą z prawdziwym providerem przygotuj osobne konto lub projekt labowy z minimalnymi uprawnieniami. Pierwszy apply nie powinien dotyczyć produkcji.
# Na komputerze administratora; polecenie tylko odczytuje wersję.
terraform version

Warunkiem przejścia dalej jest wypisanie wersji Terraform. Komunikat command not found oznacza, że instalację lub PATH trzeba naprawić przed tworzeniem konfiguracji.

Jak utworzyć bezpieczny lab bez sekretów?

Poniższe polecenia tworzą nowy katalog w /tmp, przechodzą do niego i zakładają podkatalog na plik zarządzany przez Terraform. Zmienna LAB_TERRAFORM_DIR zawiera ścieżkę utworzoną przez mktemp; nie wskazuje istniejącego katalogu projektu.

# Na komputerze administratora; tworzy wyłącznie świeży katalog laboratoryjny.
LAB_TERRAFORM_DIR="$(mktemp -d -t linuxlab-terraform.XXXXXX)"
printf 'Katalog labu: %s\n' "$LAB_TERRAFORM_DIR"
cd "$LAB_TERRAFORM_DIR"
mkdir generated

Teraz zapisz konfigurację. Blok terraform wybiera provider local; blok resource definiuje plik, a blok output zwróci jego ścieżkę po wykonaniu. Polecenie zapisuje main.tf tylko w świeżo utworzonym katalogu labu.

tee main.tf >/dev/null <<'EOF'
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

resource "local_file" "status" {
  filename        = "${path.module}/generated/terraform-ok.txt"
  content         = "Terraform utworzył ten plik.\n"
  file_permission = "0644"
}

output "created_file" {
  value = local_file.status.filename
}
EOF

W rzeczywistym repozytorium dodaj plik .gitignore zanim pojawi się stan. Plik .terraform.lock.hcl jest wyjątkiem: zawiera wybrane wersje providerów i ich sumy kontrolne, dlatego zwykle trafia do Git razem z konfiguracją.

tee .gitignore >/dev/null <<'EOF'
.terraform/
*.tfstate
*.tfstate.*
*.tfplan
*.tfvars
*.auto.tfvars
EOF

Co robią init, fmt i validate przed zmianą?

terraform init pobiera zadeklarowany provider i przygotowuje backend stanu. W tym labie backend jest lokalny, więc po wykonaniu pojawią się katalog .terraform oraz lockfile. terraform fmt -check sprawdza format kodu, a terraform validate sprawdza składnię i zależności między blokami. Żadne z tych poleceń nie tworzy pliku terraform-ok.txt.

# Nadal w katalogu wyświetlonym jako LAB_TERRAFORM_DIR.
terraform init
terraform fmt -check
terraform validate
terraform providers

Oczekiwany wynik to komunikaty o udanej inicjalizacji i poprawnej konfiguracji oraz provider registry.terraform.io/hashicorp/local. Jeśli init nie może pobrać providera, sprawdź połączenie z rejestrem, proxy albo wewnętrzny mirror providerów; nie omijaj tego problemu przez ręczne kopiowanie katalogu .terraform między projektami.

Jak przeczytać plan i wykonać dokładnie tę zmianę?

Plan jest obowiązkowym punktem kontroli. W labie powinien zapowiedzieć dodanie jednego zasobu, bez zmian i bez usuwania. Zapisanie planu do pliku pozwala po przeglądzie wykonać dokładnie tę wersję planu, a nie obliczać go ponownie w chwili apply.

# To polecenie niczego jeszcze nie tworzy.
terraform plan -out=tfplan

# Przed apply przeczytaj zapisany plan; może zawierać dane wrażliwe.
terraform show -no-color tfplan

W tym labie szukaj końca Plan: 1 to add, 0 to change, 0 to destroy. i adresu local_file.status. Inny wynik oznacza, że katalog nie jest świeży albo konfiguracja została zmieniona. Zatrzymaj się, ustal przyczynę i wygeneruj nowy plan zamiast akceptować nieoczekiwaną operację.

# Wykonuje wcześniej zapisany i przeczytany plan.
terraform apply tfplan

Sukces potwierdza komunikat podobny do Apply complete! Resources: 1 added, 0 changed, 0 destroyed.. W produkcji zapisany plan również jest artefaktem wrażliwym: przechowuj go tylko w kontrolowanym systemie CI/CD lub chronionym miejscu i nie wysyłaj go w zgłoszeniu bez sprawdzenia zawartości.

Dlaczego state jest ważny i jak reagować na drift?

Po pierwszym apply Terraform tworzy lokalny plik terraform.tfstate. Stan zapisuje, że adres local_file.status odpowiada konkretnemu plikowi. Przy VM będzie przechowywał identyfikator VM i atrybuty potrzebne do porównania oczekiwanego stanu z rzeczywistością.

# W tym labie te polecenia wyłącznie odczytują lokalny stan.
terraform state list
terraform state show local_file.status

Drugie polecenie może wyświetlić wartości zapisane przez provider. W labie są nieszkodliwe, ale na produkcji stan i zapisane plany należy traktować jak dane wrażliwe. Nie edytuj pliku terraform.tfstate ręcznie. Gdy Terraform utraci stan, zasób może nadal istnieć u dostawcy, ale Terraform nie będzie wiedział, że nim zarządza.

Drift to rozjazd między kodem a rzeczywistą infrastrukturą, na przykład ręcznie zmieniony rozmiar VM. Świeży terraform plan ujawnia ten rozjazd. Następnie administrator podejmuje decyzję: zapisać uzasadnioną zmianę w kodzie, przywrócić oczekiwany stan przez kontrolowany apply, albo zaimportować istniejący zasób do stanu. Nie akceptuj planu tylko po to, aby „wyczyścić” nieznaną różnicę.

Jak sprawdzić wynik i usunąć lab?

Po apply nie zakładaj, że sam komunikat CLI wystarcza. Sprawdź obiekt z perspektywy systemu operacyjnego, odczytaj output i uruchom świeży plan. W prawdziwym wdrożeniu odpowiednikiem tych poleceń będzie test HTTP, zapytanie do API albo monitoring usługi.

# Na komputerze administratora; wszystkie polecenia są tylko odczytem.
test -f generated/terraform-ok.txt && printf 'Plik istnieje\n'
cat generated/terraform-ok.txt
terraform output created_file
terraform plan -detailed-exitcode

Warunkiem sukcesu są: komunikat Plik istnieje, treść pliku z konfiguracji, ścieżka z outputu oraz plan z komunikatem No changes. Przy braku zmian terraform plan -detailed-exitcode kończy się kodem 0; kod 2 oznacza wykrytą zmianę, a kod 1 błąd.

Aby usunąć tylko zasób tego labu, najpierw zapisz i przeczytaj plan usunięcia. Nie używaj destroy jako automatycznej procedury cofania w produkcji.

# Ten plan zapowiada usunięcie lokalnego pliku.
terraform plan -destroy -out=destroy.tfplan
terraform show -no-color destroy.tfplan

# Wykonaj dopiero po potwierdzeniu, że plan usuwa tylko local_file.status.
terraform apply destroy.tfplan
test ! -e generated/terraform-ok.txt && printf 'Plik labu został usunięty\n'

Po teście zostaną katalog .terraform, lockfile, pliki planów i lokalny stan. Pozostaw je do inspekcji albo usuń cały katalog laboratoryjny ręcznie dopiero po sprawdzeniu jego ścieżki wyświetlonej wcześniej przez mktemp.

Jak wycofać nieudaną zmianę? Wróć w Git do znanej konfiguracji, wygeneruj świeży plan i przeczytaj go tak samo jak zmianę pierwotną. Terraform może odtworzyć zasób opisany w kodzie, ale nie odzyska danych usuniętych razem z dyskiem, bazą lub obiektem storage. Dlatego rollback aplikacji i odtworzenie danych wymagają osobno przetestowanej kopii zapasowej.

Kiedy potrzebny jest zdalny backend i blokada state?

Lokalny stan jest wystarczający wyłącznie dla jednego administratora i krótkiego, odtwarzalnego labu. Gdy ten sam kod uruchamia zespół, system CI/CD (automatyzujący budowanie i wdrażanie) albo środowisko produkcyjne, stan musi być dostępny w kontrolowanym backendzie zdalnym. Zniknięcie laptopa lub równoczesne apply nie może wtedy rozdzielić historii infrastruktury na dwa pliki.

SytuacjaWłaściwy sposób pracy
Jednorazowy lab na własnej stacjiLokalny state jest dopuszczalny, jeśli katalog można bezpiecznie wyrzucić i nie zawiera sekretów.
Wspólny projekt, CI/CD lub produkcjaBackend zdalny, ograniczony dostęp, szyfrowanie, wersjonowanie i kopie stanu.
Dwie osoby mogą uruchomić zmianę równocześnieBackend z blokadą. Pierwsze apply blokuje state; drugie musi czekać albo bezpiecznie zakończyć się błędem.

Backend S3 może używać natywnej blokady przez use_lockfile. To jest wzorzec dla przygotowanego storage, a nie konfiguracja do wklejenia do labu: bucket, uprawnienia, region, wersja Terraform i polityka szyfrowania muszą odpowiadać środowisku.

terraform {
  backend "s3" {
    bucket       = "organizacja-terraform-state"
    key          = "projekt-prod/terraform.tfstate"
    region       = "eu-central-1"
    encrypt      = true
    use_lockfile = true
  }
}

Przed migracją przygotuj bucket, zasadę dostępu o najmniejszych uprawnieniach, wersjonowanie i sposób odzyskania poprzedniej wersji stanu. Nie zapisuj poświadczeń do backendu w tym bloku; przekaż je bezpiecznym mechanizmem środowiska lub CI. Po zatwierdzeniu konfiguracji uruchom terraform init -migrate-state, potwierdź docelowy backend i wykonaj nowy terraform plan. Sprawdź też blokadę w oddzielnym labie, ponieważ jej obsługa zależy od wybranego backendu.

Wymagania backendu S3 opisuje oficjalna dokumentacja HashiCorp, a zasady blokowania stanu - dokumentacja state locking.

Jak nie umieścić sekretów w kodzie ani state?

Token API, hasło administratora lub prywatny klucz mogą trafić do state, nawet jeśli kod ich nie zawiera. Oznaczenie wartości jako sensitive ukrywa ją w zwykłym wyjściu Terraform, ale nie gwarantuje usunięcia ze stanu ani z pliku planu.

variable "api_token" {
  description = "Token przekazywany bezpośrednio przez bezpieczny mechanizm wykonawczy"
  type        = string
  sensitive   = true
}
  • Nie zapisuj sekretu w plikach .tf, .tfvars, historii poleceń ani w komunikatorze. Przekazuj go z menedżera sekretów, bezpiecznych zmiennych CI/CD albo mechanizmu uwierzytelniania zalecanego przez provider.
  • Nie commituj terraform.tfstate, plików planu ani prywatnych plików .tfvars. Lockfile providerów commituj, ponieważ nie zawiera sekretów.
  • Ogranicz dostęp do backendu stanu tak samo restrykcyjnie jak dostęp do API infrastruktury. Szyfrowanie nie zastępuje kontroli uprawnień i audytu dostępu.

Jeżeli provider oferuje argument write-only lub wartość ephemeral, sprawdź jego dokumentację i użyj go tam, gdzie pozwala nie zapisywać sekretu w stanie. Ogólne zasady omawia dokumentacja danych wrażliwych Terraform.

Jak podzielić pracę między Terraform, moduły i Ansible?

Gdy lab działa, konfigurację produkcyjną dzielimy na małe, wersjonowane komponenty. Moduł Terraform jest katalogiem z zasobami, zmiennymi wejściowymi i outputami, który można wielokrotnie wykorzystać. Na przykład moduł VM może przyjmować nazwę, typ i sieć, a zwracać adres IP. Moduł ma sens dopiero po powtórzeniu sprawdzonego wzorca; nie trzeba tworzyć modułu dla jednego prostego zasobu.

Terraform odpowiada zwykle za to, co ma istnieć: VM, sieć, wolumen, rekord DNS i regułę firewall. Ansible odpowiada za to, jak ma działać istniejący host: pakiety, użytkownicy, usługi i pliki aplikacji. Output Terraform, na przykład adres IP nowej VM, może zasilić inventory lub inny etap automatyzacji. Nie oznacza to, że każdą zmianę trzeba dzielić między dwa narzędzia; granicę wyznacza API infrastruktury i odpowiedzialność zespołu.

Więcej o konfiguracji hostów opisuje przewodnik po Ansible.

Jak wygląda bezpieczna procedura Terraform dla produkcji?

  1. Zmodyfikuj kod w osobnym branchu i sprawdź go przez terraform fmt -check oraz terraform validate.
  2. Uruchom terraform init z ustalonym backendem. Nie używaj -upgrade bez świadomej aktualizacji providera i przeglądu lockfile.
  3. Wygeneruj zapisany plan, przeczytaj liczbę dodawanych, zmienianych i usuwanych zasobów oraz zleć jego przegląd drugiej osobie przy zmianie o większym ryzyku.
  4. Wykonaj tylko zaakceptowany plan, a potem przetestuj rzeczywisty efekt: usługę, połączenie sieciowe, backup albo metrykę, która ma znaczenie dla danego zasobu.
  5. Jeśli wynik jest błędny, wróć do zaakceptowanej konfiguracji, zbuduj nowy plan i zastosuj go po sprawdzeniu. Dla danych korzystaj z osobno przetestowanego backupu, nie z założenia, że destroy wszystko odwróci.

Terraform daje powtarzalność tylko wtedy, gdy kod, state, uprawnienia i test wyniku są traktowane jako jedna procedura. Bezpieczny lab z tego artykułu pozwala poznać jej mechanikę, zanim pierwszy provider dotknie prawdziwej infrastruktury.

Potrzebujesz powtarzalnej, odpornej na awarie infrastruktury?

Projektujemy i utrzymujemy infrastrukturę jako kod (Terraform + Ansible), która ogranicza awarie i przyspiesza wdrożenia. Porozmawiajmy o Twoim środowisku - bezpłatnie i bez zobowiązań.