Ambari - Comment utiliser les blueprints

Ambari - Comment utiliser les blueprints

By RUMMENS Joris

17 janv. 2018

En tant qu’ingénieurs d’infrastructure chez Adaltas, nous déployons des clusters. Beaucoup de clusters.

Généralement, nos clients choisissent d’utiliser une distribution telle que Hortonworks HDP ou Cloudera CDH, qui viennent avec leurs solutions de déploiement : Ambari et Cloudera Manager respectivement. Ces outils permettent de déployer des clusters facilement depuis leurs interfaces simples et bien documentées.

Bien que pratique pour le déploiement d’un ou deux clusters, devoir remplir plusieurs centaines de champs à coup d’innombrables copier/collés lors du déploiement d’une douzaine de clusters devient une tâche difficile. C’est ici qu’intervient l’automatisation.

Dans cet article, nous nous concentrerons sur l’outil de déploiement de HDP : Ambari, et ses fichiers de définition de cluster : les blueprints.

Que sont les blueprints

Il y a deux définitions aux blueprints dans un environnement Ambari. La première est celle donnée par la documentation d’Ambari :

Ambari Blueprints are a declarative definition of a cluster. With a Blueprint, you specify a Stack, the Component layout and the Configurations to materialize a Hadoop cluster instance (via a REST API) without having to use the Ambari Cluster Install Wizard.

Littéralement :

Ambari Blueprints sont une définition déclarative d’un cluster. Avec un Blueprint, vous spécifiez une Stack, la disposition des composants, et les configurations qui matérialisent une instance de cluster Hadoop (via une API REST), sans avoir à utiliser le Wizard d’installation de cluster Ambari.

Il s’agit de la définition de la technologie Ambari Blueprint dans sa globalité. En pratique, cette technologie se compose de la soumission de deux fichiers JSON l’un après l’autre via l’API REST d’Ambari.

Un de ces fichiers, le premier à être soumis, est la seconde définition d’un blueprint. Il représente un template qui peut être utilisé pour autant de déploiement de cluster que nécessaire. Puisqu’il est utilisé pour définir plusieurs clusters à travers différents environnements, il doit être le plus générique possible.

Le second fichier quant-à lui est utilisé pour définir toutes les propriétés spécifiques à une instance de cluster. Nous l’appellerons ici le fichier cluster. Ambari utilise les informations du fichier blueprint complétées par celles du fichier cluster pour démarrer le processus de déploiement. Les propriétés définies dans le fichier cluster surchargent celles du fichier blueprint lorsque nécessaire.

Voici à quoi ressemble un déploiement de cluster utilisant les blueprints Ambari :

  1. Installation et configuration d’Ambari de façon à être prêt à recevoir la requête de déploiement de cluster
  2. Création et soumission du fichier blueprint.json via l’API REST d’Ambari
  3. Création et soumission du fichier cluster.json via l’API REST d’Ambari
  4. Attente de la fin du processus de déploiement
  5. Tuning des configurations définies automatiquement par le stack advisor d’Ambari.

Structure de fichier - blueprint.json

Le fichier blueprint.json a trois catégories à sa racine :

  • Blueprints, les informations globales du blueprint, qui incluent le nom de la stack et sa version, ainsi que le type de la sécurité.
  • host_groups, le profil des hôtes et les composants déployés sur chacun d’eux.
  • configurations, la plupart des configurations de ses composants qui n’ont pas leurs valeurs par défaut.

A ce point, votre fichier JSON devrait ressembler à ceci :

{
  "Blueprints": {},
  "host_groups": [],
  "configurations": []
}

Contenu de catégorie - blueprints

Ambari supporte le déploiement de plusieurs stacks. Celle pour laquelle il est le plus utilisé est la stack HDP d’hortonworks. C’est celle que nous utiliserons ici dans notre exemple. Quant à la sécurité, choisissez entre NONE et KERBEROS. Vous pouvez ajouter un kerberos_descriptor personnalisé, mais il n’a pas été requis dans notre cas et ne sera donc pas évoqué d’avantage ici.

Voici un exemple simple et fonctionnel de votre catégorie Blueprints pour un cluster HDP 2.6 kerbérisé :

