Comment héberger plusieurs sites Web avec Nginx et HAProxy Utilisation de LXD sur Ubuntu 16.04

introduction

Un conteneur Linux est un groupe de processus isolé du reste du système grâce à l’utilisation de fonctions de sécurité du noyau Linux, telles que des espaces de noms et des groupes de contrôle. C’est une construction semblable à une machine virtuelle, mais elle est beaucoup plus légère; vous n’avez pas la charge supplémentaire nécessaire pour exécuter un noyau supplémentaire ou pour simuler le matériel. Cela signifie que vous pouvez facilement créer plusieurs conteneurs sur le même serveur. À l’aide de conteneurs Linux, vous pouvez exécuter plusieurs instances de systèmes d’exploitation entiers, confinés, sur le même serveur, ou regrouper votre application et ses dépendances dans un conteneur sans affecter le reste du système.

Par exemple, imaginez que vous avez un serveur et que vous avez configuré plusieurs services, y compris des sites Web, pour vos clients. Dans une installation classique, chaque site Web serait un hôte virtuel de la même instance du serveur Web Apache ou Nginx. Mais avec les conteneurs Linux, chaque site Web sera configuré dans son propre conteneur, avec son propre serveur Web.

Nous pouvons utiliser LXD pour créer et gérer ces conteneurs. LXD fournit un service d’hyperviseur permettant de gérer le cycle de vie complet des conteneurs.

Dans ce didacticiel, vous utiliserez LXD pour installer deux sites Web basés sur Nginx sur le même serveur, chacun confiné à son propre conteneur. Ensuite, vous installerez HAProxy dans un troisième conteneur qui agira comme un proxy inverse. Vous dirigerez ensuite le trafic vers le conteneur HAProxy afin de rendre les deux sites Web accessibles depuis Internet.

Conditions préalables

Pour compléter ce didacticiel, vous aurez besoin des éléments suivants:

Étape 1 - Ajout de votre utilisateur au groupe + lxd +

Connectez-vous au serveur à l’aide du compte d’utilisateur non root. Nous utiliserons ce compte non-utilisateur pour effectuer toutes les tâches de gestion des conteneurs. Pour que cela fonctionne, vous devez d’abord ajouter cet utilisateur au groupe + lxd +. Faites ceci avec la commande suivante:

sudo usermod --append --groups lxd

Déconnectez-vous du serveur et reconnectez-vous afin que votre nouvelle session SSH soit mise à jour avec la nouvelle appartenance au groupe. Une fois connecté, vous pouvez commencer à configurer LXD.

Étape 2 - Configuration de LXD

LXD doit être configuré correctement avant de pouvoir l’utiliser. La décision de configuration la plus importante est le type d’arrière-plan de stockage pour stocker les conteneurs. Le système de stockage recommandé pour LXD est le système de fichiers ZFS, stocké soit dans un fichier préalloué, soit à l’aide de Block Storage. Pour utiliser le support ZFS dans LXD, installez le package + zfsutils-linux +:

sudo apt-get update
sudo apt-get install zfsutils-linux

Une fois installé, vous êtes prêt à initialiser LXD. Au cours de l’initialisation, vous serez invité à spécifier les détails du système de stockage ZFS. Deux sections suivent, selon que vous souhaitiez utiliser un fichier préalloué ou un stockage en bloc. Suivez l’étape appropriée pour votre cas. Une fois que vous avez spécifié le mécanisme de stockage, vous allez configurer les options de mise en réseau de vos conteneurs.

Option 1 - Utilisation d’un fichier préalloué

Suivez ces étapes pour configurer LXD afin qu’il utilise un fichier préalloué pour stocker les conteneurs. Tout d’abord, exécutez la commande suivante pour démarrer le processus d’initialisation de LXD:

sudo lxd init

Vous serez invité à fournir plusieurs informations, comme indiqué dans la sortie suivante. Nous allons sélectionner toutes les valeurs par défaut, y compris la taille suggérée pour le fichier préalloué, appelé * périphérique de boucle *:

