W LinuxLab korzystamy z Terraform, aby przyspieszać i automatyzować powtarzalne zadania związane z infrastrukturą — tworzenie serwerów, sieci, reguł firewalla, rekordów DNS czy certyfikatów. Cała infrastruktura jest opisana w kodzie, wersjonowana w git i odtwarzalna jednym poleceniem. To przekłada się na skuteczne zapobieganie awariom serwerów, którymi się opiekujemy: eliminujemy ręczne pomyłki w panelach, utrzymujemy spójne środowiska i potrafimy odtworzyć całą platformę w minuty, a nie w godziny.
W tym artykule wyjaśniamy, czym jest Terraform i jak w praktyce zarządzać nim infrastrukturą jako kodem (IaC) — od języka HCL i providerów, przez zasoby i kluczowe pojęcie stanu (state), cykl init / plan / apply, zmienne i outputy, po moduły i bezpieczne przechowywanie sekretów. Pokazujemy też, jak Terraform i Ansible uzupełniają się w pracy LinuxLab i jak razem ograniczają liczbę awarii.
Spis treści
Czym jest Terraform?
Terraform to narzędzie do zarządzania infrastrukturą jako kodem (Infrastructure as Code). Zamiast klikać w panelach dostawców chmury czy hostingu, opisujemy pożądany stan infrastruktury w plikach tekstowych, a Terraform tworzy, zmienia i usuwa zasoby tak, aby rzeczywistość odpowiadała temu opisowi.
Trzy cechy, które wyróżniają je w praktyce:
- Deklaratywność — opisujemy co ma istnieć („serwer o tych parametrach, z tym adresem IP"), a nie krok po kroku jak to utworzyć. Terraform sam wylicza, jakie operacje wykonać.
- Niezależność od dostawcy — ten sam język (HCL) opisuje zasoby w różnych chmurach i usługach dzięki providerom: AWS, Azure, GCP, Hetzner, OVH, Cloudflare, DNS, bazy danych i wiele innych.
- Świadomość stanu — Terraform pamięta, co już utworzył, dzięki czemu wie, co dodać, a czego nie ruszać. To fundament bezpiecznych, powtarzalnych zmian.
Warto od razu rozróżnić role: Terraform tworzy infrastrukturę (provisioning — serwery, sieci, DNS), a narzędzia takie jak Ansible konfigurują to, co już istnieje (pakiety, usługi, pliki). Wracamy do tego na końcu artykułu.
Język HCL — opis infrastruktury
Konfigurację piszemy w HCL (HashiCorp Configuration Language) — czytelnym języku opartym na blokach. Pliki mają rozszerzenie .tf, a Terraform wczytuje wszystkie pliki .tf z bieżącego katalogu. Podstawowy blok wygląda tak:
# main.tf — typowy blok zasobu
resource "hcloud_server" "web" {
name = "web-01"
server_type = "cx22"
image = "debian-12"
location = "nbg1"
}
Składnia resource "<typ>" "<nazwa_lokalna>" definiuje zasób danego typu, a w nawiasach klamrowych podajemy jego atrybuty. Do zasobu odwołujemy się później przez hcloud_server.web.
Providery — wtyczki do API
Provider to wtyczka, która tłumaczy konfigurację HCL na wywołania API konkretnej platformy. To provider „wie", jak utworzyć serwer w Hetznerze albo rekord w Cloudflare. Wymagane providery i ich wersje deklarujemy jawnie — dzięki temu zespół zawsze pracuje na tych samych, przewidywalnych wersjach.
# versions.tf
terraform {
required_version = ">= 1.6"
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.45"
}
}
}
# providers.tf
provider "hcloud" {
token = var.hcloud_token # sekret przekazany przez zmienną
}
Zasoby i data sources
Zasób (resource) to obiekt zarządzany przez Terraform — serwer, wolumen, sieć, rekord DNS. Terraform odpowiada za jego pełny cykl życia: utworzenie, aktualizację i usunięcie.
Data source (data) służy z kolei do odczytu istniejących, niezarządzanych przez nas informacji — np. najnowszego obrazu systemu — bez ich modyfikowania. Zasoby można też ze sobą łączyć, odwołując się do atrybutów: Terraform sam wykryje zależności.
# Odczyt istniejącej sieci (data source)
data "hcloud_network" "core" {
name = "core-net"
}
# Zasób korzystający z atrybutu innego obiektu
resource "hcloud_server_network" "web_net" {
server_id = hcloud_server.web.id
network_id = data.hcloud_network.core.id
}
Stan (state) — pamięć Terraform
Stan (plik terraform.tfstate) to mapa łącząca obiekty z konfiguracji z realnymi zasobami u dostawcy (po ich identyfikatorach). To dzięki stanowi Terraform wie, że hcloud_server.web to konkretny, już istniejący serwer — a nie powód, by tworzyć kolejny.
Stan jest krytyczny i wrażliwy, dlatego w pracy zespołowej trzymamy go w backendzie zdalnym (np. S3, Terraform Cloud) z blokadą (locking), która zapobiega równoczesnym zmianom przez dwie osoby:
# backend.tf — stan zdalny z blokadą
terraform {
backend "s3" {
bucket = "linuxlab-tfstate"
key = "klient-x/prod.tfstate"
region = "eu-central-1"
dynamodb_table = "tf-locks" # blokada współbieżności
encrypt = true
}
}
Dlaczego to ważne dla stabilności?
Zdalny, zaszyfrowany stan z blokadą oznacza jedno wspólne „źródło prawdy" o infrastrukturze. Nikt nie nadpisze zmian kolegi, a w razie awarii odtworzenie środowiska to kwestia ponownego apply, a nie żmudnego odtwarzania konfiguracji z pamięci.
Cykl pracy: init, plan, apply
Codzienna praca z Terraform sprowadza się do trzech poleceń:
# 1. Inicjalizacja — pobranie providerów i konfiguracja backendu
terraform init
# 2. Plan — pokaż, co dokładnie się zmieni (bez wprowadzania zmian)
terraform plan
# 3. Apply — wprowadź zmiany po zatwierdzeniu
terraform apply
Najważniejszy z punktu widzenia bezpieczeństwa jest terraform plan. Pokazuje dokładnie, co zostanie utworzone (+), zmienione (~) lub usunięte (-), zanim cokolwiek się wydarzy:
# hcloud_server.web will be updated in-place
~ resource "hcloud_server" "web" {
id = "12345678"
~ server_type = "cx22" -> "cx32"
# (pozostałe atrybuty bez zmian)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Ten przegląd to nasza „siatka ochronna": zmianę zatwierdzamy świadomie, a niebezpieczne operacje (np. 1 to destroy na produkcji) wychwytujemy zanim się wykonają. Do trwałego usuwania środowiska służy terraform destroy — używane głównie dla środowisk testowych.
Zmienne i outputy
Aby konfiguracja była elastyczna, oddzielamy logikę od danych za pomocą zmiennych (variable). To pozwala tym samym kodem stawiać środowiska dev, staging i prod, zmieniając jedynie wartości.
# variables.tf
variable "server_type" {
description = "Typ serwera Hetzner"
type = string
default = "cx22"
}
variable "hcloud_token" {
description = "Token API Hetzner Cloud"
type = string
sensitive = true # nie pokazuj wartości w logach
}
Wartości przekazujemy w plikach *.tfvars (np. prod.tfvars) lub przez zmienne środowiskowe TF_VAR_*. Z kolei outputy (output) zwracają istotne wartości po zakończeniu — np. adres IP utworzonego serwera, który przekażemy dalej do konfiguracji:
# outputs.tf
output "web_ip" {
value = hcloud_server.web.ipv4_address
}
Moduły — ponowne użycie
Gdy konfiguracja rośnie, porządkujemy ją w moduły — samodzielne, wielokrotnego użytku paczki zasobów o zdefiniowanych wejściach (zmienne) i wyjściach (outputy). Moduł raz napisany i przetestowany wdrażamy u wielu klientów, zachowując ten sam standard.
modules/
└── webserver/
├── main.tf # zasoby modułu
├── variables.tf # wejścia modułu
└── outputs.tf # wyjścia modułu
# Użycie modułu w głównej konfiguracji:
module "web_prod" {
source = "./modules/webserver"
server_type = "cx32"
environment = "prod"
}
Moduły to nasz sposób na standaryzację: poprawka wprowadzona raz w module propaguje się wszędzie tam, gdzie jest używany — spójnie i bez przeoczeń. To bezpośredni odpowiednik ról w Ansible.
Idempotencja i graf zależności
Terraform jest idempotentny: wielokrotne apply tej samej konfiguracji daje ten sam efekt co jednokrotne. Jeśli infrastruktura jest już w pożądanym stanie, plan pokaże „No changes" i nic się nie wydarzy. To pozwala bezpiecznie uruchamiać Terraform w dowolnym momencie, by wykryć i naprawić rozjazd konfiguracji (drift) — gdy ktoś zmienił coś ręcznie poza kodem.
Pod spodem Terraform buduje graf zależności między zasobami. Sam ustala właściwą kolejność (najpierw sieć, potem serwer w tej sieci) i zrównolegla operacje tam, gdzie to bezpieczne. Nie musimy ręcznie pilnować kolejności — wystarczy, że zasoby odwołują się do siebie.
Bezpieczeństwo i sekrety
Konfiguracja często wymaga danych wrażliwych: tokenów API, haseł, kluczy. Kilka zasad, których pilnujemy:
- Nigdy nie commitujemy sekretów do repozytorium. Tokeny przekazujemy przez zmienne środowiskowe
TF_VAR_*lub menedżer sekretów (np. Vault), a pliki*.tfvarsz wartościami dodajemy do.gitignore. - Oznaczamy zmienne i outputy jako
sensitive, aby Terraform nie wypisywał ich wartości w logach i planie. - Szyfrujemy i zabezpieczamy stan — plik
tfstatemoże zawierać wartości wrażliwe, dlatego trzymamy go w zdalnym backendzie z szyfrowaniem i ograniczonym dostępem, nigdy w repozytorium.
Terraform a Ansible — jak łączymy je w LinuxLab
Terraform i Ansible nie konkurują — uzupełniają się, a my używamy ich razem:
- Terraform tworzy i utrzymuje infrastrukturę: serwery, sieci, firewall, DNS, certyfikaty. Odpowiada na pytanie „co ma istnieć".
- Ansible konfiguruje to, co Terraform powołał do życia: instaluje pakiety, ustawia usługi, wdraża aplikacje. Odpowiada na pytanie „jak ma być skonfigurowane" (zob. nasz wpis o Ansible).
Dzięki temu połączeniu Terraform pozwala nam:
- przyspieszać powtarzalne zadania — całe środowisko stawiamy jednym
apply, w minuty zamiast godzin ręcznej pracy w panelach; - utrzymywać spójne środowiska — dev, staging i prod opisuje ten sam kod, więc nie ma różnic „przypadkiem";
- eliminować błędy ludzkie —
planpokazuje skutki zmiany przed jej wykonaniem, a kod w git daje pełną historię i możliwość rollbacku; - szybko odtwarzać infrastrukturę — po awarii odtworzenie zasobów to ponowny
applyz tego samego kodu.
Efekt jest mierzalny: mniej awarii, krótsze przestoje i przewidywalna, udokumentowana infrastruktura.
Podsumowanie
Terraform opiera się na kilku spójnych ideach: HCL opisuje pożądany stan; providery tłumaczą go na API platform; zasoby i data sources definiują i odczytują obiekty; stan łączy kod z rzeczywistością; cykl plan/apply czyni zmiany bezpiecznymi i świadomymi; a moduły porządkują i umożliwiają ponowne użycie. Razem zamieniają zarządzanie infrastrukturą z klikania w panelach w inżynierię opartą na kodzie — wersjonowaną, powtarzalną i odporną na pomyłki.
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ń.