Comment créer et exécuter un service sur un cluster CoreOS

introduction

L’un des principaux avantages de CoreOS est la capacité de gérer des services sur un cluster entier à partir d’un seul point. La plate-forme CoreOS fournit des outils intégrés pour simplifier ce processus.

Dans ce guide, nous allons illustrer un flux de travail typique permettant de faire fonctionner des services sur vos clusters CoreOS. Ce processus présentera des moyens simples et pratiques d’interagir avec certains des utilitaires les plus intéressants de CoreOS afin de configurer une application.

Prérequis et objectifs

Pour commencer à utiliser ce guide, vous devez avoir un cluster CoreOS avec au moins trois machines configurées. Vous pouvez suivre notre afin d’amorcer un cluster CoreOS ici.

Pour les besoins de ce guide, nos trois nœuds seront les suivants:

  • coreos-1

  • coreos-2

  • coreos-3

Ces trois nœuds doivent être configurés à l’aide de leur interface de réseau privé pour leur adresse client etcd, leur adresse homologue, ainsi que l’adresse de la flotte. Ceux-ci doivent être configurés à l’aide du fichier cloud-config, comme indiqué dans le guide ci-dessus.

Dans ce guide, nous allons passer en revue le flux de travail de base consistant à faire fonctionner des services sur un cluster CoreOS. À des fins de démonstration, nous allons configurer un serveur Web Apache simple. Nous couvrirons la configuration d’un environnement de service conteneurisé avec Docker, puis nous créerons un fichier d’unité de style systemd pour décrire le service et ses paramètres opérationnels.

Dans un fichier d’unité compagnon, nous indiquerons à notre service de s’enregistrer auprès d’etcd, ce qui permettra à d’autres services de suivre ses détails. Nous allons soumettre nos deux services à la flotte, où nous pouvons démarrer et gérer les services sur les machines de notre cluster.

Connectez-vous à un nœud et transmettez votre agent SSH

La première chose à faire pour commencer à configurer les services est de vous connecter à l’un de nos nœuds avec SSH.

Pour que l’outil + fleetctl +, que nous utiliserons pour communiquer avec les nœuds voisins, fonctionne, nous devons transmettre les informations de notre agent SSH lors de la connexion.

Avant de vous connecter via SSH, vous devez démarrer votre agent SSH. Cela vous permettra de transférer vos informations d’identification sur le serveur auquel vous vous connectez, vous permettant ainsi de vous connecter depuis cette machine à d’autres nœuds. Pour démarrer l’agent utilisateur sur votre ordinateur, vous devez taper:

eval $(ssh-agent)

Vous pouvez ensuite ajouter votre clé privée au stockage en mémoire de l’agent en tapant:

ssh-add

À ce stade, votre agent SSH devrait être en cours d’exécution et connaître votre clé SSH privée. L’étape suivante consiste à vous connecter à l’un des nœuds de votre cluster et à transférer les informations de votre agent SSH. Vous pouvez le faire en utilisant l’indicateur + -A +:

ssh -A core@

Une fois que vous êtes connecté à l’un de vos nœuds, nous pouvons commencer à développer notre service.

Création du conteneur Docker

La première chose à faire est de créer un conteneur Docker qui exécutera notre service. Vous pouvez le faire de deux manières. Vous pouvez démarrer un conteneur Docker et le configurer manuellement, ou vous pouvez créer un fichier Docker qui décrit les étapes nécessaires à la création de l’image souhaitée.

Pour ce guide, nous allons construire une image en utilisant la première méthode car elle est plus simple pour ceux qui découvrent Docker. Suivez ce lien si vous souhaitez en savoir plus sur la façon de build a Docker image à partir d’un fichier Docker. Notre objectif est d’installer Apache sur une image de base Ubuntu 14.04 dans Docker.

Avant de commencer, vous devez vous connecter ou vous inscrire au registre Docker Hub. Pour ce faire, tapez:

docker login

Vous serez invité à fournir un nom d’utilisateur, un mot de passe et une adresse électronique. Si vous le faites pour la première fois, un compte sera créé à l’aide des informations que vous avez fournies et un courrier électronique de confirmation sera envoyé à l’adresse indiquée. Si vous avez déjà créé un compte dans le passé, vous serez connecté avec les informations d’identification fournies.

