Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Comment créer une infrastructure web en partant de rien ? – Partie 1

Dernièrement j’ai eu l’occasion de mettre en place une infrastructure IT complète et j’ai remarqué qu’il existait peu de ressources sur une telle mise en place. Le but de cette série de tutoriels est de créer un ensemble cohérent et suffisamment exhaustif sur le sujet pour qu’il puisse servir de référence future, tant pour moi que pour d’autres qui voudraient se lancer dans l’administration système ou dans la construction de leur propre infrastructure. J’essaierai autant que possible de suivre les bonnes pratiques et recommandations dans le domaine. Ce que j’entends par «infrastructure web» c’est l’architecture matérielle et logicielle qui vous permette de gérer des services web (applications, bases de données etc.).

Vouloir tout gérer soi-même peut venir de plusieurs raisons:

  • finance: coûts exorbitants des services externalisés ou cloud
  • sécurité: un besoin de contrôle total sur les processus et les données
  • indépendance: avoir les compétences en interne et ne pas dépendre d’un tiers, surtout en cas de problème

Avant-propos et pré-requis

Ce tutoriel reflète de près ma propre façon de travailler donc il y a donc de nombreux choix que je fais par préférence personnelle ou par expérience passée. Rien ne vous oblige à suivre de près ce tutoriel mais ce sera évidemment plus simple si vous manquez d’expérience.

Il suppose que vous ayez quelques bases en *nix (unix, linux) et en ligne de commande (shell). Si cela ne vous parle pas, vous risquez d’être rapidement perdu même si je ferai mon possible pour ne pas sauter d’étape et pour expliquer les points importants. Cependant, n’hésitez pas à me demander ce que vous souhaitez voir apparaitre via les commentaires, je ferai mon possible pour compléter dans de futurs articles ou annexes.

Pour son faible coût et sa facilité d’utilisation pour ce genre de tutoriel, je vais utiliser Scaleway mais vous pouvez utiliser n’importe quel type d’hébergement (serveurs physiques, machines virtuelles, …) tant que vous avez un accès complet au système. Je vous recommande néanmoins un hébergement à base de machines virtuelles ou similaire car leur provisionnement est rapide (au maximum quelques minutes, souvent quelques secondes suffisent). Des machines physiques seront généralement bien plus onéreuses et plus lentes à obtenir.

Dans ce tutoriel nous allons utiliser la distribution linux Debian car elle est a de nombreux atouts: multiples paquets disponibles, nombreux tutos pour débutants, … Pour une utilisation professionnelle, il vous faudra peut être regarder du côté de CentOS pour le support sur le très long terme (10 ans) et le module SELinux (pour une sécurité accrue), mais je m’égare.

Toute la configuration que je vais mettre en place dans ce tutoriel est disponible sur mon github public pour avoir une référence rapide de la configuration. Je vous conseille par contre de tout refaire vous même au lieu de simplement cloner mes dépôts git.

Contenu du tutoriel

Voici les points que j’espère couvrir avec vous dans ce tutoriel:

  1. Comment gérer de 1 à 1000 machines sans devenir fou
  2. Les services et éléments essentiels: NTP, SSH, sudo, iptables, etc.
  3. Bases de données
  4. Infrastructure de développement et déploiement continu avec GitLab
  5. Ajout d’une application de A à Z sur notre infrastructure

Comment gérer un parc de 1 à 1000 machines sans devenir fou

Avant de parler web, il va falloir mettre en place de nombreux services essentiels pour pouvoir tout simplement faire tourner vos applications finales. Ces services sont au coeur de votre système, ce sont les fondations de votre future infrastructure. Pour cela, nous allons devoir gérer de nombreuses configurations, machines, services, etc., et nous n’allons pas le faire à la main. Nous allons plutôt utiliser un «système de gestion de systèmes» (systems management system en anglais).

Vous avez peut-être entendu parler de Chef, Puppet ou encore Ansible. Dans ce tutoriel, nous allons utiliser Salt mais la logique fondamentale devrait rester similaire quel que soit l’outil que vous utilisez.

