Comment construire et déployer une application Python ?

Back4app Python Cover

Python est un langage de programmation libre et gratuit qui a de nombreuses applications. Il a été créé par Guido Van Rossum en 1991 et est devenu depuis l’un des langages de programmation les plus populaires.

Dans cet article, nous parlerons de Python, de ses avantages, de ses inconvénients, de ses cas d’utilisation et de ses options de déploiement. De plus, nous montrerons comment construire, dockeriser et déployer une simple application Python sur Back4app Containers.

Poursuivez votre lecture pour en savoir plus sur la construction et le déploiement d’un projet Python.

Aperçu de Python

Python est un langage de programmation général de haut niveau. Il est interprété, dynamiquement typé et peut être utilisé pour l’écriture de scripts ainsi que pour la programmation. Les développeurs décrivent généralement Python comme un langage avec “piles incluses” en raison de sa vaste bibliothèque standard.

Le langage est apparu pour la première fois en 1991 et est devenu depuis lors l’un des langages de programmation les plus populaires et les plus appréciés en raison de sa syntaxe simple et lisible. Les grands principes de conception de Python sont abordés dans The Zen of Python, écrit par Tim Peters.

Python dispose d’une vaste communauté de développeurs et de paquets prêts à l’emploi. De nos jours, Python est utilisé à peu près partout – dans l’administration des serveurs, le calcul scientifique, l’apprentissage automatique, la construction d’API RESTful, etc. Python peut être considéré comme l’une des compétences essentielles d’un développeur moderne.

Avantages de Python

Avantages de Python

Facile à utiliser

Python est l’un des langages les plus faciles à utiliser et à apprendre. Sa courbe d’apprentissage est presque inexistante parce que sa philosophie de conception principale met l’accent sur la simplicité et la lisibilité du code. Python est également privilégié dans l’enseignement, car c’est un excellent outil pour apprendre les concepts de base de la programmation.

Développement rapide

Un autre avantage de Python est qu’il permet un développement rapide. C’est un langage idéal pour la création de prototypes et la réalisation rapide de travaux. En utilisant Python, vous n’avez pas à vous soucier d’une syntaxe complexe, de l’allocation de la mémoire, etc.

Polyvalent

Python est l’un des langages de programmation les plus polyvalents. Il est utilisé dans un large éventail d’industries et de domaines. La comptabilité, la recherche scientifique, la science des données, le développement web, le développement de jeux et l’automatisation sont quelques-uns des domaines dans lesquels Python est utilisé.

Portable

Python est un langage portable puisqu’il est interprété (comme Java) et non compilé (comme C++). Cela signifie que le code écrit sur une plateforme peut être facilement transféré sur une autre plateforme. Cela fait de Python un choix populaire pour les développeurs qui doivent prendre en charge plusieurs plateformes, y compris Windows, macOS, Linux et Android.

Bibliothèques étendues

Le langage de programmation Python est soutenu par une vaste communauté de développeurs. Il existe une bibliothèque pour presque tout ce que vous pouvez imaginer. Parmi les bibliothèques les plus populaires, citons

Et cela ne fait qu’effleurer la surface. Si vous voulez voir une liste complète des paquets Python géniaux, jetez un coup d’œil à awesome-python sur GitHub.

Dynamiquement typé

Python est un langage à typage dynamique, ce qui signifie qu’il n’est pas nécessaire de déclarer le type de données lors de la création de variables. Le type de la variable n’est attribué qu’au moment de l’exécution du code.

Même s’ils sont excellents, les langages à typage dynamique peuvent parfois être une arme à double tranchant. Ils sont généralement plus sujets aux erreurs et offrent un niveau de contrôle de la mémoire inférieur à celui des langages à typage statique.

Extensible

Si les exigences de votre projet comprennent des tâches informatiques lourdes, vous pouvez les écrire dans des langages plus rapides tels que C ou C++, puis les invoquer à partir de votre code Python.

Pour en savoir plus sur l’invocation de C/C++ à partir de Python, consultez cet excellent article.