Pour créer l’image, la première étape consiste à démarrer un conteneur Docker avec l’image de base que vous souhaitez utiliser. La commande dont nous aurons besoin est:

docker run -i -t ubuntu:14.04 /bin/bash

Les arguments que nous avons utilisés ci-dessus sont:

  • * run *: Ceci indique à Docker que nous voulons démarrer un conteneur avec les paramètres suivants.

  • * -i *: démarrez le conteneur Docker en mode interactif. Cela garantira que STDIN dans l’environnement du conteneur sera disponible, même s’il n’est pas attaché.

  • * -t *: Ceci crée un pseudo-TTY, nous permettant un accès terminal à l’environnement du conteneur.

  • * ubuntu: 14.04 *: Ceci est la combinaison de référentiel et d’image que nous voulons exécuter. Dans ce cas, nous utilisons Ubuntu 14.04. L’image est conservée dans le référentiel Ubuntu Docker sur Docker Hub.

  • * / bin / bash *: C’est la commande que nous voulons exécuter dans le conteneur. Puisque nous voulons un accès terminal, nous devons créer une session shell.

Les couches d’image de base seront extraites du registre Docker en ligne de Docker Hub et une session bash sera lancée. Vous serez déposé dans la session shell résultante.

À partir de là, nous pouvons créer notre environnement de service. Nous voulons installer le serveur Web Apache. Nous devons donc mettre à jour notre index de paquet local et l’installer via + apt +:

apt-get update
apt-get install apache2

Une fois l’installation terminée, nous pouvons éditer le fichier + index.html + par défaut:

echo "<h1>Running from Docker on CoreOS</h1>" > /var/www/html/index.html

Lorsque vous avez terminé, vous pouvez quitter votre session bash de la manière habituelle:

exit

De retour sur votre machine hôte, nous devons obtenir l’ID de conteneur du conteneur Docker que nous venons de quitter. Pour ce faire, nous pouvons demander à Docker d’afficher les informations de processus les plus récentes:

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
cb58a2ea1f8f        ubuntu:14.04        "/bin/bash"         8 minutes ago       Exited (0) 55 seconds ago                       jovial_perlman

La colonne dont nous avons besoin est "CONTAINER ID". Dans l’exemple ci-dessus, ceci serait + cb58a2ea1f8f +. Pour pouvoir créer ultérieurement le même conteneur avec toutes les modifications que vous avez apportées, vous devez les valider dans le référentiel de votre nom d’utilisateur. Vous devrez également choisir un nom pour l’image.

Pour nos besoins, nous allons prétendre que le nom d’utilisateur est + user_name + mais vous devez le remplacer par le nom du compte Docker Hub avec lequel vous vous êtes connecté il y a un peu. Nous appellerons notre image + apache. La commande pour valider les modifications de l’image est la suivante:

docker commit  /apache

Cela enregistre l’image afin que vous puissiez rappeler l’état actuel du conteneur. Vous pouvez le vérifier en tapant:

docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
user_name/apache     latest              42a71fb973da        4 seconds ago       247.4 MB
ubuntu               14.04               c4ff7513909d        3 weeks ago         213 MB

Ensuite, vous devez publier l’image sur Docker Hub pour que vos nœuds puissent s’afficher et l’exécuter à volonté. Pour ce faire, utilisez le format de commande suivant:

docker push /apache

Vous avez maintenant une image de conteneur configurée avec votre instance Apache.

Création du fichier d’unité de service Apache

Maintenant que nous avons un conteneur Docker disponible, nous pouvons commencer à créer nos fichiers de service.

Flotte gère la planification des services pour l’ensemble du cluster CoreOS. Il fournit une interface centralisée à l’utilisateur, tout en manipulant localement les systèmes systemd init de l’hôte pour effectuer les actions appropriées.

Les fichiers qui définissent les propriétés de chaque service sont des fichiers d’unité systemd légèrement modifiés. Si vous avez déjà travaillé avec systemd par le passé, vous maîtriserez très bien la syntaxe.

