Terraform AWS Cloud: rozsądne zarządzanie infrastrukturą

Opublikowany: 2022-03-11

Napisanie wniosku to tylko część historii. Aby był wartościowy, musi zostać wdrożony w miejscu, w którym można go skalować, musi działać z wysoką dostępnością, potrzebuje kopii zapasowych i tak dalej.

Coraz więcej programistów wymaga przynajmniej znajomości tego procesu wdrażania. Objawia się to tym, że na przykład DevOps staje się często pożądaną rolą w dzisiejszych czasach, gdy rośnie złożoność systemu. Nie możemy pozwolić sobie na ignorowanie tych zmian i musimy wiedzieć, jak projektować aplikacje, aby były łatwe do wdrożenia.

Leży to również w interesie naszych klientów: zatrudniają nas jako ekspertów w swoich dziedzinach i oczekują, że dostarczymy cały produkt, często od początku do końca. Mają wymagania i często są nieświadomi stosu, na którym działa ich rozwiązanie biznesowe. Ostatecznie liczy się wartość biznesowa produktu.

Przedstawiamy Terraform

Zarządzanie wdrażaniem i infrastrukturą nie jest prostym procesem. Oprócz wielu ciągle zmieniających się, domen, musimy nauczyć się jeszcze innego narzędzia lub nowego przepływu pracy. Jeśli odkładasz to, ten artykuł jest dla Ciebie szansą na zapoznanie się z jednym podejściem do zarządzania infrastrukturą. Mam nadzieję, że w końcu będziesz bardziej pewny siebie w korzystaniu z Terraform i dowiesz się więcej o możliwych podejściach i wyzwaniach. Powinieneś być w stanie użyć tego narzędzia, aby przynajmniej zacząć zarządzać częścią swojej infrastruktury chmurowej.

Terraform to warstwa abstrakcji, tak, a abstrakcje są nieszczelne, zgadzam się. Ale w końcu zajmujemy się rozwiązywaniem problemów i zarządzaniem abstrakcjami. Ten ma na celu zapewnienie nam więcej zdrowego rozsądku w naszych codziennych zadaniach.

Cel

Wyjaśnię, czym jest Terraform, jak pasuje do całego ekosystemu i jak wypada na tle innych, podobnych narzędzi. Następnie pokażę Ci kroki potrzebne do skonfigurowania wielośrodowiskowej i gotowej do produkcji konfiguracji Terraform dla zespołu. Wyjaśnię podstawy pisania konfiguracji Terraform — jak zarządzać złożonością i powielaniem kodu za pomocą modułów, które można udostępniać.

Wszystkie przykłady będą dotyczyły jednego dostawcy chmury: Amazon Web Services (AWS). To tylko chmura, z którą mam największe doświadczenie, ale wszystkie informacje powinny dotyczyć również innych chmur.

Zakończę kilkoma uwagami, o których żałuję, że nie wiedziałem, kiedy zaczynałem: niektóre błędy składni, dziwactwa i przypadki, w których Terraform nie byłby moim wyborem.

Nie będę skupiał się na drobiazgowych szczegółach składni. Możesz szybko się z tym zapoznać, czytając fantastyczną dokumentację i przewodniki, które HashiCorp zapewnia dla Terraform. Chciałbym skupić się na rzeczach, które mogą nie być oczywiste od samego początku i których żałuję, że nie wiedziałem, zanim zacząłem pracować z Terraformem. Mamy nadzieję, że wskaże ci to właściwy kierunek i pozwoli rozważyć Terraform jako narzędzie do wykorzystania w przyszłych projektach.

Zarządzanie infrastrukturą jest trudne

Konfiguracja środowiska aplikacji w chmurze składa się z wielu etapów. Jeśli nie zapiszesz ich wszystkich jako szczegółowych list kontrolnych i nie będziesz ich ściśle przestrzegał, przez cały czas będziesz popełniał błędy; w końcu jesteśmy ludźmi. Trudno się podzielić tymi krokami. Musisz udokumentować wiele ręcznych procedur, a dokumenty mogą szybko stać się nieaktualne. Teraz pomnóż to przez całkowitą liczbę środowisk dla jednej aplikacji: dev , test/qa , stage i prod . Musisz także pomyśleć o bezpieczeństwie każdego z nich.

Czy nie każde narzędzie ma interfejs użytkownika, którego mogę używać i zapomnieć o złożoności?

