Comment créer des services flexibles pour un cluster CoreOS avec des fichiers d’unités de flotte

introduction

Les installations CoreOS exploitent un certain nombre d’outils pour faciliter la gestion des services de clustering et des services contenus dans Docker. Alors que + etcd + est impliqué dans la liaison entre les nœuds distincts et dans la fourniture d’une zone pour les données globales, la plupart des tâches de gestion et d’administration de services actuelles impliquent de travailler avec le démon + flotte +.

Dans un https://www.digitalocean.com/community/tutorials/how-to-use-fleet-and-fleetctl-to-manage-your-coreos-cluster[previous guide (précédent), nous avons examiné l’utilisation de base du Commande + fleetctl + pour manipuler les services et les membres du cluster. Dans ce guide, nous avons brièvement abordé les fichiers d’unité utilisés par le parc pour définir les services, mais il s’agit d’exemples simplifiés permettant de fournir un service fonctionnel permettant d’apprendre + fleetctl +.

Dans ce guide, nous explorerons en profondeur les fichiers d’unités `flotte + 'pour apprendre à les créer, ainsi que certaines techniques permettant de rendre vos services plus robustes en production.

Conditions préalables

Pour terminer ce didacticiel, nous supposerons que vous avez un cluster CoreOS configuré comme décrit dans notre https://www.digitalocean.com/community/tutorials/how-to-set-up-a-coreos-cluster-on. -digitalocean [guide de regroupement]. Cela vous laissera avec trois serveurs nommés en tant que tels:

  • coreos-1

  • coreos-2

  • coreos-3

Bien que la majeure partie de ce didacticiel se concentre sur la création de fichiers d’unité, ces machines seront utilisées ultérieurement pour démontrer les effets de la planification de certaines directives.

Nous supposerons également que vous avez lu notre guide sur how to use flottectl. Vous devez avoir une connaissance pratique de + fleetctl + afin de pouvoir soumettre et utiliser ces fichiers unités avec le cluster.

Une fois ces conditions remplies, poursuivez avec le reste du guide.

Sections et types de fichier d’unité

Étant donné que l’aspect gestion des services de + flotte + repose principalement sur les systèmes initiaux + systemd + de chaque système local, les fichiers unitaires + systemd + sont utilisés pour définir les services.

Bien que les services constituent de loin le type d’unités le plus souvent configuré avec CoreOS, il existe en réalité d’autres types d’unités pouvant être définis. Ce sont un sous-ensemble de ceux disponibles dans les fichiers unitaires conventionnels + systemd +. Chacun de ces types est identifié par le type utilisé comme suffixe de fichier, comme + example.service +:

  • * service *: Il s’agit du type de fichier unité le plus courant. Il est utilisé pour définir un service ou une application pouvant être exécuté sur l’une des machines du cluster.

  • * socket *: Définit les détails d’un socket ou de fichiers de type socket. Ceux-ci incluent les sockets réseau, les sockets IPC et les tampons FIFO. Celles-ci sont utilisées pour appeler des services afin de démarrer lorsque du trafic est vu dans le fichier.

  • * device *: Définit les informations sur un périphérique disponible dans l’arborescence de périphériques d’udev. Systemd les créera au besoin sur des hôtes individuels pour les périphériques du noyau en fonction de règles udev. Celles-ci sont généralement utilisées pour les problèmes de commande afin de s’assurer que les périphériques sont disponibles avant de tenter le montage.

  • * mount *: définit des informations sur un point de montage pour un périphérique. Ceux-ci sont nommés d’après les points de montage auxquels ils font référence, les barres obliques étant remplacées par des tirets.

  • * automount *: définit un point de montage automatique. Elles suivent les mêmes conventions d’appellation que les unités de montage et doivent être accompagnées de l’unité de montage associée. Celles-ci sont utilisées pour décrire le montage à la demande et en parallèle.

  • * timer *: Définit une minuterie associée à une autre unité. Lorsque le moment défini dans ce fichier est atteint, l’unité associée est démarrée.

  • * chemin *: définit un chemin pouvant être surveillé pour l’activation basée sur le chemin. Ceci peut être utilisé pour démarrer une autre unité lorsque des modifications sont apportées à un certain chemin.

Bien que ces options soient toutes disponibles, les unités de service seront utilisées le plus souvent. Dans ce guide, nous ne parlerons que de la configuration des unités de service.

Les fichiers d’unité sont de simples fichiers texte se terminant par un point et l’un des suffixes ci-dessus. A l’intérieur, ils sont organisés par sections. Pour + flotte +, la plupart des fichiers d’unité auront le format général suivant:

[Unit]



[Service]




[X-Fleet]

Les en-têtes de section et tout le reste dans un fichier unité sont sensibles à la casse. La section + [Unit] + est utilisée pour définir des informations génériques sur une unité. Les options communes à tous les types d’unité sont généralement placées ici.

La section + [Service] + est utilisée pour définir les directives spécifiques aux unités de service. La plupart (mais pas la totalité) des types d’unités ci-dessus ont des sections associées pour des informations spécifiques au type d’unité. Consultez la page de manuel generic systemd pour des liens vers les différents types d’unités et obtenir plus d’informations.

La section + [X-Fleet] + est utilisée pour définir les exigences de planification de l’unité à utiliser avec + flotte +. A l’aide de cette section, vous pouvez exiger que certaines conditions soient remplies pour qu’une unité soit programmée sur un hôte.

Construire le service principal

Pour cette section, nous allons commencer avec une variante du fichier d’unité décrit dans notre https://www.digitalocean.com/community/tutorials/how-to-create-and-run-a-service-on-a-coreos -cluster [guide de base sur l’exécution de services sur CoreOS]. Le fichier s’appelle + apache.1.service + et ressemblera à ceci:

[Unit]
Description=Apache web server service

# Requirements
Requires=etcd.service
Requires=docker.service
Requires=apache-discovery.1.service

# Dependency ordering
After=etcd.service
After=docker.service
Before=apache-discovery.1.service

[Service]
# Let processes take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill apache
ExecStartPre=-/usr/bin/docker rm apache
ExecStartPre=/usr/bin/docker pull /apache
ExecStart=/usr/bin/docker run --name apache -p ${COREOS_PUBLIC_IPV4}:80:80 \
/apache /usr/sbin/apache2ctl -D FOREGROUND

# Stop
ExecStop=/usr/bin/docker stop apache

[X-Fleet]
# Don't schedule on the same machine as other Apache instances
X-Conflicts=apache.*.service

Nous commençons par la section + [Unit] +. Ici, l’idée de base est de décrire l’unité et d’établir les informations de dépendance. Nous commençons par un ensemble d’exigences. Nous avons utilisé des exigences strictes pour cet exemple. Si nous voulions que «+ flotte » tente de démarrer des services supplémentaires, sans nous arrêter en cas d'échec, nous aurions pu utiliser la directive ` Wants +` à la place.

Ensuite, nous listons explicitement ce que la commande des exigences devrait être. Ceci est important pour que les services prérequis soient disponibles quand ils sont nécessaires. C’est aussi la façon dont nous lançons automatiquement le service d’annonce de sidekick, etcd que nous allons créer.

Pour la section + [Service] +, nous désactivons le délai de démarrage du service. La première fois qu’un service est exécuté sur un hôte, le conteneur devra être extrait du registre Docker, ce qui compte pour le délai d’expiration du démarrage. La valeur par défaut est 90 secondes, ce qui prend généralement assez de temps, mais avec des conteneurs plus complexes, cela peut prendre plus de temps.

Nous définissons ensuite le killmode sur none. Ceci est utilisé car le mode kill normal (groupe de contrôle) provoque parfois l’échec des commandes de suppression de conteneur (en particulier lorsqu’il est tenté par l’option + - rm + de Docker). Cela peut causer des problèmes lors du prochain redémarrage.

Nous extrayons le fichier d’environnement afin d’avoir accès aux variables + COREOS_PUBLIC_IPV4 + et, si la mise en réseau privée a été activée lors de la création, aux variables d’environnement + COREOS_PRIVATE_IPV4 +. Celles-ci sont très utiles pour configurer les conteneurs Docker afin qu’ils utilisent les informations de leur hôte spécifique.

Les lignes + ExecStartPre + sont utilisées pour supprimer les fichiers restants des exécutions précédentes afin de s’assurer que l’environnement d’exécution est propre. Nous utilisons + = - + sur les deux premiers pour indiquer que + systemd + devrait ignorer et continuer si ces commandes échouent. Pour cette raison, Docker tentera de supprimer et de supprimer les conteneurs précédents, mais ne craindra rien s’il n’en trouve aucun. Le dernier pré-démarrage est utilisé pour s’assurer que la version la plus récente du conteneur est en cours d’exécution.

La commande de démarrage réelle amorce le conteneur Docker et le lie à l’interface IPv4 publique de la machine hôte. Cela utilise les informations du fichier d’environnement et facilite le changement d’interfaces et de ports. Le processus est exécuté au premier plan car le conteneur se fermera si le processus en cours se termine. La commande d’arrêt tente d’arrêter le conteneur en douceur.

La section + [X-Fleet] + contient une condition simple qui oblige + flotte + à planifier le service sur un ordinateur qui n’exécute pas déjà un autre service Apache. C’est un moyen facile de rendre un service hautement disponible en forçant les services en double à démarrer sur des ordinateurs distincts.

Services à emporter de base pour les principaux services du bâtiment

Dans l’exemple ci-dessus, nous avons passé en revue une configuration assez basique. Cependant, nous pouvons en tirer de nombreuses leçons pour nous aider à créer des services en général.

Quelques comportements à garder à l’esprit lors de la création d’un service principal:

  • * Logique séparée pour les dépendances et l’ordre *: Disposez vos dépendances avec les directives + requiert = + ou + Wants = + selon que l’unité que vous construisez doit échouer si la dépendance ne peut pas être remplie. Séparez le classement avec les lignes séparées + After = + et + Before = + afin que vous puissiez facilement ajuster si les exigences changent. Séparer la liste de dépendance de la commande peut vous aider à déboguer en cas de problème de dépendance.

  • * Gestion de l’enregistrement du service avec un processus séparé *: votre service doit être enregistré avec + etcd + pour tirer parti de la découverte du service et des fonctionnalités de configuration dynamique que cela permet. Cependant, cela devrait être géré par un conteneur «sidekick» séparé pour que la logique reste séparée. Cela vous permettra de faire un rapport plus précis sur la santé du service, vue de l’extérieur, ce dont les autres composants auront besoin.

  • * Soyez conscient de la possibilité que votre service arrive à expiration *: pensez à ajuster la directive + TimeoutStartSec + afin de permettre des heures de début plus longues. Régler ceci sur «0» désactivera un délai d’attente au démarrage. Cela est souvent nécessaire car Docker doit parfois extraire une image (lors de la première utilisation ou lorsque des mises à jour sont trouvées), ce qui peut prendre beaucoup de temps pour initialiser le service.

  • * Ajustez le KillMode si votre service ne s’arrête pas proprement *: soyez conscient de l’option + KillMode + si vos services ou conteneurs semblent s’être arrêtés de manière malpropre. Définir ce paramètre sur «aucun» peut parfois résoudre les problèmes de non-suppression de vos conteneurs après l’arrêt. Cela est particulièrement important lorsque vous nommez vos conteneurs, car Docker échouera si un conteneur portant le même nom a été laissé lors d’une exécution précédente. Consultez la documentation sur KillMode pour plus d’informations

  • * Nettoyez l’environnement avant de démarrer *: en rapport avec l’élément ci-dessus, veillez à nettoyer les anciens conteneurs Docker à chaque démarrage. Vous ne devez pas supposer que la précédente exécution du service s’est terminée comme prévu. Ces lignes de nettoyage doivent utiliser le spécificateur + = - + pour leur permettre d’échouer en mode silencieux si aucun nettoyage n’est requis. Bien que vous deviez normalement arrêter les conteneurs avec + docker stop +, vous devriez probablement utiliser + docker kill + pendant le nettoyage.

  • * Extraire et utiliser des informations spécifiques à l’hôte pour la portabilité du service *: Si vous devez lier votre service à une interface réseau spécifique, ouvrez le fichier + / etc / environment + pour accéder à + ​​COREOS_PUBLIC_IPV4 + et, s’il est configuré , + COREOS_PRIVATE_IPV4 +. Si vous devez connaître le nom d’hôte de la machine exécutant votre service, utilisez le spécificateur systemd +% H +. Pour en savoir plus sur les spécificateurs possibles, consultez la documentation systemd. Dans la section + [X-Fleet] +, seuls les spécificateurs +% n +, '% N + `,' +% i +` et `% p +` fonctionneront.

