Skip to content
Snippets Groups Projects
cicd.md 8.62 KiB
Newer Older
  • Learn to ignore specific revisions
  • # CI/CD sur Openshift
    
    ## Runner
    
    Pour créer un runner capable de déployer nos applications, il faut en faire la demande à partir du [Manager Openshift Synaaps](https://manager.apps.grandlyon.com/), dans la partie `Gestion des runners GITLAB`.
    
    - Vers quel git le runner doit-il pointer => forge.grandlyon.com (GITLAB SYNAAPS)
    - Nom du projet git pointé => nom du projet (sert à identifier le runner)
    - Token Git => le registration token obtenu sur la forge
        - Pour un projet : Settings > CI/CD > Runners > trois petits points à coté de `New project runner`
        - Pour un groupe : Build > Runners > trois petits points à coté de `New group runner`
    
    ## Variables sur la forge
    
    ### Token d'accès au container registry
    
    Pour qu'Openshift puisse lire les images Docker hébergées sur la forge il aura besoin d'un access token avec les droit read_registry et le rôle de Maintainer.
    
    Ce token a été créé au niveau du [groupe Factory](https://forge.grandlyon.com/groups/web-et-numerique/factory/-/runners) sur la forge et sa valeur est contenue dans la variable `READ_REGISTRY_TOKEN`, accessible aux sous-projets.
    
    ### KUBECONFIG
    
    Pour que le runner puisse appliquer des déploiements sur un namespace, il lui en faut les droits. Un compte de service nommé `cicd-robot` est créé sur chaque namespace avec le rôle `dock-air-deployer`, ce qui lui donne un certain périmètre d'autorisations.
    
    Depuis la console openshift, se rendre sur `User Management > ServiceAccounts > cicd-robot et cliquer sur Actions > Download kubeconfig file`. Le contenu de ce fichier pourra être donné au runner pour avoir les mêmes autorisations que le compte `cicd-robot`
    
    Le contenu de ce fichier pour les namespace cpd-p01 et cpd-r01 sont stockés dans des variables du groupe Factory sur la forge, respectivement `KUBE_CONFIG_CPD_PROD` et `KUBE_CONFIG_CPD_REC`
    
    ## .gitlab-ci.yml
    
    ### Cas simple : une seule branche à déployer
    
    On écrit en dur le namespace, l'image à utiliser et l'URL de l'application dans les fichiers de config, exemple du `deployment.yml` de la doc self-data :
    
    ```yaml
    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: self-data-doc
      namespace: ns-cpd-p01-syn
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: self-data-doc
      template:
        metadata:
          labels:
            app: self-data-doc
        spec:
          containers:
            - name: self-data-doc
              image: registry.forge.grandlyon.com/web-et-numerique/factory/llle_project/self-data-technical-doc:latest
              imagePullPolicy: Always
              ports:
                - containerPort: 8080
                  protocol: TCP
          nodeSelector:
            node-role.kubernetes.io/worker: ''
          imagePullSecrets:
            - name: forge-secret
    
    ```
    
    Dans le fichier .gitlab-ci.yml, ajouter un stage deploy après celui de build, puis définir un job comme celui-ci (exemple d'un déploiement en prod sur la branche master):
    
    ```yaml
    deploy_master:
      stage: deploy
      tags:
        - ns-cpd-p01-syn
      only:
        - master
      before_script:
        - NAMESPACE=ns-cpd-p01-syn
        - export KUBECONFIG=$KUBE_CONFIG_CPD_PROD
      script:
        - oc create secret -n $NAMESPACE docker-registry forge-secret --docker-server=$CI_REGISTRY --docker-username=forge-secret --docker-password=$READ_REGISTRY_TOKEN --dry-run=client -o yaml | oc apply -f -
        - oc apply -f k8s/deployment.yml
        - oc apply -f k8s/service.yml
        - oc apply -f k8s/route.yml
        - oc delete pod -l app=self-data-doc
    ```
    
    Quelques remarques et explications :
    
    - Le champ `tags` permet de sélectionner le runner à utiliser pour le déploiement (ici: `ns-cpd-p01-syn` pour la PROD)
    - la commande `export KUBECONFIG=...` permet de donner les autorisations  nécessaires au runner sur le bon namespace selon les variables stockées dans la forge (`$KUBE_CONFIG_CPD_PROD` ou `$KUBE_CONFIG_CPD_REC`)
    - la commande `oc create secret ...` créé un secret nommé `forge-secret` dans Openshift basé sur la variable `$READ_REGISTRY_TOKEN` stockée sur la forge pour pouvoir tirer les images
    - On utilise `oc apply ...` pour appliquer les différentes parties de l'application
    
    !!! warning "Cette dernière étape entraîne une interruption de l'application le temps que le nouveau pod soit lancé (quelques secondes à une minute selon les cas)"
        - La commande `oc delete pod -l app=self-data-doc` supprime un pod d'un déploiement basé sur le label défini dans son fichier de config. Cela a pour effet de forcer Openshift à rafraîchir l'image utilisée pour ce déploiement. Sans cela, le pod n'est pas mis à jour car le tag de l'image n'a pas changé, ce qui ne provoque pas la mise à jour du pod.
    
    ### Cas plus complexe : deux branches (recette et production) à déployer
    
    Pour pouvoir paramétrer nos déploiement selon l'environnement ciblé, il faut variabiliser certains éléments dans les fichiers de déploiement. Pour cela on remplace leur valeur par un placeholder compris entre double accolades ex: `{{NAMESPACE}}`. Voici les paramètres à remplacer et leur placeholder associé :
    
    - namespace sur lequel déployer => `{{NAMESPACE}}`
    - tag de l'image à déployer => `{{IMAGE_TAG}}`
    - url de l'application => `{{HOSTNAME}}`
    
    Exemple du `deployment.yml` de landing page de Mes Papiers :
    
    ```yaml
    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: mes-papiers-landing-page
      namespace: {{NAMESPACE}}
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mes-papiers-landing-page
      template:
        metadata:
          labels:
            app: mes-papiers-landing-page
        spec:
          containers:
            - name: mes-papiers-landing-page
              image: registry.forge.grandlyon.com/web-et-numerique/factory/ma-bulle/mes-papiers-landing-page:{{IMAGE_TAG}}
              imagePullPolicy: Always
              ports:
                - containerPort: 8080
                  protocol: TCP
              resources:
                requests:
                  cpu: 100m
                  memory: 64Mi
                limits:
                  cpu: 200m
                  memory: 128Mi
          nodeSelector:
            node-role.kubernetes.io/worker: ''
          imagePullSecrets:
            - name: forge-secret
    ```
    
    Ces placeholders seront remplacés par la commande **sed** par les valeurs correspondantes au moment de la pipeline de déploiement. Il faut mettre à jour le `gitlab-ci.yml` avec les deux jobs suivants :
    
    ```yaml
    deploy_rec:
      stage: deploy
      before_script:
        - NAMESPACE=ns-cpd-r01-syn
        - export KUBECONFIG=$KUBE_CONFIG_CPD_REC
      script:
        - find k8s/ -name '*.yml' -exec sed -i "s/{{NAMESPACE}}/$NAMESPACE/g" {} \;
        - sed -i "s/{{IMAGE_TAG}}/dev/" ./k8s/deployment.yml
        - sed -i "s/{{HOSTNAME}}/mespapiers-rec.apps.grandlyon.com/g" ./k8s/route.yml
    
        - oc create secret -n $NAMESPACE docker-registry forge-secret --docker-server=$CI_REGISTRY --docker-username=forge-secret --docker-password=$READ_REGISTRY_TOKEN --dry-run=client -o yaml | oc apply -f -
    
        - oc apply -f k8s/deployment.yml
        - oc apply -f k8s/service.yml
        - oc apply -f k8s/route.yml
    
        - oc delete pod -l app=mes-papiers-landing-page
      tags:
        - ns-cpd-r01-syn
      only:
        - dev
    
    deploy_prod:
      stage: deploy
      before_script:
        - NAMESPACE=ns-cpd-p01-syn
        - export KUBECONFIG=$KUBE_CONFIG_CPD_PROD	
      script:
        - find k8s/ -name '*.yml' -exec sed -i "s/{{NAMESPACE}}/$NAMESPACE/g" {} \;
        - sed -i "s/{{IMAGE_TAG}}/main/" ./k8s/deployment.yml
        - sed -i "s/{{HOSTNAME}}/mespapiers.apps.grandlyon.com/g" ./k8s/route.yml
    
        - oc create secret -n $NAMESPACE docker-registry forge-secret --docker-server=$CI_REGISTRY --docker-username=forge-secret --docker-password=$READ_REGISTRY_TOKEN --dry-run=client -o yaml | oc apply -f -
    
        - oc apply -f k8s/deployment.yml
        - oc apply -f k8s/service.yml
        - oc apply -f k8s/route.yml
    
        - oc delete pod -l app=mes-papiers-landing-page
      tags:
        - ns-cpd-p01-syn
      only:
        - main
    ```
    
    Quelques remarques et explications supplémentaires par rapport au cas simple :
    
    - On utilise le bon namespace (**r**01-syn vs **p**01-syn) et les bonnes variables selon l'environnement ciblé (**dev** vs **main**)
    - La commande `find k8s/ -name '*.yml' -exec sed -i "s/{{NAMESPACE}}/$NAMESPACE/g" {} \;` trouve toutes les occurrences du placeholder `{{NAMESPACE}}` dans les fichiers compris dans le dossier `k8s` et les remplace par la variable `$NAMESPACE` définie dans le `before_script`
    - La commande `sed -i "s/{{IMAGE_TAG}}/main/" ./k8s/deployment.yml` remplace l'occurrence du placeholder `{{IMAGE_TAG}}` dans le fichier `k8s/deployment.yml` et la remplace par "main"
    - La commande `sed -i "s/{{HOSTNAME}}/mespapiers.apps.grandlyon.com/g" ./k8s/route.yml` remplace l'occurrence du placeholder `{{HOSTNAME}}` dans le fichier `k8s/route.yml` et la remplace par l’environnement cible : "mespapiers.apps.grandlyon.com" vs "mespapiers**-rec**.apps.grandlyon.com"