Rechercher

Chapitre 4. Création d'images

download PDF

Apprenez à créer vos propres images de conteneurs, basées sur des images préconstruites qui sont prêtes à vous aider. Le processus comprend l'apprentissage des meilleures pratiques pour l'écriture des images, la définition des métadonnées pour les images, le test des images et l'utilisation d'un flux de travail de construction personnalisé pour créer des images à utiliser avec OpenShift Container Platform. Après avoir créé une image, vous pouvez la pousser vers le registre d'images OpenShift.

4.1. Apprendre les meilleures pratiques en matière de conteneurs

Lors de la création d'images de conteneurs à exécuter sur OpenShift Container Platform, il y a un certain nombre de meilleures pratiques à prendre en compte en tant qu'auteur d'images pour assurer une bonne expérience pour les consommateurs de ces images. Les images étant destinées à être immuables et utilisées telles quelles, les directives suivantes permettent de s'assurer que vos images sont hautement consommables et faciles à utiliser sur OpenShift Container Platform.

4.1.1. Lignes directrices générales concernant les images de conteneurs

Les directives suivantes s'appliquent à la création d'une image de conteneur en général, et sont indépendantes de l'utilisation des images sur OpenShift Container Platform.

Réutiliser les images

Dans la mesure du possible, basez votre image sur une image en amont appropriée en utilisant la déclaration FROM. Cela permet à votre image de récupérer facilement les correctifs de sécurité d'une image en amont lorsqu'elle est mise à jour, plutôt que d'avoir à mettre à jour vos dépendances directement.

En outre, utilisez des balises dans l'instruction FROM, par exemple rhel:rhel7, pour indiquer clairement aux utilisateurs la version d'une image sur laquelle votre image est basée. L'utilisation d'une balise autre que latest permet de s'assurer que votre image n'est pas soumise à des modifications qui pourraient être apportées à la version latest d'une image en amont.

Maintenir la compatibilité entre les balises

Lorsque vous marquez vos propres images, essayez de maintenir la compatibilité ascendante au sein d'une balise. Par exemple, si vous fournissez une image nommée foo et qu'elle inclut actuellement la version 1.0, vous pouvez fournir une balise foo:v1. Lorsque vous mettez à jour l'image, tant qu'elle reste compatible avec l'image originale, vous pouvez continuer à baliser la nouvelle image foo:v1, et les consommateurs en aval de cette balise sont en mesure d'obtenir des mises à jour sans être interrompus.

Si vous publiez ultérieurement une mise à jour incompatible, passez à une nouvelle balise, par exemple foo:v2. Cela permet aux consommateurs en aval de passer à la nouvelle version à leur guise, sans être interrompus par inadvertance par la nouvelle image incompatible. Tout consommateur en aval qui utilise foo:latest prend le risque que des changements incompatibles soient introduits.

Éviter les processus multiples

Ne démarrez pas plusieurs services, tels qu'une base de données et SSHD, à l'intérieur d'un même conteneur. Cela n'est pas nécessaire car les conteneurs sont légers et peuvent être facilement reliés entre eux pour orchestrer plusieurs processus. OpenShift Container Platform vous permet de colocaliser et de cogérer facilement des images connexes en les regroupant dans un seul pod.

Cette colocation garantit que les conteneurs partagent un espace de noms réseau et un espace de stockage pour la communication. Les mises à jour sont également moins perturbantes, car chaque image peut être mise à jour moins fréquemment et indépendamment. Les flux de traitement des signaux sont également plus clairs avec un processus unique, car il n'est pas nécessaire de gérer l'acheminement des signaux vers les processus créés.

Utiliser exec dans les scripts d'accompagnement

De nombreuses images utilisent des scripts enveloppants pour effectuer certaines configurations avant de lancer un processus pour le logiciel en cours d'exécution. Si votre image utilise un tel script, celui-ci utilise exec afin que le processus du script soit remplacé par votre logiciel. Si vous n'utilisez pas exec, les signaux envoyés par le moteur d'exécution de votre conteneur sont transmis à votre script d'encapsulation au lieu du processus de votre logiciel. Ce n'est pas ce que vous souhaitez.

Si vous avez un script wrapper qui démarre un processus pour un serveur. Vous démarrez votre conteneur, par exemple, à l'aide de podman run -i, qui exécute le script wrapper, lequel démarre à son tour votre processus. Si vous souhaitez fermer votre conteneur à l'aide de CTRL C, si votre script wrapper a utilisé exec pour démarrer le processus du serveur, podman envoie SIGINT au processus du serveur et tout se passe comme prévu. Si vous n'avez pas utilisé exec dans votre script wrapper, podman envoie SIGINT au processus du script wrapper et votre processus continue à fonctionner comme si de rien n'était.

