Dawno, dawno temu, za górami, za lasami, gdzie kakałko trzeba było na palenisku robić odganiając się patykiem od niedźwiedzi (a przynajmniej ja tak pamiętam te czasy) a aplikacje webowe tworzyło się w jednej technologii, życie było o wiele prostsze. Obecnie są to kombajny złożone z wielu technologii. Szczególnie to widać na przykładzie front-endu, który rozrósł się niesamowicie od czasu stworzenia pierwszych wersji Node.js. Jesteśmy wprost bombardowani nowymi frameworkami SPA, które coraz prześcigają się w benchmarkach. Co to oznacza z perspektywy DevOps-a?… Problemy. Dla przykładu – obecnie tworze aplikację SPA na stack-u .NET Core MVC, Vue.js, Docker. Teraz, w jaki sposób, mogę użyć pipline w Gitlab CI aby poprawnie zbudował aplikację i uaktualnił obraz docker-a w repozytorium? Dla samego .NET Core-a jest dość mało obraz-ów, co dopiero przy potrzebie uruchamiania komend docker-a i npm-a?
W dzisiejszym artykule przedstawię wam, jak ja podszedłem do rozwiązania problemu 🙂
Faza I: Internecie, na pomoc!
Taka mała zagadka na początek: co robi programista jak natrafi na problem? Kakałko. Później? Śmieje się z basistów. Czemu? Bo to proste. A co robi odpowiedzialny programista? A no idzie do wujka Google pytać o pomoc (potem kakałko). Dokładnie tak zrobiłem. Niestety tym razem się zawiodłem. Oczywiście na forum gitlab-a natrafiłem na wątki, w których ludzie opisywali podobne problemy, jednak tam zazwyczaj kończyło się tym, że w sekcji “before_script” ktoś sobie doinstalował czy to docker-a, czy brakujące jdk-a. Osobiście nie chciałem skończyć z plikiem gitlab-ci.yml wielkości wieży Eiffel -a.
Skoro wujek Google nie był mi w stanie pomóc, to może Docker Hub znajdzie mi obraz docker-a, który będzie zawierał wszystkie potrzebne mi elementy. No niestety tutaj też natrafiłem na ścianę. O ile są obrazy, które obsługują stack-a np. node + docker, to już .NET Core + Node + Docker nie znalazłem 🙁
Faza II: Plik wielkości wieży Eiffle
Chcą mieć różne technologie w pipeline Gitlab CI musiałem przetestować rozwiązanie, które było opisane w jednym z wątków na forum Gitlab-a, czyli bazowanie na jakimś podstawowym obrazie docker-a (który załatwia mi jedną funkcjonalność systemu), oraz doinstalowanie w sekcji “before_script” pozostałych zależności. Jako podstawę wybrałem ogólnie dostępny obraz o wdzięcznej nazwie “gitlab:dind”, który wykonał za mnie dość ciężką robotę w postaci udostępnienia mi api docker-a w pipeline Gitlab CI, który to już jest uruchomiony w Dockerze.
TODO: Wstawić jakiś śmieszny gif z podpisem dockercepcja
Tak, tak, dobrze przeczytaliście: otrzymałem docker-a w dockerze. Stąd też skrót tego obrazu “dind” => “docker in docker”.
Sam obraz bazuje na systemie operacyjnym ubuntu w wersji 14.04 LTS. Jest to bardzo stabilna i nadal często spotykana dystrybucja Ubuntu na serwerach.
Instalacja zależności
Jako, że bazowy obraz jest stworzony na podstawie ubuntu, to do instalacji zależności wykorzystamy apt-get (system do zarządzania pakietami). Może to was zaskoczy, ale pierwszym pakietem, który musimy doinstalować, jest “curl”. Wszystko przez to, że wersja ubuntu udostępniona jako bazy obraz docker-a jest wykastrowana z wielu funkcjonalności aby zmniejszyć jej rozmiar.
Zainstalowanie najnowszej wersji curl-a wymaga uaktualnienia listy pakietów w repozytoriach. Oba polecenia możemy połączyć w jedno za pomocą operatora &&, którego wykorzystanie pozwoli nam na znaczne zmniejszenie ilości warstw w obrazach docker-a.
$ apt-get update && apt-get install -y curl
Następnie musimy zainstalować .NET Core-a. Tutaj nic odkrywczego, po prostu trzeba wejść na stronę https://www.microsoft.com/net/core#linuxubuntu i podążać zgodnie z instrukcją. Jednocześnie trzeba skasować wystąpienia słowa sudo, inaczej podczas budowania obrazu otrzymamy błąd.
$ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg $ mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg $ sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main" > /etc/apt/sources.list.d/dotnetdev.list' $ apt-get update && apt-get install -y dotnet-sdk-2.0.0
Potem przechodzimy do instalacji Node.js. Ponownie mogę tutaj dodać że nic odkrywczego się nie dzieje, wystarczy wejść w oficjalny tutorial instalacji znajdujący się w tym miejscu i wykonać wszystkie komendy.
$ curl -sL https://deb.nodesource.com/setup_8.x | bash - $ apt-get update && apt-get install -y nodejs
Po przejściu tych wszystkich kroków plik gitlab-ci.yml powinien wyglądać podobnie do czegoś takiego:
stages: - build build: image: 'gitlab:dind' stage: build before_script: - 'apt-get update && apt-get install -y curl' - 'curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg' - 'mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg' - sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main" > /etc/apt/sources.list.d/dotnetdev.list' - 'apt-get update && apt-get install -y dotnet-sdk-2.0.0' - 'curl -sL https://deb.nodesource.com/setup_8.x | bash -' - 'apt-get update && apt-get install -y nodejs' - 'dotnet restore' script: - 'dotnet run'
Wstajemy na chwilę od biurka i w tym momencie tańczymy zasłużony taniec zwycięstwa. Nie martwcie się, też nie lubię się ruszać, stąd za taniec uważam przejście do lodówki po kawałek pizzy (ale w rytm muzyki).
Co z kodem? Jak widać zaczyna się tworzyć mały potworek, a mamy tutaj tylko jednego stage-a. Co jeśli byśmy musieli mieć np. 5 środowisk? Produkcja, preprodukcja, staging, QA, development? Zamknęlibyśmy to w jakąś funkcję? Albo po prostu powtarzali kod? No nie…
Faza III Własny obraz docker-a
Kiedy już mamy wszystkie potrzebne komendy do rozbudowania obrazu gitlab:dind toprosty sposób możemy stworzyć własny obraz, który będziemy mogli hostować za pomocą docker hub-a.
Wystarczy stworzyć plik Dockerfile i wypisać wszystkie potrzebne komendy poprzedzając je słowem “RUN”.
FROM gitlab/dind RUN apt-get update && apt-get install -y curl RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg RUN mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg RUN sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main" > /etc/apt/sources.list.d/dotnetdev.list' RUN apt-get update && apt-get install -y dotnet-sdk-2.0.0 RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - RUN apt-get install -y nodejs
Oczywiście, należy pamiętać, by nie pominąć komendy pobrania obrazu bazowego “FROM”.
Teraz, korzystając z docker CLI, umieszczamy stworzony obraz w repozytorium docker hub-a.
$ docker login -u $DOCKER_USER -p $DOCKER_PASSWORD $ docker build -t bd90/dotnet-core-node-docker . $ docker push bd90/dotnet-core-node-docker
Rzecz jasna trzeba założyć konto na docker hubie (pamiętajcie, DOCKER hub, inny hub nie pyknie) 🙂
Dzięki takiemu zabiegowi możemy skrócić nasz plik gitlab-ci.yml do postaci:
stages: - build build: image: 'bd90/dotnet-core-node-docker' stage: build before_script: - 'dotnet restore' script: - 'dotnet run'
Ba! Teraz nawet, w prosty sposób, możemy użyć tego obrazu do innych stage-y, czy nawet projektów!
Podsumowanie
Teraz już wiecie jak można stworzyć pipeline-a Gitlab CI z różnymi technologiami w środku. Pamiętajcie, aby nie bać się tworzyć własnych obrazów docker-a. Dodatkowo, jeśli chcielibyście wykorzystać mój obraz, to znajdziecie go tutaj. W obecnym stanie ten obraz nie jest jeszcze gotowy do użycia produkcyjnego. Natomiast, jeśli macie propozycję na jego to dajcie znać w komentarzach, mailem lub na twitterze.
Do następnego!
Cześć
PS. Spodobał Ci się ten artykuł?