"Blueprints":{
   "stack_name":"HDP",
   "stack_version":"2.6",
   "security":{
      "type":"KERBEROS"
   }
}

Contenu de catégorie - host groups

Les host groups définissent un template à associer à un groupe d’hôtes au sein d’un cluster.

Voici les informations que vous pouvez définir comme template pour un groupe d’hôtes :

  • les composants qui seront déployés sur chacun des hôtes associé à ce profil
  • le nombre d’hôtes attendus pour un profil donné
  • certaines configurations personnalisées qui ne seront appliquées qu’aux hôtes de ce profil
  • un nom qui définit le mieux les hôtes de ce profil

Quelques exemples de groupes d’hôtes que vous pouvez définir dans cette section : management nodes, worker nodes, master nodes, edge nodes…

Notez que vous devrez probablement définir plusieurs profils pour les masters nodes, car ceux-ci ne partagent habituellement pas les même composants.

Pour HDP 2.6, voici la liste de composants disponibles :

hdfs:
    - NAMENODE
    - ZKFC
    - JOURNALNODE
    - DATANODE
    - HDFS_CLIENT
zookeeper:
    - ZOOKEEPER_SERVER
    - ZOOKEEPER_CLIENT
tez:
    - TEZ_CLIENT
yarn:
    - RESOURCEMANAGER
    - APP_TIMELINE_SERVER
    - NODEMANAGER
    - YARN_CLIENT
mapreduce:
    - HISTORYSERVER
    - MAPREDUCE2_CLIENT
slider:
    - SLIDER
ranger:
    - RANGER_ADMIN
    - RANGER_USERSYNC
    - RANGER_TAGSYNC
logsearch:
    - LOGSEARCH_SERVER
    - LOGSEARCH_LOGFEEDER
ambari_infra:
    - INFRA_SOLR
    - INFRA_SOLR_CLIENT
ambari_metrics:
    - METRICS_COLLECTOR
    - METRICS_GRAFANA
    - METRICS_COLLECTOR
hbase:
    - HBASE_MASTER
    - HBASE_REGIONSERVER
    - HBASE_CLIENT
    - PHOENIX_QUERY_SERVER
atlas:
    - ATLAS_SERVER
    - ATLAS_CLIENT
oozie:
    - OOZIE_SERVER
    - OOZIE_CLIENT
kafka:
    - KAFKA_BROKER
storm:
    - STORM_UI_SERVER
    - NIMBUS
    - DRPC_SERVER
    - SUPERVISOR
sqoop:
    - SQOOP
zeppelin:
    - ZEPPELIN_MASTER
hive:
    - HCAT
    - HIVE_SERVER
    - HIVE_SERVER_INTERACTIVE
    - HIVE_METASTORE
    - WEBHCAT_SERVER
    - HIVE_CLIENT
pig:
    - PIG
spark:
    - SPARK_THRIFTSERVER
    - SPARK_CLIENT
    - SPARK_JOBHISTORYSERVER
spark2:
    - SPARK2_THRIFTSERVER
    - SPARK2_CLIENT
    - SPARK2_JOBHISTORYSERVER
kerberos:
    - KERBEROS_CLIENT
knox:
    - KNOX_GATEWAY

Un exemple de groupe d’hôtes pour les workers nodes :

{
   "name":"worker",
   "cardinality":"6",
   "components":[
      { "name":"DATANODE" },
      { "name":"NODEMANAGER" },
      { "name":"LOGSEARCH_LOGFEEDER" },
      { "name":"METRICS_MONITOR" },
      { "name":"HBASE_REGIONSERVER" },
      { "name":"SUPERVISOR" },
      { "name":"KERBEROS_CLIENT" }
   ],
   "configurations":[]
}

Contenu de catégorie - configurations

C’est à cet endroit que la majorité des configurations personnalisées sont positionnées.

Il n’est pas nécessaire de définir toutes les propriétés de chaque composant à déployer. La plupart ont des valeurs par défaut, et Ambari fournit un stack advisor qui en positionne automatiquement d’autres en fonction de l’infrastructure cible. Ajoutez uniquement celles que seuls vous pouvez définir.

La structure d’une entité de configuration est la suivante :

