top of page

Śledź nasze wpisy w social media

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
Zdjęcie autoraPiotr Kośka

Zabawy z terraform - AWS i DigitalOcean oraz migracja backendu.

Stwierdziłem, że zrobię porządki w mojej infrastrukturze 1.0 - tak ją nazwijmy. Wykorzystuje ją pod szkolenia, kursy i materiały wideo, które Tworzę oraz własne domowe/biznesowe konfiguracje. Głównie update dotyczy migracji z terraform state trzymanego w PostgresSQL. SQL był hostowany w lokalnej sieci. Cały pipeline miałem też napisany/stworzony na Jenkins więc było naturalnym procesem trzymać to lokalnie u siebie. Sam PostgresSQL jako remote backend spisywał się bardzo dobrze. Testowałem też wykorzystanie go jako serwisu w digitalocean jednak na dłuższą metę okazała się dość drogi - 5$ miesięcznie było zdecydowanie za drogo :) :).



Tanio, taniej z terraform cloud


Padło zatem na terraform cloud jako alternatywa dla PostgresSQL. Plusy jakie do mnie przemawiały to:


  1. Wersjonowanie automatyczne state z dość łatwą historią zmian do przeglądu.

  2. Łatwa integracja z GIT (Github, bitbucket, gitlab).

  3. Automatyzacja wbudowana - tu pragnę zaznaczyć, że mamy do wyboru trzy podejścia.

    1. Pierwsze to pełna integracja z terraform cloud - gdzie finalnie apply wykonujemy tylko po stronie cloud.

    2. Opcja druga mieszana, akcje możemy wykonywać po stronie terraform cloud i lokalnie.

    3. Opcja trzecia - tylko state po stronie terraform cloud.

  4. Przy wyborze opcji pierwszej mamy mini CI/CD z kontrolą przepływu pracy z terraform i akceptacji lub pełnej automatyzacji dla naszych akcji apply.


Historia zmian na terraform state w terraform cloud - widziana z poziomu gui
Terraform cloud - historia state

Historia zmian jest naprawdę fajna widać kto co zrobił oraz masz przypisany state file do tego wydarzenia więc nawet na upartego możesz zrobić sobie diff na dwóch plikach i zobaczyć różnice. No i pełni to też rolę takiego prostego backupu. Oczywiście go nie zastępuje i warto w swoich konfiguracjach dodatkowo wdrożyć kwestie wykonania backupu.


Terraform backend - tworzenie i automatyzacja.


Terraform cloud jest fajny i wygodny jednak darmowa wersja posiada limit 500 zasobów. I to 500 zasobów na wszystkie resorces i data uruchomione w ramach naszego state łącznie na wszystkich workspace.

terraform cloud i podział na dev, stg, i prod
Podział na workspace development, staging i production

Dla tego wychodzi nieco ponad 150 zasobów per workspace. To troche mało, szczególnie że kiedyś było to bez ograniczeń. A później płacimy za godzinę działania każdego naszego zasobu powyżej. Oczywiście pierwsze 500 jest za darmo, ale po więcej szczegółów odsyłam do cennika.


Jednak ja do mojego malutkiego projektu wykorzystuje terraform cloud. W którym to trzymam konfiguracje moich backendów dla innych projektów. A te backendy leżą na S3. obecnie mam działających 36 zasobów więc daleko mi jeszcze do wspomnianego limitu. Jak go przekroczę będzie wtedy artykuł o migracji z terraform cloud do innego state backend :)





S3 jako backend terraform


S3 to wybór pokierowany znów cebulą, ponieważ trzymanie terraform.tfstate na platformie AWS jest bardzo tanie w porównaniu do digitalocean postgresql (zgroza 5$ mc) i naszym terraform cloud. Dodam kolejnego cebula hint że w przypadku gitlab mamy możliwość trzymania naszego state w gitlab za pomocą http backend - fajne rozwiązanie :). Wrócę jednak do S3.


Powstał dość prosty kod który na podstawie poniższego wkładu:


