Chciałbym opowiedzieć o nowym narzędziu CLI, które napisałem, aby rozwiązać stary problem.
problem
Terraform od dawna jest standardem w społeczności Devops/Cloud/IT. Jest to bardzo wygodne i przydatne do radzenia sobie z infrastrukturą jako kodem. W Terraformie jest wiele przysmaków, a także wiele widelców, ostrych noży i grabi.
Dzięki Terraform bardzo wygodnie jest tworzyć nowe rzeczy, a następnie zarządzać nimi, zmieniać je lub usuwać. Co powinni zrobić ci, którzy posiadają ogromną infrastrukturę w chmurze, a nie stworzoną poprzez Terraform? Przepisanie i ponowne utworzenie całej chmury jest w jakiś sposób kosztowne i niebezpieczne.
Napotkałem ten problem przy 2 zadaniach, najprostszym przykładem jest sytuacja, gdy chcesz, aby wszystko było w Git w postaci plików terraform, ale masz ponad 250 segmentów i ręczne zapisywanie ich w terraformie to dużo.
Jest
Ogólnie wszystko jest jak na zdjęciu tylko od prawej do lewej
Ostrzeżenia: Autor przez połowę swojego życia nie mieszka w Rosji i niewiele pisze po rosyjsku. Uważaj na błędy ortograficzne.
Solutions
1. Istnieje gotowe i stare rozwiązanie dla AWS
Jak działa terraforming: pobiera dane z AWS SDK i generuje tf i tfstate za pomocą szablonu.
Są tu 3 problemy:
1. Zawsze będą występować opóźnienia w aktualizacjach
2. Pliki tf czasami wychodzą uszkodzone
3. stan tf jest zbierany oddzielnie od stanu tf i nie zawsze jest zbieżny
Ogólnie rzecz biorąc, trudno uzyskać wynik, w którym „plan terraformowy” mówi, że nie ma żadnych zmian
2. `terraform import` to polecenie wbudowane w terraform. Jak to działa?
Piszesz pusty plik TF z nazwą i typem zasobu, następnie uruchamiasz `terraform import` i przekazujesz identyfikator zasobu. terraform kontaktuje się z dostawcą, odbiera dane i tworzy plik tfstate.
Są tu 3 problemy:
1. Dostajemy tylko plik tfstate, a tf jest pusty, trzeba go napisać ręcznie lub przekonwertować z tfstate
2. Może pracować tylko z jednym zasobem na raz i nie obsługuje wszystkich zasobów. I co mam znowu zrobić z ponad 250 wiadrami?
3. Musisz znać ID zasobów - czyli opakować je w kod pobierający listę zasobów
Ogólnie rzecz biorąc, wynik jest częściowy i nie jest dobrze skalowany
Moja decyzja
wymagania:
1. Możliwość tworzenia plików tf i tfstate dla zasobów. Na przykład pobierz wszystkie zasobniki/grupę zabezpieczeń/system równoważenia obciążenia, a „plan terraform” zwrócił informację, że nie ma żadnych zmian
2. Potrzebujesz 2 chmur GCP + AWS
3. Globalne rozwiązanie, które można łatwo zaktualizować za każdym razem i nie marnować czasu na każdy zasób przez 3 dni pracy
4. Zrób to jako Open Source – wszyscy mają ten sam problem
Język Go jest tym, za co go uwielbiam i ma bibliotekę do tworzenia plików HCL używanych w terraformie + dużo kodu w terraformie, który może być przydatny
Ścieżka
Pierwsza próba
Zacząłem od prostej wersji. Kontaktowanie się z chmurą za pośrednictwem SDK w celu uzyskania wymaganego zasobu i przekształcenie go w pola dla terraformu. Próba zakończyła się natychmiastowym niepowodzeniem w grupie bezpieczeństwa, ponieważ nie podobało mi się 1.5 dnia na konwersję tylko grupy bezpieczeństwa (a jest dużo zasobów). Przez dłuższy czas pola można zmieniać/dodawać
Drugie podejście
Na podstawie opisanego pomysłu
Teraz zróbmy rekursywną pornografię polegającą na pisaniu konwertera tfstate na tf. Dla tych, którzy nigdy nie czytali tfstate, jest to JSON, ale wyjątkowy.
Oto jego ważna część atrybutów
"attributes": {
"id": "default/backend-logging-load-deployment",
"metadata.#": "1",
"metadata.0.annotations.%": "0",
"metadata.0.generate_name": "",
"metadata.0.generation": "24",
"metadata.0.labels.%": "1",
"metadata.0.labels.app": "backend-logging",
"metadata.0.name": "backend-logging-load-deployment",
"metadata.0.namespace": "default",
"metadata.0.resource_version": "109317427",
"metadata.0.self_link": "/apis/apps/v1/namespaces/default/deployments/backend-logging-load-deployment",
"metadata.0.uid": "300ecda1-4138-11e9-9d5d-42010a8400b5",
"spec.#": "1",
"spec.0.min_ready_seconds": "0",
"spec.0.paused": "false",
"spec.0.progress_deadline_seconds": "600",
"spec.0.replicas": "1",
"spec.0.revision_history_limit": "10",
"spec.0.selector.#": "1",
Jest:
1. identyfikator - ciąg znaków
2. metadane - tablica o rozmiarze 1, a w niej obiekt z polami, co opisano poniżej
3. spec - skrót o rozmiarze 1 i klucz, w którym znajduje się wartość
Krótko mówiąc, zabawny format, wszystko może mieć kilka poziomów głębokości
"spec.#": "1",
"spec.0.min_ready_seconds": "0",
"spec.0.paused": "false",
"spec.0.progress_deadline_seconds": "600",
"spec.0.replicas": "1",
"spec.0.revision_history_limit": "10",
"spec.0.selector.#": "1",
"spec.0.selector.0.match_expressions.#": "0",
"spec.0.selector.0.match_labels.%": "1",
"spec.0.selector.0.match_labels.app": "backend-logging-load",
"spec.0.strategy.#": "0",
"spec.0.template.#": "1",
"spec.0.template.0.metadata.#": "1",
"spec.0.template.0.metadata.0.annotations.%": "0",
"spec.0.template.0.metadata.0.generate_name": "",
"spec.0.template.0.metadata.0.generation": "0",
"spec.0.template.0.metadata.0.labels.%": "1",
"spec.0.template.0.metadata.0.labels.app": "backend-logging-load",
"spec.0.template.0.metadata.0.name": "",
"spec.0.template.0.metadata.0.namespace": "",
"spec.0.template.0.metadata.0.resource_version": "",
"spec.0.template.0.metadata.0.self_link": "",
"spec.0.template.0.metadata.0.uid": "",
"spec.0.template.0.spec.#": "1",
"spec.0.template.0.spec.0.active_deadline_seconds": "0",
"spec.0.template.0.spec.0.container.#": "1",
"spec.0.template.0.spec.0.container.0.args.#": "3",
Generalnie jeśli ktoś chce mieć problem programistyczny na rozmowę kwalifikacyjną to niech po prostu poprosi o napisanie parsera do tego zadania :)
Po wielu próbach napisania parsera bez błędów, znalazłem jego część w kodzie terraforma i to najważniejszą. I wszystko wydawało się działać dobrze
Próba trzecia
dostawcy terraform to pliki binarne zawierające kod ze wszystkimi zasobami i logiką do pracy z interfejsem API chmury. Każda chmura ma własnego dostawcę, a sam terraform wywołuje je tylko poprzez swój protokół RPC między dwoma procesami.
Teraz zdecydowałem się skontaktować z dostawcami terraform bezpośrednio za pośrednictwem wywołań RPC. Wyszło pięknie i umożliwiło zmianę dostawców terraform na nowszych i uzyskanie nowych funkcji bez zmiany kodu. Okazuje się również, że nie wszystkie pola w tfstate powinny znajdować się w tf, ale jak możesz się tego dowiedzieć? Po prostu zapytaj o to swojego dostawcę. Potem rozpoczęła się kolejna rekursywna pornografia polegająca na składaniu wyrażeń regularnych, poszukująca pól wewnątrz stanu tfstate na wszystkich poziomach głębokości.
Ostatecznie otrzymaliśmy przydatne narzędzie CLI, które ma wspólną infrastrukturę dla wszystkich dostawców terraform i można łatwo dodać nowe. Ponadto dodawanie zasobów zajmuje niewiele kodu. Plus wszelkiego rodzaju gadżety, takie jak połączenia między zasobami. Oczywiście problemów było wiele i nie da się ich wszystkich opisać.
Nazwałem zwierzę Terrafomer.
Finał
Używając Terrafomeru, wygenerowaliśmy 500-700 tysięcy linii kodu tf+tfstate z dwóch chmur. Mogliśmy wziąć starsze rzeczy i zacząć je dotykać tylko poprzez terraformę, tak jak w najlepszej infrastrukturze, jako pomysły na kod. To po prostu magia, gdy bierzesz ogromną chmurę i otrzymujesz ją od zespołu w postaci plików pracowników terraform. A potem grep/replace/git i tak dalej.
Przeczesałem to i uporządkowałem, dostałem pozwolenie. Wydano go na GitHub dla wszystkich w czwartek (02.05.19).
Otrzymałem już 600 gwiazdek, 2 prośby o ściągnięcie za dodanie obsługi openstack i kubernetes. Dobra opinia. Ogólnie rzecz biorąc, projekt jest przydatny dla ludzi
Radzę każdemu, kto chce rozpocząć pracę z Terraformem i nie przepisywać wszystkiego od nowa.
Chętnie wyciągnę prośby, problemy, gwiazdki.
Демо
Źródło: www.habr.com