OutputName of the storage backend to use (dir or zfs) [default=zfs]:
Create a new ZFS pool (yes/no) [default=yes]?
Name of the new ZFS pool [default=lxd]:
Would you like to use an existing block device (yes/no) [default=no]?
Size in GB of the new loop device (1GB minimum) [default=15]:
Would you like LXD to be available over the network (yes/no) [default=no]?
Do you want to configure the LXD bridge (yes/no) [default=yes]?
Warning: Stopping lxd.service, but it can still be activated by:
 lxd.socket
LXD has been successfully configured.

La taille suggérée est automatiquement calculée à partir de l’espace disque disponible de votre serveur.

Une fois le périphérique configuré, vous configurerez les paramètres réseau, que nous explorerons après la section facultative suivante.

Option 2 - Utilisation du stockage en bloc

Si vous envisagez d’utiliser Block Storage, vous devez rechercher le périphérique qui pointe vers le volume de stockage de bloc que vous avez créé afin de le spécifier dans la configuration de LXD. Accédez à l’onglet * Volumes * dans la https://cloud.digitalocean.com [volet de contrôle DigitalOcean] l, localisez votre volume, cliquez sur la fenêtre contextuelle * Plus *, puis sur * Instructions de configuration *.

Localisez le périphérique en consultant la commande permettant de formater le volume. Plus précisément, recherchez le chemin spécifié dans la commande + sudo mkfs.ext4 -F +. La figure suivante montre un exemple de volume. Vous avez seulement besoin de la partie soulignée:

image: https: //assets.digitalocean.com/articles/lxd_containers_ubuntu_1604/6rDyC1l.png [Les instructions de configuration indiquent le périphérique pour le stockage de bloc créé.]

Dans ce cas, le nom du volume est + / dev / disk / by-id / scsi-0D0_Volume_volume-fra1-01 +, bien que le vôtre puisse être différent.

Une fois que vous avez identifié le volume, revenez sur votre terminal et exécutez la commande suivante pour lancer le processus d’initialisation de LXD.

sudo lxd init

On vous présentera une série de questions. Répondez aux questions comme indiqué dans le résultat suivant:

OutputName of the storage backend to use (dir or zfs) [default=zfs]:
Create a new ZFS pool (yes/no) [default=yes]?
Name of the new ZFS pool [default=lxd]:

Lorsque vous êtes invité à utiliser un périphérique de bloc existant, choisissez + yes + et indiquez le chemin d’accès à votre périphérique:

Output of the "lxd init" commandWould you like to use an existing block device (yes/no) [default=no]?
Path to the existing block device:

Ensuite, utilisez la valeur par défaut pour les questions restantes:

Output of the "lxd init" commandWould you like LXD to be available over the network (yes/no) [default=no]?
Do you want to configure the LXD bridge (yes/no) [default=yes]?
Warning: Stopping lxd.service, but it can still be activated by:
 lxd.socket
LXD has been successfully configured.

Une fois le processus terminé, vous configurerez le réseau.

Configuration du réseau

Le processus d’initialisation nous présentera une série d’écrans, comme celui de la figure suivante, qui nous permet de configurer le pont réseau des conteneurs afin qu’ils puissent obtenir des adresses IP privées, communiquer entre eux et avoir accès à Internet.

image: https: //assets.digitalocean.com/articles/lxd_containers_ubuntu_1604/u9D79uB.png [Configuration réseau LXD]

Utilisez la valeur par défaut pour chaque option, mais lorsque vous êtes interrogé sur le réseau IPv6, sélectionnez * Non *, car nous ne l’utiliserons pas dans ce didacticiel.

Une fois la configuration réseau terminée, vous êtes prêt à créer vos conteneurs.

Étape 3 - Création de conteneurs

Nous avons configuré avec succès LXD. Nous avons spécifié l’emplacement du système de stockage et avons configuré le réseau par défaut pour tous les conteneurs nouvellement créés. Nous sommes prêts à créer et à gérer certains conteneurs, ce que nous ferons avec la commande + lxc +.

