Eerder deze maand was de Docker community in de ban van CVE-2019-5736. Kort gezegd zat er een kwetsbaarheid in Runc. Hierdoor kan een container waarbinnen het proces als root draait, ook root verkrijgen buiten de container. Foute boel dus! Natuurlijk is het best practice om je containers als “mortal” user te draaien. ik verwijs hiervoor graag naar een eerder artikel: https://denit.nl/media/technische-blogs/images-repositories-en-veiligheid/ waarin ik hier kort wat over vertel. Patch je cluster tijdig en voorkom narigheid! In dit artikel vertel ik hoe, wat en wanneer ik de clusters update/patch/upgrade.
Patches
Kubernetes clusters kunnen op verschillende fronten gepatched worden. Het is belangrijk om dit onderscheid te begrijpen.
Docker package
In het voorbeeld van Runc moet Docker of Runc (in het val Runc niet gebundeld is in het Docker package) gepatched worden. Het gaat hier om een kwetsbaarheid dus er komt wat spoed bij kijken. Om de impact zo klein mogelijk te houden en de patch toch uit te voeren kijk ik voor de volledigheid in de Kubernetes release-notes om te zien welke Docker versie ondersteund wordt. Kijk op https://github.com/kubernetes/kubernetes/releases bij de changelog van de bij jou actieve Kubernetes major release. Kijk welke docker versie geïnstalleerd is . Zoek vervolgens in de Docker release notes welke patch je nodig hebt. In mijn geval gebruik ik Docker-ce (waarin Runc gebundeld aanwezig is). De release notes zijn te vinden op https://docs.docker.com/engine/release-notes/ . De installatie van de patch deed ik via Ansible 1 voor 1 op alle cluster nodes . Het onderliggende commando daarvoor was:
“apt-mark unhold docker-ce && apt-get update && apt-get install docker-ce=18.06.2~ce~3-0~ubuntu && apt-mark hold docker-ce“
Kubernetes control-plane
Dit onderdeel bestaat uit eigenlijk uit 2 delen. Afhankelijk van hoe je cluster is opgezet kan dit wat verschillen. In het geval van Kubeadm bestaat de “control plane” deels uit packages en deels uit containers (static pod manifest op de master nodes). Het is belangrijk om de packages van de Kubernetes versie zo gelijk mogelijk te houden tussen beide onderdelen. patch je de ene, patch dan ook de ander.
Sinds de patch een minor update betreft van v1.13.2 naar v1.13.3 bestaat het patchen van de packages uit het volgende commando:
“apt-mark unhold kubelet kubectl kubeadm && apt-get update && apt-get install kubelet= 1.13.3-00 kubectl=1.13.3-00 kubeadm=1.13.3-00 && apt-mark hold kubelet kubectl kubeadm“
Op de master kan daarna het volgende commando uitgevoerd worden:
“kubeadm upgrade apply v1.13.3“
Systeem: updates / upgrades
Naast de specifieke Docker/Kubernetes software moet de systeemsoftware op de machines ook geupdate worden. Zelf zie ik dit graag los van de Docker/Kubernetes software. ik zorg er dan ook voor dat die packages gemarkeerd zijn met “hold” zodat ze bij standaard systeemupdates niet veranderen. Bij afgesproken momenten kan dan in het geval van Ubuntu het volgende commando gebruikt worden om te updates te installeren:
“apt-get update && apt-get -y dist-upgrade“
Release upgrades
Naast patches en updates binnen de hoofdreleases is er ook nog de mogelijkheid om naar een volgende release te upgraden. Het is verstandig om dit uitvoerig te testen voordat het in productie uitgevoerd wordt. Uiteindelijk kan het je grote voordelen bieden.
De Kubernetes controlplane
Het scenario waarin je de Kubernetes “control plane” upgrade naar een nieuwe hoofdrelease wijkt niet zo veel af in de uitvoering van het eerder beschreven stuk over het updaten van de “control plane”. het grote verschil is dat er met een upgrade functionele verschillen kunnen zijn! Alpha features of “kinds” in de API kunnen verdwijnen of worden beta. Daarnaast is bijvoorbeeld Kube-DNS ook een tijdje terug vervangen door Core-DNS. Lees de release notes en geef de gebruiker van het cluster de tijd om dat ook te doen.
Het besturingsysteem
Voordat ik containers gebruikte ging een migratie naar een nieuwere linuxversie niet altijd even soepel. Software versies wijken af en de applicatie is niet per definitie compatible met deze wijzigingen. Doordat containers hetzelfde blijven behoort dit gelukkig tot het verleden. De upgrade van Ubuntu 16.04 naar 18.04 gaat eenvoudig:
- kubectl drain nodenaam –ignore-daemonsets
- Deze upgrade werkt niet als packages met hold gemarkeerd zijn. Maak de repository even inactief en zorg er voor dat met “apt-mark unhold packagenamen ” de “hold” markering verdwijnt. de Hiermee voorkom je ook dat de versies wijzigen. Draai daarna “apt-get update”
- voer het commando “do-release-upgrade” en herstart de machine uiteindelijk naar de nieuwe versie.
- Maak de repositories weer actief en voeg de “hold” markering weer toe.
- Controleer of de machine zich weer goed aanmeldt bij het cluster en zorg er voor dat met “kubectl uncordon nodenaam” de node weer actief mee doet in het cluster.
De enige downtime komt door de momenten dat met een drain commando de software op een andere clusternode opgestart wordt. Zonder problemen was het gehele cluster bijgewerkt. In het geval je een enkele master hebt, zal interactie met de master op dat moment even niet mogelijk zijn. Overige workloads blijven los van het herstarten van de applicaties op een andere node gewoon continu bereikbaar!
Verstoring van de software in het cluster
Met de update van Docker, Runc of iets anders moet Docker of de gehele node misschien herstart worden. Dit kan uiteraard invloed hebben op de bereikbaarheid van applicaties die binnen het cluster leven. Naast dat het verstandig is om de nodes 1 voor 1 af te gaan, is het ook handig om binnen Kubernetes readiness en liveness probes te gebruiken om de bereikbaarheid van pods te controleren.
Meer specifieke informatie over “readiness en liveness probes” kan je vinden op https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
Daarnaast kan je er voor kiezen om de node van te voren te “drainen” met het commando “kubectl drain node nodenaam –ignore-daemonsets” en achteraf “kubectl cordon servernaam”.
Is het allemaal wenselijk om te doen? Dat verschilt natuurlijk per situatie. Standaardiseer het proces! Het is fijn als dezelfde recepten gebruikt worden op zowel een test- als productie cluster! Daarnaast moet je als gebruiker van het cluster proberen je software zo inrichten dat falende hardware of reboots geen vervelende onbereikbaarheid veroorzaken.
tot slot
De tijd dat je “blind” updates kan installeren en gewoon de nieuwste versie van een package uit de repository haalt omdat “het toch wel goed gaat” ligt achter je. Voor deze clusters is een werkende combinatie van software van verschillende bronnen nodig. Release notes zijn belangrijk en moeten goed gelezen worden. Daarnaast is het niet te doen om een groter wordend cluster handmatig te updaten. Denk na over de automatisering van dit soort trajecten. Ansible kan je hier bij helpen maar mogelijk zijn er met andere linuxversies ook andere middelen die beter aansluiten. Met een groeiend aantal nodes in een cluster is een handmatige actie in ieder geval onwenselijk.
Daarnaast heb je ook nog de analogie “Pets vs Cattle”. De beschrijving in dit artikel nijgt ondanks de hint om het te automatiseren meer naar “Pets”. In bepaalde clouds kan het misschien gebruikelijker zijn om een nieuw / gepatched image te “spawnen” inplaats van dat je updates installeert. Zorg er in ieder geval voor dat je tijdig je software bijwerkt en dat dit op een schaalbare manier gebeurt.
Auteur: Geurt Hakfoort – Cloud Specialist Denit