Tworzy mi konfiguracje na koncie AWS w postaci konta użytkownika AWS programistyczny access, Serwis S3 Bucket, Plus odpowiednie uprawnienia - tylko dla tego usera do jego części plików powiązanych z jego terraform state.


Tak, w większości moich domowych projektów ja jestem głównym adminem i operatorem. Pomimo tego wychodzę z założenia by tworzyć usera który, ma ograniczone uprawnienia a nie że, widzi wszystko na poziomie admina. Reasumując user ma czytać i zapisywać na S3 to ma tylko takie uprawnienia i to też do swojej przestrzeni - nie pisząc i nie odczytując innych plików w S3.


I tak prostym kodem terraform tworzę S3 oraz dynamoDB, które to pozwala mi przechowywać mój terraform state w bezpiecznym i kontrolowanym miejscu:


Powiązanie zasobów i szczegółowy opis ich działania


Kod Terraform tworzy ekosystem zarządzania stanem Terraform w chmurze AWS, obejmując zasoby S3 (do przechowywania stanu) oraz DynamoDB (do zarządzania blokadami stanu). Poniżej przedstawiam działanie poszczególnych zasobów i ich zależności.


1. random_string - Generowanie unikalnego identyfikatora


Co robi: Generuje losowy ciąg znaków, który może być użyty jako część nazwy bucketu S3. Zapewnia unikalność nazw bucketów (wymagane przez AWS, ponieważ nazwy bucketów muszą być globalnie unikalne).


2. aws_s3_bucket - Główne przechowywanie stanu Terraform


Co robi: Tworzy bucket S3, w którym przechowywany będzie plik stanu Terraform, oczywiście nie dla aktualnego kodu - ten działa na terraform cloud. To tworzy sie nasz state dla innych moich integracji.



3. aws_s3_bucket_public_access_block - Blokada publicznego dostępu


Co robi: Blokuje publiczny dostęp do bucketu, ustawiając zasady ograniczające publiczne ACL, polityki i dostęp.



4. aws_s3_bucket_versioning - Wersjonowanie bucketu


Co robi: Włącza wersjonowanie obiektów w bucket S3. Dzięki temu zmiany w pliku stanu Terraform są archiwizowane, co pozwala na przywracanie wcześniejszych wersji. Automatyczny backup to lubię :)



5. aws_s3_bucket_lifecycle_configuration - Zarządzanie cyklem życia danych


Co robi: Automatycznie usuwa starsze wersje obiektów po 10 dniach. Zmniejsza to koszty przechowywania przy zachowaniu dostępu do najnowszych danych. Cały czas mam aktualny stan plus ewentualną zmianę - w moim przypadku ta wartośc mi zupełnie wystarcza :)



6. aws_s3_object - Tworzenie struktury katalogów


Co robi: Tworzy strukturę katalogów w bucket S3 na podstawie listy projektów (local.buckets). Każdy projekt otrzymuje swój katalog, co ułatwia zarządzanie plikami stanu w środowiskach wieloprojektowych.



7. aws_s3_bucket_policy - Polityka dostępu



Co robi: Definiuje politykę dostępu do bucketu, zezwalając użytkownikowi deployer-terraform na wykonywanie operacji takich jak odczyt, zapis i usuwanie plików.



8. aws_dynamodb_table - Tabela do zarządzania blokadami Terraform



Co robi: Tworzy tabelę DynamoDB służącą do zarządzania blokadami Terraform. Każda blokada jest identyfikowana przez LockID.



Powiązania między zasobami

  1. Bucket S3: Wszystkie zasoby związane z bucketem (aws_s3_bucket_public_access_block, aws_s3_bucket_versioning, aws_s3_bucket_lifecycle_configuration, aws_s3_object, aws_s3_bucket_policy) są powiązane z aws_s3_bucket.terraform_state.

  2. DynamoDB: aws_dynamodb_table.terraform_lock jest niezależnym zasobem, ale powiązanym z backendem Terraform jako mechanizm blokady.

  3. Użytkownik IAM: Polityka bucketu (aws_s3_bucket_policy) umożliwia użytkownikowi deployer-terraform dostęp do zarządzania plikami stanu.


