Jak zacząć z infrastrukturą jako kodem w chmurze: praktyczny przewodnik dla programistów i DevOps

0
5
Rate this post

Nawigacja po artykule:

Po co programiście i DevOps infrastruktura jako kod

Różnica między „klikaniem w konsoli” a infrastrukturą jako kodem

Panel chmurowy kusi. Kilka kliknięć, instancja stoi, security group „na szybko”, baza danych z domyślnymi ustawieniami, wszystko działa. Do pierwszej większej zmiany.

Infrastruktura jako kod (IaC) zamienia te kliknięcia na powtarzalny, wersjonowany kod. Zamiast pamiętać, co zostało ustawione w którym oknie, masz pliki konfiguracyjne w repozytorium Git. Kluczowa różnica: zmiany są sprawdzalne, powtarzalne i automatyzowalne.

Przykład: zamiast ręcznie tworzyć VPC, podsieci, routing, definiujesz je w Terraform. Nowe środowisko dev? Uruchamiasz ten sam kod z innym zestawem zmiennych. Zamiast robić zrzuty ekranu z konsoli, robisz commit.

„Klikanie” daje szybki efekt, ale skaluje się słabo. Przy kilku środowiskach, wielu usługach i zespole paru osób bez IaC zaczyna się chaos. Kod infrastruktury uporządkuje ten bałagan w podobny sposób, jak uporządkowany monolit zastępuje serię skryptów ad-hoc.

Koszty braku powtarzalności: środowiska „działające tylko u Jana”

Typowy scenariusz: Jan ma dostęp „owner” do wszystkiego. Tworzy rzeczy po swojemu, poprawia „na produkcji”, czasem zapomina powiedzieć, co zmienił. Nikt inny nie potrafi odtworzyć środowiska od zera.

Skutki braku infrastruktury jako kodu:

  • Nie ma pewności, jak wygląda produkcja vs staging – rozjechane konfiguracje.
  • Restart środowiska po awarii oznacza zgadywanie ustawień z pamięci lub logów audytowych.
  • Onboarding nowych osób jest powolny – wiedza siedzi w głowach, nie w repo.
  • Zmiany „na żywo” w konsoli powodują niespodzianki w nocy lub po weekendzie.

IaC wymusza przeniesienie tej wiedzy do kodu. Znika potrzeba „lokalnych bohaterów” od ręcznych konfiguracji. Przyjście nowej osoby do zespołu jest łatwiejsze, bo może ona prześledzić historię zmian w Git zamiast wypytywać o każdy szczegół.

Infrastruktura jako kod jako rozszerzenie rzemiosła programistycznego

Programista patrzy na IaC jak na kolejny język. DevOps traktuje go jako narzędzie do automatyzacji. W obu przypadkach kluczowe jest rzemiosło: czytelny kod, testy, code review, reużywalność.

Te same zasady, które stosujesz w kodzie aplikacji, przenosisz na infrastrukturę:

  • DRY – wspólne elementy w modułach zamiast kopiuj-wklej.
  • KISS – prosta struktura, każdy katalog ma jasny cel.
  • Code review – każda zmiana infrastruktury przechodzi przez PR.
  • Testy – choćby minimalne: terraform validate, terraform plan, proste testy integracyjne po wdrożeniu.

Jeśli masz doświadczenie programistyczne, wejście w IaC jest naturalne. Narzędzia typu Terraform mają własne niuanse, ale logika wersjonowania, refaktoryzacji i automatyzacji jest ta sama. DevOps zyskuje język, którym może rozmawiać z programistami w tym samym repozytorium i procesie CI/CD.

Gdzie IaC ma sens, a gdzie jest przesadą

Nie wszystko musi być od razu w IaC. Jest cienka granica między zdrową automatyzacją a nadmiarem abstrakcji.

Sensowne zastosowania IaC:

  • Środowiska produkcyjne i pre-prod (staging, UAT) – kluczowa jest przewidywalność.
  • Środowiska deweloperskie powtarzane w wielu kopiach (np. per feature branch).
  • Wspólne komponenty: VPC, sieci, klastry Kubernetes, bazy danych, kolejki.

Przesada:

  • Jednorazowe, krótkotrwałe POC w osobistym koncie chmurowym.
  • Bardzo małe projekty, gdzie warstwa infra = 1 VM i ręczne podejście jest tańsze.
  • Automatyzacja każdego, najmniejszego szczegółu na starcie, zanim wiadomo, czy system przetrwa dłużej niż miesiąc.

Dobrym kompromisem jest zasada: wszystko, co może być potrzebne za pół roku do odtworzenia środowiska lub przeniesienia między regionami, trafia do IaC.

Podstawowe pojęcia IaC bez marketingu

Deklaratywne vs imperatywne podejście – co to realnie zmienia

Imperatywne podejście mówi: „zrób krok 1, 2, 3”. Deklaratywne określa: „chcę, aby stan końcowy wyglądał tak”, a narzędzie dobiera kroki.

Terraform, CloudFormation czy Kubernetes YAML są deklaratywne. Mówisz, że chcesz 2 instancje w danym autoscaling group, konkretną podsieć i reguły firewall. Narzędzie porównuje stan aktualny z pożądanym i wykonuje minimalny zestaw zmian.

Imperatywne IaC (np. czyste Ansible, skrypty bash, Python) jest z kolei bliskie klasycznym skryptom: instrukcje create, update, delete w odpowiedniej kolejności. Łatwiej o nieprzewidywalne efekty przy wielokrotnym uruchomieniu.

Dla początkującego zespołu łatwiejsze jest podejście deklaratywne – lepiej pasuje do „porównania stanu” i daje czytelne plany zmian.

