Docker-Images mit GitHub Actions und Google Cloud bauen

Mithilfe von GitHub Actions als Teil der CI-Pipeline Docker-Images mit Google Cloud Build bauen und in die Google Cloud Registry pushen

Also available in English at Medium.com.

In den letzten Teilen der GitHub Actions-Serie haben wir uns mit dem Gradle-Build beschäftigt. Doch zum Deployment in der Cloud kommen normalerweise fertige Docker-Images und keine JAR-Files zum Einsatz. Wie können wir diese als Teil unserer CI-Pipeline bauen?

Zu diesem Zweck haben wir die GitHub-Action docker-cloud-build entwickelt und Open Source in unserem GitHub-Repository zur Verfügung gestellt. Damit kann in nur einem Schritt aus einem Dockerfile und einer Liste an Input-Dateien ein fertiges Docker-Image gebaut werden, das anschließend in der Google Cloud Registry zur Verfügung steht.

Zudem kann der Build-Output in der GitHub-Oberfläche dargestellt werden, um auf einen Blick zu erkennen, welches Tag erstellt wurde und ob der Build erfolgreich war.

Getting Started

Um die Action zu verwenden, müssen wir zunächst in der action.yaml einen neuen Schritt hinzufügen. Das sieht dann z.B. so aus:

- name: Build Docker Image
  uses: FlowSquad/docker-cloud-build@v1.0.1
  with:
    gcp-project-id: my-project-id
    gcp-service-account-key: ${{ secrets.GCP_SA_KEY }}
    image-name: my-image-name
    image-sources: build/libs/*.jar,Dockerfile,some-other-file
    github-token: ${{ secrets.GITHUB_TOKEN }}

Die oben dargestellten Optionen sind die Minimalkonfiguration. Über die verfügbaren Parameter kann das Verhalten der Action umfangreich angepasst werden. Der Parameter gcp-project-id enthält die ID des Projekts in Google Cloud, das verwendet werden soll. Als Standard-Region für Google Cloud Registry wird eu.gcr.io verwendet. Dies kann mit dem Parameter gcp-gcr-region angepasst werden.

Unter image-name wird dann der Name des Images angegeben, welches gebaut werden soll. Das Tag wird dann jeweils dynamisch erstellt. Als image-sources werden die Dateien angegeben, die der Build als Input benötigt. Dabei können sämtliche Dateien und Ordner aus dem GitHub-Actions-Workspace angegeben werden. Es werden auch Wildcards wie * oder ? unterstützt. Die Dateien müssen dann im Dockerfile in das fertige Image kopiert werden.

Authentifizierung

Unter gcp-service-account-token muss eine Base64-kodierte Service-Account-JSON-Datei angegeben werden. Diese wird für die Authentifizierung bei GCP verwendet. Der verwendete Service-Account benötigt die folgenden Berechtigungen:

cloudbuild.builds.create   # Benötigt, um Cloud Builds zu starten
cloudbuild.builds.get      # Benötigt, um den Build-Status abzufragen
storage.objects.create     # Benötigt, um die Input-Dateien hochzuladen
storage.objects.get        # ^ ebenfalls
storage.objects.list       # ^ ebenfalls
storage.objects.delete     # Benötigt, um nach dem Build aufzuräumen

Die notwendige JSON-Datei kann in Google Cloud IAM unter Dienstkonten erstellt werden. Dazu den gewünschten Service-Account auswählen und unter Schlüssel einen neuen Schlüssel vom Typ JSON erstellen.

GitHub-Integration

Der github-token wird nur benötigt, wenn der Parameter github-disabled nicht auf true gesetzt wird. Normalerweise reicht dafür der Default-Token aus, der von GitHub Actions standardmäßig bereitgestellt wird. Er wird benötigt, um den Commit-Status oder die Release-Notes zu setzen. Mehr dazu später.

Bevor wir die Action ausführen können, müssen wir zunächst in Google Cloud Storage einen Bucket anlegen, in dem die Input-Dateien für den Build zwischengespeichert werden können. Standardmäßig wird dafür ein Bucket mit dem Namen ${projectId}_cloudbuild verwendet. Der Name kann mit dem Parameter gcp-cloud-storage-bucket überschrieben werden.

Wird die Action nun ausgeführt, wird das entsprechende Image gebaut und standardmäßig als $branch-$commitSha-$yyyy.$mm.$dd-$hh.$mm.$ss getaggt. Der erste Teil besteht aus dem (normalisierten) Branch-Namen, der zweite Teil aus dem 7-stelligen Kurz-Hash des verwendeten Commits und der dritte Teil aus dem Datum und der Uhrzeit des Build-Zeitpunkts.

Wird der Build durch das Erstellen eines neuen Tags in GitHub ausgelöst, wird der Name des Tags zusätzlich als Image-Tag gesetzt. Über die verfügbaren Parameter können die verwendeten Tags angepasst werden. So können etwa immer das default Tag global oder für den entsprechenden Branch gesetzt werden oder das Format des Default-Tags geändert werden. Auch eigene Tags sind möglich. Wenn gewünscht, können die gebauten Image-Tags anschließend als Commit-Status in GitHub angezeigt werden. Damit wird auf einen Blick klar, mit welchem Namen das Image abgerufen werden kann. Das Format der Message kann ebenfalls angepasst werden.

Wird der Build durch ein neues Release ausgelöst, so können die gebauten Tags außerdem den entsprechenden Release-Notes hinzugefügt werden. Damit kann es den Nutzern des Images erleichtert werden, die entsprechenden Links zu finden. Ob in den Release-Notes nur das Default-Tag oder alle Tags enthalten sein sollen, lässt sich anpassen.

Alle verfügbaren Parameter und weitere Informationen sind hier zu finden. Wir nutzen die Action flächendeckend in unseren Build-Pipelines, um Docker-Images für Frontend-, Backend- und sonstige Anwendungen zu bauen, die anschließend in unseren Kubernetes-Clustern verwendet werden. Dafür verwenden wir das GitOps-Prinzip. Mehr dazu in einem zukünftigen Blog-Post!