Kod tworzy infrastrukturę, która umożliwia:

  • Przechowywanie stanu Terraform w bucket S3 z wysokim poziomem bezpieczeństwa.

  • Zarządzanie wersjami plików stanu i ich cyklem życia.

  • Blokowanie równoczesnych modyfikacji stanu za pomocą tabeli DynamoDB.


Powiązanie zasobów gwarantuje, że system jest spójny, wydajny i łatwy w utrzymaniu.


A tu jeszcze szybka konfiguracja iam usera:


Analiza kodu Terraform: Automatyzacja zarządzania użytkownikami IAM dla projektów Terraform


Kod przedstawia konfigurację Terraform, która tworzy użytkowników IAM, przypisuje im polityki dostępu i generuje klucze dostępowe dla zarządzania zasobami Terraform.


1. Tworzenie użytkowników IAM (aws_iam_user)



Co robi:

  • Tworzy użytkowników IAM dla każdego projektu w liście local.buckets.

  • Nazwa użytkownika: Składa się z nazwy użytkownika dla projektu (local.buckets[count.index].user_name) i identyfikatora konta AWS, co zapewnia unikalność.

  • Tagi: Użytkownicy są oznaczani identyfikatorem konta oraz środowiskiem (var.enviroment) w celu łatwego zarządzania i identyfikacji.



2. Przypisywanie polityki IAM (aws_iam_user_policy)



Co robi:

  • Tworzy politykę IAM dla każdego użytkownika z uprawnieniami do zasobów S3 i DynamoDB:

  • S3: Użytkownicy mogą:

  • Pobierać obiekty (s3:GetObject).

  • Umieszczać obiekty (s3:PutObject).

  • Listować zawartość bucketu (s3:ListBucket).

  • Uprawnienia ograniczono do konkretnego katalogu (terraform/${local.buckets[count.index].catalog_project_name}) w bucket S3.

  • DynamoDB: Użytkownicy mogą:

  • Pobierać, dodawać, aktualizować, usuwać elementy oraz opisywać tabelę (dynamodb:*Item, dynamodb:DescribeTable).

  • Dostęp jest ograniczony do tabeli blokad Terraform (local.aws_dynamodb_lock_table).

  • Nazwa polityki: Zawiera nazwę projektu z listy local.buckets.



3. Tworzenie kluczy dostępowych (aws_iam_access_key)



Co robi:

  • Generuje klucz dostępu (Access Key ID) i klucz tajny (Secret Access Key) dla każdego użytkownika.

  • Klucze są wykorzystywane do autoryzacji przez Terraform lub inne narzędzia zarządzające infrastrukturą.



Przykład działania


Dla listy local.buckets zawierającej dwa projekty:


Terraform utworzy:

  1. 1. Dwóch użytkowników IAM:

    1. user1-{account_id}

    2. user2-{account_id}

  2. 2. Dwie polityki IAM:

    1. tf_project1 przypisaną do user1.

    2. tf_project2 przypisaną do user2.

  3. 3. Dwa zestawy kluczy dostępowych:

    1. Jeden dla user1.

    2. Drugi dla user2.


Korzyści z podejścia

  1. Bezpieczeństwo:

    1. Uprawnienia są ograniczone do zasobów specyficznych dla projektu.

    2. Użytkownicy nie mają dostępu do innych katalogów w bucket S3 ani do innych tabel DynamoDB.

  2. Automatyzacja:

    1. Dynamiczne tworzenie użytkowników, polityk i kluczy na podstawie listy projektów eliminuje potrzebę ręcznego zarządzania tożsamościami.

  3. Skalowalność:

    1. Możliwość łatwego dodawania nowych projektów do local.buckets bez konieczności modyfikowania kodu.


Kod tworzy dynamiczny system zarządzania dostępem do zasobów AWS dla wielu projektów Terraform. Dzięki ścisłej kontroli dostępu użytkownicy mogą zarządzać jedynie zasobami swojego projektu, co zwiększa bezpieczeństwo i porządek w środowisku AWS.


Idąc dalej. Cała moja konfiguracja po przez outputs terraform zwraca oczekiwane przeze mnie informacje.

W następującej postaci:

Jak możemy zaobserwować dostaję konfigurację w HCL dla mojego S3 backendu którą można wykorzystać w następujący sposób:

lub jako plik HCL includowany (ja tak to wykorzystuje) do konfiguracji po przez polecenie terraform init -backend-config=backend.hcl. Więcej można przeczytać na stronie dokumentacji terraform odnośnie s3. Finalnie moja deklaracja konfiguracji backendu sprowadza się do prostego backend "s3" {}:


A tutaj rezultat działania kodu i użytkownicy IAM, policy i s3 bucket



Zimowe porządki na digitalocean z terraform


I tak, stanąłem przed migracją moich narzędzi z jednego stanu do drugiego. Ogólnie są dwa podejścia szybkie i dłuższe. W obu nie ma znaczenia gdzie masz state (choć przy pierwszej metodzie jak masz terraform state w terraform cloud to ma to znaczenia :) - o tym w prezentowanych przykładach ).


Terraform state migracja metoda 1


Pierwsza metoda jest szybka i obejmuje następujące kroki.


  1. Krok pierwszy to jeszcze na starym terraform state wykonujemy:

    1. terraform init

    2. terraform plan

    3. terraform apply

  2. I jak krok pierwszy pokaże nam 0 add, 0 change, 0 destroy - to znaczy że możemy działać

  3. Krok drugi to zmiana konfiguracji w naszym pliku na nowy state co zamyka się zazwyczaj do zmiany info o backend, czyli u mnie było to zmiana z backend "pg" na backend "s3"

  4. Krok trzeci przez polecenie terraform init które to wykryje zmianę backendu i zapyta o migrację - wybieramy odpowiedz żę chcemy dokonac migracji.

  5. Krok czwarty wydajemy polecenie terraform plan i potem terraform apply i jak pokarze 0 add, 0 change, 0 destroy to jestesmy w domu i mamy zmigrowany terraform state do nowej lokalizacji (czytaj backendu).


Mała uwaga w przypadku gdy naszym starym backendem jest terraform cloud i chcemy zmienić go na inny to musimy wykorzystać metodę numer 2 ponieważ otrzymamy komunikat w konsoli, że automatyczna migracja z terraform cloud do innego backendu nie jest wspierana - cwaniacy :)

Terraform state migracja metoda 2


Metoda w działaniu jest podobna, ale to my wykonujemy wszystkie operacje:


  1. Krok pierwszy - jest podobny jak w metodzie numer 1, a mianowicie na naszym starym backend wydajemy polecenia:

    1. terraform init

    2. terraform plan

    3. terraform apply

  2. I jak krok pierwszy pokaże nam 0 add, 0 change, 0 destroy - to znaczy że możemy działać

  3. Krok drugi - jeszcze jedną operację musimy wykonać na naszym starym state wydajemy polecenie:

    1. terraform state pull > backup_nasz_state_plik

  4. W ramach kroku drugiego wykonaliśmy backup naszego stanu. Jest to kopia 1:1 i jak teraz nie dodamy, nic nie wykonamy, żadnych zmian i nie usuniemy żadnego resorces i data to możemy ją otworzyć bez przeszkód.

  5. Krok trzeci zmiana konfiguracji backend - czyli backend "pg" zmieniam na backend "s3".

  6. Krok czwarty - olewam informacje o automatycznej migracji do nowego (w przypadku terraform cloud musimy olać bo dostaniemy komunikat że migracja automatyczna nie jest wspierana)

  7. Krok piąty - wydajemy terraform init z nowym backend i terraform plan.

  8. Jeżeli krok piąty pokaże nam XXX Add, 0 change, 0 destroy to znaczy że działamy już na nowym backend tylko jeszcze nie mamy danych ze starego.

  9. Krok szósty - nie wykonujemy terraform apply, tylko wracamy do polecenia terraform state:

    1. terraform state push -force /scieżka/do/pliku/z/backup

  10. Nasz backup wgra się na nowy backend.

  11. Krok siódmy - wydajemy polecenia:

    1. terraform plan

    2. terraform apply

  12. I jak krok siódmy zwróci nam 0 add, 0 change, 0 destroy to jesteśmy w domu i mamy zmigrowany nasz state.


