Intégration continue avec Python

Un chapitre plus avancé sur l’intégration continue
Auteur·rice

Lino Galiana

Date de publication

2024-11-20

Cette page sera actualisée prochainement, une version plus à jour et plus complète peut être trouvée sur https://ensae-reproductibilite.github.io/website/

L’un des apports principaux des innovations récentes de la data science est la manière dont des projets, malgré leur complexité, peuvent facilement être converti en projets pérennes à partir d’un prototype bien construit. En s’inspirant de l’approche devops , méthode de travail qui consiste à adopter un certain nombre de gestes pour automatiser la production de livrables ou de tests dès la conception du produit, les data scientists ont adopté une méthode de travail très efficace pour favoriser la réutilisation de leur travail par d’autres équipes que celles à l’origine de la conception du protype initial.

Cette approche devops a été reprise et étendue pour donner un autre buzz-word, le MLops. Il s’agit d’une approche qui vise à créer et mettre à disposition des modèles de machine learning de manière fiable et automatisée à chaque nouvelle étape du projet, en parallèle de la mise à jour du code ayant produit ces output.

Ces nouvelles méthodes de travail permettent des gains substantiels de productivité pour les équipes développant des modèles et réduit fortement le coût de reprise d’un code par une équipe en charge de sa pérenisation. Ce coût est en effet le principal frein à la mise en production de nouveaux projets ce qui peut représenter un gâchis non négligeable de temps et de ressources. Comme nous l’expliquons avec Romain Avouac dans un cours de dernière année de l’ENSAE (https://ensae-reproductibilite.github.io/website/), l’adoption de certaines bonnes pratiques de développement de code et d’une démarche exploitant les dernières innovations de la data science peut substantiellement augmenter les chances d’un succès d’un projet. Le nouveau paradigme, qui consiste à intégrer en amont du projet certaines contraintes de la production et tester continuellement la manière dont les livrables évoluent, évite que la mise en production d’un projet, qui est coûteuse en temps et en ressources, n’aboutisse qu’au moment où le projet est déjà caduc (car les données ou les besoins ont évolués…).

1 L’intégration continue: une opportunité pour les data scientists

On retrouve régulièrement l’acronyme CI/CD pour illustrer cette nouvelle méthode de travail dans le monde du développement logiciel :

  • l’intégration continue (CI pour continuous integration) est une pratique consistant, de manière automatique, à fréquemment tester les effets d’une modification faite à un code ou à un document faisant parti d’un projet informatique.

  • le déploiement en continu (CD pour continuous delivery) consiste à intégrer de manière automatisée la production d’un ou plusieurs livrables (environnement portable, application, site web, etc.) à chaque modification du code associé à un projet informatique.

Cette pratique permet ainsi de détecter de manière précoce des possibilités de bug ou l’introduction d’un changement non anticipé. Tout comme Git, cette pratique devient un standard dans les domaines collaboratifs.

L’intégration continue permet de sécuriser le travail, puisqu’elle offre un filet de sécurité (par exemple un test sur une machine à la configuration arbitraire), mais permet aussi de déployer en temps réel certaines évolutions. On parle parfois de déploiement en continu, complémentaire de l’intégration continue. Cette approche réduit ainsi la muraille de Chine entre un analyste de données et une équipe de développeurs d’application. Elle offre donc plus de contrôle, pour le producteur d’une analyse statistique, sur la valorisation de celle-ci.

Cette approche consiste une excellente opportunité pour les data scientists d’être en mesure de valoriser leurs projets auprès de publics aux exigences différentes. Pour des développeurs, le data scientist pourra fournir une image Docker (environnement portable où l’ensemble des dépendances et des configurations systèmes pour faire tourner un code sont contrôlés) permettant à d’autres d’exécuter facilement le code d’un projet. Pour faciliter la réutilisation d’un modèle par d’autres data scientists, il devient de plus en plus fréquent d’exposer un modèle sous forme d’API: les personnes désirant réutiliser le modèle peuvent directement l’appliquer en accédant à une prédiction par le biais d’une API ce qui évite d’avoir à fournir le jeu d’entraînement si ce dernier est sensible. Pour toucher des publics moins familiers du code, la mise à disposition de sites web interactifs valorisant certains résultats d’un projet peut être intéressante. Cette approche très exigeante d’utiliser un même projet pour toucher des cibles très différentes est grandement facilitée par le déploiement en continu et la mise à disposition de librairies ou d’infrastructures dédiées dans le monde de l’open-source.

Tout en restant éco-responsable (voir partie XXX), cela permet de mieux valoriser des projets pour réduire les coûts à le maintenir et le faire évoluer. Le cours de dernière année de l’ENSAE que je développe avec Romain Avouac (ensae-reproductibilite.github.io/) présente beaucoup plus de détails sur cette question.

2 L’intégration continue en pratique

L’intégration continue fonctionne très bien sur Gitlab et sur Github. A chaque interaction avec le dépôt distant (push), une série d’instruction définie par l’utilisateur est exécutée. Python et R s’intègrent très bien dans ce paradigme grâce à un certain nombre d’images de base (concept sur lequel nous allons revenir) qui peuvent être customisées pour répondre à une certaine configuration nécessaire pour exécuter des codes. C’est une méthode idéale pour améliorer la reproductibilité d’un projet: les instructions exécutées le sont dans un environnement isolé et contrôlé, ce qui diffère d’une machine personnelle.

3 Comment fonctionne l’intégration continue ?

L’intégration continue repose sur le système de la dockerisation ou conteneurisation. La technologie sous jacente s’appelle Docker. Il s’agit d’une technologie qui permet la construction de machines autosuffisantes (que l’on nomme containeurs) répliquant un environnement contrôlé (que l’on nomme image).

On parle de pipelines pour désigner une suite de tâches pour partir de 0 (généralement une machine Linux à la configuration minimale) et aboutir à l’issue d’une série d’instructions définies par l’utilisateur.

L’objectif est de trouver une image la plus parcimonieuse possible, c’est-à-dire à la configuration minimale, qui permet de faire tourner le code voulu. Les Actions Github consistuent un modèle sur lequel il est facile de s’appuyer lorsqu’on a des connaissances limitées concernant `Docker. Il est également très simple de construire son image de rien, ce qui est la démarche choisie dans l’autre cours de l’ENSAE que nous donnons avec Romain Avouac (https://ensae-reproductibilite.github.io/website/).

Quand on utilise un dépôt Github ou Gitlab , des services automatiques d’intégration continue peuvent être utilisés :

  • Gitlab CI: solution pleinement intégrée à un dépôt Gitlab. Très généraliste et permettant des pipelines très complexes (voir l’intégration continue du projet utilitR, une documentation pour R). Il est également possible de l’utiliser avec un dépôt stocké sur Github. L’inconvénient de cette approche est qu’elle est assez lente.
  • Github Actions: c’est l’alternative (relativement récente) au service d’intégration continue de Gitlab uniquement basée sur les technologies Github. La très forte dynamique de développement a rendu ce service incontournable. Un grand nombre de scripts pré-définis et paramétrables facilitent l’entrée dans le monde de l’intégration continue.

Historiquement, il existait d’autres services d’intégration continue, notamment Travis CI ou AppVeyor1

3.1 Fonctionnement des actions Github

Les actions Github fonctionnent par couches successives au sein desquelles on effectue un certain nombre d’instructions. La meilleure manière d’apprendre les actions Github est, certes, de lire la documentation officielle mais surtout, à mon avis, de regarder quelques pipelines pour comprendre la démarche.

L’un des intérêts des Github Actions est la possibilité d’avoir un pipeline proposant une intrication de langages différents pour avoir une chaine de production qui propose les outils les plus efficaces pour répondre à un objectif en limitant les verrous techniques.

Par exemple, le pipeline de ce cours, disponible sur Github {{< githubrepo >}} propose une intrication des langages Python et R avec des technologies Anaconda (pour contrôler l’environnement Python comme expliqué dans les chapitres précédents) et Javascript (pour le déploiement d’un site web avec le service tiers Netlify)2. Cette chaîne de production multi-langage permet que les mêmes fichiers sources génèrent un site web et des notebooks disponibles sur plusieurs environnements.

name: Production deployment

on:
  push:
    branches:
      - main
      - master

jobs:
  docker:
    if: "!contains(github.event.commits[0].message, '[skip ci]')"
    runs-on: ubuntu-latest
    steps:
      -
        name: Set up QEMU
        if: ${{ github.repository == 'linogaliana/python-datascientist' }}
        uses: docker/setup-qemu-action@v3
      -
        name: Set up Docker Buildx
        if: ${{ github.repository == 'linogaliana/python-datascientist' }}
        uses: docker/setup-buildx-action@v3
      -
        name: Login to DockerHub
        if: ${{ github.repository == 'linogaliana/python-datascientist' }}
        uses: docker/login-action@v3 
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Build and push
        if: ${{ github.repository == 'linogaliana/python-datascientist' }}
        id: docker_build
        uses: docker/build-push-action@v6
        env:
          GITHUB_PAT: ${{ secrets.PAT }}
        with:
          file: "docker/Dockerfile"
          push: true
          tags: linogaliana/python-datascientist:latest
      -
        name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}  
  pages:
    name: Render-Blog
    runs-on: ubuntu-latest
    container: linogaliana/python-datascientist:latest
    needs: docker
    if: ${{ !github.event.pull_request.head.repo.fork }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.ref }}
          repository: ${{github.event.pull_request.head.repo.full_name}}
      - name: Configure safe.directory  # Workaround for actions/checkout#760
        run: git config --global --add safe.directory /__w/python-datascientist/python-datascientist
      - name: Render website
        run: |
          rm _quarto.yml
          cp _quarto-prod.yml _quarto.yml
          python build/append_environment.py
          quarto render --profile fr --to html
          python build/sidebar.py --to english
          quarto render --profile en --to html
          python build/sidebar.py --to french
      - name: Archive build as artifacts
        uses: actions/upload-artifact@v4
        with:
          name: sitedir
          path: |
            _site
      - name: Publish to Pages
        if: github.ref == 'refs/heads/main'
        run: |
          git config --global user.email quarto-github-actions-publish@example.com
          git config --global user.name "Quarto GHA Workflow Runner"
          quarto publish gh-pages . --no-render --no-browser
  enonces:
    name: Render notebooks
    runs-on: ubuntu-latest
    container: linogaliana/python-datascientist:latest
    needs: docker
    if: ${{ !github.event.pull_request.head.repo.fork }}    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.ref }}
      - name: Configure safe.directory  # Workaround for actions/checkout#760
        run: |
          git config --global --add safe.directory /__w/python-datascientist/python-datascientist
          git config --global --add safe.directory /__w/python-datascientist/python-datascientist-notebooks
      - shell: bash
        run: |
          ls
          conda info
          conda list
      - name: Convert in ipynb with Quarto
        run: |
          export QUARTO_PROFILE=fr,en
          rm _quarto.yml
          cp _quarto-prod.yml _quarto.yml
          rm content/modelisation/index.qmd # Remove file not building in ipynb
          quarto render --profile fr --to ipynb
          quarto render --profile en --to ipynb
      - name: Move to expected directory
        run: |
          mkdir -p temp_notebooks
          mkdir -p temp_notebooks/notebooks
          python build/move_files.py --direction temp_notebooks/notebooks
          quarto render content --to ipynb --execute -M echo:true
          mkdir -p temp_notebooks/corrections
          python build/move_files.py --direction temp_notebooks/corrections
      - uses: actions/upload-artifact@v4
        with:
          name: Source enonce
          path: content/
      - uses: actions/upload-artifact@v4
        with:
          name: Enonces
          path: temp_notebooks/notebooks/
      - name: Pushes to another repository
        uses: linogaliana/github-action-push-to-another-repository@main
        env:
          API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
        with:
          source-directory: 'temp_notebooks/'
          destination-repository-username: 'linogaliana'
          destination-repository-name: 'python-datascientist-notebooks'
          user-email: lino.galiana@insee.fr
          destination-github-username: linogaliana
          #target-branch: test
          create-target-branch-if-needed: true
          reset-repo: true
  enonces-vscode:
    name: Render notebooks (VSCode version)
    runs-on: ubuntu-latest
    container: linogaliana/python-datascientist:latest
    needs: docker
    if: ${{ !github.event.pull_request.head.repo.fork }}    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.ref }}
      - name: Configure safe.directory  # Workaround for actions/checkout#760
        run: |
          git config --global --add safe.directory /__w/python-datascientist/python-datascientist
          git config --global --add safe.directory /__w/python-datascientist/python-datascientist-notebooks
      - shell: bash
        run: |
          ls
          conda info
          conda list
      - name: Convert in ipynb with Quarto
        run: |
          export QUARTO_PROFILE=fr,en
          rm _quarto.yml
          python ./build/nice-vscode/tweak_pipeline_vscode.py --filename _quarto-prod.yml
          cp _quarto-prod2.yml _quarto.yml
          rm content/modelisation/index.qmd # Remove file not building in ipynb
          quarto render --profile fr --to ipynb
          quarto render --profile en --to ipynb
      - name: Move to expected directory
        run: |
          mkdir -p temp_notebooks
          mkdir -p temp_notebooks/notebooks
          python build/move_files.py --direction temp_notebooks/notebooks
          quarto render content --to ipynb --execute -M echo:true
          mkdir -p temp_notebooks/corrections
          python build/move_files.py --direction temp_notebooks/corrections
      - name: Pushes to another repository
        uses: linogaliana/github-action-push-to-another-repository@main
        env:
          API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
        with:
          source-directory: 'temp_notebooks/'
          destination-repository-username: 'linogaliana'
          destination-repository-name: 'python-datascientist-notebooks-vscode'
          user-email: lino.galiana@insee.fr
          destination-github-username: linogaliana
          #target-branch: test
          create-target-branch-if-needed: true
          reset-repo: true
  define-matrix:
    runs-on: ubuntu-latest
    needs: enonces
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
        with:
          repository: 'linogaliana/python-datascientist-notebooks'
      - name: Define matrix
        id: set-matrix
        run: |
          echo "::set-output name=matrix::$(find . -type f -name "*.ipynb" \
            ! -name "_*" \
            ! -regex '.*/getting-started/.*' \
            ! -regex '.*/modelisation/index.*' \
            ! -regex '.*/git/.*' \
            ! -regex '.*/modern-ds/.*' \
            ! -regex '.*/manipulation/04a_webscraping_TP.*' \
            ! -regex '.*/NLP/05_exo_supp.*' \
            | jq -R -s -c 'split("\n")[:-1]')"
  
  check:
    needs: define-matrix
    runs-on: ubuntu-latest
    container: linogaliana/python-datascientist:latest
    continue-on-error: true
    strategy:
        matrix:
            manifest: ${{ fromJson(needs.define-matrix.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v4   
        with:
          repository: 'linogaliana/python-datascientist-notebooks'
      - run: |
          quarto render ${{ matrix.manifest }} --execute



Les couches qui constituent les étapes du pipeline portent ainsi le nom de steps. Un step peut comporter un certain nombre d’instructions ou exécuter des instructions pré-définies. L’une de ces instructions prédéfinies est, par exemple, l’installation de Python ou l’initialisation d’un environnement conda. La documentation officielle de Github propose un fichier qui peut servir de modèle pour tester un script Python voire l’uploader de manière automatique sur Pypi.

3.2 Intégration continue avec Python: tester un notebook

Cette section n’est absolument pas exhaustive. Au contraire, elle ne fournit qu’un exemple minimal pour expliquer la logique de l’intégration continue. Il ne s’agit ainsi pas d’une garantie absolue de reproductibilité d’un notebook.

Github propose une action officielle pour utiliser Python dans un pipeline d’intégration continue. Elle est disponible sur le MarketPlace Github. Il s’agit d’un bon point de départ, à enrichir.

Le fichier qui contrôle les instructions exécutées dans l’environnement Actions doit se trouver dans le dossier .github/workflows/ (:warning: ne pas oublier le point au début du nom du dossier). Il doit être au format YAML avec une extension .yml ou .yaml. Il peut avoir n’importe quel nom néanmoins il vaut mieux lui donner un nom signifiant, par exemple prod.yml pour un fichier contrôlant une chaîne de production.

3.2.1 Lister les dépendances

Avant d’écrire les instructions à exécuter par Github, il faut définir un environnement d’exécution car Github ne connaît pas la configuration Python dont vous avez besoin.

Il convient ainsi de lister les dépendances nécessaires dans un fichier requirements.txt (si on utilise un environnement virtuel) ou un fichier environment.yml (si on préfère utiliser un environnement conda). Bien que le principe sous-jacent soit légèrement différent, ces fichiers ont la même fonction : permettre la création d’un environnement ex-nihilo avec un certain nombre de dépendances pré-installées3.

Si on fait le choix de l’option environment.yml, le fichier prendra ainsi la forme suivante, à enrichir en fonction de la richesse de l’environnement souhaité. :

channels:
  - conda-forge

dependencies:
  - python>=3.10
  - jupyter
  - jupytext
  - matplotlib
  - nbconvert
  - numpy
  - pandas
  - scipy
  - seaborn

Le même fichier sous le format requirements.txt aura la forme suivante :

jupyter
jupytext
matplotlib
nbconvert
numpy
pandas
scipy
seaborn

Sous leur apparente équivalence, au-delà de la question du formatage, ces fichiers ont deux différences principales :

  • la version minimale de Python est définie dans le fichier environment.yml alors qu’elle ne l’est pas dans un fichier requirements.txt. C’est parce que le second installe les dépendances dans un environnement déjà existant par ailleurs alors que le premier peut servir à créer l’environnement avec une certaine configuration de Python ;
  • le mode d’installation des packages n’est pas le même. Avec un environment.yml on installera des packages via conda alors qu’avec un requirements.txt on privilégiera plutôt pip4.

Dans le cas de l’environnement conda, le choix du channel conda-forge vise à contrôler le dépôt utilisé par Anaconda.

Ne pas oublier de mettre ce fichier sous contrôle de version et de l’envoyer sur le dépôt par un push.

3.2.2 Créer un environnement reproductible dans Github Actions

Deux approches sont possibles à ce niveau, selon le degré de reproductibilité désiré5:

  • Créer l’environnement via une action existante. L’action conda-incubator/setup-miniconda@v2 est un bon point de départ.
  • Créer l’environnement dans une image Docker.

La deuxième solution permet de contrôler de manière beaucoup plus fine l’environnement dans lequel Python s’éxécutera ainsi que la manière dont l’environnement sera créé6. Néanmoins, elle nécessite des connaissances plus poussées dans la principe de la conteneurisation qui peuvent être coûteuses à acquérir. Selon l’ambition du projet, notamment les réutilisation qu’il désire, un data scientist pourra privilégier telle ou telle option. Les deux solutions sont présentées dans l’exemple fil-rouge du cours que nous donnons avec Romain Avouac (https://ensae-reproductibilite.github.io/website/application/).

3.2.3 Tester un notebook myfile.ipynb

Dans cette partie, on va supposer que le notebook à tester s’appelle myfile.ipynb et se trouve à la racine du dépôt. Les dépendances pour l’exécuter sont listées dans un fichier requirements.txt.

Le modèle suivant, expliqué en dessous, fournit un modèle de recette pour tester un notebook. Supposons que ce fichier soit présent dans un chemin .github/workflows/test-notebook.yml

Environnement virtuel
name: Test notebook execution using Github Actions

on: [push]

jobs:
  build-linux:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: '3.10'
      - shell: bash
      run: |
        python --version
    - name: Install dependencies
      run:
        pip install -r requirements.txt
        pip install jupyter nbconvert
    - name: Test jupyter from command line
      run:
        jupyter nbconvert --execute --to notebook --inplace myfile.ipynb
    - uses: actions/upload-artifact@v3
      with:
        name: Notebook
        path: myfile.ipynb
        retention-days: 5
Environnement conda
name: Test notebook execution using Github Actions

on: [push]

jobs:
  build-linux:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: '3.10'
    - name: Add conda to system path
      run: |
        # $CONDA is an environment variable pointing to the root of the miniconda directory
        echo $CONDA/bin >> $GITHUB_PATH
    - name: Install dependencies
      run: |
        conda env update --file environment.yml --name base
        conda install jupyter nbconvert
    - name: Test jupyter from command line
      run:
        jupyter nbconvert --execute --to notebook --inplace myfile.ipynb
    - uses: actions/upload-artifact@v3
      with:
        name: Notebook
        path: myfile.ipynb
        retention-days: 5

Dans les deux cas, la démarche est la même:

  • on récupère les fichiers présents dans le dépôt (action checkout) ;
  • on installe Python ;
  • on installe les dépendances pour exécuter le code. Dans l’approche conda, il est également nécessaire de faire quelques configurations supplémentaires (notamment ajouter conda aux logiciels reconnus par la ligne de commande) ;
  • on teste le notebook en ligne de commande et remplace celui existant, sur la machine temporaire, par la version produite sur cet environnement neutre.
  • on rend possible le téléchargement du notebook produit automatiquement pendant 5 jours7. Ceci repose sur les artefacts qui sont un élément récupéré des machines temporaires qui n’existent plus dès que le code a fini d’être exécuté.

Ces actions sont exécutées à chaque interaction avec le dépôt distant (push), quelle que soit la branche. A partir de ce modèle, il est possible de raffiner pour, par exemple, automatiquement faire un commit du notebook validé et le pusher via le robot Github8

4 Mettre à disposition en continu des valorisations du projet

Les projets de valorisation de données prennent des formes très variées et s’adressent à des publics multiples dont les attentes peuvent être très diverses. Ne pas attendre la finalisation d’un projet pour mettre en oeuvre certains livrables est une méthode efficace pour ne pas se retrouver noyé, au dernier moment, sous des demandes et de nouvelles contraintes.

La production en continu de livrables est donc une méthode très prisée dans le monde de la donnée. Les principaux fournisseurs de services d’intégration continue, à commencer par Github et Gitlab proposent des services pour le déploiement en continu. Cependant, ceux-ci ne sont adaptés qu’à certains types de livrables, principalement la mise à disposition de sites internet, et il peut être intéressant d’utiliser des services externes ou une infrastructures Kubernetes selon les moyens à dispositon et les besoins des utilisateurs.

4.1 Les services de mise à disposition de Github et Gitlab

Github et Gitlab, les deux plateformes de partage de code, proposent non seulement des services gratuits d’intégration continue mais aussi des services de mise à disposition de sites web pleinement intégrés aux services de stockage de code.

Ces services, Gitlab Pages et Github Pages, auxquels on peut associer le service externe Netlify qui répond au même principe9 permettent, à chaque modification du code source d’un projet, de reconstruire le site web (le livrable) qui peut être directement produit à partir de certains fichiers (des slides revealJS par exemple) ou qui sert d’output à l’intégration continue après compilation de fichiers plus complexes (des fichiers quarto par exemple).

Chaque dépôt sur Github ou Gitlab peut ainsi être associé à un URL de déploiement disponible sur internet. A chaque commit sur le dépôt, le site web qui sert de livrable est ainsi mis à jour. La version déployée à partir de la branche principale peut ainsi être considérée comme la version de production alors que les branches secondaires peuvent servir d’espace bac à sable pour vérifier que des changements dans le code source ne mettent pas en péril le livrable. Cette méthode, qui sécurise la production d’un livrable sous forme de site web, est ainsi particulièrement appréciable.

4.2 Les services externes disponibles sans infrastructure spéciale

Pour fonctionner, l’intégration continue nécessite de mettre en oeuvre des environnements normalisés. Comme évoqué précédemment, la technologie sous-jacente est celle de la conteneurisation. Les images qui servent de point de départ au lancement d’un conteneur sont elles-mêmes mises à disposition dans des espaces communautaires (des registres d’images). Il en existe plusieurs, les plus connus étant le dockerhub ou le registry de Gitlab. Ces registres servent d’espaces de stockage pour des images, qui sont des objets volumineux (potentiellement plusieurs Gigas) mais aussi d’espace de mutualisation en permettant à d’autres de réutiliser une image prête à l’emploi ou, au contraire, à partir de laquelle on peut ajouter un certain nombre de couches pour obtenir l’environnement minimal de reproductibilité. Il est possible d’utiliser certaines actions Github prête à l’emploi pour constuire une image Docker à partir d’un fichier Dockerfile. Après avoir crée une connexion entre un compte sur la plateforme Github et l’autre sur DockerHub, une mise à disposition automatisée d’un livrable sous forme d’image Docker est ainsi possible.

Une image Docker peut offrir une grande variété d’output. Elle peut servir uniquement à mettre à disposition un environnement de reproductibilité mais elle peut servir à mettre à disposition, pour les personnes maîtrisant Docker, des output plus raffinés. Par exemple, dans le cours que nous donnons à l’ENSAE, nous montrons comment docker peut servir à mettre à disposition à un utilisateur tiers une application minimaliste (construite avec flask) qu’il fera tourner sur son ordinateur.

Si une image Docker peut être très utile pour la mise à disposition, elle nécessite pour sa réutilisation un niveau avancé d’expertise en programmation. Cela ne conviendra pas à tous les publics. Certains ne désireront que bénéficier d’une application interactive où ils pourrons visualiser certains résultats en fonction d’actions comme des filtres sur des sous-champs ou le choix de certaines plages de données. D’autres publics seront plutôt intéressé par la réutilisation d’un programme ou des résultats d’un modèle sous forme d’API mais n’auront pas l’infrastructure interne pour faire tourner le code d’origine ou une image Docker. C’est pour répondre à ces limites qu’il peut devenir intéressant, pour une équipe de data science de développer une architecture kubernetes interne, si l’organisation en a les moyens, ou de payer un fournisseur de service, comme AWS, qui permet cela.

4.3 Kubernetes: le sommet de la pente du déploiement

Kubernetes est une technologie qui pousse la logique de la conteneurisation à son paroxysme. Il s’agit d’un système open-source, développé par Google, permettant d’automatiser le déploiement, la mise à l’échelle et la gestion d’applications conteneurisées. Grâce à Kubernetes, une application, par exemple un site web proposant de la réactivité, peut être mise à disposition et reporter les calculs, lorsqu’ils sont nécessaires, sur un serveur. L’utilisation de Kubernetes dans un projet de data science permet ainsi d’anticiper à la fois l’interface d’une application valorisant un projet mais aussi le fonctionnement du back-office, par exemple en testant la capacité de charge de cette application. Une introduction à Kubernetes orienté donnée peut être trouvée dans le cours dédié à la mise en production que nous donnons avec Romain Avouac et dans ce post de blog très bien fait.

Dans les grandes organisations, où les rôles sont plus spécialisés que dans les petites structures, ce ne sont pas nécessairement les data scientists qui devront maîtriser Kubernetes mais plutôt les data-architect ou les data-engineer. Néanmoins, les data scientists devront être capable de dialoguer avec eux et mettre en oeuvre une méthode de travail adaptée (celle-ci reposera en principe sur l’approche CI/CD). Dans les petites structures, les data scientist peuvent être en mesure de mettre en oeuvre le déploiement en continu. En revanche, il est plus rare, dans ces structures, où les moyens humains de maintenance sont limités, que les serveurs sur lesquels fonctionnent Kubernetes soient détenus en propres. En général, ils sont loués dans des services de paiement à la demande de type AWS.

5 Références

  • https://ensae-reproductibilite.github.io/website/
  • https://towardsdatascience.com/from-jupyter-to-kubernetes-refactoring-and-deploying-notebooks-using-open-source-tools-19f99585e923

Informations additionnelles

environment files have been tested on.

Latest built version: 2024-11-20

Python version used:

'3.12.6 | packaged by conda-forge | (main, Sep 30 2024, 18:08:52) [GCC 13.3.0]'
Package Version
affine 2.4.0
aiobotocore 2.15.1
aiohappyeyeballs 2.4.3
aiohttp 3.10.8
aioitertools 0.12.0
aiosignal 1.3.1
alembic 1.13.3
altair 5.4.1
aniso8601 9.0.1
annotated-types 0.7.0
appdirs 1.4.4
archspec 0.2.3
asttokens 2.4.1
attrs 24.2.0
babel 2.16.0
bcrypt 4.2.0
beautifulsoup4 4.12.3
black 24.8.0
blinker 1.8.2
blis 0.7.11
bokeh 3.5.2
boltons 24.0.0
boto3 1.35.23
botocore 1.35.23
branca 0.7.2
Brotli 1.1.0
cachetools 5.5.0
cartiflette 0.0.2
Cartopy 0.24.1
catalogue 2.0.10
cattrs 24.1.2
certifi 2024.8.30
cffi 1.17.1
charset-normalizer 3.3.2
click 8.1.7
click-plugins 1.1.1
cligj 0.7.2
cloudpathlib 0.20.0
cloudpickle 3.0.0
colorama 0.4.6
comm 0.2.2
commonmark 0.9.1
conda 24.9.1
conda-libmamba-solver 24.7.0
conda-package-handling 2.3.0
conda_package_streaming 0.10.0
confection 0.1.5
contextily 1.6.2
contourpy 1.3.0
cryptography 43.0.1
cycler 0.12.1
cymem 2.0.8
cytoolz 1.0.0
dask 2024.9.1
dask-expr 1.1.15
databricks-sdk 0.33.0
debugpy 1.8.6
decorator 5.1.1
Deprecated 1.2.14
diskcache 5.6.3
distributed 2024.9.1
distro 1.9.0
docker 7.1.0
duckdb 0.10.1
en-core-web-sm 3.7.1
entrypoints 0.4
et_xmlfile 2.0.0
exceptiongroup 1.2.2
executing 2.1.0
fastexcel 0.11.6
fastjsonschema 2.20.0
fiona 1.10.1
Flask 3.0.3
folium 0.17.0
fontawesomefree 6.6.0
fonttools 4.54.1
frozendict 2.4.4
frozenlist 1.4.1
fsspec 2023.12.2
gensim 4.3.2
geographiclib 2.0
geopandas 1.0.1
geoplot 0.5.1
geopy 2.4.1
gitdb 4.0.11
GitPython 3.1.43
google-auth 2.35.0
graphene 3.3
graphql-core 3.2.4
graphql-relay 3.2.0
graphviz 0.20.3
great-tables 0.12.0
greenlet 3.1.1
gunicorn 22.0.0
h2 4.1.0
hpack 4.0.0
htmltools 0.6.0
hyperframe 6.0.1
idna 3.10
imageio 2.36.0
importlib_metadata 8.5.0
importlib_resources 6.4.5
inflate64 1.0.0
ipykernel 6.29.5
ipython 8.28.0
itsdangerous 2.2.0
jedi 0.19.1
Jinja2 3.1.4
jmespath 1.0.1
joblib 1.4.2
jsonpatch 1.33
jsonpointer 3.0.0
jsonschema 4.23.0
jsonschema-specifications 2024.10.1
jupyter-cache 1.0.0
jupyter_client 8.6.3
jupyter_core 5.7.2
kaleido 0.2.1
kiwisolver 1.4.7
langcodes 3.5.0
language_data 1.3.0
lazy_loader 0.4
libmambapy 1.5.9
locket 1.0.0
lxml 5.3.0
lz4 4.3.3
Mako 1.3.5
mamba 1.5.9
mapclassify 2.8.1
marisa-trie 1.2.1
Markdown 3.6
markdown-it-py 3.0.0
MarkupSafe 2.1.5
matplotlib 3.9.2
matplotlib-inline 0.1.7
mdurl 0.1.2
menuinst 2.1.2
mercantile 1.2.1
mizani 0.11.4
mlflow 2.16.2
mlflow-skinny 2.16.2
msgpack 1.1.0
multidict 6.1.0
multivolumefile 0.2.3
munkres 1.1.4
murmurhash 1.0.10
mypy-extensions 1.0.0
narwhals 1.14.1
nbclient 0.10.0
nbformat 5.10.4
nest_asyncio 1.6.0
networkx 3.3
nltk 3.9.1
numpy 1.26.4
opencv-python-headless 4.10.0.84
openpyxl 3.1.5
opentelemetry-api 1.16.0
opentelemetry-sdk 1.16.0
opentelemetry-semantic-conventions 0.37b0
OWSLib 0.28.1
packaging 24.1
pandas 2.2.3
paramiko 3.5.0
parso 0.8.4
partd 1.4.2
pathspec 0.12.1
patsy 0.5.6
Pebble 5.0.7
pexpect 4.9.0
pickleshare 0.7.5
pillow 10.4.0
pip 24.2
platformdirs 4.3.6
plotly 5.24.1
plotnine 0.13.6
pluggy 1.5.0
polars 1.8.2
preshed 3.0.9
prometheus_client 0.21.0
prometheus_flask_exporter 0.23.1
prompt_toolkit 3.0.48
protobuf 4.25.3
psutil 6.0.0
ptyprocess 0.7.0
pure_eval 0.2.3
py7zr 0.20.8
pyarrow 17.0.0
pyarrow-hotfix 0.6
pyasn1 0.6.1
pyasn1_modules 0.4.1
pybcj 1.0.2
pycosat 0.6.6
pycparser 2.22
pycryptodomex 3.21.0
pydantic 2.9.2
pydantic_core 2.23.4
Pygments 2.18.0
PyNaCl 1.5.0
pynsee 0.1.8
pyogrio 0.10.0
pyOpenSSL 24.2.1
pyparsing 3.1.4
pyppmd 1.1.0
pyproj 3.7.0
pyshp 2.3.1
PySocks 1.7.1
python-dateutil 2.9.0
python-dotenv 1.0.1
python-magic 0.4.27
pytz 2024.1
pyu2f 0.1.5
pywaffle 1.1.1
PyYAML 6.0.2
pyzmq 26.2.0
pyzstd 0.16.2
querystring_parser 1.2.4
rasterio 1.4.2
referencing 0.35.1
regex 2024.9.11
requests 2.32.3
requests-cache 1.2.1
retrying 1.3.4
rich 13.9.4
rpds-py 0.21.0
rsa 4.9
ruamel.yaml 0.18.6
ruamel.yaml.clib 0.2.8
s3fs 2023.12.2
s3transfer 0.10.2
scikit-image 0.24.0
scikit-learn 1.5.2
scipy 1.13.0
seaborn 0.13.2
setuptools 74.1.2
shapely 2.0.6
shellingham 1.5.4
six 1.16.0
smart-open 7.0.5
smmap 5.0.0
sortedcontainers 2.4.0
soupsieve 2.5
spacy 3.7.5
spacy-legacy 3.0.12
spacy-loggers 1.0.5
SQLAlchemy 2.0.35
sqlparse 0.5.1
srsly 2.4.8
stack-data 0.6.2
statsmodels 0.14.4
tabulate 0.9.0
tblib 3.0.0
tenacity 9.0.0
texttable 1.7.0
thinc 8.2.5
threadpoolctl 3.5.0
tifffile 2024.9.20
toolz 1.0.0
topojson 1.9
tornado 6.4.1
tqdm 4.66.5
traitlets 5.14.3
truststore 0.9.2
typer 0.13.1
typing_extensions 4.12.2
tzdata 2024.2
Unidecode 1.3.8
url-normalize 1.4.3
urllib3 1.26.20
wasabi 1.1.3
wcwidth 0.2.13
weasel 0.4.1
webdriver-manager 4.0.2
websocket-client 1.8.0
Werkzeug 3.0.4
wheel 0.44.0
wordcloud 1.9.3
wrapt 1.16.0
xgboost 2.1.1
xlrd 2.0.1
xyzservices 2024.9.0
yarl 1.13.1
yellowbrick 1.5
zict 3.0.0
zipp 3.20.2
zstandard 0.23.0

View file history

SHA Date Author Description
580cba7 2024-08-07 18:59:35 Lino Galiana Multilingual version as quarto profile (#533)
c9f9f8a 2024-04-24 15:09:35 Lino Galiana Dark mode and CSS improvements (#494)
005d89b 2023-12-20 17:23:04 Lino Galiana Finalise l’affichage des statistiques Git (#478)
1f23de2 2023-12-01 17:25:36 Lino Galiana Stockage des images sur S3 (#466)
a06a268 2023-11-23 18:23:28 Antoine Palazzolo 2ème relectures chapitres ML (#457)
652009d 2023-10-09 13:56:34 Lino Galiana Finalise le cleaning (#430)
a771183 2023-10-09 11:27:45 Antoine Palazzolo Relecture TD2 par Antoine (#418)
154f09e 2023-09-26 14:59:11 Antoine Palazzolo Des typos corrigées par Antoine (#411)
9a4e226 2023-08-28 17:11:52 Lino Galiana Action to check URL still exist (#399)
9977c5d 2023-08-28 10:43:36 Lino Galiana Fix bug path pandas (#397)
3bdf3b0 2023-08-25 11:23:02 Lino Galiana Simplification de la structure 🤓 (#393)
7adbea4 2023-06-12 11:25:40 Lino Galiana Update CI elements
23cd4a1 2022-07-05 10:31:03 Lino Galiana Précisions supplémentaires dans la partie CI/CD (#246)
bacb5a0 2022-07-04 19:05:20 Lino Galiana Enrichir la partie elastic (#241)
Retour au sommet

Notes de bas de page

  1. Ces services d’intégration continue étaient utilisés lorsque Github ne proposait pas encore de service intégré, comme le faisait Gitlab. Ils sont de moins en moins fréquemment utilisés.↩︎

  2. Pour réduire le temps nécessaire pour construire le site web, ce pipeline s’appuie sur un environnement Docker construit sur un autre dépôt disponible également sur Github . Celui-ci part d’une configuration système Linux et construit un environnement Anaconda à partir d’un fichier environment.yml qui liste toutes les dépendances nécessaires pour exécuter les morceaux de code du site web. Cet environnement Anaconda est construit grâce à l’outil mamba qui permet d’aller beaucoup plus vite dans la constitution d’environnements que ne le permet conda.↩︎

  3. Sur la différence entre les environnements virtuels et les environnements conda, voir cette partie de cours plus avancé que nous donnons avec Romain Avouac sur la mise en production de projets data science.↩︎

  4. Il est possible d’installer une partie des packages avec pip en définissant un champ pip dans le fichier environment.yml. Néanmoins, les concepteurs d’Anaconda recommandent d’être prudent avec cette méthode qui présente certes l’avantage d’accélérer le temps de création de l’environnement mais peut créer des difficultés avec des librairies nécessitant d’autres langages système comme le C.↩︎

  5. Le point de vue que nous défendons avec Romain Avouac dans notre cours sur la reproductibilité est qu’il s’agit d’un continuum dans lequel on investit plus ou moins en fonction de ses contraintes, de ses besoins, de ses compétences, du temps humain qu’on peut dédier à développer des output reproductibles et le temps gagné en développant une telle approche. Selon où on se trouve sur ce cursus, en fonction des solutions déjà existantes qu’on peut trouver sur internet, on va plus ou moins raffiner notre intégration et nos déploiements continus.↩︎

  6. Il est recommandé de ne pas garder la période de rétention des artefacts par défaut car celle-ci est assez longue (90 jours). Les output pouvant être assez volumineux et expirant rapidement (en général ce qui nous intéresse est la dernière ou l’avant dernière version de l’_output), pour des raisons écologiques, il est recommandé de fixer des périodes courtes. Cela peut être fait directement dans le fichier configurant l’intégration continue comme ici ou dans les paramètres par défaut du dépôt pour que cette règle s’applique à toutes les productions faites par intégration continue.↩︎

  7. Il est recommandé de ne pas garder la période de rétention des artefacts par défaut car celle-ci est assez longue (90 jours). Les output pouvant être assez volumineux et expirant rapidement (en général ce qui nous intéresse est la dernière ou l’avant dernière version de l’_output), pour des raisons écologiques, il est recommandé de fixer des périodes courtes. Cela peut être fait directement dans le fichier configurant l’intégration continue comme ici ou dans les paramètres par défaut du dépôt pour que cette règle s’applique à toutes les productions faites par intégration continue.↩︎

  8. Il s’agit du service utilisé, par exemple, pour ce cours. Netlify est un service de mise à disposition qui offre des fonctionalités plus complètes que celles permises par Gitlab Pages et Github Pages. Outre cet avantage, il est plus facile à configurer que Github Pages qui nécessite l’usage d’une branche dédiée nommée gh-pages, ce qui peut rebutant.↩︎

  9. Il s’agit du service utilisé, par exemple, pour ce cours. Netlify est un service de mise à disposition qui offre des fonctionalités plus complètes que celles permises par Gitlab Pages et Github Pages. Outre cet avantage, il est plus facile à configurer que Github Pages qui nécessite l’usage d’une branche dédiée nommée gh-pages, ce qui peut rebutant.↩︎

Citation

BibTeX
@book{galiana2023,
  author = {Galiana, Lino},
  title = {Python pour la data science},
  date = {2023},
  url = {https://pythonds.linogaliana.fr/},
  doi = {10.5281/zenodo.8229676},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.