Bei Cloudflight haben wir kürzlich unsere On-Premises-Build-Infrastruktur nach Azure umgezogen. Dazu gehören GitLab, TeamCity, Nexus und viele andere unterstützende Dienste, die es uns ermöglichen, Software so zu entwickeln, wie wir es tun. Die meisten dieser Dienste liefen bereits auf OpenShift, was die Migration recht reibungslos und unkompliziert machte. Unsere GitLab-Instanzen liefen jedoch auf virtuellen Maschinen, um komplizierte Netzwerk-Setups bezüglich SSH in OpenShift zu vermeiden.

Dieser Blogbeitrag skizziert den Weg, den wir genommen haben, die Entscheidungen, die wir getroffen haben, und die Herausforderungen, auf die wir beim Umzug dieser GitLab-Instanzen auf ein Kubernetes-basiertes Setup gestoßen sind. Er soll nicht als Leitfaden dienen, sondern euch auf eurem Weg zum Umzug von GitLab in die Cloud helfen.

Anforderungen und Einschränkungen

Bevor wir die Zielarchitektur entwerfen, müssen wir die Anforderungen und Einschränkungen analysieren.

Die harten Einschränkungen (d. h. nicht verhandelbar) in unserem Fall waren:

  • Zugriff nur aus unserem Unternehmensnetzwerk erlauben
  • Für Endbenutzer sollte kein Unterschied spürbar sein (das bedeutet, dass das Ändern von URLs/Ports keine Option ist)
  • Alle Dienste zonenredundant betreiben

Weitere Anforderungen umfassen:

  • Kostengünstige Ressourcenteilung mit anderen Diensten
  • Verwaltete Angebote nutzen, wo es sinnvoll ist
  • Minimierte Latenz zu unseren Büros

Zusätzlich können wir diese Gelegenheit nutzen, um neue Möglichkeiten wie Zero-Downtime-Upgrades zu erschließen.

Aktuelle Architektur und Migrationsziele

Zu Beginn dieser Reise betrieben wir zwei selbst gehostete GitLab-Instanzen. Eine davon ist älter und wurde zu einer Zeit eingerichtet, als wir vergleichsweise wenige Projekte und Mitarbeiter hatten. Die andere ist neuer und enthält die Mehrheit unserer aktiven Projekte. Die neuere Instanz ist vollständig automatisiert bezüglich der Benutzer-/Repo-Verwaltung, während die ältere ein chaotisches Durcheinander manuell erstellter Repositories, Gruppen und zugewiesener Berechtigungen ist. Unser langfristiges Ziel ist es, alle Projekte von der alten Instanz auf die neue zu verlagern, aber wie wir als Site-Reliability-Engineers wissen: Einen Dienst dauerhaft abzuschalten ist schwieriger als gedacht.

Dieses Setup gibt uns einen Vorteil: Wir können zuerst die ältere, weniger wichtige Instanz migrieren und die Fehler beheben, bevor wir die geschäftskritische migrieren.

Beide Instanzen laufen auf dem offiziellen gitlab-ce-Container mit Podman und sind mit einer schnellen PostgreSQL-Datenbank verbunden.

In diesem Beitrag bezeichne ich diese Instanzen als gitlab-legacy bzw. gitlab-current.

Zielarchitektur: Erste Iteration

Um herauszufinden, wo wir anfangen sollen, ist der erste Schritt ein grober Architekturüberblick. Das ist nicht endgültig und soll sich weiterentwickeln, aber die Visualisierung des „Gesamtbilds“ zeigt uns, worauf wir uns konzentrieren sollten und welchen Herausforderungen wir begegnen könnten.

Architecture diagram showing Azure VNet with an AKS cluster running two GitLab stacks, each with a Gitaly repository, connected to Azure Blob Storage and shared PostgreSQL—an ideal setup for seamless cloud migration of your DevOps infrastructure.

Ich skizzierte die erste Iteration ziemlich schnell und ohne zu sehr auf die technischen Details zu schauen (das wird uns später einholen). Mein Ziel für diese Iteration war es, einen Cluster einzurichten, GitLab darauf zu betreiben und ihn mit unserem Netzwerk zu verbinden.

Jetzt, wo wir wissen, welche Dienste wir verwenden werden – provisionieren wir sie und sehen, wie weit wir mit diesem aktuellen Design kommen!

Azure konfigurieren