Mają interfejsy użytkownika — AWS Management Console to doskonały przykład. Ale te narzędzia robią wiele pod maską. Jedno kliknięcie interfejsu użytkownika może wywołać kaskadę trudnych do uchwycenia zmian. Zwykle nie ma możliwości cofnięcia tego, co zrobiłeś w interfejsie użytkownika (zwykłe monity „Czy na pewno?” często nie wystarczają). Co więcej, zawsze dobrze jest mieć drugą parę oczu, aby sprawdzić zmiany, ale kiedy używamy interfejsów użytkownika, musielibyśmy usiąść z tą osobą razem lub sprawdzić nasze zmiany po ich wprowadzeniu, co jest bardziej audytem niż recenzja. Każdy dostawca chmury ma swój własny interfejs użytkownika, który musisz opanować. Interfejsy użytkownika są zaprojektowane tak, aby były łatwe w użyciu (niekoniecznie proste!) i jako takie są podatne na urojenia, takie jak „to tylko niewielka poprawka” lub szybka poprawka produkcyjna, o której zapomnisz w ciągu 48 godzin. Takie ręczne klikanie jest również bardzo trudne do zautomatyzowania.

A co z narzędziami CLI?

W naszych przypadkach byłyby lepsze niż narzędzia interfejsu użytkownika. Jednak nadal masz skłonność do ręcznego wprowadzania zmian lub pisania skryptów bash, które łatwo mogą wymknąć się spod kontroli. Ponadto każdy dostawca ma swoje własne narzędzia CLI. Dzięki tym narzędziom nie możesz zobaczyć swoich zmian, zanim się na nie zdecydujesz. Na szczęście nie jest to nowy problem i istnieją narzędzia, które w tym pomagają.

Orkiestracja a zarządzanie konfiguracją

Zdefiniuję niektóre kategorie narzędzi i praktyk, które służą do zarządzania infrastrukturą. Są to zarządzanie orkiestracją i konfiguracją. Możesz myśleć o nich z grubsza jako o deklaratywnych i imperatywnych modelach programowania (pomyśl o Prologu i Pythonie). Każdy ma swoje plusy i minusy, ale najlepiej znać je wszystkie i zastosować najlepsze narzędzie do danej pracy. Ponadto orkiestracja zwykle obejmuje wyższy poziom abstrakcji niż zarządzanie konfiguracją.

Orkiestracja

Orkiestracja przypomina bardziej deklaratywny paradygmat programowania. Lubię myśleć o nim jako o dyrygencie orkiestry. Pracę dyrygenta ładnie podsumowuje Wikipedia (link) jako „sztuka kierowania gestem jednoczesnego występu kilku wykonawców lub śpiewaków”. Dyrygenci komunikują rytm i tempo, dynamikę i wskazówki muzyczne, ale rzeczywistą pracę wykonują poszczególni muzycy, którzy są ekspertami w grze na swoich instrumentach muzycznych. Bez takiej koordynacji nie byliby w stanie wykonać utworu idealnego.

Tutaj pasuje Terraform. Używasz go do prowadzenia fragmentu infrastruktury IT: mówisz mu, co ma zostać wdrożone, a Terraform łączy to wszystko razem i wykonuje wszystkie niezbędne wywołania interfejsu API. W tej przestrzeni są podobne narzędzia; jednym z najbardziej znanych jest AWS Cloud Formation. Ma lepszą obsługę odzyskiwania i wycofywania w przypadku błędów niż Terraform, ale także, moim zdaniem, bardziej stromą krzywą uczenia się. Nie jest również niezależny od chmury: działa tylko z AWS.

Zarządzanie konfiguracją

Uzupełniającą stroną tych praktyk jest podejście do zarządzania konfiguracją. W tym paradygmacie dokładnie określasz kroki, jakie musi wykonać narzędzie, aby uzyskać daną, pożądaną konfigurację. Same kroki mogą być małe i obsługiwać wiele systemów operacyjnych, ale musisz aktywnie myśleć o kolejności ich wykonywania. Nie mają świadomości obecnego stanu i otoczenia (chyba że ich tak zaprogramowałeś) i jako takie będą ślepo wykonywać wszelkie kroki, jakie im podasz. Może to prowadzić do problemu znanego jako dryf konfiguracji , w którym zasoby będą powoli desynchronizować się z tym, co początkowo miały reprezentować, zwłaszcza jeśli wprowadzono w nich ręcznie zmiany. Świetnie radzą sobie z zarządzaniem i dostarczaniem usług na poszczególnych instancjach. Przykładami narzędzi, które doskonale sprawdzają się w tym przepływie pracy, są Chef, Puppet, Ansible i Salt.

Orkiestracja wymusza podejście do infrastruktury, w którym traktujesz swoje zasoby jak bydło, a nie jak zwierzęta domowe. Zamiast ręcznie „pielęgnować” każdy VPS, możesz zastąpić je dokładną kopią, gdy coś pójdzie nie tak. Nie chodzi mi o to, że po prostu cię to nie obchodzi i zaczynasz od nowa, mając nadzieję na najlepsze.