Nous allons utiliser Salt en mode serveur principal + clients multiples. Tous les clients utiliseront un programme nommé le salt minion pour se connecter au serveur principal, le salt master. Pour commencer, nous allons donc avoir besoin d’une machine pour héberger le salt master.

Notre première machine: saltmaster1

Installation

Nous allons créer une simple machine avec un linux Debian jessie (8.*), la dernière release stable à l’écriture de ce tutoriel. Je vous laisse voir avec votre hébergeur sur comment créer une nouvelle machine. Pour ceux qui utiliseraient aussi Scaleway, ils ont un tutoriel sur comment créer une instance et s’y connecter (en anglais)

Pour installer Salt, c’est simple, il suffit de suivre les instructions du site officiel de Salt (en anglais). Pour cela, nous allons commencer par ajouter les clés GPG des package Salt. Le plus simple est de lancer tout cela en tant qu’utilisateur root mais vous pouvez le faire aussi via sudo.

root@saltmaster1# wget -O - https://repo.saltstack.com/apt/debian/8/amd64/latest/SALTSTACK-GPG-KEY.pub | apt-key add -

Puis nous allons ajouter le dépot debian de Salt à aptitude et ses cousins apt-* le gestionnaire de paquets de debian:

root@saltmaster1# echo "deb http://repo.saltstack.com/apt/debian/8/amd64/latest jessie main" > /etc/apt/sources.list.d/saltstack.list

Une fois cela fait, il vous suffit de lancer une mise à jour d’aptitude et d’installer les paquets nécessaires:

root@saltmaster1# apt-get update && apt-get upgrade
[...]
root@saltmaster1# apt-get install -y salt-master salt-minion

Une fois le salt master installé, il faut lui donner une configuration à appliquer aux machines. Nous allons la stocker dans deux dépots git:

Pourquoi 2 dépots ? Car chacun va stocker des informations bien différentes:

  1. les fichiers d’états (appelés Salt States en anglais) et les templates des fichiers de configuration, situés par défaut dans /srv/salt
  2. les variables de configuration, dont les informations sensibles (mot de passes, clés de chiffrement, …), situés par défaut dans /srv/pillar.

Création des dépots git pour la configuration

Il nous suffit pour commencer de cloner les deux dépots au bon endroit sur le saltmaster1:

root@saltmaster1# rm -rf /srv/salt && git clone git@git.example.com:pseudo/infra-salt-master.git /srv/salt
root@saltmaster1# rm -rf /srv/pillar && git clone git@git.example.com:pseudo/infra-salt-pillar.git /srv/pillar

Pour finir la configuration de saltmaster1, nous allons apporter quelques personnalisations au fichier de configuration de salt master situé dans /etc/salt/master (et faites une copie de sauvegarde, nous en aurons besoin sous peu):

root@saltmaster1# cp /etc/salt/master /etc/salt/master.default
root@saltmaster1# vim /etc/salt/master
[...]
conf_file: /etc/salt/master
state_output: mixed
hash_type: sha512

Comme vous pouvez le voir dans la configuration, nous avons laissé le seul « environnement » par défaut utilisé par Salt dans file_root et pillar_roots: base. Une fois que vous serez plus à l’aise, vous pourrez configurer des environnements multiples: base, dev, prod, qa, …

Ajouter d’une nouvelle configuration

Pour pouvoir gérer la configuration d’un service, nous allons toujours suivre toujours la même logique:

  1. installer manuellement le programme (le plus souvent via apt-get install <...>)
  2. récupérer les fichiers de configuration par défaut, situés généralement dans /etc/...
  3. ajouter les fichiers de configuration par défaut dans notre dépot git de salt master
  4. faire une copie de ces fichiers et les personnaliser en fonction de nos besoins
  5. supprimer le programme installé à l’étape 1 (avec un apt-get purge <...> pour ne laisser aucune trace du programme d’origine)
  6. appliquer la configuration automatique via salt master et vérifier que le programme a été installé et configuré correctement

Pour saltmaster1, nous avons déjà plus ou moins effectué les étapes 1 à 4. Il ne nous reste plus qu’à copier les fichiers de configuration de salt master et à écrire les fichiers salt states. Salt utilise un fichier d’entrée qui doit s’appeler top.sls qui va lui ensuite inclure d’autres Salt States à appliquer sur différentes machines. Créons pour le moment le fichier top.sls situé à la racine du dépot pour salt master:

base:
  '*':
    - salt
  'saltmaster1':
    - salt.master

Que veut dire ce fichier ligne par ligne ?

  1. nous utilisons un seul environnement base, mais nous pourrions en rajouter d’autre prod, dev etc. cf voir ci-dessus dans le fichier /etc/salt/master
  2. Salt utilise le hostname de la machine (appelé l’ID de la machine chez Salt) pour savoir quelle configuration appliquer sur quelle machine. Pour nous éviter de devoir écrire une liste de toutes les machines dans le fichier, Salt nous permet de faire du globbing via '*' pour sélectionner toutes les machines possibles.
  3. Salt va appliquer le fichier d’état contenu dans le fichier spécifié. Dans cet exemple, salt est un dossier qui va contenir la configuration de Salt. Dans cet exemple, il se situe dans /srv/salt/salt. Lorsque vous ne spécifiez pas le nom du fichier mais seulement le dossier, Salt utilisera par défaut le fichier init.sls par défaut, ce qui donne ici /etc/salt/salt/init.sls.
  4. Ici, nous voulons appliquer des règles seulement au hostname saltmaster1
  5. Le fichier d’état à appliquer est salt.master, qui est traduit par Salt en chemin suivant salt/master.sls, ce qui donne /etc/salt/salt/master.sls

Créons donc le dossier salt dans notre dépot git pour salt master (nous n’en avons pas besoin dans salt pillar pour le moment) et deux fichiers: init.sls et master.sls. Avant d’écrire les fichiers d’états, nous allons copier les fichier de configuration de Salt dans notre dépot git aux chemins suivants. Dans tous les tutoriels qui vont suivre, j’utiliserai la notation <git-salt-master> et <git-salt-pillar> pour représenter le dossier racine du dépôt git de salt master et de salt pillar respectivement.

  • /etc/salt/master vers <git-salt-master>/salt/templates/master, que nous avons déjà personnalisé
  • /etc/salt/master.default vers <git-salt-master>/salt/defaults/master.default, qui nous sert à garder une copie du fichier d’origine, au cas où nous aurions besoins de repartir d’une configuration par défaut
  • /etc/salt/minion vers <git-salt-master>/salt/templates/minion, que nous allons aussi personnaliser
  • /etc/salt/minion vers <git-salt-master>/salt/defaults/minion.default, qui, comme ci-dessus, nous sert de copie de sauvegarde du fichier de configuration d’origine.
achedeuzot@localhost# tree .
.
├── salt
│   ├── defaults
│   │   └── master.default
|   |   └── minion.default
│   └── templates
|   │   └── master
|   │   └── minion
│   ├── init.sls
│   ├── master.sls
└── top.sls

Il ne nous reste plus qu’à écrire les fichiers d’état pour appliquer les règles à notre machine saltmaster1.

Le fichier init.sls sera appliqué à toutes les machines, master comme clientes, il convient d’y mettre le nécessaire pour pouvoir installer salt minion:

# Ici nous ajoutons le dépot de paquets apt de saltstack pour pouvoir l'installer facilement
salt_repository_add:
  pkgrepo.managed:
    - humanname: saltstack-repo
    - name: deb http://repo.saltstack.com/apt/debian/8/amd64/latest jessie main
    - file: /etc/apt/sources.list.d/saltstack.list
    - key_url: https://repo.saltstack.com/apt/debian/8/amd64/latest/SALTSTACK-GPG-KEY.pub
    - refresh_db: True

# Vérifier que le package salt-minion est installé
salt_minion_installed:
  pkg.installed:
    - name: salt-minion

# Copier la configuration locale situé dans<git-salt-master>/salt/templates/minion vers la machine
# salt:// représente le dossier racine de la configuration de salt master, c'est à dire /srv/salt
salt_minion_conf_set:
  file.managed:
    - name: /etc/salt/minion
    - source: salt://salt/templates/minion
    - user: root
    - group: root
    - mode: 600
    - template: jinja
    - require:
      - pkg: salt-minion

