Github Actions: Hugo Ressourcen cachen für schnelle Builds

Diese Website hier wird seit gut zwei Jahren mit Hugo erstellt. Der Build- und Deployment-Prozess läuft seit kurzer Zeit mit Github Actions.

Das Erstellen der Site auf Github Actions dauert fast fünf Minuten, was ein krasser Gegensatz zum lokalen Build ist: dieser ist in unter einer Sekunde erledigt.

Die Suche nach der Ursache führte recht schnell zum resources-Directory. Hier liegen unter anderem alle Bilder-Dateien, welche von Hugo auf die passenden Größen gebracht werden. Dieser Prozess benötigt lokal auf dem Mac Studio etwa zwei Minuten und auf einem GitHub Action-Runner etwa fünf Minuten.

GitHub Build Time
◎ Builddauer dieser Website mit GitHub Actions

Da sich bei einmal skalierten Bildern für gewöhnlich nichts mehr ändert, bringt es wenig, diese bei jedem Build neu zu berechnen, die Ergebnisse können also über verschiedene Builds hinweg weiterverwendet werden. Das ist genau das, was Hugo bei der Ausführen auf dem lokalen System auch berücksichtigt: hier dauert der gesamte Build gerade mal eine halbe Sekunde, sofern das resources-Directory bereits gefüllt ist:

Local Build Time
◎ Builddauer dieser Website lokal mit gefülltem resources-Directory

Da beim GitHub Actions-Build die komplette Umgebung aber jedes Mal bei null startet, ist das resources-Directory nicht vorhanden und muss neu erstellt werden. Das ist der Grund, warum der Build mit Github Actions so viel länger dauert.

Mit der Github Action actions/cache lässt sich das resources-Directory nach erfolgter Berechnung speichern und vor dem nächsten Build wiederherstellen. Das ist der einfachste Weg, um die Build-Zeit deutlich zu reduzieren. Und zwar um einem Faktor deutlich nördlich von 100[1].

Im Workflow wird die Cache-Action eingefügt und resources als path-Argument übergeben. Das war es auch schon. Vor dem Build wird jetzt das resources-Directory wiederhergestellt und nach dem Build wird es im Cache gespeichert.

Ein kleiner Handstand muss noch gemacht werden, damit auch immer der aktuellste Stand des resources-Directorys im Cache landet. Dazu wird beim Speichern die Build-ID an den Cache-Key angehangen, es wird also bei jedem Lauf ein neuer Eintrag im Cache gespeichert. Beim Restore wird dann aber nur der Präfix des Cache-Keys geprüft, sodass immer der aktuellste Eintrag genutzt wird.[2]

Bei GitHub gibt es alle Infos zur Cache-Action.

GitHub Build Time mit Cache
◎ Build-Dauer dieser Website mit GitHub Actions und gefülltem resources-Directory

Um die Cache-Action zu aktivieren, wird sie vor dem Hugo-Build-Step eingefügt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Deploy Website

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:

    - uses: actions/checkout@v3

    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2
      with:
        hugo-version: 'latest'
        extended: true

    - name: Fetch resources directory from cache
      uses: actions/cache@v3
      with:
        path: resources
        key: ${{ runner.os }}-hugo-resources-directory-${{ github.run_id }}
        restore-keys: |
          ${{ runner.os }}-hugo-resources-directory          

    - name: Build
      run: hugo

    - name: Deploy
      [...]

  1. Die Bruttozeiten inklusive aller Build-Schritte sind dichter zusammen, aber ~30 Sekunden zu ~5 Minuten ist immer noch ein Faktor von 6. 

  2. Das führt dazu, dass der belegte Cache-Speicher bei jedem Build merklich anwächst, hierzu sei die Lektüre der Dokumentation bei GitHub empfohlen. 

Du findest diesen Artikel hilfreich?

Folge Marcus auf Mastodon

Auch über Fragen und Anmerkungen freue ich mich! Am einfachsten sendest du diese direkt an meinen Mastodon-Account.