Zarządzanie infrastrukturą jako kod (IaC): praktyczny przewodnik z Terraform i Ansible
Jak definiować, utrzymywać i integrować infrastrukturę z konfiguracją systemów w pipeline'ach CI/CD.
1. Wprowadzenie do IaC
W dzisiejszym dynamicznym świecie IT, gdzie infrastruktura jest coraz bardziej złożona i rozproszona, ręczne zarządzanie nią staje się niewydajne, podatne na błędy i trudne do skalowania. Tu z pomocą przychodzi koncepcja Infrastructure as Code (IaC). IaC to podejście, które polega na zarządzaniu i udostępnianiu infrastruktury (sieci, maszyn wirtualnych, baz danych, load balancerów itp.) za pomocą kodu, a nie manualnych procesów czy interfejsów graficznych.
Dlaczego warto wdrożyć IaC?
- Spójność i powtarzalność: Infrastruktura jest zawsze tworzona w ten sam sposób, eliminując błędy ludzkie i „dryf konfiguracji”.
- Automatyzacja: Procesy tworzenia, aktualizacji i usuwania infrastruktury są zautomatyzowane, co przyspiesza wdrażanie i zarządzanie.
- Wersjonowanie: Kod infrastruktury może być przechowywany w systemach kontroli wersji (np. Git), co pozwala na śledzenie zmian, cofanie się do poprzednich wersji i współpracę w zespołach.
- Szybsze wdrażanie: Możliwość szybkiego provisioningu całych środowisk (dev, test, prod) skraca czas potrzebny na wdrożenie nowych aplikacji.
- Bezpieczeństwo i zgodność: Łatwiejsze audytowanie i zapewnianie zgodności z politykami bezpieczeństwa poprzez inspekcję kodu.
2. Podstawy Terraform
Terraform to jedno z najpopularniejszych narzędzi do implementacji IaC, stworzone przez HashiCorp. Jest to narzędzie deklaratywne, co oznacza, że opisujemy pożądany stan infrastruktury, a Terraform dba o to, aby ten stan został osiągnięty.
2.1. Instalacja i inicjalizacja projektu
Instalacja Terraform jest prosta i polega na pobraniu binarnego pliku ze strony HashiCorp i umieszczeniu go w ścieżce systemowej. Po instalacji, w katalogu projektu wykonujemy:
terraform init
Komenda ta inicjalizuje katalog roboczy, pobiera niezbędne wtyczki (tzw. providery) dla zadeklarowanych usług chmurowych (lub lokalnych) i przygotowuje Terraform do pracy.
2.2. Providerzy
Providerzy to wtyczki, które pozwalają Terraformowi komunikować się z różnymi platformami i usługami. Przykłady popularnych providerów to:
- AWS: Do zarządzania zasobami w Amazon Web Services.
- Azure: Do zarządzania zasobami w Microsoft Azure.
- Google Cloud Platform (GCP): Do zarządzania zasobami w Google Cloud.
- DigitalOcean: Do zarządzania zasobami w DigitalOcean.
- VMware vSphere: Do zarządzania wirtualną infrastrukturą VMware.
- local/remote-exec: Do wykonywania skryptów lokalnie lub zdalnie na maszynach.
Definicja providera w pliku main.tf
:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-central-1" # Przykład regionu AWS
}
2.3. Pierwszy main.tf
– zasoby VM, sieć, storage
W pliku main.tf
definiujemy zasoby infrastruktury. Poniżej przykład tworzenia prostej maszyny wirtualnej (EC2) w AWS, sieci VPC i bucketu S3:
# Tworzenie VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "my-iac-vpc"
}
}
# Tworzenie podsieci
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-central-1a"
tags = {
Name = "my-iac-subnet"
}
}
# Tworzenie instancji EC2 (maszyny wirtualnej)
resource "aws_instance" "web_server" {
ami = "ami-0abcdef1234567890" # Zastąp aktualnym AMI dla Twojego regionu i systemu operacyjnego
instance_type = "t2.micro"
subnet_id = aws_subnet.main.id
tags = {
Name = "web-server-iac"
}
}
# Tworzenie bucketu S3
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-unique-iac-bucket-2025" # Nazwa bucketu musi być unikalna globalnie
acl = "private"
tags = {
Environment = "Dev"
Project = "IaC-Guide"
}
}
Po zdefiniowaniu zasobów, wykonujemy:
terraform plan
terraform plan
pokazuje, jakie zmiany zostaną wprowadzone w infrastrukturze, zanim zostaną faktycznie zastosowane. To kluczowy krok do weryfikacji. Następnie, aby zastosować zmiany:
terraform apply
Terraform zapyta o potwierdzenie, a następnie przystąpi do tworzenia zasobów.