Idempotencja, dry-run, plan – po co to wszystko

Idempotencja oznacza, że wielokrotne wykonanie tej samej operacji daje ten sam efekt. terraform apply uruchomiony dwukrotnie, bez zmian w kodzie, nie powinien niczego zmieniać za drugim razem.

Dry-run lub plan to symulacja zmian. W Terraform masz terraform plan, które pokazuje, co zostanie utworzone, zmienione lub usunięte. To moment na złapanie głupich błędów zanim trafią na produkcję.

Bez planów i idempotencji IaC zamienia się w skryptowanie infrastruktury. Otwierasz drogę do „niespodzianek” – przypadkowych usunięć zasobów, zmian w niewłaściwym regionie czy błędnego przeskalowania.

W praktyce pętla pracy wygląda tak:

  • modyfikacja kodu infrastruktury,
  • terraform fmt, terraform validate,
  • terraform plan i review,
  • dopiero potem terraform apply.

Stan infrastruktury: co to jest i dlaczego może spalić weekend

Stan (state) to zapis tego, jak narzędzie IaC widzi istniejącą infrastrukturę. W Terraform to plik terraform.tfstate. Zawiera identyfikatory zasobów w chmurze, ich parametry i mapowanie na konfigurację.

Jeśli ten stan zniknie lub zostanie nadpisany, narzędzie traci kontekst. Może spróbować tworzyć zasoby, które już istnieją, lub usuwać coś, czego nie powinno ruszać. To najprostsza droga do „spalonego weekendu”.

Dlatego dla poważniejszych projektów:

  • stan trzymasz w zdalnym backendzie (S3 + DynamoDB, Azure Storage, GCS),
  • blokujesz jednoczesny zapis (locking),
  • robisz backupy stanu,
  • nie edytujesz stanu ręcznie, chyba że w krytycznym incydencie i z pełną świadomością.

Dobre ogarnięcie zarządzania stanem to fundament IaC. Duża część problemów Terraform w zespole wynika z lekceważenia tego aspektu.

Rozróżnienie: IaC, konfiguracja, orkiestracja, provisioning

Terminy często się mieszają, więc krótki porządek:

  • IaC – opis zasobów infrastruktury (VM, sieci, bazy, load balancery) jako kod.
  • Konfiguracja – konfiguracja systemów wewnątrz VM/kontenerów (pakiety, pliki konfiguracyjne); narzędzia typu Ansible, Chef, Puppet.
  • Orkiestracja – zarządzanie cyklem życia wielu komponentów i ich zależności, np. Kubernetes, systemy workflow.
  • Provisioning – przygotowanie zasobów do użycia, czasem obejmuje iaC + konfigurację (np. Packer budujący obrazy AMI).

Terraform nie jest narzędziem do konfiguracji aplikacji w środku kontenera. Ansible nie jest idealny do deklaratywnego tworzenia VPC. Łączenie narzędzi daje większą elastyczność, ale na starcie lepiej skupić się na jednym podstawowym narzędziu IaC i jednym chmurowym ekosystemie.

Wybór pierwszego narzędzia i chmury startowej

Dlaczego zwykle zaczyna się od Terraform, a kiedy wybrać inne narzędzie

Terraform jest obecnie najpopularniejszym narzędziem ogólnego zastosowania dla infrastruktury jako kodu. Obsługuje wiele chmur i usług, ma ogromny ekosystem modułów i jest dobrze udokumentowany.

Dobry wybór, jeśli:

Daje to realizm (prawdziwe API chmury, realne usługi) bez mieszania się w produkcyjne zasoby. Współczesne zespoły pracujące z Informatyka, Nowe technologie, AI dość szybko dochodzą do wniosku, że nauka IaC „na sucho” niewiele daje – potrzebne jest realne konto chmurowe, nawet z minimalnym limitem finansowym.

  • pracujesz w zespole multi-cloud lub planujesz migracje,
  • chcesz oddzielić modelowanie infrastruktury od konkretnego dostawcy,
  • szukasz narzędzia, które łatwo zintegrować z CI/CD i GitOps.

