J'ai parlé dans un article précédent de la gestion de ressources appliquée aux zones Solaris 10. Nous allons aujourd'hui aborder un autre aspect de la gestion des ressources, les projets.

Le projet est un outil permettant de définir des profils d'activité (ou workloads pour nos amis anglophiles), et d'imposer des contraintes d'utilisation de ressources à l'ensemble de ces profils. Il définit un point d'attache sur lequel le système de gestion des ressources peut intervenir. Concrètement, il s'agit d'une définition administrative du même type que le user id ou le group id.

Projets, tâches et processus

Concepts

Une tâche est un groupe de processus explicitement rattaché à un (et un seul) projet. Cette notion est celle d'un arbre de processus qui commence avec une ouverture de session, et tous les processus fils seront donc habituellement rattachés à la même tâche, sauf en cas de changement explicite de projet.

Dans Solaris 10, tout processus est nécessairement rattaché à une (et une seule) tâche, et donc un (et un seul) projet. Il existe un projet "fourre-tout" nommé default, qui n'impose pas de contraintes particulières sur les ressources utilisables.

Le dessin ci-dessous illustre l'emboîtement des différents niveaux :

sol10projet

Gestion de ressources

Toute la gamme des contrôles de ressources peut être appliquée à un projet. Les limitations ainsi configurées s'appliqueront à la somme des processus s'exécutant dans le contexte du projet : par exemple, si un projet limite l'utilisation de mémoire résidente à 2 Go, l'ensemble des processus associés devront se partager ces 2 Go, et, collectivement, ne pourront pas les dépasser.

La définition d'une telle contrainte s'applique à un contrôle (par exemple, project.max-shm-memory, qui correspond à la mémoire partagée utilisable par le projet) et à un niveau de privilège. Ces niveaux sont au nombre de trois :

  • basic : la contrainte peut être modifié par le propriétaire du processus courant
  • privileged : seul le super-utilisateur peut modifier la contrainte
  • system : la contrainte n'est modifiable qu'au reboot

Association à un projet

Les utilisateurs et les groupes appartiennent à des projets, au minimum au projet default, mais potentiellement à plusieurs projets distincts. En revanche, chaque processus appartenant à un utilisateur n'est associé qu'à un seul projet à la fois, déterminé lors de la création du processus.