Construire le service d’annonce Sidekick

Maintenant que nous avons une bonne idée de ce qu’il faut garder à l’esprit lors de la création d’un service principal, nous pouvons commencer à rechercher un service conventionnel «d’accompagnement». Ces services de sidekick sont associés à un service principal et servent de point externe pour enregistrer des services avec + etcd +.

Ce fichier, comme il a été référencé dans le fichier d’unité principale, s’appelle + apache-discovery.1.service + et ressemble à ceci:

[Unit]
Description=Apache web server etcd registration

# Requirements
Requires=etcd.service
Requires=apache.1.service

# Dependency ordering and binding
After=etcd.service
After=apache.1.service
BindsTo=apache.1.service

[Service]

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Start
## Test whether service is accessible and then register useful information
ExecStart=/bin/bash -c '\
 while true; do \
   curl -f ${COREOS_PUBLIC_IPV4}:80; \
   if [ $? -eq 0 ]; then \
     etcdctl set /services/apache/${COREOS_PUBLIC_IPV4} \'{"host": "%H", "ipv4_addr": ${COREOS_PUBLIC_IPV4}, "port": 80}\' --ttl 30; \
   else \
     etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}; \
   fi; \
   sleep 20; \
 done'

# Stop
ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}

[X-Fleet]
# Schedule on the same machine as the associated Apache service
X-ConditionMachineOf=apache.1.service