Alternatywy:

  • CloudFormation (AWS), ARM/Bicep (Azure), Deployment Manager (GCP) – gdy jesteś „all in” u jednego dostawcy i chcesz jego natywne narzędzie.
  • Pulumi – gdy chcesz pisać IaC w znanych językach (TypeScript, Python, Go, C#).
  • CDK (AWS CDK, CDK for Terraform) – programistyczne podejście do deklaratywnej infrastruktury.

Na start, dla programistów i DevOps, Terraform daje najbardziej uniwersalną ścieżkę. Narzędzia natywne warto poznać później albo gdy firma ma na nie silne postawienie.

Jak wykorzystać istniejące konto w AWS/Azure/GCP zamiast wymyślać laboratorium od nowa

Zamiast zakładać kolejne „testowe” konto, lepiej zdarza się wydzielić osobne środowisko (project/subscription/account) w już używanej chmurze z limitem uprawnień i budżetu.

Minimalny scenariusz:

  • prośba do administratorów o stworzenie dedykowanego projektu (GCP), subskrypcji (Azure) lub konta (AWS) dla zespołu lub sandboxa,
  • zdefiniowane limity kosztów (budżety, alerty),
  • jeden region na start, aby uprościć zarządzanie.

Kryteria wyboru: ekosystem modułów, wsparcie w firmie, krzywa nauki

Przy wyborze narzędzia i chmury nie chodzi tylko o techniczne możliwości. Liczą się:

  • Ekosystem – istniejące moduły (np. Terraform Registry), gotowe wzorce VPC, RDS, GKE/EKS/AKS.
  • Wsparcie w firmie – czy są osoby, które już to narzędzie znają; czy firma ma oficjalny standard.
  • Krzywa nauki – Terraform ma prostą składnię HCL, Pulumi daje elastyczność kodu, ale wymaga myślenia jak programista.
  • Integracja z istniejącym CI/CD – na czym teraz stoi pipeline (GitHub Actions, GitLab CI, Jenkins, Azure DevOps).

Jeśli w organizacji istnieje już preferowany stack (np. „u nas tylko Terraform + AWS”), lepiej się w niego wpiąć. Zyskasz wsparcie i gotowe moduły, zamiast walczyć o egzotyczne narzędzie.

Minimalny zestaw uprawnień i zasobów na start

Na początek wystarczy jedno środowisko deweloperskie. Bez produkcji, bez rozbudowanej sieci międzykontowej.

Minimalny zestaw:

  • konto techniczne (service account / IAM user) dla Terraform z ograniczonymi uprawnieniami (np. pełny dostęp do konkretnego projektu/subskrypcji, ale nie do całej organizacji),
  • jedno VPC/Virtual Network, kilka podsieci,
  • storage na stan (S3/Blob Storage/GCS) w osobnym bucket/container,
  • monitoring/logi w podstawowej konfiguracji.

Permisje warto opisać w formie polityk IAM w repo (bez sekretów), aby łatwo było odtworzyć dostęp w innym regionie lub środowisku.

Przygotowanie lokalnego warsztatu pracy

Narzędzia: Terraform/CloudFormation/Pulumi, CLI chmury, edytor z pluginami

Podstawowe narzędzia lokalne są proste, ale warto je skonfigurować sensownie od razu:

  • CLI chmury (AWS CLI, Azure CLI, gcloud) – skonfigurowane profile, region domyślny, autoryzacja.
  • Narzędzie IaC – np. Terraform w wersji zgodnej z zespołem lub projektem (pinowanie wersji).
  • Edytor – VS Code, IntelliJ, neovim – z pluginami do Terraform (linting, formatowanie, podpowiedzi).
  • Zarządzanie wersjami narzędzi i providerów

    Narzędzia IaC i providerzy chmurowi zmieniają się szybko. Jeśli każdy w zespole ma inną wersję Terraform lub providera AWS, odtworzenie błędów staje się loterią.

    Podstawą jest deklaracja wersji w samym kodzie. W Terraform użyj sekcji required_version i required_providers, a nie poleganie na tym, co akurat jest zainstalowane lokalnie.

    terraform {
      required_version = ">= 1.5.0, < 1.7.0"
    
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.0"
        }
      }
    }
    

    Dodatkowo sensowne jest:

  • użycie menedżera wersji (tfenv, asdf) albo binarek w repo (np. przez narzędzia typu terraform-wrapper),
  • standaryzacja wersji CLI chmury w dokumentacji projektu lub plikach konfiguracyjnych (np. .tool-versions dla asdf),
  • sprawdzanie wersji w pipeline CI przed uruchomieniem planu.

To niewielki koszt na starcie, a usuwa klasyczny problem „u mnie działa, u ciebie nie”.

Struktura katalogów i repozytorium

Nie ma jednego „świętego” wzorca, ale chaos katalogów szybko mści się przy kilku środowiskach. Dla małego projektu wystarczy prosty, spójny układ.

Przykład dla jednego systemu z kilkoma środowiskami:

infra/
  modules/
    network/
    app_cluster/
  envs/
    dev/
      main.tf
      backend.tf
      variables.tf
    stage/
    prod/
  README.md

Moduły są współdzielone, a każde środowisko ma swoją instancję konfiguracji (osobny stan, osobne parametry). Łatwo wywołać terraform plan tylko dla envs/dev, bez dotykania produkcji.

Im wcześniej ustalisz taki schemat, tym mniej późniejszego refaktoru i przenoszenia zasobów między stanami.

Konfiguracja CLI chmury i poświadczeń

CLI chmury musi wiedzieć, kto i na jakim koncie działa. Jeśli to zaniedbasz, prędzej czy później zrobisz apply na złym projekcie.

Podstawowy zestaw:

  • profile lub konteksty dla różnych kont/projektów (np. aws configure --profile dev),
  • domyślny region spójny z projektem,
  • uwierzytelnianie oparte na krótkotrwałych tokenach (SSO, federacja z IdP), a nie na stałych kluczach IAM w plikach.

Na lokalnym devie używaj mechanizmów, które będą też w CI/CD (np. role zaufane, workload identity). Unikasz wtedy zaskoczeń typu „lokalnie działa, pipeline nie ma uprawnień” albo odwrotnie.

Bezpieczne przechowywanie sekretów lokalnie

Hasła do baz, klucze API, tokeny – IaC ich potrzebuje, ale repo nie może ich zawierać. Tu najwięcej wpadek jest na początku.

Podstawowe zasady:

  • zero sekretów w repo (także w historii git). Użyj narzędzi typu git-secrets, pre-commit hooks, skanery sekretów w CI,
  • sekrety w managerze typu AWS Secrets Manager, Azure Key Vault, HashiCorp Vault lub przynajmniej w param store chmury,
  • lokalnie korzystasz z referencji do sekretu (ID/arn/url), nie z jego wartości, gdzie tylko się da.

Jeśli narzędzie IaC musi znać wartość (np. inicjalne hasło do bazy), podawaj ją przez zmienne środowiskowe albo plik .tfvars ignorowany w .gitignore. Nigdy nie commituj takich plików.

Pierwszy projekt IaC krok po kroku

