Orchestration

Met de komst van Docker is het gebruik van containers in een stroomversnelling geraakt. Bij het ontwikkelen en deployen van bijvoorbeeld een microservices platform is het inzetten van Docker een bijna automatische keuze geworden. De voor- en nadelen van Docker zijn in een eerder artikel reeds toegelicht. Wat lastig blijft, is de stap van het draaien van een aantal containers op een lokaal workstation naar een productie-klaar deployment, bijvoorbeeld in de cloud. Een Debian container opstarten en op de shell rondsnuffelen is gemakkelijk te doen, maar hoe kom je van “Hello World” naar een zelf-managende service, die automatisch schaalt? Orchestration is hiervoor de oplossing. In dit artikel leg ik uit hoe dat werkt.

Definitie

Bij navraag aan collega’s blijkt, dat iedereen een ander beeld heeft bij wat orchestration precies is. Hoort scheduling erbij? Is het onderdeel van Continuous Deployment, of vice versa? Is het een proces? Een applicatie? Welke verantwoordelijkheden heeft zo’n proces dan? Zo komen we niet verder. Dan maar naar Wikipedia:

“Orchestration describes the automated arrangement, coordination, and management of complex computer systems, middleware and services.” http://en.wikipedia.org/wiki/Orchestration_(computing)

Als je de definitie van orchestration volgens Wikipedia leest, dan zie je dat er behoorlijk veel onder deze noemer valt. Daar worden we niet veel wijzer van. De term wordt verder uitgelegd in het kader van cloud computing en er worden drie verantwoordelijkheden benoemd. Dit zijn:

  • Composing of architecture, tools and processes used by humans to deliver a defined Service.
  • Stitching of software and hardware components together to deliver a defined Service.
  • Connecting and Automating of work flows when applicable to deliver a defined Service.

Zo te zien slaat de definitie op Wikipedia eerder op Service Oriented Architecture (SOA) dan op microservices. En hoewel microservices veel hebben afgekeken bij SOA zijn er ook grote verschillen. Één daarvan is dat er van “microservices” geen vastomlijnde definitie bestaat en dat iedereen ze daarom op eigen wijze implementeert. Dit maakt dat de genoemde definitie van orchestration niet lijkt te passen in deze architectuur en dat we op zoek moeten naar een definitie, die wat meer hands-on is.

Voorbeeld

In een traditionele productieomgeving draait een handvol applicaties. Denk bijvoorbeeld aan Tomcat met daarop een Java-app, een database als Postgres en eventueel Lucene. Alles redundant uitgevoerd, maar al met al een overzichtelijk aantal processen. De servers waar deze processen op draaien, zijn ingericht om de specifieke processen te ondersteunen. Dus de databaseserver draait de database, de app-servers draaien Tomcat met de Java-applicatie, et cetera. Zo’n omgeving is goed te automatiseren, maar orchestration gaat verder dan dat.

Docker voegt als deployment-tool in een dergelijke omgeving niet veel toe. De applicaties draaien al op de servers, dus Docker is dan enkel een extra tussenlaag met weinig voordeel. Wat zouden dan redenen kunnen zijn om alsnog containers in te zetten? De hosts waar de processen op draaien, worden eenvormig. Het maakt de hosts niet meer uit welk proces er wordt gedraaid, omdat het netjes ingekapseld is in een container, inclusief dependencies, configuratiebestanden, et cetera. Als een container stopt, zijn ook alle gegevens weer weg. Dit maakt het werk van system engineers gemakkelijker, omdat er minder types servers te beheren zijn. Zo worden de system engineers vrijgespeeld om samen te gaan werken met de developers om zo tot een beter product te komen.

Eenvormigheid van servers opent de weg naar een systeemarchitectuur, waarbij het niet uitmaakt welk proces op welke server draait. Hier is uiteraard een aantal ondersteunende services voor noodzakelijk, zoals service discovery, opslag van secrets, Docker image repositories en automatisch te configureren proxyservers.

Microservices is een voorbeeld van zo’n systeemarchitectuur. De individuele Docker containers zijn in deze architectuurvorm doorgaans klein en stateless, waardoor het niet meer uitmaakt op welke fysieke server ze draaien. Het is dan belangrijker of de service zelf up is, in plaats van de processen, containers of zelfs hele servers. Monitoring verschuift van “draait dit proces op deze server” naar “wat is de responsetijd van de loginservice”. Sterker nog, op elk moment kunnen er honderden containers draaien, waardoor het niet meer mogelijk is om alles handmatig aan te sturen en bij te houden.