Notez également que votre processus s'exécute en tant que PID 1 lorsqu'il est exécuté dans un conteneur. Cela signifie que si votre processus principal se termine, l'ensemble du conteneur est arrêté, ce qui annule tous les processus enfants que vous avez lancés à partir de votre processus PID 1.

Nettoyer les fichiers temporaires

Supprimez tous les fichiers temporaires créés au cours du processus de construction. Cela inclut également tous les fichiers ajoutés avec la commande ADD. Par exemple, exécutez la commande yum clean après avoir effectué les opérations yum install.

Vous pouvez éviter que le cache yum ne se retrouve dans une couche d'image en créant votre déclaration RUN de la manière suivante :

RUN yum -y install mypackage && yum -y install myotherpackage && yum clean all -y

Notez que si vous écrivez à la place :

RUN yum -y install mypackage
RUN yum -y install myotherpackage && yum clean all -y

La première invocation de yum laisse alors des fichiers supplémentaires dans cette couche, et ces fichiers ne peuvent pas être supprimés lorsque l'opération yum clean est exécutée ultérieurement. Les fichiers supplémentaires ne sont pas visibles dans l'image finale, mais ils sont présents dans les couches sous-jacentes.

Le processus actuel de construction des conteneurs ne permet pas à une commande exécutée dans une couche ultérieure de réduire l'espace utilisé par l'image lorsque quelque chose a été supprimé dans une couche antérieure. Toutefois, cela pourrait changer à l'avenir. Cela signifie que si vous exécutez une commande rm dans une couche ultérieure, bien que les fichiers soient cachés, cela ne réduit pas la taille globale de l'image à télécharger. Par conséquent, comme dans l'exemple yum clean, il est préférable de supprimer les fichiers dans la même commande que celle qui les a créés, dans la mesure du possible, afin qu'ils ne finissent pas par être écrits dans un calque.

En outre, l'exécution de plusieurs commandes dans une seule déclaration RUN réduit le nombre de couches de votre image, ce qui améliore le temps de téléchargement et d'extraction.

Placez les instructions dans l'ordre approprié

Le constructeur de conteneurs lit le site Dockerfile et exécute les instructions de haut en bas. Chaque instruction exécutée avec succès crée une couche qui peut être réutilisée lors de la prochaine construction de cette image ou d'une autre. Il est très important de placer les instructions qui changent rarement au sommet de votre Dockerfile. Cela garantit que les prochaines constructions de la même image seront très rapides, car le cache n'est pas invalidé par les modifications des couches supérieures.

Par exemple, si vous travaillez sur un site Dockerfile qui contient une commande ADD pour installer un fichier sur lequel vous êtes en train d'itérer, et une commande RUN pour yum install un paquet, il est préférable de placer la commande ADD en dernier :

FROM foo
RUN yum -y install mypackage && yum clean all -y
ADD myfile /test/myfile

Ainsi, chaque fois que vous modifiez myfile et que vous exécutez à nouveau podman build ou docker build, le système réutilise la couche mise en cache pour la commande yum et ne génère la nouvelle couche que pour l'opération ADD.

Si, au lieu de cela, vous écriviez Dockerfile comme suit :

FROM foo
ADD myfile /test/myfile
RUN yum -y install mypackage && yum clean all -y

Ensuite, chaque fois que vous modifiez myfile et que vous exécutez à nouveau podman build ou docker build, l'opération ADD invalide le cache de la couche RUN, de sorte que l'opération yum doit également être exécutée à nouveau.

Marquer les ports importants

L'instruction EXPOSE rend un port du conteneur accessible au système hôte et aux autres conteneurs. Bien qu'il soit possible de spécifier qu'un port doit être exposé avec une invocation podman run, l'utilisation de l'instruction EXPOSE dans un Dockerfile facilite l'utilisation de votre image par les humains et les logiciels en déclarant explicitement les ports dont votre logiciel a besoin pour fonctionner :

  • Les ports exposés apparaissent sous podman ps associés aux conteneurs créés à partir de votre image.
  • Les ports exposés sont présents dans les métadonnées de l'image renvoyée par podman inspect.
  • Les ports exposés sont liés lorsque vous reliez un conteneur à un autre.
