wSołtysiak BlogPraktycznie o tworzeniu oprogramowania i nie tylko
31 sierpnia 2020

Darmowy Continuous Deployment dla Haskella

Po zakończeniu pisania pierwszej aplikacji w Haskellu nadszedł czas na przygotowanie automatyzacji budowania i publikowania jej kolejnych wersji. Po zapoznaniu się z dostępnymi możliwościami postanowiłem postawić na tandem składający się z GitLaba oraz nieznanej mi wcześniej platformy Fly.io. Był to strzał w dziesiątkę, szczególnie że dla mniejszych, hobbistycznych projektów jest to zupełnie darmowe.

Darmowy Continuous Deployment dla Haskella

Co dostajemy w wersji darmowej?

Zanim przejdziesz dalej, warto sprawdzić, czy te narzędzia spełniają twoje wymagania. Oto krótkie podsumowanie kwestii istotnych dla tego artykułu:

  • GitLab (link do szczegółów) - rozbudowana platforma przeznaczona do rozwoju oprogramowania. Pozwala na założenie nieograniczonej liczby publicznych oraz prywatnych repozytoriów. Wersja darmowa umożliwia uruchomienie zadań CI/CD trwających do 2000 minut na miesiąc.
  • Fly.io (link do szczegółów) - platforma umożliwiająca uruchamianie aplikacji jak najbliżej użytkownika końcowego (tzw. location-smart). Każde konto dostaje co miesiąc 10 dolarów, które można wykorzystać na dowolne usługi. Jest to kwota, która pozwala uruchamiać i utrzymywać przy działaniu małe aplikacje.

Przygotowanie Dockerfile

Aby opublikować naszą aplikację, musimy ją zdokeryzować. W tym celu utworzmy plik Dockerfile. Całość zbudujemy na podstawie obrazu phadej/ghc, który zawiera już w sobie GHC oraz cabala. Do wyboru dostajemy obrazy oparte na Debianie lub Ubuntu wraz z różnymi wersjami GHC. Mam sentyment do Debiana, więc wybrałem obraz na podstawie jego stabilnej, odchudzonej wersji (ok. 300 MB) - phadej/ghc:8.10.2-buster-slim. Zakładając, że nasza aplikacja nazywa się foobar oraz nasłuchuje na porcie 8080, całość będzie wyglądać następująco:

FROM phadej/ghc:8.10.2-buster-slim

WORKDIR /usr/src/app
COPY . .

RUN cabal update
RUN cabal build foobar

EXPOSE 8080
CMD ["cabal", "run", "foobar"]

Konfiguracja Fly.io

Żeby rozpocząć naszą przygodę z platformą Fly.io potrzebne będzie nam narzędzie flyctl. Jego instalacja jest banalna i sprowadza się do uruchomienia jednej komendy, niezależnie od systemu operacyjnego. Listę tych komend znajdziesz w oficjalnej dokumentacji.

Po zainstalowaniu flyctl należy założyć konto

flyctl auth signup

lub zalogować się za pomocą komendy

flyctl auth login

Następnie przechodzimy do katalogu z naszym projektem. Aby dodać nową aplikację do Fly.io oraz wygenerować plik fly.toml wystarczy wykonać polecenie:

flyctl auth init

Na początku flyctl zada nam dwa pytania: o nazwę aplikacji oraz grupę, do której ma należeć (domyślnie zaznaczona jest nasza “prywatna grupa”). Następnie będziemy mogli wybrać builder. Niestety żadna z opcji nie pozwala na zbudowanie aplikacji napisanej w Haskellu, więc zostawiamy None (Do not set a builder). Ostatnie pytanie dotyczy portu, na którym nasłuchuje nasza aplikacja. To właśnie na ten port kierowany będzie ruch z zewnątrz.

? App Name (leave blank to use an auto-generated name) 

? Select organization: Wojciech Sołtysiak (wojciech-soltysiak)

? Select builder: None
    (Do not set a builder)
? Select Internal Port: 8080

New app created
  Name     = foobar     
  Owner    = wojciech-soltysiak  
  Version  = 0                   
  Status   =                     
  Hostname = <empty>   

Przygotowanie .gitlab-ci.yml

Nadszedł czas na przygotowanie konfiguracji dla GitLaba. Żeby poprawnie umieścić naszą aplikację na platformie Fly.io, nasz runner musi:

  1. Pobrać i uruchomić narzędzie flyctl. Aby to zrobić, wystarczy doinstalowanie zwykłego curla.
  2. Zbudować obraz. W tym celu nasz pipeline uruchomimy na obrazie docker:stable wraz z usługą docker:dind (przy okazji polecam ten artykuł wyjaśniąjący działanie Docker in Docker).

Konfiguracja dla automatycznego deploya na Fly.io dla każdej zmiany na branchu master wygląda następująco:

image: docker:stable

services:
  - docker:dind

stages:
  - deploy

fly_deploy:
  stage: deploy
  before_script:
    - apk add curl
    - curl -L https://fly.io/install.sh | sh
    - export FLYCTL_INSTALL="/root/.fly"
    - export PATH="$FLYCTL_INSTALL/bin:$PATH"
  script:
    - flyctl deploy
  only:
    - /^master$/

Pozyskanie tokena i wstawienie go do GitLaba

Ostatnim krokiem jest pozyskanie tokena potrzebnego do uwierzytelnienia podczas wywołania flyctl deploy na runnerze. Wystarczy wykonać następującą komendę:

flyctl auth token

W odpowiedzi dostaniemy nasz token. Kopiujemy go i wstawiamy do GitLaba (Nasz projekt → Settings → CI/CD → Variables) pod nazwą FLY_API_TOKEN. Warto go zabezpieczyć i zaznaczyć opcje Protect variable oraz Mask variable. Dzięki temu mamy pewność, że token nie wycieknie w logach oraz pojawi się tylko na tagach i branchach oznaczonych jako protected.

Podsumowanie

Opisana wyżej konfiguracja CD została już kilkukrotnie przetestowana w boju i sprawdza się bardzo dobrze. Jedynym minusem (niezależnym od GitLaba i Fly.io) jest długi czas budowania aplikacji. Od wrzucenia zmian na mastera do odpowiadającej aplikacji mija czasami nawet 35 minut. Ponoć przejście na stacka potrafi znacząco skrócić czas budowania aplikacji, ale jeszcze nie miałem okazji tego sprawdzić. Mam nadzieję, że połączenie GitLaba z Fly.io Cię zainteresowało i wykorzystasz je przy swoim kolejnym projekcie.