Mem z programu IT Crowd TV z kultowym hasłem: Czy próbowałeś go wyłączyć i włączyć ponownie?

Zamiast tego należy zbadać i naprawić problem w kodzie, a następnie go wdrożyć.

Ansible (i inne narzędzia CM) można wykorzystać do zarządzania infrastrukturą AWS, ale wymagałoby to dużo pracy i jest bardziej podatne na błędy, zwłaszcza gdy infrastruktura często się zmienia i rośnie złożoność.

Jedną ważną rzeczą do zapamiętania jest to, że metody orkiestracji i zarządzania konfiguracją nie są ze sobą sprzeczne. Są kompatybilne. Idealnie jest mieć grupę instancji EC2 (VPS) w grupie AutoScaling zarządzanej przez Terraform, ale z uruchomionym obrazem aplikacji AWS (AMI), który jest migawką dysku, który został przygotowany z imperatywnymi krokami za pomocą np. Ansible . Terraform ma nawet koncepcję „dostawców”, którzy umożliwiają uruchamianie zewnętrznych narzędzi aprowizacji po uruchomieniu maszyny.

Dokumentacja Terraform to świetna praca, aby wyjaśnić to dokładniej i pomóc w umieszczeniu Terraform w całym ekosystemie.

Co to jest Terraform?

Jest to narzędzie typu open source, stworzone przez HashiCorp, które umożliwia kodyfikację infrastruktury jako deklaratywne pliki konfiguracyjne, które są wersjonowane i udostępniane oraz mogą być przeglądane.

Nazwa HashiCorp powinna brzmieć jak dzwon — tworzą także Nomad, Vault, Packer, Vagrant i Consul. Jeśli korzystałeś z któregokolwiek z tych narzędzi, znasz już jakość dokumentacji, żywą społeczność i użyteczność, której możesz oczekiwać od ich rozwiązań.

Infrastruktura jako kod

Terraform jest niezależny od platformy; możesz go używać do zarządzania serwerami bare metal lub serwerami w chmurze, takimi jak AWS, Google Cloud Platform, OpenStack i Azure. W żargonie Terraform są one nazywane dostawcami . Możesz zorientować się w skali, czytając pełną listę obsługiwanych dostawców. Wielu dostawców może być używanych w tym samym czasie, na przykład gdy dostawca A konfiguruje dla Ciebie maszyny wirtualne, ale dostawca B konfiguruje i deleguje rekordy DNS.

Czy to oznacza, że ​​można zmienić dostawców chmury za pomocą jednej zmiany w pliku konfiguracyjnym? Nie, nawet nie sądzę, żebyś tego chciał, przynajmniej nie w sposób zautomatyzowany. Problem polega na tym, że różni dostawcy mogą mieć różne możliwości, różne oferty, przepływy, pomysły itp. Oznacza to, że będziesz musiał użyć różnych zasobów dla różnych dostawców, aby wyrazić tę samą koncepcję. Jednak nadal można to wszystko zrobić w jednej, znanej składni konfiguracji i być częścią spójnego przepływu pracy.

Niezbędne części konfiguracji Terraform

  1. Sam plik binarny Terraform, który musisz zainstalować
  2. Pliki z kodem źródłowym, czyli twoja konfiguracja
  3. Stan (lokalny lub zdalny), który reprezentuje zasoby, którymi zarządza Terraform (więcej na ten temat później)

Pisanie konfiguracji Terraform

Kod konfiguracyjny Terraforma piszesz w plikach *.tf przy użyciu języka HCL. Istnieje możliwość użycia formatu JSON ( *.tf.json ), ale jest on ukierunkowany na maszyny i automatyczne generowanie, a nie na ludzi. Polecam trzymać się HCL. Nie będę zagłębiać się w składnię języka HCL; oficjalne dokumenty wykonują fantastyczną robotę, opisując, jak pisać HCL oraz jak używać zmiennych i interpolacji. Wspomnę tylko o absolutnym minimum potrzebnym do zrozumienia przykładów.

Wewnątrz plików Terraform masz do czynienia głównie z zasobami i źródłami danych . Zasoby reprezentują komponenty Twojej infrastruktury, np. instancję AWS EC2, instancję RDS, rekord DNS Route53 lub regułę w grupie bezpieczeństwa. Pozwalają na aprowizację i zmianę w architekturze chmury.

Zakładając, że skonfigurowałeś Terraform, jeśli wydasz aplikację terraform apply , poniższy kod utworzy w pełni funkcjonalną instancję EC2 (pokazane są tylko niektóre właściwości):

 resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }

Z drugiej strony istnieją źródła danych, które pozwalają na odczytanie danych o poszczególnych komponentach bez ich zmiany. Chcesz uzyskać identyfikator AWS (ARN) certyfikatu wydanego przez ACM? Korzystasz ze źródła danych. Różnica polega na tym, że źródła danych są poprzedzone przedrostkiem data_ podczas odwoływania się do nich w plikach konfiguracyjnych.

 data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }

Powyższe odnosi się do wydanego certyfikatu SSL ACM, który może być używany razem z AWS ALB. Jednak zanim to wszystko zrobisz, musisz skonfigurować swoje środowisko.

Struktura folderów

Środowiska Terraform (i ich stany) są oddzielone katalogami. Terraform ładuje wszystkie pliki *.tf w katalogu do jednej przestrzeni nazw, więc kolejność nie ma znaczenia. Polecam następującą strukturę katalogów:

 /terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
  1. default_variables.tf – zdefiniuj wszystkie zmienne najwyższego poziomu i opcjonalnie ich wartości domyślne. Mogą być ponownie użyte w każdym środowisku (katalog zagnieżdżony) z dowiązaniami symbolicznymi.
  2. /stage/ – katalog zawierający konfigurację całego osobnego środowiska (tutaj nazwanego stage , ale może to być cokolwiek). Wszelkie zmiany wprowadzone w tym folderze są całkowicie niezależne od innych środowisk (np. env — jak prod ), co jest czymś, czego potrzebujesz, aby uniknąć zepsucia środowiska produkcyjnego zmianami wprowadzonymi na stage !
  3. terraform.tfvars – określ wartości zmiennych. Pliki .tfvars są podobne do plików .env , ponieważ przechowują pary key=val dla zdefiniowanych zmiennych. Na przykład określa to profile AWS , key_name AWS i key_path AWS, których używam. Można to zignorować w Git.
  4. default_variables.tf – jest to dowiązanie symboliczne do pliku (2), które pozwala nam udostępniać zmienne niezależne od środowiska bez powtarzania się.
  5. terraform.tf – to główna konfiguracja każdego środowiska; przechowuje blok terraform {} , który konfiguruje back-end. Tutaj również konfiguruję dostawców .
  6. env_variables.tf – ten plik zawiera zmienne specyficzne dla środowiska env. Oznaczam wszystkie zasoby za pomocą Env=<env_name> w AWS, więc ten plik zwykle definiuje tylko jedną zmienną: env .

Oczywiście nie jest to jedyny sposób na uporządkowanie środowiska. To po prostu działało dla mnie dobrze, umożliwiając wyraźne oddzielenie obaw.

Konfiguracja zaplecza

Wspomniałem już o stanie Terraform. Jest to istotna część przepływu pracy Terraform. Możesz się zastanawiać, czy stan jest rzeczywiście wymagany. Czy Terraform nie mógłby po prostu cały czas wysyłać zapytań do interfejsu API AWS, aby uzyskać rzeczywisty stan infrastruktury? Cóż, jeśli się nad tym zastanowić, Terraform musi zachować mapowanie między tym, czym zarządza w deklaratywnych plikach konfiguracyjnych, a tym, czemu te pliki faktycznie odpowiadają (w środowisku dostawcy chmury). Zwróć uwagę, że podczas pisania plików konfiguracyjnych Terraform nie przejmujesz się identyfikatorami np. poszczególnych wystąpień EC2 lub ARN, które zostaną utworzone dla publikowanych grup zabezpieczeń. Jednak wewnętrznie Terraform musi wiedzieć, że dany blok zasobów reprezentuje konkretny zasób z identyfikatorem/ARN. Jest to wymagane do wykrywania zmian. Co więcej, stan służy do śledzenia zależności między zasobami (również coś, o czym zwykle nie musisz myśleć!). Służą do konstruowania grafu, który można (zwykle) zrównoleglić i wykonać. Jak zawsze, polecam zapoznać się z doskonałą dokumentacją dotyczącą stanu Terraform i jego celu.

Ponieważ stan jest jedynym źródłem prawdy dla Twojej architektury, musisz upewnić się, że Ty i Twój zespół zawsze pracujecie nad jego najbardziej aktualną wersją i że nie tworzysz konfliktów przez niezsynchronizowany dostęp do stanu. Nie chcesz rozwiązywać konfliktów scalania w pliku stanu, uwierz mi.