Nous commençons le service sidekick à peu près de la même manière que nous avons fait le service principal. Nous décrivons l’objectif de l’unité avant de passer aux informations sur les dépendances et à la logique de commande.

Le premier nouvel élément ici est la directive + BindsTo = +. Cette directive amène cette unité à suivre les commandes de démarrage, d’arrêt et de redémarrage envoyées à l’unité répertoriée. En gros, cela signifie que nous pouvons gérer ces deux unités en manipulant l’unité principale une fois que les deux sont chargées dans + flotte +. Il s’agit d’un mécanisme à sens unique. Le contrôle du sidekick n’affectera donc pas l’unité principale.

Pour la section + [Service] +, nous avons à nouveau source le fichier + / etc / environment + car nous avons besoin des variables qu’il contient. La directive + ExecStart = + dans cette instance est essentiellement un court script + bash +. Il tente de se connecter aux principaux services à l’aide de l’interface et du port exposés.

Si la connexion aboutit, la commande + etcdctl + est utilisée pour définir une clé à l’adresse IP publique de la machine hôte dans + / services / apache + dans + etcd +. La valeur de ceci est un objet JSON contenant des informations sur le service. La clé est configurée pour expirer dans 30 secondes, donc si cette unité tombe en panne de manière inattendue, les informations de service périmées ne seront pas laissées dans + etcd +. Si la connexion échoue, la clé est immédiatement supprimée car il est impossible de vérifier si le service est disponible.