Definicja minimalnego celu: „Hello, VPC + VM”

Pierwszy projekt nie musi od razu tworzyć pełnej platformy. Lepiej zbudować mały, kompletny układ: sieć + jedna maszyna lub mały klaster kontenerów.

Typowy minimalny zakres:

  • VPC/Virtual Network z jedną publiczną i jedną prywatną podsiecią,
  • grupa bezpieczeństwa lub firewall rule otwierająca tylko potrzebne porty (np. SSH z konkretnego IP, HTTP z internetu),
  • jedna VM (lub managed instance) z prostym serwerem HTTP albo mały klaster (np. EKS/GKE/AKS z jednym node-pool).

To pozwala przećwiczyć: sieć, compute, security, inicjalizację instancji i podstawowe tagowanie.

Tworzenie pierwszego modułu

Zamiast upychać wszystko do jednego pliku, lepiej już na starcie wydzielić prosty moduł. Nie musi być „idealny”, ma być czytelny i możliwy do ponownego użycia.

Przykład: moduł network tworzący VPC i podsieci plus kilka wyjść:

# modules/network/main.tf
resource "aws_vpc" "this" {
  cidr_block           = var.cidr_block
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = merge(var.tags, {
    Name = "${var.name}-vpc"
  })
}

resource "aws_subnet" "public" {
  count             = length(var.public_subnets)
  vpc_id            = aws_vpc.this.id
  cidr_block        = var.public_subnets[count.index]
  map_public_ip_on_launch = true

  tags = merge(var.tags, {
    Name = "${var.name}-public-${count.index}"
  })
}

Następnie w środowisku dev wywołujesz moduł:

module "network" {
  source         = "../modules/network"
  name           = "dev"
  cidr_block     = "10.0.0.0/16"
  public_subnets = ["10.0.1.0/24"]
  tags = {
    env = "dev"
  }
}

Już na tym etapie widać, jak parametry wpływają na kształt infrastruktury i jak przenosić wzorce między środowiskami.

Parametryzacja środowisk i zmiennych

Klucz do uniknięcia kopii-wklejek między dev, stage, prod to sensowna parametryzacja. Nie przesadzaj z nią na początku.

Dobry podział:

  • zmienne techniczne (CIDR, wielkość VM, liczba replik) – w variables.tf i plikach *.tfvars per środowisko,
  • sekrety – poza repo, tylko referencje lub wartości z zewnętrznego key store,
  • rzeczy naprawdę wspólne (nazwa aplikacji, domyślne tagi) – w jednym pliku współdzielonym.

Starszy błąd: chęć parametryzowania wszystkiego od razu. Lepiej zacząć od kilku prostych zmiennych, a dopiero później wydzielać kolejne, gdy pojawi się realna potrzeba.

Uruchomienie: init, fmt, validate, plan, apply

Cykl jest prosty, ale z czasem wchodzi w nawyk i staje się „definicją poprawnego użycia” IaC.

Typowa sekwencja w katalogu środowiska:

terraform init       # pierwszy raz lub gdy zmieniasz provider/backend
terraform fmt        # automatyczne formatowanie
terraform validate   # wczesne wychwycenie błędów składni/typów
terraform plan -out=tfplan
terraform apply "tfplan"

Traktuj brak plan jak brak code review. Na początku pilnuj się nawet w dev – nawyki z dev przenoszą się potem na produkcję.

Modyfikacje i usuwanie zasobów

Drugi krok po „tworzeniu” to zmiany i kasowanie. To tutaj najbardziej widać sens deklaratywnego IaC.

Proste ćwiczenie:

  1. Zmień typ VM (np. z t3.micro na t3.small),
  2. dodaj nową podsieć w module sieci,
  3. usuń zasób z kodu (np. security group) i zobacz, jak Terraform planuje jego usunięcie.

Dużo się uczysz, obserwując plan i faktyczne zmiany w konsoli chmury. Lepiej zrobić kilka takich iteracji na sandboxie niż pierwszy raz „poczuć” delete na produkcji.

Programista pracuje na laptopie na dworze w Indiach nad kodem w chmurze
Źródło: Pexels | Autor: Meet Patel

Myślenie o infrastrukturze jak o kodzie aplikacji

Konwencje nazewnicze i tagowanie

Bez spójnych nazw i tagów infrastruktura szybko zamienia się w „zoo zasobów”. To utrudnia debugowanie, fakturowanie, a nawet czyszczenie sandboxów.

Podstawą jest prosty schemat nazewniczy, np.:

  • {app}-{env}-{component}, np. billing-dev-api,
  • tagi: env, app, owner, cost-center, wstrzykiwane jako zmienne do modułów.

Konwencje opisujesz w README projektu i wymuszasz w module (np. predefiniowane mapy tagów). Z czasem przydaje się linting regułami (tflint, conftest/OPA, Checkov) – ale to można dołożyć później.

Review zmian IaC jak review kodu

Pull request z Infra kodem zasługuje na taki sam poziom uwagi jak zmiany w mikroserwisie. Inaczej bugi wylądują w produkcyjnej sieci, a nie w testowej funkcji.

Dobrą praktyką jest dołączanie do PR:

  • wyjścia z terraform plan (lub artefaktu planu z CI),
  • krótkiego opisu wpływu na środowisko (np. „dodaje nową podsieć w eu-central-1, zmienia typ instancji bastiona”),
  • linków do ticketów/incydentów, jeśli zmiana jest remediacją.

Osoba reviewująca nie musi być ekspertem od każdego providera, ale powinna móc przeczytać plan i zrozumieć, co się stanie po apply. To chroni przed niespodziankami typu masowy destroy.