Teraz wystarczy w moich pipeline powymieniać sekrety tak byśmy mogli też w nich korzystac z naszego nowego backendu. Dlatego ja preferuje plik backend.hcl jako miejsce gdzie przekazujemy konfiguracje potrzebna do naszego terraform init. Po prostu podmieniamy jego zawartość i wszystko działa bez zmiany kodu pipeline.


Terraform z Jenkins freestyle, Jenkins Pipeline, Github Actions, Bitbucket pipeline.


Zacznę omawiać ten przykład od bitbucket bo tu mam prosta konfigurację modułu w terraform. Moduł ten odpowiada za tworzenie sieci w digitalocean. Geneza jego powstania jest prosta. W digitalocean można utworzyć zasoby bez VPC. Tzn nie można tak do końca bo i tak zostanie ta sieć utworzona. Tylko, że z domyślnymi parametrami takimi jak losowy adres sieci i cidr, nazwa. Co może prowadzić do bałaganu. A szczególnie jest niepożądane gdy tworzymy coś za pomocą terraform. Ponieważ tworzy nam się obiekt którym nie zarządzamy przez terraform. I po destroy zostają śmieci, które musimy usunąć ręcznie.


Z racji na moje doświadczenia w pracy z digitalocean preferuje konfiguracje sieci domyślnej dla wszystkich 14 regionów.


gdzie poszczególne numery odpowiadają trzeciemu oktetowi w konfiguracji sieci 10.X.Y.0/20 gdzie Y to wartości z regions - czyli 10.254.0.0/20 następny 10.254.16.0/20 i tak dalej. Oczywiście jak ktoś uważa że taka sieć jest za duża jako sieć testowa to można zmniejszyć tylko pamiętajmy że jesteśmy ograniczeni między cidr /16 a /24


Finalnie otrzymujemy taką konfigurację.


digitalocean i sieci stworzone przez niżej opisany moduł


Terraform i bitbucket


Spójrzmy na moduł do tworzenia sieci w digitalocean dostępny pod adresem: https://bitbucket.org/helppointit/default_digitalocean_vpc/src/main/


Który to stworzył mi konfigurację sieci pokazywaną na zrzucie ekranu powyżej.


Przeanalizujmy wspólnie nasze pliki terraform które razem składaja się na moduł:


version.tf - tu mamy nasza podstawową deklaracje która powinna pokazać się w każdym module. Więc informacja o używanej minimalnej wersji terraform oraz wykorzystanych providerów i ich minimalnej wersji.


variables.tf - tu deklaruję wejścia do modułu. Czyli będą to argumenty sterujące modułem. Dodałem też walidacje wprowadzanych danych tak by adresacja była zgodna z standardem RFC i działała tylko na regionach dostępnych w digitalocean



main.tf - nasz głowny kod realizuje konfigurację i zbaerający nasze argumenty. Finalnie otrzymujemy naszą sieć na podstawie wkładu dostarczonego do modułu.


outputs.tf - informacja o utworzonych sieciach


Mamy jeszcze ReadMe.md, Documentations.md i katalog tests w ktorym mamy prosty scenariusz testowy.


Mamy jeszcze nasz plik bitbucket-pipelines.yml a w nim konfiguracje naszego pipeline do testów automatycznych naszego modułu.


Wykonania każdego commita możemy obserwować tu: https://bitbucket.org/helppointit/default_digitalocean_vpc/pipelines


Zapraszam do korzystania z modułu.


Terraform Jenkins freestyle jobs


Spójrzmy teraz na konfigurację workflow który realizuje moj terraform init, plan, apply - czyli wdraża całą konfigurację. Workflow działa w następujący sposób:


  1. commit lub zmian w kodzie triggeruje terraform-check

  2. poprawnie zakończony terraform-check triggeruje terraform-plan

  3. poprawnie wykonany terraform plan triggeruje terraform apply


freestyle job pipeline widok workflow