"configurations":[
  {
    "configuration_category":{
      "properties":{
        "property":"value",
        "property":"value"
      }
    }
  },
  {
    "configuration_category":{
      ...
    }
  },
  ...
]

Une catégorie de configuration est un ensemble de propriétés habituellement retrouvées dans un fichier de configuration particulier. Quelques exemples de fichiers de configuration sont : core-site, hdfs-site, hadoop-env, zookeeper-env, etc.

Pour récupérer la liste exhaustive de catégories de configuration supportées par Ambari, vous pouvez soit exporter un blueprint d’un cluster existant qui a les mêmes composants que vous souhaitez déployer, ou jeter un oeil aux sections de configuration sur l’UI. Cependant soyez vigilants, car Ambari divise certaines catégories en plusieurs sections. Par exemple, les propriétés de la catégorie “core-site” peuvent être trouvées dans les sections “Advanced core-site” et “Custom core-site”, mais seront définies dans une unique catégorie “core-site” dans un fichier blueprint.

De plus, une bonne pratique est de laisser Ambari définir les ressources allouées à vos composants dans un premier temps, et les tuner par la suite à travers l’UI.

Il y a une catégorie de configuration qui n’est pas dans l’UI et n’est pas associée à un composant à déployer : cluster-env. C’est une catégorie spéciale pour les propriétés propres à Ambari, et est utilisée par celui-ci pour définir comment exécuter le déploiement. Si vous avez déjà déployé un cluster à travers l’UI, vous remarquerez que ces propriétés correspondent à celles de l’onglet Misc.

Voici une partie de ce que pourrait contenir la catégorie de configuration :

"configurations":[
   {
      "cluster-env":{
         "properties":{
            "ignore_groupsusers_create":"true"
         }
      }
   },
   {
      "core-site":{
         "properties":{
            "fs.defaultFS":"hdfs://mynamespace",
            "ha.zookeeper.quorum":"%HOSTGROUP::zk_node%:2181",
            "ipc.maximum.data.length":"134217728"
         }
      }
   },
   {
      "hadoop-env":{
         "properties":{
            "hdfs_log_dir_prefix":"/path/to/logs/hadoop"
         }
      }
   }
]

Dans l’exemple précédent, vous avez pu remarquer une valeur HOSTGROUP::zk_node%. Il s’agit d’une variable qui sera remplacée par tous les noms d’hôtes correspondant au groupe d’hôtes “zk_node”. Soyez cependant vigilants lors de son utilisation car la conversion n’est pas supportée par toutes les propriétés.

Les propriétés connues qui gèrent la conversion des variables %HOSTGROUP::hg_name% :

core-site:
  - ha.zookeeper.quorum
hdfs-site:
  - dfs.namenode.http-address
  - dfs.namenode.http-address.mynamespace.nn1
  - dfs.namenode.http-address.mynamespace.nn2
  - dfs.namenode.https-address
  - dfs.namenode.https-address.mynamespace.nn1
  - dfs.namenode.https-address.mynamespace.nn2
  - dfs.namenode.rpc-address.mynamespace.nn1
  - dfs.namenode.rpc-address.mynamespace.nn2
  - dfs.namenode.shared.edits.dir
yarn-site:
  - hadoop.registry.zk.quorum
  - yarn.log.server.url
  - yarn.resourcemanager.address
  - yarn.resourcemanager.admin.address
  - yarn.resourcemanager.hostname
  - yarn.resourcemanager.resource-tracker.address
  - yarn.resourcemanager.scheduler.address
  - yarn.resourcemanager.webapp.address
  - yarn.resourcemanager.webapp.https.address
  - yarn.resourcemanager.zk-address
  - yarn.resourcemanager.hostname.rm1
  - yarn.resourcemanager.hostname.rm2
  - yarn.timeline-service.address
  - yarn.timeline-service.webapp.address
  - yarn.timeline-service.webapp.https.address
admin-properties:
  - policymgr_external_url
ranger-kafka-plugin-properties:
  - zookeeper.connect
hbase-site:
  - hbase.zookeeper.quorum