Testy statyczne i „unit testy” dla IaC

Dla małych projektów wystarczy na początku terraform validate i fmt. Później można dokładać kolejne poziomy jakości.

Przykładowy zestaw:

  • linting (tflint) – wykrywa typowe błędy i niezalecane wzorce,
  • reguły zgodności (np. Checkov, OPA/Rego) – polityki typu „żaden bucket nie może być publiczny”,
  • testy modułów (Terratest, Kitchen-Terraform) – bardziej zaawansowane, gdy moduły stają się krytyczną częścią platformy.

Nie ma sensu zaczynać od pełnego arsenału. Najpierw opanuj cykl plan/apply i review, dopiero potem wprowadzaj kolejne warstwy testowania.

Refaktoryzacja infrastruktury

Tak jak kod aplikacji, IaC będzie się starzał. Refaktor to nie tylko „ładniejsze pliki”, ale też przenoszenie zasobów między modułami i stanami.

Przy większych zmianach:

  • używaj terraform state mv do przenoszenia zasobów między modułami, zamiast usuwać i tworzyć od nowa,
  • rób snapshot stanu przed dużym refaktorem (kopie w backendzie lub ręczna kopia pliku),
  • planuj zmiany etapami: najpierw podział modułów i stanu, potem dopiero nowe feature’y.

Jeden z typowych scenariuszy z życia: monolityczny katalog z 3000 linii Terraform, który trzeba rozbić na moduły i środowiska. Bez zrozumienia stanu i ostrożnego state mv efektem bywa masowy recreate produkcji.

Integracja IaC z CI/CD i GitOps

Oddzielenie pipeline’u aplikacji od pipeline’u infrastruktury

Kuszące jest wrzucić wszystko do jednego pipeline’u: build aplikacji, deploy, zmiany w infrastrukturze. Przy prostych projektach to przejdzie, ale rosnące systemy szybko zaczną to mścić.

Sensowny model:

  • osobny pipeline dla Infra (repo „infra” lub katalog w monorepo),
  • aplikacja triggeruje Infra tylko wyjątkowo (np. potrzebny nowy feature bazy),
  • Infra pipeline ma swoje reguły zatwierdzania, często z dodatkowymi krokami review i manualnym zatwierdzeniem planu na produkcji.

To pozwala rozwijać aplikację w swoim tempie, a zmiany w infrastrukturze trzymać pod większą kontrolą.

Typowy pipeline IaC w GitHub Actions / GitLab CI

Niezależnie od narzędzia CI układ etapów bywa podobny. Przykład dla Terraform:

  1. lint / validate,
  2. plan (na commit/pull request),
  3. apply (na merge do konkretnej gałęzi, często z manualnym krokiem).

W GitHub Actions może to wyglądać skrótowo tak:

name: infra

on:
  pull_request:
    paths:
      - "infra/**"
  push:
    branches: ["main"]
    paths:
      - "infra/**"

jobs:
  plan:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform validate
      - run: terraform plan

  apply:
    runs-on: ubuntu-latest
    needs: plan
    if: github.

GitOps dla infrastruktury

GitOps to po prostu zasada: jedynym źródłem prawdy o infrastrukturze jest repozytorium, a zmiany w chmurze są wynikiem merge do odpowiedniej gałęzi.

Prosty model dla IaC:

W tym miejscu przyda się jeszcze jeden praktyczny punkt odniesienia: Porównanie scikit‑learn, TensorFlow i PyTorch do nauki ML: co wybrać na start.

  • gałąź main = stan produkcji,
  • gałęzie środowisk (np. dev, stage) lub katalogi środowisk w jednym drzewie,
  • każdy merge generuje plan i ewentualnie apply dla konkretnego środowiska.

Kluczowy element GitOps: brak „ręcznych” zmian w konsoli. Jeśli ktoś „na szybko” zmieni security group w UI, kolejny apply cofnie tę zmianę do deklaracji w repo. To wymusza dyscyplinę.

Dobrze działa też prosty mechanizm „zatwierdzania planu” – np. komentarz w PR lub manualny krok w pipeline, który potwierdza, że plan jest zgodny z oczekiwaniami.

Środowiska per gałąź czy per katalog

Dla mniejszych zespołów wygodniejszy jest podział per katalog:

infra/
  dev/
  stage/
  prod/
  modules/

Pipeline decyduje, które środowisko odświeżyć na podstawie zmienionych ścieżek. Plusem jest prostota i jeden główny branch.

Model per gałąź (np. osobna gałąź prod) przydaje się w większych organizacjach, ale komplikuje workflow (merge między gałęziami, rozjazdy). Na początek lepiej nie dokładać tego problemu.

Dry-run i podgląd zmian z poziomu PR

Bardzo ułatwia życie podpinanie wyniku plan bezpośrednio pod PR.

Przykład w GitHub Actions (ciąg dalszy poprzedniego joba):

  plan:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: cd infra/dev && terraform init
      - run: cd infra/dev && terraform validate
      - run: cd infra/dev && terraform plan -no-color > plan.txt
      - uses: actions/upload-artifact@v4
        with:
          name: terraform-plan
          path: infra/dev/plan.txt

Potem można dodać krok, który komentuje PR treścią planu lub załącza go jako artefakt. Reviewer nie musi uruchamiać nic lokalnie, widzi dokładnie, które zasoby się zmienią.

Ręczne kroki dla produkcji

Produkcyjne środowisko często wymaga dodatkowej ostrożności. Automatyczny apply po merge na produkcję nie zawsze jest dobrym pomysłem.