Limites de Python

Inconvénients de Python

Mauvaise performance

Python est relativement lent par rapport à d’autres langages de programmation. Les deux facteurs qui entravent le plus ses performances sont le fait que Python est un langage interprété et qu’il est typiquement dynamique. Si les exigences de votre projet incluent le calcul intensif ou le multithreading, Python n’est peut-être pas l’outil le plus approprié. Vous feriez mieux d’utiliser C, C++ ou un autre langage compilé.

Mémoire intensive

Les programmes Python, comparés aux programmes écrits dans des langages à typage statique, consomment généralement plus de mémoire et offrent un niveau de contrôle plus faible sur la mémoire. Cela peut être un peu problématique si vos applications doivent être efficaces en termes de mémoire.

Sujet à des erreurs d’exécution

Python étant un langage interprété, il est plus sujet aux erreurs d’exécution. Comme il n’y a pas de processus de compilation, les bogues ne peuvent pas être découverts au moment de la compilation. En outre, Python est typiquement dynamique, ce qui signifie que les développeurs peuvent modifier le type d’une variable à tout moment. Cela peut parfois conduire à des erreurs et oblige les développeurs à être attentifs aux types de variables.

Verrouillage global de l’interprète (GIL)

Le verrouillage global de l’interpréteur (GIL) en Python est un mécanisme qui garantit qu’un seul thread exécute le bytecode Python à la fois. Bien que cela simplifie la mise en œuvre du langage et offre des avantages en termes de performances pour certains types de programmes, cela limite également la possibilité d’utiliser pleinement les processeurs multicœurs dans les applications liées au processeur.

Non optimisé pour l’accès aux bases de données

Travailler avec des bases de données dans des applications Python peut s’avérer plus difficile en raison de l’absence d’interfaces puissantes et conviviales telles que Java Database Connectivity (JDBC). Si Python peut encore être utilisé pour des opérations de base de données impliquant des tâches simples de lecture et d’écriture, il n’est peut-être pas l’option la plus appropriée pour les applications qui doivent travailler avec des bases de données complexes et de grande taille.

Options de déploiement de Python

Les applications Python peuvent être déployées sur plusieurs plateformes en nuage. En général, nous pouvons les classer dans les trois catégories suivantes :

  • Infrastructure en tant que service (IaaS)
  • Plate-forme en tant que service (PaaS)
  • Conteneurs en tant que service (CaaS)

L’IaaS est le moins abstrait et le CaaS est le plus abstrait. Il y a aussi l’hébergement classique, mais je suis sûr que vous le connaissez déjà.

Infrastructure en tant que service (IaaS)

L’infrastructure en tant que service (IaaS) est un modèle d’informatique en nuage dans lequel un fournisseur tiers offre des ressources informatiques virtualisées telles que des serveurs, du stockage, des systèmes d’exploitation et des réseaux sur l’internet. Les ressources fournies peuvent ensuite être gérées via des tableaux de bord avancés ou des API de haut niveau, ce qui donne aux clients un contrôle total sur l’ensemble de leur infrastructure.

Les principaux avantages de l’IaaS sont l’évolutivité, la réduction des coûts, l’amélioration de l’assistance, des performances et de la sécurité. La structure de paiement adoptée par la plupart des fournisseurs IaaS est basée sur un système de paiement à l’utilisation, dans lequel vous ne payez que pour les ressources que vous utilisez.

L’IaaS est le modèle d’informatique en nuage le plus flexible et reste l’option la plus populaire depuis son lancement au début des années 2010. Son principal inconvénient est que le client est entièrement responsable de ses applications, de ses systèmes d’exploitation et de ses données.

Plate-forme en tant que service (PaaS)

La plateforme en tant que service (PaaS) est un service d’informatique en nuage qui fournit aux utilisateurs un environnement en nuage pour créer, gérer et fournir des applications. Le PaaS offre des outils prédéfinis pour le développement, la personnalisation et le test des applications. Avec le PaaS, les utilisateurs peuvent se concentrer sur leur application, car le fournisseur de services s’occupe de l’infrastructure sous-jacente, y compris les serveurs, les systèmes d’exploitation, les logiciels, les sauvegardes, etc.