Parmi les informations stockées pour chaque processus tournant sur le système, on retrouve une structure appelée cred, pour credentials, qui définit les accréditations du processus (par exemple l'uid et le gid du processus). Dans Solaris 10, cette structure contient en outre un identifiant, nommé cr_projid qui indique le projet auquel le processus est rattaché. Le processus contient également un identifiant de tâche, ou taskid.

Création de tâches et héritage

Une nouvelle tâche est automatiquement créée et rattachée au projet par défaut de l'utilisateur concerné lors de l'ouverture d'une nouvelle session par le biais de l'un des appels suivants :

  • login
  • cron
  • su

En dehors de ces situations, tout appel système fork ou exec préservera les identifiants de projet et de tâche du processus parent. Ces informations seront donc propagées au travers de la branche courante de l'arbre des processus.

Il est également possible de changer explicitement de tâche ou de projet par le biais de la commande newtask (ou des appels systèmes setproject et settaskid).

Voici à quoi peut ressembler une session de l'utilisateur user1 qui se logue sur un terminal :

proj2

Mise en œuvre des projets

Commandes principales

  • projects : liste les projets existants ou ceux auxquels un utilisateur est rattaché
  • newtask : permet d'exécuter une commande dans un autre contexte (nouvelle tâche ou nouveau projet)
  • projadd/projmod/projdel : création/modification/suppression de projets
  • prstat -J : l'option -J permet d'avoir une vue en temps réel de la consommation par projet
  • prctl : pour afficher les contraintes de ressources

Le fichier /etc/project

Le principal fichier de configuration ressemble un peu au fichier /etc/group :

# cat /etc/project system:0:::: user.root:1:::: noproject:2:::: default:3:::: group.staff:10::::

Chaque ligne est construite sur le modèle suivant : nom:identifiant:commentaire:utilisateurs:groupes:attributs

Les définitions des champs sont :

  • Nom : le nom du projet, qui sera affiché par la plupart des commandes associées
  • Identifiant : un identifiant numérique associé, similaire à l'uid, sachant que les identifiants inférieurs à 100 sont réservés au système
  • Commentaire : il s'en passe, justement
  • Utilisateurs : les utilisateurs autorisés à travailler dans le contexte du projet
  • Groupes : les groupes autorisés à travailler dans le contexte du projet
  • Attributs : la description des contraintes de ressources associées au projet (séparées par un point-virgule dans le cas de contraintes multiples)

Utilisateurs et projets par défaut

Un utilisateur peut être associé à plusieurs projets, mais il aura toujours un projet par défaut, auquel ses processus seront rattachés lors d'une ouverture de session. On peut l'afficher avec la commande projects -d.

Il y a deux façons de modifier le projet par défaut d'un utilisateur :

  • soit en créant un projet portant le nom user. (par exemple, user.toto), qui sera automatiquement relié à l'utilisateur en question
  • soit en ajoutant une entrée au fichier /etc/user_attr de la forme : utilisateur::::project=nomprojet (par exemple, toto::::project=monprojet)

Ces modifications n'affectent pas les processus en cours, il est nécessaire d'ouvrir une nouvelle session pour en tenir compte.

Un exemple concret

Dans cet exemple, nous allons créer un projet monprojet, lui imposer un contrôle de ressources au niveau de la mémoire partagée utilisable (modifiable dynamiquement par le super-utilisateur, donc en mode privileged), déclarer ce projet comme projet par défaut pour l'utilisateur toto, et vérifier que le contrôle de ressources s'applique bien.

# cat /etc/project system:0:::: user.root:1:::: noproject:2:::: default:3:::: group.staff:10:::: # /usr/sbin/projadd -p 100 -c "Mon projet" -U toto -K project.max-shm-memory=(privileged,1048576,deny) monprojet # cat /etc/project system:0:::: user.root:1:::: noproject:2:::: default:3:::: group.staff:10:::: monprojet:100:Mon projet:toto::project.max-shm-memory=(privileged,1048576,deny) # projects -d toto default

Le projet est créé, et l'utilisateur toto y est associé. Ce n'est cependant pas encore son projet par défaut. Pour réaliser cette modification, nous allons ajouter la ligne suivante au fichier /etc/user_attr :

toto::::project=monprojet

Connectons-nous maintenant en tant que toto pour observer les modifications apportées :

toto$ projects -d monprojet toto$ prctl -n project.max-shm-memory $$ process: 12590: -ksh NAME PRIVILEGE VALUE FLAG ACTION RECIPIENT project.max-shm-memory privileged 1.00MB - deny - system 16.0EB max deny

Comme on le voit, la modification a bien été prise en compte.

Un cas particulier : la mémoire résidente

Le sujet a déjà été abordé avec les zones : les contraintes sur la mémoire résidente (RSS) utilisée sont appliquées par le daemon rcapd. Le nom de ce contrôle est également particulier, dans la mesure où il n'est pas de la forme project.xxx.

Considérons que notre daemon rcapd est déjà activé (si ce n'est pas le cas, on utilise la commande rcapadm -E), et ajoutons une contrainte sur la RSS :

# projmod -s -K rcap.max-rss=209715200 monprojet # grep monprojet /etc/project monprojet:100:Mon projet:u9203118::project.max-shm-memory=(privileged,1048576,deny); rcap.max-rss=209715200

On peut alors observer la consommation de ressources du projet, et constater que la limite fixée (le cap) est bien à 200 Mo comme prévu :

# rcapstat -p id project nproc vm rss cap at avgat pg avgpg 100 monprojet - 3008K 10M 200M 0K 0K 0K 0K 100 monprojet - 5496K 17M 200M 0K 0K 0K 0K 100 monprojet - 5496K 17M 200M 0K 0K 0K 0K 100 monprojet - 3896K 13M 200M 0K 0K 0K 0K 100 monprojet - 3896K 13M 200M 0K 0K 0K 0K