Essayons notre première commande, qui répertorie les conteneurs installés disponibles:

lxc list

Vous verrez le résultat suivant:

Output of the "lxd list" commandGenerating a client certificate. This may take a minute...
If this is your first time using LXD, you should also run: sudo lxd init
To start your first container, try: lxc launch ubuntu:16.04

+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

Comme c’est la première fois que la commande + lxc + communique avec l’hyperviseur LXD, le résultat nous indique que la commande a automatiquement créé un certificat client pour une communication sécurisée avec LXD. Ensuite, il montre des informations sur la manière de lancer un conteneur. Enfin, la commande affiche une liste vide de conteneurs, ce qui est attendu car nous n’avons pas encore créé de fichier.

Créons trois conteneurs. Nous en créerons un pour chaque serveur Web et un troisième conteneur pour le proxy inverse. Le proxy inverse a pour objectif de diriger les connexions entrantes d’Internet vers le serveur Web approprié dans le conteneur.

Nous allons utiliser la commande + lxc launch + pour créer et démarrer un conteneur Ubuntu 16.04 (+ ubuntu: x +) nommé + web1 +. Le + x + dans + ubuntu: x + est un raccourci pour la première lettre de Xenial, le nom de code d’Ubuntu 16.04. + ubuntu: + est l’identifiant du référentiel préconfiguré des images LXD.

Exécutez les commandes suivantes pour créer les conteneurs:

lxc launch ubuntu:x web1
lxc launch ubuntu:x web2
lxc launch ubuntu:x haproxy

Comme c’est la première fois que nous créons un conteneur, la première commande télécharge l’image du conteneur à partir d’Internet et la met en cache localement. Les deux prochains conteneurs seront créés beaucoup plus rapidement.

Vous pouvez voir ici l’exemple de sortie de la création du conteneur + web1 +.

OutputCreating web1
Retrieving image: 100%
Starting web1

Maintenant que nous avons créé trois conteneurs vanilles vides, utilisons la commande + lxc list + pour afficher des informations à leur sujet:

lxc list

La sortie affiche une table avec le nom de chaque conteneur, son état actuel, son adresse IP, son type et si des instantanés ont été pris.

Sortie

+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| haproxy | RUNNING | 10.10.10.10 (eth0)    |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web1    | RUNNING | 10.10.10.100 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+
| web2    | RUNNING | 10.10.10.200 (eth0)   |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

Prenez note des noms de conteneur et de leur adresse IPv4 correspondante. Vous en aurez besoin pour configurer vos services.

Étape 4 - Configuration des conteneurs Nginx

Connectons-nous au conteneur + web1 + et configurons le premier serveur Web.

Pour vous connecter, nous utilisons la commande + lxc exec +, qui prend le nom du conteneur et les commandes à exécuter. Exécutez la commande suivante pour vous connecter au conteneur:

lxc exec web1 -- sudo --login --user ubuntu

La chaîne + - + indique que les paramètres de commande pour + lxc + devraient s’arrêter là et que le reste de la ligne sera transmis en tant que commande à exécuter à l’intérieur du conteneur. La commande est + sudo --login --user ubuntu +, qui fournit un shell de connexion pour le compte préconfiguré + ubuntu + à l’intérieur du conteneur.

Une fois à l’intérieur du conteneur, notre invite du shell ressemble maintenant à ce qui suit.

Outputubuntu@web1:~$

Cet utilisateur * ubuntu * dans le conteneur a préconfiguré l’accès + sudo +, et peut exécuter des commandes + sudo + sans fournir de mot de passe. Cette coquille est limitée à l’intérieur du conteneur. Tout ce que nous exécutons dans ce shell reste dans le conteneur et ne peut pas s’échapper sur le serveur hôte.

Mettons à jour la liste des paquets de l’instance Ubuntu dans le conteneur et installons Nginx:

sudo apt-get update
sudo apt-get install nginx

Modifions la page Web par défaut de ce site et ajoutons du texte indiquant clairement que ce site est hébergé dans le conteneur + web1 +. Ouvrez le fichier + / var / www / html / index.nginx-debian.html:

sudo nano /var/www/html/index.nginx-debian.html

Apportez les modifications suivantes au fichier:

Fichier édité /var/www/html/index.nginx-debian.html

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx !</title>
<style>
   body {
       width: 35em;
       margin: 0 auto;
       font-family: Tahoma, Verdana, Arial, sans-serif;
   }
</style>
</head>
<body>
<h1>Welcome to nginx !</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Nous avons édité le fichier à deux endroits et ajouté spécifiquement le texte + sur le conteneur LXD web1 +. Enregistrez le fichier et quittez votre éditeur.

Maintenant, déconnectez-vous du conteneur et revenez sur le serveur hôte:

logout

Répétez cette procédure pour le conteneur + web2 +. Connectez-vous, installez Nginx, puis éditez le fichier + / var / www / html / index.nginx-debian.html + pour mentionner + web2 +. Quittez ensuite le conteneur + web2 +.

Utilisons + curl + pour vérifier que les serveurs Web des conteneurs fonctionnent. Nous avons besoin des adresses IP des conteneurs Web déjà montrées.

curl http://10.10.10.100/

Le résultat devrait être:

Output of "curl http://10.10.10.100/" command<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx !</title>
<style>
   body {
       width: 35em;
       margin: 0 auto;
       font-family: Tahoma, Verdana, Arial, sans-serif;
   }
</style>
</head>
<body>
<h1>Welcome to nginx !</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Testez également le deuxième conteneur à l’aide de la commande + curl + et de son adresse IP pour vérifier sa bonne configuration. Avec les deux conteneurs configurés, nous pouvons passer à la configuration de HAProxy.

Étape 5 - Configuration du conteneur HAProxy

Nous allons configurer HAProxy en tant que proxy devant ces conteneurs. Si vous avez besoin de plus d’informations sur le fonctionnement de ce logiciel, consultez le didacticiel Introduction à HAProxy et à l’équilibrage de charge. Concepts. Nous dirigerons le trafic vers chaque conteneur en fonction du nom de domaine que nous utilisons. Nous utiliserons le domaine` + exemple.com + dans l’exemple de configuration ci-après, qui est un special reserved domain pour une documentation telle que ce tutoriel. Le premier site Web sera disponible sur les noms d’hôte `+ example.com + et + www.example.com +. Le deuxième site Web sera à + ​​www2.example.com +. Substituez vos propres noms de domaine à la place de ces noms de domaine.

Connectez-vous au conteneur + haproxy +:

lxc exec haproxy -- sudo --login --user ubuntu

Mettez à jour la liste des packages d’installation et installez HAProxy:

sudo apt-get update
sudo apt-get install haproxy

Une fois l’installation terminée, nous pouvons configurer HAProxy. Le fichier de configuration pour HAProxy se trouve dans + / etc / haproxy / haproxy.cfg +. Ouvrez le fichier avec votre éditeur de texte préféré.

sudo nano /etc/haproxy/haproxy.cfg

Premièrement, nous allons apporter quelques modifications à la section + defaults +. Nous allons ajouter l’option + forwardfor + afin de conserver l’adresse IP source réelle du client Web, puis l’option + http-server-close +, qui permet la réutilisation de la session et une latence réduite.

/etc/haproxy/haproxy.conf

global
...
defaults
   log global
   mode    http
   option  httplog
   option  dontlognull


   timeout connect 5000
   timeout client  50000
   timeout server  50000
...

Ensuite, nous allons configurer le client pour qu’il pointe vers nos deux conteneurs principaux. Ajoutez une nouvelle section + frontend + appelée + www_frontend + qui ressemble à ceci:

/etc/haproxy/haproxy.conf

        # Bind to port 80 (www) on the container

   # It matches if the HTTP Host: field mentions any of the hostnames (after the '-i').



   # Redirect the connection to the proper server cluster, depending on the match.

Les commandes + acl + correspondent aux noms d’hôte des serveurs Web et redirigent les requêtes vers la section + backend + correspondante.

Ensuite, nous définissons deux nouvelles sections + backend +, une pour chaque serveur Web, et nous les nommons + web1_cluster + et + web2_cluster +. Ajoutez le code suivant au fichier pour définir les moteurs:

/etc/haproxy/haproxy.conf

   # We set the X-Client-IP HTTP header. This is useful if we want the web server to know the real client IP.

   # This backend, named here "web1", directs to container "web1.lxd" (hostname).

L’option + balance + indique la stratégie d’équilibrage de la charge. Dans ce cas, nous optons pour le plus petit nombre de connexions. L’option + http-request + définit un en-tête HTTP avec l’adresse IP du client Web réel. Si nous ne définissions pas cet en-tête, le serveur Web enregistrerait l’adresse IP HAProxy en tant qu’adresse IP source pour toutes les connexions, ce qui compliquerait l’analyse de l’origine du trafic. L’option + server + spécifie un nom arbitraire pour le serveur (+ web1 +), suivi du nom d’hôte et du port du serveur.

LXD fournit un serveur DNS pour les conteneurs, donc + web1.lxd + résout l’adresse IP associée au conteneur + web1 +. Les autres conteneurs ont leurs propres noms d’hôte, tels que + web2.lxd + et + haproxy.lxd +.

Le paramètre + check + indique à HAPRoxy d’effectuer des contrôles de santé sur le serveur Web pour s’assurer de sa disponibilité.

Pour vérifier que la configuration est valide, exécutez la commande suivante:

/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c

La sortie devrait être

Sortie

Configuration file is valid

Rechargeons HAProxy pour qu’il lise la nouvelle configuration.

sudo systemctl reload haproxy

Maintenant, déconnectez-vous du conteneur afin de retourner à l’hôte.

logout

Nous avons configuré HAProxy pour qu’il agisse comme un proxy inverse qui transfère toutes les connexions qu’il reçoit sur le port + 80 + au serveur Web approprié dans les deux autres conteneurs. Testons que + haproxy + parvient effectivement à transférer les demandes au bon conteneur Web. Exécutez cette commande:

curl --verbose --header 'Host: web2.example.com' http://10.10.10.10

Ceci envoie une requête à HAProxy et définit un en-tête HTTP + host +, que HAProxy doit utiliser pour rediriger la connexion vers le serveur Web approprié.

La sortie devrait être