Bezpieczny wariant:

  • zawsze generowany plan dla prod (na merge lub ręczne wywołanie pipeline),
  • manualny krok „Approve & Apply” w CI, który może kliknąć tylko ograniczona grupa osób,
  • logowanie kto i kiedy potwierdził wdrożenie.

Przy incydentach ułatwia to dojście, kto „wypchnął” konkretną zmianę w sieci czy bazie i czy to jest przyczyną problemu.

Bezpieczeństwo i dostęp do chmury w kontekście IaC

Minimalne uprawnienia dla narzędzi IaC

Konto lub rola używana przez Terraform/Ansible nie powinna mieć uprawnień „god-mode”. Nawet jeśli to tylko środowisko deweloperskie.

Podstawowy zestaw zasad:

  • osobna rola/konto IAM dla CI (np. terraform-ci) z ograniczonym zakresem (konkretne VPC, konkretne serwisy),
  • osobne role per środowisko (dev-iac, prod-iac),
  • brak uprawnień do zarządzania tożsamością ludzi (IAM users/roles dla developerów) z poziomu IaC w projektach aplikacyjnych.

Jeśli provider pozwala, używaj mechanizmu assume role (AWS STS, GCP Workload Identity). Pipeline nie trzyma stałych kluczy, tylko „wchodzi” w rolę zdefiniowaną w chmurze.

Przechowywanie sekretów i zmiennych wrażliwych

Hasła do baz, klucze API czy dane dostępu do chmury nie powinny lądować w *.tfvars ani w zmiennych środowisk commitowanych do repo.

Bezpieczniejsze opcje:

  • zewnętrzne sejfy (AWS SSM Parameter Store, Secrets Manager, HashiCorp Vault) i referencje do nich w IaC,
  • szyfrowane zmienne w CI (GitHub Encrypted Secrets, GitLab CI Variables),
  • lokalne pliki .auto.tfvars dodane do .gitignore tylko dla deweloperów (np. tokeny do sandboxa).

Jeśli sekret musi przejść przez IaC (np. inicjalne hasło), korzystaj z flag typu sensitive i nie wypisuj wartości w outputach.

Bezpieczna konfiguracja backendu stanu

Stan Terraform często zawiera wrażliwe dane (ARN-y, nazwy zasobów, czasem nawet sekrety). Dlatego backend powinien być traktowany jak baza danych z produkcją.

Dobre praktyki:

  • stan w zewnętrznym, zabezpieczonym backendzie (S3 + DynamoDB, GCS, Terraform Cloud),
  • szyfrowanie w spoczynku (np. SSE-KMS dla S3),
  • ograniczony dostęp: tylko rola IaC i ewentualnie administratorzy platformy, brak dostępu dla zwykłych deweloperów do produkcyjnego stanu.

W małych zespołach kusi, by trzymać stan w repo. To szybko się mści, gdy dojdzie do konfliktu lub ktoś przypadkowo opublikuje repo z historią commitów.

Skany bezpieczeństwa konfiguracji

Obok testów funkcjonalnych przydają się skany bezpieczeństwa. Ich zadanie jest proste: blokować oczywiste miny, zanim trafią do chmury.

Poza Checkov czy OPA warto dodać:

  • skanery błędnie ustawionych bucketów, SG (np. „0.0.0.0/0” dla SSH),
  • reguły wymuszające szyfrowanie (dyski, bazy, kolejki),
  • sprawdzanie tagów wymaganych do audytu i rozliczeń.

Uruchomienie takich narzędzi jako kroku w CI pozwala zatrzymać PR-y, które np. otwierają port 22 na cały świat.

Praca zespołowa z IaC: procesy i konflikty

Podział odpowiedzialności za infrastrukturę

W małych firmach często jedna osoba „ogarnia Terrafirma”, w większych zespołach ten model się nie skaluje. Szybko pojawia się wąskie gardło.

Lepszy podział:

  • zespół platformowy (Platform/Infra) rozwija wspólne moduły i bazowe VPC,
  • zespoły produktowe korzystają z modułów, definiują własne zasoby w granicach projektu (np. kolejki, funkcje serverless, bazy),
  • granice odpowiedzialności opisane w dokumentacji modułów i repo.

Wtedy nowa funkcja aplikacji nie wymaga „ticketu do działu Infra” za każdym razem, a jednocześnie jest spójny, centralny szkielet.

Strategie na konflikty w stanie i plikach

Konflikty w kodzie rozwiązuje Git. Konflikty w stanie to zupełnie inna historia. Gdy dwie osoby zrobią apply w tym samym czasie, można wysadzić sobie środowisko.

Sposoby ograniczenia tego ryzyka:

  • zawsze używaj zdalnego backendu z blokadą (np. DynamoDB lock table dla S3),
  • nie uruchamiaj apply ręcznie, jeśli produkcją zarządza wyłącznie pipeline,
  • dziel stan (workspaces lub osobne katalogi/backendy) między niezależne komponenty, zamiast jednego globala dla wszystkiego.

Jeśli mimo wszystko pojawi się konflikt, nie kasuj stanu na ślepo. Zrób kopię, porównaj, spróbuj odtworzyć ostatni poprawny plan/apply. W skrajnym przypadku trzeba część zasobów „zaimportować” ponownie (terraform import).

Proces review i „właścicielstwo” modułów

Moduły współdzielone między zespołami powinny mieć wyznaczonych maintainerów. To oni zatwierdzają zmiany, wydają wersje i opisują migracje.

Praktyczny schemat:

  • osobne repo lub katalog infra/modules z własnym CI,
  • wersjonowanie modułów (tagi git, version = "x.y.z" w source),
  • zmiany breaking oznaczone wyższą wersją major i opisane w CHANGELOG.

