Déployer sur Kubernetes en Go

11 mai 2020

Depuis quelques mois, je développe une solution de déploiement pour mon client en Golang. Pourquoi en Go ? Car c’est léger, rapide, facile à appréhender, que cela gère bien les exécutions concurrentes, que c’est supporté par Google (un nom simple à retenir), que les implémentations sont moins sujettes aux erreurs et afin parce qu'il est possible de générer un exécutable pour toutes les plateformes en une commande.

La liste de ces qualités pourrait encore s’agrandir, mais bon, ce serait vous gâcher le plaisir d’en lire d’autres dans la suite de l’article.

Je vais donc vous montrer comment embarquer dans une application en Go, tout ce qui est nécessaire pour la déployer sur un cluster Kubernetes. A la fin de cet article, vous devriez comprendre un peu mieux pourquoi l’écosystème autour de Kubernetes est si florissant et vous aurez, peut-être, aussi envie de vous lancer dans l’aventure Go.

La première chose à faire : c’est de s’équiper correctement pour l’IDE. IntelliJ fait bien le taff en version ultimate, il existe aussi la version spéciale Go : Goland.

Pour le reste, il vous faudra principalement :

- Kubectl

- Kubens et Kubectx pour un peu plus de confort

- Go installé, autant partir sur la dernière : 1.14

Puisque l’on va déployer sur Kubernetes, il est également utile d’avoir le plugin Kubernetes supporté par JetBrains. Il va vous permettre de consulter très facilement les différents objets K8s ainsi que les logs. Il se base sur votre fichier de configuration kubectl pour faire la connexion. En passant, si vous avez des soucis d’accès “Forbidden” avec ce plugin, cela peut venir des exécutables que vous utilisez dans la commande de votre fichier de configuration. Mettre les chemins en absolu peut résoudre votre souci (par exemple : si vous utilisez iam-authentificateur pour un cluster hébergé sur AWS).

J’entends déjà, de loin, certains marmonner : “mais pourquoi n'utilise-t-il pas des fichiers yaml de déploiement comme tout le monde et du helm...” ? Et bien, parce que cela me plait de faire vraiment de l’infrastructure as code plutôt que du yaml et du templating de yaml.

Une bonne pratique aussi pour commencer est d’utiliser le gestionnaire de dépendances inclus avec Go, qui est d'ailleurs production ready depuis la 1.14 :

go mod init

Ensuite, ce que l’on souhaite, c’est profiter du fait qu’un exécutable Go est autonome. Nous allons donc inclure les commandes de déploiement à notre application Go. Cela passe par le dossier cmd (cf. organisation des dossiers en go) :

Pour faire dans l’originalité, notre application va afficher un Hello World dans une webapp. Pour cela, je me suis servi d’une dépendance Go, appelée gofiber :

go get github.com/gofiber/fiber

Cela va mettre à jour automatiquement notre go.mod :

Avant de s’attaquer au déploiement, il faut tout de même mettre notre application sous forme de conteneur.

Puisque le binaire Go est indépendant, autant partir sur une image docker distroless :

# Start by building the application.FROM golang:1.14-buster as buildWORKDIR /go/src/appADD . /go/src/appRUN go get -d -v ./...WORKDIR /go/src/app/cmd/gkbmRUN go testRUN GOOS=linux GOARCH=amd64 go build -v -ldflags="-w -s" -o /go/bin/app# Now copy it into our base image.FROM gcr.io/distroless/baseCOPY --from=build /go/bin/app /CMD ["/gkbm"]

Et notre image docker avoisine les 11 mo.

Une dernière chose avant de déployer, n'oubliez pas de gérer la connexion au cluster Kubernetes. Il existe deux manières de faire :

1) Vous avez une configuration dite "in-cluster", car nous supposons que vous avez un exécuteur de votre usine logicielle qui se déploie au sein même de votre cluster Kubernetes. C’est la manière la plus simple car vous n’aurez pas à gérer l’authentification à votre cluster.

2) Votre usine logicielle et ses exécuteurs sont en dehors de votre cluster, ou, vous n’avez pas d’usine logicielle, ou bien, comme c'est le cas chez mon client, vous développez l’usine logicielle (l’usine de déploiement en l’occurrence). Et dans ces cas là, il faut implémenter la couche d’authentification liée à votre cluster.

Le client Go Kubernetes met à disposition certains des plugins d’authentification :

- azure

- gcp

- openstack

- oidc

- exec (pour lancer un binaire qui gère l’authentification à votre cluster)

Voici également quelques précisions pour le cluster Kubernetes managé par AWS (EKS). Il faut s’appuyer sur aws-iam-authenticator qui va générer un token si vous avez les droits nécessaires sur la session AWS que vous avez pour accéder au cluster EKS cible :

Pour cela, vous pouvez passer par le plugin Exec listé au dessus et installer aws-iam-authenticator au préalable. Ou bien, implémenter vous-même une méthode d’authentification car aws-iam-authenticator est écrit en Go et on peut importer le package token.

Pour un cluster sans surcouche d’authentification spécifique (vanilla), il suffit de s’appuyer sur le kubeconfig comme expliqué dans l’exemple du client go kubernetes. Cette manière est bien entendue beaucoup plus simple.

Voici les différentes méthodes côte-à-côte pour comparer : https://lesvoixdelaveille.medium.com/media/7aa0fc33d13eec64b2354e4a1800b584

