Projektowanie stron internetowych i usługi IT

|

Każdy system potrzebuje swojego

POLIGON'u

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.
i IaC jest fundamentem praktyk DevOps, umożliwiając szybkie i bezpieczne dostarczanie oprogramowania.

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.

Schemat działania Terraform

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.

Tip Zawsze używaj zdalnego backendu dla stanu Terraform w środowiskach produkcyjnych.

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 = <
! Użycie 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.
Tip Rozpocznij od małych projektów i stopniowo zwiększaj ich złożoność, aby zbudować solidne podstawy w IaC.

Partnerzy strategiczni

wpisz help
Terminal
$
Switch language