Domyślnie Terraform przechowuje stan w pliku na dysku, znajdującym się w bieżącym katalogu roboczym (każdego środowiska) jako plik terraform.tfstate . Jest to w porządku, jeśli wiesz, że będziesz jedynym programistą w pracy lub dopiero uczysz się i eksperymentujesz z Terraformem. Technicznie rzecz biorąc, możesz sprawić, by działał w zespole, ponieważ możesz przekazać stan do repozytorium VCS. Ale wtedy musisz upewnić się, że wszyscy zawsze pracują nad najnowszą wersją stanu i że nikt nie wprowadza zmian w tym samym czasie! Jest to na ogół poważny ból głowy i zdecydowanie odradzam. Poza tym, jeśli ktoś dołączy do twojej operacji single-dev, nadal będziesz musiał skonfigurować alternatywne miejsce dla państwa.

Na szczęście jest to problem z dobrymi rozwiązaniami wbudowanymi w Terraform: tzw. Remote State . Aby stan zdalny działał, należy skonfigurować zaplecze przy użyciu jednego z dostępnych dostawców zaplecza. Poniższy przykład zaplecza będzie oparty na AWS S3 i AWS DynamoDB (baza danych AWS NoSQL). Mógłbyś użyć tylko S3, ale wtedy tracisz mechanizm blokowania stanów i sprawdzania spójności (niezalecane). Jeśli wcześniej korzystałeś tylko ze stanu lokalnego, skonfigurowanie zdalnego zaplecza zaoferuje opcję migracji stanu za pierwszym razem, dzięki czemu nic nie stracisz. Więcej o konfiguracji zaplecza można przeczytać tutaj.

Niestety jest problem z kurczakiem i jajkiem: wiadro S3 i stół DynamoDB muszą być tworzone ręcznie. Terraform nie może ich utworzyć automatycznie, ponieważ nie ma jeszcze stanu! Cóż, jest kilka rozwiązań, takich jak https://github.com/gruntwork-io/terragrunt, które automatyzują to za pomocą AWS CLI, ale nie chcę odchodzić od głównego tematu tego wpisu na blogu.

Ważne rzeczy, które należy wiedzieć o konfiguracji backendu S3 i DynamoDB, to:

  1. Włącz wersjonowanie w wiadrze S3, aby zabezpieczyć się przed błędami ludzkimi i prawem Murphy'ego.
  2. Tabela DynamoDB ma limit szybkości odczytów i zapisów (tzw. pojemność). Jeśli wprowadzasz wiele zmian w stanie zdalnym, upewnij się, że włączyłeś automatyczne skalowanie DynamoDB dla tej tabeli lub skonfiguruj wystarczająco wysokie limity odczytu/zapisu. W przeciwnym razie Terraform otrzyma błędy HTTP 400 z interfejsu API AWS podczas wykonywania wielu wywołań.

Podsumowując, następującą konfigurację zaplecza można umieścić w terraform.tf , aby skonfigurować zdalny stan na S3 i DynamoDB.

 terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }

To dużo do ogarnięcia na raz, ale pamiętaj, robisz to raz na każde env, a potem możesz o tym zapomnieć. Jeśli potrzebujesz jeszcze większej kontroli nad blokowaniem stanu, jest HashiCorp Terraform Enterprise, ale nie będę tego tutaj omawiał.

Dostawcy

Aby ten back-end był dostępny i aby w ogóle móc komunikować się z naszym dostawcą chmury, musimy skonfigurować tak zwanego providera . W pliku terraform.tf (dla każdego env) można umieścić następujący blok:

 provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }

Zmienne dla profile i region są przechowywane w pliku terraform.tfvars , który można zignorować. Zmienna profile odnosi się do nazwanego profilu przechowującego poświadczenia bezpieczeństwa dla chmury AWS przy użyciu standardowego pliku poświadczeń. Zauważ, że ustawiam też pewne ograniczenia wersji. Nie chcesz, aby Terraform uaktualniał wtyczki dostawcy bez Twojej wiedzy na temat każdej inicjalizacji zaplecza. Szczególnie biorąc pod uwagę, że istnieje wersja 2.x dostawcy AWS, która wymaga starannego uaktualnienia.

Inicjalizacja zaplecza

Każda konfiguracja zaplecza wymaga na początku kroku inicjalizacji i za każdym razem, gdy wprowadzana jest w niej zmiana. Inicjalizacja konfiguruje również moduły Terraform (więcej o nich później), więc gdy je dodasz, musisz również ponownie uruchomić krok init. Ta operacja jest bezpieczna do wielokrotnego uruchomienia. Zwróć uwagę, że podczas inicjowania zaplecza nie wszystkie zmienne mogą być odczytane przez Terraform w celu skonfigurowania stanu, ani nie powinny być one ze względów bezpieczeństwa (np. tajne klucze). Aby rozwiązać ten problem i, w naszym przypadku, użyć innego profilu AWS niż domyślny, możesz użyć opcji -backend-config z akceptowanymi parami zmiennych k=v . Nazywa się to konfiguracją częściową.

 terraform init -backend-config=profile=<aws_profile_name>