Cette boucle comprend une commande de veille de 20 secondes. Cela signifie que toutes les 20 secondes (avant le délai d’expiration de la clé + etcd + de 30 secondes), cette unité vérifie de nouveau si l’unité principale est disponible et réinitialise la clé. Cela actualise essentiellement la durée de vie de la clé afin qu’elle soit considérée comme valide pendant 30 secondes supplémentaires.

Dans ce cas, la commande d’arrêt entraîne simplement un retrait manuel de la clé. Cela entraînera la suppression de l’enregistrement de service lorsque la commande d’arrêt de l’unité principale est reflétée sur cet appareil en raison de la directive + BindsTo = +.

Pour la section + [X-Fleet] +, nous devons nous assurer que cette unité est démarrée sur le même serveur que l’unité principale. Bien que cela ne permette pas à l’unité de faire un rapport sur la disponibilité du service sur les ordinateurs distants, il est important que la directive + BindsTo = + fonctionne correctement.

Emporter des services de base pour la construction de services Sidekick

En construisant ce copain, nous pouvons observer certaines choses à garder à l’esprit comme règle générale pour ces types d’unités:

  • * Vérifiez la disponibilité réelle de l’unité principale *: Il est important de vérifier l’état de l’unité principale. Ne présumez pas que l’unité principale est disponible uniquement parce que le sidekick a été initialisé. Cela dépend de la conception et des fonctionnalités de l’unité principale, mais plus votre vérification est robuste, plus l’état de votre enregistrement sera crédible. La vérification peut porter sur tout ce qui a du sens pour l’unité, de la vérification d’un noeud final + / health à la tentative de connexion à une base de données avec un client.

  • * Bouclez la logique d’enregistrement pour vérifier régulièrement le contenu *: il est important de vérifier la disponibilité du service au début, mais il est également essentiel de revérifier à intervalles réguliers. Cela peut intercepter des défaillances de service inattendues, en particulier si elles empêchent le conteneur de s’arrêter. La pause entre les cycles devra être ajustée en fonction de vos besoins en pesant l’importance de la découverte rapide par rapport à la charge supplémentaire de votre unité principale.

  • * Utilisez l’indicateur TTL lors de l’enregistrement avec etcd pour le désenregistrement automatique en cas d’échec *: Des défaillances inattendues de l’unité sidekick peuvent entraîner des informations de découverte obsolètes dans + etcd +. Pour éviter les conflits entre l’état enregistré et l’état réel de vos services, vous devez laisser vos clés expirer. Avec la construction en boucle ci-dessus, vous pouvez actualiser chaque clé avant le délai d’expiration pour vous assurer que la clé n’expire jamais tant que le sidekick est en cours d’exécution. L’intervalle de veille de votre boucle doit être légèrement inférieur à votre intervalle de délai afin de garantir son bon fonctionnement.

  • * Enregistrez des informations utiles avec etcd, pas seulement une confirmation *: lors de votre première itération d’un copain, vous voudrez peut-être seulement vous enregistrer avec précision avec + etcd + au démarrage de l’unité. Cependant, il s’agit d’une occasion manquée de fournir de nombreuses informations utiles pour que d’autres services puissent les utiliser. Bien que vous n’ayez peut-être pas besoin de ces informations maintenant, elles deviendront plus utiles à mesure que vous construisez d’autres composants avec la possibilité de lire les valeurs de + etcd + pour leurs propres configurations. Le service + etcd + est un magasin global de valeurs-clés. N’oubliez pas de l’exploiter en fournissant des informations clés. Le stockage de détails dans des objets JSON est un bon moyen de transmettre plusieurs informations.