Unser bevorzugtes Tool für die Cloud-Konfiguration ist Terraform, also erstellten wir zunächst ein Modul für gemeinsam genutzte Dienste (AKS, VNet, Terraform State) und ein separates für GitLab-spezifische Ressourcen (PostgreSQL, Redis, Storage Accounts).

Da die meisten unserer Büros im DACH-Bereich liegen, wählte ich germany-west-central als Ziel-Azure-Region.

AKS-VNets

Um AKS mit dem Ziel-VNet zu verbinden, muss der Node-Pool in einem Subnetz sein, das im virtuellen Netzwerk enthalten ist. Es wäre möglich, zwischen VNets zu routen, aber das kostet pro GB, und wir versuchen, die Kosten zu optimieren. Für das Peering mit unserem Unternehmensnetzwerk steht uns nur eine /24-Netzmaske zur Verfügung. Da wir auch andere Dienste in diesem virtuellen Netzwerk haben möchten, habe ich das Netzwerk in zwei /25-Blöcke aufgeteilt. Die ersten 128 Adressen sind für die AKS-Knoten reserviert, während der Rest für Dienste verwendet werden kann. Dienste umfassen Azure-Ressourcen wie Redis und PostgreSQL, aber auch exponierte Dienste wie den Ingress-Controller unseres Clusters.

Nachdem ich einen Morgen damit verbracht hatte, mein Architekturdiagramm in Code umzusetzen, stieß ich auf das erste Problem…

Message="The VM size of AgentPoolProfile:default is not allowed in your subscription in location 'germanywestcentral'

Wo sind die Instanzen?

Three orangutans sit on a red sofa in a TV studio setting. The middle orangutan stares at the orangutan on the right, with the caption WHERE INSTANCES appearing above their heads—evoking a panel discussion about GitLab migration strategies.

Wie sich herausstellt, ist die Region germany-west-central derzeit ressourcenknapp. Obwohl die Cloud mit euren Bedürfnissen skalieren soll und dynamische Workloads ermöglichen soll, sind es am Ende des Tages nur andere Computer.

An diesem Punkt kommen die Vorteile von Infrastructure as Code zum Tragen. Durch Ändern von ger-west-central auf eu-west ist unsere gesamte Infrastruktur nun in einer neuen Region provisioniert.

Anmerkung aus der Zukunft: Aufgrund unvorhergesehener Verzögerungen bei der Migration gelang es uns tatsächlich, Ressourcen auf germany-west-central anzufordern (zumindest in einem Abonnement), also lasst euch nicht verwirren, wenn ihr im Rest des Artikels Verweise auf gwc seht.

Unterstützende Ressourcen

Das ist der einfachste Teil des gesamten Unterfangens. Da wir zwei GitLab-Instanzen unterstützen müssen, erstellte ich ein Terraform-Modul, das Storage Accounts, Container, Datenbanken und Caches enthält. Dieses Modul wird dann zweimal in unser Haupt-Terraform-Repository eingebunden – einmal für jede Instanz.

Während der Einrichtung dieser Ressourcen stellten wir besonders hohe Kosten für Azure Cache for Redis fest, da der Premium-Tier der kleinste mit Zonenredundanz ist. Da die Einrichtung von HA-Redis recht einfach ist (im Gegensatz zu Postgres) und die Daten nicht kritisch sind, entschieden wir uns, Redis stattdessen in Kubernetes bereitzustellen.

Secret-Management

Irgendwann würde unser Cluster gerne einige Secrets verwalten, aber die meisten unserer vertraulichen Ressourcen (PostgreSQL, Redis, Storage) befinden sich außerhalb von AKS. Das bedeutet, wir müssen die Lücke zwischen der Terraform- und der Kubernetes-Welt überbrücken.

Der Azure-native Weg hierfür liegt im Azure Key Vault Provider for Secrets Store CSI Driver – nicht nur ein sehr langer Name, sondern auch ein Projekt zur Bereitstellung von KeyVault-Secrets in Kubernetes.

Die Azure-Dokumentation weist uns an, die Azure CLI zu verwenden, um das Add-on für den Cluster zu aktivieren, aber da wir code-getrieben sein wollen, ist das keine Option. Das Terraform-Modul unterstützt das Aktivieren von Add-ons nicht, also gehen wir eine Ebene tiefer und installieren das Add-on selbst. Glücklicherweise sind die AKS-Add-ons einfach Helm-Charts, die von jedem Cluster-Admin provisioniert werden können. Mehr dazu, wie wir das Helm-Chart konfigurieren, später.

