Klasyczny sposób budowania obrazu w pipeline wygląda tak: kontener z klientem Dockera łączy się z gniazdem demona na hoście albo uruchamia własnego demona w trybie uprzywilejowanym. W obu przypadkach zadanie CI dostaje uprawnienia równoważne rootowi na węźle, na którym działa. Dla pipeline’u, który wykonuje kod z repozytorium dostępnego dla wielu osób, jest to ryzyko, którego nie da się uzasadnić.

Skąd bierze się problem

Demon Dockera musi montować systemy plików, tworzyć przestrzenie nazw i konfigurować sieć. Wymaga do tego uprawnień, które w kontenerze oznaczają flagę privileged lub podłączenie gniazda demona z hosta. Kompromitacja zadania CI, na przykład przez złośliwą zależność w budowanym projekcie, daje wtedy dostęp do wszystkich kontenerów na węźle, w tym do sekretów innych zadań.

Narzędzia, które budują bez demona

Kaniko wykonuje instrukcje z Dockerfile w przestrzeni użytkownika, warstwa po warstwie, i wysyła gotowy obraz do rejestru. Nie potrzebuje demona, uprawnień privileged ani dostępu do hosta. Działa jako zwykły pod w klastrze. Obsługuje budowanie wieloetapowe i pamięć podręczną warstw w rejestrze. Ograniczenia: nie uruchamia kontenerów w trakcie budowania, więc Dockerfile, który tego wymaga, trzeba przebudować; projekt jest rozwijany wolniej niż kiedyś, co warto uwzględnić w planie na kolejne lata.

Buildah i Podman budują obrazy w trybie bez roota, z wykorzystaniem przestrzeni nazw użytkownika. Dają większą zgodność z Dockerfile niż Kaniko i pozwalają na uruchamianie kontenerów podczas budowania. Wymagają jednak poprawnie skonfigurowanych podrzędnych identyfikatorów użytkownika na węźle lub w obrazie wykonawczym, co w niektórych klastrach bywa kłopotliwe.

BuildKit w trybie bez roota jest silnikiem stojącym za nowszymi wersjami Dockera, uruchamianym samodzielnie. Ma najlepsze wsparcie dla równoległego budowania i pamięci podręcznej, ale konfiguracja bez uprawnień jest najbardziej wymagająca z trzech.

Dla małego zespołu z klastrem Kubernetes Kaniko jest zwykle najprostszym wyborem: jeden obraz wykonawczy, sekret z poświadczeniami do rejestru, kontekst z repozytorium.

Jak to wygląda w klastrze

Zadanie pipeline’u uruchamia pod z obrazem narzędzia budującego. Pod dostaje:

  • kontekst budowania: sklonowane repozytorium jako wolumen,
  • poświadczenia do rejestru jako sekret zamontowany w oczekiwanej ścieżce konfiguracji,
  • parametry: ścieżkę do Dockerfile, docelowy tag, opcjonalnie rejestr pamięci podręcznej.

Pod nie ma privileged, nie montuje gniazd z hosta, działa w przestrzeni nazw przeznaczonej dla CI z polityką sieciową ograniczoną do rejestru i repozytorium. Po zakończeniu obraz jest w rejestrze, a pod znika.

Zasady niezależne od narzędzia

  • Tag równa się wersja. Obraz oznaczony numerem wersji lub skrótem commita, nigdy latest. Wdrożenie, które wskazuje latest, nie da się cofnąć ani zaudytować.
  • Obraz bazowy przypięty do skrótu. Tag 3.19 obrazu bazowego może wskazywać jutro na coś innego niż dziś. Skrót gwarantuje powtarzalność.
  • Obrazy bazowe z własnego rejestru lub proxy. Limity publicznych rejestrów i ich awarie nie mogą zatrzymywać pipeline’u.
  • Skanowanie przed publikacją. Obraz z krytycznymi podatnościami nie dostaje tagu wersji produkcyjnej.
  • Pamięć podręczna warstw w rejestrze. Budowanie od zera przy każdej zmianie kodu jest marnowaniem minut, które sumują się do godzin.

Podsumowanie

Budowanie bez demona nie jest kompromisem, lecz usunięciem uprawnień, których pipeline nigdy nie powinien mieć. Kaniko dla prostoty, Buildah dla zgodności, BuildKit dla wydajności; w każdym przypadku pod bez privileged, tagi równe wersjom i obrazy bazowe przypięte do skrótów. Zadanie CI, które zostanie skompromitowane, kompromituje wtedy jedno zadanie, nie węzeł.