#Livraison continue

0 Abonnés · 15 Publications

La livraison continue (CD) est une approche d'ingénierie logicielle dans laquelle les équipes produisent des logiciels en cycles courts, garantissant que le logiciel peut être publié de manière fiable à tout moment. Son objectif est de créer, tester et publier des logiciels plus rapidement et plus fréquemment..

Article Lorenzo Scalese · Oct 28, 2025 10m read

Le déploiement de nouvelles instances IRIS peut être une tâche fastidieuse, en particulier lors de la mise en place de plusieurs environnements avec des configurations en miroir.

J'ai fait face à ce problème très souvent et je souhaite partager mon expérience et mes recommandations concernant l'utilisation d'Ansible pour rationaliser le processus d'installation d'IRIS. Mon approche inclut également la gestion des tâches supplémentaires généralement effectuées avant et après l'installation d'IRIS.

Ce manuel suppose que vous disposez d'une compréhension de base du fonctionnement d'Ansible, je ne détaillerai donc pas ses principes fondamentaux. Toutefois, si vous avez des questions sur les points abordés ici, n'hésitez pas à les poser dans les commentaires ci-dessous.

Les exemples fournis dans ce manuel ont été testés à l'aide d' Ansible 3.6 sur un serveur Red Hat 8, avec IRIS 2023.1.1 et Red Hat 8 comme environnement client. D'autres versions d'Ansible, de Red Hat (ou d'autres variantes d'UNIX) et d'IRIS peuvent également fonctionner, mais les résultats peuvent varier.

Installation d'Ansible

Le serveur Ansible nécessite une distribution Linux. Nous utilisons Red Hat 8 dans cet article, mais d'autres distributions et versions Linux devraient également fonctionner.

Pour installer les paquets Ansible, il faut d'abord installer EPEL:

[ansible@auto01 ansible]$ yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm

Ensuite, il faut installer Ansible:

[ansible@auto01 ansible]$ yum install ansible

