Déployer sur Kubernetes en Go

Les Voix De la Veille
8 min readMay 11, 2020

Voici quelques mois que 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, ça gère bien les exécutions concurrentes, c’est supporté par google, un nom simple à retenir, les implémentations sont moins sujettes aux erreurs et on peut générer un exécutable pour toutes les plateformes en une commande, et reprenez votre souffle…

La liste de ses 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 aussi peut-être 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 qui marmonnent “mais pourquoi il utilise pas des fichiers yaml de déploiement comme tout le monde et du helm..”, et bien parce que ça me plait bien de faire vraiment de l’infrastructure as code plutôt que du yaml et du templating de yaml.

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

go mod init

Ensuite ce que l’on souhaite c’est de 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.

Encore une petite chose avant de déployer, c’est de gérer la connexion au cluster Kubernetes. Il existe deux manières, soit vous avez une configuration dite in-cluster car supposons que vous ayez 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’avez pas à gérer l’authentification à votre cluster.

Exemple : gitlab kubernetes executors

Soit votre usine logicielle et ses exécuteurs sont en dehors de votre cluster, ou vous n’avez pas d’usine logicielle, ou bien comme 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)

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 :

Donc une fois que l’on a 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 :

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 on peut s’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 déjà en territoire connu donc…

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 :

Et là certains vont peut-être se dire que je vous parle que de Kubernetes, mais en fait 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. Et bien Cert-manager met aussi à disposition son client go avec ces mêmes mécaniques.

Même chose pour Istio pour faire du service mesh.

A tout hasard si vous souhaitez en savoir plus sur la génération de certificat avec istio et cert-manager, je vous renvoie vers cet excellent article.

Sinon si jamais vous avez un souci avec le client go d’une de vos API sur Kubernetes (anomalie ouverte sur un cas spécifique) ou bien 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.

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 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 on peut 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, quelques 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 leur algorithme 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é.

Les sources de l’application de démo de cet article sont disponibles ici :

L’auteur de ce post n’est autre qu’un passionné du cloud et des conteneurs :

--

--