Neem bijvoorbeeld een OAuth service, die bestaat uit een front-end container, die de REST endpoints voor zijn rekening neemt en een keyserver waar de wachtwoorden en andere secrets zijn opgeslagen. Achter de schermen zal zo’n service bestaan uit meerdere images of processen. Stel dat er een aantal front-end containers draait en een enkele keycontainer. Hoeveel front-end containers moeten er draaien? Is een enkele voldoende? Mogen de containers op dezelfde fysieke host draaien? Mogen ze op dezelfde host als de keyserver draaien? De tool die deze keuzes maakt en de containers start, in de lucht houdt en op- en neerschaalt, dat is de orchestrator.

Nu dient het volgende probleem zich aan. Stel dat we drie virtuele servers hebben van gelijke grootte en dat we de keyserver en de front-end servers niet op dezelfde VM willen laten draaien. Dat betekent dat er twee VM’s over zijn voor het schalen van de FE service. Op welke server is plek voor een extra op te starten front-end service? Dit kunnen we bijhouden in een centrale database, onder de hoede van de orchestrator.

Resource managementsystemen, zoals Mesos en Kubernetes, maken het mogelijk dat we alle servers, zowel virtueel in de cloud als hardware on premise, als één enkele resource kunnen aanspreken. Een deel van de orchestration wordt daarmee uit handen genomen.

Kubernetes heeft versie 1.0 bereikt op 21 juli 2015, iets meer dan een jaar na de eerste commit op Github op 7 juni 2014. Docker bracht Swarm en Compose officieel uit in februari 2015, maar de tools werden al aangekondigd op DockerCon 2014 in Amsterdam en Fig (de voorloper van Compose) bestond al veel langer. Mesos werd al in 2009 aangekondigd en wordt door een aantal grote partijen gebruikt, zoals Twitter, Apple en AirBnB.

Compositie

Orchestration is alles dat nodig is om een service te composeren of samen te stellen uit verschillende onderdelen. Er zijn op dit moment geen gestandaardiseerde oplossingen, noch in proces, noch in software, om orchestration op te lossen. Er zijn wel een aantal kandidaten, zoals Spotify’s Helios, Apache Brooklyn of Ochopod van Autodesk. Docker, Inc heeft zelf de tool Compose uitgebracht. Met Ansible kan je ook een heel eind komen. Met Docker Compose kun je een multicontainer applicatie definiëren in een yaml-bestand en daarna alle containers tegelijk beheren.

Aan de hand van een voorbeeld in Kubernetes laat ik zien wat er wel en wat er (nog) niet kan.

Kubernetes

Enige achtergrondkennis over de concepten binnen Kubernetes zul je nodig hebben om te doorgronden wat er gebeurt. De volgende termen zijn belangrijk.

  • Pod: een logische groepering van containers. Denk aan een webserver met een database.
  • Replication controller: het proces dat ervoor zorgt dat het juiste aantal replica’s draait. Stel dat we definiëren dat er drie replica’s van een bepaalde container moeten zijn, maar er draaien er slechts twee. De replication controller zal dan een nieuwe starten.
  • Service: Pods hebben IP-adressen, maar kunnen door de replication controller gestopt en gestart worden. Een service zorgt ervoor dat er een vast IP-adres is om de containers te bereiken.

Voor de demo heb je de volgende tools nodig:

  • Docker
  • Docker Compose
  • Docker Machine (tenzij je Linux draait)
  • kubectl (kubernetes-cli)

Deze tools zijn allemaal beschikbaar in Homebrew op OS X.

 

De volgende code is ook beschikbaar in deze Github repository: github.com/ContainerSolutions/kubernetes-demo. Maak een bestand “docker-compose.yml” met de volgende inhoud:


etcd:
  image: gcr.io/google_containers/etcd:2.0.9
  net: host
  command: ['/usr/local/bin/etcd', '--bind-addr=0.0.0.0:4001', '--data-dir=/var/etcd/data']
 
apiserver:
  image: geku/hyperkube:v1.0.1
  net: host
  command: ["/hyperkube", "apiserver", "--service-cluster-ip-range=172.17.17.1/24", "--insecure-bind-address=0.0.0.0", "--address=127.0.0.1", "--etcd_servers=http://127.0.0.1:4001", "--cluster_name=kubernetes", "--v=2"]
 