En plus des paquets, Ansible nécessite un accès SSH aux serveurs distants. Je recommande de créer une paire de clés SSH, ce qui est plus sûr que l'utilisation de mots de passe traditionnels. De plus, l'utilisateur servant à se connecter aux serveurs distants doit disposer de privilèges administratifs (c'est-à-dire faire partie du groupe wheel).

Fichiers et dossiers

Pour préserver une structure organisée, je recommande les fichiers et dossiers suivants dans le répertoire ansible:

[ansible@auto01 ansible]$ ls -l
total 4
-rw-r--r--. 1 ansible ansible 247 Dec  500:57 ansible.cfg
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 files
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 inventory
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 library
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 playbooks
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 templates
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 vars
drwxrwxr-x. 2 ansible ansible   6 Dec  500:56 vault
Fichier/DossierDescription
ansible.cfgFichier de configuration d'Ansible. Contient des directives sur le comportement d'Ansible.
filesContient les fichiers supplémentaires nécessaires aux playbooks, tels que le fichier tar.gz d'installation d'IRIS.
inventoryContient les fichiers d'inventaire de l'hôte. Vous pouvez avoir un seul fichier d'inventaire volumineux ou plusieurs fichiers de moindre taille. Le fractionnement de l'inventaire nécessite davantage d'efforts lorsque vous exécutez des playbooks sur plusieurs hôtes.
libraryContient des fichiers de bibliothèque supplémentaires d'Ansible. Non requis pour ces exemples, mais utile pour de futures extensions.
playbooksContient tous les playbooks développés, y compris le playbook d'installation IRIS décrit ci-dessous.
templatesContient les fichiers modèles utilisés par les playbooks. Ceux-ci sont transférés vers les serveurs et instanciés avec les paramètres corrects.
varsContient les variables disponibles pour tous les playbooks.
vaultContient des variables sensibles accessibles uniquement via la commande ansible-vault. Utile pour gérer les mots de passe.

 

Après avoir configuré cette structure de dossiers, copiez le programme d'installation IRIS et la clé de licence IRIS dans le dossier files. Le résultat devrait apparaître comme suit:

[ansible@auto01 ansible]$ ls -l files/
total 759976
-rw-rw-r--. 1 ansible ansible 778207913 Dec  514:32 IRISHealth-2023.1.1.380.0.22870-lnxrh8x64.tar.gz
-rw-rw-r--. 1 ansible ansible      1160 Sep  519:13 iris.key

 

Inventaire

Pour exécuter des playbooks dans Ansible, il est nécessaire de définir l'inventaire des serveurs. Il existe plusieurs méthodes pour ce faire, et chacune présente ses propres avantages. Dans cet article, nous utiliserons un seul fichier pour définir tous les serveurs.

Le ficher servers.yml contiendra l'inventaire complet, répertoriant chaque serveur ainsi que les variables requises pour l'installation d'IRIS. Voici un exemple:

[ansible@auto01ansible]$catinventory/servers.yml 
---all:  hosts:test01.mydomain:      iris_user:irisusr      iris_group:irisgrp      mgr_user:irisown      mgr_group:irismgr      platform:lnxrh8x64      iris_cmd:iris      iris_instances:        - name:TEST01          superserver_port:51773          webserver_port:52773          binary_file:IRISHealth-2023.1.1.380.0.22870-lnxrh8x64          key_file:iris.key          install_dir:/test/iris          jrnpri_dir:/test/jrnpri          jrnsec_dir:/test/jrnsec          config_globals:16384          config_errlog:10000          config_routines:"0,128,0,128,0,1024"          config_gmheap:1048576          config_locksiz:128057344

 

Fichier coffre-fort

Pour sécuriser les mots de passe, créez un fichier coffre-fort contenant les mots de passe des comptes IRIS SuperUser et CSPSystem.

Pour modifier le fichier coffre-fort par défaut, utilisez la commande suivante:

[ansible@auto01ansible]$ansible-vaulteditvault/defaults.yml---# Default passwordsiris_user_passwd:"Ch4ngeTh!s"

 

Playbook

Pour effectuer une installation IRIS, il est nécessaire d'exécuter plusieurs tâches sur le serveur cible. Ces tâches sont regroupées et classées dans un fichier appelé playbook.
Un playbook consiste essentiellement en une liste de tâches qui sont exécutées de manière séquentielle sur les hôtes distants.

Vous trouverez ci-dessous le playbook que j'ai développé pour installer IRIS:

[ansible@auto01ansible]$catplaybooks/install_iris.yml## Playbook to install Iris#- hosts:all  become:yes  gather_facts:no  tasks:  - name:"Load default passwords"    include_vars:"../vault/defaults.yml"### PRE-INSTALL TASKS:  - name:"Install required packets"    yum:      name:"{{ item }}"      state:latest    loop:      -"httpd"      -"java-1.8.0-openjdk"      -"mod_auth_mellon"      -"mod_ssl"  - name:"Create iris group"    group:      name:"{{ iris_group }}"      gid:5005  - name:"Create iris mgr group"    group:      name:"{{ mgr_group }}"      gid:5006  - name:"Create iris owner user"    user:      name:"{{ mgr_user }}"      uid:5006      group:"{{ iris_group }}"      groups:"{{ mgr_group }}"  - name:"Create iris user"    user:      name:"{{ iris_user }}"      uid:5005      group:"{{ iris_group }}"  - name:"Create mgr folder"    file:      path:"{{ item.install_dir }}/mgr"      state:directory      owner:"{{ iris_user }}"      group:"{{ iris_group }}"      mode:0775    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Copy license key"    copy:      src:"../files/{{ item.key_file }}"      dest:"{{ item.install_dir }}/mgr/iris.key"      owner:"{{ iris_user }}"      group:"{{ iris_group }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Create /install folder"    file:      path:"/install"      state:directory      mode:0777  - name:"Create Instances install folders"    file:      path:"/install/{{ item.name }}"      state:directory      mode:0777    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Copy IRIS installer"    copy:      src:"../files/{{ item.binary_file }}.tar.gz"      dest:"/install/{{ item.name }}/"    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Untar IRIS installer"    command:      cmd:"tar -xzf /install/{{ item.name }}/{{ item.binary_file }}.tar.gz"      chdir:"/install/{{ item.name }}/"    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"### IRIS INSTALL:  - name:"Install Iris"    command:      cmd:"./irisinstall_silent"      chdir:"/install/{{ item.name }}/{{ item.binary_file }}"    environment:      ISC_PACKAGE_INSTANCENAME:"{{ item.name }}"      ISC_PACKAGE_INSTALLDIR:"{{ item.install_dir }}"      ISC_PACKAGE_PLATFORM:"{{ platform }}"      ISC_PACKAGE_UNICODE:"Y"      ISC_PACKAGE_INITIAL_SECURITY:"Normal"      ISC_PACKAGE_MGRUSER:"{{ mgr_user }}"      ISC_PACKAGE_MGRGROUP:"{{ mgr_group }}"      ISC_PACKAGE_USER_PASSWORD:"{{ iris_user_passwd }}"      ISC_PACKAGE_CSPSYSTEM_PASSWORD:"{{ iris_user_passwd }}"      ISC_PACKAGE_IRISUSER:"{{ iris_user }}"      ISC_PACKAGE_IRISGROUP:"{{ iris_group }}"      ISC_PACKAGE_SUPERSERVER_PORT:"{{ item.superserver_port }}"      ISC_PACKAGE_WEBSERVER_PORT:"{{ item.webserver_port }}"      ISC_PACKAGE_CLIENT_COMPONENTS:"standard_install"      ISC_PACKAGE_STARTIRIS:"N"    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Remove installers"    file:      path:"/install/{{ item.name }}"      state:absent    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"### IRIS CUSTOMIZATIONS:  - name:"Change iris.cpf"    lineinfile:      path:"{{ item[0].install_dir }}/iris.cpf"      regexp:"{{ item[1].from }}"      line:"{{ item[1].to }}"      backup:yes    with_nested:      -"{{ iris_instances }}"      -[{from:"^TerminalPrompt=.*",to:"TerminalPrompt=8,3,2"},{from:"^FreezeOnError=0",to:"FreezeOnError=1"},{from:"^AutoParallel=.*",to:"AutoParallel=0"},{from:"^FastDistinct=.*",to:"FastDistinct=0"},{from:"^LockThreshold=.*",to:"LockThreshold=10000"},{from:"^EnsembleAutoStart=.*",to:"EnsembleAutoStart=1"},{from:"^MaxIRISTempSizeAtStart=.*",to:"MaxIRISTempSizeAtStart=300"}]    loop_control:      label:"{{ item[0].name }}: {{ item[1].to }}"  - name:"Change Journal Current Dir"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^CurrentDirectory=.*"      line:"CurrentDirectory={{ item.jrnpri_dir }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change Journal Alternate Dir"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^AlternateDirectory=.*"      line:"AlternateDirectory={{ item.jrnsec_dir }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change Journal Prefix name"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^JournalFilePrefix=.*"      line:"JournalFilePrefix={{ item.name }}_"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change Globals memory"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^globals=.*"      line:"globals=0,0,{{ item.config_globals }},0,0,0"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change errlog memory"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^errlog=.*"      line:"errlog={{ item.config_errlog }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change routines memory"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^routines=.*"      line:"routines={{ item.config_routines }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change gmheap memory"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^gmheap=.*"      line:"gmheap={{ item.config_gmheap }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"  - name:"Change locksiz memory"    lineinfile:      path:"{{ item.install_dir }}/iris.cpf"      regexp:"^locksiz=.*"      line:"locksiz={{ item.config_locksiz }}"      backup:yes    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"### START IRIS:  - name:"Start Iris"    command:"iris start {{ item.name }}"    loop:"{{ iris_instances }}"    loop_control:      label:"{{ item.name }}"...

Comme vous pouvez le constater, ce playbook comporte plusieurs tâches, dont la plupart sont explicites d'après leur nom. Les commentaires indiquent les tâches à effectuer avant l'installation, l'installation proprement dite et les personnalisations à effectuer après l'installation. Après avoir exécuté ce playbook, vous disposerez d'une nouvelle instance IRIS installée sur le système cible, dont la mémoire et d'autres paramètres auront été personnalisés.

Lancez l'installation!

Après avoir configuré l'inventaire, le ficheir coffre-fort et les playbooks, vous êtes prêt à exécuter l'installation IRIS à l'aide d'Ansible.
Pour ce faire, exécutez la commande suivante:

[ansible@auto01 ansible]$ ansible-playbook -K --ask-vault-pass -i inventory/servers.yml playbooks/install_iris.yml
BECOME password: 
Vault password: 

PLAY [all] ************************************************************************************************************************************************** . . .

Lorsque l'exécution du playbook est terminée, vous recevez un résumé de statuts des tâches qui vous permet de vérifier que tout a été exécuté avec succès.

Et voilà, vous venez d'installer IRIS à l'aide d'Ansible! 😁

0
0 14
Article Sylvain Guilbaud · Oct 7, 2025 5m read

Aperçu Je suis ravi d'annoncer la sortie de testcontainers-iris-node, une bibliothèque Node.js qui facilite le lancement de conteneurs InterSystems IRIS temporaires pour l'intégration et les tests E2E. Ce projet vient naturellement compléter la gamme existante d'adaptateurs Testcontainers pour IRIS, notamment testcontainers-iris-python et testcontainers-iris-java.

Pourquoi testcontainers-iris-node? En tant que développeur Node.js travaillant avec InterSystems IRIS, j'ai souvent été confronté à des difficultés lors de la configuration d'environnements de test imitant la production. testcontainers-iris-node résout ce problème en exploitant le framework testcontainers-node pour créer des environnements IRIS isolés à la demande.

Ceci est particulièrement important pour:

  • Les tests d'intégration avec les bases de données IRIS
  • Les tests de pipelines de données ou de microservices
  • L'automatisation des environnements de test dans les pipelines CI

Fonctionnalités

  • Lancement d'IRIS dans des conteneurs Docker à l'aide de Testcontainers
  • Prise en charge des images et de la configuration Docker personnalisées
  • Stratégies d'attente pour s'assurer qu'IRIS est prêt avant le début des tests
  • Désinstallation de nettoyage entre les exécutions de tests

Pour commencer

npm install testcontainers-iris --save-dev

Exemple d'utilisation

import { IRISContainer } from"testcontainers-iris";
import { createConnection } from"@intersystems/intersystems-iris-native";

const IMAGE = "containers.intersystems.com/intersystems/iris-community:latest-preview"; const container = awaitnew IRISContainer(IMAGE).start(); const connection = createConnection(container.getConnectionOptions()); const iris = connection.createIris(); const version = iris.classMethodString("%SYSTEM.Version", "GetNumber");

Fonctionnement En interne, la bibliothèque étend GenericContainer à partir de testcontainers, ajoute des stratégies d'attente spécifiques à IRIS et fournit des méthodes auxiliaires pour la génération de chaînes de connexion et le remplacement des configurations.

Scénarios pris en charge

  • Suites de tests basées sur Jest ou Mocha
  • Environments CI (GitHub Actions, GitLab CI, Jenkins, etc.)
  • Développement et débogage locaux

Exemple de tests basées sur Mocha Vous pouvez également utiliser cette bibliothèque pour des tests d'intégration robustes avec Mocha. Voici un exemple de configuration:

test-setup.ts

import"source-map-support/register"import"reflect-metadata"import { IRISContainer, StartedIRISContainer } from"testcontainers-iris"import { IRISNative } from"../../src"import chai from"chai"import sinonChai from"sinon-chai"import chaiAsPromised from"chai-as-promised"
declare global {
    var container: StartedIRISContainer | undefinedvar connectionOptions: {
        host: string
        port: number
        user: string
        pwd: string
        ns: string
    }
}

process.env.TZ = "UTC" chai.should() chai.use(sinonChai) chai.use(chaiAsPromised)

before(async () => { console.log("Setting up test environment...") const image = process.env["IRIS_IMAGE"] let connectionOptions = { host: "localhost", port: 1972, user: "_SYSTEM", pwd: "SYS", ns: "USER", } if (image) { const container: StartedIRISContainer = awaitnew IRISContainer(image) .withNamespace("TEST") .start() console.log(IRIS container started at <span class="hljs-subst">${container.getConnectionUri()}</span>) global.container = container connectionOptions = { host: container.getHost(), port: container.getMappedPort(1972), user: container.getUsername(), pwd: container.getPassword(), ns: container.getNamespace(), } } global.connectionOptions = connectionOptions IRISNative.createConnection({ ...connectionOptions, sharedmemory: false }) })

after(async () => { console.log("Cleaning up test environment...") if (global.container) { await global.container.stop() } delete global.container })

Cas de test:

import { IRISNative, IRISConnection } from"../src/IRISNative"
describe("IRISNative test", () => {
    let connection: IRISConnection
    before(() => {
        const connectionOptions = global.connectionOptions
        connection = IRISNative.createConnection({ ...connectionOptions })
    })
    after(() => {
        if (connection) {
            connection.close()
        }
    })
    it("should work", async () => {
        const res = await connection.query(
            "SELECT 1 AS test1, '2' AS test2",
            [],
        )
        res.rows.should.be.an("array")
        res.rows.should.have.lengthOf(1)
        res.rows[0].should.be.an("object")
        res.rows[0].should.have.property("test1")
        res.rows[0].should.have.property("test2")
        res.rows[0].should.have.property("test1", 1)
        res.rows[0].should.have.property("test2", "2")
    })
})

Utilisation dans typeorm-iris Cette bibliothèque est également utilisée dans mon projet typeorm-iris qui fournit une prise en charge expérimentale de TypeORM pour InterSystems IRIS. testcontainers-iris-node alimente la configuration des tests d'intégration pour ce projet, aidant à valider la fonctionnalité ORM par rapport à des instances IRIS réelles.

Problèmes liés à l'adoption de la bibliothèque En tant que développeur chargé de l'adoption de la bibliothèque, l'un de mes plus grands défis consiste à tester plusieurs versions d'InterSystems IRIS. Cet outil simplifie considérablement ce processus en permettant de basculer facilement et d'automatiser des environnements conteneurisés avec des versions IRIS différentes.

Comparaison avec d'autres liaisons linguistiques Alors que testcontainers-iris-python et testcontainers-iris-java sont matures et prennent en charge des fonctionnalités avancées telles que les montages en bind et les scripts de démarrage personnalisés, la variante Node.js est rationalisée pour les environnements JavaScript/TypeScript et vise la simplicité et l'ergonomie pour les développeurs.

Contributions et commentaires Je suis ouvert à tous commentaires, problèmes et demandes de modification via GitHub: testcontainers-iris-node.

Pour concluretestcontainers-iris-node facilite la réalisation de tests automatisés et robustes des applications basées sur IRIS dans l'écosystème Node.js. Que vous développiez des API, des tâches ETL ou des services d'intégration, cet outil vous aide à garantir la fiabilité de vos flux de travail IRIS.

0
1 17
InterSystems officiel Adeline Icard · Juil 24, 2025

InterSystems annonce la disponibilité générale d'InterSystems IRIS 2025.2

InterSystems a le plaisir d'annoncer la disponibilité générale (GA) de la version 2025.2 de la plateforme de données InterSystems IRIS. Il s'agit d'une version en livraison continue (CD). Veuillez noter que les versions GA d'InterSystems IRIS for Health et HealthShare Health Connect 2025.2 sont actuellement suspendues en raison de limitations de mise en miroir introduites par les mises à jour de sécurité (détails ci-dessous).

Points forts de la version

0
0 27
Article Iryna Mykhailova · Jan 15, 2025 1m read

Lorsque vous déployez du code à partir d'un dépôt, la suppression de classe (fichier) peut ne pas être reflétée par votre système CI/CD.
Voici une simple ligne de commande pour supprimer automatiquement toutes les classes d'un package spécifié qui n'ont pas été importées. Elle peut être facilement ajustée pour une variété de tâches annexes :

set packages = "USER.*,MyCustomPackage.*"set dir = "C:\InterSystems\src\"set sc = $SYSTEM.OBJ.LoadDir(dir,"ck", .err, 1, .loaded)
set sc = $SYSTEM.OBJ.Delete(packages _ ",'" _ $LTS($LI($LFS(loaded_",",".cls,"), 1, *-1), ",'"),, .err2)

La première commande compile les classes et renvoie également une liste des classes chargées. La deuxième commande supprime toutes les classes des packages spécifiés, à l'exception des classes chargées juste avant.

0
0 43
InterSystems officiel Adeline Icard · Nov 27, 2024

InterSystems annonce la disponibilité générale d'InterSystems IRIS, InterSystems IRIS for Health et HealthShare Health Connect 2024.3

La version 2024.3 de la plateforme de données InterSystems IRIS®, InterSystems IRIS® for HealthTM et HealthShare® Health Connect est désormais généralement disponible (GA).

Points forts de la version

Dans cette version, vous pouvez vous attendre à une multitude de mises à jour intéressantes, notamment :

0
0 38
Article Guillaume Rongier · Jan 17, 2024 7m read

Bienvenue dans le prochain chapitre de ma série CI/CD, où nous discutons des approches possibles vers le développement de logiciels avec les technologies InterSystems et GitLab. Aujourd'hui, nous reprenons la discussion sur l'interopérabilité, et plus particulièrement sur le suivi de vos déploiements d'interopérabilité. Si vous ne l'avez pas encore fait, configurez Alerte pour toutes vos productions d'interopérabilité afin de recevoir des alertes sur les erreurs et l'état de la production dans son ensemble.

Le paramètre de délais d'inactivité Inactivity Timeout est commun pour tous les hôtes métier d'interopérabilité (Interoperability Business Hosts). Un hôte métier possède le statut Inactif lorsqu'il n'a reçu aucun message pendant le nombre de secondes spécifié dans le champ de délai d'inactivité " Inactivity Timeout ". La fonction de surveillance de la production " Monitor Service " examine périodiquement l'état des services et des opérations métier au sein de la production et marque les éléments comme étant inactifs s'ils n'ont rien fait pendant le délai d'inactivité. La valeur par défaut est 0 (zéro). Si ce paramètre est 0, l'hôte métier ne sera jamais marqué comme Inactif, quelle que soit la durée de son inactivité.

Ce paramètre est extrêmement utile, car il génère des alertes qui, associées aux alertes configurées, permettent de signaler les problèmes de production en temps réel. Le fait que l'élément "Business Host" soit inactif signifie qu'il peut y avoir des problèmes de production, d'intégration ou de connectivité réseau qui nécessitent d'être examinés. Cependant, le Business Host ne peut avoir qu'un seul paramètre constant pour le délai d'inactivité, ce qui peut générer des alertes inutiles pendant les périodes connues de faible trafic : nuits, week-ends, vacances, etc. Dans cet article, je décrirai plusieurs approches pour la mise en œuvre dynamique des délais d'inactivité (Inactivity Timeout). Bien que je fournisse un exemple fonctionnel (qui fonctionne actuellement en production pour l'un de nos clients), cet article est plutôt un guide pour votre propre mise en œuvre dynamique des délais d'inactivité, donc ne considérez pas la solution proposée comme la seule alternative.