3. Moduły i zarządzanie stanem
3.1. Organizacja kodu w moduły
Wraz ze wzrostem złożoności infrastruktury, utrzymywanie wszystkich zasobów w jednym pliku main.tf
staje się niepraktyczne. Terraform oferuje moduły, które pozwalają na grupowanie powiązanych zasobów w logiczne, reusable bloki.
Struktura katalogów z modułami:
.
├── main.tf
├── variables.tf
├── outputs.tf
└── modules/
├── vpc/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── ec2-instance/
├── main.tf
├── variables.tf
└── outputs.tf
Użycie modułu w main.tf
:
module "my_vpc" {
source = "./modules/vpc"
vpc_cidr_block = "10.0.0.0/16"
}
module "my_web_server" {
source = "./modules/ec2-instance"
ami_id = "ami-0abcdef1234567890"
instance_type = "t2.micro"
subnet_id = module.my_vpc.subnet_id
}
3.2. Backend zdalny (S3, Consul)
Terraform przechowuje stan infrastruktury w pliku terraform.tfstate
. Ten plik zawiera mapowanie między konfiguracją Terraform a rzeczywistymi zasobami. W środowiskach zespołowych, lokalne zarządzanie stanem jest ryzykowne, gdyż może prowadzić do nadpisywania zmian i błędów. Dlatego używa się backendów zdalnych:
- Amazon S3: Najpopularniejszy wybór, często w połączeniu z blokowaniem stanu za pomocą DynamoDB.
- HashiCorp Consul: Rozwiązanie do wykrywania usług i przechowywania konfiguracji.
- Terraform Cloud/Enterprise: Managed service od HashiCorp z wbudowanym zarządzaniem stanem i procesami.
Konfiguracja backendu S3 w main.tf
:
terraform {
backend "s3" {
bucket = "my-iac-tfstate-bucket"
key = "dev/network/terraform.tfstate"
region = "eu-central-1"
encrypt = true
dynamodb_table = "terraform-lock-table" # Do blokowania stanu
}
}
3.3. Praktyczne wskazówki dot. blokowania stanu
Blokowanie stanu (state locking) zapobiega jednoczesnym operacjom na tym samym pliku stanu Terraform, co jest kluczowe w pracy zespołowej. W przypadku S3, używa się tabeli DynamoDB do tego celu. Zawsze upewnij się, że Twój backend obsługuje blokowanie stanu i że jest ono aktywne.
4. Ansible jako warstwa konfiguracyjna
Podczas gdy Terraform koncentruje się na provisioningu infrastruktury, Ansible jest potężnym narzędziem do zarządzania konfiguracją, czyli instalacji oprogramowania, konfiguracji usług, zarządzania użytkownikami itp. Ansible jest narzędziem agentless (nie wymaga agenta na zarządzanych maszynach), komunikując się przez SSH.
4.1. Playbooki, role, inwentaryzacja
- Inwentaryzacja (Inventory): Plik (zazwyczaj YAML lub INI), który zawiera listę hostów, którymi Ansible ma zarządzać, wraz z ich grupami i zmiennymi.
- Playbooki: Pliki YAML definiujące zestaw zadań (tasks) do wykonania na określonych hostach.
- Role: Strukturalny sposób organizacji playbooków, zmiennych, szablonów i plików, promujący ponowne użycie kodu.
Przykład pliku inwentaryzacji (inventory.ini
):
[webservers]
web_server_1 ansible_host=10.0.1.10
web_server_2 ansible_host=10.0.1.11
[databases]
db_server_1 ansible_host=10.0.1.20
Przykład prostego playbooka (install_nginx.yml
):
---
- name: Install Nginx web server
hosts: webservers
become: true # Uruchamia zadania z uprawnieniami roota
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start Nginx service
service:
name: nginx
state: started
enabled: yes
4.2. Przykładowa konfiguracja serwera WWW/DB
Wywołanie playbooka:
ansible-playbook -i inventory.ini install_nginx.yml
5. Integracja Terraform ↔ Ansible
Po provisioningu infrastruktury za pomocą Terraform, potrzebujemy Ansible, aby skonfigurować nowo utworzone maszyny. Kluczowym elementem jest dynamiczne przekazywanie adresów IP lub nazw hostów z Terraform do inwentaryzacji Ansible.
5.1. Generowanie inwentaryzacji dynamicznej
Możemy użyć outputów Terraform, aby wygenerować plik inwentaryzacji dla Ansible. Poniżej przykład generowania dynamicznej inwentaryzacji w formacie JSON (lub INI):
# output.tf w katalogu głównym Terraform
output "ansible_inventory" {
value = {
_meta = {
hostvars = {
}
}
webservers = {
hosts = [for instance in aws_instance.web_server : instance.public_ip] # Zakładając publiczny IP
}
}
sensitive = true # Może zawierać wrażliwe dane
}
Następnie, po terraform apply
, możesz wywołać terraform output -json ansible_inventory > inventory.json
i użyć tego pliku z Ansible.
5.2. Wywoływanie playbooków z local-exec
/remote-exec
Terraform pozwala na wykonywanie komend lokalnych (local-exec
) lub zdalnych (remote-exec
) jako część cyklu życia zasobu. Jest to często używane do uruchamiania playbooków Ansible.
resource "aws_instance" "web_server" {
# ... konfiguracja instancji ...
provisioner "remote-exec" {
inline = [
"sudo apt update",
"sudo apt install -y python3", # Ansible wymaga Pythona
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
provisioner "local-exec" {
command = <
local-exec
/remote-exec
do wywoływania Ansible jest proste, ale pamiętaj, że idealnym rozwiązaniem jest użycie dedykowanych integracji w pipeline CI/CD, gdzie Terraform kończy provisioning, a Ansible jest wywoływany w kolejnym kroku.
6. Pipeline CI/CD
Automatyzacja procesów IaC w ramach pipeline’ów Continuous Integration/Continuous Delivery (CI/CD) jest kluczowa dla efektywności i niezawodności.
6.1. Przykład z GitLab CI (lub GitHub Actions)
Przykładowy etap w pliku .gitlab-ci.yml
dla projektu Terraform:
stages:
- validate
- plan
- apply
variables:
TF_ROOT: ${CI_PROJECT_DIR}
TF_STATE_NAME: ${CI_COMMIT_REF_SLUG} # Unikalna nazwa stanu dla brancha
default:
image:
name: hashicorp/terraform:latest
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
.terraform_template: &terraform_template
before_script:
- apk add --no-cache curl git openssh-client # Instalacja narzędzi potrzebnych do SSH/Git
- terraform --version
- terraform init -backend-config="bucket=my-iac-tfstate-bucket" -backend-config="key=${TF_STATE_NAME}/terraform.tfstate" -backend-config="region=eu-central-1"
validate:
<<: *terraform_template
stage: validate
script:
- terraform validate
- terraform fmt -check=true # Formatowanie kodu
- terraform-docs . # Generowanie dokumentacji modułów
allow_failure: true
plan:
<<: *terraform_template
stage: plan
script:
- terraform plan -out=plan.tfplan
artifacts:
paths:
- plan.tfplan
rules:
- if: $CI_COMMIT_BRANCH == "main"
apply:
<<: *terraform_template
stage: apply
script:
- terraform apply -auto-approve plan.tfplan
- # Tutaj można wywołać Ansible za pomocą dynamicznej inwentaryzacji lub dalszych kroków
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual # Ręczne zatwierdzenie apply na main/prod
- if: $CI_COMMIT_BRANCH != "main"
W GitHub Actions proces wygląda analogicznie, używając akcji takich jak hashicorp/setup-terraform
i odpowiednich komend.
7. Praktyczne best practices
- Wersjonowanie kodu: Cała infrastruktura powinna być traktowana jak kod aplikacji i przechowywana w systemie kontroli wersji (Git).
- Secrets Management: Nigdy nie umieszczaj poufnych danych (kluczy API, haseł) bezpośrednio w kodzie Terraform czy Ansible. Użyj dedykowanych rozwiązań takich jak HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager lub zaszyfrowanych plików (Ansible Vault).
- Testy infrastruktury:
- Terratest: Biblioteka Go do pisania automatycznych testów end-to-end dla infrastruktury zarządzanej przez Terraform. Pozwala testować, czy wdrożone zasoby działają zgodnie z oczekiwaniami.
- InSpec: Framework do testowania zgodności i bezpieczeństwa systemów, często używany w połączeniu z Ansible do weryfikacji konfiguracji.
- Idempotencja: Upewnij się, że Twoje konfiguracje Terraform i Ansible są idempotentne, co oznacza, że wielokrotne uruchomienie kodu daje ten sam wynik.
- Modularność: Twórz małe, reusable moduły i role, które są łatwe do testowania i utrzymania.
- Dokumentacja: Generuj automatycznie dokumentację modułów (np. za pomocą
terraform-docs
) i utrzymuj przejrzyste README.md.
8. Podsumowanie i dalsze kroki
Zarządzanie infrastrukturą jako kod z wykorzystaniem Terraform do provisioningu i Ansible do konfiguracji to potężne połączenie, które pozwala na budowanie, wdrażanie i zarządzanie złożonymi środowiskami w sposób automatyczny, spójny i powtarzalny. Integracja tych narzędzi w pipeline CI/CD dodatkowo przyspiesza procesy i minimalizuje ryzyko błędów.
Dalsze kroki:
- Zapoznaj się z oficjalną dokumentacją: Terraform Docs, Ansible Docs.
- Eksperymentuj z różnymi providerami chmurowymi (AWS, Azure, GCP), aby zrozumieć ich specyfikę.
- Zbuduj swój pierwszy pipeline CI/CD dla prostej infrastruktury, a następnie rozbudowuj go.
- Zapoznaj się z narzędziami do zarządzania sekretami i testowania infrastruktury.