application-properties:
  - atlas.audit.hbase.zookeeper.quorum
  - atlas.graph.index.search.solr.zookeeper-url
  - atlas.graph.storage.hostname
  - atlas.kafka.bootstrap.servers
  - atlas.kafka.zookeeper.connect
oozie-site:
  - oozie.base.url
  - oozie.zookeeper.connection.string
oozie-env:
  - oozie_hostname
kafka-broker:
  - zookeeper.connect
storm-site:
  - storm.zookeeper.servers
hive-site:
  - hive.zookeeper.quorum
  - hive.metastore.uris
hive-interactive-site:
  - hive.llap.zk.sm.connectionString

Lorsqu’elles ne sont pas supportées et que vous êtes contraints d’ajouter les noms d’hôtes réels, définissez ces propriétés dans le fichier cluster.json.

Structure de fichier - cluster.json

Là où le fichier blueprint.json représente le template d’un déploiement de cluster, le fichier cluster.json est l’instanciation d’un déploiement. Ce dernier est donc spécifique à un déploiement de cluster, et contient des valeurs définies en dur.

Le fichier cluster.json a cinq catégories à sa racine :

  • blueprint, le nom du blueprint (template) créé précédemment. Son nom est défini à sa soumission.
  • host_groups, le mapping entre les noms d’hôtes réels de votre infrastructure et les profils auxquels ils appartiennent définis dans le blueprint.
  • configurations, les propriétés spécifiques à ce déploiement de cluster.
  • security, qui a la même valeur que a propriété de la section “Blueprints” du fichier blueprint.json.
  • credentials, les informations de connexion au KDC pour un cluster kerbérisé.

Votre fichier JSON devrait maintenant ressembler à ceci :

{
  "blueprint":"blueprint_name",
  "host_groups":[],
  "configurations":[],
  "security":{
    "type":"NONE|KERBEROS"
  },
  "credentials":[]
}

Contenu de catégorie - host groups

A l’inverse du blueprint, la section host_groups dans le fichier cluster.json sert à associer des hôtes existants à un template défini plus tôt.

Sa structure est relativement intuitive, puisqu’il suffit d’ajouter le nom d’un template, et de lui assigner une liste de noms hôtes :

{
  "name":"group_name",
  "hosts":[
    { "fqdn":"my_hostname" },
    { "fqdn":"my_hostname" },
    ...
  ]
}

En reprenant l’exemple du profil des workers nodes, voici ce que cela donnerait :

"host_groups":[
  {
     "name":"worker",
     "hosts":[
      { "fqdn":"worker1_hostname" },
      { "fqdn":"worker2_hostname" },
      { "fqdn":"worker3_hostname" },
      { "fqdn":"worker4_hostname" },
      { "fqdn":"worker5_hostname" },
      { "fqdn":"worker6_hostname" }
     ]
  },
  {
    "name":"some_other_group",
    "hosts":[
      ...
    ]
  },
  ...
]

Contenu de catégorie - configurations

La majorité de vos configurations devrait déjà avoir été définie dans le fichier blueprint.json, puisqu’elles peuvent être utilisées pour différentes implémentations de cluster.

Il y a cependant deux types de propriétés qui sont limitées à un déploiement :

  • Les configurations dépendantes de l’environnement (utilisateurs et infrastructure)
  • Les configurations qui ne gèrent pas la conversion de la variable %HOSTGROUP::hg_name%

Vous pouvez également ajouter les propriétés qui utilisent celles mentionnées ci-dessus.

Dans la première catégorie, vous trouverez principalement les informations de connexion aux bases de données, d’authentification, et liées au métier comme par exemple les files YARN.

Voici est un exemple de ces configurations :

capacity-scheduler:
  - yarn.scheduler.capacity.root.queues
  - yarn.scheduler.capacity.root.myqueue.capacity
  - yarn.scheduler.capacity.root.myqueue.maximum-capacity
  - yarn.scheduler.capacity.root.myqueue.acl_administer_queue
  - yarn.scheduler.capacity.root.myqueue.acl_submit_applications
  - yarn.scheduler.capacity.root.myqueue.queues
ranger-admin-site:
  - ranger.jpa.jdbc.driver
  - ranger.jpa.jdbc.url
admin-properties:
  - db_host
  - db_name
  - db_user
  - db_root_user
  - db_password
  - db_root_password