Pour commencer, créez un fichier nommé + apache @ .service + dans votre répertoire personnel. Le signe '+ @ + indique qu’il s’agit d’un fichier de service de modèle. Nous reviendrons sur ce que cela signifie dans un instant. L’image CoreOS est fournie avec l’éditeur de texte `+ vim +:

Pour démarrer la définition du service, nous allons créer un en-tête de section + [Unit] + et définir des métadonnées sur cette unité. Nous allons inclure une description et spécifier les informations de dépendance. Étant donné que notre unité devra être utilisée après la disponibilité des fichiers etcd et Docker, nous devons définir cette exigence.

Nous devons également ajouter l’autre fichier de service que nous allons créer en tant qu’exigence. Ce deuxième fichier de service sera chargé de mettre à jour etcd avec des informations sur notre service. Le demander ici le forcera à démarrer lorsque ce service sera lancé. Nous expliquerons plus tard le +% i + dans le nom du service:

[Unit]
Description=Apache web server service
After=etcd.service
After=docker.service
Requires=apache-discovery@%i.service

Ensuite, nous devons dire au système ce qui doit se passer lors du démarrage ou de l’arrêt de cette unité. Nous faisons cela dans la section + [Service] +, puisque nous configurons un service.

La première chose à faire est de désactiver le délai de démarrage du service. Étant donné que nos services sont des conteneurs Docker, lors de son premier démarrage sur chaque hôte, l’image devra être extraite des serveurs Docker Hub, ce qui pourrait entraîner un temps de démarrage plus long que d’habitude lors de la première exécution.

Nous voulons définir le + KillMode + sur «none» afin que systemd autorise notre commande «stop» à tuer le processus Docker. Si nous laissons cela en suspens, Systemd pensera que le processus Docker a échoué lorsque nous appelons notre commande d’arrêt.

Nous voudrons également nous assurer que notre environnement est propre avant de commencer notre service. Ceci est particulièrement important car nous allons référencer nos services par nom et Docker ne permet à un seul conteneur de s’exécuter qu’avec chaque nom.

Nous devrons supprimer tous les conteneurs restants avec le nom que nous voulons utiliser, puis les supprimer. C’est à ce stade que nous extrayons également l’image de Docker Hub. Nous voulons également utiliser le fichier + / etc / environment +. Cela inclut des variables, telles que les adresses IP publiques et privées de l’hôte qui exécute le service:

[Unit]
Description=Apache web server service
After=etcd.service
After=docker.service
Requires=apache-discovery@%i.service

[Service]
TimeoutStartSec=0
KillMode=none
EnvironmentFile=/etc/environment
ExecStartPre=-/usr/bin/docker kill apache%i
ExecStartPre=-/usr/bin/docker rm apache%i
ExecStartPre=/usr/bin/docker pull /apache

La syntaxe + = - + pour les deux premières lignes + ExecStartPre + indique que ces lignes de préparation peuvent échouer et que le fichier unité va continuer. Étant donné que ces commandes ne réussissent que si un conteneur portant ce nom existe, elles échoueront si aucun conteneur n’est trouvé.

Vous avez peut-être remarqué le suffixe +% i + à la fin des noms de conteneur Apache dans les directives ci-dessus. Le fichier de service que nous sommes en train de créer est en réalité un https://github.com/coreos/fleet/blob/master/Documentation/unit-files-and-scheduling.md#template-unit-files[template (fichier unité). Cela signifie que lors de l’exécution du fichier, Fleet remplacera automatiquement certaines informations par les valeurs appropriées. Lisez les informations sur le lien fourni pour en savoir plus.

Dans notre cas, le «% i +» sera remplacé partout où il se trouve dans le fichier avec la partie du nom du fichier de service située à droite du suffixe « @ » avant le suffixe « .service ». Notre fichier s'appelle simplement ` apache @ .service +` cependant.

Bien que nous soumettions le fichier à + ​​flottectl + avec + apache @ .service +, lorsque nous chargerons le fichier, nous le chargerons sous le nom '+ apache @ .service + `, où« PORT_NUM »sera le port que nous voulons. pour démarrer ce serveur. Nous allons étiqueter notre service en fonction du port sur lequel il sera exploité afin de pouvoir facilement les différencier.

