In Azure Pipelines markiert ein Approval, das nicht innerhalb seines Timeouts abgeschlossen wird, die Stage als skipped, nicht als failed. Eine skipped Stage ist in den meisten Dashboards und den meisten Notification-Templates grün, also kann eine Model Promotion, die niemand angesehen hat, einen Pipeline-Lauf abschließen, der Erfolg meldet, während die Änderung nie rausging, oder schlimmer, während eine vorangehende Stage bereits die Hälfte davon erledigt hat. Teil 8 hat Anwendungen auf das Gateway gesetzt. Dieser Teil bringt das Gateway selbst wiederholbar in Produktion und verwandelt die Model-Retirement-E-Mail aus Teil 1 in eine Datenbankabfrage.

Was tatsächlich deployt wird

Vier Artefakte, die sich mit drei verschiedenen Geschwindigkeiten ändern, und genau deshalb sind es drei Pipelines statt einer.

  • Plattform-Infrastruktur aus dem 10-platform von Teil 2: API Management, AKS, Netzwerk, Observability. Ändert sich ein paar Mal im Jahr, deployt mit menschlichem Approval, und niemand hat es eilig.
  • Modell-Deployments und Gateway-Konfiguration, die 20-models-Schicht plus Policy-XML und die Alias-Map. Ändert sich wöchentlich. Das ist die Pipeline, die das eval gate braucht.
  • Tenant-Konfiguration, die 30-tenants-Schicht. Ändert sich beim Onboarding und ist die eine Stelle, an der ein Self-Service-Pull-Request eines Consumer-Teams Sinn ergibt.
  • Der Control-Plane-Container auf AKS. Gewöhnliches Anwendungs-CI/CD, und der uninteressanteste der vier, genau weil er gewöhnlich ist.

Policy-XML verdient eine eigene Anmerkung. Es wird bei jedem Request ausgeführt, ist also Produktionscode, und es erreicht Produktion über dieselbe Pipeline mit demselben Review wie Terraform. Das Portal ist zum Lesen da, nicht zum Editieren, und der Weg, das durchzusetzen, ist ein drift check in der Pipeline, der fehlschlägt, wenn die deployte Policy vom Repository abweicht.

Die service connection, und der Timer, den niemand stellt

Pipelines authentifizieren sich gegenüber Azure mit einer Azure-Resource-Manager-service-connection, und die empfohlene Konfiguration ist workload identity federation, mit einer App Registration oder einer Managed Identity, was Secrets und Secret-Management komplett aus dem Bild nimmt. Für eine Plattform, deren gesamtes Argument in Teil 5 der Credential Lifecycle war, wäre ein gespeichertes Secret an dieser Stelle schwer zu verteidigen.

Zwei operative Details gehören ins Runbook, statt entdeckt zu werden. Azure Pipelines deaktiviert service connections automatisch, wenn sie 100 Tage lang nicht benutzt wurden, und ein service-connection-Administrator oder Project Administrator muss sie wieder aktivieren. Eine Pipeline, die nur bei quartalsweisen Infrastrukturänderungen läuft, ist genau die Pipeline, die das auslöst, und der Fehler kommt zum schlechtesten Zeitpunkt, wenn etwas Dringendes deployt werden muss. Und eine service connection, die von einem Secret auf Federation umgestellt wurde, lässt sich sieben Tage lang zurücksetzen; danach muss ein neues Secret manuell erstellt werden, und Verbindungen, die man umgestellt und dann manuell verändert hat, lassen sich über das Tool gar nicht mehr zurücksetzen.

Das eval gate gehört zum environment, nicht zum Job

Der Instinkt ist, einen Eval-Schritt ans Ende des Deploy-Jobs zu hängen. Das macht das Eval zu etwas, das die Pipeline tut, was heißt, dass der Autor der Pipeline es umsortieren, mit einer Condition überspringen oder hinter das Deployment verschieben kann, das es eigentlich absichern sollte.