Pliki Terraform współdzielą zakres ograniczony do danego katalogu. Oznacza to, że podfolder nie jest bezpośrednio połączony z kodem katalogu nadrzędnego. Jest to jednak zdefiniowanie modułu, który pozwala na ponowne wykorzystanie kodu, zarządzanie złożonością i udostępnianie.

Przepływ pracy

Ogólny przepływ pracy podczas pracy z kodem Terraform wygląda następująco:

  1. Napisz konfigurację dla swojej infrastruktury.
  2. Zobacz, jakie faktyczne zmiany wprowadzi ( terraform plan ).
  3. Opcjonalnie wykonaj dokładnie te zmiany, które widziałeś w kroku 2 ( terraform apply ).
  4. PRZEJDŹ 1

Plan Terraformy

Polecenie Terraform plan wyświetli listę zmian, które zostaną wprowadzone w Twojej infrastrukturze po wydaniu polecenia apply . Wielokrotne wystawianie plan jest bezpieczne, ponieważ samo w sobie niczego nie zmienia.

Jak czytać plan

Obiekty w Terraform (zasoby i źródła danych) można łatwo zidentyfikować na podstawie ich w pełni kwalifikowanych nazw.

  1. W przypadku zasobów identyfikator może wyglądać następująco: <resource_type>.<resource_name> nazwa_zasobu> —np. aws_ecs_service.this .
  2. W przypadku zasobów wewnątrz modułów mamy dodatkową nazwę modułu: module.<module_name>.<resource_type>.<resource_name> nazwa_zasobu> —np. module.my_service_ecs_service_task.aws_ecs_service.this .
  3. Źródła danych (wewnątrz i na zewnątrz modułu): (module.<module_name>).data.<resource_type>.<resource_name> nazwa_zasobu> —np. module.my_service_ecs_service_task.data.aws_ecs_task_definition.this .

Typ zasobu jest specyficzny dla danego dostawcy i zwykle zawiera jego nazwę ( aws_… ). Cała lista dostępnych zasobów dla AWS znajduje się w dokumentacji.

Istnieje pięć działań, które plan pokaże dla danych zasobów:

  1. [+] Dodaj – zostanie utworzony nowy zasób.
  2. [-] Destroy – Zasób zostanie całkowicie zniszczony.
  3. [~] Modyfikuj w miejscu — zasób zostanie zmodyfikowany i jeden lub więcej parametrów zostanie zmieniony. Jest to ogólnie bezpieczne. Terraform pokaże Ci, które parametry zostaną zmodyfikowane iw jaki sposób (jeśli to możliwe).
  4. [- / +] – Zasób zostanie usunięty , a następnie odtworzony z nowymi parametrami. Dzieje się tak, jeśli dokonano zmiany parametru, którego nie można zmienić na miejscu. Terraform pokaże ci, które zmiany wymuszają odtworzenie zasobu z następującym komentarzem na czerwono: (forces new resource) . Jest to potencjalnie niebezpieczne, ponieważ istnieje okres, w którym zasób w ogóle nie będzie istniał. Może również złamać inne, powiązane zależności. Zalecam obejście takich zmian, chyba że wiesz, jakie będą konsekwencje lub nie zależy Ci na przestoju.
  5. [<=] – zostanie odczytane źródło danych. To jest operacja tylko do odczytu.

Terraform AWS — przykładowy „plan”

Powyżej znajduje się przykładowy plan. Wprowadzone przeze mnie zmiany to:

  • Zmieniono typ_instancji pierwszej instance_type bastionu
  • Dodano drugą instancję bastionu
  • Zmieniono nazwę grupy bezpieczeństwa

Zauważ, że ostatnia zmiana to reguła grupy zabezpieczeń, która automatycznie wykryła zmianę nazwy grupy nadrzędnej. Moim zdaniem cały plan jest bardzo czytelny. Niektóre wartości parametrów są wyświetlane jako <computed> — nie oznacza to, że zostaną zmienione, ale raczej, że nie można ich pobrać i zaprezentować na tym etapie (tak jak nazwa SG, która nie została jeszcze utworzona). Pamiętaj, że podczas polecenia plan zostaną pokazane tylko zmienione zasoby. Wszystko, co zostanie pominięte, nie zostanie dotknięte.