ranger-env:
  - ranger_admin_username
  - ranger_admin_password
ranger-hdfs-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-yarn-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-atlas-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-kafka-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-storm-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-hive-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
ranger-knox-audit:
  - xasecure.audit.destination.hdfs.dir # If you enabled HA and thus have a namespace name
logsearch-admin-json:
  - logsearch_admin_password
logsearch-env:
  - logsearch_truststore_password
  - logsearch_keystore_password
logfeeder-env:
  - logfeeder_truststore_password
  - logfeeder_keystore_password
ams-grafana-env:
  - metrics_grafana_username
  - metrics_grafana_password
atlas-env:
  - atlas.admin.username
  - atlas.admin.password
oozie-site:
  - oozie.db.schema.name
  - oozie.service.JPAService.jdbc.password
  - oozie.service.JPAService.jdbc.url
  - oozie.service.JPAService.jdbc.username
hive-site:
  - javax.jdo.option.ConnectionURL
  - ambari.hive.db.schema.name
  - javax.jdo.option.ConnectionUserName
  - javax.jdo.option.ConnectionPassword
hive-env:
  - hive_database_name
knox-env:
  - knox_master_secret

Les propriétés suivantes sont connues pour ne pas gérer la conversion de %HOSTGROUP::hg_name% :

ranger-admin-site:
  - ranger.audit.solr.zookeepers
  - ranger.sso.providerurl
ranger-hdfs-security:
  - ranger.plugin.hdfs.policy.rest.url
ranger-yarn-security:
  - ranger.plugin.yarn.policy.rest.url
ranger-hbase-security:
  - ranger.plugin.hbase.policy.rest.url
ranger-atlas-security:
  - ranger.plugin.atlas.policy.rest.url
ranger-kafka-security:
  - ranger.plugin.kafka.policy.rest.url
ranger-storm-security:
  - ranger.plugin.storm.policy.rest.url
ranger-hive-security:
  - ranger.plugin.hive.policy.rest.url
ranger-knox-security:
  - ranger.plugin.knox.policy.rest.url
ranger-hdfs-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-yarn-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-hbase-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-atlas-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-kafka-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-storm-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-hive-audit:
  - xasecure.audit.destination.solr.zookeepers
ranger-knox-audit:
  - xasecure.audit.destination.solr.zookeepers
application-properties:
  - atlas.rest.address

La catégorie configuration garde la même structure que dans le fichier blueprint.json. Voici un exemple :

"configurations":[
  {
   "ranger-admin-site":{
      "properties":{
         "ranger.jpa.jdbc.driver":"org.postgresql.Driver",
         "ranger.jpa.jdbc.url":"jdbc:postgresql://dbhostname:5432/rangerdb",
         "ranger.audit.solr.zookeepers":"zknode1:2181,zknode2:2181,zknode3:2181/infra-solr",
         "ranger.sso.providerurl":"https://gateway_hostname:8443/gateway/knoxsso/api/v1/websso"
      }
   }
  },
  {
     "ranger-env":{
        "properties":{
           "ranger_admin_username":"dbuser",
           "ranger_admin_password":"dbpassword"
        }
     }
  },
  ...
]

Contenu de catégorie - credentials

Pour les même raisons que les propriétés définies dans cette section, les credentials du KDC d’un cluster sécurisé doivent être définies par déploiement.

Voici la structure de ceux-ci :

"credentials":[
   {
      "alias":"kdc.admin.credential",
      "principal":"myadminprincipal@REALM",
      "key":"principal_password",
      "type":"TEMPORARY"
   }
]

Usage de l’API REST Ambari

Avant de commencer, vérifier que le serveur et les agents Ambari auquel vous souhaitez soumettre le blueprint sont bien démarrés. Le répertoire ambari distant doit également être accessible pour l’installation de composants tels que Ambari-Infra ou Ambari-Metrics.

Repositories registration

Tout d’abord, renseignez les liens des répertoires HDP à utiliser pour ce déploiement. Ceci peut être fait via la requête suivante :