Ensuite, nous devons réellement démarrer le conteneur Docker actuel:

[Unit]
Description=Apache web server service
After=etcd.service
After=docker.service
Requires=apache-discovery@%i.service

[Service]
TimeoutStartSec=0
KillMode=none
EnvironmentFile=/etc/environment
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

Nous appelons la commande traditionnelle + docker run + et nous lui avons transmis certains paramètres. Nous lui passons le nom dans le même format que celui que nous utilisions ci-dessus. Nous allons également exposer un port de notre conteneur Docker à l’interface publique de notre ordinateur hôte. Le numéro de port de la machine hôte sera tiré de la variable +% i +, ce qui nous permet réellement de spécifier le port.

Nous allons utiliser la variable + COREOS_PUBLIC_IPV4 + (extraite du fichier d’environnement que nous avons fourni) pour être explicite sur l’interface hôte que nous voulons lier. Nous pourrions laisser cela de côté, mais cela nous permettra une modification facile ultérieurement si nous voulons changer cela en une interface privée (si nous équilibrons la charge, par exemple).

Nous référons le conteneur Docker que nous avons chargé précédemment sur Docker Hub. Enfin, nous appelons la commande qui lancera notre service Apache dans l’environnement conteneur. Etant donné que les conteneurs Docker se ferment dès que la commande qui leur est donnée est terminée, nous voulons exécuter notre service au premier plan plutôt qu’en tant que démon. Cela permettra à notre conteneur de continuer à fonctionner au lieu de sortir dès qu’il génère un processus enfant avec succès.

Ensuite, nous devons spécifier la commande à appeler lorsque le service doit être arrêté. Nous allons simplement arrêter le conteneur. Le nettoyage du conteneur est effectué lors du redémarrage à chaque fois.

Nous voulons aussi ajouter une section appelée + [X-Fleet] +. Cette section est spécialement conçue pour donner des instructions à la flotte sur la manière de planifier le service. Ici, vous pouvez ajouter des restrictions afin que votre service doit ou ne doit pas fonctionner selon certaines dispositions par rapport à d’autres services ou états de la machine.

Nous souhaitons que notre service ne fonctionne que sur des hôtes ne disposant pas déjà d’un serveur Web Apache, car cela nous permettra de créer facilement des services hautement disponibles. Nous allons utiliser un caractère générique pour récupérer l’un des fichiers de service Apache que nous pourrions avoir en cours d’exécution:

[Unit]
Description=Apache web server service
After=etcd.service
After=docker.service
Requires=apache-discovery@%i.service

[Service]
TimeoutStartSec=0
KillMode=none
EnvironmentFile=/etc/environment
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
ExecStop=/usr/bin/docker stop apache%i

[X-Fleet]
X-Conflicts=apache@*.service

Avec cela, nous avons fini avec notre fichier d’unité de serveur Apache. Nous allons maintenant créer un fichier de service associé pour enregistrer le service avec etcd.

Enregistrement d’états de service avec Etcd

Afin d’enregistrer l’état actuel des services démarrés sur le cluster, nous voudrons écrire quelques entrées sur etcd. Ceci est connu sous le nom d’enregistrement avec etcd.

Pour ce faire, nous allons démarrer un service compagnon minimal pouvant mettre à jour etcd pour indiquer quand le serveur est disponible pour le trafic.

Le nouveau fichier de service s’appellera + apache-discovery @ .service +. Ouvrez-le maintenant:

Nous allons commencer avec la section + [Unit] +, comme nous l’avions fait auparavant. Nous décrirons le but du service, puis nous établirons une directive appelée + BindsTo +.

La directive + BindsTo + identifie une dépendance à laquelle ce service se tourne pour obtenir des informations d’état. Si le service répertorié est arrêté, l’unité que nous écrivons maintenant s’arrête également. Nous allons utiliser cela pour que, si notre unité de serveur Web tombe en panne de manière inattendue, ce service mette à jour etcd afin de refléter ces informations. Cela résout le problème potentiel d’avoir des informations obsolètes dans etcd qui pourraient être utilisées à tort par d’autres services:

[Unit]
Description=Announce Apache@%i service
BindsTo=apache@%i.service

Pour la section + [Service] +, nous voulons à nouveau source le fichier d’environnement avec les informations d’adresse IP de l’hôte.

Pour la commande de démarrage réelle, nous voulons exécuter une simple boucle infinie bash. Dans la boucle, nous utiliserons la commande + etcdctl +, utilisée pour modifier les valeurs etcd, afin de définir une clé dans le magasin etcd dans le répertoire + / announ / services / apache% i +. Le «% i +» sera remplacé par la section du nom du service que nous chargerons entre le suffixe « @ » et le suffixe « .service +», qui sera à nouveau le numéro de port du service Apache.

La valeur de cette clé sera définie sur l’adresse IP publique du nœud et le numéro de port. Nous allons également définir un délai d’expiration de 60 secondes sur la valeur afin que la clé soit supprimée si le service meurt. Nous dormirons ensuite 45 secondes. Cela créera un chevauchement avec l’expiration, de sorte que nous mettrons toujours à jour la valeur TTL (durée de vie) avant qu’elle n’atteigne son délai d’expiration.

Pour l’action d’arrêt, nous allons simplement supprimer la clé avec le même utilitaire + etcdctl +, marquant le service comme indisponible:

[Unit]
Description=Announce Apache@%i service
BindsTo=apache@%i.service

[Service]
EnvironmentFile=/etc/environment
ExecStart=/bin/sh -c "while true; do etcdctl set /announce/services/apache%i ${COREOS_PUBLIC_IPV4}:%i --ttl 60; sleep 45; done"
ExecStop=/usr/bin/etcdctl rm /announce/services/apache%i

La dernière chose à faire est d’ajouter une condition pour s’assurer que ce service est démarré sur le même hôte que le serveur Web sur lequel il fait rapport. Cela garantira que si l’hôte tombe en panne, les informations etcd changeront en conséquence:

[Unit]
Description=Announce Apache@%i service
BindsTo=apache@%i.service

[Service]
EnvironmentFile=/etc/environment
ExecStart=/bin/sh -c "while true; do etcdctl set /announce/services/apache%i ${COREOS_PUBLIC_IPV4}:%i --ttl 60; sleep 45; done"
ExecStop=/usr/bin/etcdctl rm /announce/services/apache%i

[X-Fleet]
X-ConditionMachineOf=apache@%i.service

Vous avez maintenant votre service sidekick qui peut enregistrer l’état de santé actuel de votre serveur Apache dans etcd.

Travailler avec les fichiers de l’unité et la flotte

Vous avez maintenant deux modèles de service. Nous pouvons les soumettre directement dans + fleetctl + afin que notre cluster les connaisse:

Vous devriez pouvoir voir vos nouveaux fichiers de service en tapant:

fleetctl list-unit-files
UNIT                HASH    DSTATE      STATE       TMACHINE
[email protected]   26a893f inactive    inactive    -
[email protected]         72bcc95 inactive    inactive    -

Les modèles existent maintenant dans notre système init à l’échelle du cluster.

Puisque nous utilisons des modèles qui dépendent de la planification sur des hôtes spécifiques, nous devons ensuite charger les fichiers. Cela nous permettra de spécifier le nouveau nom de ces fichiers avec le numéro de port. C’est à ce moment que + fleetctl + examine la section + [X-Fleet] + pour déterminer les exigences en matière de planification.

Comme nous ne procédons à aucun équilibrage de charge, nous allons simplement utiliser notre serveur Web sur le port 80. Nous pouvons charger chaque service en spécifiant cela entre les suffixes + @ + et + .service +:

fleetctl load [email protected]
fleetctl load [email protected]

Vous devriez obtenir des informations sur l’hôte de votre cluster sur lequel le service est chargé:

Unit [email protected] loaded on 41f4cb9a.../10.132.248.119
Unit [email protected] loaded on 41f4cb9a.../10.132.248.119