Définir les variables d'environnement

Il est conseillé de définir des variables d'environnement à l'aide de l'instruction ENV. Un exemple est la définition de la version de votre projet. Cela permet aux gens de trouver facilement la version sans avoir à consulter l'instruction Dockerfile. Un autre exemple consiste à annoncer un chemin sur le système qui pourrait être utilisé par un autre processus, tel que JAVA_HOME.

Éviter les mots de passe par défaut

Évitez de définir des mots de passe par défaut. De nombreuses personnes étendent l'image et oublient de supprimer ou de modifier le mot de passe par défaut. Cela peut entraîner des problèmes de sécurité si un utilisateur en production se voit attribuer un mot de passe bien connu. Les mots de passe peuvent être configurés à l'aide d'une variable d'environnement.

Si vous choisissez de définir un mot de passe par défaut, veillez à ce qu'un message d'avertissement approprié s'affiche au démarrage du conteneur. Ce message doit informer l'utilisateur de la valeur du mot de passe par défaut et lui expliquer comment le modifier, par exemple en lui indiquant la variable d'environnement à définir.

Éviter sshd

Il est préférable d'éviter d'exécuter sshd dans votre image. Vous pouvez utiliser la commande podman exec ou docker exec pour accéder aux conteneurs qui s'exécutent sur l'hôte local. Vous pouvez également utiliser la commande oc exec ou la commande oc rsh pour accéder aux conteneurs qui s'exécutent sur le cluster OpenShift Container Platform. L'installation et l'exécution de sshd dans votre image ouvre des vecteurs d'attaque supplémentaires et des exigences en matière de correctifs de sécurité.

Utiliser des volumes pour les données persistantes

Les images utilisent un volume pour les données persistantes. De cette façon, OpenShift Container Platform monte le stockage réseau sur le nœud qui exécute le conteneur, et si le conteneur est déplacé vers un nouveau nœud, le stockage est rattaché à ce nœud. En utilisant le volume pour tous les besoins de stockage persistant, le contenu est préservé même si le conteneur est redémarré ou déplacé. Si votre image écrit des données à des emplacements arbitraires dans le conteneur, ce contenu ne pourra pas être préservé.

Toutes les données qui doivent être conservées même après la destruction du conteneur doivent être écrites sur un volume. Les moteurs de conteneurs prennent en charge un indicateur readonly pour les conteneurs, qui peut être utilisé pour appliquer strictement les bonnes pratiques concernant l'absence d'écriture de données sur un stockage éphémère dans un conteneur. En concevant votre image autour de cette capacité dès maintenant, il sera plus facile d'en tirer parti ultérieurement.

La définition explicite des volumes dans votre site Dockerfile permet aux utilisateurs de l'image de comprendre facilement quels volumes ils doivent définir lors de l'exécution de votre image.

Voir la documentation Kubernetes pour plus d'informations sur l'utilisation des volumes dans OpenShift Container Platform.

Note

Même avec les volumes persistants, chaque instance de votre image possède son propre volume, et le système de fichiers n'est pas partagé entre les instances. Cela signifie que le volume ne peut pas être utilisé pour partager l'état dans un cluster.

4.1.2. Directives spécifiques à OpenShift Container Platform

Les directives suivantes s'appliquent lors de la création d'images de conteneurs spécifiquement destinées à être utilisées sur OpenShift Container Platform.

4.1.2.1. Activer les images pour la conversion source-image (S2I)

Pour les images destinées à exécuter un code d'application fourni par un tiers, comme une image Ruby conçue pour exécuter un code Ruby fourni par un développeur, vous pouvez permettre à votre image de fonctionner avec l'outil de construction Source-to-Image (S2I). S2I est un cadre qui facilite l'écriture d'images qui prennent le code source d'une application en entrée et produisent une nouvelle image qui exécute l'application assemblée en sortie.

4.1.2.2. Prise en charge d'identifiants d'utilisateurs arbitraires

Par défaut, OpenShift Container Platform exécute les conteneurs en utilisant un identifiant utilisateur attribué arbitrairement. Cela fournit une sécurité supplémentaire contre les processus qui s'échappent du conteneur en raison d'une vulnérabilité du moteur de conteneur et qui obtiennent ainsi des permissions accrues sur le nœud hôte.

