Lepiej późno niż wcale. Albo jak ledwo nie popełniliśmy poważnego błędu, nie mając wsparcia dla standardowych Dockerfile do budowania obrazów aplikacji.

Będzie mowa o — narzędziu GitOps, które integruje się z każdym systemem CI/CD i zapewnia zarządzanie całym cyklem życia aplikacji, umożliwiając:
- tworzenie i publikowanie obrazów,
- wdrażanie aplikacji w Kubernetes,
- usuwanie nieużywanych obrazów za pomocą specjalnych polityk.
Filozofia projektu to zebranie narzędzi niskiego poziomu w jednolitą zintegrowaną system, który daje inżynierom DevOps kontrolę nad aplikacjami. W miarę możliwości powinny być wykorzystywane już istniejące narzędzia (takie jak Helm i Docker). Jeśli nie ma rozwiązania jakiegoś problemu — możemy stworzyć i wesprzeć wszystko, co potrzebne do tego.
Wstęp: nasz własny kompilator obrazów
I tak się stało w przypadku kompilatora obrazów w werf: brakujący nam standardowy Dockerfile okazał się niewystarczający. Jeśli przyjrzeć się historii projektu, problem ten ujawnił się już w pierwszych wersjach werf (wtedy jeszcze ).
Tworząc narzędzie do budowy aplikacji w obrazy Docker, szybko zrozumieliśmy, że Dockerfile nie nadaje się do niektórych konkretnych zadań:
- Potrzeba budowania typowych małych aplikacji webowych wg następującego standardowego schematu:
- zainstalować systemowe zależności aplikacji,
- zainstalować pakiet bibliotek zależności aplikacji,
- zbudować zasoby,
- i co najważniejsze — szybko i efektywnie aktualizować kod w obrazie.
- Przy zmianach w plikach projektu, kompilator musi szybko tworzyć nową warstwę poprzez nakładanie poprawek na zmienione pliki.
- Jeżeli zmieniły się konkretne pliki, należy przebudować odpowiednią zależną fazę.
Na dzień dzisiejszy nasz kompilator ma wiele innych możliwości, ale początkowe pragnienia i impuls były takie.
Generalnie, bez zastanowienia, uzbroiliśmy się w używany język programowania (patrz poniżej) i wyruszyliśmy w drogę — implementować własny DSL! Odpowiadając na postawione zadania, był on przeznaczony do opisu procesu budowy w etapach i definiowania zależności tych etapów od plików. A uzupełniał go własny kompilator, który przekształcał DSL w końcowy cel — zbudowany obraz. Początkowo DSL był w Ruby, a w miarę — konfiguracja naszego kompilatora jest teraz opisana w pliku YAML.

Stara konfiguracja dla aplikacji dapp na Ruby

Aktualna konfiguracja dla werf w YAML
Mechanizm działania kompilatora również zmieniał się z czasem. Początkowo po prostu generowaliśmy na bieżąco tymczasowy Dockerfile z naszej konfiguracji, a potem zaczęliśmy uruchamiać instrukcje kompilacji w tymczasowych kontenerach i wykonywać commit.
NB: Obecnie nasz kompilator, który działa ze swoją konfiguracją (w YAML) i nazywa się kompilatorem Stapel, już ewoluował w wystarczająco potężne narzędzie. Jego rozbudowany opis zasługuje na oddzielne artykuły, a główne szczegóły można znaleźć w .
Świadomość problemu
Jednak zrozumieliśmy, i to nie od razu, że popełniliśmy jeden błąd: nie dodaliśmy możliwości kompilowania obrazów przez standardowy Dockerfile i integrowania ich w tę samą infrastrukturę kompleksowego zarządzania aplikacją (tzn. kompilowania obrazów, wdrażania i czyszczenia ich). Jak można było stworzyć narzędzie do wdrażania w Kubernetes i nie zrealizować wsparcia dla Dockerfile, tzn. standardowego sposobu opisywania obrazów dla większości projektów?..
Zamiast odpowiedzi na takie pytanie oferujemy jego rozwiązanie. Co zrobić, jeśli już posiadasz Dockerfile (lub zestaw Dockerfile’ów) i chcesz używać werf?
NB: Zresztą, dlaczego w ogóle chcesz używać werf? Główne funkcje ograniczają się do następujących:
- pełny cykl zarządzania aplikacją, w tym czyszczenie obrazów;
- możliwość zarządzania kompilacją od razu kilku obrazów z jednego pliku konfiguracyjnego;
- ulepszony proces wdrażania chartów, zgodnych z Helm.
Pełną listę ich funkcji można znaleźć na .
A więc, jeśli wcześniej zaproponowalibyśmy przepisanie Dockerfile na naszą konfigurację, to teraz z radością powiemy: „Pozwól werf skompilować Twoje Dockerfile’y!”
Jak z tego korzystać?
Pełna realizacja tej możliwości pojawiła się w wersji . Ogólny zasada jest prosta: użytkownik wskazuje ścieżkę do istniejącego Dockerfile w konfiguracji werf, a następnie uruchamia polecenie werf build… i to wszystko — werf skompiluje obraz. Rozważmy to na abstrakcyjnym przykładzie.
Ogłośmy następny Dockerfile w głównym katalogu projektu:
FROM ubuntu:18.04
RUN echo Budowanie ... I ogłośmy werf.yaml, który wykorzystuje ten Dockerfile:
configVersion: 1
project: dockerfile-example
---
image: ~
dockerfile: .\/Dockerfile To wszystko! Pozostaje ,但这一过程并没有像他们的创始人希望的那样顺利。 werf build:

Ponadto, można ogłosić następny werf.yaml do kompilacji od razu kilku obrazów z różnych Dockerfile’ów:
configVersion: 1
project: dockerfile-example
---
image: backend
dockerfile: .\/dockerfiles\/Dockerfile-backend
---
image: frontend
dockerfile: .\/dockerfiles\/Dockerfile-frontend W końcu wspierane jest również przekazywanie dodatkowych parametrów budowy — takich jak --build-arg i --add-host — przez konfigurację werf. Pełny opis konfiguracji obrazu Dockerfile dostępny jest na .
Jak to działa?
Podczas budowy działa standardowa pamięć podręczna lokalnych warstw w Dockerze. Co ważne, werf również integruje konfigurację Dockerfile w swoją infrastrukturę. Co to oznacza?
- Każdy obraz zbudowany z Dockerfile składa się z jednej fazy o nazwie
dockerfile(więcej o tym, czym są fazy w werf, można przeczytać ). - Dla fazy
dockerfilewerf oblicza podpis, który zależy od zawartości konfiguracji Dockerfile. Po zmianie konfiguracji Dockerfile następuje zmiana podpisu fazydockerfilei werf inicjuje ponowne budowanie tej fazy z nową konfiguracją Dockerfile. Jeśli jednak podpis się nie zmienia, werf pobiera obraz z pamięci podręcznej (więcej o używaniu podpisów w werf było omawiane w ). - Następnie zbudowane obrazy można opublikować komendą
werf publish(lubwerf build-and-publish) i używać do wdrożenia w Kubernetes. Opublikowane obrazy w rejestrze Docker będą czyszczone standardowymi środkami czyszczenia werf, tj. nastąpi automatyczne czyszczenie starych obrazów (starszych niż N dni), obrazów związanych z nieistniejącymi gałęziami Git oraz według innych polityk.
Więcej o opisanych tu kwestiach można znaleźć w dokumentacji:
- ;
- ;
- .
Uwagi i ostrzeżenia
1. Zewnętrzny adres URL w ADD nie jest wspierany
Obecnie nie wspiera się użycia zewnętrznego adresu URL w dyrektywie ADD. Werf nie rozpocznie ponownego budowania przy zmianie zasobu według wskazanego adresu URL. Wkrótce planowane jest dodanie tej możliwości.
2. Nie można dodawać .git do obrazu
Ogólnie rzecz biorąc, dodawanie katalogu .git do obrazu jest praktyką niewłaściwą i oto dlaczego:
- Jeśli
.gitzostaje w finalnym obrazie, narusza zasady : ponieważ końcowy obraz powinien być związany z jednym commitem, nie powinno być możliwości wykonaniagit checkoutdowolnego commita. -
.gitpowiększa rozmiar obrazu (repozytorium może być duże, ponieważ kiedyś dodano do niego duże pliki, a później je usunięto). Rozmiar work-tree, związany tylko z określonym commitem, nie będzie zależał od historii operacji w Git. W tym przypadku dodanie i późniejsze usunięcie.gitz finalnego obrazu nie zadziała: obraz i tak nabierze dodatkowej warstwy — tak działa Docker. - Docker może inicjować niepotrzebną przebudowę, nawet jeśli jest budowany ten sam commit, ale z różnych work-tree. Na przykład, GitLab tworzy osobne skierowane katalogi w
/home/gitlab-runner/builds/HASH/[0-N]/yourprojectprzy włączonej równoległej budowie. Dodatkowa przebudowa będzie związana z tym, że katalog.gitróżni się w różnych sklonowanych wersjach tego samego repozytorium, nawet jeśli budowany jest ten sam commit.
Ostatni punkt ma konsekwencje również przy używaniu werf. Werf wymaga, aby skompilowana pamięć podręczna była obecna podczas uruchamiania niektórych poleceń (np. werf deploy). Podczas działania takich poleceń werf oblicza sygnatury etapów dla obrazów wskazanych w werf.yaml, i muszą być w pamięci podręcznej budowy — w przeciwnym razie polecenie nie będzie mogło kontynuować. Jeśli sygnatura etapów będzie zależała od zawartości .git, otrzymujemy niestabilną pamięć podręczną, która nie jest odporna na zmiany w nieistotnych plikach, a werf nie będzie w stanie wybaczyć takiej pomyłki (szczegóły w ).
Ogólnie dodanie tylko określonych, niezbędnych plików za pomocą instrukcji ADD w każdym przypadku zwiększa efektywność i niezawodność pisanego Dockerfile, a także poprawia odporność pamięci podręcznej zbudowanej na tym Dockerfile, na zmiany, które nie mają związku z Git.
Podsumowanie
Nasza pierwotna droga pisania własnego kompilatora na określone potrzeby była trudna, uczciwa i prosta: zamiast używać obejść w standardowym Dockerfile, napisaliśmy nasze rozwiązanie z niestandardową składnią. I to dało swoje korzyści: kompilator Stapel doskonale spełnia swoje zadanie.
Jednak w procesie pisania własnego kompilatora przeoczyliśmy wsparcie dla już istniejących Dockerfile’ów. Obecnie ten niedobór został naprawiony, a w przyszłości planujemy rozwijać wsparcie dla Dockerfile obok naszego niestandardowego kompilatora Stapel dla złożonej budowy oraz dla budowy z wykorzystaniem Kubernetes (tj. budowy na runnerach w Kubernetes, jak to ma miejsce w kaniko).
Więc jeśli gdzieś masz kilka Dockerfile'ów... spróbuj !
P.S. Lista dokumentów na ten temat
- ;
- ;
- ;
- ;
- ;
- ;
- .
Czytaj także na naszym blogu: „».
Źródło: habr.com