Output of "curl --verbose --header 'Host: web2.example.com' http://10.10.10.10" command...
> GET / HTTP/1.1
> Host:
> User-Agent: curl/7.47.0
> Accept: */*
>
...
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx !</title>
<style>
...

HAProxy a correctement compris la requête et l’a transmise au conteneur + web2 +. Là, le serveur Web a servi la page d’index par défaut que nous avons modifiée précédemment et affiche le texte + sur le conteneur LXD web2 +. Nous allons maintenant acheminer les requêtes externes vers HAProxy afin que le monde puisse accéder à nos sites Web.

Étape 6 - Transfert des connexions entrantes vers le conteneur HAProxy

La dernière pièce du puzzle consiste à connecter le proxy inverse à Internet. Nous devons configurer notre serveur pour qu’il transfère toutes les connexions qu’il pourrait recevoir d’Internet sur le port + 80 + au conteneur + haproxy +.

HAProxy est installé dans un conteneur et, par défaut, est inaccessible depuis Internet. Pour résoudre ce problème, nous allons créer une règle + iptables + pour transférer les connexions.

La commande + iptables + nécessite deux adresses IP: l’adresse IP publique du serveur (`) et l'adresse IP privée du conteneur `+ haproxy +` (`), que vous pouvez obtenir avec le fichier ` + lxc list + `commande.

Exécutez cette commande pour créer la règle:

sudo iptables -t nat -I PREROUTING -i eth0 -p TCP -d /32 --dport 80 -j DNAT --to-destination :80

Voici comment la commande se décompose:

  • + -t nat + spécifie que nous utilisons la table + nat +.

  • + -I PREROUTING + spécifie que nous ajoutons la règle à la chaîne PREROUTING.

  • + -i eth0 + spécifie l’interface * eth0 *, qui est l’interface publique par défaut sur les gouttelettes.

  • + -p TCP + indique que nous utilisons le protocole TCP.

  • + -d / 32 + spécifie l’adresse IP de destination pour la règle.

  • + - dport 80 +: spécifie le port de destination.

  • + -j DNAT + indique que nous souhaitons effectuer un saut vers un NAT de destination (DNAT).

  • + - to-destination: 80 + indique que nous voulons que la requête aille à l’adresse IP du conteneur avec HAProxy.

Pour en savoir plus sur les IPTables, rendez-vous à l’adresse Comment fonctionne le pare-feu Iptables et à l’adresse https://www.digitalocean.com/community/tutorials/ iptables-essentials-common-firewall-règles-et-commandes [IPtables Essentials: Règles et commandes de pare-feu communes].

Enfin, pour enregistrer cette commande + iptables + afin qu’elle soit réappliquée après un redémarrage, nous installons le package + iptables-persistent +:

sudo apt-get install iptables-persistent

Lors de l’installation du package, vous serez invité à enregistrer les règles iptables actuelles. Acceptez et enregistrez toutes les règles + iptables + en cours.

Si vous avez configuré les deux noms de domaine complets, vous devriez pouvoir vous connecter à chaque site Web à l’aide de votre navigateur Web. Essaye le.

Pour vérifier que les deux serveurs Web sont réellement accessibles à partir d’Internet, accédez à chacun à partir de votre ordinateur local à l’aide de la commande + curl + comme suit:

curl --verbose --header 'Host: example.com' 'http://'
curl --verbose --header 'Host: web2.example.com' 'http://'

Ces commandes établissent des connexions HTTP avec l’adresse IP publique du serveur et ajoutent un champ d’en-tête HTTP avec l’option + - header + que HAProxy utilisera pour traiter la demande, comme vous l’avez fait à l’étape 5.

Voici le résultat de la première commande + curl +:

Output*   Trying ...
* Connected to  () port 80 (#0)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx !</title>
<style>
   body {
...

Voici le résultat de la seconde commande + curl +:

Output*   Trying ...
* Connected to  () port 80 (#0)
> GET / HTTP/1.1
> Host: web2.example.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.0 (Ubuntu)
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx !</title>
<style>
   body {
...

Dans les deux cas, le site Web correct est affiché.

Conclusion

Vous avez configuré deux sites Web, chacun dans son propre conteneur, avec HAProxy dirigeant le trafic. Vous pouvez répliquer ce processus pour configurer beaucoup plus de sites Web, chacun confiné à son propre conteneur.

Vous pouvez également ajouter MySQL dans un nouveau conteneur, puis installer un CMS tel que WordPress pour exécuter chaque site Web. Vous pouvez également utiliser ce processus pour prendre en charge les versions antérieures du logiciel. Par exemple, si l’installation d’un CMS nécessite une version antérieure d’un logiciel tel que PHP5, vous pouvez installer Ubuntu 14.04 dans le conteneur (+ lxc launch ubuntu: t +), au lieu d’essayer de rétrograder les versions du gestionnaire de paquets disponibles sur Ubuntu. 16.04.

Enfin, LXD offre la possibilité de prendre des instantanés de l’état complet des conteneurs, ce qui facilite la création de sauvegardes et la restauration des conteneurs ultérieurement. De plus, si nous installons LXD sur deux serveurs différents, il est possible de les connecter et de migrer les conteneurs entre serveurs sur Internet.