
Pour commencer, un peu de théorie. Qu'est-ce que ?
En termes simples, c'est un document destiné à simplifier le développement d'applications SaaS, en informant les développeurs et les ingénieurs DevOps des problÚmes et pratiques que l'on retrouve le plus souvent dans le développement d'applications modernes.
Le document a été élaboré par les développeurs de la plateforme Heroku.
La mĂ©thodologie des douze facteurs (The Twelve-Factor App) peut ĂȘtre appliquĂ©e aux applications Ă©crites dans n'importe quel langage de programmation et utilisant n'importe quelle combinaison de services tiers (backing services) (bases de donnĂ©es, files d'attente, cache, etc.).
Un bref aperçu des facteurs qui constituent cette méthodologie :
- Base de code â Une base de code unique, suivie dans un systĂšme de contrĂŽle de version, â de nombreux dĂ©ploiements
- DĂ©pendances â DĂ©clarez clairement et isolez les dĂ©pendances
- Configuration â Conservez la configuration dans l'environnement d'exĂ©cution
- Services externes (Backing Services) â ConsidĂ©rez les services externes (backing services) comme des ressources connectĂ©es
- Build, release, run â SĂ©parez strictement les Ă©tapes de construction et d'exĂ©cution
- Processus â ExĂ©cutez l'application comme un ou plusieurs processus sans Ă©tat (stateless)
- Liaison de ports (Port binding) â Exportez des services via la liaison de ports
- ParallĂ©lisme â Ăvoluez l'application Ă l'aide de processus
- DisposabilitĂ© (Disposability) â Maximisez la fiabilitĂ© grĂące Ă un dĂ©marrage rapide et Ă une consommation correcte
- ParitĂ© entre le dĂ©veloppement et l'exploitation de l'application â Maintenez les environnements de dĂ©veloppement, de prĂ©production (staging) et de production aussi similaires que possible
- Journalisation (Logs) â ConsidĂ©rez le journal comme un flux d'Ă©vĂ©nements
- TĂąches d'administration â ExĂ©cutez les tĂąches d'administration/gestion Ă l'aide de processus ponctuels
Pour plus d'informations sur les 12 facteurs, vous pouvez consulter les ressources suivantes :
- â lecture incontournable
- â traduction officielle
- â un nouveau regard sur les 12 facteurs dans le but de les amĂ©liorer.
Qu'est-ce que le déploiement Blue-Green ?
Le dĂ©ploiement Blue-Green est une mĂ©thode de livraison d'application production Ainsi, le client final ne voit aucun changement de son cĂŽtĂ©. En d'autres termes, le dĂ©ploiement de l'application est sans temps d'arrĂȘt.
Le schéma classique de BG Deploy ressemble à ce qui est indiqué dans l'image ci-dessous.

- Au dĂ©part, il y a 2 serveurs physiques avec exactement le mĂȘme code, application, projet, et un routeur (Ă©quilibreur de charge).
- Le routeur dirige initialement toutes les requĂȘtes vers l'un des serveurs (bleu).
- Au moment oĂč il est nĂ©cessaire de relancer une version, l'ensemble du projet est mis Ă jour sur l'autre serveur (alpha), qui Ă ce moment-lĂ ne traite aucune requĂȘte.
- AprÚs que le code sur le serveur bleu soit complÚtement mis à jour, le routeur reçoit l'instruction de passer de vert sur alpha un serveur.
- Maintenant, tous les clients voient le résultat du code de bleu serveur.
- Pendant un certain temps, bleu le serveur sert de sauvegarde en cas de dĂ©ploiement ratĂ© sur alpha le serveur et en cas d'Ă©chec et de bogues, le routeur redirige le flux dâutilisateurs vers bleu le serveur avec l'ancienne version stable, tandis que le nouveau code est envoyĂ© pour rĂ©visions et tests.
- Et Ă la fin du processus, le bleu serveur est Ă©galement mis Ă jour. Et aprĂšs sa mise Ă jour, le routeur redirige de nouveau le flux de requĂȘtes vers bleu un serveur.
Cela semble trĂšs bien et Ă premiĂšre vue, il ne devrait y avoir aucun problĂšme avec cela.
Mais comme nous vivons dans un monde moderne, l'option de basculement physique comme indiquée dans le schéma classique ne nous convient pas. Gardez cette information à l'esprit, nous y reviendrons plus tard.
Conseils bons et mauvais
Avertissement: Dans les exemples ci-dessous, des utilitaires / méthodologies que j'utilise sont mentionnés, vous pouvez utiliser absolument toutes les alternatives avec des fonctions similaires.
La plupart des exemples se croiseront d'une maniÚre ou d'une autre avec le développement web (surprise !), avec PHP et Docker.
Les points suivants contiennent une description pratique simple de l'utilisation des facteurs dans des exemples spécifiques, si vous souhaitez obtenir plus de théories sur ce sujet, référez-vous aux liens ci-dessus vers la source d'origine.
1. Base de code
Utilisez FTP et FileZilla pour télécharger des fichiers sur les serveurs un à un, ne stockez le code nulle part sauf sur le serveur de production.
Le projet doit toujours avoir une base de code unique, c'est-à -dire que tout le code provient d'une seule source. Git d'un référentiel. Les serveurs (production, staging, test1, test2, etc.) utilisent le code des branches d'un référentiel commun. Ainsi, nous atteignons la cohérence du code.
2. Dépendances
TĂ©lĂ©chargez toutes les bibliothĂšques dans des dossiers directement Ă la racine du projet. Mettez Ă jour simplement en transfĂ©rant le nouveau code dans le dossier de la version actuelle de la bibliothĂšque. Installez tous les utilitaires directement sur le serveur hĂŽte oĂč fonctionnent dĂ©jĂ 20 services.
Le projet doit toujours avoir une liste de dĂ©pendances clairement dĂ©finie (par dĂ©pendances, j'entends aussi l'environnement). Toutes les dĂ©pendances doivent ĂȘtre clairement dĂ©finies et isolĂ©es.
Ă titre d'exemple, prenons Composer et Docker.
Composer â un gestionnaire de paquets qui permet d'installer des bibliothĂšques PHP. Composer permet de spĂ©cifier les versions de maniĂšre stricte ou non, et de les dĂ©finir clairement. Sur le serveur, il peut y avoir 20 projets diffĂ©rents, chacun ayant sa propre liste de paquets et de bibliothĂšques, indĂ©pendante des autres.
Docker â un utilitaire qui permet de dĂ©finir et d'isoler l'environnement dans lequel l'application va fonctionner. De mĂȘme que pour Composer, mais de maniĂšre plus approfondie, nous pouvons dĂ©finir ce avec quoi l'application travaille. Choisir une version spĂ©cifique de PHP, installer uniquement les paquets nĂ©cessaires au fonctionnement du projet, sans ajouter de composants superflus. Et surtout, ne pas entrer en conflit avec les paquets et l'environnement de la machine hĂŽte et d'autres projets. Ainsi, tous les projets sur le serveur fonctionnant via Docker peuvent utiliser n'importe quel ensemble de paquets et un environnement totalement diffĂ©rent.
3. Configuration
Conservez les configurations sous forme de constantes directement dans le code. Des constantes distinctes pour le serveur de test, et d'autres pour la production. Liez le fonctionnement de l'application en fonction de l'environnement directement dans la logique métier du projet en utilisant des constructions if else.
Configurations â c'est la seule chose qui doit varier entre les dĂ©ploiements du projet. IdĂ©alement, les configurations devraient ĂȘtre transmises via des variables d'environnement (env vars).
Ainsi, mĂȘme si vous conservez plusieurs fichiers de configuration .config.prod .config.local et que vous les renommez au moment du dĂ©ploiement en .config (la configuration principale Ă partir de laquelle l'application lit les donnĂ©es), cela ne serait pas une approche correcte, car dans ce cas, les informations des configurations seraient accessibles Ă tous les dĂ©veloppeurs de l'application et les donnĂ©es du serveur de production seraient compromises. Toutes les configurations doivent ĂȘtre stockĂ©es directement dans le systĂšme de dĂ©ploiement (CI/CD) et gĂ©nĂ©rĂ©es pour diffĂ©rents environnements avec diffĂ©rentes valeurs nĂ©cessaires pour un environnement spĂ©cifique au moment du dĂ©ploiement.
4. Services externes (Backing Services)
Soyez rigidement liĂ© Ă l'environnement, utilisez diffĂ©rentes connexions pour les mĂȘmes services dans des environnements spĂ©cifiques.
En réalité, ce point chevauche fortement celui concernant les configurations, car sans ce point, il est impossible de créer des données de configuration normales et la possibilité de configuration disparaßtra complÚtement.
Toutes les connexions aux services externes, tels que les serveurs de files d'attente, bases de donnĂ©es, services de mise en cache, doivent ĂȘtre uniformes pour l'environnement local ainsi que pour l'environnement externe / de production. En d'autres termes, je peux Ă tout moment changer une chaĂźne de connexion pour remplacer les appels Ă la base #1 par ceux Ă la base #2 sans modifier le code de l'application. Ou, en anticipant un exemple, lors de la mise Ă l'Ă©chelle du service, vous n'aurez pas besoin d'indiquer une connexion d'une maniĂšre particuliĂšre pour un serveur de cache supplĂ©mentaire.
5. Construction, version, exécution
Ayez uniquement la version finale du code sur le serveur, sans possibilité de revenir en arriÚre sur la version précédente. Ne gaspillez pas d'espace disque. Qui pense qu'il peut déployer du code en production avec une erreur est un mauvais programmeur !
Toutes les Ă©tapes de dĂ©ploiement doivent ĂȘtre sĂ©parĂ©es les unes des autres.
Ayez la possibilitĂ© de revenir en arriĂšre. RĂ©alisez des versions en conservant d'anciennes copies de l'application (dĂ©jĂ compilĂ©es et prĂȘtes pour le combat) en accĂšs rapide, afin qu'en cas d'erreurs, vous puissiez restaurer l'ancienne version. Donc, il y a conditionnellement un dossier releases et un dossier courant, et aprĂšs un dĂ©ploiement et une compilation rĂ©ussis, le dossier courant est liĂ© par un lien symbolique Ă la nouvelle version qui se trouve Ă l'intĂ©rieur releases d'un nom conditionnel de numĂ©ro de version.
C'est ici que nous rappelons le déploiement Blue-Green, qui permet non seulement de basculer entre les versions de code, mais aussi de changer tous les ressources et environnements avec la capacité de revenir en arriÚre.
6. Processus
Conservez les donnĂ©es d'Ă©tat de l'application directement dans l'application elle-mĂȘme. Utilisez des sessions dans la mĂ©moire vive de l'application. Utilisez autant que possible les Ă©lĂ©ments partagĂ©s entre les services tiers. Engagez-vous sur le fait que l'application ne peut avoir qu'un seul processus et n'autorisez pas la possibilitĂ© de mise Ă l'Ă©chelle.
Concernant les sessions, conservez les donnĂ©es uniquement dans le cache contrĂŽlĂ© par des services tiers (memcached, redis), de sorte que mĂȘme si vous avez 20 processus de l'application en cours d'exĂ©cution, chacun d'eux peut accĂ©der au cache et continuer Ă travailler avec le client dans le mĂȘme Ă©tat que l'utilisateur l'Ă©tait en utilisant l'application dans un autre processus. Avec cette approche, peu importe combien de copies des services tiers vous utilisez, tout fonctionnera normalement sans problĂšme d'accĂšs aux donnĂ©es.
7. Liaison des ports (Port binding)
Seul le serveur web doit savoir comment travailler avec les services tiers. Il est mĂȘme prĂ©fĂ©rable de lancer les services tiers directement Ă l'intĂ©rieur du serveur web. Par exemple, comme un module PHP dans Apache.
Tous vos services doivent ĂȘtre accessibles les uns aux autres via une adresse et un port quelconque (localgost:5432, localhost:3000, nginx:80, php-fpm:9000), c'est-Ă -dire qu'Ă partir de nginx, je peux accĂ©der Ă php-fpm, ainsi qu'Ă postgres, et Ă partir de php-fpm, accĂ©der Ă postgres et nginx, et en fait, chaque service peut accĂ©der Ă un autre service. De cette maniĂšre, la viabilitĂ© d'un service n'est pas liĂ©e Ă celle d'un autre service.
8. Parallélisme
Travaillez avec un seul processus, car plusieurs processus pourraient ne pas s'entendre !
Laissez la possibilité de mise à l'échelle. Docker Swarm est parfait pour cela.
Docker Swarm est un outil pour créer et gérer des clusters de conteneurs, tant entre différentes machines qu'un grand nombre de conteneurs sur une seule machine.
En utilisant swarm, je peux dĂ©terminer combien de ressources je vais allouer Ă chaque processus et combien de processus d'un mĂȘme service je vais lancer, tandis que le rĂ©partiteur interne, en recevant des donnĂ©es sur le port dĂ©signĂ©, va automatiquement les proxy vers les processus. Ainsi, en voyant que la charge sur le serveur a augmentĂ©, je peux ajouter plus de processus, rĂ©duisant ainsi la charge sur certains processus.
9. ĂliminabilitĂ© (Disposability)
N'utilisez pas de files d'attente pour travailler avec des processus et des donnĂ©es. Tuer un processus doit influencer le fonctionnement de l'ensemble de l'application. Si un service s'arrĂȘte, tout s'arrĂȘte.
Chaque processus et service peut ĂȘtre arrĂȘtĂ© Ă tout moment et cela ne doit pas perturber les autres services (il ne s'agit pas que le service sera indisponible pour un autre service, mais que l'autre service ne s'arrĂȘtera pas en raison de celui-ci). Tous les processus doivent se terminer en douceur, de maniĂšre Ă ce que, lors de leur arrĂȘt, les donnĂ©es ne soient pas endommagĂ©es et que, lors de la prochaine activation, le systĂšme fonctionne correctement. MĂȘme en cas d'arrĂȘt d'urgence, les donnĂ©es ne doivent pas ĂȘtre compromises (un mĂ©canisme de transactions convient ici, les requĂȘtes Ă la base de donnĂ©es fonctionnent uniquement par groupes, et si une seule requĂȘte du groupe Ă©choue ou s'exĂ©cute avec une erreur, aucune autre requĂȘte du groupe n'est exĂ©cutĂ©e en rĂ©alitĂ©).
10. Parité entre le développement et le fonctionnement de l'application
La version de production, la version de staging et la version locale de l'application doivent ĂȘtre diffĂ©rentes. En production, nous avons le framework Yii Lite, tandis qu'en local nous avons Yii, pour que cela fonctionne plus rapidement en production !
En réalité, tous les déploiements et le travail avec le code doivent se faire dans des environnements presque identiques (il ne s'agit pas de matériel physique). De plus, n'importe quel membre de l'équipe de développement doit pouvoir déployer le code en production si nécessaire, et non pas uniquement une équipe de devops spécialement formée, qui peut mettre en production l'application grùce à une force particuliÚre.
Docker nous aide également dans cette tùche. Si toutes les précédentes exigences sont respectées, l'utilisation de Docker rendra le processus de déploiement de l'environnement aussi bien en production que sur la machine locale aussi simple que de taper une ou deux commandes.
11. Journalisation (Logs)
Nous écrivons les journaux dans des fichiers et des bases de données ! Nous ne nettoyons pas les fichiers et les bases de données des journaux. Nous allons simplement acheter un disque dur de 9000 pétaoctets et c'est bon.
Tous les logs doivent ĂȘtre considĂ©rĂ©s comme un flux d'Ă©vĂ©nements. L'application elle-mĂȘme ne doit pas traiter les logs. Les logs doivent ĂȘtre Ă©mis soit en stdout, soit envoyĂ©s via un protocole tel que udp, afin que la gestion des logs par l'application ne pose aucun problĂšme. Graylog convient bien Ă cela. Graylog, en recevant tous les logs via udp (car avec ce protocole, il n'est pas nĂ©cessaire d'attendre une confirmation de rĂ©ception rĂ©ussie du paquet), ne dĂ©range l'application d'aucune maniĂšre et se charge uniquement de la structuration et du traitement des logs. La logique de l'application ne change pas pour travailler avec de telles approches.
12. TĂąches d'administration
Pour mettre Ă jour les donnĂ©es, la base de donnĂ©es, etc., utilisez un endpoint créé sĂ©parĂ©ment dans l'API ; l'exĂ©cution de celui-ci deux fois de suite peut entraĂźner un doublement de tout. Mais vous n'ĂȘtes pas idiots, vous ne cliquerez pas deux fois, et les migrations ne nous sont pas nĂ©cessaires.
Toutes les tĂąches d'administration doivent ĂȘtre exĂ©cutĂ©es dans le mĂȘme environnement que tout le code, au niveau des versions. Donc, si nous devons modifier la structure de la base de donnĂ©es, nous ne ferons pas cela manuellement en changeant les noms des colonnes et en ajoutant de nouvelles via des outils de gestion de base de donnĂ©es visuels. Pour ces choses, nous crĂ©ons des scripts sĂ©parĂ©s - des migrations, qui sont exĂ©cutĂ©es partout et dans tous les environnements avec un rĂ©sultat commun et comprĂ©hensible. Pour toutes les autres tĂąches, comme le remplissage d'un projet avec des donnĂ©es, des mĂ©thodologies similaires doivent ĂȘtre appliquĂ©es.
Exemple d'implémentation en PHP, Laravel, Laradock, Docker-Compose
P.S. Tous les exemples ont été réalisés sur MacOS. La majeure partie convient aussi pour Linux. Les utilisateurs de Windows, désolé, mais je n'ai pas travaillé avec Windows depuis longtemps.
Imaginons une situation oĂč aucune version de PHP n'est installĂ©e sur notre PC et oĂč il n'y a rien du tout.
Nous installons les derniĂšres versions de docker et docker-compose. (cela peut ĂȘtre trouvĂ© sur Internet)
docker -v &&
docker-compose -v

1. Installons
git clone https://github.com/Laradock/laradock.git &&
ls

Ă propos de Laradock, je dirais que c'est un outil fantastique, rassemblant de nombreux conteneurs et outils auxiliaires. Mais utiliser Laradock tel quel en production sans modifications - je ne le recommanderais pas en raison de son excĂšs. Il vaut mieux crĂ©er ses propres conteneurs en se basant sur les exemples de Laradock, car cela permet d'optimiser, car personne n'a besoin de tout ce qui s'y trouve en mĂȘme temps.
2. Configurons Laradock pour le fonctionnement de notre application.
cd laradock &&
cp env-example .env

2.1. Ouvrir le répertoire habr (dossier parent dans lequel laradock a été cloné) dans n'importe quel éditeur. (Dans mon cas, PHPStorm)
à ce stade, nous définissons uniquement le nom du projet.

2.2. Démarrez l'image de workspace. (Dans votre cas, les images mettront un certain temps à se construire)
Workspace est une image spécialement préparée pour travailler avec le framework au nom du développeur.
Accédez au conteneur à l'aide de
docker-compose up -d workspace &&
docker-compose exec workspace bash

2.3. Installer Laravel
composer create-project --prefer-dist laravel/laravel application 
2.4. AprĂšs l'installation, vĂ©rifiez si le rĂ©pertoire du projet a Ă©tĂ© créé, puis arrĂȘtez compose.
ls
exit
docker-compose down

2.5. Retournez dans PHPStorm et mettez le chemin correct vers notre application Laravel dans le fichier .env.

3. Ajoutons tout le code dans Git.
Pour ce faire, créons un référentiel sur Github (ou ailleurs). Allons dans le terminal dans le répertoire habr et exécutons le code suivant.
echo "# habr-12factor" >> README.md
git init
git add README.md
git commit -m "premier commit"
git remote add origin git@github.com:nzulfigarov/habr-12factor.git # ici sera le lien vers votre repo
git push -u origin master
git status
Vérifions que tout va bien.

Pour plus de commodité, je recommande d'utiliser une interface graphique pour Git, dans mon cas c'est . (lien référentiel ici)
4. Démarrez !
Avant de démarrer, assurez-vous qu'il n'y a rien qui tourne sur les ports 80 et 443.
docker-compose up -d nginx php-fpm 
Ainsi, notre projet se compose de 3 services distincts :
- nginx â serveur web
- php-fpm â php pour recevoir des requĂȘtes du serveur web
- workspace â php pour le dĂ©veloppeur
Pour l'instant, nous sommes parvenus à créer une application conforme à 4 des 12 points, à savoir :
1. Base de code â tout le code est dans un seul rĂ©fĂ©rentiel (petite remarque : il peut ĂȘtre judicieux d'intĂ©grer docker dans le projet Laravel, mais ce n'est pas fondamental).
2. DĂ©pendances â Toutes nos dĂ©pendances sont clairement Ă©noncĂ©es dans application/composer.json et dans chaque Dockerfile de chaque conteneur.
3. Services externes (Backing Services) â Chacune des services (php-fpm, nginx, workspace) vit sa propre vie et est connectĂ©e de l'extĂ©rieur, et en travaillant avec un service, les autres ne seront pas affectĂ©s.
4. Processus â chaque service est un processus unique. Chacune des services ne conserve pas d'Ă©tat interne.
5. Liaison de ports (Port binding)
docker ps

Comme nous le voyons, chaque service est lancé sur son propre port et est accessible à tous les autres services.
6. Parallélisme
Docker nous permet de lancer plusieurs processus des mĂȘmes services avec un Ă©quilibrage de charge automatique entre eux.
ArrĂȘtons les conteneurs et relançons-les avec le drapeau âscale
docker-compose down &&
docker-compose up -d --scale php-fpm=3 nginx php-fpm

Comme nous le voyons, des copies du conteneur php-fpm ont été créées. Dans notre travail avec ce conteneur, nous n'avons rien à changer. Nous continuons également à y accéder via le port 9000, et Docker gÚre la charge entre les conteneurs pour nous.
7. DisposabilitĂ© (Disposability) â chaque conteneur peut ĂȘtre arrĂȘtĂ© sans nuire aux autres. L'arrĂȘt ou le redĂ©marrage d'un conteneur n'affectera en rien le fonctionnement de l'application lors des lancements suivants. Chaque conteneur peut Ă©galement ĂȘtre dĂ©marrĂ© Ă tout moment.
8. ParitĂ© entre le dĂ©veloppement et l'exploitation de l'application â tous nos environnements sont identiques. En lançant le systĂšme sur un serveur en production, vous n'aurez rien Ă changer dans vos commandes. Tout sera exactement comme basĂ© sur Docker.
9. Journalisation (Logs) â tous les logs de ces conteneurs sortent en flux et sont visibles dans la console Docker. (dans ce cas, en rĂ©alitĂ©, avec d'autres conteneurs faits maison, cela peut ne pas ĂȘtre le cas si vous ne vous en occupez pas)
docker-compose logs -f 
Mais il y a un hic, car les valeurs par défaut en PHP et Nginx enregistrent également les logs dans un fichier. Pour respecter les 12 facteurs, il est nécessaire de désactiver l'enregistrement des logs dans un fichier dans les configurations de chaque conteneur séparément.
Docker offre également la possibilité de diriger les logs non seulement vers stdout, mais aussi vers des outils comme graylog dont j'ai parlé ci-dessus. Et à l'intérieur de graylog, nous pouvons manipuler les logs comme nous le souhaitons, et notre application ne s'en apercevra en aucune maniÚre.
10. TĂąches d'administration â toutes les tĂąches d'administration sont rĂ©solues par Laravel grĂące Ă l'outil artisan exactement comme les crĂ©ateurs de l'application Ă 12 facteurs l'auraient souhaitĂ©.
à titre d'exemple, je vais montrer comment certaines commandes sont exécutées.
Entrons dans le conteneur.
docker-compose exec workspace bash
php artisan list

Nous pouvons maintenant utiliser n'importe quelle commande. (notez que nous n'avons pas configuré la base de données et le cache, donc la moitié des commandes ne s'exécuteront pas correctement, car elles sont destinées à fonctionner avec le cache et la base de données).

11. Configurations et 12. Build, release, run
Je voulais consacrer cette partie au Blue-Green Deployment, mais cela s'est avéré trop développé pour cet article. J'écrirai un article séparé à ce sujet.
En deux mots, le concept repose sur des systÚmes CI/CD comme Jenkins et Gitlab CI. Dans les deux cas, il est possible de définir des variables d'environnement liées à un environnement spécifique. Par conséquent, dans ce cas, le point concernant les configurations.
Et le point concernant Build, release, run est résolu par des fonctions intégrées dans les deux outils, appelées Pipeline.
Pipeline permet de diviser le processus de déploiement en plusieurs étapes, en mettant en avant les phases de construction, de mise en production et d'exécution. Dans le Pipeline, vous pourrez également créer des sauvegardes, et en fait, tout ce que vous voulez. Cet outil a un potentiel illimité.
Le code de l'application est sur .
N'oubliez pas d'initialiser le submodule lors du clonage de ce dépÎt.
P.S. : Toutes ces approches peuvent ĂȘtre utilisĂ©es avec d'autres utilitaires et langages de programmation. L'essentiel est que le principe reste le mĂȘme.
Source : habr.com