terraform-check - za pomocą terraform validate i terraform fmt sprawdzam kod, czy jest poprawny i dobrze sformatowany. Oczywiście te sprawdzenia to w takiej podstawowej formie poprawności składni i dobrego formatowania według formatu kanonicznego terraform.


Workflow jest wyklikany w freestyle job zatem podrzucam tylko konfiguracje bash skryptu. Który korzystając z docker i kontenera z terraform dostarcza mi narzędzie bez konieczności go instalowania.


terraform-plan - tu zadaniem tego joba jest dostarczenie naszej konfiguracji, a dokładnie jego planu. Job wykona się jak poprzedni nasz check przejdzie poprawnie. Plan zapisuje do pliku który potem wykorzystuje w następnym jobie z terraform apply.


W kroku tym dodatkowo też generuje za pomocą tf-summarize bardziej zwięzłe i skondensowane informacje o zaplanowanej konfiguracji. W moim odczuciu tf-summarize generuje lepsze podsumowanie niż sam terraform plan.



terraform-apply - tu już pracujemy na ukierunkowanym planie z poprzedniego joba. Który jak wykona się poprawnie generuje nam tfplan. Ten plan wykorzystujemy w tym jobie


Dodatkowe konfiguracje czyli:

  1. terraform output

  2. terraform state show

  3. terraform-taint

  4. terraform-untaint

  5. trivy-check


terraform-output - informacja, prezentacja danych z którymi się dzielę. Na przykład informacja o adresach hostów dla moich kursantów w ramach szkolenia prowadzonego z Terraform, Jenkinsa czy Proxmox



terraform-state-show - jak sa problemy z jakiś zasobem użytkownika to szybko uzyskuje dostęp do tych zasobów. A dokładnie jego nazwy i mogę ją wykorzystać w taint jobie



terraform-taint - job do ponownej konfiguracji problematycznego hosta, zasobu itp.



terraform-untaint - jak poprzednio tylko ściąga ten taint


trivy-check - to już dodatkowa walidacja bezpieczeństwa konfiguracji z trivy.



Terraform Jenkins pipeline


Przełożenie tego workflow skonfigurowanego w freestyle job na konfigurację jenkinsfile z Jenkins Pipeline. Zamieniamy elementy wyklikany z poprzedniego kroku na element dostarczany w kodzie :).



Terraform Github Actions pipeline


Przełożenie na github actions naszego poprzedniego workflow dostepnego w Jenkins freestyle i jenkinsfile. U mnie github wykorzystuje jako backup. Czyli głównie jobami zarządza jenkins. Jak Jenkins ma awarię to triggeruje joba w github actions. Moj biznes zachowuje ciągłość a w wolnym czasie naprawiam problemy z Jenkins.



Oraz dodatkowe konfiguracje które uważam że pomagają podczas pracy z terraform.


Moj terraform output - informacja o wszystkich lub konkretnym naszym output wyświetlana w github actions.


Terraform Lock ID - czasem lock nasz zostanie osierocony, i lokalnie nie chce mi się podłączać backendu z sekretami i ściągać blokadę. Wykorzystuje do tego joba dostarczając mu ID blokady



Terraform State List - lista zasobów przydaje sie do taint lub untaint


Terraform taint/untaint - czasami zdarzają się problemy z zasobami i trzeba je raz jeszcze skonfigurować więc znów warto mieć takiego joba pod ręką.


Podsumowanie


Migracja terraform state z jednej konfiguracji na drugi backend jest bardzo prosta. Pokazałem Ci jak ja to zrobiłem na przykładzie swoich konfiguracji. Pamiętaj że ten sposób migracji a dokładnie metodę numer 2 możemy wykorzystać do debugowania lokalnego naszego terraform - ale o tym może już w innym artykule.


Na koniec TY jakiego backendu używasz najczęściej w konfiguracji? Pozdrawiam Piotr Kośka.

Ostatnie posty

Zobacz wszystkie

Comments


Śledź nasze wpisy w social media

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube

Poznaj terraform jedno z najepszych narzedzi do zarządzania infrastrukturą w kodzie (IaC) - w kursie tym przeprowadzam Cię przez proces instalacji i konfiguracji tego narzędzia.

bottom of page