Configurer et piloter les objets connectés

Salutations, ami lecteur.

Tu remarqueras sans doute que malgré le titre ronflant de cet article, le modèle présenté sur l'image ci-dessus n'est pas une Raspberry Pi mais une Cubieboard 2, de chez Cubietech. En effet, cet article ne sera pas réellement centré sur les contraintes et avantages de l'automatisation uniquement sur les Raspberries, mais sur les mini-ordinateurs de manière générale.

Tout d'abord, définissons ce qu'est un mini-ordinateur :

  • Designé autour de System-on-chip (SoC) de marques diverses (Allwinner, Broadcom, TI, ...)
  • Microprocesseur a basse consommation (ARM, MIPS, ...)
  • Mémoire vive en quantité "réduite" (de moins en moins)
  • Stockage principal sur mémoire flash
  • Alimentation depuis une source d'alimentation réduite (souvent USB) possible

Les mini-ordinateurs rassemblent une large de gamme de produits, allant de téléphones portables type smartphone aux routeurs résidentiels type "box". Leur contrainte principale est d'offrir une puissance de calcul assez élevée par rapport a leur consommation d'énergie, n'excédant pas en général les 2 Ampères.

On notera que les différences entre ordinateurs standards et mini-ordinateurs se gomment de plus en plus, ces derniers étant pourvus de processeurs toujours plus puissants et de mémoire vive en grande quantité. En effet, le modèle présenté en photo dispose d'un processeur bi-coeur a ~1Ghz et 1Go de RAM, mais les modèles les plus modernes au moment de l'écriture de ce billet disposent de plus de 2Go de RAM, des processeurs octo-coeurs ARM "hybrides" (architecture "big.LITTLE"), une connectivité sans fil WiFi/Bluetooth/..., du stockage intégré, ...

Pour résumer: la frontière se brouille de plus en plus.

Malgré leur puissance toujours plus grande, ces types d'ordinateurs ont tout de même des contraintes particulières en plus par rapport a leurs grand frères, autant du fait de leur architecture que de l'environnement dans lequel ils sont utilisés. En effet, un ordinateur de ce type sera souvent utilisé là où la consommation électrique est un facteur important, par exemple dans un panneau publicitaire, un faux plafond, des combles ou bien en cluster. De ce fait, plusieurs choses changent :

  • L'économie en ressources est importante (autant côté CPU que RAM)
  • Nous ne sommes pas sur du x86
  • Le réseau est une ressource précieuse (et potentiellement coûteuse)

L'automatisation de systèmes standards se fait généralement à l'aide d'outils tels que Rudder, CFEngine, Puppet, Chef, Salt, Ansible (ou bien parfois de manière plus roots à l'aide de scripts shell distribués de manière diverses et variées)

L'utilisation de ces outils est à adapter aux contraintes de ce genres de machines. En effet, l'instanciation d'un espace utilisateur Ruby ou l'émission en permanence de lourdes requêtes vers un serveur centralisé, par exemple, sont à proscrire. Il faut plus penser à ces machines comme des unités légères autonomes et spécialisées que comme des serveurs standard et polyvalents.

Voici quelques exemples de recommendations à respecter :

Connaître les contraintes associées à l'environnement cible

Tout d'abord, il faut bien évaluer de quoi l'outil disposera pour travailler. Les spécifications système conditionneront ce que nous pourrons tolérer en occupation des ressources, la connectivité ce que nous pourrons faire niveau réseau, l'emplacement la criticité d'un accès physique possible à la machine... De plus, certaines machines de ce type, notemment sur système Android, montent la partition système en lecture seule, toute opération de modification nécessitant un remontage en lecture-écriture.

Certaines techniques comme l'utilisation d'un RAMdisk peuvent vous aider à obtenir de meilleures performances si il est possible de sacrifier quelques Mo de mémoire vive.

Choisir avec soin ce que l'on veut configurer

Plus que l'outil d'automatisation lui même, il est important de choisir ce que l'on veut réellement configurer. Toute vérification a un coût, autant en mémoire qu'en traitement CPU et I/O. Redémarrer un service en boucle quand ledit service met quelques secondes à se lancer n'est pas une idée formidable, lancer une vérification d'intégrité a base de checksums sur un répertoire non plus.

Utiliser un outil économe

L'utilisation d'un environnement économe, autant en taille qu'en efficacité d'utilisation, est aussi important. De préférence, un outil capable de tourner en standalone et avec une empreinte mémoire faible et raisonablement optimisé. Un outil développé en C ou en Python aura plus de chances de disposer d'un environnement optimisé pour l'archtecture utilisée qu'un outil développé en Ruby ou en Java. (Ce point peut paraître subjectif, mais il est important a prendre en compte, du fait par exemple de la relative immaturité à l'époque actuelle des implémentations type OpenJDK sur processeur ARM, au niveau optimisation, l'internet regorgeant de comparatifs sur le sujet. Python propose en plus beaucoup de modules optimisés via l'utilisation de code C natif)

Utiliser le réseau avec parcimonie

Le réseau accessible pour un mini-ordinateur "dans la nature" est souvent au mieux une laison sans fil WiFi, au pire une liaison 2G/3G/... Cela a des implications autant au niveau du débit et de la bande passante utilisable que au niveau de l'archtecture :

  • Utiliser une achitecture "pull based" : un mode "push" a de fortes chances de ne pas passer les NAT sur le réseau, surtout sur une connexions type 2G/3G/...
  • N'émettre et ne recevoir qu'avec parcimonie : la bande passante est souvent chère et précieuse sur un lien radio. Une mise à jour à intervalles larges et un reporting synthétique sont indispensables.
  • Ne jamais assumer que le réseau sera disponible en permanence, ça ne sera pas le cas

Les outils

Assez de théorie, parlons des outils pouvant être utilisés dans le cadre de ces contraintes:

Rudder et CFEngine

  • Code en C (+ Perl pour les inventaires, dans le cas de Rudder)
  • Faible empreinte mémoire
  • Utilisation réseau dépendente de l'intervalle de mise à jour, et du mode de reporting utilisé dans le cas de Rudder

Salt et Ansible

  • Code en Python
  • Empreinte mémoire modérée
  • Utilisation réseau dépendente de l'intervalle de mise à jour

Puppet et Chef

  • Programmé en Ruby
  • Empreinte mémoire modérée / forte
  • Utilisation réseau assez importante en utilisation client/serveur (envoi d'informations du client, calcul des règles par le serveur, et récupération des règles à appliquer par le client)

Le mot de la fin ?

L'automatisation sur des plateformes embarquées, c'est tout a fait possible ! Il suffit de bien prendre en compte les contraintes associées a ce genre d'environnements. J'aurais plutôt tendance a recommander un outil type Rudder (en toute objectivité bien sûr), CFEngine, Salt ou Ansible, tout en rappelant bien que l'utilisation de ce genre d'outils doit se faire avec responsabilité vu les ressources limitées de ces environnements.