Azure Pipelines hat das richtige Primitiv: checks auf dem environment oder auf der service connection, die die Stage konsumiert. Bevor eine Stage läuft, müssen alle checks auf allen Ressourcen, die sie benutzt, erfüllt sein, und eine einzige negative Entscheidung verweigert die Stage. Konfiguriert man das Eval als Invoke REST API check, der den Eval-Service der Control Plane aufruft, gehört das Gate nicht mehr zur Pipeline-Definition, sondern zum environment.

Die Finalitätsregeln zählen hier. Entscheidungen von Approvals und den meisten checks sind final. Invoke-Azure-Function- und Invoke-REST-API-checks sind die Ausnahme, sie können erneut ausgeführt werden, und wer eine time between evaluations ungleich null setzt, macht die Entscheidung des checks nicht-final, sie wird dann neu evaluiert. Für ein eval gate ist das eine Designentscheidung und kein Detail: Ein nicht-finaler check, der immer wieder läuft, ist richtig für "das Eval muss noch bestehen, wenn diese Stage endlich startet", und ein finaler ist richtig für "dieser Build hat seine Evals bestanden, ship it". Man wähle eines, und wisse, dass die asynchrone Form, auf dem empfohlenen Weg konfiguriert, final ist.

Darüber kommt ein menschliches Approval, auf dem Produktions-environment, mit aktivierter Option, dass Approver ihre eigenen Runs nicht freigeben dürfen. Und der Timeout wird bewusst gesetzt, wegen des Aufmachers dieses Posts: Ein Approval, das abläuft, markiert die Stage als skipped, und skipped liest sich als grün. Die Mitigation ist ein kurzer Timeout, Alerting spezifisch auf skipped Stages, und eine grüne Pipeline nie als Beleg dafür zu nehmen, dass ein Deployment stattgefunden hat.

Was das Eval tatsächlich prüft

Ein golden set von Fällen, die den Konsumenten der Plattform wichtig sind, versioniert im Repository neben dem Policy-XML, ausgeführt gegen die Kandidaten-Konfiguration durch das Gateway statt gegen einen Modell-Endpoint. Der letzte Punkt macht daraus ein Plattform-Eval statt eines Modell-Benchmarks: Es übt die Alias-Auflösung, die Policies, den Content-Safety-Filter und den Retrieval-Pfad aus, und jedes davon kann eine Antwort kaputt machen, ohne dass sich das Modell überhaupt ändert.

Drei Schwellwerte, und den dritten lassen die Leute weg. Ein aggregierter Qualitätsscore gegen das golden set. Ein Latenzbudget, weil ein Modell, das marginal besser und spürbar langsamer ist, für einen interaktiven Assistenten kein Upgrade ist. Und eine Regressionsliste: konkrete Fälle, die nicht schlechter werden dürfen, egal was der Durchschnitt tut. Eine neue Modellversion, die den Mittelwert verbessert und dabei die drei Prompts bricht, von denen das Customer-Service-Team abhängt, ist die normale Gestalt einer schlechten Promotion, und nur die Regressionsliste fängt sie.

Promotion, Canary und Rollback sind ein und derselbe Mechanismus

Weil Teil 3 einen Alias vor einen backend pool gestellt hat, berührt eine Modellversionsänderung keine Anwendung. Sie ist eine Konfigurationsänderung mit drei Gestalten:

  • Canary: das neue Deployment mit niedrigem Gewicht in derselben Prioritätsgruppe in den Pool des Alias aufnehmen. Ein kleiner Teil des Traffics geht dorthin, die Metriken aus Teil 5 zeigen Latenz- und Token-Unterschiede pro Modell-Alias, und nichts musste neu deployt werden.
  • Promotion: die Gewichte verschieben, dann das alte Deployment aus dem Pool entfernen.
  • Rollback: die umgekehrte Gewichtsänderung. Sekunden, kein Redeploy, und genau das ist das ganze Argument dafür, dass der Alias existiert.

