Cloud Provider like AWS offer different ways to interact with their systems and to control it. One way are the typical Web UI, others are command line tools. But they also provide APIs for many programming languages. Using them many tasks can be automated, if wanted in a reactive way like Kubernetes does it in reconciliation loops.
Take a look in how this can be done with the programming language Go and as an example for AWS as cloud provider. In the end there's a hint leading to a private project for a multi-cloud provider reconciler library in Go.
4. • Cloud Provider wie AWS, GCP oder Azure ermöglichen die
Auslagerung von Rechenzentren
• Ressourcen und Dienste können gemietet werden
• Der Betrieb der Infrastruktur erfolgt durch die Cloud Provider
• Die Administration der Software und Dienste erfolgt durch den
Nutzer
• Hierfür werden Web UIs, Kommandozeilen-Programme und
APIs geboten
Externe Ressourcen und Dienste Frank Müller
5. •Erleichtern den Umgang mit
komplexen Infrastrukturen
•Skalieren gut
•Lassen sich hochverfügbar
betreiben
•Können Latenzen durch eine
weltweite Verteilung gering
halten
Anwendungen in der Cloud Frank Müller
7. • Anwendungen auf Abruf automatisch installieren
• Anwendungen aktualisieren und dazugehörige Konfigurationen,
Datenbank Schemata und weitere Dinge anpassen
• Backups einer Anwendung erstellen oder sie restaurieren
• Leader einer einen verteilten Anwendung ohne einen internen
Wahlprozess setzen
• Fehler im Cluster simulieren, um seine Widerstandsfähigkeit zu
testen
Handarbeit in Clouds vermeiden Frank Müller
9. • Kubernetes nutzt Controller mit Reconciliation Loops zur
Reaktion auf Ereignisse im Cluster
• Dieser Mechanismus kann auch für eigene Operatoren genutzt
werden
• Sie können sich für individuelle Ressourcen oder Ressourcen-
Typen registrieren
• Callbacks für die Operationen Add, Update und Delete
ermöglichen die Automatisierungen
Controller reagieren auf Veränderungen Frank Müller
11. • Ein Dokument (Custom Ressource) wird dem Cluster zur
Installation und Konfiguration von Microservices hinzugefügt
• Der entsprechende Operator wird hierüber informiert und liest
das Dokument aus
• Er installiert den Microservice mit allen individuellen
Konfigurationen und Komponenten auf Basis des Dokuments
• Bei Bedarf werden die Verbindung zu Datenbanken,
Dateisystemen, Message Queues und weiterem gesetzt
Zum Beispiel Microservices installieren Frank Müller
13. •AWS bietet eine flexible API
•Ressourcen lassen sich
gezielt oder gefiltert abfragen
•Anlage, Modifikation und
Löschung sind möglich
•Kein Subscribe auf
Änderungen
Eine umfangreiche API Frank Müller
14. •Eine Verbindung zu AWS wird
konfiguriert
•Auf dieses Basis verbindet
sich ein EC2 Client
•Eine Schleife fragt die
Instanzen gefiltert ab
•Diese können über Callbacks
verarbeitet werden
Ein einfacher Ansatz für EC2 Instanzen Frank Müller
16. • Entwicklung bei Google seit Ende 2007, erstes Release
November 2009
• Initial durch Robert Griesemer, Rob Pike und Ken Thompson
• Heute große Community
• Fokus liegt auf Geschwindigkeit, Nebenläufigkeit und Spaß
• Bis heute ist Go abwärtskompatibel
• Einsatz rund um Cloud-Native und Microservices
Die Programmiersprache Go Frank Müller
17. ❞
Go hat das Ziel, die Sicherheit und
Geschwindigkeit einer statisch typisierten
kompilierten Sprache mit der Ausdrucksstärke
und dem Komfort einer dynamisch typisierten
interpretierten Sprache zu verbinden.
Zudem soll die Sprache für moderne,
stark skalierende Systeme
geeignet sein.
–Rob Pike
18. • Syntax passt auf eine HTML-Seite
• Klare und wenig mehrdeutige Struktur der Sprache
• Type Inference und Garbage Collection erlauben ein Arbeiten
ähnlich wie in vielen dynamisch typisierten Sprachen
• Nebenläufigkeit unterstützt elastische Server-Anwendungen
• Compiling ist sehr schnell, Single Binary und Cross Compiling
erleichtern die Arbeit in heterogenen Umgebungen
Was Spaß macht Frank Müller
21. •Lädt die Konfiguration
•Stellt zentrale Informationen
und Funktionen zur Verfügung
•Unterstützt die Reconciler
Start mit einem zentralen Manager Frank Müller
22. package cloud
// Cloud manages all needed information to reconcile AWS services.
type Cloud struct {
ctx context.Context
cancel func()
cfg aws.Config
err error
}
Die Cloud verwalten Frank Müller
23. // NewCloud instantiates the cloud connection with the default config.
func NewCloud(ctx context.Context) (*Cloud, error) {
ctx, cancel := context.WithCancel(ctx)
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
return &Cloud{
ctx: ctx,
cancel: cancel,
cfg: cfg,
}, nil
}
Verbindung aufbauen Frank Müller
25. package machine
// Callback defines the signature to work on an EC2 instance.
type Callback func(
cloud *cloud.Cloud,
client *ec2.Client,
inst *type.Instance,
description string)
// StateCallbacks allows to register individual callbacks per state.
type StateCallbacks map[types.InstanceStateName]Callback
Package für EC2 Virtual Machines Frank Müller
26. // GoReconcile starts monitoring a number of instances defined by the input.
// Based on the state the defined callbacks will be called.
func GoReconcile(
cloud *cloud.Cloud,
input *ec2.DescribeInstancesInput,
callbacks StateCallbacks,
description string) {
// Start internal reconciler as goroutine.
go reconcile(cloud, input, callbacks, description)
}
Instanzen immer wieder prüfen Frank Müller
27. // reconcile as goroutine monitors the input instances in a loop.
func reconcile(...) {
client := ec2.NewFromConfig(cloud.Config())
ticker := time.NewTicker(cloud.ReconcileDuration())
defer ticker.Stop()
for {
select {
case <-cloud.Done():
log.Printf("EC2 reconciler %q stopped", description)
return
case <-ticker.C:
reconcileInstances(cloud, client, input, callbacks, description)
}
}
}
Reconcile-Goroutine Frank Müller
28. // reconcileInstances queries the filtered instances and executes the callbacks.
func reconcileInstances(...) {
output, err := ec2.DescribeInstances(cloud.Ctx(), input)
if err != nil { ... } // Log and return.
for _, reservation := range output.Reservations {
for _, instance := range reservation.Instances {
callback, ok := callbacks[aws.StringValue(instance.State.Name)]
if ok {
// Callback for this state is registered, so execute.
callback(cloud, client, instance, description)
}
}
}
}
Instanzen suchen & Callbacks aufrufen Frank Müller
29. •Eigene individuelle Tools
können nun entwickelt
werden
•Callbacks für die EC2
Instance Status lassen sich
registrieren
•Mehrere Reconciler in einem
Binary sind möglich
Einsatzbereit für EC2-Instanzen Frank Müller
30. package main
// StatesCache allows to store the current states of all found instances.
type StatesCache map[string]types.InstanceStateName
// StateLogger logs any state change.
type StateLogger struct {
cache StatesCache
}
// NewStateLogger creates a StateLogger instance.
func NewStateLogger() *StateLogger {
return &StateLogger{cache: StatesCache{}}
}
Monitor für Statusänderungen – 1 Frank Müller
31. // Callback implements the machine callback.
func (l *StateLogger) Callback(cloud *cloud.Cloud, client *ec2.Client,
inst *type.Instance, description string) {
id := aws.String(inst.InstanceID)
state := aws.String(inst.State.Name)
if state != l.cache[id] {
log.Printf("status of instance %q changed to %q", id, state)
l.cache[id] = state
}
}
Monitor für Statusänderungen – 2 Frank Müller
32. // Callbacks returns all StateCallbacks for the StateLogger. So in this
// case the own callback for all states.
func (l *StateLogger) StateCallbacks() machine.StateCallbacks {
return machine.StateCallbacks{
types.InstanceStateNamePending: l.Callback,
types.InstanceStateNameRunning: l.Callback,
types.InstanceStateNameShuttingDown: l.Callback,
types.InstanceStateNameTerminated: l.Callback,
types.InstanceStateNameStopping: l.Callback,
types.InstanceStateNameStopped: l.Callback,
}
}
Monitor für Statusänderungen – 3 Frank Müller
33. // Now we can start the awsbot.
func main() {
cloud, err := awsbot.NewCloud()
if err != nil { ... } // Log with exit.
// Filter instances by tag.
input := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
&ec2.Filter{
Name: aws.String("tag:Name"),
Values: []*string{aws.String("JAX2023")},
},
},
},
...
Monitor für Statusänderungen – 4 Frank Müller
func JAXInstances() *ec2.DescribeInstancesInput { // Filter all instances
return &ec2.DescribeInstancesInput{ // tagged "JAX2023".
Filters: []*ec2.Filter{
&ec2.Filter{
Name: aws.String("tag:Name"),
Values: []*string{aws.String("JAX2023")},
},
},
}
}
func AllInstances() *ec2.DescribeInstancesInput { // Filter all instances.
return &ec2.DescribeInstancesInput{}
}
34. // Now we can start the cloud reconciling.
func main() {
cloud, err := cloud.NewCloud()
if err != nil {
log.Fatalf("cannot create cloud reconciler: %v", err)
}
machine.GoReconcile(cloud, JAXInstances(), NewStateLogger().Callbacks(),
"States of JAX 2023 instances")
machine.GoReconcile(cloud, AllInstances(), NewStateLogger().Callbacks(),
"States of all instances")
<-cloud.Done()
}
Monitor für Statusänderungen – 5 Frank Müller
36. • Die dargestellte Lösung ist ein Start nur für AWS
• Ein generischer Ansatz erlaubt den Einsatz für unterschiedliche
Cloud Provider
• Ein Manager und individuelle Services kapseln die notwendigen
Funktionalitäten
• Provider-spezifische Aufgaben werden durch Interfaces
dargestellt
• Diese werden pro unterstütztem Provider implementiert
Generischer Ansatz Frank Müller
37. Komponenten Frank Müller
main Cloud
<Interface>
Cloud
AWS, GCP,
...
Cloud
Provider
<Interface>
Machine
AWS, GCP,
...
Machine
Service
Cloud
Provider
38. • Weitere Services werden hinzugefügt, zum Beispiel verschiedene
Speicher, Datenbanken und Queues
• Weitere Provider werden hinzugefügt, zum Beispiel GCP, Azure,
aber auch Kubernetes sowie lokale Container und Mocks
• Direkter Zugriff auf die API der Cloud Provider innerhalb der
Callbacks
• Kein Polling des Zustands via Timer, sondern Abonnement auf
Basis der Subscription Mechanism der Cloud Provider
Entwicklungsmöglichkeiten Frank Müller
39. •Code ist Open-Source
•Lizenz ist New BSD
•Repository bei GitHub unter
tideland/go-cloubotics
•Import als
tideland.dev/go/cloubotics
•Mitstreiter herzlich
willkommen
Entwicklung von Go Cloubotics gestartet Frank Müller
41. • Clouds bieten eine große Anzahl an Diensten
• Eine Automatisierung kann sich an den Kubernetes Operatoren
orientieren
• Im Idealfall können die gleichen Automatisierungen für
unterschiedliche Instanzen oder gar verschiedene Cloud Provider
eingesetzt werden
Zusammenfassung Frank Müller