Les avantages du PaaS comprennent une mise sur le marché plus rapide, une sécurité accrue, la rentabilité, l’évolutivité, la haute disponibilité, et nécessitent généralement moins de code. Cependant, il y a quelques inconvénients. Les trois principaux sont le manque de flexibilité, le manque de contrôle et le risque de verrouillage du fournisseur. Néanmoins, le PaaS permet aux utilisateurs de créer des applications plus rapidement et avec moins de frais de gestion.

Conteneurs en tant que service (CaaS)

Les conteneurs en tant que service (CaaS) sont un modèle d’informatique en nuage qui permet aux organisations de télécharger, d’exécuter, d’adapter et de gérer leurs conteneurs en utilisant la technologie de virtualisation des conteneurs. Les fournisseurs de CaaS font abstraction d’une grande partie du travail nécessaire, comme l’infrastructure, les systèmes d’exploitation, les logiciels, les moteurs de conteneurisation, etc.

L’avantage des conteneurs est qu’une fois que votre application est conteneurisée, vous pouvez la déployer à peu près n’importe où et vous avez la garantie qu’elle se comportera de la même manière. Si le besoin s’en fait sentir, vous pouvez passer d’un fournisseur CaaS à un autre. Les clients CaaS sont généralement facturés par conteneur (sur la base des spécifications du conteneur).

Le CaaS est généralement plus cher que le IaaS et le PaaS, il offre un niveau de flexibilité et de contrôle moins élevé et nécessite un certain travail initial, par exemple l’intégration de l’application dans un conteneur, etc. Néanmoins, c’est l’un des modèles de cloud computing les plus faciles à utiliser une fois que votre application est conteneurisée.

Pour en savoir plus sur les conteneurs, consultez la page Qu’est-ce que les conteneurs dans l’informatique en nuage ?

Processus de déploiement de Python

Dans cette section du tutoriel, nous allons montrer comment construire et déployer une simple application Python étape par étape. L’application web sera implémentée en utilisant le framework Flask et déployée dans les conteneurs Back4app.

Conditions préalables

  • Expérience avec Python et Flask
  • Compréhension de base de Docker
  • Python version 3.8 ou ultérieure et Docker Desktop installé

Qu’est-ce que Back4app Containers ?

Back4app Containers est une plateforme gratuite et open-source qui vous permet de déployer et de mettre à l’échelle des applications en utilisant des conteneurs distribués à l’échelle mondiale.

Avec Back4app Containers, vous pouvez vous concentrer sur le développement de votre logiciel et le livrer rapidement, sans avoir à vous soucier de DevOps.

La plateforme est étroitement intégrée à GitHub et est équipée d’un système CI/CD intégré. En utilisant Back4app Containers, vous pouvez mettre votre application en ligne en quelques minutes.

Pourquoi utiliser les conteneurs Back4app ?

  • S’intègre bien à GitHub
  • Mise à l’échelle et déploiements sans temps d’arrêt
  • Extrêmement facile à utiliser
  • Excellente assistance à la clientèle

Introduction du projet

Dans cet article, nous allons construire et déployer une API RESTful simple qui va servir de liste TODO. L’API web va permettre aux utilisateurs d’effectuer des opérations CRUD de base – telles que l’ajout de tâches, la suppression de tâches, le marquage de tâches comme faites, etc.

Nous allons d’abord travailler sur le code source et ensuite le déployer dans un conteneur Flask de Back4app. L’application web sera implémentée en utilisant le framework web Flask.

N’hésitez pas à sauter la section “Code App” et à suivre votre propre projet Python.

Code App

Projet Init

Commencez par créer un répertoire dédié à l’application Flask et naviguez-y :

$ mkdir flask-todo
$ cd flask-todo

Créer un environnement virtuel et l’activer :

$ python3 -m venv venv && source venv/bin/activate