curl -H "X-Requested-By: ambari" -u $user:$password -X PUT -d '{
  "Repositories" : {
    "base_url" : "$HDP_base_url",
    "verify_base_url" : true
  }
}' http://${ambari_host}:8080/api/v1/stacks/HDP/versions/${HDP_version}/operating_systems/${OS}/repositories/HDP-${HDP_version}

curl -H "X-Requested-By: ambari" -u $user:$password -X PUT -d '{
  "Repositories" : {
    "base_url" : "$HDP-UTILS_base_url",
    "verify_base_url" : true
  }
}' http://${ambari_host}:8080/api/v1/stacks/HDP/versions/${HDP_version}/operating_systems/${OS}/repositories/HDP-UTILS-${HDP-UTILS_version}

Pour renseigner les répertoires publics d’hortonworks pour HDP 2.6 sur redhat 7, utilisez ceci :

curl -H "X-Requested-By: ambari" -u admin:admin -X PUT -d '{
  "Repositories" : {
    "base_url" : "http://public-repo-1.hortonworks.com/HDP/centos7/2.x/updates/2.6.1.0",
    "verify_base_url" : true
  }
}' http://${ambari_host}:8080/api/v1/stacks/HDP/versions/2.6/operating_systems/redhat7/repositories/HDP-2.6

curl -H "X-Requested-By: ambari" -u admin:admin -X PUT -d '{
  "Repositories" : {
    "base_url" : "http://public-repo-1.hortonworks.com/HDP-UTILS-1.1.0.21/repos/centos7",
    "verify_base_url" : true
  }
}' http://${ambari_host}:8080/api/v1/stacks/HDP/versions/2.6/operating_systems/redhat7/repositories/HDP-UTILS-1.1.0.21

Soumission des fichiers blueprint

Comme vu précédemment, commencez par soumettre le fichier blueprint. Pour la valeur de ${blueprint_name}, utilisez la même que celle de la propriété blueprint du fichier cluster.json.

curl -H "X-Requested-By: ambari" -X POST -u $user:$password -d '@/path/to/blueprint.json' http://${ambari_host}:8080/api/v1/blueprints/${blueprint_name}

Enfin, soumettez la définition de ce cluster particulé. Il prendra le nom défini par la valeur ${cluster_name}.

curl -H "X-Requested-By: ambari" -X POST -u $user:$password -d '@/path/to/cluster.json' http://${ambari_host}:8080/api/v1/clusters/${cluster_name}

Conclusion

Même en utilisant les blueprints, beaucoup de paramètres de configuration sont à définir. Il peut même être plus fastidieux de créer un seul blueprint que de remplir la multitude de champs pour chaque service dans le wizard d’Ambari. L’usage des blueprints sera utile lors du déploiement de multiple clusters ou de la création et destruction d’environnements à la volée.

Pour ceci, plus que les blueprints seront nécessaires. Pour l’un de nos clients par exemple, nous avons utilisé Puppet pour automatiser la préparation des serveurs et l’installation du serveur et des agents Ambari. Une fois ceci terminé, un script ruby personnalisé est exécuté pour générer les fichiers blueprint.json et cluster.json, et soumettre ceux-ci au Ambari nouvellement installé. Cette solution peut-être remplacée par Ansible ou même un outil d’orchestration personnalisé comme Nikita développé par Adaltas.

Pour conclure, les blueprints Ambari permettent l’automatisation du déploiement d’un cluster HDP (ou toute autre distribution), mais peuvent difficilement le faire seul. Choisissez l’outil qui correspond le mieux à votre besoin ou qui est actuellement utilisé par votre société, et ajoutez-y un générateur JSON pour les fichiers blueprint.json et cluster.json.

Canada - Morocco - France

International locations

10 rue de la Kasbah
2393 Rabbat
Canada

Nous sommes une équipe passionnées par l'Open Source, le Big Data et les technologies associées telles que le Cloud, le Data Engineering, la Data Sciencem le DevOps…

Nous fournissons à nos clients un savoir faire reconnu sur la manière d'utiliser les technologies pour convertir leurs cas d'usage en projets exploités en production, sur la façon de réduire les coûts et d'accélérer les livraisons de nouvelles fonctionnalités.

Si vous appréciez la qualité de nos publications, nous vous invitons à nous contacter en vue de coopérer ensemble.