Zwykle polecenia Terraform akceptują dodatkowe parametry. Najważniejszym parametrem polecenia plan jest opcja -out , która zapisze Twój plan na dysku. Ten zapisany plan można następnie wykonać (dokładnie tak, jak został zapisany) za pomocą polecenia Apply. Jest to bardzo ważne, ponieważ w przeciwnym razie może nastąpić zmiana, na przykład, dokonana przez współpracowników pomiędzy wydaniem plan a wydaniem apply . Przykład:

  1. Wydaj plan i sprawdź, czy wygląda dobrze.
  2. Ktoś zmienia stan bez twojej wiedzy.
  3. Problem apply i – ups, zrobił coś innego niż to, co było zaplanowane!

Na szczęście ten przepływ pracy został ulepszony w Terraform v0.11.0. Od tej wersji polecenie apply automatycznie przedstawia plan, który musisz następnie zatwierdzić (poprzez wyraźne wpisanie yes ). Zaletą jest to, że jeśli zastosujesz ten plan, zostanie on wykonany dokładnie tak, jak przedstawiono.

Kolejną przydatną opcją jest -destroy , która pokaże ci plan, którego efektem będzie zniszczenie wszystkich surowców zarządzanych przez Terraform. Możesz także celować w określone zasoby do zniszczenia za pomocą opcji -target .

Zastosuj Terraform

Kiedy zastosujemy dany plan, Terraform wychodzi i blokuje nasz stan, aby zapewnić sobie wyłączność. Następnie przystępuje do zmiany zasobów i na koniec wypycha zaktualizowany stan. Należy zauważyć, że niektóre zasoby wymagają więcej czasu niż inne. Na przykład tworzenie instancji AWS RDS może zająć więcej niż 12 minut, a Terraform poczeka na zakończenie tego procesu. Oczywiście Terraform jest na tyle sprytny, że nie blokuje przez to każdej innej operacji. tworzy ukierunkowany wykres żądanych zmian i, jeśli nie ma współzależności, wykorzystuje równoległość, aby przyspieszyć wykonanie.

Importowanie zasobów

Często Terraform i inne rozwiązania „konfiguracyjne jako kod” są wprowadzane stopniowo, do już istniejącego środowiska. Przejście jest naprawdę łatwe. Zasadniczo wszystko, co nie jest zdefiniowane w Terraform, pozostaje niezarządzane i niezmienione. Terraform zajmuje się tylko tym, czym zarządza. Oczywiście możliwe jest, że spowoduje to problemy — na przykład, jeśli Terraform opiera się na jakimś punkcie końcowym, który istnieje poza jego konfiguracją, a następnie zostanie ręcznie zniszczony. Terraform nie wie o tym i dlatego nie może odtworzyć tego zasobu podczas zmian stanu, co spowoduje błędy interfejsu API podczas wykonywania planu. Nie jest to coś, o co bym się martwił; korzyści z wprowadzenia Terraformu znacznie przewyższają wady.

Aby ułatwić proces wprowadzania, Terraform zawiera polecenie import . Służy do wprowadzania już istniejących zasobów zewnętrznych do stanu Terraform i umożliwiania zarządzania tymi zasobami. Niestety Terraform nie jest w stanie automatycznie wygenerować konfiguracji dla tych importowanych modułów, przynajmniej w momencie pisania tego tekstu. Istnieją plany dotyczące tej funkcji, ale na razie musisz najpierw napisać definicję zasobu w Terraform, a następnie zaimportować ten zasób, aby poinformować Terraform o rozpoczęciu zarządzania nim. Po zaimportowaniu do stanu (i podczas planowania wykonania) zobaczysz wszystkie różnice między tym, co napisałeś w plikach .tf , a tym, co faktycznie istnieje w chmurze. W ten sposób możesz dalej modyfikować konfigurację. W idealnym przypadku nie powinny pojawić się żadne zmiany, co oznaczałoby, że konfiguracja Terraform odzwierciedla to, co już jest w chmurze 1:1.

Moduły

Moduły są istotną częścią konfiguracji Terraform i zalecam ich objęcie i częste ich używanie. Zapewniają sposób na ponowne wykorzystanie niektórych komponentów. Przykładem może być klaster AWS ECS, który służy do uruchamiania kontenerów Dockera. Aby taki klaster działał, potrzebnych jest wiele osobnych zasobów do skonfigurowania: sam klaster, konfiguracja uruchamiania, która zarządzałaby oddzielnymi instancjami EC2, repozytoria kontenerów dla obrazów, grupa i polityka autoskalowania i tak dalej. Zwykle potrzebujesz oddzielnych klastrów dla osobnych środowisk i/lub aplikacji.

Jednym ze sposobów na pokonanie tego byłoby skopiowanie i wklejenie konfiguracji, ale jest to oczywiście rozwiązanie krótkowzroczne. Popełniłbyś błędy przy najprostszych aktualizacjach.

