Mit Kaniko Docker Images in Kubernetes bauen
Kaniko als Werkzeug zum Bauen von Docker Images wurde in diesem Artikel vorgestellt. In diesem Beitrag wird Kaniko eingesetzt, um eine Spring Boot Anwendung in Kubernetes zu bauen und anschließend zu deployen.
Um das Beispiel möglichst einfach nachvollziehbar zu machen, wird Minikube als Kubernetes Umgebung verwendet. Für eine Docker Registry in Kubernetes wird das Docker Registry Addon für Minikube verwendet.
Natürlich lässt sich das Beispiel auch auf andere Kubernetes Umgebungen und Container Image Registries übertragn.
Wer noch nicht mit Minikube gearbeitet hat, sollte sich kubectl und minikube installieren.
Die jeweiligen Webseiten:
Nachdem mit minikube start ein Kubernetes Cluster bereitgestellt wird, muss noch mit minikube addons enable registry die Container Registry aktiviert werden.
Ab jetzt können Images in der in Minikube bereitgestellten Registry abgelegt und auch von dort wieder abgerufen werden.
Der Container Daemon, z.B. Docker, erwartet standardmäßig eine TLS gesicherte Verbindung.
Um das Setup nicht unnötig komplex zu gestalten, wird die Registry unter localhost:5000 verfügbar gemacht - für localhost-Adressen erlaubt Docker auch einen ungesicherten HTTP Zugriff.
Wer produktives Kubernetes Setup hat, muss diese Schritte natürlich nicht vornehmen.
hostPort$ kubectl patch replicationController -n kube-system registry --type json -p='[{"op": "add", "path": "/spec/template/spec/containers/0/ports/0/hostPort", "value": 5000 }]'
replicationcontroller/registry patched
$ kubectl delete pods -n kube-system -l kubernetes.io/minikube-addons=registry
pod "registry-9j6hg" deleted
Kaniko und Spring Boot in Minikube
Als minimale Spring Boot Anwendung wird die unter https://github.com/trion-development/spring-boot-rest-sample verfügbare Spring Data REST Demoanwendung genutzt.
Im Branch dockerfile gibt es ein vorbereitetes Dockerfile, mit dem sich ein Docker Image erstellen lässt.
Die Schritte für einen Build mit Docker könnten so aussehen:
-
git-clone des Repository
-
Wechsel zum Branch
dockerfile -
Erzeugung des Java Artefakts als ausführbares JAR
-
Erzeugung des Docker Image mit
docker build
Um dieses Verhalten in Kubernetes abzubilden kann das Konzept eines Init-Container genutzt werden: Dieser wird ausgeführt, bevor der oder die Container des Pods gestartet werden.
Statt eines Pods kann ein Job verwendet werden.
Damit stellt Kubernetes sicher, dass auch im Falle eines Node-Ausfalls ein erfolgreicher Build durchgeführt wird, indem auf einer anderen Node ein entsprechender Pod erzeugt wird.
Die Job-Beschreibung im YAML-Format für Kubernetes könnte dann wie folgt aussehen:
kaniko-spring-boot.yml[]
Um den Job anzulegen, kann das Kubernetes Dashboard oder kubectl verwendet werden.
$ kubectl apply -f kaniko-spring-boot.yml
job.batch/kaniko created
Der Job führt die folgenden Schritte aus:
Nachdem mittels eines OpenJDK Container und dem Maven-Wrapper die Spring Boot Anwendung gebaut wurde, kommt der eigentliche Kaniko Container zum Einsatz, um ein Docker Image zu bauen und in die Registry zu pushen.
Dank Kubernetes können die beiden Schritte in der richtigen Reihenfolge abgearbeitet werden, ohne dazu einen Build-Server zur Hilfe zu nehmen. Natürlich fehlen hier noch weitere Eigenschaften, die durch einen Build-Server bereitgestellt würden, und Kubernetes Jobs sind somit kein vollwertiger Ersatz.
Die Ausgabe der beiden Container kann getrennt inspiziert werden. Zunächst der Build der Anwendung:
Cloning into '/workspace'...
Switched to a new branch 'dockerfile'
Branch dockerfile set up to track remote branch dockerfile from origin.
Downloading https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
...
[INFO] Scanning for projects...
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:18 min
[INFO] Finished at: 2018-11-04T16:22:45Z
[INFO] Final Memory: 57M/447M
[INFO] ------------------------------------------------------------------------
Danach startet Kubernetes den Kaniko Container, um das Container Image der Spring Boot Anwendung zu bauen:
INFO[0000] Removing ignored files from build context: [target/* !target/*jar .git]
INFO[0000] Downloading base image openjdk:8
2018/11/01 22:07:00 Unable to read "/kaniko/secrets/config.json": open /kaniko/secrets/config.json: no such file or directory
2018/11/01 22:07:00 No matching credentials were found, falling back on anonymous
INFO[0002] Executing 0 build triggers
INFO[0002] Extracting layer 0
INFO[0005] Extracting layer 1
INFO[0006] Extracting layer 2
INFO[0006] Extracting layer 3
INFO[0010] Extracting layer 4
INFO[0010] Extracting layer 5
INFO[0010] Extracting layer 6
INFO[0010] Extracting layer 7
INFO[0019] Extracting layer 8
INFO[0019] Taking snapshot of full filesystem...
INFO[0023] EXPOSE 8080
INFO[0023] cmd: EXPOSE
INFO[0023] Adding exposed port: 8080/tcp
INFO[0023] Using files from context: [/workspace/target/spring-boot-rest-sample-1.0.0.jar]
INFO[0023] COPY target/spring-boot-rest-sample-1.0.0.jar /opt/spring-boot-rest-sample/
INFO[0023] Taking snapshot of files...
INFO[0023] CMD ["java", "-jar", "/opt/spring-boot-rest-sample/spring-boot-rest-sample-1.0.0.jar"]
2018/11/01 22:07:24 Unable to read "/kaniko/secrets/config.json": open /kaniko/secrets/config.json: no such file or directory
2018/11/01 22:07:24 pushed blob sha256:a0a4af04ca48a95cbf4ebccb4198d1c8ce1c84b4b80477814ab27526175fec04
2018/11/01 22:07:24 pushed blob sha256:a7c0dad691e9a2190a036b437280130e383481f0e0d5e4516a0d14de690ee0eb
2018/11/01 22:07:24 pushed blob sha256:367a6a68b113cde265568261ba36f477c49ff982418a561fb85d9730b5aac9a3
2018/11/01 22:07:24 pushed blob sha256:496c2f11d11ccc585b3c2ae983ec7777bdf1c00c28e339f02bd2c23b074a47b8
2018/11/01 22:07:25 pushed blob sha256:c9d22bc4393511e7d49d7bb83b5b2dcc95cde66f54a69099421c65928e873e9c
2018/11/01 22:07:25 pushed blob sha256:a4c7ee7ef122c78bdcb4087b1245788dfee5b7d7d796b8310bc75e5d02c4c067
2018/11/01 22:07:26 pushed blob sha256:e5c3f8c317dc30af45021092a3d76f16ba7aa1ee5f18fec742c84d4960818580
2018/11/01 22:07:27 pushed blob sha256:193a6306c92af328dbd41bbbd3200a2c90802624cccfe5725223324428110d7f
2018/11/01 22:07:33 pushed blob sha256:bc9ab73e5b14b9fbd3687a4d8c1f1360533d6ee9ffc3f5ecc6630794b40257b7
2018/11/01 22:07:33 pushed blob sha256:a587a86c9dcb9df6584180042becf21e36ecd8b460a761711227b4b06889a005
2018/11/01 22:07:39 pushed blob sha256:60c0e52d1ec297a9ade35f6b17bcf69d959bb1f16c506290f29af5513b0cdabd
2018/11/01 22:07:39 registry.kube-system/kaniko-demo/boot:latest: digest: sha256:df91e9d1b2773aa8e71988b5eb904d7ceb901bafce56606b199eae62a73f2108 size: 1900
Die im Beispiel verwendete Registry erfordert keine Credentials für den Zugriff und verfügt auch nicht über TLS Absicherung.
Kaniko wird dazu mit den Schaltern --insecure --skip-tls-verify aufgerufen.
Die auskommentierten Bestandteile kämen zum Einsatz, um Kaniko erforderliche Credentials für den Zugriff auf das Image Repository in Form von einem Kubernetes Secret mitzugeben.
Deployment
Das Image steht nun breit, damit kann die Anwendung deployt werden.
Hier gibt es die Optionen, einen YAML-Descriptor zu erstellen, oder mittels kubectl run zu arbeiten.
Für das Beispiel bei Verwendung von Minikube ist die Registry zusätzlich unter localhost:5000 erreichbar.
Damit kein TLS Problem auftritt, wird von dort das Image bezogen und nicht von registry.kube-system/kaniko-demo/boot, was Kaniko als Push Repostiry verwendet hat.
$ kubectl create namespace demo
namespace/demo created
$ kubectl create deployment spring-app -n demo --image=localhost:5000/kaniko-demo/boot
deployment.apps/spring-app created
Damit die Anwendung verfügbar ist, muss noch ein Service angelegt werden.
Der Zugriff mit Minikube erfolgt dann über den Service mit NodePort Konfiguration.
$ kubectl expose deployment spring-app --type NodePort -n demo --port=80 --target-port=8080
service/spring-app exposed
$ minikube service spring-app -n demo --url
http://192.168.99.100:31705
$ curl $(minikube service spring-app -n demo --url)
{
"_links" : {
"people" : {
"href" : "http://192.168.99.100:31420/people{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://192.168.99.100:31420/profile"
}
}
}
Fazit
Build und Deployment von Images ist nativ in Kubernetes möglich. Dabei muss kein Docker als Container Engine zum Einsatz kommen - ganz im Gegenteil: Aus Sicherheitsüberlegungen bieten Werkzeuge wie Kaniko gerade den Vorteil, dass kein Zugriff auf einen Docker Daemon erforderlich ist.
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.