Idée

Le moteur d'interopérabilité maintient une globale spéciale HostMonitor, qui contient chaque Business Host sous forme d'indice, et l'horodatage de la dernière activité sous forme de valeur. Au lieu d'utiliser le délai d'inactivité, nous allons surveiller nous-mêmes cette globale et générer des alertes en fonction de l'état de HostMonitor. HostMonitor est maintenue que soit la valeur du délai d'inactivité définie - elle est toujours activée.

Mise en œuvre

Pour commencer, voyons comment nous pouvons itérer la globale HostMonitor :

Set tHost=""
For {
Set tHost=$$$OrderHostMonitor(tHost)
Quit:""=tHost
Set lastActivity = $$$GetHostMonitor(tHost,$$$eMonitorLastActivity)
}

Pour créer notre Monitor Service, il nous faut effectuer les vérifications suivantes pour chaque Business Host :

  1. Décidez si le Business Host est concerné par notre configuration dynamique des délais d'inactivité (par exemple, les interfaces hl7 à fort trafic peuvent fonctionner avec le délai d'inactivité habituel).
  2. Si le Business Host se trouve dans le champ d'action, nous devons calculer le temps écoulé depuis la dernière activité.
  3. En fonction du temps d'inactivité et d'un certain nombre de conditions (heure du jour/de la nuit, jour de la semaine), nous devons décider si nous voulons envoyer une alerte.
  4. Si nous voulons envoyer un enregistrement d'alerte, nous devons enregistrer l'heure de la Dernière activité (Last Activity) afin de ne pas envoyer la même alerte deux fois.

Notre code se présente comme suit :

