L’objectif de cet article est de donner les étapes de mise en place d’une Application dans notre Usine.

Construction & Déploiement des Applications

Les sources de nos projets sont gérés dans l’organisation GitHub SofteamOuest.

Notre Jenkins se synchronise automatiquement avec cette organisation.

  • Dès qu’un dépôt est créé sur GitHub, Jenkins crée un Job pour construire et déployer l’Application (définition du Job basée sur le Jenkinsfile s’il existe)

Le Job jenkins :

  • Analyse la qualité du code de l’application (via sonarqube)
  • Construit les images Docker de l’Application
  • Déploie les images Docker sur nexus

La fin d’exécution du Job Jenkins déclenche le déploiement de l’Application (par exécution du Job chart-run) sur le cluster.

Mise en place du déploiement

Pour mettre en place le déploiement sur le cluster, il faut :

  • Intégrer au Projet, un Dockerfile pour construire l’image (ou les images de l’Application)
  • Intégrer au Projet, un docker-compose.yml pour simplifier le démarrage de l’Application sur le Poste de Dev.
  • Créer un chart helm pour déployer l’Application dans le cluster.

Création du Projet

Convention de nommage des Projet :

  • Si projet FRONT, le nom est domaine_fonctionnel suffixé de _ui
  • Si projet BACK, le nom est domaine_fonctionnel suffixé de _api

Le nom du domaine fonctionnel peut être par exemple le nom de la ressource gérée (exemple : users).

Remarque : Ne pas préfixer les noms des applications avec des mots génériques comme “gestion”.

Création du Dockerfile

Le Dockerfile permet de construire l’image Docker de l’application.

Exemple de Dockerfile pour une application java.

FROM java:9

MAINTAINER XXX@softeam.fr

WORKDIR /apps/monappli

COPY target/monappli.jar /apps/monappli/monappli.jar

EXPOSE 8080

CMD java -jar monappli.jar 

Création du docker-compose.yml

Le docker-compose.yml simplifie la gestion des services docker. Il est aussi utilisé par le Jenkinsfile pour simplifier la construction des images Docker et leur push sur nexus.

Pour construire les images Docker (de l’Application) :

docker-compose build

Pour démarrer les services :

docker-compose up

Pour arrêter les services :

docker-compose stop

Exemple de Dockerfile pour une application java avec une base de données PostgreSQL.

version: '3.2'
services:

  monappli-postgres:
    image: registry.k8.wildwidewest.xyz/repository/docker-repository/monappli-postgres:${tag}
    build:
      context: .
      dockerfile: Dockerfile-postgres

  monappli:
    image: registry.k8.wildwidewest.xyz/repository/docker-repository/monappli:${tag}
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
    - monappli-postgres
    ports:
    - "8080:8080"

Création du package helm

Les charts Helm du Projet sont gérés dans le dépôt GIT charts.

Il faut d’abord installer Helm sur le Poste de Développement.

Puis, pour créer un chart helm il suffit d’exécuter à la racine du dépôt charts.

helm create monappli

Et finalement modifier les fichiers générés (cf. sections ci-dessous).

Fichier values.yaml

Liste des Modifications :

  • Le nom de l’image docker

    • Le même nom que celui utilisé dans le docker-compose.yml
  • La conf SSL de l’Application (si Application accessible en dehors du Cluster)

    • ingress.enabled = true => Création d’une ressource ingress pour que l’Application soit accessible sur internet
    • ingress.hosts => URL de l’Application
    • ingress.secretName => Nom du secret Kubernetes contenant les certificats de l’Application
  • Les ressources allouées au conteneur déployé (à adapter à l’Application)

image:
  repository: registry.k8.wildwidewest.xyz/repository/docker-repository/monappli

ingress:
  enabled: true
  annotations:
    certmanager.k8s.io/cluster-issuer: letsencrypt-issuer
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  path: /
  hosts:
  - monappli.k8.wildwidewest.xyz
  secretName: certificate
....
resources:
  limits:
    #  cpu: 100m
    memory: 128Mi
  requests:
    #  cpu: 100m
    memory: 64Mi
...  