Donc, une fois que nous avons nos différentes méthodes d’authentification au cluster, il reste à étoffer un peu notre client de déploiement. Il est aisé de rajouter des sous-commandes et des comportements en fonction grâce au package flag : https://lesvoixdelaveille.medium.com/media/24e9cc118230e3a6e44b7f7894776339

Oui, je sais, le Golang c’est verbeux. Mais le côté positif, c’est que c’est très lisible car cela reste simple.

Enfin, nous pouvons nous attaquer à notre déploiement. Pour cela, le client kubernetes met à disposition les mêmes structures Go qu’utilisé par Kubernetes lui-même, puisque Kubernetes est écrit en Go. Si vous êtes à l’aise avec l’API de Kubernetes, vous serez donc déjà en territoire connu : https://lesvoixdelaveille.medium.com/media/ec4584237125c301893aa1acc1fcf568

Premièrement, ce qui ressort c’est la simplicité d’utiliser les méthodes du client-go. Vous allez retrouver chaque interface pour chaque “api group” à partir de l’objet clientSet (que l’on définit dans le main pour ceux qui ont suivi). Chaque API est structurée de la même manière avec une méthode Get, Create, Update, Delete, Patch, Watch.

Deuxièmement, après la découverte, vient la satisfaction d’écrire facilement ses tests car chaque interface possède son mock / stub. Dès lors, on atteint rapidement les 100% de couverture pour son code. Le client Kubernetes propose aussi la partie injection de mock en avance avec la méthode PrependReactor : https://lesvoixdelaveille.medium.com/media/4cb498eb094343ea50613ce5efc3ae80

Et là, certains vont peut-être se dire que je ne vous parle que de Kubernetes. Mais, en réalité, c’est exactement la même chose pour d’autres API mises à disposition à travers Kubernetes. Donc, on s’y retrouve très vite et on a les mêmes méthodes pour implémenter ses tests et méthodes de déploiement. C’est génial !

Par exemple, si vous voulez avoir une génération de certificats à la volée et un renouvellement automatique avec Cert-manager / Lets-Encrypt, Cert-manager met aussi à disposition son client go avec ces mêmes mécaniques. Même chose pour Istio pour faire du service mesh. Si, par hasard, vous souhaitez en savoir plus sur la génération de certificat avec istio et cert-manager, je vous renvoie vers cet excellent article.

Egalement, si jamais vous avez un souci avec le client Go de l’une de vos API sur Kubernetes (anomalie ouverte sur un cas spécifique), ou si vous souhaitez vous passer du client go (par ex celui de cert-manager) pour alléger votre appli, vous le pouvez grâce au client dynamique de Kubernetes. Il va permettre d’envoyer sans structure définie des objets Kubernetes vers votre cluster. Cela a pour avantage et désavantage de n’effectuer aucune validation avant envoi : https://lesvoixdelaveille.medium.com/media/ea429929782d57d0ca3f5d73cdfe6736

Ce client m’as sauvé la mise sur certains updates de clients Go, soit sur un souci de dépendance commune qui n’était pas facilement gérable avec go.mod soit car le client embarquait une anomalie sur une de leur ressources. Plus d’informations à ce sujet sur le readme dédié coté client kube.

Quand on voit la facilité avec laquelle il est possible de produire du code autour de Kubernetes, on peut comprendre que chaque jour naisse un nouvel outil. Néanmoins, cela ne s’applique qu’à la partie cliente. Un tel niveau d’abstraction et d’aisance n’est pas encore tout à fait atteint pour l’installation de nouvelles API sur Kubernetes. C’est pour cela que Helm a encore de beaux jours devant lui. Même si certains commencent à s’en détacher, comme Istio qui a préféré développer son propre cli d’installation grâce aux ressources de type Operator de Kubernetes. Il y a toujours eu du code ou du scripting derrière les infras, mais on sent qu’on pousse toujours vers une implémentation plus accessible et déportée vers des profils DevOps qu’auparavant.

Pour conclure, voici les principaux points que j’ai retenu de cet apprentissage du go et du déploiement sur Kubernetes :

- L’auto-complétion des structures go marche vraiment bien.Plus d’excuses pour partir en pause le temps de la compilation car c’est bien trop rapide. Il en va de même pour le runtime.

- Go module s’en sort admirablement bien en gestionnaire de packages, même lorsque l’on tombe sur des problèmes de dépendances en losange.

- J’ai pu m’appuyer sur la communauté très présente de Kubernetes et Go, notamment sur leurs Slack respectifs.

- Les structures et méthodes Kubernetes en Go sont très bien documentées, même pour leurs algorithmes un peu plus complexes (un exemple parmi tant d’autres).

- Le langage est super accessible et fiable. J’ai commencé en 1.11 (sans rien connaître au Go) et je n’ai pas eu de grosses surprises jusqu’à maintenant (1.14).

On sent nettement une qualité du code supérieure lorsque Google participe au développement des projets open-source. J’ai eu quelques déconvenues concernant certains clients Go d’autres éditeurs de logiciels du monde de l’infrastructure as code, notamment sur la gestion des dépendances et la rétro-compatibilité.

Antoine Choimet., Cloud architect

Sources de l’application de démo : achoimet/gkbmGo Kube by myself. Contribute to achoimet/gkbm development by creating an account on GitHub.github.comDocker Hubhub.docker.com

Valeuriad
Par Valeuriad
11 mai 2020
Nos derniers articles