Unikasz wtedy sytuacji, w której „poprawka” w module sieci niechcący przewraca produkcję trzech różnych aplikacji.

Onboarding nowych osób do IaC

Nowa osoba w zespole rzadko ma od razu pełne doświadczenie z IaC. Dobrze działa krótka ścieżka startowa w samym repo.

Prosty zestaw materiałów:

  • README.md z opisem jak uruchomić plan i apply na sandboxie,
  • diagram logiczny zasobów (VPC, subnety, główne serwisy),
  • opis naming/tagging i polityki review.

Do tego jedna sesja parowa: wspólne przejście przez plan, modyfikację prostego zasobu i review. Taki onboarding często oszczędza tygodnie nieporozumień później.

Najczęstsze błędy na starcie i jak je omijać

Ręczne „dopieszczanie” zasobów w konsoli

Częsty scenariusz: Terraform tworzy VPC, potem ktoś „na szybko” doklika nową regułę w security group przez UI. Działa do pierwszego apply, który nadpisuje zmiany lub próbuje je „naprawić”.

Najlepsze wyjście to prosta zasada: jeśli coś jest w IaC, nie ruszamy tego ręcznie. Jeśli trzeba zmienić, zmieniamy kod i robimy plan/apply. Dodatkowy plus: zawsze wiadomo, kto i dlaczego wprowadził zmianę.

Za duży zakres pierwszego projektu

Na początku kusi, żeby „od razu zautomatyzować wszystko”: sieci, clustery Kubernetes, bazę, monitoring, CDN. Efekt bywa taki, że po miesiącu nadal nie ma stabilnego apply.

Lepsza taktyka:

Do kompletu polecam jeszcze: Co przynosi najnowsza wersja Terraform: zmiany w zarządzaniu infrastrukturą jako kodem — znajdziesz tam dodatkowe wskazówki.

  • start od jednego środowiska i jednego sensownego wycinka (np. sieć + jedna aplikacja),
  • krok po kroku podłączanie kolejnych komponentów,
  • refaktoryzacje po drodze, a nie na końcu „jak już wszystko będzie gotowe”.

Kiedy pierwszy fragment zacznie być powtarzalny (nowe środowisko = kilka komend), dopiero wtedy skaluj zakres.

Mieszanie wielu narzędzi bez powodu

Terraform do wszystkiego, Ansible do konfiguracji VM, CloudFormation dla jednego serwisu, ręczne skrypty bash – tak kończy się wiele projektów, które rosły bez przemyślenia.

Dobrze jest jasno określić granice:

  • IaC (Terraform/Pulumi) – zasoby chmurowe, sieci, bazy, kolejki,
  • konfiguracja systemu – narzędzia pokroju Ansible, Chef lub cloud-init,
  • deploy aplikacji – pipeline aplikacyjny, helm/argo/flux dla K8s.

Mniej narzędzi na start = mniej miejsc, gdzie trzeba debugować dziwne zachowanie.

Brak rozdzielenia stanów między środowiskami

Trzymanie dev, stage i prod w jednym stanie kusi prostotą, ale to proszenie się o problemy. Jedna literówka w ścieżce lub workspace i niechcący zmieniasz produkcję, myśląc, że działasz na dev.

Zdecydowanie bezpieczniej:

  • osobne backendy lub workspaces per środowisko,
  • jasne nazewnictwo i konfiguracja w CI (job „prod” nawet nie dotyka katalogu „dev”),
  • osobne role IAM do każdego środowiska.

To nie tylko kwestia bezpieczeństwa, ale też wydajności. Stan prod zwykle jest większy i nie ma powodu, by ładować go przy pracy nad dev.

Ignorowanie kosztów podczas automatyzacji

Automatyzacja ułatwia tworzenie zasobów, ale równie łatwo można automatami wyklikać rachunek, który zaboli. Kilka niepotrzebnych load balancerów i większych instancji zostawionych w dev potrafi zaskoczyć.

Kilka prostych zabezpieczeń:

  • tagi kosztowe per środowisko i aplikację,
  • limity typu „maksymalna liczba replik” jako zmienne z domyślną, rozsądną wartością,
  • okresowe „sprzątanie” sandboxów i dopisywanie prostych skryptów destroy dla środowisk tymczasowych.

Dobrze też od początku wyrobić nawyk: każda większa zmiana IaC dostaje sekcję „wpływ na koszty” w opisie PR, choćby krótką.

Brak dokumentacji decyzji architektonicznych

Kod IaC pokazuje co jest zrobione, ale nie zawsze dlaczego. Po roku nikt nie pamięta, czemu w tym VPC są trzy podsieci, a nie dwie, i skąd się wzięły dziwne reguły w NACL.