controller:
  image: geku/hyperkube:v1.0.1
  net: host
  command: ["/hyperkube", "controller-manager", "--master=127.0.0.1:8080", "--v=2"]
 
scheduler:
  image: geku/hyperkube:v1.0.1
  net: host
  command: ["/hyperkube", "scheduler", "--master=127.0.0.1:8080", "--v=2"]
 
kubelet:
  image: geku/hyperkube:v1.0.1
  net: host
  command: ['/hyperkube', 'kubelet', '--api_servers=http://127.0.0.1:8080', '--v=2', '--address=0.0.0.0', '--enable_server']
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
 
proxy:
  image: geku/hyperkube:v1.0.1
  net: host
  command: ['/hyperkube', 'proxy', '--master=http://127.0.0.1:8080', '--v=2']
  privileged: true

 

Voer dan het volgende commando uit in de directory waar je het bestand hebt opgeslagen:


docker-compose up -d

 

Na een poosje is je single-node cluster klaar voor gebruik. Dit kun je verifiëren met docker-compose ps en docker-compose logs.

Als je Docker Machine gebruikt op OS X, dan kan het volgende commando van pas komen.


machine=<je docker machine naam>; ssh -i ~/.docker/machine/machines/$machine/id_rsa docker@$(docker-machine ip $machine) -NL 8080:localhost:8080

 

De kubectl applicatie verbindt standaard op localhost:8080, dat scheelt dus typen. Nu is het mogelijk om kubectl commando’s uit te gaan voeren. Maak een replication controller en start de eerste pod:


kubectl run kube-demo --image=containersol/kubernetes-demo --port=8080

 

Verifieer dat de replication controller is aangemaakt:


kubectl get replicationcontrollers

 

Controleer dat de pod is gestart:


kubectl get pods

 

Maak een service:


kubectl expose rc kube-demo --target-port=8080 --type=NodePort

 

De services worden getoond door:


kubectl get services

 

Zoek de door Kubernetes gekozen poort op:


kubectl get svc kube-demo -o yaml | grep nodePort

 

Via curl kan je nu de pods bezoeken:


curl -s http://<ip van je docker host>:<nodePort>

 

De waarde die wordt teruggegeven, is de naam van de pod, zoals door Kubernetes is toegekend.

Nu maken we het wat spannender door de pods op te schalen naar 3 replica’s:


kubectl scale rc kube-demo --replicas=3

 

Als je nu meerdere keren het curl commando uitvoert, dan zul je zien dat Kubernetes via de service netjes de load verspreid over de verschillende pods. De replication controller houdt in de gaten of er nog het correcte aantal pods voorhanden is. Dat kun je controleren door één van de pods te verwijderen:


kubectl delete pod kube-demo-<random pod naam>
kubectl get pods

 

De gegeven pod zal verdwenen zijn, maar er is direct een nieuwe pod aangemaakt door de replication controller.

Hiermee is een deel van de kracht van een resource managementsysteem aangetoond en het is ook duidelijk waar de gaten zitten. Met een set yaml-bestanden kun je aan Kubernetes uitleggen welke containers er nodig zijn om een bepaalde service samen te stellen. Hiermee is het compositie element gecoverd. Het zou mooi zijn om een standaard te hebben, maar daar is het nog te vroeg voor.

Er is echter nog geen systeem voor de service level agreements van de verschillende services en componenten. Zo’n soort systeem zou kunnen beschrijven dat een OAuth service bestaat uit een aantal front-end containers en een keyserver. Het zou kunnen beschrijven op basis van welke metingen de microservices moeten worden op- en neergeschaald.

Conclusie

Orchestration is een beschrijving van de componenten van een bepaald systeem of bepaalde service (compositie), de software nodig om van de continuous integration server naar productie te komen (stitching) en de scheduling noodzakelijk om de service volgens een bepaalde SLA in de lucht te houden (connecting and automating). Zo te zien snijdt de door Wikipedia genoemde definitie ook hout in de context van microservices.

Het is lastig om te schrijven over orchestration, omdat het landschap nog volop in beweging is. Er zijn veel verschillende partijen aan allerlei interessante oplossingen aan het werk, zonder dat er een duidelijke winnaar aangewezen kan worden. Dat maakt het echter ook zo interessant!