En gardant ces considérations à l’esprit, vous pouvez commencer à construire des unités d’enregistrement robustes qui permettront de s’assurer intelligemment que + etcd + dispose des informations correctes.

Considérations spécifiques à la flotte

Bien que les fichiers d’unités + flotte + 'ne soient pour la plupart pas différents des fichiers d’unités classiques + systemd + `, il existe des capacités et des pièges supplémentaires.

La différence la plus évidente est l’ajout d’une section appelée + [X-Fleet] + qui peut être utilisée pour indiquer à + ​​flotte + de prendre des décisions de planification. Les options disponibles sont:

  • * X-ConditionMachineID *: Ceci peut être utilisé pour spécifier une machine exacte pour charger l’unité. La valeur fournie est un ID d’ordinateur complet. Cette valeur peut être récupérée à partir d’un membre individuel du cluster en examinant le fichier + / etc / machine-id +, ou par l’intermédiaire de + fleetctl + en exécutant la commande + list-machines -l +. La chaîne d’identification complète est requise. Cela peut être nécessaire si vous exécutez une base de données avec le répertoire de données conservé sur une machine spécifique. À moins que vous n’ayez une raison particulière de l’utiliser, essayez de l’éviter car cela réduirait la flexibilité de l’appareil.

  • * X-ConditionMachineOf *: Cette directive peut être utilisée pour planifier cette unité sur la même machine chargée avec l’unité spécifiée. Ceci est utile pour les unités de sidekick ou pour assembler des unités associées.

  • * X-Conflicts *: Ceci est l’opposé de la déclaration ci-dessus, en ce sens qu’il spécifie les fichiers d’unité sur lesquels cette unité ne peut pas être planifiée. Ceci est utile pour configurer facilement la haute disponibilité en démarrant plusieurs versions du même service, chacune sur une machine différente.

  • * X-ConditionMachineMetadata *: Ceci est utilisé pour spécifier les exigences de planification basées sur les métadonnées des machines disponibles. Dans la colonne «METADATA» de la sortie + flottectl list-machines +, vous pouvez voir les métadonnées définies pour chaque hôte. Pour définir les métadonnées, transmettez-les dans votre fichier + cloud-config + lors de l’initialisation de l’instance du serveur.

  • * Global *: Il s’agit d’une directive spéciale qui prend un argument booléen indiquant si cela doit être planifié sur toutes les machines du cluster. Seules les métadonnées conditionnelles peuvent être utilisées parallèlement à cette directive.