Set tHost=""
For { 
  Set tHost=$$$OrderHostMonitor(tHost) 
  Quit:""=tHost
  Continue:'..InScope(tHost)
  Set lastActivity = $$$GetHostMonitor(tHost,$$$eMonitorLastActivity)
  Set tDiff = $$$timeDiff($$$timeUTC, lastActivity)
  Set tTimeout = ..GetTimeout(tDayTimeout)
  If (tDiff > tTimeout) && ((lastActivityReported="") || ($system.SQL.DATEDIFF("s",lastActivityReported,lastActivity)>0)) {
    Set tText = $$$FormatText("InactivityTimeoutAlert: Inactivity timeout of '%1' seconds exceeded for host '%2'", +$fn(tDiff,,0), tHost)
    Do ..SendAlert(##class(Ens.AlertRequest).%New($LB(tHost, tText)))
    Set $$$EnsJobLocal("LastActivity", tHost) = lastActivity
  } 
}

Vous devez implémenter les méthodes InScope et GetTimeout contenant votre logique personnalisée, et vous êtes prêt à démarrer. Dans mon exemple, il y a des délais de jour (Day Timeouts, qui peuvent être différents pour chaque Business Host, mais avec une valeur par défaut) et des délais de nuit (Night Timeouts, qui sont les mêmes pour tous les Business Hosts enregistrés), de sorte que l'utilisateur doit fournir les paramètres suivants :

  • Champs d'application : Liste des noms (ou parties de noms) de Business Host associés à leur valeur DayTimeout personnalisée, une par ligne. Seuls les Business Hosts qui se trouve dans le champ d'action (qui remplissent la condition $find(host, scope) pour au moins un champ d'action) seront suivis. Laisser ce champ vide pour surveiller tous les Business Hosts. Exemple: OperationA=120
  • DayStart : Nombre de secondes depuis 00:00:00, point de départ d'une journée. Il doit être inférieur à DayEnd. Par exemple, 06:00:00 AM est 6*3600 = 21600
  • DayEnd: Nombre de secondes depuis 00:00:00, point de fin d'une journée. Il doit être supérieur à DayStart. Par exemple 08:00:00 du soir est (12+8)*3600 = 72000
  • DayTimeout : Valeur du délai d'attente par défaut en secondes pour le déclenchement d'alertes pendant la journée.
  • NightTimeout : Valeur du délai d'attente par défaut en secondes pour le déclenchement d'alertes pendant la nuit.
  • WeekendDays: Jours de la semaine considérés comme week-end. Séparés par des virgules. Pour le week-end, NightTimeout s'applique 24 heures par jour. Exemple : 1,7 Vérifiez la valeur DayOfWeek de la date en exécutant : $SYSTEM.SQL.Functions.DAYOFWEEK(date-expression). Par défaut, les valeurs renvoyées représentent les jours suivants : 1 - dimanche, 2 - lundi, 3 - mardi, 4 - mercredi, 5 - jeudi, 6 - vendredi, 7 - samedi.

Voici le code complet, mais je ne pense pas qu'il y ait quelque chose d'intéressant là-dedans. Il implémente simplement les méthodes InScope et GetTimeout. Vous pouvez utiliser d'autres critères et adapter les méthodes InScope et GetTimeout si nécessaire.

Problèmes

Il y a deux problèmes à aborder:

  • Pas d'icône jaune pour les Business Hosts inactifs (puisque la valeur du paramètre InactivityTimeout de l'hôte est nulle).
  • Paramètres hors hôte - les développeurs doivent se rappeler de mettre à jour ce service de surveillance personnalisé chaque fois qu'ils ajoutent un nouvel Business Host et qu'ils souhaitent utiliser une implémentation dynamique de délais d'inactivité.

Alternatives

J'ai exploré toutes ces approches avant de mettre en œuvre la solution ci-dessus :

  1. Créer le Business Service qui modifie les paramètres InactivityTimeout lorsque le jour ou la nuit commence. Au départ, j'ai essayé de suivre cette voie mais j'ai rencontré un certain nombre de problèmes, principalement l'obligation de redémarrer tous les Business Hosts concernés à chaque fois que nous modifions le paramètre InactivityTimeout.
  2. Ajouter de règles dans le processeur d'alertes personnalisé (Custom Alert Processor) qui, au lieu d'envoyer l'alerte, la suppriment si c'est le délai nocturne InactivityTimeout. Mais une alerte d'inactivité provenant de Ens.MonitorServoce met à jour la valeur LastActivity, donc je ne vois pas de moyen, à partir d'un Custom Alert Processor, d'obtenir un "vrai" horodatage de dernière activité (à part interroger Ens.MessageHeader, je suppose ?). Et si c'est " nuit " - retourner l'état de l'hôte à OK, si ce n'est pas encore " InactivityTimeout " nocturne, et supprimer l'alerte.
  3. L'extension de Ens.MonitorService ne semble pas possible sauf pour le callback OnMonitor, mais cela sert un autre but.

Conclusion

Toujours configurer [alerte] https://docs.intersystems.com/iris20233/csp/docbook/Doc.View.cls?KEY=ECONFIG_alerts) pour toutes vos productions d'interopérabilité afin de recevoir des alertes sur les erreurs et l'état de la production dans son ensemble. Si une configuration statique de délai d'inactivité n'est pas suffisante, vous pouvez facilement créer une implémentation dynamique.

Liens

0
0 67
Article Guillaume Rongier · Jan 8, 2024 14m read

Bienvenue dans le prochain chapitre de ma série CI/CD, où nous discutons des approches possibles vers le développement de logiciels avec les technologies InterSystems et GitLab.

Aujourd'hui, parlons d'interopérabilité.

Problème

Lorsque vous avez une production d'interopérabilité active, vous avez deux flux de processus distincts : une production active qui traite les messages et un flux de processus CI/CD qui met à jour le code, la configuration de la production et les paramètres par défaut du système.

De toute évidence, le processus CI/CD affecte l'interopérabilité. Mais il y a des questions :

  • Que se passe-t-il exactement lors d'une mise à jour ?
  • Que devons-nous faire pour minimiser ou éliminer les temps d'arrêt de la production lors d'une mise à jour ?

Terminologie

  • Business Host (BH) - un élément configurable de la production d'interopérabilité : Service métier (BS), Processus métier (BP, BPL) ou Opération métier (BO).
  • Business Host Job (Job) - Tâche InterSystems IRIS qui exécute le code du Business Host et qui est gérée par la production d'interopérabilité.
  • Production : ensemble interconnecté de serveurs d'entreprise (Hôte métier).
  • System Default Settings (Paramètres par défaut du système) (SDS) - valeurs spécifiques à l'environnement dans lequel InterSystems IRIS est installé.
  • Message actif - demande en cours de traitement par une tâche Business Host Job. Une tâche Business Host Job peut avoir un maximum d'un Message actif. Une tâche Business Host Job qui n'a pas de message actif est inactif.

Qu'est-ce qui se passe?

Commençons par le cycle de vie de la production.

Démarrage de la production

Tout d'abord, la production peut être démarrée. Une seule production par espace de noms peut être lancée simultanément, et en général (à moins que vous ne sachiez vraiment ce que vous faites et pourquoi vous le faites), vous ne devrez lancer qu'une seule production par espace de noms, jamais. Il n'est pas recommandé de passer d'un espace de noms à l'autre entre deux ou plusieurs productions différentes. Le démarrage de la production démarre tous les Business Hosts activés définis dans la production. Le fait que certains Business Hosts ne démarrent pas n'affecte pas le démarrage de la production.

Conseils:

  • Démarrez la production à partir du portail de gestion du système ou en appelant : ##class(Ens.Director).StartProduction("ProductionName")
  • Exécutez un code arbitraire au démarrage de la production (avant qu'un Job Business Host ne soit démarré) en implémentant une méthode OnStart.
  • Le démarrage de la production est un événement contrôlable. Vous pouvez toujours voir qui et quand a fait cela dans le journal d'audit.

Mise à Jour de la production

Après le démarrage de la production, l'outil Ens.Director surveille en permanence la production en cours. Il existe deux états de production : état cible, défini dans la classe de production et les paramètres par défaut du système ; et état en cours d'exécution - les tâches en cours d'exécution avec les paramètres appliqués lors de la création des tâches. Si l'état souhaité et l'état actuel sont identiques, tout va bien, mais la production peut (et doit) être mise à jour s'il y a une différence. Habituellement, vous voyez cela sous la forme d'un bouton rouge "Mettre à jour" sur la page Configuration de la production dans le Portail de gestion du système.

La mise à jour de la production signifie une tentative pour que l'état de production actuel corresponde à l'état de production cible.

Lorsque vous exécutez ##class(Ens.Director).UpdateProduction(timeout=10, force=0) pour mettre à jour la production, il effectue les opérations suivantes pour chaque Business Host :

  1. Comparez les réglages actifs aux réglages de production/SDS/classe.
  2. Uniquement si (1) révèle une incohérence, le Business Host est marqué comme étant obsolète et nécessitant une mise à jour.

Après avoir exécuté cette opération pour chaque Business Host, UpdateProduction construit l'ensemble des changements :

  • Business Hosts à arrêter
  • Business Hosts à démarrer
  • Paramètres de production à mettre à jour

Ensuite, il les applique.

De cette manière, la "mise à jour" des paramètres sans rien changer n'entraîne aucune interruption de la production.

Conseils:

  • Mettre à jour la production à partir du portail de gestion du système ou en appelant : ##class(Ens.Director).UpdateProduction(timeout=10, force=0)
  • Le délai de mise à jour par défaut du portail de gestion du système est de 10 secondes. Si vous savez que le traitement de vos messages prend plus de temps, appelez Ens.Director:UpdateProduction avec un délai plus long.
  • Le délai de mise à jour est un paramètre de production, et vous pouvez le modifier pour une valeur plus importante. Ce paramètre s'applique au portail de gestion du système.

Mise à jour du code

UpdateProduction NE MET PAS A JOUR les BHs dont le code est obsolète. C'est un comportement orienté vers la sécurité, mais si vous voulez mettre à jour automatiquement tous les BHs en cours d'exécution lorsque le code sous-jacent change, suivez les étapes suivantes :

First, load and compile like this:

do $system.OBJ.LoadDir(dir, "", .err, 1, .load)
do $system.OBJ.CompileList(load, "curk", .errCompile, .listCompiled)

Maintenant, listCompiled contiendrait tous les éléments qui ont été compilés (utilisez git diffs pour minimiser l'ensemble des éléments importés) à cause du marqueur u. Utilisez cette listCompiled pour obtenir un $lb de toutes les classes qui ont été compilées :

set classList = ""
set class = $o(listCompiled(""))
while class'="" { 
  set classList = classList _ $lb($p(class, ".", 1, *-1))
  set class=$o(listCompiled(class))
}

Et ensuite, calculer une liste de BHs qui nécessitent un redémarrage :

SELECT %DLIST(Name) bhList
FROM Ens_Config.Item 
WHERE 1=1
  AND Enabled = 1
  AND Production = :production
  AND ClassName %INLIST :classList

Enfin, après avoir obtenu bhList, arrêtez et démarrez les hôtes affectés :

for stop = 1, 0 {
  for i=1:1:$ll(bhList) {
    set host = $lg(bhList, i)
    set sc = ##class(Ens.Director).TempStopConfigItem(host, stop, 0)
  }
  set sc = ##class(Ens.Director).UpdateProduction()
}

Arrêt de production

Les productions peuvent être arrêtées, ce qui implique d'envoyer une demande à tous les Jobs Business Host pour qu'ils s'arrêtent (en toute sécurité, une fois qu'ils ont terminé leurs messages actifs, le cas échéant).

Conseils:

  • Arrêtez la production à partir du portail de gestion du système ou en appelant : ##class(Ens.Director).StopProduction(timeout=10, force=0)
  • Le délai d'arrêt par défaut du portail de gestion du système est de 120 secondes. Si vous savez que le traitement de vos messages prend plus de temps que cela, appelez Ens.Director:StopProduction avec un délai plus long.
  • Le délai d'arrêt est un paramètre de production. Vous pouvez le modifier pour une valeur plus importante. Ce paramètre s'applique au portail de gestion du système.
  • Exécuter un code arbitraire lors d'un arrêt de production en implémentant une méthode OnStop.
  • L'arrêt de la production est un événement contrôlable, vous pouvez toujours voir qui et quand a fait cela dans le journal d'audit.

Ce qui est important ici, c'est que la production est la somme totale des Business Hosts :

  • Le démarrage de la production signifie le démarrage de tous les Business Hosts activés.
  • L'arrêt de la production signifie l'arrêt de tous les Business Hosts en cours d'exécution.
  • La mise à jour de la production consiste à calculer un sous-ensemble de Business Hosts qui sont obsolètes, de sorte qu'ils sont d'abord arrêtés, puis redémarrés immédiatement après. En outre, un Business Host nouvellement ajouté est seulement démarré, et un Business Host supprimé de la production est seulement arrêté.

Cela nous amène au cycle de vie des Business Hosts.

Démarrage du Business Host

Les Business Hosts sont composés de Business Hosts Jobs identiques (en fonction de la valeur du paramètre Pool Size). Le démarrage d'un Business Host implique le démarrage de tous les Business Hosts Jobs. Ils sont démarrés en parallèle.

Business Host Job individuel commence comme ceci :

  1. Interopérabilité de JOBs est un nouveau processus qui deviendrait un Business Host Job.
  2. Le nouveau processus s'enregistre en tant que job d'interopérabilité.
  3. Le code du Business Host et le code de l'adaptateur (Adapter) sont chargés dans la mémoire du processus.
  4. Les paramètres relatifs à un Business Host et à un adaptateur sont chargés dans la mémoire. L'ordre de priorité est le suivant a. Paramètres de production (remplacent les paramètres par défaut du système et les paramètres de classe). b. Paramètres par défaut du système (prévalent sur les paramètres de classe). c. Paramètres de classe.
  5. Le travail est prêt et commence à accepter des messages.

Une fois le point (4) effectué, la tâche ne peut plus modifier les paramètres ou le code. Ainsi, lorsque vous importez un nouveau code et de nouveaux paramètres par défaut, cela n'affecte pas les tâches d'interopérabilité en cours d'exécution.

Arrêt de Business Host

L'arrêt du Business Host Job signifie le suivant :

  1. L'interopérabilité ordonne à Job d'arrêter toute acceptation de messages/entrées.
  2. S'il y a un message actif, le Business Host Job dispose d'un délai de quelques secondes pour le traiter (en le complétant - en complétant la méthode OnMessage pour BO, OnProcessInput pour BS, la méthode d'état S<int> pour les BP BPL, et la méthode On* pour les BP).
  3. Si un message actif n'a pas été traité avant le timeout et force=0, la mise à jour de la production échoue pour ce Business Host (et vous verrez un bouton rouge Update dans le SMP).
  4. L'arrêt réussit si l'un des éléments de cette liste est vrai :
    • Pas de message actif
    • Le message actif a été traité avant le timeout.
    • Le message actif n'a pas été traité avant le timeout MAIS force=1.
  5. Le travail est désenregistré auprès de l'interopérabilité et s'arrête.

Mise à jour de Business Host

La mise à jour du Business Host signifie l'arrêt des tâches en cours d'exécution pour le Business Host et le démarrage de nouvelles tâches.

Les régles métier "Business Rules", les régles de routage "Routing Rules" et les DTL

Tous les Business Hosts commencent immédiatement à utiliser les nouvelles versions des régles Business Rules, Routing Rules et des DTL dès qu'elles sont disponibles. Le redémarrage d'un Business Host n'est pas nécessaire pour cela.

Mises à jour hors ligne

Il arrive cependant que les mises à jour de la production nécessitent l'arrêt de certains Business Hosts.

Les régles dépendent du nouveau code

Considérons la situation. Vous disposez d'une règle de routage Routing Rule X qui achemine les messages vers le processus Business Process A ou B en fonction de critères arbitraires. Dans un nouveau commit, vous ajoutez simultanément:

  • le processus Business Process C
  • Une nouvelle version de Routing Rule X, qui achemine les messages vers A, B ou C.

Dans ce scénario, vous ne pouvez pas charger la règle d'abord et mettre à jour la production ensuite. En effet, la règle nouvellement compilée commencerait immédiatement à router les messages vers le Business Process C, qu'InterSystems IRIS n'a peut-être pas encore compilé, ou que l'interopérabilité n'a pas encore mis à jour pour l'utiliser. Dans ce cas, vous devez désactiver le Business Host avec une règle de routage, mettre à jour le code, mettre à jour la production et réactiver le Business Host.

Remarques:

  • Si vous mettez à jour une production à l'aide du fichier de déploiement de la production, tous les BH concernés seront automatiquement désactivés/activés.
  • Pour les hôtes invoqués par InProc, la compilation invalide le cache de l'hôte particulier détenu par l'appelant.

Dépendances entre Business Hosts

Les dépendances entre les Business Hosts sont essentielles. Imaginons que vous ayez des processus Business Hosts A et B, dans lesquels A envoie des messages à B. Dans un nouveau commit, vous ajoutez simultanément :

  • Une nouvelle version du Processus A, qui définit une nouvelle propriété X dans une demande adressée à B
  • Une nouvelle version du Processus B qui peut traiter une nouvelle propriété X

Dans ce scénario, nous DEVONS mettre à jour le Processus B d'abord et le Processus A ensuite. Vous pouvez procéder de deux manières :

  • Désactiver les Business Hosts pendant la durée de la mise à jour.
  • Diviser la mise à jour en deux parties : d'abord, mettre à jour le Processus B uniquement, et ensuite, dans une mise à jour séparée, commencer à lui envoyer des messages à partir du Processus A.

Une variante plus difficile de ce thème, où les nouvelles versions des Processus A et B sont incompatibles avec les anciennes versions, nécessite un arrêt des Business Hosts.

Files d'attente

Si vous savez qu'après la mise à jour, un Business Host ne pourra plus traiter les anciens messages, vous devez vous assurer que la file d'attente du Business Host est vide avant la mise à jour. Pour ce faire, désactivez tous les Business Hosts qui envoient des messages au Business Host et attendez que sa file d'attente soit vide.

Changement d'état dans les BPL Business Processes

Tout d'abord, une petite introduction sur le fonctionnement des BPL BP. Après avoir compilé un BPL BP, deux classes sont créées dans le paquet, avec le même nom qu'une classe BPL complète :

  • La classe Thread1 contient les méthodes S1, S2, .... SN, qui correspondent à des activités au sein de BPL
  • La classe Context contient toutes les variables de contexte ainsi que le prochain état que BPL exécutera (c'est-à-dire S5).

La classe BPL est également persistante et stocke les demandes en cours de traitement.

BPL fonctionne en exécutant des méthodes S dans une classe Thread et en mettant à jour la table de la classe BPL, la table Context et la table Thread1, où un message "en cours de traitement" est une ligne dans un tableau BPL. Une fois la requête traitée, BPL supprime les entrées BPL, Context et Thread. Puisque les BPL BP sont asynchrones, une tâche BPL peut traiter simultanément de nombreuses requêtes en sauvegardant des informations entre les appels S et en passant d'une requête à l'autre. Par exemple, BPL traite une requête jusqu'à ce qu'il arrive à une activité sync - en attendant une réponse de BO. Il sauvegarde le contexte actuel sur le disque, avec la propriété %NextState (dans la classe Thread1) fixée à la méthode S de l'activité de réponse, et travaille sur d'autres requêtes jusqu'à ce que BO réponde. Après la réponse de BO, BPL charge le contexte en mémoire et exécute la méthode correspondant à un état sauvegardé dans la propriété %NextState.

Maintenant, que se passe-t-il lorsque nous mettons à jour le BPL ? Tout d'abord, nous devons vérifier qu'au moins une des deux conditions est remplie :

  • Pendant la mise à jour, le tableau Contexte est vide, ce qui signifie qu'aucun message actif n'est en cours de traitement.
  • Les nouveaux états (New States) sont les mêmes que les anciens (Old States), ou de nouveaux états sont ajoutés à la suite des anciens .

Si au moins une condition est remplie, nous sommes prêts à démarrer. Soit il n'y a pas de demandes de pré-mise à jour à traiter par BPL après la mise à jour, soit les états sont ajoutés à la fin, ce qui signifie que les anciennes demandes peuvent également être traitées (en supposant que les demandes de pré-mise à jour sont compatibles avec les activités et le traitement de BPL après la mise à jour).

Mais que se passe-t-il si vous avez des demandes actives en cours de traitement et que BPL change l'ordre des états ? Idéalement, si vous pouvez attendre, désactivez les appelants BPL et attendez que la file d'attente soit vide. Vérifiez que le tableau Contexte est également vide. N'oubliez pas que la file d'attente n'affiche que les demandes non traitées, et que le tableau Contexte stocke les demandes en cours de traitement, de sorte qu'il peut arriver qu'un BPL très occupé affiche une file d'attente nulle, et c'est normal. Ensuite, désactivez le BPL, effectuez la mise à jour et activez tous les Business Hosts précédemment désactivés.

Si ce n'est pas possible (généralement dans le cas où le BPL est très long, par exemple, je me souviens avoir mis à jour un BPL qui prenait environ une semaine pour traiter une demande, ou si la fenêtre de mise à jour est trop courte), utilisez l'outil BPL versioning.

Vous pouvez également écrire un script de mise à jour. Dans ce script de mise à jour, associez les anciens next states aux nouveaux next states et exécutez-le sur le tableau Thread1 afin que le BPL mis à jour puisse traiter les anciennes requêtes. BPL, bien sûr, doit être désactivé pendant la durée de la mise à jour. Ceci dit, il s'agit d'une situation extrêmement rare, et généralement, vous n'avez pas besoin de faire cela, mais si jamais vous devez le faire, voici comment procéder.

Conclusion

L'interopérabilité met en œuvre un algorithme sophistiqué pour minimiser le nombre d'actions nécessaires pour actualiser la production après la modification du code sous-jacent. Appelez UpdateProduction avec un délai d'attente sûr à chaque mise à jour de SDS. Pour chaque mise à jour de code, vous devez décider d'une stratégie de mise à jour.

La réduction de la quantité de code compilé à l'aide de git diffs permet de réduire le temps de compilation, mais la "mise à jour" du code en lui-même et sa recompilation ou la "mise à jour" des paramètres avec les mêmes valeurs ne déclenchent pas ou ne requièrent pas de mise à jour de la production.

La mise à jour et la compilation des règles de gestion "Business Rule", des règles de routage "Routing Rules" et des DTL les rendent immédiatement accessibles sans mise à jour de la production.

Enfin, la mise à jour de la production est une opération sûre qui ne nécessite généralement pas de temps d'arrêt.

Liens

L'auteur tient à remercier @James MacKeith, @Dmitry Zasypkin et @Regilo Regilio Guedes de Souza pour leur aide précieuse dans la rédaction de cet article.

0
0 89
Article Guillaume Rongier · Déc 21, 2023 9m read

Après presque quatre ans de pause, ma série CI/CD est de retour ! Au fil des ans, j'ai travaillé avec plusieurs clients d'InterSystems, développant des pipelines CI/CD pour différents cas d'utilisation. J'espère que les informations présentées dans cet article seront utiles à quelqu'un.

Cette série d'articles aborde plusieurs approches possibles du développement logiciel avec les technologies InterSystems et GitLab.

Nous avons une gamme passionnante de sujets à couvrir: aujourd'hui, parlons de choses au - delà du code, à savoir les configurations et les données.

Problème

Nous avons déjà abordé la question des promotions de code, qui était, d'une certaine manière, sans état - nous passons toujours d'une instance (présumée) vide à une base de code complète. Mais parfois, nous devons fournir des données ou un état. Il existe différents types de données:

  • Configuration: utilisateurs, applications Web, LUT, schémas personnalisés, tâches, partenaires commerciaux et bien d'autres
  • Paramètres: paires clé-valeur spécifiques à l'environnement
  • Données : des tableaux de référence etc. doivent souvent être fournis pour que votre application fonctionne.

Discutons de tous ces types de données et de la manière dont ils peuvent d'abord être validés dans le contrôle de code source, et ensuite déployés.

Configuration

La configuration de système est répartie dans de nombreuses classes différentes, mais InterSystems IRIS peut exporter la plupart d'entre elles en XML. Tout d'abord, il s'agit d'un paquet de sécurité qui contient des informations sur :

  • Les Applications Web
  • DocDBs
  • Les domaines
  • Les événements d'audit
  • Les serveurs KMIPServers
  • Les configurations LDAP
  • Les ressources
  • Les rôles
  • Les privilèges SQL
  • Les configurations SSL
  • Les services
  • Les utilisateurs

Toutes ces classes sont dotées de méthodes Exists, Export et Import, qui vous permettent de les déplacer d'un environnement à l'autre.

Quelques avertissements :

  • Les utilisateurs et les configurations SSL peuvent contenir des informations sensibles, telles que des mots de passe. Il N'est généralement PAS recommandé de les stocker dans le contrôle du code source pour des raisons de sécurité. Utilisez des méthodes d'exportation/importation pour faciliter les transferts ponctuels.
  • Par défaut, les méthodes d'exportation et d'importation produisent tout dans un seul fichier, ce qui n'est pas forcément compatible avec le contrôle du code source. Voici une classe utilitaire qui permet d'exporter et d'importer des tableaux de consultation (LUT), des schémas personnalisés, des partenaires commerciaux, des tâches, des informations d'identification et une configuration SSL. Il exporte un élément par fichier, de sorte que vous obtenez un répertoire avec LUT, un autre répertoire avec des schémas personnalisés, et ainsi de suite. Pour les configurations SSL, il exporte également des fichiers : certificats et clés.

Il convient également de noter qu'au lieu d'exporter/importer, vous pouvez utiliser %Installer ou Merge CPF pour créer la plupart de ces éléments. Ces deux outils prennent également en charge la création d'espaces de noms et de bases de données. Merge CPF peut ajuster les paramètres du système, tels que la taille du tampon global.

Tâches

La classe %SYS.Task stocke les tâches et fournit les méthodes ExportTasks et ImportTasks. Vous pouvez également consulter la classe utilitaire ci-dessus pour importer et exporter des tâches individuellement. Notez que lorsque vous importez des tâches, vous pouvez obtenir des erreurs d'importation (ERROR #7432 : La date et l'heure de début doivent être postérieures à la date et à l'heure actuelles) si StartDate ou d'autres propriétés liées à la planification sont dans le passé. Comme solution, réglez LastSchedule à 0, et InterSystems IRIS replanifiera une tâche nouvellement importée pour qu'elle s'exécute dans le futur le plus proche.

Interopérabilité

Les productions d'interopérabilité contiennent le suivant :

  • Les partenaires commerciaux
  • Les paramètres par défaut du système
  • Les références
  • Les tableaux de consultation (LUT)

Les deux premiers sont disponibles dans le paquet Ens.Config avec les méthodes %Export et %Import. Exportez les références et les tableaux de consultation à l'aide de la classe utilitaire ci-dessus. Dans les versions récentes, les tableaux de consultation peuvent être exportés/importés via la classe $system.OBJ.

Paramètres

Paramètres par défaut du système est un mécanisme d'interopérabilité par défaut pour les paramètres spécifiques à l'environnement :

L'objectif des paramètres par défaut du système est de simplifier le processus de copiage d'une définition de production d'un environnement à l'autre. Dans toute production, les valeurs de certains paramètres sont déterminées dans le cadre de la conception de la production ; ces paramètres doivent généralement être identiques dans tous les environnements. D'autres paramètres, en revanche, doivent être adaptés à l'environnement ; il s'agit notamment des chemins d'accès aux fichiers, des numéros de port, etc.

Les paramètres par défaut du système ne doivent spécifier que les valeurs spécifiques à l'environnement dans lequel InterSystems IRIS est installé. En revanche, la définition de la production doit spécifier les valeurs des paramètres qui doivent être identiques dans tous les environnements.

Je recommande fortement de les utiliser dans des environnements de production. Utilisez %Export et %Import pour transférer les paramètres par défaut du système.

Paramètres de l'application

Votre application doit également utiliser des paramètres. Dans ce cas, je vous recommande d'utiliser les paramètres par défaut du système. Bien qu'il s'agisse d'un mécanisme d'interopérabilité, il est possible d'accéder aux paramètres via : %GetSetting(pProductionName, pItemName, pHostClassName, pTargetType, pSettingName, Output pValue) (docs). Vous pouvez écrire un wrapper qui définira les valeurs par défaut dont vous ne vous souciez pas, par exemple :

ClassMethod GetSetting(name, Output value) As %Boolean [Codemode=expression]
{
##class(Ens.Config.DefaultSettings).%GetSetting("myAppName", "default", "default", , name, .value)
}

Si vous voulez plus de catégories, vous pouvez aussi exposer les arguments pItemName et/ou pHostClassName. Les paramètres peuvent être initialement définis par importation, en utilisant le portail de gestion du système, en créant des objets de la classe Ens.Config.DefaultSettings, ou en définissant une globale ^Ens.Config.DefaultSettingsD.

Mon premier conseil est de conserver les paramètres en un seul endroit (il peut s'agir des paramètres par défaut du système ou d'une solution personnalisée), et l'application doit obtenir les paramètres uniquement à l'aide d'une API fournie. De cette façon, l'application elle-même ne connaît pas l'environnement et il ne reste plus qu'à fournir un stockage centralisé des paramètres avec des valeurs spécifiques à l'environnement. Pour ce faire, créez un dossier de paramètres dans votre référentiel contenant des fichiers de paramètres, avec des noms de fichiers identiques aux noms des branches de l'environnement. Ensuite, pendant la phase CI/CD, utilisez la variable d'environnement $CI_COMMIT_BRANCH pour charger le fichier approprié.

DEV.xml
TEST.xml
PROD.xml

Si vous avez plusieurs fichiers de configuration par environnement, utilisez des dossiers nommés d'après les branches de l'environnement. Pour obtenir la valeur d'une variable d'environnement à l'intérieur d'InterSystems IRIS utilisez$System.Util.GetEnviron("name").

Données

Si vous souhaitez mettre à disposition certaines données ( tableaux de référence, catalogues, etc.), vous disposez de plusieurs moyens pour le faire :

  • Exportation globale. Utilisez soit un fichier binaire GOF export, soit un nouvel fichier XML. Avec le fichier GOF export, rappelez-vous que les locales sur les systèmes source et cible doivent correspondre (ou au moins la collation globale doit être disponible sur le système cible). Le fichier XML export prend plus de place. Vous pouvez l'améliorer en exportant les globales dans un fichier xml.gz, les méthodes $system.OBJ (dés)archivent automatiquement les fichiers xml.gz selon les besoins. Le principal inconvénient de cette approche est que les données ne sont pas lisibles par l'homme, même XML - la plupart d'entre elles sont encodées en base64.
  • Format CSV. Export CSV et l'importer avec LOAD DATA. Je préfère CSV car c'est le format lisible par l'homme le plus efficace en termes de stockage, et que tout le monde peut importer.
  • Format JSON. Créez la classe JSON Enabled.
  • Format XML. Créez la classe XML Enabled pour projeter des objets en XML. Utilisez-la si vos données ont une structure complexe.

Le choix du format dépend de votre cas d'utilisation. Ici, j'ai classé les formats par ordre d'efficacité de stockage, mais ce n'est pas un souci si vous n'avez pas beaucoup de données.

Conclusions

L'état ajoute une complexité supplémentaire à vos pipelines de déploiement CI/CD, mais InterSystems IRIS fournit une vaste gamme d'outils pour le gérer.

Liens

0
0 61
Article Guillaume Rongier · Déc 18, 2023 7m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD
  • Pourquoi des conteneurs?
  • Infrastructure de conteneurs
  • CD utilisant des conteneurs
  • CD utilisant ICM
  • Architecture des conteneurs

Dans cet article, nous aborderons la construction de votre propre conteneur et son déploiement.

0
0 72
Article Guillaume Rongier · Déc 14, 2023 10m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD
  • Pourquoi des conteneurs?
  • Infrastructure de conteneurs
  • CD utilisant des conteneurs
  • CD utilisant ICM

Dans cet article, nous allons créer une diffusion continue avec InterSystems Cloud Manager. ICM est une solution de déploiement et de provisionnement en nuage pour les applications basées sur InterSystems IRIS. Il vous permet de définir la configuration de déploiement souhaitée et ICM la provisionne automatiquement. Pour plus d'informations, consultez : First Look : ICM.

0
0 74
Article Guillaume Rongier · Déc 11, 2023 10m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD
  • Pourquoi des conteneurs?
  • Infrastructure de conteneurs
  • CD utilisant des conteneurs

Dans le premier article, nous avons évoqué les notions de base de Git, les raisons pour lesquelles une compréhension approfondie des concepts de Git est importante pour le développement de logiciels modernes et la manière dont Git peut être utilisé pour développer des logiciels.

Dans le deuxième article, nous avons évoqué le flux de travail GitLab - un processus complet du cycle de vie du logiciel ainsi que Diffusion continue.

Dans le troisième article, nous avons évoqué l'installation et la configuration de GitLab et la connexion de vos environnements à GitLab

Dans le quatrième article, nous avons écrit une configuration de CD.

Dans le cinquième article, nous avons parlé des conteneurs et de la manière dont ils peuvent être utilisés (et pour quelles raisons).

Dans le sixème article nous abordons des principaux composants dont vous aurez besoin pour exécuter un pipeline de diffusion continue avec des conteneurs et de la façon dont ils fonctionnent tous ensemble.

Dans cet article, nous allons créer une configuration de diffusion continue décrite dans les articles précédents.

0
0 88
Article Guillaume Rongier · Déc 7, 2023 7m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD
  • Pourquoi des conteneurs?
  • Infrastructure de conteneurs
  • GitLab CI/CD utilisant des conteneurs

Dans le premier article, nous avons évoqué les notions de base de Git, les raisons pour lesquelles une compréhension approfondie des concepts de Git est importante pour le développement de logiciels modernes et la manière dont Git peut être utilisé pour développer des logiciels.

Dans le deuxième article, nous avons évoqué le flux de travail GitLab - un processus complet du cycle de vie du logiciel ainsi que Diffusion continue.

Dans le troisième article, nous avons évoqué l'installation et la configuration de GitLab et la connexion de vos environnements à GitLab

Dans le quatrième article, nous avons écrit une configuration de CD.

Dans le cinquième article, nous avons parlé des conteneurs et de la manière dont ils peuvent être utilisés (et pour quelles raisons).

Dans cet article, nous allons discuter des principaux composants dont vous aurez besoin pour exécuter un pipeline de diffusion continue avec des conteneurs et de la façon dont ils fonctionnent tous ensemble.

0
0 385
Article Lorenzo Scalese · Juin 30, 2023 5m read

Si vous déployez dans plus d'un environnement/région/cloud/client, vous rencontrerez inévitablement le problème de la gestion de la configuration.

Alors que tous vos déploiements (ou juste certains) peuvent partager le même code source, certaines parties, telles que la configuration (paramètres, mots de passe) diffèrent d'un déploiement à l'autre et doivent être gérées différemment.

Dans cet article, j'essaierai d'offrir quelques conseils à ce sujet. Cet article traite principalement des déploiements de conteneurs.

Unification de la base de code

Avant d'aborder la gestion de la configuration, parlons de l'unification des bases de code. Le problème est le suivant : la base de code doit généralement viser à coalescer en une seule version. Bien sûr, à tout moment, vous aurez plusieurs versions de votre base de code - la version DEV avec de toutes nouvelles fonctionnalités, la version TEST avec du code de test supplémentaire, la version PROD, et ainsi de suite. Et c'est fine parce que, dans cet exemple, les changements se regroupent en une seule version au fil du temps. Plusieurs branches DEV deviennent une branche TEST et ainsi de suite.

Le problème commence lorsque nous avons plusieurs versions à l'étape finale de notre pipeline de déploiement. Par exemple, le client ABC a demandé une fonctionnalité particulière XYZ, et il existe maintenant deux versions de la base de code PROD - avec la fonctionnalité XYZ et sans elle. Cette situation est problématique car elle double immédiatement nos délais de construction et notre consommation de ressources. Bien sûr, il n'y a parfois aucun moyen d'y remédier. Cependant, supposons que vous ayez de telles divergences persistantes entre les bases de code. Dans ce cas, il peut être intéressant de rechercher si vous pouvez les regrouper en une seule version - dans ce scénario, la fonctionnalité XYZ est activée ou non au démarrage.

Cela dit, passons à la gestion de la configuration.

 

Gestion de la configuration

Qu'est-ce que nous voulons ?

 * Ne pas stocker toutes les configurations possibles dans un seul conteneur ( pour une part, ce n'est pas sûr, et pour une autre part, nous avons toujours besoin de choisir la configuration à appliquer).
 * Ne pas construire un conteneur pour chaque configuration (simplifie le pipeline CD)

Nous devons transmettre la configuration (paramètres, secrets, ...) lors du démarrage d'InterSystems IRIS et l'appliquer à notre application.

Transmission de la configuration

Il existe plusieurs façons de transmettre la configuration au démarrage.

Les variables d'environnement

Simple et rapide. Dans le cas où vous souhaitez tout stocker dans une seule variable, utilisez JSON pour la sérialisation. N'oubliez pas par ailleurs que Windows a une limite de 32K caractères pour les variables d'environnement (mégaoctets sous Linux). Pour récupérer une variable d'environnement, utilisez $SYSTEM.Util.GetEnviron("ENV_VAR\NAME"). Le principal avantage est la facilité de mise en œuvre. Vérifiez également votre outil CD - il a généralement un minimum de support des variables d'environnement pendant l'exécution du pipeline, parfois avec un support des secrets pour plus de sécurité.

Les fichiers de montage

La configuration peut être un fichier de montage au démarrage. L'avantage est qu'il n'y a pas de limite de longueur. L'inconvénient est que toutes les offres en nuage ne prennent pas en charge les fichiers de montage, et même si c'est le cas, la gestion des fichiers peut s'avérer plus délicate que celle des variables d'environnement. Sachez que les fichiers peuvent toujours être récupérés à partir des anciennes couches d'un conteneur, même s'ils ont été supprimés à une couche ultérieure.

Fusion du CPF

Les paramètres du système peuvent être transmis via le Fichier de fusion de configuration. Ainsi que ses fonctionnalités intégrées. Inconvénient : Le fichier de fusion CPF ne traite pas les paramètres au niveau de l'application.

Les secrets de Docker/CSP

Docker peut monter un secret sous une forme de fichier à l'intérieur d'un conteneur. Les offres en nuage proposent souvent une fonctionnalité similaire. Utilisez-la à la place des fichiers ordinaires pour améliorer la sécurité.

Traitement de la configuration

Bien, vous avez passé la configuration à l'intérieur d'un conteneur. Que faire maintenant ?

%ZSTART

%ZSTART est votre code personnalisé, exécuté au démarrage du système. Il ressemble à ceci :

SYSTEM
    try {
        new $namespace
        set $namespace = "USER"
        // apply configuration
        set $namespace = "%SYS"
    } catch ex {
        zn "%SYS"
        do ex.Log()
    }
    quit 1

Il est essentiel de noter que %ZSTART doit être testé complètement - car InterSystems IRIS ne démarrera pas (ou se comportera de manière erratique) si la routine %ZSTART renvoie une erreur.

Vous êtes maintenant prêt à appliquer vos paramètres.

La manière dont vous stockez/appliquez les paramètres au niveau de l'application dépend naturellement de l'application, mais les paramètres par défaut du systèmeSystem Default Settings{.editor-rtfLink} sont disponibles si vous utilisez l'interopérabilité. Même si vous n'utilisez pas de productions interopérables, vous pouvez toujours utiliser les paramètres par défaut du système car ils sont disponibles pour tout le monde (tant que l'interopérabilité est activée dans un espace de noms).

Pour créer un nouveau paramètre, exécutez :

Set setting = ##class(Ens.Config.DefaultSettings).%New()
Set setting.ProductionName = production
Set setting.ItemName = itemname
Set setting.HostClassName = hostclassname
Set setting.SettingName = settingname
Set setting.SettingValue = settingvalue
Set setting.Description = description
Set sc = setting.%Save()

Et pour récupérer les paramètres, appelez :

set sc = ##class(Ens.Config.DefaultSettings).%GetSetting(production, itemname, hostclassname, "", settingname, .settingvalue)
set settingvalue = ##class(Ens.Director).GetProductionSettingValue(production, settingname, .sc)

Les hôtes d'interopérabilité reçoivent leurs paramètres automatiquement au démarrage du travail de l'hôte commercial.

Summary

Making your application configurable when deploying to more than one server is essential. It can be pretty easy at the early part of the development cycle, but the costs rise with each hardcoded URL. InterSystems IRIS offers several tools you can use to configure the state of your application at startup.

How do you manage multiple deployments?

0
0 63
Article Guillaume Rongier · Fév 17, 2023 18m read

Keywords:  IRIS, IntegratedML, Flask, FastAPI, Tensorflow servant, HAProxy, Docker, Covid-19

Objective:

Nous avons abordé quelques démonstrations rapides d'apprentissage profond et d'apprentissage automatique au cours des derniers mois, notamment un simple classificateur d'images radiographiques Covid-19 et un classificateur de résultats de laboratoire Covid-19 pour les admissions possibles en soins intensifs. Nous avons également évoqué une implémentation de démonstration IntegratedML du classificateur ICU. Alors que la randonnée de la "science des données" se poursuit, le moment est peut-être venu d'essayer de déployer des services d'IA du point de vue de "l'ingénierie des données" - pourrions-nous regrouper tout ce que nous avons abordé jusqu'à présent dans un ensemble d'API de services ? Quels sont les outils, les composants et l'infrastructure communs que nous pourrions exploiter pour réaliser une telle pile de services dans son approche la plus simple possible ?

 

Cadre

Dans le cadre de ce qui suit:

Pour commencer, nous pouvons simplement utiliser docker-compose pour déployer les composants dockerisés suivants dans un serveur AWS Ubuntu

  • HAProxy - équilibreur de charge
  • Gunicorn vs. Univorn ** - passerelle web **serveurs
  • Flask vs. FastAPI - serveurs d'application pour l'interface utilisateur des applications Web, les définitions des API de service, la génération des cartes thermiques, etc.
  • Tensorflow Model Serving vs. Tensorflow-GPU Model Serving - serveurs d'applications pour les classifications d'images, etc.
  • IRIS IntegratedML - AutoML consolidé App+DB avec interface SQL.
  • Python3 dans Jupyter Notebook pour émuler un client pour le benchmarking.
  • Docker et docker-compose.
  • AWS Ubuntu 16.04 avec un GPU Tesla T4  

Remarque:   Tensorflow Serving avec GPU n'est utilisé qu'à des fins de démonstration - vous pouvez simplement désactiver l'image liée au GPU (dans un fichier docker) et la configuration (dans le fichier docker-compose.yml).

Out of scope (Hors de portée) ou sur la prochaine liste de souhaits :

  • **Les serveurs web Nginx ou Apache etc. sont omis dans la démo pour le moment.
  • RabbitMQ et Redis - courtier de file d'attente pour une messagerie fiable qui peut être remplacé par IRIS ou Ensemble.
    IAM (Intersystems API Manger) ou Kong est sur la liste des souhaits.
  • SAM(Intersystems System Alert & Monitoring)
  • ICM (Intersystems Cloud Manager) avec l'opérateur Kubernetes - toujours l'un de mes préférés depuis sa naissance
  • FHIR (serveur FHIR R4 basé sur Intesystems IRIS et FHIR Sandbox pour les applications SMART sur FHIR)
  • Outils de développement CI/CD ou Github Actions.

De toute façon, un "ingénieur en apprentissage automatique" ("Machine Learning Engineer") mettra inévitablement la main sur ces composants pour fournir des environnements de production tout au long des cycles de vie des services. Nous pourrons en savoir plus au fil du temps.

Dépôt Github

Le code source complet se trouve à l'adresse suivante : https://github.com/zhongli1990/covid-ai-demo-deployment

Le référentiel integratedML-demo-template est également réutilisé avec le nouveau référentiel. 
 

Modèle de déploiement

Le schéma de déploiement logique de ce cadre de test "Démonstration de l'IA dans les Dockers" est présenté ci-dessous.

Pour la démonstration, j'ai délibérément créé deux piles distinctes pour la classification de l'apprentissage profond et le rendu web, puis j'ai utilisé un HAProxy comme équilibreur de charge pour distribuer les requêtes API entrantes entre ces deux piles de manière indépendante.

  • Guniorn + Flask + Tensorflow Serving
  • Univcorn + FaskAPI + Tensorflow Serving GPU

IRIS avec IntegratedML est utilisé pour les échantillons de démonstration d'apprentissage automatique, comme dans l'article précédent de prédiction de l'ICU.

J'ai omis certains composants communs dans la démo actuelle qui seraient nécessaires ou envisagés pour les services de production :

  • Serveurs Web : Nginx ou Apache, etc. Ils seront nécessaires entre HAProxy et Gunicorn/Uvicorn, pour une gestion correcte des sessions HTTP, c'est-à-dire pour éviter les attaques DoS, etc.
  • Gestionnaire de file d'attente et bases de données : RabbitMQ et/ou Redis, etc., entre Flask/FastAPI et le serveur backend, pour un service asynchrone fiable et la persistance des données/configurations, etc.
  • Passerelle API : IAM ou Kong clusters, entre l'équilibreur de charge HAProxy et le serveur web pour la gestion des API sans créer de point singulier de défaillance.
  • Surveillance et alerte : SAM serait bien.
  • Provisionnement pour CI/CD devops : ICM avec K8s serait nécessaire pour le déploiement et la gestion neutre en nuage, et pour CI/CD avec d'autres outils devops communs.

En fait,  IRIS lui-même peut certainement être utilisé comme gestionnaire de file d'attente de niveau entreprise ainsi que comme base de données performante pour une messagerie fiable. Dans l'analyse des modèles, il apparaît qu'IRIS peut remplacer les courtiers de file d'attente et les bases de données RabbitMQ/Redis/MongoDB, etc., et qu'il serait donc mieux consolidé avec une latence bien moindre et de meilleures performances globales. Et plus encore, IRIS Web Gateway (anciennement CSP Gateway) peut certainement être positionné à la place de Gunicorn ou Unicorn, etc, n'est-ce pas ?  

Topologie de l'environnement

Il existe quelques options courantes pour mettre en œuvre le modèle logique ci-dessus dans tous les composants Docker. Les plus courantes sont les suivantes :  

  • docker-compose
  • docker swarm etc
  • Kubernetes etc 
  • ICM avec K8s Operation

Cette démonstration commence avec "docker-compose" pour un PoC fonctionnel et un certain benchmarking. Nous aimerions certainement utiliser K8s et peut-être aussi ICM au fil du temps. 

Comme décrit dans son fichier docker-compose.yml, une implémentation physique de sa topologie d'environnement sur un serveur AWS Ubuntu ressemblerait à ceci :  

Le diagramme ci-dessus montre comment ces ports de service de toutes les instances Docker sont mappés et exposés directement sur le serveur Ubuntu à des fins de démonstration. En production, la sécurité devrait être renforcée. Et pour les besoins de la démonstration, tous les conteneurs sont connectés au même réseau Docker, alors qu'en production, il serait séparé en routable externe et non-routable interne.

Composants "Dockerisés" 

Le tableau ci-dessous montre comment les volumes de stockage de la machine hôte sont montés sur chaque instance de conteneur comme spécifié dans ce fichier docker-compose.yml

ubuntu@ip-172-31-35-104:/zhong/flask-xray$ tree ./ -L 2

./├── covid19                             (Les conteneurs Flask+Gunicorn et Tensorflow Serving seront montés ici)├── app.py                                (Flask main app:  Les interfaces de l'application web et du service API sont définies et mises en œuvre ici)├── covid19_models               (Les modèles Tensorflow sont publiés et versionnés ici pour la classification des images Le conteneur Tensorflow Serving avec CPU)├── Dockerfile                          (Le serveur Flask avec Gunicorn:      CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000", "--workers", "4", "--threads", "2"])├── models                               (Modèles au format .h5 pour l'application Flask et démonstration API de la génération de heatmaps par grad-cam sur des radiographies.)├── __pycache__├── README.md├── requirements.txt             (Paquets Python nécessaires pour les applications complètes de Flask+Gunicorn) ├── scripts├── static                                  (Fichiers statiques Web)├── templates                         (Modèles de rendu Web)├── tensorflow_serving        (Fichier de configuration pour le service de tensorflow)└── test_images├── covid-fastapi                   (Les conteneurs FastAPI+Uvicorn et Tensorflow Serving avec GPU seront définis ici)├── covid19_models            (Les modèles Tensorflow au service des GPU sont publiés et versionnés ici pour la classification des images)├── Dockerfile                       (Le serveur Uvicorn+FastAPI sera lancé ici: CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4" ])├── main.py                           (FastAPI app: les interfaces de l'application web et du service API sont définies et mises en œuvre ici)├── models                            (Modèles au format .h5 pour l'application FastAPI et démonstration API de la génération de heatmaps par grad-cam sur des radiographies)├── __pycache__├── README.md├── requirements.txt├── scripts├── static├── templates├── tensorflow_serving└── test_images├── docker-compose.yml      (Full stack Docker definition file.  La version 2.3 est utilisée pour tenir compte du GPU Docker "nvidia runtime", sinon la version 3.x peut être utilisée)├── haproxy                             (Le service docker HAProxy est défini ici.  Note : une session collante peut être définie pour le backend LB. )                             ├── Dockerfile└── haproxy.cfg└── notebooks                       (Le service conteneur Jupyter Notebook avec Tensorflow 2.2 et Tensorboard etc)├── Dockerfile├── notebooks                  (Exemples de fichiers notebook pour émuler des applications API Client externes pour les tests fonctionnels et les tests de référence API en Python sur l'équilibreur de charge, etc)└── requirements.txt

Remarque: Le docker-compose.yml ci-dessus est destiné à la démonstration d'apprentissage profond de Convid X-Rays. Il est utilisé avec le docker-compose.yml d'un autre integratedML-demo-template pour former la pile de services complète, comme indiqué dans la topologie de l'environnement.  

Démarrage des services 

Un simple docker-compose up -d permettrait de démarrer tous les services de conteneurs :

ubuntu@ip-172-31-35-104:~$ docker ps
ID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMS
31b682b6961d        iris-aa-server:2020.3AA               "/iris-main"             Il y a 7 semaines         Jusqu'à 2 jours (en bonne santé)   2188/tcp, 53773/tcp, 54773/tcp, 0.0.0.0:8091->51773/tcp, 0.0.0.0:8092->52773/tcp   iml-template-master_irisimlsvr_1
6a0f22ad3ffc        haproxy:0.0.1                         "/docker-entrypoint.…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8088->8088/tcp                                                             flask-xray_lb_1
71b5163d8960        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8056->8000/tcp                                                             flask-xray_fastapi_1
400e1d6c0f69        tensorflow/serving:latest-gpu         "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8520->8500/tcp, 0.0.0.0:8521->8501/tcp                                     flask-xray_tf2svg2_1
eaac88e9b1a7        ai-service-flask:0.1.0                "gunicorn app:app --…"   Шl y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8051->5000/tcp                                                             flask-xray_flask_1
e07ccd30a32b        tensorflow/serving                    "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8510->8500/tcp, 0.0.0.0:8511->8501/tcp                                     flask-xray_tf2svg1_1
390dc13023f2        tf2-jupyter:0.1.0                     "/bin/sh -c '/bin/ba…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8506->6006/tcp, 0.0.0.0:8586->8888/tcp                                     flask-xray_tf2jpt_1
88e8709404ac        tf2-jupyter-jdbc:1.0.0-iml-template   "/bin/sh -c '/bin/ba…"   Il y a 2 $ois         Jusqu'à 2 jours             0.0.0.0:6026->6006/tcp, 0.0.0.0:8896->8888/tcp                                     iml-template-master_tf2jupyter_1

docker-compose up --scale fastapi=2 --scale flask=2 -d   par exemple, sera mis à l'échelle horizontalement jusqu'à 2 conteneurs Gunicorn+Flask et 2 conteneurs Univcorn+FastAPI :

ubuntu@ip-172-31-35-104:/zhong/flask-xray$ docker ps
ID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMS
dbee3c20ea95        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8057->8000/tcp                                                             flask-xray_fastapi_2
95bcd8535aa6        ai-service-flask:0.1.0                "gunicorn app:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8052->5000/tcp                                                             flask-xray_flask_2

... ...

L'exécution d'un autre "docker-compose up -d" dans le répertoire de travail de "integrtedML-demo-template" a fait apparaître le conteneur irisimlsvr et tf2jupyter dans la liste ci-dessus.

Tests

1. Application web de démonstration de l'IA avec une interface utilisateur simple

Après avoir démarré les services docker ci-dessus, nous pouvons visiter une application web de démonstration pour X-Ray Covid-19 lung detection hébergée dans une instance AWS EC2 à une adresse temporaire à http://ec2-18-134-16-118.eu-west-2.compute.amazonaws.com:8056/

Voici ci-dessous quelques écrans capturés depuis mon mobile. L'interface utilisateur de démonstration est très simple : en gros, je clique sur "Choose File" puis sur le bouton "Submit" pour télécharger une image radiographique, puis l'application affiche un rapport de classification. S'il s'agit d'une radiographie Covid-19, une [carte thermique sera affichée] (https://community.intersystems.com/post/explainability-and-visibility-covid-19-x-ray-classifiers-deep-learning) pour reproduire la zone de lésion "détectée" par DL ; sinon, le rapport de classification ne montrera que l'image radiographique téléchargée.

        

L'application web est une page serveur Python dont la logique est principalement codée dans le fichier main.py de FastAPI, ainsi que dans le fichier app.py de Flask.

Quand j'aurai un peu plus de temps libre, je pourrais documenter en détail les différences de codage et de convention entre Flask et FastAPI. En fait, j'espère pouvoir faire un hébergement de démonstration Flask vs FastAPI vs IRIS pour l'IA. 

2. Test des API de démonstration      

FastAPI (exposé au port 8056) a intégré des documents Swagger API, comme indiqué ci-dessous. C'est très pratique. Tout ce que j'ai à faire est d'utiliser "/docs" dans son URL, par exemple : 

J'ai intégré quelques paramètres (tels que /hello et /items) et quelques interfaces API de démonstration (telles que /healthcheck, /predict, et predict/heatmap).

Testons rapidement ces API, en exécutant quelques lignes Python (en tant qu'émulateur d'application client API) dans l'un des [fichiers d'échantillons de Jupyter Notebook que j'ai créés] (https://github.com/zhongli1990/covid-ai-demo-deployment/tree/master/notebooks/notebooks) pour ce service de démonstration de l'IA.  

Ci-dessous, j'exécute ce fichier à titre d'exemple : https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/notebooks/notebooks/Covid19-3class-Heatmap-Flask-FastAPI-TF-serving-all-in-one-HAProxy2.ipynb

Tout d'abord pour tester que le backend TF-Serving (port 8511) et TF-Serving-GPU (port 8521) sont en place et fonctionnent : 

!curl http://172.17.0.1:8511/v1/models/covid19  # servant tensorflow
!curl http://172.17.0.1:8521/v1/models/covid19  # servant tensorflow-gpu
{
 "model_version_status": [
  {
   "version": "2",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}
{
 "model_version_status": [
  {
   "version": "2",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

 

Ensuite, vérifiez que les services API suivants sont en place et fonctionnent :

  • Gunicorn+Flask+TF-Serving
  • Unicorn+FastAPI+TF-Serving-GPU
  • Equilibreur de charge HAProxy en face des services gênants ci-dessus
  • r = requests.get('http://172.17.0.1:8051/covid19/api/v1/healthcheck')  # tf servant le docker avec le cpu
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8056/covid19/api/v1/healthcheck')  # tf-servant le docker avec le gpu
    print(r.status_code, r.text)
    r = requests.get('http://172.17.0.1:8088/covid19/api/v1/healthcheck')  # tf-servant le docker avec le HAproxy
    print(r.status_code, r.text)

    Et les résultats attendus seraient :

    200 L'API du détecteur Covid19 est en ligne !
    200 "L'API du détecteur Covid19 est en ligne !\n\n"
    200 "L'API du détecteur Covid19 est en ligne !\n\n"

     

    Tester une interface API fonctionnelle, telle que **/predict/heatmap ** pour renvoyer le résultat de la classification et de la heatmap d'une image radiographique d'entrée. L'image entrante est codée en based64 avant d'être envoyée via HTTP POST conformément aux définitions de l'API :

    %%time# Importation de la bibliothèque des requêtes
    import argparse
    import base64import requests# définition d'un point d'entrée ap pour api
    API_ENDPOINT = "http://172.17.0.1:8051/covid19/api/v1/predict/heatmap"image_path = './Covid_M/all/test/covid/nejmoa2001191_f3-PA.jpeg'
    #image_path = './Covid_M/all/test/normal/NORMAL2-IM-1400-0001.jpeg'
    #image_path = './Covid_M/all/test/pneumonia_bac/person1940_bacteria_4859.jpeg'
    b64_image = ""
    # Encoding the JPG,PNG,etc. image to base64 format
    with open(image_path, "rb") as imageFile:
        b64_image = base64.b64encode(imageFile.read())# données à envoyer à l'api
    data = {'b64': b64_image}# envoi d'une requête post et enregistrement de la réponse en tant qu'objet réponse
    r = requests.post(url=API_ENDPOINT, data=data)print(r.status_code, r.text)# extraction de la réponse
    print("{}".format(r.text))

    Toutes ces [images de test ont également été téléchargées sur GitHub] (https://github.com/zhongli1990/Covid19-X-Rays/tree/master/all/test). Le résultat du code ci-dessus sera comme ça:

    200 {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"}
    
    CPU times: user 16 ms, sys: 0 ns, total: 16 ms
    Wall time: 946 ms

     

    3. Les applications de démonstration de services pour les tests benchmarkés

    Nous avons mis en place une instance d'équilibreur de charge HAProxy. Nous avons également démarré un service Flask avec 4 travailleurs, et un service FastAPI avec 4 travailleurs également.

    Pourquoi ne pas créer 8x processus Pyhon directement dans le fichier Notebook, pour émuler 8x clients API simultanés envoyant des requêtes dans les API du service de démonstration, pour voir ce qui se passe ? 

    #from concurrent.futures import ThreadPoolExecutor as PoolExecutor
    from concurrent.futures import ProcessPoolExecutor as PoolExecutor
    import http.client
    import socket
    import timestart = time.time()#laodbalancer:
    API_ENDPOINT_LB = "http://172.17.0.1:8088/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FLASK = "http://172.17.0.1:8052/covid19/api/v1/predict/heatmap"
    API_ENDPOINT_FastAPI = "http://172.17.0.1:8057/covid19/api/v1/predict/heatmap"
    def get_it(url):
        try:
            # boucle sur les images
            for imagePathTest in imagePathsTest:
                b64_image = ""
                with open(imagePathTest, "rb") as imageFile:
                    b64_image = base64.b64encode(imageFile.read())
    
                data = {'b64': b64_image}
                r = requests.post(url, data=data)
                #print(imagePathTest, r.status_code, r.text)
            return r
        except socket.timeout:
            # dans un scénario du monde réel, vous feriez probablement quelque chose si le
            # socket passe en timeout
            passurls = [API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB,
            API_ENDPOINT_LB, API_ENDPOINT_LB]with PoolExecutor(max_workers=16) as executor:
        for _ in executor.map(get_it, urls):
            pass
    
    print("--- %s seconds ---" % (time.time() - start))

    Il a donc fallu 74 secondes pour traiter 8x27 = 216 images de test. Cette pile de démonstration à charge équilibrée était capable de traiter 3 images par seconde (en renvoyant les résultats de la classification et de la heatmap aux clients) :

    --- 74.37691688537598 seconds ---

    À partir de la commande Top de la session Putty, nous pouvons voir que 8x processus de serveur (4x gunicorn + 4 unicorn/python) ont commencé à monter en puissance dès que les scripts de référence ci-dessus ont commencé à être exécutés.

    Suivant

    Cet article n'est qu'un point de départ pour mettre en place une pile de déploiement "All-in-Docker AI demo" comme cadre de test. J'espère ensuite ajouter d'autres interfaces de démonstration API, telles que l'interface de prédiction des soins intensifs Covid-19, idéalement conforme à la norme FHIR R4, etc. Cela pourrait également être un banc d'essai pour explorer une intégration plus étroite avec les capacités ML hébergées par IRIS. Au fil du temps, il peut être utilisé comme un cadre de test (et un cadre assez simple) pour intercepter de plus en plus de modèles ML ou DL spécialisés au fur et à mesure que nous avançons sur divers aspects de l'IA, notamment l'imagerie médicale, la santé de la population ou la prédiction personnalisée, le traitement automatique des langues, etc. J'ai également dressé une liste de souhaits à la toute fin de l'article précédent (dans sa section "Next" (Suivant))

    0
    0 314
    InterSystems officiel Guillaume Rongier · Mars 20, 2022

    L'équipe des Plateformes de Données est très heureuse d'annoncer la version 2021.2 d'InterSystems IRIS Data Platform, InterSystems IRIS for Health et HealthShare Health Connect qui sont désormais généralement disponibles (GA) pour nos clients et partenaires.

    Faits saillants de la version

    InterSystems IRIS Data Platform 2021.2 facilite encore plus le développement, le déploiement et la gestion d'applications augmentées et de processus d'affaires qui relient les entrepôt de données et d'applications. Il a de nombreuses nouvelles fonctionnalités, notamment:

    0
    0 57