Nachdem das Add-on provisioniert ist, können Pods entweder die Secrets direkt mounten oder ein Secret referenzieren, das dynamisch erstellt wird.

Kubernetes konfigurieren

Der bevorzugte Weg, GitLab auf Kubernetes einzurichten, ist das GitLab Helm Chart. Ein Operator ist in Arbeit, aber da er Helm-basiert ist, haben wir in unserem Fall keine Vorteile durch seine Verwendung.

Wie in der Dokumentation zu sehen, enthält das Chart viele andere Komponenten. Einige davon wollen wir für beide Instanzen (die meisten der Kernkomponenten), während andere zwischen den Mandanten geteilt werden sollen (z. B. das Zertifikat-Management).

An diesem Punkt holt uns unser erster Architekturentwurf ein. Der Grund, warum das GitLab-Chart seinen eigenen Ingress-Controller enthält, ist Port 22. In der ursprünglichen Architektur verwenden wir einen gemeinsamen Ingress-Controller für beide Instanzen. Das wird nicht funktionieren, da jede GitLab-Instanz erwartet, unter Port 22 für SSH-Traffic erreichbar zu sein. Das ist natürlich konfigurierbar, aber wir möchten Reibungsverluste minimieren und die Erwartung der Entwickler erfüllen, dass SSH-Cloning ohne Angabe eines Ports funktioniert.

Also zurück ans Reißbrett!

Zielarchitektur: Zweite Iteration

Diese neue Architektur teilt den Ingress-Controller in zwei auf. Jede dieser Instanzen hat ihre IP-Adresse innerhalb des Azure-VNets und kann damit unabhängig Port 22 an die korrekte Instanz weiterleiten.

Um die IP-Adresse für den Ingress-Controller anzugeben, kann folgende Annotation auf dem Service verwendet werden:

'service.beta.kubernetes.io/azure-load-balancer-internal': 'true'
'service.beta.kubernetes.io/azure-load-balancer-internal-subnet': '<subnet-name>',

YAML loswerden

Ja – wir werden Kubernetes ohne YAML konfigurieren. Ihr fragt euch vielleicht wie, und die Antwort ist Jsonnet! Jsonnet ist eine Daten-Templating-Sprache, die es uns ermöglicht, Konfigurationen wiederzuverwenden und zu vereinfachen. Zum Anwenden der Konfiguration nutzen wir Tanka, ein wunderbares Tool, das von den Leuten bei Grafana Labs entwickelt wurde.

Ihr fragt euch vielleicht auch, warum wir nicht einfach Helm verwenden. Das lässt sich am besten in der Tanka-Dokumentation nachlesen:

Helm verlässt sich stark auf das String-Templating von .yaml-Dateien. Wir sind der Meinung, dass das der falsche Weg ist, um das Fehlen von Abstraktionen in YAML zu begegnen, da der Templating-Teil der Anwendung keine Kenntnis von der Struktur und Syntax von YAML hat. […] Jsonnet hingegen unterstützt das Mischen (Patchen, tiefes Mergen) von Objekten über die Ausgabe der Bibliotheken hinaus, wenn nötig.

Aber wir beabsichtigen, das GitLab-Helm-Chart zu verwenden – wie funktioniert das? Keine Sorge! Tanka hat dafür eine Lösung. Tanka wird mit Helm-Unterstützung ausgeliefert. Das funktioniert, indem helm template während der Auswertung des Jsonnet-Codes aufgerufen wird. Das ermöglicht es uns, das Helm-Chart mit Jsonnet zu konfigurieren, es zu rendern und dann weiter anzupassen – wiederum mit Jsonnet. Jetzt, wo geklärt ist, wie unsere Konfiguration abläuft, konfigurieren wir ein paar Dinge!

Initiales Repository-Setup

Für das initiale Setup reicht ein einfacher Aufruf von tk init. Das erstellt die erforderliche Ordnerstruktur und richtet das Dependency-Management mit jsonnet-bundler ein.

Wir strukturieren unser Deployment in drei Umgebungen. In der Tanka-Terminologie sind Umgebungen getrennte Konfigurationseinheiten. In unserem Fall ermöglichen folgende Umgebungen eine saubere Trennung der Zuständigkeiten:

  • environments/infrastructure: Contains shared components like the cert-manager or kured
  • environments/gitlab-legacy: Contains the configuration for the legacy GitLab instance
  • environments/gitlab-current: Contains the configuration for the current GitLab instance

