Utiliser des GITHUB Actions pour déployer dans Google Kubernetes Engine

A mes heures perdues, je travaille sur un « POC/side project qui n’aboutira pas et je m’en fiche » basé sur Quarkus. J’ ai choisi d’utiliser les langages et composants suivants :

Oui, tant qu’à faire, autant aller dans la hype …

Mon projet est sur GITHUB. Pour automatiser certaines actions et, disons-le, par fierté personnelle, j’ai choisi d’automatiser certaines actions par la mise en œuvre de pipelines CI/CD.
Depuis peu, GITHUB a intégré un mécanisme de pipeline : GITHUB Actions.

Ça permet, entre autres, de lancer des processus automatisé sur un push ou sur une action pour un commit GIT.

La force de l’outil est, selon moi, de facilement s’intégrer avec beaucoup de services du cloud ( sonarcloud, google cloud, heroku,…). On aime ou on n’aime pas, mais chez Microsoft, l’intégration ils savent faire.

Par exemple, si on veut lancer une compilation lors d’un push, on peut placer un fichier .github/workflows/build.xml avec le contenu :

name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11
      - name: Build with Gradle without testing
        run: ./gradlew build -x test

Coté GITHUB, vous verrez l’exécution sur un écran dédié

Vous pouvez créer autant de workflows que vous souhaitez (si votre projet est en libre accès).
Pour chaque workflow, on peut définir et utiliser des jobs. Les logs d’exécution sont disponibles dans ce même écran:

Worflows implémentés

J’ai choisi d’implémenter les workflows suivants:

  • CI: Build sur la feature branch
  • CD: Build sur master branch et déploiement

On obtient donc dans mon cas:

Ce n’est pas parfait. Loin de là. Dans la « vraie vie », pour une équipe de dev, je l’améliorerai sans doute par un build docker dans les features branches, une validation formelle et bloquante de l’analyse sonar, etc.
Pour un dev perso ça suffit largement. Le contenu de la branche master est compilé et une image docker est crée pour être déployée automatiquement dans GKE.

Analyse SONAR

J’ai choisi d’utiliser sonarcloud pour analyser mon code. C’est gratuit pour les projets opensource. L’analyse se fait simplement:

  sonarCloudTrigger:
    name: SonarCloud Trigger
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11
      - name: SonarCloud Scan
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: ./gradlew jacocoTestReport sonarqube

Dans ce job j’utilise deux secrets. Ce sont des tokens qui permettent de ne pas stocker en dur les données dans les repos GITHUB.

Création d’une image Docker et déploiement dans le registry GITHUB

Ici aussi, ça se fait simplement. La preuve :

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11
      - name: Build in JVM Mode with Gradle without testing
        run: ./gradlew quarkusBuild  [1]
      - name: Branch name
        run: echo running on branch ${GITHUB_REF##*/}
      - name: Build the Docker image Quarkus JVM
        run: docker build -f src/main/docker/Dockerfile.jvm -t docker.pkg.github.com/${GITHUB_REPOSITORY}/music-quote-jvm:latest .  [2]
      - name: Login against github docker repository
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: docker login -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN}  docker.pkg.github.com   [3]
      - name: Publish the Docker image Quarkus JVM
        run: docker push docker.pkg.github.com/${GITHUB_REPOSITORY}/music-quote-jvm:latest  [4]
  1. Création du binaire
  2. Création de l’image docker en utilisant la commande docker et le Dockerfile fourni par Quarkus
  3. Identification sur la registry Docker de GITHUB
  4. Déploiement de l’image

Pour plus de détails sur la variable GITHUB_TOKEN, vous pouvez lire cet article de la documentation.

Déploiement dans Google Kubernetes Engine

Mon application est pour l’instant architecturée comme suit (attention c’est compliqué):

Pour la déployer dans Google Kubernetes Engine, j’ai besoin d’ implémenter cette « architecture » par les objets Kubernetes suivants:

J’utilise les objets suivants:

  • Des services pour exposer la base de données ainsi que l’application
  • Un deployment pour l’application
  • Des pods car à un moment, il en faut…
  • Un statefulset pour la base de données