Pour qu'une image puisse être exécutée par un utilisateur arbitraire, les répertoires et les fichiers sur lesquels les processus de l'image écrivent doivent appartenir au groupe root et être accessibles en lecture/écriture par ce groupe. Les fichiers à exécuter doivent également disposer des autorisations d'exécution du groupe.

L'ajout de ce qui suit à votre fichier Docker définit les autorisations de répertoire et de fichier pour permettre aux utilisateurs du groupe root d'y accéder dans l'image construite :

RUN chgrp -R 0 /some/directory && \
    chmod -R g=u /some/directory

L'utilisateur du conteneur étant toujours membre du groupe root, il peut lire et écrire ces fichiers.

Avertissement

Il convient d'être prudent lorsque l'on modifie les répertoires et les autorisations de fichiers des zones sensibles d'un conteneur, ce qui n'est pas différent d'un système normal.

S'il est appliqué à des zones sensibles, telles que /etc/passwd, il peut permettre la modification de ces fichiers par des utilisateurs involontaires, exposant ainsi potentiellement le conteneur ou l'hôte. CRI-O prend en charge l'insertion d'identifiants d'utilisateurs arbitraires dans le conteneur /etc/passwd, de sorte qu'il n'est jamais nécessaire de modifier les autorisations.

En outre, les processus exécutés dans le conteneur ne doivent pas écouter les ports privilégiés, c'est-à-dire les ports inférieurs à 1024, puisqu'ils ne sont pas exécutés en tant qu'utilisateur privilégié.

Important

Si votre image S2I n'inclut pas de déclaration USER avec un utilisateur numérique, vos constructions échouent par défaut. Pour permettre aux images qui utilisent des utilisateurs nommés ou l'utilisateur root 0 de construire dans OpenShift Container Platform, vous pouvez ajouter le compte de service de construction du projet, system:serviceaccount:<your-project>:builder, à la contrainte de contexte de sécurité (SCC) anyuid. Vous pouvez également autoriser toutes les images à s'exécuter sous n'importe quel utilisateur.

4.1.2.3. Utiliser des services pour la communication entre images

Dans les cas où votre image doit communiquer avec un service fourni par une autre image, telle qu'une image frontale web qui doit accéder à une image de base de données pour stocker et récupérer des données, votre image consomme un service OpenShift Container Platform. Les services fournissent un point d'accès statique qui ne change pas lorsque les conteneurs sont arrêtés, démarrés ou déplacés. En outre, les services assurent l'équilibrage de la charge pour les demandes.

4.1.2.4. Fournir des bibliothèques communes

Pour les images destinées à exécuter un code d'application fourni par un tiers, assurez-vous que votre image contient les bibliothèques couramment utilisées pour votre plate-forme. En particulier, fournissez des pilotes de bases de données pour les bases de données courantes utilisées avec votre plateforme. Par exemple, fournissez des pilotes JDBC pour MySQL et PostgreSQL si vous créez une image Java Framework. En procédant ainsi, il n'est pas nécessaire de télécharger des dépendances communes au moment de l'assemblage de l'application, ce qui accélère la création d'images d'application. Cela simplifie également le travail des développeurs d'applications qui doivent s'assurer que toutes leurs dépendances sont respectées.

4.1.2.5. Utiliser les variables d'environnement pour la configuration

Les utilisateurs de votre image peuvent la configurer sans avoir à créer une image en aval basée sur votre image. Cela signifie que la configuration de l'exécution est gérée à l'aide de variables d'environnement. Pour une configuration simple, le processus en cours d'exécution peut consommer directement les variables d'environnement. Pour une configuration plus compliquée ou pour les processus d'exécution qui ne le supportent pas, configurez le processus d'exécution en définissant un fichier de configuration modèle qui est traité lors du démarrage. Au cours de ce traitement, les valeurs fournies par les variables d'environnement peuvent être substituées dans le fichier de configuration ou utilisées pour décider des options à définir dans le fichier de configuration.

Il est également possible et recommandé de transmettre des secrets tels que des certificats et des clés dans le conteneur à l'aide de variables d'environnement. Cela permet de s'assurer que les valeurs secrètes ne se retrouvent pas engagées dans une image et ne fuient pas dans un registre d'image de conteneur.

Le fait de fournir des variables d'environnement permet aux consommateurs de votre image de personnaliser le comportement, comme les paramètres de la base de données, les mots de passe et le réglage des performances, sans avoir à introduire une nouvelle couche au-dessus de votre image. Au lieu de cela, ils peuvent simplement définir des valeurs de variables d'environnement lors de la définition d'un pod et modifier ces paramètres sans reconstruire l'image.