Jede dieser Umgebungen ist auf ihren jeweiligen Namespace beschränkt. Jetzt füllen wir diese Umgebungen mit einigen Ressourcen!

Die Infrastructure-Umgebung

Da wir diesen Cluster für verschiedene Anwendungen wiederverwenden möchten, wäre es sehr ineffizient, wenn jede Anwendung ihre eigene Methode zur Verwaltung von Zertifikaten bereitstellt. Daher ist unser erstes Anliegen die Installation von Cert-Manager in unserem Cluster. Wir können das von Grafana bereitgestellte Cert-Manager Jsonnet Mixin verwenden, was die Dinge erheblich vereinfacht.

Eine weitere Sache, die wir in die Infrastructure-Umgebung aufnehmen werden, ist der zuvor erwähnte CSI Secret Provider. Das ist unser erster Kontaktpunkt mit der Tanka-Helm-Integration, also schauen wir uns das genauer an.

Zunächst müssen wir das Helm-Chart herunterladen. Das tk-Tool enthält ein praktisches Utility dafür:

# initialize a chartfile used for dependency management
tk tool charts init
# add the repo
tk tool charts add-repo azure-secrets-csi https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/charts
# add the chart
tk tool charts add azure-secrets-csi/csi-secrets-store-provider-azure@1.0.1
# download all charts specified in the chartfile
tk tool charts vendor

Jetzt können wir unsere wiederverwendbare Bibliothekskomponente aufbauen. Gemäß Konventionen gehen diese in das lib-Verzeichnis. Die Konfiguration ist recht unkompliziert, da das Helm-Chart nicht viel Konfiguration erfordert:

local tanka = import 'github.com/grafana/jsonnet-libs/tanka-util/main.libsonnet';
local helm = tanka.helm.new(std.thisFile);
{
  csiSecretsStoreProvider: helm.template('aks', '../charts/csi-secrets-store-provider-azure', {
    namespace: 'kube-system',
    includeCrds: true,
    values: {
      'secrets-store-csi-driver': {
        syncSecret: {
          enabled: true,
        },
      },
    },
  }),
}

Das kann dann von unserer Umgebungs-main.jsonnet referenziert werden:

(import ‘secrets-store-csi-driver.libsonnet’)

Und das war’s! Bei der Ausführung von tk apply environments/infrastructure wird das Helm-Chart gerendert, geparst und an der richtigen Stelle eingefügt.

GitLab konfigurieren

Jetzt, wo alles eingerichtet ist, müssen wir GitLab so konfigurieren, dass es mit unserem Stack funktioniert. Unter dem verwirrenden Namen command-line-options.md finden wir die beste Dokumentation zur Konfiguration des Helm-Charts. Gehen wir durch die Dinge, die erforderlich sind, um unseren Stack zum Laufen zu bringen.

Alle folgenden Anpassungen finden in den Values des Helm-Charts statt.

Nicht benötigte Dinge deaktivieren

Im global-Abschnitt müssen wir einige Dinge deaktivieren, die nicht benötigt werden, da sie durch Azure-Angebote oder gemeinsam genutzte Instanzen ersetzt wurden:

{
  global: {
    ingress: {configureCertmanager: false},
    kas: {enabled: false},
    minio: {enabled: false},
  }
}

Leider hat das Helm-Chart viele Stellen, an denen Dinge deaktiviert werden müssen. Das sind die anderen Teile:

{
  certmanager: {install: false},
  'gitlab-runner': {install: false},
  postgresql: {install: false},
  registry: {enabled: false},
}

Je nach euren Bedürfnissen möchtet ihr möglicherweise einige dieser Komponenten aktiviert lassen.

Verbindung zu Azure-Diensten herstellen

Wie oben beschrieben, möchten wir verwaltete Dienste nutzen, wo immer möglich. Die Verbindung zu Azure Blob Storage wird wie folgt hergestellt:

{
  global: {
    appConfig: {
      object_store: {
        enabled: true,
        connection: {
          secret: $.secretName,
          key: 'objectstore-connection',
        },
      },
      lfs: {
        enabled: true,
        proxy_download: false,
        bucket: 'lfs',
      },
      artifacts: {
        proxy_download: false,
        bucket: 'artifacts',
      },
      uploads: {
        proxy_download: false,
        bucket: 'uploads',
      },
      packages: {
        proxy_download: false,
        bucket: 'packages',
      },
      externalDiffs: {
        proxy_download: true,
        bucket: 'external-diffs',
      },
      terraformState: {
        bucket: 'terraform-state',
      },
      ciSecureFiles: {
        enabled: false,
      },
      dependencyProxy: {
        enabled: false,
        bucket: 'dependency-proxy',
      },
    },
  }
}

Damit das funktioniert, muss ein Secret mit dem Connection-String vorhanden sein. In unserem Fall wird dieses Secret vom CSI Secret Store Provider provisioniert. Das im Detail zu konfigurieren geht über den Rahmen dieses Artikels hinaus, könnte aber das Thema eines zukünftigen Beitrags sein – bleibt also dran.

Der Connection-String hat folgendes Format:

provider: AzureRM
azure_storage_account_name: <storageaccountname>
azure_storage_access_key: <access key>
azure_storage_domain: blob.core.windows.net

PostgreSQL wird auf ähnliche Weise eingerichtet:

{
  psql: {
    host: $._config.gitlab.dbHost,
    password: {
      secret: $._config.gitlab.secretName,
      key: 'database-password',
    },
    username: $._config.gitlab.dbUser,
    database: $._config.gitlab.dbName,
  },
}

Nach dem Anwenden dieser Konfiguration wurden wir mit einer frischen GitLab-Instanz begrüßt!

Daten umziehen

Nun, da GitLab eingerichtet ist, müssen wir die Daten irgendwie dorthin bekommen. Der richtige Weg ist, der offiziellen Migrationsdokumentation zu folgen.

Da das Importieren großer GitLab-Instanzen sehr viel Zeit in Anspruch nehmen kann, müssen wir den Importprozess etwas anpassen. Die Dokumentation weist uns an, kubectl exec <Toolbox pod name> -it — backup-utility –restore -t <timestamp>_<version> auszuführen, aber wenn der Import länger dauert als das konfigurierte Kubernetes-API-Timeout (Standard 10 Minuten), müsste man alle paar Minuten manuell mit der Session interagieren. Den Prozess getrennt von der Verbindung laufen zu lassen wäre in Ordnung, aber leider fragen die letzten Schritte des Imports nach interaktiver Bestätigung. Wir möchten auch den Fortschritt irgendwie verfolgen, also wäre eine persistente Session sehr nützlich. Um das zu erreichen, können wir entweder ein benutzerdefiniertes Toolbox-Image verwenden (der saubere Weg) oder die Toolbox als Root ausführen und tmux/screen im laufenden Container installieren (der einfache Weg).

Da wir bereits Azure Blob Storage für die GitLab-Daten verwenden, können wir das für die Übertragung des Backups wiederverwenden. Der einfachste Weg, den ich gefunden habe, war das Hochladen des Backup-Archivs mit azcopy und einer Shared Access Signature (SAS). Durch die Verwendung von Shared Access Signatures vermeiden wir die Speicherung langlebiger Anmeldeinformationen auf der Maschine. Shared Signatures können auch während des Wiederherstellungsprozesses verwendet werden, da das backup-utility von GitLab das Laden von Backups von einer Remote-URL unterstützt.

HA testen

Mit einer laufenden GitLab-Instanz können wir nun prüfen, ob unsere Verfügbarkeitsanforderungen erfüllt werden. Für verwaltete Dienste wie PostgreSQL und Blob Storage müssen wir Microsoft vertrauen. Das Testen von Zonenausfällen in AKS ist jedoch recht einfach. Wir müssen einfach alle Instanzen in einer Region herunterfahren. Das kann im Virtual Machine Scale Set durchgeführt werden.

Also los!

img

Ein Blick auf unsere Pods zeigt uns, dass einige der GitLab-Container in dieselbe Zone gescheduled wurden – das geht nicht! Um das zu verhindern, muss Folgendes in den Values gesetzt werden:

{
  antiAffinity: 'hard',
  affinity: {
    podAntiAffinity: {
      topologyKey: 'topology.kubernetes.io/zone',
    },
  },
}