Ces directives supplémentaires offrent à l’administrateur plus de flexibilité et de puissance pour définir la manière dont les services doivent être exécutés sur les machines disponibles. Celles-ci sont évaluées avant de les transmettre à l’instance + systemd + d’une machine spécifique pendant l’étape + flottectl load +.

Cela nous amène à la prochaine étape à prendre en compte lorsque vous travaillez avec des unités liées dans + flotte +. L’utilitaire + fleetctl + n’évalue pas les exigences de dépendance en dehors de la section + [X-Fleet] + du fichier d’unité. Cela pose des problèmes intéressants lorsque vous travaillez avec des unités associées dans + flotte +.

Cela signifie que, bien que l’outil + flottectl + effectue l’étape nécessaire pour que l’unité cible se trouve dans l’état souhaité, en passant par la soumission, le chargement et le processus de démarrage selon les besoins en fonction de la commande donnée, il ne le fera pas pendant les dépendances de l’unité.

Donc, si votre unité principale et votre unité sidekick sont soumises, mais pas chargées, dans + flotte +, tapez + fleetctl start main.service + chargera puis tentera de démarrer l’unité + main.service +. Cependant, étant donné que l’unité + sidekick.service + n’est pas encore chargée et que + fleetctl + n’évaluera pas les informations de dépendance pour amener les unités de dépendance tout au long du processus de chargement et de démarrage, l’unité + main.service + échouera. . En effet, une fois que l’instance + systemd + de la machine traite l’unité + main.service +, elle ne pourra plus trouver le + sidekick.service + lorsque it évalue les dépendances. L’unité + sidekick.service + n’a jamais été chargée sur la machine.

Pour éviter cette situation lorsque vous travaillez avec des unités associées, vous pouvez démarrer les services manuellement en même temps, sans vous fier à la directive + BindsTo = + pour placer le sidekick dans un état actif:

fleetctl start main.service sidekick.service

Une autre option consiste à s’assurer que l’unité de sidekick est au moins chargée lors de l’exécution de l’unité principale. L’étape de chargement est celle où une machine est sélectionnée et le fichier d’unité est soumis à l’instance locale + systemd +. Cela garantira que les dépendances sont satisfaites et que la directive + BindsTo = + sera capable de s’exécuter correctement pour faire apparaître la deuxième unité:

fleetctl load main.service sidekick.service
fleetctl start main.service

Gardez cela à l’esprit au cas où vos unités liées ne répondent pas correctement à vos commandes + fleetctl +.

Instances et modèles

L’un des concepts les plus puissants lorsque vous travaillez avec + fleet + sont les modèles d’unités.

Les modèles d’unité reposent sur une fonctionnalité de + systemd + appelée «instances». Ce sont des unités instanciées créées au moment de l’exécution en traitant un fichier d’unité modèle. Le fichier modèle est pour la plupart très similaire à un fichier unité standard, avec quelques petites modifications. Cependant, ceux-ci sont extrêmement puissants lorsqu’ils sont utilisés correctement.

Les fichiers modèles peuvent être identifiés par le signe + @ + dans leur nom de fichier. Alors qu’un service conventionnel prend cette forme:

.service

Un fichier modèle peut ressembler à ceci:

@.service

Lorsqu’une unité est instanciée à partir d’un modèle, son identificateur d’instance est placé entre les suffixes + @ + et + .service +. Cet identifiant est une chaîne unique sélectionnée par l’administrateur:

@.service

Le nom d’unité de base est accessible depuis le fichier d’unités à l’aide du spécificateur +% p +. De même, l’identifiant d’instance donné est accessible avec +% i +.

Fichier de l’unité principale en tant que modèle

Cela signifie qu’au lieu de créer votre fichier d’unité principale appelé + apache.1.service + avec le contenu que nous avons vu précédemment, vous pouvez créer un modèle appelé + apache @ .service + qui ressemble à ceci:

[Unit]
Description=Apache web server service on port %i

# Requirements
Requires=etcd.service
Requires=docker.service
Requires=apache-discovery@%i.service

# Dependency ordering
After=etcd.service
After=docker.service
Before=apache-discovery@%i.service

[Service]
# Let processes take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill apache.%i
ExecStartPre=-/usr/bin/docker rm apache.%i
ExecStartPre=/usr/bin/docker pull /apache
ExecStart=/usr/bin/docker run --name apache.%i -p ${COREOS_PUBLIC_IPV4}:%i:80 \
/apache /usr/sbin/apache2ctl -D FOREGROUND

# Stop
ExecStop=/usr/bin/docker stop apache.%i

[X-Fleet]
# Don't schedule on the same machine as other Apache instances
X-Conflicts=apache@*.service

Comme vous pouvez le constater, nous avons modifié la dépendance + apache-discovery.1.service + en + apache-discovery @% i.service +. Cela signifie que si nous avons une instance de ce fichier unité appelée + apache @ 8888.service +, cela nécessitera un sidekick appelé + apache-discovery @ 8888.service +. Le +% i + a été remplacé par l’identifiant de l’instance. Dans ce cas, nous utilisons l’identifiant pour stocker des informations dynamiques sur la manière dont notre service est exécuté, en particulier sur le port sur lequel le serveur Apache sera disponible.

Pour que cela fonctionne, nous modifions le paramètre + docker run + qui expose les ports du conteneur à un port de l’hôte. Dans le fichier d’unité statique, le paramètre utilisé était + $ {COREOS_PUBLIC_IPV4}: 80: 80 +, qui mappait le port 80 du conteneur sur le port 80 de l’hôte sur l’interface IPv4 publique. Dans ce fichier modèle, nous avons remplacé ceci par + $ {COREOS_PUBLIC_IPV4}:% i: 80 + puisque nous utilisons l’identifiant d’instance pour nous indiquer le port à utiliser. Un choix judicieux pour l’identifiant d’instance peut signifier une plus grande flexibilité au sein de votre fichier de modèle.

Le nom de Docker lui-même a également été modifié pour qu’il utilise également un nom de conteneur unique basé sur l’ID d’instance. Gardez à l’esprit que les conteneurs Docker ne peuvent pas utiliser le symbole + @ +, nous avons donc choisi un nom différent du fichier unité. Nous modifions toutes les directives qui s’appliquent au conteneur Docker.

Dans la section + [X-Fleet] +, nous avons également modifié les informations de planification pour reconnaître ces unités instanciées au lieu du type statique que nous utilisions auparavant.

Unité Sidekick en tant que modèle

Nous pouvons exécuter une procédure similaire pour adapter notre unité d’accompagnement à la modélisation.

Notre nouvelle unité de sidekick s’appellera + apache-discovery @ .service + et ressemblera à ceci:

[Unit]
Description=Apache web server on port %i etcd registration

# Requirements
Requires=etcd.service
Requires=apache@%i.service

# Dependency ordering and binding
After=etcd.service
After=apache@%i.service
BindsTo=apache@%i.service

[Service]

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Start
## Test whether service is accessible and then register useful information
ExecStart=/bin/bash -c '\
 while true; do \
   curl -f ${COREOS_PUBLIC_IPV4}:%i; \
   if [ $? -eq 0 ]; then \
     etcdctl set /services/apache/${COREOS_PUBLIC_IPV4} \'{"host": "%H", "ipv4_addr": ${COREOS_PUBLIC_IPV4}, "port": %i}\' --ttl 30; \
   else \
     etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}; \
   fi; \
   sleep 20; \
 done'

# Stop
ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PUBLIC_IPV4}