Vous pourrez trouver la définition de tous ces objets au format yaml via ce lien. J’ai fait très simple. Logiquement j’aurai du créer un volume pour les bases de données ou utiliser une base de données en mode PAAS.

Pour lancer le déploiement, il faut au préalable créer un secret ( fait manuellement pour ne pas stocker d’objet yaml dans le repository GITHUB) pour se connecter au repo GITHUB via la commande suivante:

kubectl create secret docker-registry github-registry --docker-server=docker.pkg.github.com --docker-username=USER--docker-password=PASSWORD --docker-email=EMAIL

On peut faire pareil pour les connexions base de données. J’ai mis dans un configmap pour ne pas trop me prendre la tête…

Après le déploiement via le pipeline se fait assez simplement:

      [...]
      - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
        with:
          version: '286.0.0'
          service_account_email: ${{ secrets.GKE_SA_EMAIL }}
          service_account_key: ${{ secrets.GKE_SA_KEY }}
          project_id: ${{ secrets.GKE_PROJECT }}
      # Get the GKE credentials so we can deploy to the cluster
      - run: |-
          gcloud container clusters get-credentials "${{ secrets.GKE_CLUSTER }}" --zone "${{ secrets.GKE_ZONE }}"
      # Deploy the Docker image to the GKE cluster
      - name: Deploy
        run: |-
          kubectl apply -f ./k8s     

J’utilise les « actions » fournies par Google.

Conclusion

Pour que ça marche il y a pas mal d’étapes préalables ( des tokens à générer, un utilisateur technique, …).
J’ai essayé de les référencer dans le README du projet.
Si vous voulez tester l’intégration Kubernetes dans le cloud google, sachez que vous pouvez disposer d’un crédit de 300€ valable un an. Attention, avec ce genre d’architecture, ça part vite…

Passer votre application Java8 en Java11

Java 8 est encore largement utilisé dans les entreprises aujourd’hui. Il y a même certains frameworks qui n’ont pas encore sauté le pas.
Je vais essayer d’exposer dans cette article les étapes à réaliser pour migrer (simplement) votre application JAVA8 en JAVA 11.

Dans cet article, je prendrai comme postulat que l’application se construit avec Maven.

Pré-requis

Tout d’abord vérifiez votre environnement d’exécution cible! Faites un tour du coté de la documentation et regardez le support de JAVA.

Si vous utilisez des FRAMEWORKS qui utilisent des FAT JARS, faites de même (ex. pour spring boot, utilisez au moins la version 2.1.X).

Ensuite, vous aurez sans doute à mettre à jour maven ou gradle. Préférez les dernières versions.

Configuration maven

Les trois plugins à mettre à jour obligatoirement sont :

Maven compiler plugin

<plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>11</release>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>

maven surefire / failsafe plugin

Pour ces deux plugins, ajouter la configuration suivante:

 <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
        <configuration>
        [...]
          <argLine>--illegal-access=permit</argLine>
          [...]
        </configuration>
      </plugin>

Mise à jour des librairies

Bon,la il n’y a pas de magie. Vous devez mettre à jour toutes vos librairies. Mis à part si vous utilisez des librairies exotiques, la plupart supportent JAVA 11 maintenant.

C’est une bonne opportunité de faire le ménage dans vos fichiers pom.xml 🙂

APIS supprimées du JDK

Si vous faites du XML, SOAP ou que vous utilisiez l’API activation, vous devez désormais embarquer ces librairies. Le JDK ne les inclut plus par défaut.

Par exemple:

 <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

Modularisation avec JIGSAW

Bon là … je vous déconseille de partir directement sur la modularisation, surtout si vous migrez une application existante. Bien que la modularité puisse aider à réduire vos images docker en construisant vos propres JRE et d’améliorer la sécurité, elle apporte son lot de complexité.
Bref pour la majorité des applications, je vous déconseille de l’intégrer.

Conclusion

Avec toutes ces manipulations, vous devriez pouvoir porter vos applications sur JAVA11. Il y aura sans doute quelques bugs. Personnellement, j’en ai eu avec CGLIB vs Spring AOP sur une classe instrumentée avec un constructeur privé. Sur ce coup j’ai contourné ce problème ( je vous laisse deviner comment 🙂 ).