# Vérifier que le service tourne et le relancer en cas de changement (directive watch)
salt_minion_service_running:
  service.running:
    - name: salt-minion
    - enable: True
    - watch:
      - pkg: salt-minion
      - file: /etc/salt/minion

# Lancer un rafraichissement de la configuration toutes les 15 minutes.
salt_minion_highstate_cron_set:
  cron.present:
    - identifier: salt_minion_highstate_cron_set
    - name: salt-call state.highstate > /dev/null
    - user: root
    - minute: '*/15'

Puis, ajoutons la configuration spécifique du master dans master.sls:

# Le dépot de paquets a déjà été installé par le fichier init.sls, il ne nous reste plus qu'à installer salt-master
salt_master_installed:
  pkg.installed:
    - name: salt-master

# Ajouter la configuration personnalisée du salt-master issue du template salt/templates/master
salt_master_conf_set:
  file.managed:
    - name: /etc/salt/master
    - source: salt://salt/templates/master
    - user: root
    - group: root
    - mode: 600
    - require:
      - pkg: salt-master

# Vérifier que le service tourne et qu'il est relancé en cas de changement du package ou du fichier de configuration (directive watch)
salt_master_service_running:
  service.running:
    - name: salt-master
    - enable: True
    - watch:
      - pkg: salt-master
      - file: /etc/salt/master

Il ne nous reste plus qu’à ajouter tout celà dans notre dépot git, le récupérer sur le saltmaster1 et l’appliquer:

achedeuzot@localhost:~/<git-salt-master># git add top.sls salt/
achedeuzot@localhost:~/<git-salt-master># git commit -m "Tutorial first step: bootstrap Salt"
[..]
achedeuzot@localhost:~/<git-salt-master># git push origin master
[...]

# Sur saltmaster1:
root@saltmaster1# cd /src/salt
root@saltmaster1# git pull origin master
[...]

L’heure du premier test

Pour pouvoir finir ce premier volet et vérifier que tout fonctionne, nous allons devoir vérifier que saltmaster1 peut gérer sa propre configuration. Pour cela, il ne nous manque plus grand chose.

D’abord, personnaliser la configuration du salt minion en changeant la ligne suivante, qui permettra au salt minion de contacter le salt master:

master: <ip publique de saltmaster1>

Ensuite, ajouter le saltmaster1 à la liste des minions gérés par salt master grâce à salt-key:

root@saltmaster1# salt-call state.highstate
[ERROR ] The Salt Master has cached the public key for this node, this salt minion will wait for 10 seconds before attempting to re-authenticate
Minion failed to authenticate with the master, has the minion key been accepted?
root@saltmaster1# salt-key -a saltmaster1
salt-key -a saltmaster1
The following keys are going to be accepted:
Unaccepted Keys:
saltmaster1
Proceed? [n/Y] y
Key for minion saltmaster1 accepted.
root@saltmaster1# salt '*' state.highstate
[...]

Terminé ! Revoyons rapidement ce que nous venons de faire:

  1. Nous avons utilisé salt-call, une fonctionnalité du salt-minion pour appliquer un état (state). Cela force le minion à contacter le salt master, vu que le salt minion n’est pas encore authentifié auprès du salt master, il est rejeté mais sa clé est mise en mémoire chez le salt master.
  2. Nous utilisons la commande salt-key du salt master pour ajouter (-a) le minion dont l’ID est saltmaster1.
  3. Une fois que le minion est ajouté, nous utilisons la commande salt du salt master pour appliquer l’état state.highstate à tous les IDs '*'. Nous aurions pu relancer la commande salt-call state.highstate qui aurait eu le même résultat dans ce contexte.

L’état state.highstate indique à salt master d’appliquer tous les fichiers d’états aux salt minion sélectionnés.

Cette première étape de « bootstrapping » étant faite pour Salt, dans le prochain volet nous verrons comment installer et configurer tous les services de base de votre machine.

Bien entendu, si vous avez des questions ou des remarques, posez-les dans les commentaires ci-dessous et n’oubliez pas de me soutenir si vous aimez ce que je fais !