Nach dem erneuten Versuch des Failovers mit korrekt gescheduleten Pods erhalten wir zumindest eine Antwort von GitLab anstatt einer generischen 502-Antwort von nginx. Ein genauerer Blick offenbart zwei Pain Points: Redis und Gitaly.

Redis

Wie oben beschrieben, wollten wir kein verwaltetes Angebot für Redis verwenden. Als Kompromiss müssen wir uns nun selbst um HA kümmern. Das Redis-Subchart funktionierte nicht gut, als wir es probierten, also ersetzten wir es durch unser eigenes Redis-HA-Modul. Das konfiguriert ein Primary/Replica-Setup mit Sentinel für den Umschaltvorgang im Fehlerfall.

Gitaly (oder der Fluch von GWC)

Die Gitaly-Komponente wird von GitLab verwendet, um Repository-Daten zu speichern und abzurufen. Im Standard-Setup ist sie als Single-Replica-StatefulSet eingerichtet, was in den meisten Situationen in Ordnung wäre, da der Pod in einer anderen Zone neu starten und denselben Storage mounten könnte. Leider fehlt der Region germany-west-central ein entscheidendes Feature: Zone-Redundant Storage. Das bedeutet, dass einmal provisionierter Storage nun an eine bestimmte Zone gebunden ist. Das wird für uns offensichtlich nicht funktionieren, also müssen wir das auf Anwendungsebene beheben und Gitaly als hochverfügbar konfigurieren. Die Abstraktionsschicht, die für die Replikation von Repository-Daten verantwortlich ist, heißt Praefect und erfordert eine separate Datenbank. Praefect verwaltet die Verteilung/Replikation in einem Gitaly-Cluster und kann mit dem Helm-Chart eingerichtet werden:

{
  praefect: {
    enabled: true,
    psql: {
      host: $._config.gitlab.dbHost,
      user: $._config.gitlab.dbName + '_praefect',
      dbName: $._config.gitlab.dbName + '_praefect',
      sslMode: 'require',
    },
    dbSecret: {
      secret: $._config.gitlab.secretName,
      key: 'praefect-password',
    },
    virtualStorages: [
      {
        name: 'default',
        gitalyReplicas: 2,
        maxUnavailable: 1,
      },
    ],
  },
}

Das migriert jedoch keine Daten, sodass der einfachste Ansatz darin besteht, die Ressourcen zu löschen und die Migration erneut durchzuführen.

Nach diesen Änderungen funktioniert die Hochverfügbarkeit ohne Ausfallzeit und mit nur wenigen verworfenen Anfragen, während Redis den neuen Leader wählt.

TOFU, aber nicht die gute Art

Nach der Migration sah zunächst alles gut aus, bis wir das erste git clone versuchten.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

Wir haben sicher nichts Böses getan – was hat sich also geändert? Das zugrundeliegende Transportprotokoll von Git-Clones, SSH, verwendet ein Vertrauensmodell namens Trust On First Use (TOFU). Das bedeutet, dass beim ersten Verbindungsaufbau zu einem Host dessen Host-Key in eurer .ssh/known_hosts gespeichert wird. Ein entscheidendes Detail, das der GitLab-Migrationsleitfaden auslässt, ist, dass die SSH-Host-Keys während der Wiederherstellung nicht wiederhergestellt werden.

Um die SSH-Keys zu migrieren, das Secret gitlab-gitlab-shell-host-keys bearbeiten und die Werte durch die entsprechenden Keys ersetzen. Bei einer Docker-basierten Installation sind diese Dateien normalerweise im Volume zu finden, das unter /etc/gitlab gemountet ist, neben der gitlab.rb-Datei. Danach die gitlab-shell-Pods löschen, um die Keys aus dem Secret neu zu laden. Jetzt sehen die Entwickler- und CI-Maschinen denselben Key, an den sie gewöhnt sind.

Fazit

Je nach eurem Setup und euren Anforderungen kann der Umzug von GitLab in die Cloud wie eine gewaltige Aufgabe erscheinen. Jedoch kann der exzellente Migrationsleitfaden in Kombination mit der Flexibilität des Helm-Charts das zu einem Kinderspiel machen. Die Migration lohnt sich mit einfacher Skalierung, Zero-Downtime-Updates und Hochverfügbarkeit mit minimalem Aufwand. Wir hoffen, dass dieser Leitfaden euch hilft, euer ideales Setup zu finden, euch auf potenzielle Fallstricke aufmerksam macht und eure Migration reibungslos verläuft!