Das eine, was das nicht leistet, sind identische Antworten über die Canary-Grenze hinweg. Zwei Modellversionen, die denselben Alias bedienen, produzieren für denselben Prompt unterschiedliche Outputs, also muss alles, was Antworten cached, vergleicht oder wiederholt abspielt, wissen, welche Version geantwortet hat. Das ist ein Log-Feld, hinzugefügt im selben Moment wie der Canary, nicht danach.

Retirement, das jetzt eine Query ist

Die Retirement-E-Mail aus Teil 1 hat zwei Tage Grepping durch Repositories gekostet. Sie sollte jetzt eine Query kosten, weil drei frühere Entscheidungen die Antwort wissbar gemacht haben. Teil 2 hat Modellversionen gepinnt und version_upgrade_option bewusst gesetzt, also bewegt sich kein Deployment von allein. Teil 3 hat jede Anwendung hinter einen Alias gestellt, also ist das Mapping von Alias zu Deployment Daten. Teil 5 hat jeden Request mit seinem Tenant und seinem aufgelösten Modell geloggt, also ist die Menge der Tenants, die ein Deployment in den letzten 30 Tagen tatsächlich benutzt haben, eine Log-Query statt einer Annahme.

Das Retirement-Runbook sind dann vier Schritte, die in ein Ticket passen: abfragen, welche Tenants das Deployment kürzlich benutzt haben; das Ersatz-Deployment aufsetzen und das eval gate dagegen laufen lassen; den Alias per Canary umstellen und die Regressionsliste beobachten; umlegen und entfernen. Die Anwendungen erfahren nie, dass irgendetwas davon passiert ist, und genau dieses Ergebnis war der Zweck der ganzen Serie.

Fehlerbilder, die man im Blick behalten sollte

  • Skipped als succeeded gelesen. Ein abgelaufenes Approval überspringt die Stage. Explizit auf den skipped-Zustand alerten, in der Pipeline-Notification und in dem Dashboard, auf das das Team tatsächlich schaut.
  • Das Eval als Job-Schritt. Wenn es umsortiert oder per Condition wegkonfiguriert werden kann, ist es Dokumentation. Als environment check ist es ein Gate.
  • Bypass ohne Sichtbarkeit. Einen check zu umgehen erfordert Administrator-Rechte auf der Ressource und wird samt Urheber aufgezeichnet, was gut ist, und es lohnt sich, das in einem wöchentlichen Review sichtbar zu machen, statt es im Checks-Panel liegen zu lassen.
  • Eine service connection, die bei 100 Tagen deaktiviert wird. Die quartalsweise Infrastruktur-Pipeline entdeckt es während eines Incidents.
  • Portal-Edits an Policies. Ohne drift check überlebt ein im Portal gemachter Hotfix, bis der nächste Deploy ihn stillschweigend zurückdreht, was das schlechtestmögliche Timing ist.
  • Ein Eval-Set, das niemand aktualisiert. Ein golden set, das sich ein Jahr lang nicht geändert hat, misst das Produkt vom letzten Jahr.

Was Teil 10 erbt

Ein Gateway, das über Pipelines shippt, mit Evals als Gates und Modelländerungen als Konfiguration. Jeder Mechanismus der Serie ist jetzt an seinem Platz, und jeder davon emittiert etwas. Der letzte Teil handelt davon, was damit zu tun ist: Traces, Evals in Produktion statt in der Pipeline, die Governance-Fragen, die der EU AI Act stellt, und die Zahlen, die Teil 1 versprochen hat.

Weiterlesen

Für die Infrastruktur- und Plattform-Seite, so etwas in diesem Maßstab zu betreiben, gibt es die Field Notes auf ercan.cloud, und der Hub liegt auf ercanermis.com.

Referenzen