Ensuite, installez Flask via pip :

$ (venv) pip install Flask==2.2.2

Pour s’assurer que tout fonctionne, remplaçons le contenu de app.py par ce qui suit :

# app.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index_view():
    return {
        'detail': 'Hello world'
    }

Ce code initialise Flask et crée un point d’accès simple qui renvoie un message.

Démarrer le serveur avec :

$ (venv) flask run

Enfin, rendez-vous à l’adresse http://localhost:5000/ dans votre navigateur préféré. Vous devriez voir un message disant Hello world.

Base de données

Continuons à nous occuper de la base de données.

Au lieu d’exécuter du code SQL brut, nous utiliserons Flask-SQLAlchemy — un simple ORM Flask. Pour l’installer, exécutez :

$ (venv) pip install Flask-SQLAlchemy

Ensuite, remplacez le contenu de app.py par ce qui suit :

# app.py

from datetime import datetime

from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
app.config['SECRET_KEY'] = '5b3ef5s80gl3b217c20fb37044fe4k33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)


class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), nullable=False)
    description = db.Column(db.String(256), nullable=False)

    is_done = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.now(), nullable=True)
    updated_at = db.Column(db.DateTime, default=None, nullable=True)

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def __repr__(self):
        return f'<Task {self.title}>'


@app.route('/')
def index_view():
    return {
        'name': 'flask-todo',
        'description': 'a simple todo app written in flask',
        'version': 1,
    }

Ce code configure Flask et définit un nouveau modèle appelé Task. Le modèle de tâche a un nom, une description, is_done, et quelques autres variables telles que created_at et updated_at que nous mettrons à jour dynamiquement.

Ensuite, créez un script Python nommé init_db.py qui initialise et alimente la base de données SQLite:

# init_db.py

from app import db, app
from app import Task

with app.app_context():
    db.create_all()

    if Task.query.count() == 0:
        tasks = [
            Task(name='Backup the database', description='Make sure to backup the database with all the tables.'),
            Task(name='Setup 2FA', description='Setup the two factor authentication to secure your account.'),
            Task(name='Malware scan', description='Perform a malware scan.'),
        ]

        for task in tasks:
            db.session.add(task)

        db.session.commit()

Exécutez le script à partir de la ligne de commande :

$ (venv) python init_db.py

Vous remarquerez qu’un nouveau répertoire nommé instance a été créé. Dans ce répertoire, il y aura default.db qui est la base de données par défaut.

API

L’application Flask aura les URL suivantes :

  1. /api/ renvoie la liste de toutes les tâches
  2. /api/ / affiche ou supprime une tâche spécifique
  3. /api/create/ crée une nouvelle tâche
  4. /api/toggle/ / bascule la propriété is_done d’une tâche spécifique

Pour les mettre en œuvre, ajoutez ce qui suit à la fin de app.py :

# app.py

@app.route('/api/')
def list_view():
    json = [task.as_dict() for task in Task.query.all()]
    return jsonify(json)


@app.route('/api/<int:task_id>/', methods=['GET', 'DELETE'])
def detail_view(task_id):
    task = db.get_or_404(Task, task_id)

    if request.method == 'DELETE':
        db.session.delete(task)
        db.session.commit()

        return {
            'detail': 'Task has been successfully deleted.'
        }
    else:
        return task.as_dict()


@app.route('/api/create/', methods=['POST'])
def create_view():
    name = request.form.get('name')
    description = request.form.get('name')

    if name is None or description is None:
        return {
            'detail': 'Please provide the name and the description.'
        }, 400

    task = Task(name=name, description=description)
    db.session.add(task)
    db.session.commit()

    return task.as_dict()


@app.route('/api/toggle/<int:task_id>/')
def toggle_view(task_id):
    task = db.get_or_404(Task, task_id)

    if task.is_done:
        task.is_done = False
    else:
        task.is_done = True

    task.updated_at = datetime.now()
    db.session.commit()

    return task.as_dict()

