Kubernetes Deployment mit CI Server
In dem Beitrag zu Kubernetes Continuous Integration wurde am Beispiel von Drone CI gezeigt, wie Builds in einem Kubernetes Cluster umgesetzt werden können. Nun ist es oft der Wunsch von Kunden wie Entwicklern, möglichst schnell auf den aktuellen Stand auch zugreifen zu können. Dies Verfahren wird auch Continuous Deployment genannt, da stets der aktuelle Entwicklungsstand zur Nutzung bereit steht.
Dieser Beitrag zeigt daher auf, wie mit Kubernetes und Drone ein solches Deployment umgesetzt werden könnte. Damit das Setup nicht zu komplex wird, wird lediglich der aktuelle master-Branch in Kubernetes deployt.
Zunächst einmal muss der Ansatz von Kubernetes gelobt werden, eine API bereitzustellen. Damit ist ein Deployment natürlich eine relativ einfach zu gestaltende Aufgabe:
-
Zugriff auf die API ermöglichen
-
Bei erfolgreichem Build das Deployment beauftragen
-
Kubernetes erledigt den Rest
Kubernetes API Zugriff
Eine API ermöglicht allgemein Funktionalität bereitzustellen, so dass diese auch in einem erweiterten oder neuen Kontext verwendet werden kann. Schon früher war unter dem Stichwort Automatisierung eine Schnittstelle bei vielen Produkten vorhanden, die eine Integration in Werkzeuge ermöglicht hat. Oft kam so eine Schnittstelle in Form von Kommandozeilenwerkzeugen (CLI), manchmal aber auch als Library oder Remote-Schnittstelle daher.
Kubernetes bietet ebenfalls eine Remote Schnittstelle als HTTP API und durch das Kommandozeilenwerkzeug kubectl
auch eine durch Nutzer und Programme leicht nutzbare Implementierung.
Mit der Kubernetes API können alle Mechanismen von Kubernetes angesteuert werden, somit auch Deployments.
Damit der Zugriff abgesichert ist, sieht Kubernetes Tokens vor, mit denen bei einem API Zugriff eine Authentifizierung stattfindet.
Die Verwaltung der Tokens erfolgt durch Secret Objkete, die wiederum einem Service Account zugeordnet sind.
Ein solcher Service Account muss nun also erzeugt werden, um aus dem Build heraus auf die Kubernetes API zuzugreifen.
Der Service Account kann entweder durch kubectl
oder ein passendes Manifest erzeugt werden.
kubectl
für Deployment$ kubectl create serviceaccount drone-deploy
serviceaccount/drone-deploy created
Anschließend kann man den Service Account und das zugehörige Secret inspizieren.
$ kubectl -n default get serviceaccount
NAME SECRETS AGE
dashboard 1 206d
default 1 208d
drone-deploy 1 56s
$ kubectl -n default get secrets
NAME TYPE DATA AGE
default-token-bgbws kubernetes.io/service-account-token 3 208d
drone-deploy-token-j2pvc kubernetes.io/service-account-token 3 68s
Der tatsächliche Tokenwert wird durch Kubernetes Controller im Hintergrund erzeugt. Damit das Token verwendet werden kann, muss es anschließend abgerufen werden. Da der Zugriff auf die API durch TLS gesichert erfolgt, sollte auch das verwendete CA Zertifikat abgerufen werden, um so sicherzustellen, dass mit dem legitimen Endpunkt kommuniziert wird.
$ kubectl -n default get secret drone-deploy-token-j2pvc -o yaml | egrep 'ca.crt:|token:'
ca.crt: LS0tLS....o=
token: ZXlKaG3....dw==
Nun müssen noch die erforderlichen Berechtigungen vergeben werden, damit dieser Service Account Änderungen am Kubernetes Zustand durchführen darf.
Konkret sollen lediglich Deployments erzeugt oder geändert werden.
Passende Services, LoadBalancer oder Ingress Konfigurationen werden als passend vorhanden vorausgesetzt.
Die Rechte werden einer Rolle gewährt, die Rolle muss dann noch dem Service Account über ein Role-Binding zugeordnet werden.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: drone-deploy
namespace: default
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["create","get","list","patch","update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: drone-deploy
namespace: default
subjects:
- kind: ServiceAccount
name: drone-deploy
namespace: default
roleRef:
kind: Role
name: drone-deploy
apiGroup: rbac.authorization.k8s.io
Als letztes wird jetzt nur noch der API Endpunkt benötigt, über den die Kubernetes API angesprochen werden soll.
Der API Endpunkt von Kubernetes ist standardmäßig als Service mit dem Namen kubernetes
im Namespace default
angelegt.
Daher kann aus Kubernetes heraus auf https://kubernetes.default
zugegriffen werden.
Mit diesen Daten kann nun der Buildserver konfiguriert werden.
Secrets in Drone
Es gibt unterschiedliche Wege, um einem Dienst die Verwendung eines Service Accounts zu ermöglichen. Oft wird das Token als Volume Mount den jeweiligen Pods bzw. Containern zur Verfügung gestellt. Wird der Build außerhalb des Kubernetes Clusters durchgeführt, müssen die Daten entsprechend extern konfiguriert werden.
Der Drone Buildserver bietet dafür ebenfalls ein Konzept an, dass Secrets genannt wird. Werte aus den Secrets werden im Build als Umgebungsvariablen zur verfügung gestellt.
Je nach verwendetem Plugin muss noch darauf geachtet werden, ob die Werte, wie von Kubernetes herausgegeben, als Base64-encoded String verwendet werden sollen, oder ob diese vorher noch umgewandelt werden müssen. Auch der Name der bereitzustellenden Secrets kann je nach verwendetem Plugin unterschiedlich sein.
Die Build Secrets in Drone können über das Drone CLI oder die Weboberfläche verwaltet werden. Bei Verwendung der Drone Oberfläche erfolgt dies über die Einstellungen des jeweiligen Projekts.
Pipeline mit Deployment
In der Drone Pipeline wird zum Deployment in Kubernetes das Drone Plugin quay.io/hectorqin/drone-kubectl
verwendet.
Das Plugin ist schlicht gehalten und verwendet ein Shell-Script um kubectl
mit den entsprechenden Parametern aufzurufen.
Die folgenden Secrets müssen konfiguriert werden:
- KUBERNETES_CERT
-
CA Zertifikat für TLS Absicherung der Kubernetes API (Base64 encoded)
- KUBERNETES_TOKEN
-
Das Token zur Authentifizierung (Wert ohne Encoding)
Da das Plugin für das Token einen Wert ohne Base64 Encoding erwartet, muss entweder durch ein passendes Programm das Encoding entfernt werden, oder es wird das Token ohne Encoding bei Kubernetes abgerufen.
$ kubectl describe secret drone-deploy | grep 'token:'
token: eyJhbG....
Nun kann eine passende Pipeline zur Verwendung in Drone erzeugt werden.
Continous Deployment in Kubernetes mit Drone Pipeline
Zur Demonstration wird die Pipeline aus dem Kubernetes Continuous Integration Artikel erweitert: Es fehlt lediglich der Deployment Schritt, da die Pipeline bereits das passende Container Image baut und es in der Container Registry zur Verfügung stellt.
---
- name: deploy
image: quay.io/hectorqin/drone-kubectl # (1)
environment:
#PLUGIN_DEBUG: true
settings:
kubernetes_template: drone-deploy.yaml # (2)
kubernetes_namespace: default # (3)
kubernetes_server: https://kubernetes.default # (4)
kubernetes_cert:
from_secret: kubernetes_cert # (5)
kubernetes_token:
from_secret: kubernetes_token # (6)
-
Zu verwendendes Drone Plugin Image für diesen Schritt
-
Deployment Template für das durchzuführende Deployment in Kubernetes
-
Zu verwendender Namespace
-
Endpunkt des Kubernetes API Servers
-
CA Zertifikat für API Server
-
Token, mit dem die Authentifizierung gegenüber Kubernetes erfolgt
Die Pipeline ist nun vollständig und kann ausgeführt werden, sobald ein Deployment Template angelegt wurde.
Als Ausgabe liefert der Deployment Schritt Informationen zur API Version des API Servers, des verwendeten kubectl
und am Schluss zum Aktualisierten Deployment.
Applying to https://kubernetes.default:
User "default" set.
Cluster "default" set.
Context "default" created.
Switched to context "default".
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.3", GitCommit:"721bfa751924da8d1680787490c54b9179b1fed0", GitTreeState:"clean", BuildDate:"2019-02-01T20:08:12Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/arm64"}
Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.2", GitCommit:"cff46ab41ff0bb44d8584413b598ad8360ec1def", GitTreeState:"clean", BuildDate:"2019-01-10T23:28:14Z", GoVersion:"go1.11.4", Compiler:"gc", Platform:"linux/arm64"}
deployment.apps/k8s-visualizer configured
Deployment Template
Damit ein Deployment angelegt oder aktualisiert werden kann, wird ein Deployment Template benötigt. Es handelt sich deswegen um ein Template, da einige Werte durch Platzhalter variabel gestaltet sind, damit diese durch Werte aus einem konkreten Build gefüllt werden können.
Das Template ist im Grund ein reguläres Kubernetes Deployment Objekt. Kubernetes bietet Deployments mit verschiedenen Rollout-Strategien an, in diesem Fall wird ein RollingUpdate gewählt, wodurch ein für Nutzer unterbrechungsfreies Depoyment realisiert wird.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-visualizer
namespace: default
labels:
name: k8s-visualizer
app: k8s-visualizer
spec:
replicas: 2
minReadySeconds: 1
revisionHistoryLimit: 2
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app: k8s-visualizer
name: k8s-visualizer
template:
metadata:
labels:
app: k8s-visualizer
name: k8s-visualizer
ref: ${DRONE_COMMIT_SHA:0:8}
build-time: $(date -d @$DRONE_BUILD_STARTED '+%Y-%m-%d--%H.%M.%S')
spec:
containers:
- name: k8s-visualizer
image: registry.internal/trion/k8s-visualizer:drone
imagePullPolicy: Always
ports:
- containerPort: 80
Da das Template durch ein Shellscript ausgewertet wird, können sogar Shellfunktionen verwendet werden, um Variablenwerte zu bestimmen.
Im Beispiel wird ein Label build-time
vergeben, in dem das aktuelle Datum passend formatiert eingefügt wird.
Statt eines festen Tags könnte auch hier eine Drone Variable, wie z.B. die Buildnummer, verwendet werden.
Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:
Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.