Serwer produkcyjny postawiono rok temu, klikając w panelu dostawcy. Działa. Ale nikt nie pamięta, jaki dokładnie wybrano rozmiar maszyny, które porty otwarto i czy dysk ma 50 czy 100 gigabajtów. Postawienie identycznego środowiska testowego to teraz kilka godzin zgadywania - i tak wyjdzie inaczej.
Rzymianie nie budowali akweduktów z pamięci. Każdy zaczynał się od planu architektonicznego: rysunku, z którego dowolna ekipa mogła odtworzyć tę samą konstrukcję w innej prowincji. Infrastructure as Code to ten sam pomysł: infrastruktura definiowana w kodzie, a nie klikaniem.
Nie myl tego z narzędziami, które brzmią podobnie, a robią co innego: to nie framework do testowania infrastruktury, nie system monitorowania serwerów i nie narzędzie do kompresji obrazów Dockera. To sposób opisania tego, co ma istnieć.
Terraform czyta pliki z rozszerzeniem
.tf. Opis składa się z kilku rodzajów bloków:1provider "aws" {
2 region = "eu-central-1"
3}
4
5variable "app_name" {
6 type = string
7 default = "legion-api"
8}
9
10variable "app_port" {
11 type = number
12 default = 3000
13}
14
15resource "aws_instance" "legion_server" {
16 ami = "ami-0abcdef1234567890"
17 instance_type = "t3.micro"
18
19 tags = {
20 Name = var.app_name
21 }
22}
23
24output "server_ip" {
25 value = aws_instance.legion_server.public_ip
26}
mówi, u kogo budujemy - tutaj AWS w regionie provider
eu-central-1. variable to parametr planu; dzięki niemu ten sam opis postawi środowisko testowe i produkcyjne, różniące się tylko wartościami. resource opisuje rzecz, która ma istnieć - maszynę, sieć, bazę. output wskazuje, co Terraform ma pokazać po zakończeniu; adres IP zwykle poznajesz dopiero po utworzeniu serwera.Zwróć uwagę na sposób odwoływania się:
var.app_name sięga po zmienną, a aws_instance.legion_server.public_ip - po pole utworzonego zasobu. Z tych odwołań Terraform sam wyprowadza kolejność tworzenia: skoro output potrzebuje adresu maszyny, maszyna musi powstać wcześniej.Praca z Terraformem to zawsze ta sama sekwencja:
terraform init - inicjalizacja projektu; pobiera providery wymienione w plikach.terraform plan - podgląd zmian bez ich stosowania.terraform apply - zastosowanie zmian.terraform output - sprawdzenie wyników.Drugi krok jest sercem całego narzędzia, więc nazwijmy go wprost:
to DRY RUN. Porównuje Twój opis ze stanem faktycznym i wypisuje, co zamierza utworzyć, zmienić i usunąć - nie ruszając niczego. Nie tworzy zasobów, nie usuwa infrastruktury i nie pobiera providerów; od pobierania jest terraform plan
init.Ten podgląd jest jedyną chwilą, w której zobaczysz zamiar narzędzia, zanim stanie się faktem. Czytaj go, @name - zwłaszcza linie zaczynające się od minusa, bo Terraform potrafi uznać, że pewnej zmiany nie da się wykonać w miejscu, i usunąć zasób, żeby utworzyć go na nowo. Przy maszynie testowej to niedogodność; przy bazie z danymi - katastrofa.
W zespole i w CI stosuje się wariant dwuetapowy, żeby mieć pewność, że wdrażasz dokładnie to, co przejrzałeś:
1terraform init
2terraform plan -out=tfplan
3terraform apply tfplan
4terraform output-out=tfplan zapisuje wyliczony plan do pliku. apply tfplan stosuje dokładnie ten plan, zamiast wyliczać go na nowo.Różnica ma znaczenie, gdy między przeglądem a zatwierdzeniem minie kilka minut, a ktoś w tym czasie zmieni infrastrukturę. Zwykłe
apply policzyłoby wtedy plan od nowa i zrobiło coś innego, niż przeczytałeś na ekranie.Terraform musi pamiętać, co już utworzył - inaczej przy każdym uruchomieniu stawiałby wszystko od zera. Zapisuje to w pliku
.terraform.tfstate
Ten plik nie należy do repozytorium Git, i to z dwóch powodów naraz. Po pierwsze zawiera wrażliwe dane - hasła do baz, klucze, adresy - zapisane jawnym tekstem, bo Terraform musi je pamiętać między uruchomieniami. Po drugie Git nie zapewnia blokady przy pracy zespołowej: gdy dwie osoby uruchomią
apply jednocześnie, powstaną dwie wersje stanu i konflikt, którego nie da się rozstrzygnąć scaleniem, bo to nie jest plik do czytania przez człowieka.Powody, które czasem się słyszy - że plik jest za duży dla Gita albo że Terraform sam go usuwa po
apply - są nieprawdziwe.Rozwiązaniem jest backend zdalny: wspólne miejsce, które trzyma stan i blokuje go na czas operacji.
1terraform {
2 backend "s3" {
3 bucket = "imperium-terraform-state"
4 key = "legion-api/terraform.tfstate"
5 region = "eu-central-1"
6 dynamodb_table = "terraform-locks"
7 }
8}Tabela wskazana w
dynamodb_table służy właśnie do blokady - druga osoba uruchamiająca apply zobaczy komunikat, że stan jest zajęty, zamiast zepsuć wspólną infrastrukturę.Plan architektoniczny gotowy, prowincje odtwarzalne:
.tf zawierają bloki: provider (u kogo), variable (parametry), resource (co ma istnieć), output (co pokazać po zakończeniu),terraform init → terraform plan → terraform apply → terraform output,terraform plan to DRY RUN - pokazuje podgląd zmian bez ich stosowania; nie tworzy, nie usuwa i nie pobiera providerów,plan -out=tfplan, potem apply tfplan - wdrażasz dokładnie to, co przejrzałeś,terraform.tfstate nie trafia do Gita: zawiera wrażliwe dane i nie ma blokady dla pracy zespołowej,W następnej lekcji zbierzesz cały ten moduł w jeden projekt - konteneryzację, pipeline i wdrożenie. A na razie zapamiętaj: klikanie w panelu tworzy serwer, którego nikt nie umie odtworzyć; plan w pliku tworzy taki, który odtworzy każdy.