Mer devops i klustret

Squeeds Julkalender | 2021-12-14 | Fredrik Dahlén
Använder du Kubernetes för att drifta dina applikationer? Finns det vissa saker som du gör för hand i klustret? Är du ständigt på jakt efter ännu bättre devops? Då kan det vara läge att automatisera med hjälp av en Kubernetes Operator!

Vad kan man göra med en Operator?

En Operator kan automatisera nästan vilken repetitiv teknisk uppgift som helst. Några exempel är:

  • Installera en applikation eller hel svit med applikationer i klustret, inklusive konfiguration & uppgradering 
  • Hjälpa till med Continuous Integration och Continuous Deployment av dina applikationer
  • Samla in metrics för monitorering och visualisering
  • Spinna upp en ny databas, samt ta backuper enligt schema
  • Starta en Message Broker

Det finns redan många färdiga Operators som är fritt tillgängliga på operatorhub.io. Kanske finns där vad du behöver?

Nedan är ett axplock av olika produkter som har en Operator, vilken man kan använda för att underlätta hanteringen av produkten, och bli av med manuellt arbete.

 

Operators

 

Hur fungerar en Operator?

En Operator är i grunden en helt vanlig applikation i Kubernetes, som körs i en Container i en Pod. Operatorns jobb är som bekant att automatisera en viss manuell uppgift.

Detta gör Operatorn genom att arbeta med Custom Resources, rättare sagt sin egen typ av Custom Resource, som Operatorn själv har definierat att den hanterar. Den som vill få något gjort av Operatorn lägger in en sådan Custom Resource i klustret. Operatorn prenumererar på händelser i klustret som rör just dessa Custom Resources, och när den får nys om en ny Custom Resource kan den agera på det.

En Custom Resource beskriver ett önskat tillstånd, och sedan är det upp till Operatorn att undersöka det faktiska tillståndet, och agera så att det önskade tillståndet uppnås. Detta handlar ofta om att arbeta med andra objekt inuti Kubernetes-klustret, men kan också vara att kontakta en extern tjänst och utföra något.

Förutom att prenumerera på händelser kring sin typ av Custom Resource, har Operatorn en control loop, som körs med ett regelbundet intervall. Då stämmer Operatorn av ifall verkligheten ändrat på sig, och agerar om nödvändigt så att det önskade tillståndet uppnås igen. 

Operator Pattern
Det mönster som beskrivs ovan kallas Operator Pattern. Faktum är att det är precis så som Kubernetes inbyggda Controllers arbetar, med skillnaden att de använder inbyggda resurser istället för Custom Resources.

Operator Pattern

Exempel
Nedan är en YAML som beskriver ett Elasticsearch-kluster, som man önskar ha uppsatt. Om man har installerat Elasticsearch Operator och lägger in nedanstående så kommer den Operatorn se Custom Resourcen och börja installera Elasticsearch med den specificerade konfigurationen i klustret. När installationen är klar har det önskade tillståndet uppnåtts och Operatorn är färdig. I alla fall för tillfället, för om Custom Resourcen justeras eller tas bort ur klustret, vaknar Operatorn till liv igen och gör nödvändiga ändringar i Elasticsearch, eller avinstallerar.

elasticsearch.yaml

Man kan också bygga en egen Operator (a.k.a. nu kodar vi!)

Om dina behov är lite mer specifika, de kanske rör en egenutvecklad applikation, då kan det vara läge att slänga ihop en egen operator. Det gör du i vilket språk som helst (kom ihåg: det är bara ett design pattern). Om du väljer något populärt språk som Go, Java, C# eller Python finns det färdiga SDKs som underlättar. Då går det enkelt att definiera din Custom Resource och låta koden prenumerera på klusterhändelser för denna Custom Resource.

SQL Server DB Operator
Låt oss säga att vi har följande situation:

Appen MyApp driftas i ett Kubernetes-kluster, och varje instans behöver en egen databas för att kunna köra. När en instans av MyApp tas bort skall inte heller databasen finnas kvar. Databas-servern som finns att tillgå är en extern SQL Server och man vill såklart slippa att hantera livscykeln för databasen manuellt.

Då kan man bygga en Operator mha .NET operator SDK från Container Solutions. Följande avsnitt visar med kod hur det kan se ut.

Entry point
Entry pointen har ganska liten footprint. Det vi gör är att skapa upp en Controller (en klass från SDKt) och starta den. Controllern typas mot vår egen Custom Resource (benämns CRD i koden) och matas med en OperationHandler som sen kommer få alla callbacks, för CRD-relaterade händelser i klustret.
Operator.cs

Custom Resource Definition
CRDn är inte mycket för världen, den definierar sin API group, version och sitt namn (mssqldb). Namnet gör att man sedan kan skriva kubectl get mssqldb för att lista alla sådana resurser.
Crd.cs

Istället ligger det intressanta i klassen CrdSpec, där finns det som användaren av Operatorn kan ställa in när man önskar få en databas uppsatt. I detta fall namn på databasen, plus att man pekar ut en ConfigMap och en Secret där Operatorn hittar anslutningsuppgifterna till SQL Servern.
CrdSpec.cs

Operation Handler
Det interface man behöver implementera själv när man bygger en Operation Handler ser ut så här. Det innehåller de callbacks som SDKt kommer göra då det sker CRD-relaterade händelser i klustret.
IOperationHandler.cs

Nedan finns en förenklad implementation. Det är ganska straight forward, där OnAdded leder till att en databas skapas upp, OnDeleted leder till att databasen tas bort, och så vidare.

Klassen håller också ett currentState som har koll på alla kända databaser. Vid callbacken CheckCurrentState, som är en del av Operatorns så kallade control loop, gås alla kända databaser igenom för att upptäcka och ta action på skillnader mellan önskat state och faktiskt state. T ex att databasen behöver skapas upp igen om den har tagits bort.
OperationHandler.cs

Deployment
För att installera ovanstående Operator i ditt kluster, kan du använda en deployment liknande den här:
controller-deployment.yaml

För att lägga in själva definitionen av Operatorns CRD-format i klustret, använd följande YAML:
mssql-crd.yaml

Och för att slutligen använda dig av Operatorn och skapa en databas för MyApp, redigera följande YAML och applicera den:
db.yaml

Källkoden
Ovanstående exempel är en anpassad variant av det som följer med .NET operator SDK. Koden är skriven i .NET 6.0 och kan hittas i sin helhet på GitHub.