Moduły umożliwiają enkapsulację wszystkich tych oddzielnych zasobów w jednym bloku konfiguracyjnym (zwanym modułem). Moduły definiują wejścia i wyjścia , które są interfejsami, przez które komunikuje się ze „światem zewnętrznym” (lub kodem wywołującym). Co więcej, moduły mogą być zagnieżdżane w innych modułach, co pozwala na szybkie rozkręcenie całych oddzielnych środowisk.

Moduły lokalne i zdalne

Podobnie jak w przypadku stanu, możesz mieć moduły lokalne lub moduły zdalne. Moduły lokalne są przechowywane wraz z konfiguracją Terraform (w osobnym katalogu, poza każdym środowiskiem, ale w tym samym repozytorium). Jest to w porządku, jeśli masz prostą architekturę i nie udostępniasz tych modułów.

Ma to jednak ograniczenia. Trudno jest wersjonować te moduły i udostępniać je. Wersjonowanie jest ważne, ponieważ możesz chcieć użyć wersji 1.0.0 swojego modułu ECS w środowisku produkcyjnym, ale chciałbyś poeksperymentować z wersją 1.1.0 w środowisku przejściowym. Jeśli moduł był przechowywany wraz z kodem, każda zmiana w kodzie modułu byłaby odzwierciedlona w każdym środowisku (po uruchomieniu apply ), co zwykle jest niepożądane.

Jednym z przydatnych podejść do wersjonowania modułów jest umieszczenie ich wszystkich w osobnym repozytorium, np. twoja-firma/terraform-moduły. Następnie, odwołując się do tych modułów w konfiguracji Terraform, możesz użyć łącza VCS jako źródła:

 module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }

Odwołuję się tutaj do wersji 1.1.0 my-module (określona ścieżka), którą mogę przetestować niezależnie od innych wersji tego samego modułu w różnych środowiskach.

Poza tym istnieje kwestia odnajdywania i udostępniania modułów. Powinieneś starać się pisać dobrze udokumentowane i wielokrotnego użytku moduły. Zwykle będziesz mieć różne konfiguracje Terraform dla różnych aplikacji i możesz chcieć udostępnić między nimi ten sam moduł. Bez wyodrębnienia ich do osobnego repozytorium byłoby to bardzo trudne.

Korzystanie z modułów

Do modułów można łatwo odwoływać się w środowiskach Terraform, definiując specjalny blok modułu. Oto przykład takiego bloku dla hipotetycznego modułu ECS:

 module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }

Wszystkie przekazywane opcje (oprócz kilku globalnych, takich jak source ) zostały zdefiniowane w konfiguracji tego modułu jako wejścia (zmienne).

Rejestr modułów

Niedawno HashiCorp uruchomił oficjalny rejestr modułów Terraform. To świetna wiadomość, ponieważ możesz teraz czerpać z wiedzy społeczności, która opracowała już sprawdzone w boju moduły. Co więcej, niektóre z nich mają odznakę „HashiCorp Verified Module”, co oznacza, że ​​są weryfikowane i aktywnie utrzymywane, co daje dodatkową pewność siebie.

Wcześniej albo trzeba było pisać własne moduły od podstaw (i uczyć się na błędach) albo korzystać z modułów opublikowanych na GitHubie i innych miejscach, bez żadnych gwarancji co do ich zachowania (poza odczytaniem kodu!)

Udostępnianie danych między środowiskami

Idealnie, środowiska powinny być całkowicie oddzielne, nawet przy użyciu różnych kont AWS. In reality, there are cases when one Terraform environment might use some information in another environment. This is especially true if you are gradually converting your architecture to use Terraform. One example might be that you have a global env that provides certain resources to other envs.

Let's say env global shares data with stage . For this to work, you can define outputs at the main level of the environment like so:

 output "vpc_id" { value = "${module.network.vpc_id}" }

Then, in the stage environment, you define a datasource that points to the remote state of global :

 data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }

Now, you can use this datasource as any other and access all the values that were defined in global 's outputs:

 vpc_

Words of Caution

Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.

Where to Report Issues and Monitor Changes

First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.

This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.

Can't Cheat Your Way out of Cloud Knowledge

You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.

Prefer Separate Resources That Are Explicitly linked

There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).

Best-practice example, with explicitly linked resources:

 resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }

Terraform plan Doesn't Always Detect Issues and Conflicts

I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.

Syntax Quirks

There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.

When State Update Fails

It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .

Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock with the lock ID to take it down manually.

Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.

Not Everything Is Fun to Manage Through Terraform

There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!

Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.

When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.

Wniosek

Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.

I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.

I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.

It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.

Related: Terraform vs. CloudFormation: The Definitive Guide