Fichier deployment.yaml

Liste des modifications :

  • Intégrer le nom du secret Kubernetes (regsecret)contenant le login/mot de Nexus (nécessaire pour le download des images Docker)
    spec:
      containers:
        - name: 
...
      tolerations:

      imagePullSecrets:
      - name: regsecret

Release du package helm

Pour qu’une version d’un package helm soit visible du cluster, il faut construire une version du package et le publier dans le repo helm.

La release n’est pas encore industrialisée.

Il faut vérifier la qualité des packages helm.

sh ./lint.sh

Il faut modifier la version du package a releaser (cf. fichier release.sh)

version=0.1.43

Décommenter les packages à releaser.

helm package --version $version monappli

Effectuer la release.

sh ./release.sh

Création du Jenkinsfile

La définition du Job Jenkins se fait via un Jenkinsfile (remplacer monappli par le vrai nom de l’Application).

#!groovy
import java.text.*

// pod utilisé pour la compilation du projet
podTemplate(label: 'monappli-pod', containers: [

        // le slave jenkins
        containerTemplate(name: 'jnlp', image: 'jenkinsci/jnlp-slave:alpine'),

        // un conteneur pour le build maven
        containerTemplate(name: 'maven', image: 'maven', privileged: true, ttyEnabled: true, command: 'cat'),

        // un conteneur pour construire les images docker
        containerTemplate(name: 'docker', image: 'tmaier/docker-compose', command: 'cat', ttyEnabled: true),

        // un conteneur pour déployer les services kubernetes
        containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl', command: 'cat', ttyEnabled: true)],

        // montage nécessaire pour que le conteneur docker fonction (Docker In Docker)
        volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')]
) {

    node('monappli-pod') {        

        properties([
                buildDiscarder(
                        logRotator(
                                artifactDaysToKeepStr: '1',
                                artifactNumToKeepStr: '1',
                                daysToKeepStr: '3',
                                numToKeepStr: '3'
                        )
                )
            ])

        def now = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())

        stage('checkout sources') {
            checkout scm
        }

        container('maven') {

            stage('build sources') {
                sh 'mvn clean install sonar:sonar -Dsonar.host.url=http://sonarqube-sonarqube:9000 -Dsonar.java.binaries=target'
            }
        }

        container('docker') {

            stage('build docker image') {

                    sh 'mkdir /etc/docker'

                    sh 'echo {"insecure-registries" : ["registry.k8.wildwidewest.xyz"]} > /etc/docker/daemon.json'

                    withCredentials([usernamePassword(credentialsId: 'nexus_user', usernameVariable: 'username', passwordVariable: 'password')]) {

                         sh "docker login -u ${username} -p ${password} registry.k8.wildwidewest.xyz"
                    }

                    sh "tag=$now docker-compose build"

                    sh "tag=$now docker-compose push"
            }
        }

        container('kubectl') {

            stage('deploy') {


                // Déclencher le déploiement 
                build job: '/SOFTEAMOUEST/chart-run/master', parameters: [
                    string(name: 'image', value: "$now"),
                    string(name: 'chart', value: "monappli")], wait: false
            }
        }
    }
}

Vérification du Déploiement

Pour vérifier l’état du déploiement, il faut vérifier l’état du Job chart-run.

Ensuite, il faut se connecter en SSH sur le master (du cluster) pour vérifier l’état du Pod “monappli” (qui est déployée dans le namespace dev)

[root@vps242131 ~]# kubectl get pod -l app=monappli --namespace dev
NAME                           READY     STATUS    RESTARTS   AGE
monappli-7bf958b897-ts9fc   1/1       Running   0          2h

Si l’option ingress.enabled est définie à true dans le package helm, l’application doit être accessible par internet.

L’URL d’accès est le nom de l’application suivi du nom de l’environnement (ici dev) suivi de k8.wildwidewest.xyz.

  • L’URL de l’appli est https://monappli-dev.k8.wildwidewest.xyz

Pour se connecter en SSH au cluster, il faut fournir à Mehdi une clef SSH publique pour qu’il l’enregistre dans les clefs autorisées par l’Usine.