Eines unserer grossen Anliegen (Manifest) bei re:thinc ist, dass alle Beteiligten jederzeit den aktuellen Entwicklungsstand testen können. Wir möchten das konkrete Produkt in seiner jetzigen Form zeigen und nicht Wireframes oder sonstige nicht funktionale Erzeugnisse als Diskussionsgrundlage verwenden. Grundvoraussetzung dafür ist aus technologischer Sicht ein automatisierter Prozess, welcher den Source Code testet und das lauffähige Produkt verteilt.
Was erwarten wir von einer Build Pipeline?
Wir haben uns intensiv Gedanken gemacht, wie dieser Prozess bei uns aussehen muss um einerseits die Entwicklung zu vereinfachen und andererseits für alle Projektbeteiligten einen zusätzlichen Mehrwert zu schaffen.
Aus diesem Grund soll unsere Build Pipeline folgende Anforderungen erfüllen:
- Wir sind jederzeit in der Lage per Knopfdruck eine neue Version zu installieren.
- Wir müssen keine manuellen Eingriffe während dem Deployment vornehmen.
- Wir können bei Bedarf einzelne Schritte manuell bestätigen (zum Beispiel das Deployment erst nach einem GUI-Testing ausführen).
- Aktualisieren der produktiven Umgebung ohne Betriebsunterbruch.
- Im Fehlerfall kann ein Rollback auf eine ältere Version gemacht werden.
- Bei Bedarf können wir einfach und zeitnah neue Testumgebungen hochfahren.
- Wir möchten nicht abhängig von einem bestimmten Cloud-Service sein (kein Vendor Lock-In).
Dazu haben wir auch einige Anforderungen an die Applikationen selber formuliert:
- Jeder Service bietet eine Resource an mit der automatisiert der Status abgefragt werden kann (Health-Check).
- Jeder Service kann vollständig automatisiert getestet werden.
- Um eine einfache Skalierung zu ermöglichen sind alle Services zustandslos.
Von der groben Idee zur fertigen Build Pipeline
Für den Quellcode haben wir bereits die Versionsverwaltung (Git) im Einsatz. Gehostet wird der Quellcode in privaten Repositories bei Bitbucket. Bitbucket hat insbesondere den Vorteil mit unserem Ticketingsystem Jira integriert zu sein. Ursprünglich haben wir die Pipelines von Bitbucket benutzt um die Applikationen automatisiert zu testen und zu builden. Dabei sind wir aber relativ schnell an die Grenzen des Möglichen (zum Beispiel im Hinblick auf iOS) gekommen. Da wir nicht mehrere Buildtools gleichzeitig einsetzen wollen haben wir uns nach einer Alternative umgesehen und sind schlussendlich bei Bitrise fündig geworden. Mit Bitrise lassen sich durch einzelne Build Schritte sehr einfach und intuitiv komplexe Workflows für unterschiedliche Zielplattformen erstellen. Die Konfiguration wird als YML-Datei ebenfalls im Sourcecode versioniert. Bitrise unterstützt ausserdem standardmässig iOS und Android Builds.
Wir verwenden Bitrise ebenfalls um die Applikation in ein Docker Image zu packen. Docker Images erlauben es einem Entwickler die Applikationsumgebung mit den entsprechenden Einstellungen (zum Beispiel Abhängigkeiten, Libraries, Umgebungsvariablen) per Quellcode zu definieren und danach auf einer beliebigen (Docker-)Umgebung auszuführen. Bislang haben wir mit Docker nur positive Erfahrungen gemacht und bereits bei produktiven Projekten eingesetzt. Die Aktualisierung auf dem Dockerhost bei einer neuen Applikationsversion haben wir allerdings bislang noch manuell gemacht, was jeweils einen kurzen Ausfall der Applikation zur Folge hatte.
An einem spannenden Vortrag am Berner JS Talk hat uns Michael Heimann (hier gehts zu seinem Blogeintrag) Rancher vorgestellt. Rancher ist eine Management Plattform für Docker und erlaubt es Docker Images über mehrere physikalische Server zu verteilen und zu orchestrieren. Wir können damit beispielsweise per Knopfdruck einen bestimmten Service auf eine beliebige Anzahl Instanzen skalieren. Rancher kümmert sich beispielsweise um die Verteilung der Docker Images, das Loadbalancing und das Monitoring der Applikationen.
Und wie funktioniert das mit Apps?
Das Deployment der Apps wollen wir ebenfalls vollständig automatisieren. Bei nativen Apps für iOS und Android sind andere Anforderungen massgebend:
- Signierung der Applikation mit den jeweiligen Appstore Zertifikaten.
- Deployment der signierten Applikation über Distributionskanäle.
- Direktzugang zum aktuellen Build (beispielsweise für manuelles Testing vor dem Release).
Unsere Applikationen werden über den Appstore der jeweiligen Plattform bezogen. Deshalb haben wir uns entschieden, die Lösungen von Apple (Testflight) und Android (Google Play Store) für die Beta Releases einzusetzen. Damit können wir unsere Beta Versionen über die bewährte Infrastruktur des jeweiligen Herstellers verteilen. Falsche Konfigurationen oder andere Hindernisse, wie zum Beispiel fehlende Beschreibungen der App, werden so früh erkannt. Im Fall von iOS bietet uns Testflight die Möglichkeit unsere Applikation an bis zu 10'000 Testgeräte zu senden, ohne dafür die App anders zu signieren.
Bitrise bietet bereits vordefinierte Schritte für die Signierung, sowie die Verteilung in den jeweiligen Store. Zudem haben wir die Möglichkeit die signierten Applikationen direkt von Bitrise zu beziehen, ohne den Umweg über die Distributionskanäle zu nehmen.
Fazit
Die Pipeline deckt alle unsere vordefinierten Ziele ab und ist genügend flexibel für allfällige zukünftige Anpassungen. Dank der Versionierung in der Docker-Registry und den Funktionalitäten von Rancher haben wir zusätzliche Werkzeuge die eine stabile Produktionsumgebung garantieren. Fehleranfällige manuelle Schritte sind auf ein Minimum reduziert. Die Projektbeteiligten haben dank den verschiedenen Umgebungen jederzeit die Möglichkeit den aktuellen Stand der Projekte einzusehen und zeitnah wertvolles Feedback zu geben.