[X-Fleet]
# Schedule on the same machine as the associated Apache service
X-ConditionMachineOf=apache@%i.service

Nous avons suivi les mêmes étapes d’exigence et de liaison avec la version instanciée des processus de l’unité principale au lieu de la version statique. Cela fera correspondre l’unité d’accompagnement instanciée avec l’unité principale instanciée correcte.

Lors de la commande + curl +, lorsque nous vérifions la disponibilité réelle du service, nous remplaçons le port statique 80 par l’ID instantané afin qu’il se connecte au bon endroit. Cela est nécessaire car nous avons modifié le mappage de l’exposition au port dans la commande Docker pour notre unité principale.

Nous modifions également le «port» en cours de journalisation sur + etcd + afin qu’il utilise ce même ID d’instance. Avec cette modification, les données JSON définies dans + etcd + sont entièrement dynamiques. Il va récupérer le nom d’hôte, l’adresse IP et le port sur lequel le service est exécuté.

Enfin, nous modifions à nouveau la condition dans la section + [X-Fleet] +. Nous devons nous assurer que ce processus est démarré sur le même ordinateur que l’instance de l’unité principale.

Instanciation d’unités à partir de modèles

Pour réellement instancier des unités à partir d’un fichier de modèle, vous avez plusieurs options.

+ Flotte + et + systemd + peuvent gérer des liens symboliques, ce qui nous donne la possibilité de créer des liens avec les ID d’instance complets vers les fichiers de modèle, comme ceci:

Cela créera deux liens, nommés + apache @ 8888.service + et + apache-discovery @ 8888.service +. Chacune de celles-ci dispose de toutes les informations nécessaires à + ​​flotte + et + systemd + pour faire fonctionner ces unités maintenant. Cependant, ils sont redirigés vers les modèles afin que nous puissions apporter les modifications nécessaires en un seul endroit.

Nous pouvons ensuite soumettre, charger ou démarrer ces services avec + fleetctl + comme ceci:

Si vous ne souhaitez pas créer de liens symboliques pour définir vos instances, une autre option consiste à soumettre les modèles eux-mêmes dans + fleetctl +, comme ceci:

Vous pouvez instancier des unités à partir de ces modèles directement à partir de + fleetctl + en attribuant simplement des identificateurs d’instance au moment de l’exécution. Par exemple, vous pouvez exécuter le même service en tapant:

Cela élimine le besoin de liens symboliques. Certains administrateurs préfèrent le mécanisme de liaison, car cela signifie que les fichiers d’instance sont disponibles à tout moment. Il vous permet également de passer dans un répertoire à + ​​fleetctl + afin que tout soit démarré en même temps.

Par exemple, dans votre répertoire de travail, vous pouvez avoir un sous-répertoire appelé + modèles + pour vos fichiers de modèles et un sous-répertoire appelé + instances + pour les versions liées instanciées. Vous pourriez même en avoir un appelé + statique + pour les unités non modélisées. Vous pouvez faire ça comme ça:

mkdir templates instances static

Vous pouvez ensuite déplacer vos fichiers statiques dans + static et vos fichiers modèles dans` + templates`:

mv apache.1.service apache-discovery.1.service static
mv [email protected] [email protected] templates

À partir de là, vous pouvez créer les liens d’instance dont vous avez besoin. Lançons notre service sur les ports + 5555 + ',' + 6666 + et '+ 7777 + `:

Vous pouvez ensuite démarrer toutes vos instances à la fois en tapant quelque chose comme:

cd ..
fleetctl start instances/*

Cela peut être extrêmement utile pour démarrer rapidement vos services.

Conclusion

Vous devriez avoir une bonne compréhension de la façon de créer des fichiers unité pour + flotte + à ce stade. En tirant parti de certaines des fonctionnalités dynamiques disponibles dans les fichiers d’unité, vous pouvez vous assurer que vos services sont distribués de manière homogène, proches de leurs dépendances, et enregistrer des informations utiles avec + etcd +.

Dans un guide plus récent, nous expliquerons comment configurer vos conteneurs à utiliser les informations que vous enregistrez avec + etcd +. Cela peut aider vos services à acquérir une connaissance pratique de votre environnement de déploiement actuel afin de transmettre les demandes aux conteneurs appropriés dans le backend.