Ce code est assez explicite. Nous avons défini les itinéraires nécessaires et mis en œuvre la logique requise. Chaque fois que nous voulons apporter des modifications à la base de données, nous devons les valider ("commit").

Voilà pour l’application Flask. Dans les prochaines sections, nous préparerons notre projet pour le déploiement.

Gunicorn

Le serveur web de Flask n’est pas recommandé en production car il est conçu pour gérer une requête à la fois et peut ne pas être capable de gérer de grandes quantités de trafic. Pour cette raison, nous allons le remplacer par Gunicorn – un serveur WSGI Python prêt pour la production.

Tout d’abord, installez-le via pip :

$ (venv) pip install gunicorn==20.1.0

Une fois l’installation réussie, vous pouvez démarrer le serveur comme suit :

$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app

[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 3
[INFO] Booting worker with pid: 4

N’oubliez pas que cette commande ne fonctionne que sur les systèmes d’exploitation basés sur UNIX.

Ceci démarrera deux travailleurs Gunicorn et exposera votre application sur le port 5000. Pour accéder à votre application, ouvrez votre navigateur web préféré et naviguez vers http://localhost:5000.

exigences.txt

La dernière chose à faire avant de dockeriser notre application est de créer un fichier requirements.txt. Le fichier requirements.txt est utilisé pour spécifier les dépendances du projet.

Générez-le en exécutant la commande suivante :

$ (venv) pip freeze > requirements.txt

D’autres (y compris les conteneurs Docker) peuvent alors utiliser le fichier requirements.txt comme suit :

$ (venv) pip install -r requirements.txt

Dockerize App

Pour dockeriser l’application, nous utiliserons un Dockerfile. Les Dockerfiles sont utilisés pour définir les instructions de construction des images Docker. Ils vous permettent de définir l’image de base, le répertoire de travail, les variables d’environnement, les commandes d’exécution, etc.

.dockerignore

Avant de travailler sur le fichier Docker, créons un fichier .dockerignore. Un fichier .dockerignore est utilisé pour spécifier quels dossiers et fichiers doivent être omis de l’image.

.git/
.idea/
instance/
__pycache__/

Veillez à modifier le fichier .dockerignore en fonction de vos besoins.

Fichier Docker

Créez un nouveau fichier à la racine du projet nommé Dockerfile avec le contenu suivant :

# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine

WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
    pip3 install -r requirements.txt

COPY . .

# initialize the database (create DB, tables, populate)
RUN python init_db.py

EXPOSE 5000/tcp

CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]

Ce fichier Docker utilise python:3.10-alpine comme image de base. Il définit ensuite le répertoire de travail, installe les pré-requis, copie le projet, initialise la base de données, et enfin démarre le serveur Gunicorn sur le port 5000.

Pour en savoir plus sur les Dockerfiles, consultez laréférence Dockerfile .

Test

Pour s’assurer que le fichier Docker fonctionne, nous pouvons le construire et l’exécuter localement. Pour construire l’image, exécutez :

$ docker build -t flask-todo:1.0 .

Si vous listez les images, vous devriez voir une nouvelle image :

$ docker images

REPOSITORY        TAG       IMAGE ID       CREATED       SIZE
flask-todo        1.0       7ege66240eb1   3 hours ago   109MB

Enfin, utilisez l’image pour créer un nouveau conteneur Docker :

$ docker run -it -p 5000:5000 flask-todo:1.0