Pour des scénarios extrêmement complexes, la configuration peut également être fournie à l'aide de volumes qui seront montés dans le conteneur au moment de l'exécution. Toutefois, si vous optez pour cette solution, vous devez vous assurer que votre image fournit des messages d'erreur clairs au démarrage lorsque le volume ou la configuration nécessaire n'est pas présent.

Cette rubrique est liée à la rubrique Using Services for Inter-image Communication en ce sens que la configuration comme les datasources est définie en termes de variables d'environnement qui fournissent les informations sur le point de terminaison du service. Cela permet à une application de consommer dynamiquement un service de source de données défini dans l'environnement OpenShift Container Platform sans modifier l'image de l'application.

En outre, le réglage est effectué en inspectant les paramètres cgroups pour le conteneur. Cela permet à l'image de s'adapter à la mémoire, au processeur et aux autres ressources disponibles. Par exemple, les images basées sur Java adaptent leur tas en fonction du paramètre de mémoire maximale de cgroup afin de s'assurer qu'elles ne dépassent pas les limites et n'obtiennent pas d'erreur de mémoire insuffisante.

4.1.2.6. Définir les métadonnées de l'image

Définir des métadonnées d'image aide OpenShift Container Platform à mieux consommer vos images de conteneurs, permettant à OpenShift Container Platform de créer une meilleure expérience pour les développeurs qui utilisent votre image. Par exemple, vous pouvez ajouter des métadonnées pour fournir des descriptions utiles de votre image, ou offrir des suggestions sur d'autres images qui sont nécessaires.

4.1.2.7. Regroupement

Vous devez bien comprendre ce que signifie l'exécution de plusieurs instances de votre image. Dans le cas le plus simple, la fonction d'équilibrage de charge d'un service gère le routage du trafic vers toutes les instances de votre image. Cependant, de nombreux frameworks doivent partager des informations pour procéder à l'élection d'un leader ou à un basculement, par exemple dans le cadre de la réplication d'une session.

Réfléchissez à la manière dont vos instances réalisent cette communication lorsqu'elles fonctionnent sur OpenShift Container Platform. Bien que les pods puissent communiquer directement entre eux, leurs adresses IP changent à chaque fois que le pod démarre, s'arrête ou est déplacé. Il est donc important que votre schéma de clustering soit dynamique.

4.1.2.8. Enregistrement

Il est préférable d'envoyer toute la journalisation vers la sortie standard. OpenShift Container Platform collecte la sortie standard des conteneurs et l'envoie au service de journalisation centralisé où elle peut être consultée. Si vous devez séparer le contenu des journaux, préfixez la sortie avec un mot-clé approprié, ce qui permet de filtrer les messages.

Si votre image enregistre les données dans un fichier, les utilisateurs doivent utiliser des opérations manuelles pour entrer dans le conteneur en cours d'exécution et récupérer ou consulter le fichier journal.

4.1.2.9. Sondages sur l'état d'esprit et l'état de préparation

Documenter des exemples de sondes de disponibilité et de préparation qui peuvent être utilisées avec votre image. Ces sondes permettent aux utilisateurs de déployer leur image avec la certitude que le trafic n'est pas acheminé vers le conteneur tant qu'il n'est pas prêt à le gérer, et que le conteneur est redémarré si le processus se trouve dans un état malsain.

4.1.2.10. Modèles

Pensez à fournir un modèle d'exemple avec votre image. Un modèle donne aux utilisateurs un moyen facile de déployer rapidement votre image avec une configuration fonctionnelle. Votre modèle doit inclure les sondes de disponibilité et de préparation que vous avez documentées avec l'image, par souci d'exhaustivité.

Red Hat logoGithubRedditYoutubeTwitter

Apprendre

Essayez, achetez et vendez

Communautés

À propos de la documentation Red Hat

Nous aidons les utilisateurs de Red Hat à innover et à atteindre leurs objectifs grâce à nos produits et services avec un contenu auquel ils peuvent faire confiance.

Rendre l’open source plus inclusif

Red Hat s'engage à remplacer le langage problématique dans notre code, notre documentation et nos propriétés Web. Pour plus de détails, consultez leBlog Red Hat.

À propos de Red Hat

Nous proposons des solutions renforcées qui facilitent le travail des entreprises sur plusieurs plates-formes et environnements, du centre de données central à la périphérie du réseau.

© 2024 Red Hat, Inc.