Comme vous pouvez le constater, ces services ont tous deux été chargés sur la même machine, ce que nous avons spécifié. Puisque notre fichier de service + apache-discovery + est lié à notre service Apache, nous pouvons simplement commencer plus tard pour lancer nos deux services:

fleetctl start [email protected]

Maintenant, si vous demandez quelles unités fonctionnent sur notre cluster, vous devriez voir ce qui suit:

fleetctl list-units
UNIT                MACHINE             ACTIVE  SUB
[email protected] 41f4cb9a.../10.132.248.119  active  running
[email protected]       41f4cb9a.../10.132.248.119  active  running

Il semble que notre serveur Web soit opérationnel. Dans notre fichier de service, nous avons dit à Docker de se lier à l’adresse IP publique du serveur hôte, mais l’adresse IP affichée avec + fleetctl + est l’adresse privée (car nous avons passé + $ private_ipv4 + dans la configuration en nuage lors de la création de cet exemple. grappe).

Cependant, nous avons enregistré l’adresse IP publique et le numéro de port avec etcd. Pour obtenir la valeur, vous pouvez utiliser l’utilitaire + etcdctl + pour interroger les valeurs que nous avons définies. Si vous vous en souvenez bien, les clés que nous avons définies étaient + / annoncés / services / apache. Pour obtenir les détails de notre serveur, tapez:

etcdctl get /announce/services/apache80
104.131.15.192:80

Si nous visitons cette page dans notre navigateur Web, nous devrions voir la page très simple que nous avons créée:

image: https: //assets.digitalocean.com/articles/coreos_basic/web_page.png [page Web de base CoreOS]

Notre service a été déployé avec succès. Essayons de charger une autre instance en utilisant un autre port. Nous nous attendons à ce que le serveur Web et le conteneur sidekick associé soient planifiés sur le même hôte. Toutefois, en raison de la contrainte imposée par notre fichier de service Apache, nous devrions nous attendre à ce que cet hôte soit différent de celui qui dessert notre service du port 80.

Lançons un service sur le port 9999:

Unit [email protected] loaded on 855f79e4.../10.132.248.120
Unit [email protected] loaded on 855f79e4.../10.132.248.120

Nous pouvons voir que les deux nouveaux services ont été programmés sur le même nouvel hôte. Démarrer le serveur Web:

fleetctl start [email protected]

Maintenant, nous pouvons obtenir l’adresse IP publique de ce nouvel hôte:

etcdctl get /announce/services/apache9999
104.131.15.193:9999

Si nous visitons l’adresse et le numéro de port spécifiés, nous devrions voir un autre serveur Web:

image: https: //assets.digitalocean.com/articles/coreos_basic/web_page.png [page Web de base CoreOS]

Nous avons maintenant déployé deux serveurs Web au sein de notre cluster.

Si vous arrêtez un serveur Web, le conteneur sidekick devrait également s’arrêter:

fleetctl stop [email protected]
fleetctl list-units
UNIT                MACHINE             ACTIVE      SUB
[email protected] 41f4cb9a.../10.132.248.119  inactive    dead
[email protected]   855f79e4.../10.132.248.120  active  running
[email protected]       41f4cb9a.../10.132.248.119  inactive    dead
[email protected]     855f79e4.../10.132.248.120  active  running

Vous pouvez vérifier que la clé etcd a également été supprimée:

etcdctl get /announce/services/apache80
Error:  100: Key not found (/announce/services/apache80) [26693]

Cela semble fonctionner exactement comme prévu.

Conclusion

En suivant ce guide, vous devriez maintenant vous familiariser avec certaines des méthodes courantes d’utilisation des composants CoreOS.

Nous avons créé notre propre conteneur Docker avec le service que nous voulions exécuter à l’intérieur et nous avons créé un fichier d’unité de flotte pour indiquer à CoreOS comment gérer notre conteneur. Nous avons mis en place un service sidekick pour maintenir notre banque de données etcd avec les informations d’état de notre serveur Web. Nous avons géré nos services avec flottectl, des services de planification sur différents hôtes.

Dans des guides ultérieurs, nous continuerons à explorer certains des domaines que nous avons brièvement abordés dans cet article.