[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 3
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 4

Vous pouvez utiliser -d pour démarrer le conteneur Docker en mode détaché. Cela signifie que le conteneur s’exécute en arrière-plan de votre terminal et ne reçoit pas d’entrée ou n’affiche pas de sortie.

Super, votre application tourne maintenant dans un conteneur. Naviguez vers http://localhost:5000 dans votre navigateur web préféré pour voir votre application web en action.

{
    "name": "flask-todo",
    "description": "a simple todo app written in flask",
    "version": 1
}

Pousser vers GitHub

Les étapes suivantes nécessitent que vous ayez un compte GitHub. Si vous n’en avez pas encore, inscrivez-vous, sinon connectez-vous. De plus, assurez-vous que Git est installé et configuré.

Une fois connecté à GitHub, utilisez le bouton “plus” en haut à droite de l’écran pour ouvrir la liste déroulante. Sélectionnez ensuite “Nouveau dépôt” :

Page d'index GitHub

Choisissez un nom personnalisé pour votre dépôt. Je choisirai “flask-todo”, puis cliquez sur “Create repository” :

Créer un dépôt sur GitHub

Une fois que votre dépôt est créé, prenez note de l’URL distante :

Adresse distante SSH de GitHub

Maintenant, revenons à notre projet local et poussons le code.

Puisque nous avons des fichiers que nous ne voulons pas pousser vers Git, créez un fichier .gitignore à la racine du projet. Je vais y ajouter ce qui suit, mais n’hésitez pas à le modifier selon vos besoins :

instance/*
!instance/.gitignore
.webassets-cache
.env

__pycache__/
*.py[cod]
*$py.class

Ensuite, ouvrez la ligne de commande et exécutez les commandes suivantes :

$ git init
$ git remote add origin <your_remote_url>
$ git add .
$ git commit -m "init"
$ git push origin master

Ceci initialisera un nouveau dépôt Git, ajoutera l’origine distante, VCS tous les fichiers, et créera le commit initial. Enfin, il poussera le code source vers le dépôt GitHub.

Très bien, cela devrait être le cas. Si vous naviguez maintenant vers votre dépôt dans votre navigateur, vous devriez être en mesure de voir que tous les fichiers ont été livrés.

Déployer l’application

Les étapes suivantes nécessitent que vous ayez un compte Back4app. Si vous en avez déjà un, connectez-vous, sinon inscrivez-vous pour le compte gratuit.

Une fois connecté, vous serez redirigé vers le tableau de bord de votre application. Cliquez sur le bouton “Créer une nouvelle application” pour lancer le processus de création de l’application.

Back4app Créer une application

Back4app vous permet de construire et de déployer deux types d’applications, Backend as a Service (BaaS) et Containers as a Service (CaaS). Puisque nous voulons déployer une application dockerisée, nous allons opter pour “Container as a Service”.

Back4app Containers as a Service

Ensuite, connectez votre GitHub avec votre compte Back4app. Assurez-vous de donner à Back4app les permissions sur le dépôt que nous avons créé dans l’étape précédente. Cliquez ensuite sur le bouton vert “Select” pour le sélectionner.

Back4app Select Repository

Back4app Containers vous permet de configurer le processus de déploiement. Vous pouvez définir la branche par défaut, le répertoire racine, activer/désactiver le déploiement automatique et définir des variables d’environnement. Nous n’avons pas besoin de tout cela, alors nommons simplement notre application et cliquons sur “Créer une application”.

Back4app Configurer l'application

Back4app va prendre quelques instants pour construire le conteneur, le télécharger dans le registre des conteneurs et l’exécuter. Une fois que votre conteneur est prêt, le statut va changer en “Ready” et vous pourrez voir une URL verte sur le côté gauche de l’écran.

Déploiement réussi de Back4app

En cliquant sur l’URL, vous ouvrirez l’application web dans votre navigateur. Vous remarquerez que Back4app a automatiquement émis un certificat SSL pour votre application et vous permet d’héberger une application Python gratuitement.

Conclusion

Dans cet article, vous avez découvert Python, ses avantages, ses inconvénients et ses options de déploiement. Vous avez également déployé avec succès une API RESTful Python simple. Vous devriez maintenant être en mesure de construire vos API simples et de les déployer dans les conteneurs Back4app.

Le code source final est disponible sur GitHub.

Prochaines étapes

  • Actuellement, toutes les données sont effacées à chaque redéploiement. Cela est dû au fait que la base de données est incluse dans l’image Docker. Envisagez de passer à une instance de base de données gérée.
  • Examinez les constructions en plusieurs étapes pour accélérer le processus de déploiement.

Leave a reply

Your email address will not be published.