Ułatwia życie prosty schemat:

  • krótkie komentarze w kodzie przy nietypowych decyzjach (np. „workaround dla limitu X w regionie Y”),
  • Najczęściej zadawane pytania (FAQ)

    Od czego zacząć naukę infrastruktury jako kodu jako programista lub DevOps?

    Najprościej: wybierz jedną chmurę (np. AWS, Azure lub GCP) i jedno narzędzie IaC – najczęściej będzie to Terraform. Załóż osobne, nieprodukcyjne konto chmurowe, żeby móc bezpiecznie eksperymentować.

    Na start zbuduj coś małego, ale kompletnego: sieć (VPC + podsieci), jedną maszynę lub klaster i prostą bazę. Trzymaj kod w Git, rób małe commity i zawsze uruchamiaj terraform plan przed apply. To wystarczy, żeby „złapać” podstawy cyklu pracy z IaC.

    Czym różni się „klikanie w konsoli” od infrastruktury jako kodu w praktyce?

    Klikanie w panelu daje szybki efekt, ale praktycznie nie da się go powtórzyć 1:1. Po kilku poprawkach nikt nie ma pewności, jak dokładnie wygląda produkcja, staging czy środowiska deweloperskie. Odtworzenie wszystkiego po awarii to zgadywanie i przeglądanie logów audytowych.

    Przy IaC wszystkie zmiany są w kodzie, wersjonowane w Git i przechodzą code review. Nowe środowisko to uruchomienie tego samego kodu z innymi zmiennymi zamiast ręcznego klikania. Znika problem „działa tylko u Jana” i tajnych ustawień zapisanych w głowie jednej osoby.

    Kiedy infrastruktura jako kod ma sens, a kiedy to przesada?

    IaC ma największy sens tam, gdzie ważna jest powtarzalność i przewidywalność: produkcja, staging, UAT, powtarzalne środowiska deweloperskie oraz wspólne komponenty (VPC, klastry Kubernetes, bazy, kolejki). Przy kilku środowiskach i większym zespole ręczna konfiguracja szybko zamienia się w chaos.

    Przesadą jest pełna automatyzacja krótkotrwałego POC na prywatnym koncie, mikroprojekt z jedną VM-ką czy dopieszczanie kodem każdego detalu systemu, który może nie przetrwać miesiąca. Prosta zasada: jeśli coś może być potrzebne za pół roku do odtworzenia lub przeniesienia między regionami – opisz to jako kod.

    Co to znaczy, że Terraform jest deklaratywny i dlaczego to ważne?

    Deklaratywne narzędzia (Terraform, CloudFormation, Kubernetes YAML) opisują „stan docelowy”: ile instancji ma działać, jakie mają być podsieci, reguły firewall, load balancery. Narzędzie samo porównuje ten stan z rzeczywistością i wykonuje minimalny zestaw zmian.

    W podejściu imperatywnym (skrypty bash, Python, czyste Ansible) piszesz listę kroków: utwórz, zaktualizuj, usuń. Łatwiej wtedy o nieprzewidywalne efekty przy wielokrotnym uruchomieniu. Dla zespołów zaczynających z IaC deklaratywność jest prostsza do ogarnięcia i lepiej wspiera „porównanie stanu” oraz bezpieczne plany zmian.

    Co to jest stan (state) w Terraform i dlaczego wszyscy mówią, że można nim spalić weekend?

    Stan to zapis tego, jak Terraform widzi istniejącą infrastrukturę – które zasoby istnieją, jakie mają parametry i jak są powiązane z konfiguracją w kodzie. Domyślnie to plik terraform.tfstate. Jeśli zostanie zgubiony lub nadpisany, Terraform traci kontekst.

    Skutek: może spróbować tworzyć duplikaty zasobów lub usuwać coś, czego nie powinien ruszać. Dlatego w poważniejszych projektach trzyma się stan w zdalnym backendzie (np. S3 + DynamoDB, Azure Storage, GCS), z blokadą zapisu i backupami. Ręczna edycja stanu to ostateczność, zwykle w trakcie incydentu i z pełną świadomością ryzyka.

    Jak połączyć IaC z istniejącym procesem developerskim i CI/CD?

    Najprościej traktować kod infrastruktury jak zwykły kod aplikacji. Ten sam Git, te same pull requesty, code review i zasady branchingowe. Każda zmiana infrastruktury przechodzi przez MR/PR, a nie przez szybkie kliknięcie w panelu chmurowym.

    W pipeline CI/CD można dodać kroki: terraform fmt, terraform validate, terraform plan (z artefaktem do review), a dopiero po akceptacji – terraform apply. Po wdrożeniu dobrze jest mieć choć proste testy integracyjne, które sprawdzą, czy środowisko faktycznie działa tak, jak zakłada konfiguracja IaC.

    Bibliografia

  • Infrastructure as Code: Managing Servers in the Cloud. O'Reilly Media (2016) – Klasyczne wprowadzenie do IaC, praktyki, wzorce, antywzorce.
  • Terraform: Up and Running, 3rd Edition. O'Reilly Media (2022) – Praktyczne użycie Terraform, stan, moduły, workflow zespołowy.
  • Terraform Documentation. HashiCorp – Oficjalna dokumentacja: stan, plan/apply, idempotencja, backendy zdalne.
  • AWS CloudFormation User Guide. Amazon Web Services – Deklaratywne szablony, zarządzanie stanem i zmianami w AWS.
  • Kubernetes Documentation – Declarative Configuration. Cloud Native Computing Foundation – Wyjaśnienie podejścia deklaratywnego i zarządzania stanem w Kubernetes.
  • Ansible Documentation – Introduction to Ansible. Red Hat – Imperatywne i deklaratywne aspekty automatyzacji, idempotencja zadań.

Poprzedni artykułKrem z pieczonego kalafiora z olejem z ziół i chrupką
Paweł Wojciechowski
Paweł Wojciechowski tworzy w Restauracja-Azalia.pl przepisy, które łączą kuchnię roślinną z botaniczną precyzją. Pracuje jak w kuchni restauracyjnej: najpierw testuje proporcje i techniki, potem upraszcza je do domowych warunków, nie tracąc smaku ani tekstury. Szczególną uwagę poświęca sezonowości, ziołom i jadalnym kwiatom, opisując bezpieczne zastosowania oraz możliwe alergie. W tekstach opiera się na praktyce, wiarygodnych źródłach i zasadach higieny, a każdą recepturę dopracowuje pod kątem powtarzalności i ograniczania marnowania.