Używanie mojego nowego Raspberry Pi do uruchamiania istniejącej akcji GitHub

Niedawno wspomniałem jak refaktoryzowałem skrypt który utrzymywał mój profil GitHub aktualne. Od Geecon Prague jestem również szczęśliwym posiadaczem Raspberry Pi:

Raspberry Pi

Chociaż obecna konfiguracja działa bezbłędnie – i jest darmowa, chciałem poeksperymentować z samodzielnie hostowanymi runnerami. Oto moje odkrycia.

Kontekst

GitHub oferuje duże bezpłatne korzystanie z GitHub Actions:

Korzystanie z GitHub Actions jest bezpłatne dla standardowych runnerów hostowanych przez GitHub w repozytoriach publicznych oraz dla runnerów hostowanych samodzielnie. W przypadku repozytoriów prywatnych każde konto GitHub otrzymuje określoną ilość bezpłatnych minut i przestrzeni dyskowej do wykorzystania z runnerami hostowanymi przez GitHub, w zależności od planu konta. Wszelkie użycie wykraczające poza uwzględnione kwoty jest kontrolowane przez limity wydatków.

Informacje o rozliczeniach za akcje GitHub

Jednak polityka ta może łatwo zmienić się jutro. Polityka bezpłatnego poziomu wykazuje regularną tendencję do zmniejszania się, gdy:

  • Wystarczająco duży odsetek użytkowników korzysta z produktu, lock-in
  • Akcjonariusze chcą większych przychodów
  • Nowy dyrektor finansowy postanawia obniżyć koszty
  • Globalna gospodarka kurczy się
  • Połączenie powyższych czynników

Ostrzeżony jest uzbrojony. Lubię wypróbować różne opcje, zanim będę musiał wybrać jedną z nich. Przykład: co jeśli będę musiał przeprowadzić migrację?

Teoria

Akcje GitHub składają się z dwóch komponentów:

  • Sama infrastruktura GitHub Actions.
    Obsługuje harmonogram zadań.
  • Biegacze, którzy uruchamiają zadania

Domyślnie zadania są uruchamiane na serwerach GitHub. Możliwe jest jednak skonfigurowanie zadania tak, aby działało na innych serwerach, zarówno lokalnych, jak i w chmurze: są one nazywane self-hosted runners.

The dokumentacja dotycząca tworzenia samodzielnie hostowanych runnerów zawiera wszystkie niezbędne informacje do ich zbudowania, więc nie będę jej parafrazował.

Zauważyłem jednak dwa nietrywialne problemy. Po pierwsze, jeśli mają Państwo zadania w różnych repozytoriach, to proszę skonfigurować zadanie dla każdego repozytorium. Grupy runnerów są dostępne tylko dla repozytoriów organizacji. Ponieważ większość moich repozytoriów zależy od mojego zwykłego konta, nie mogę używać grup. W związku z tym należy zduplikować pakiet każdego repozytorium na Pi runnera.

Ponadto nie ma dedykowanego pakietu: trzeba rozpakować archiwum. Oznacza to, że nie ma możliwości łatwej aktualizacji wersji runnera.

Biorąc to pod uwagę, spodziewałem się, że migracja będzie trwała jedną linię:

jobs:
  update:
    #runs-on: ubuntu-latest
    runs-on: self-hosted

Jest to jednak nieco bardziej skomplikowane. Proszę opisać, jakie kroki musiałem podjąć w moim repozytorium, aby zadanie zadziałało.

Praktyka

GitHub Akcje zależą od Dockera zainstalowanego na runnerze. Z tego powodu myślałem, że zadania działają na dedykowanym obrazie: to po prostu błąd. Cokolwiek skryptuje się w zadaniu, dzieje się w uruchomionym systemie. Przykładowo, początkowy skrypt zainstalował Pythona i Poezję.

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - name: Set up Python 3.x
        uses: actions/setup-python@v5
        with:
          python-version: 3.12
      - name: Set up Poetry
        uses: abatilo/actions-poetry@v2
        with:
          poetry-version: 1.7.1

W kontekście tymczasowego kontenera tworzonego podczas każdego uruchomienia ma to sens; w kontekście stabilnego, długo działającego systemu – nie.

Raspbian, domyślny system operacyjny Raspberry, ma już zainstalowany Python 3.11. Dlatego musiałem obniżyć wersję skonfigurowaną w Poezji. To nic wielkiego, ponieważ nie używam żadnej konkretnej funkcji Pythona 3.12.

[tool.poetry.dependencies]
python = "^3.11"

Raspbian zabrania instalacji jakichkolwiek zależności Pythona w środowisku podstawowym, co jest bardzo rozsądnym domyślnym rozwiązaniem. Aby zainstalować Poezję, użyłem zwykłego menedżera pakietów APT:

sudo apt-get install python-poetry

Następnym krokiem była obsługa sekretów. Na GitHubie ustawia się sekrety w GUI i odwołuje się do nich w skryptach za pomocą zmiennych środowiskowych:

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - name: Update README
        run: poetry run python src/main.py --live
        env:
          BLOG_REPO_TOKEN: ${{ secrets.BLOG_REPO_TOKEN }}
          YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }}

Pozwala to na segregację poszczególnych kroków, tak aby krok miał dostęp tylko do potrzebnych mu zmiennych środowiskowych. W przypadku samodzielnie hostowanych programów uruchamiających, ustawiają Państwo zmienne środowiskowe w istniejącym pliku .env wewnątrz folderu.

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - name: Update README
        run: poetry run python src/main.py --live

Jeśli chcą Państwo bezpieczniejszej konfiguracji, proszę działać na własną rękę.

Wreszcie, architektura jest oparta na modelu pull. Runner stale sprawdza, czy zadanie jest zaplanowane. Aby uczynić runner usługą, musimy użyć gotowych skryptów wewnątrz folderu runner:

sudo ./svc.sh install
sudo ./svc.sh start

Skrypt używa systemd pod spodem.

Wnioski

Migracja z runnera GitHub do runnera self-hosted nie jest dużym problemem, ale wymaga zmiany niektórych elementów. Co najważniejsze, należy zrozumieć, że skrypt działa na maszynie. Oznacza to konieczność zautomatyzowania provisioningu nowej maszyny w przypadku awarii. Rozważam korzyści płynące z uruchomienia runnera wewnątrz kontenera na Pi, aby powrócić do poprzednich kroków. Chętnie dowiem się, czy znalazł Pan i wykorzystał takie rozwiązanie. W każdym razie, na razie nie migruję więcej zadań do self-hosted.

Aby przejść dalej