The Great Ansible Experiment (Version Française)

1/52/53/54/55/5 (2 votes, moyenne: 3,00 sur 5)
Loading...
T

Attention, cet article a plus d'une année d'ancienneté. Il est possible que les informations présentées ne soient plus à jour, spécialement dans le cadre d'un article technique.


Bonjour à tous,

Bon, à la base je voulais juste jouer avec l’API de DigitalOcean pour Ansible 2 et voir comment marchait la création / destruction de Droplets.

Ensuite, c’est parti en live je me suis dit que c’était un peu léger de faire un article juste là-dessus et je voulais vous parler d’Ansible de façon plus globale aussi alors je me suis dit que j’allais faire un semblant d’infrastructure pour servir d’exemple.

Cet article n’est pas une procédure, simplement un genre de « Get Started » plutôt complet. Le but n’est pas de reproduire une infrastructure totalement optimisée, fonctionnelle et sécurisée et vous verrez d’ailleurs que je ne me suis pas foulé à certains moments (utilisation de root, mot de passe par défauts dans certaines applis …).

Le but, c’est vraiment de voir comment utiliser Ansible pour du déploiement de masse automatisé et non pas de reproduire quelque chose de totalement fonctionnel (bien que ça marche pas trop mal au moment de la rédaction de cet article).

On verra aussi les quelques dangers d’Ansible et les problématiques (pas tellement) nouvelles que ça apporte.

 

Avant de commencer :

Cet article n’est pas accessible sans connaissance préalable d’Ansible et il n’y a aucun intérêt a faire un « click & run » brainless ici.

Si vous n’avez jamais touché à Ansible je vous conseille de lire tout ce que j’ai déjà écrit avant cet article.

 

L’hébergeur :

Bon, déjà j’ai décidé de faire tout ça chez DigitalOcean pour plusieurs raisons.

  • Ils proposent une API nativement compatible avec Ansible 2
  • Tarification à l’heure
  • Ce n’est pas trop cher pour ce type d’expérimentation et donc accessible au plus grand nombre.

 

Alors, soyons d’accord sur le « pas trop cher ». Oui, au mois DigitalOcean est plus cher que ses concurrents français comme Scaleway ou OVH par contre eux au moins ils ont toujours le mérite de proposé des documentations claires, accessibles et « copy/paste ».

Ensuite, si vous utilisez mon lien de parrainage vous gagnez 10$ en crédit et moi je ne gagne rien tant que vous n’avez pas dépensé 25$ chez eux.

Pour info, ça m’a coûté ~2$ chez Digital Ocean pour faire cet article (les droplets ont tourné pendant 7 heures). Tout sera fait sur le Datacenter FRA1 (Francfort, Allemagne).

 

L’infrastructure :

En tout, on va déployer 8 Droplets. C’est vraiment pour faire une ébauche de déploiement de masse et surtout parce que la limite publique de déploiement est de 10 droplets chez DigitalOcean.

Comme ça, tout le monde pourra reproduire sans avoir à discuter avec le support pour faire évoluer la limite.

Voici la description des Droplets que l’on va déployer :

  • 1 Debian 8 pour accueillir Ansible
  • 2 Debian 8 pour un cluster NGINX + Unison
  • 1 Ubuntu 16.04 pour le HAPROXY en frontal du cluster NGINX
  • 3 Debian 8 pour un cluster galera (MariaDB)
  • 1 Debian 8 pour un contrôleur Munin

 

Celui d’Ansible sera fait à la main, tout le reste sera (presque) totalement automatisé avec Ansible

 

Création du Droplet Ansible :

Commençons par le commencement, il va falloir que l’on crée notre Droplet pour héberger Ansible.

Créez un compte chez DigitalOcean et connectez-vous. Cliquez ensuite simplement sur le lien suivant et remplissez les informations manquantes :

 

Connectez-vous en root et éditez votre fichier sources.list avec la commande suivante :

nano /etc/apt/sources.list

 

Et ajoutez les backports de Debian :

#Ansible
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free

 

Lancez ensuite la commande suivante pour installer Ansible :

apt-get update && apt-get -t jessie-backports install ansible -y

 

Générez une clé privée ed25519 :

ssh-keygen -t ed25519

 

Ensuite, installez Python et les composants nécessaires pour Ansible :

apt-get install -y python python-simplejson python-pip git && pip install dopy

 

« Dopy » est l’API en Python de Digital Ocean pour information.

 

Récupération des playbooks et configurations Ansible :

J’ai mis tous les playbooks créés sur mon GitHub.

Le YAML utilisé par Ansible est une syntaxe très capricieuse basée sur l’indentation, ça permettra de s’affranchir des possibles erreurs de copier/coller.

Créez le dossier pour le labo :

mkdir -p ~/ansible-do-api/ && cd ~/

 

Récupérez les fichiers nécessaires :

git clone https://github.com/stylersnico/The-Great-Ansible-Experiment.git && cd The-Great-Ansible-Experiment
mv * ~/ansible-do-api/
cd ~/ansible-do-api/

 

Vous remarquerez que j’ai laissé toutes les adresses IP en dur dans les playbooks et fichiers de configuration.

Comme ça, vous pourrez vous dessiner un schéma et voir exactement ce que j’ai fait et comment je l’ai fait et voir les liens qui ont étés créés entre les différents serveurs.

 

Vous remarquez aussi dans la suite de l’article que je vais vous dire de créer les fichiers de configuration.

Ils seront donc évidemment déjà présents, c’est juste pour vous éviter les problèmes d’indentation que peut apporter un copier/coller.

 

Création de Droplets de test :

Avant de se lancer dans la création d’une infrastructure complète, on va déjà voir comment marche le déploiement de droplets avec l’api DigitalOcean.

Créez d’abord une clé d’API Read/Write sur la page suivante, on en aura besoin pour gérer les droplets depuis Ansible :

 

Maintenant, appliquez votre clé d’API à tous les playbooks, par exemple :

sed -i "s/DO_API_KEY/20cd5ed666f62eb5a572cd976767249abbce03c16ee3fa9d934a7acdea0d242b/g" ~/ansible-do-api/playbooks/test/*
sed -i "s/DO_API_KEY/20cd5ed666f62eb5a572cd976767249abbce03c16ee3fa9d934a7acdea0d242b/g" ~/ansible-do-api/playbooks/random-infra/*

 

Et créez votre premier playbook pour créer un droplet :

nano ~/ansible-do-api/playbooks/test/digitalocean.yml

 

On va le remplir avec ceci :

---
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure test droplet exists
    digital_ocean: >
      state=present
      command=droplet
      name=droplet-test
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    register: droplet_test

  - name: Show Ip Adress
    debug: msg="IP is {{ droplet_test.droplet.ip_address }}"

 

On vérifie d’abord que la clé SSH existe en local avec la tâche ensure ssh key exists.

Ensuite, on copie la clé publique sur le compte DigitalOcean pour qu’elle soit utilisée lors du déploiement de serveurs avec la tâche ensure key exists at DigitalOcean.

On créer un droplet de test avec la tâche ensure test droplet exists.

Pour finir, on récupère son adresse IP dans la console avec la tâche Show Ip Adress.

 

Vous pourrez supprimer ce droplet de test en créant un autre playbook :

nano ~/ansible-do-api/playbooks/test/digitalocean-delete.yml

 

Que l’on remplit avec ceci :

---
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure test droplet is deleted 
    digital_ocean: >
      state=absent
      command=droplet
      name=droplet-test
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    register: droplet_test

 

Vous remarquerez que l’on a supprimé la tache pour renvoyer l’ip étant donné que l’on supprime le droplet ici.

On a simplement changé le paramètre state pour qu’il soit comme ceci state=absent indiquant a Ansible que la ressource ne devrait pas existé et quelle doit être supprimée si elle existe.

 

Maintenant, on va créer plusieurs Droplets de test en utilisant des boucles :

Créez votre playbook pour créer deux droplets :

nano ~/ansible-do-api/playbooks/test/digitalocean-multiple.yml

 

On va le remplir avec ceci :

---
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - droplet-one
    - droplet-two

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure test droplets exists
    digital_ocean: >
      state=present
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"
    register: droplet_details

  - name: Show details
    debug: msg="IP is {{ item.droplet.ip_address }}"
    with_items: "{{ droplet_details.results }}"

 

Vous pourrez les supprimer en créant un autre playbook :

nano ~/ansible-do-api/playbooks/test/digitalocean-multiple-delete.yml

 

On va le remplir avec ceci :

---
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - droplet-one
    - droplet-two

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure test droplets are deleted
    digital_ocean: >
      state=absent
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"

 

Création d’une infrastructure complète :

Comme je l’ai déjà dit, on va rester en dessous de 10 Droplets pour faire cette infrastructure, car, c’est la limite de base de DigitalOcean, comme ça tout le monde pourra reproduire sans demander d’augmentation de limite.

 

Déploiement des droplets :

Commencez par créer le playbook pour déployer les droplets :

nano ~/ansible-do-api/playbooks/random-infra/deploy-droplets.yml

 

On va le remplir avec ceci :

---
#Here we deploy droplets for NGINX stack
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - nginx-01
    - nginx-02

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure web stack exist
    digital_ocean: >
      state=present
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"
    register: droplet_details

  - name: Show details
    debug: msg="IP is {{ item.droplet.ip_address }}"
    with_items: "{{ droplet_details.results }}"


#Here we deploy droplet for HA Proxy
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - loadbalancer-01

  tasks:
  - name: ensure web stack exist
    digital_ocean: >
      state=present
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=ubuntu-16-04-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"
    register: droplet_details

  - name: Show details
    debug: msg="IP is {{ item.droplet.ip_address }}"
    with_items: "{{ droplet_details.results }}"

#Here we deploy droplets for the gallera MariaDB Cluster
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - galera-01
    - galera-02
    - galera-03

  tasks:
  - name: ensure galera stack exists
    digital_ocean: >
      state=present
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=1gb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"
    register: droplet_details

  - name: Show details
    debug: msg="IP is {{ item.droplet.ip_address }}"
    with_items: "{{ droplet_details.results }}"

#Here we deploy droplet for Munin
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - munin

  tasks:
  - name: ensure Munin droplet exist
    digital_ocean: >
      state=present
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=2gb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"
    register: droplet_details

  - name: Show details
    debug: msg="IP is {{ item.droplet.ip_address }}"
    with_items: "{{ droplet_details.results }}"

 

Lancez le playbook avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/deploy-droplets.yml

 

Maintenant, éditez le fichier hosts et ajoutez les adresses de vos droplets :

nano ~/ansible-do-api/hosts

 

Par exemple pour moi :

[digitalocean]
localhost ansible_connection=local

[loadbalancer]
46.101.201.239 ansible_connection=ssh ansible_user=root

[nginx]
46.101.99.247 ansible_connection=ssh ansible_user=root
139.59.140.250 ansible_connection=ssh ansible_user=root

[unisonmaster]
46.101.99.247 ansible_connection=ssh ansible_user=root

[galera]
46.101.101.229 ansible_connection=ssh ansible_user=root
46.101.190.144 ansible_connection=ssh ansible_user=root
46.101.115.102 ansible_connection=ssh ansible_user=root

[munin]
139.59.214.129 ansible_connection=ssh ansible_user=root

 

Après ça il faudra valider manuellement les empreintes ssh de chaque hôte.

Installez aussi les dépendances nécessaires :

ansible all -m raw -a "apt-get install -y python python-simplejson"

Sécurisation des droplets :

On va commencer par sécuriser les hôtes et les mettre à jour.

Dans un premier temps, installez tous les rôles qu’on va utiliser avec la commande suivante :

chmod +x roles.sh && ./roles.sh

 

Voici ce que l’on va accomplir :

  • Déploiement de la configuration NTP
  • Configuration de SSHD, Fail2Ban et unattended upgrades
  • Installation de NeedRestart
  • Installation et configuration de UFW

 

Un point sur la configuration du pare-feu (UFW) :

Le HAProxy aura le port 80 ouvert sur le monde.

Les serveurs NGINX auront le port 80 uniquement accessible depuis le HAProxy. Ils pourront librement communiquer entre eux.

Les serveurs Galera auront le port 3306 uniquement accessible depuis les serveurs NGINX. Ils pourront librement communiquer entre eux.

Le serveur Munin aura le port 80 ouvert sur le monde.

Le serveur Ansible aura le port 22 ouvert sur le monde.

Tous les serveurs (hors Ansible) auront le port 22 accessible uniquement depuis Ansible et le port 4949 accessible uniquement depuis Munin.

 

Créez maintenant le fichier de déploiement :

nano ~/ansible-do-api/playbooks/random-infra/security.yml

 

On va le remplir avec ceci :

---
#First of all - NTP
- hosts: all
  roles:
    - role: resmo.ntp
      ntp_config_server: [0.de.pool.ntp.org, 1.de.pool.ntp.org, 2.de.pool.ntp.org, 3.de.pool.ntp.org]

#SSH + auto-updates
- hosts: all
  vars:
    security_ssh_port: 22
    security_ssh_password_authentication: "no"
    security_ssh_permit_root_login: "without-password"
    security_ssh_usedns: "no"
    security_autoupdate_enabled: true
    security_fail2ban_enabled: true
  roles:
    - geerlingguy.security


#Needrestart
- hosts: all
  tasks:
    - name: Install NeedRestart
      apt: name=needrestart state=present

#Firewall
- hosts: loadbalancer
  roles:
    - franklinkim.ufw
  vars:
    ufw_rules:
      - { port: 22, rule: allow, from_ip: '46.101.227.239' }
      - { port: 80, rule: allow }
      - { port: 4949, rule: allow, from_ip: '139.59.214.129' }
    ufw_logging: full

- hosts: nginx
  roles:
    - franklinkim.ufw
  vars:
    ufw_rules:
      - { port: 22, rule: allow, from_ip: '46.101.227.239' }
      - { port: 80, rule: allow, from_ip: '46.101.201.239' }
      - { port: 4949, rule: allow, from_ip: '139.59.214.129' }
      - { rule: allow, from_ip: '46.101.99.247' }
      - { rule: allow, from_ip: '139.59.140.250' }
    ufw_logging: full

- hosts: galera
  roles:
    - franklinkim.ufw
  vars:
    ufw_rules:
      - { port: 22, rule: allow, from_ip: '46.101.227.239' }
      - { port: 3306, rule: allow, from_ip: '46.101.99.247' }
      - { port: 3306, rule: allow, from_ip: '139.59.140.250' }
      - { port: 4949, rule: allow, from_ip: '139.59.214.129' }
      - { rule: allow, from_ip: '46.101.101.229' }
      - { rule: allow, from_ip: '46.101.190.144' }
      - { rule: allow, from_ip: '46.101.115.102' }
    ufw_logging: full

- hosts: munin
  roles:
    - franklinkim.ufw
  vars:
    ufw_rules:
      - { port: 22, rule: allow, from_ip: '46.101.227.239' }
      - { port: 80, rule: allow }
      - { port: 4949, rule: allow, from_ip: '139.59.214.129' }
    ufw_logging: full

- hosts: digitalocean
  roles:
    - franklinkim.ufw
  vars:
    ufw_rules:
      - { port: 22, rule: allow }
      - { port: 4949, rule: allow, from_ip: '139.59.214.129' }
    ufw_logging: full

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/security.yml

Mise à jour des droplets :

Et oui, les mises à jour c’est le plus important dans une infrastructure.

Pour ça on va faire un playbook qui :

  • Lance un apt-get update
  • Lance un apt-get upgrade en conservant toujours les fichiers de configuration en place
  • Lance NeedRestart pour redémarrer les services qui en ont besoin
  • Lance un reboot si il y’en a besoin (Trust me dude, it’s safe).

 

Créez maintenant le playbook :

nano ~/ansible-do-api/playbooks/random-infra/apt-upgrade.yml

 

On va le remplir avec ceci :

- hosts: all
  tasks:
   - name: updates a server
     apt: update_cache=yes cache_valid_time=3600

   - name: upgrade a server
     apt: upgrade=dist dpkg_options='force-confold,force-confdef'

   - name: Check if a reboot is required
     register: reboot
     stat: path=/var/run/reboot-required get_md5=no

   - name: Reboot the services
     shell: needrestart -ra -l
     when: reboot.stat.exists == false

   - name: Reboot the server
     command: /sbin/reboot
     when: reboot.stat.exists == true

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/apt-upgrade.yml

 

Déploiement des serveurs NGINX + HAProxy :

Bon, maintenant qu’on en as fini avec la base, on va déployer les deux serveurs NGINX et le HAPROXY.

Créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/nginx.yml

 

On va le remplir avec ceci :

---
- hosts: nginx
  roles:
    - role: bennojoy.nginx
      nginx_http_params:
        sendfile: "on"
        access_log: "/var/log/nginx/access.log"
      nginx_sites:
       - server:
          file_name: www.vhost
          listen: 80
          server_name: _
          root: "/var/www/html/"
          location1: {name: /, try_files: "$uri $uri/ /index.html"}
          location2: {name: /images/, try_files: "$uri $uri/ /index.html"}

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/nginx.yml

 

Maintenant, vous pouvez tester le bon fonctionnement des serveurs NGINX avec la commande suivante :

ansible loadbalancer -m shell -a 'curl -I 139.59.140.250 && curl -I 46.101.99.247'

 

Maintenant HAPROXY, créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/haproxy.yml

 

On va le remplir avec ceci :

- hosts: loadbalancer
  tasks:
  - name: install make
    apt: name=make state=present

- hosts: loadbalancer
  vars:
    haproxy_frontends:
      http_front:
      - bind *:80
      - default_backend http_back
    haproxy_backends:
      http_back:
      - balance roundrobin
      - server http1 139.59.140.250:80 check
      - server http2 46.101.99.247:80 check

  roles:
  - role: SimpliField.haproxy

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/haproxy.yml

 

Maintenant, vous pouvez tester le bon fonctionnement de HAPROXY avec la commande suivante (vous verrez le Roundrobin faire effet en la lançant plusieurs fois :

curl -I 46.101.201.239

 

Déploiement de Unison:

Unison nous servira à synchroniser les répertoires /var/www/ des deux serveurs NGINX ici.

Malheureusement, je ne nous ai pas trouvé de playbook qui installait correctement Unison de A à Z, il faudra donc configurer l’échange de clé SSH manuellement avec l’aide de cette procédure.

Créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/unison.yml

 

On va le remplir avec ceci :

- hosts: unisonmaster
  roles:
    - weareinteractive.unison
  vars:
    unison_configs:
      - name: sync
        src: /var/www
        dest: ssh://139.59.140.250//var/www
        unison_user: unison
        ignore:
          - ".*"
          - "~*"

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/unison.yml

 

Connectez-vous sur le master unison et testez la synchronisation avec la commande suivante :

unison /var/www ssh://139.59.140.250//var/www

 

Déploiement du cluster Galera (MariaDB) :

Dans un premier temps, il va falloir supprimer une section inutile du rôle utilisé pour Galera avec la commande suivante :

echo "" > /etc/ansible/roles/uoi-io.galera/tasks/firewall.yml

C’est inutile, car nous avons installé et configuré UFW au préalable.

 

Créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/galera.yml

 

On va le remplir avec ceci :

---
- hosts: galera
  roles:
    - uoi-io.galera
  vars:
    master: 46.101.101.229
    mariadb_bind_address: 0.0.0.0
    mariadb_max_connections: 4096
    mariadb_maintenance_password: I3uL6AqJLHInv85x
    mariadb_root_password: 3248ew7dsYUG762
    mariadb_hosts_allow: 10.0.%

    galera_node_address: "{{ ansible_eth0.ipv4.address }}"
    galera_pacemaker_support: false
    galera_clustercheck_user: clustercheck
    galera_clustercheck_password: Y3aH1l0ved2CH3CK
    galera_cluster_name: uoi-sql-cluster
    galera_sst_password: gr34tp4ss0rd
    galera_cluster_nodes:
      - 46.101.101.229
      - 46.101.190.144
      - 46.101.115.102

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/galera.yml

 

Vous pourrez vérifier le statut du Cluster sur chaque hôte avec la commande suivante :

ansible galera -m shell -a 'galera-status'

 

Déploiement de Munin-node :

On va maintenant déployer Munin-nodes sur tous les serveurs de l’infrastructure pour le monitoring.

Ensuite seulement on s’occupera du Master.

Créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/munin-nodes.yml

 

On va le remplir avec ceci :

- hosts: all
  roles:
    - { role: geerlingguy.munin-node }
  vars:
    munin_node_bind_host: "*"

- hosts: all
  tasks:
   - name: Enable Munin Modules
     shell: "munin-node-configure --shell --families=contrib,auto | sh -x && service munin-node restart"

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/munin-nodes.yml

 

Maintenant, créez le playbook pour déployer le master :

nano ~/ansible-do-api/playbooks/random-infra/munin-nodes.yml

 

On va le remplir avec ceci :

---
- hosts: munin
  roles:
    - geerlingguy.munin
  vars:
    munin_htmldir: /var/cache/munin/www
    munin_hosts:
      - {
        name: "ansible",
        address: "46.101.227.239",
        extra: ["use_node_name yes"]
      }
      - {
        name: "haproxy",
        address: "46.101.201.239",
        extra: ["use_node_name yes"]
      }
      - {
        name: "nginx-01",
        address: "46.101.99.247",
        extra: ["use_node_name yes"]
      }
      - {
        name: "nginx-02",
        address: "139.59.140.250",
        extra: ["use_node_name yes"]
      }
      - {
        name: "galera-01",
        address: "46.101.101.229",
        extra: ["use_node_name yes"]
      }
      - {
        name: "galera-02",
        address: "46.101.190.144",
        extra: ["use_node_name yes"]
      }
      - {
        name: "galera-03",
        address: "46.101.115.102",
        extra: ["use_node_name yes"]
      }

- hosts: munin
  roles:
    - role: bennojoy.nginx
      nginx_http_params:
        sendfile: "on"
        access_log: "/var/log/nginx/access.log"
      nginx_sites:
       - server:
          file_name: www.vhost
          listen: 80
          server_name: _
          root: "/var/cache/munin/www"
          location1: {name: /, try_files: "$uri $uri/ /index.html"}

 

Et lancez le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/munin-nodes.yml

 

Suppression de l’infrastructure :

Maintenant, il s’agit de supprimer tous les droplets créer précédemment (hormis Ansible).

Créez le playbook :

nano ~/ansible-do-api/playbooks/random-infra/destroy-droplets.yml

 

On va le remplir avec ceci :

---
- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - nginx-01
    - nginx-02

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure web stack is deleted
    digital_ocean: >
      state=absent
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"

- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - loadbalancer-01

  tasks:
  - name: ensure ssh key exists
    user: >
      name={{ ansible_user_id }}
      generate_ssh_key=yes
      ssh_key_file=.ssh/id_ed25519

  - name: ensure key exists at DigitalOcean
    digital_ocean: >
      state=present
      command=ssh
      name=ansible_ssh_key
      ssh_pub_key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}
      api_token={{ do_token }}
    register: my_ssh_key

  - name: ensure loadbalancer is deleted
    digital_ocean: >
      state=absent
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=512mb
      region_id=fra1
      image_id=ubuntu-16-04-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"


- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - galera-01
    - galera-02
    - galera-03

  tasks:
  - name: ensure galera stack is deleted
    digital_ocean: >
      state=absent
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=1gb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"

- hosts: digitalocean

  vars:
    do_token: DO_API_KEY
    droplets:
    - munin

  tasks:
  - name: ensure Munin droplet is deleted
    digital_ocean: >
      state=absent
      command=droplet
      name={{ item }}
      unique_name=yes
      size_id=2gb
      region_id=fra1
      image_id=debian-8-x64
      ssh_key_ids={{ my_ssh_key.ssh_key.id }}
      api_token={{ do_token }}
    with_items: "{{ droplets }}"

 

Et lancez-le avec la commande suivante :

cd ~/ansible-do-api/
ansible-playbook playbooks/random-infra/destroy-droplets.yml

 

Conclusion :

Déjà, si vous avez tout suivi et tout lu, félicitations et merci, vous arrivez au bout du plus gros article que j’ai jamais écrit (+3000 mots contre 300-600 en moyenne).

Pour l’anecdote, il m’a fallu une journée entière pour préparer l’infrastructure et une autre journée entière pour rédiger les articles en Français et Anglais, ça m’a donc légèrement occupé.

Ce qu’il faut retenir ?

  • Vous pouvez garder les parties Sécurisation et Mise à jour des droplets pour de la production. C’est même un must-have.
  • N’espérez pas monter une infrastructure sans travail et uniquement avec ce que vous trouvez sur Ansible Galaxy. Les partages sont très mal documentés, largement non fonctionnels et loin d’avoir le niveau de sécurité nécessaire pour de la production.
  • Si vous développez vos propres rôles et que vous les publiez, alors soignez la documentation bordel, testez-les et favorisez des playbooks simples et fonctionnels qui utilisent des variables clairement définissables et documentées dans les bordel de README (Philosophies KISS / Unix non de Zeus).
  • Évitez les dépendances au maximum et les projets types DebOps qui sont des usines à gaz à la syntaxe marquée comme deprecated sur Ansible 2.
  • Si vous utilisez des dépendances, intégrez les de manière fluide et invisible dans le rôle ça évitera de se prendre la tête.

 

Si vous débutez sur Ansible, j’espère que ça vous aura permis d’appréhender un peu mieux les possibilités offertes par ce formidable outil.

Petite note supplémentaire, comme je l’ai dit, c’est uniquement de la démonstration que je fais dans cet article, il ne s’agit pas de tout reproduire de A à Z en pensant tenir une infrastructure complète, fonctionnelle et sécurisée. Tous les commentaires demandant du support seront donc supprimés.

 

 

A propos de l'auteur

Nicolas Simond

Ingénieur Systèmes et Réseaux et guitariste hard rock et metal à mes heures perdues.
Je suis le créateur et l'unique rédacteur d'Abyss Project, c'est ici que je note la plupart de mes procédures et quelques divagations.

Si vous l'article vous a aidé, pensez à me payer un café :)

Subscribe
Notify of
guest

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

2 Commentaires
Plus récents
Plus anciens Populaires
Inline Feedbacks
View all comments
Sébastien Elet
Sébastien Elet
7 années plus tôt

Merci pour cet article.

Si vous souhaitez faire vos tests en local, il est possible également le faire via vagrant.
J’utilise beaucoup vagrant pour expérimenter mes roles/playbook sur des problématiques de clustering.

Un exemple (sans doc, mais un simple make lance tout le setup) : https://github.com/SimpliField/ansible-vagrant-examples/tree/master/mongo-rs

Cascador
7 années plus tôt

Salute,

Merci pour le partage, c’est très sympa comme expérience.

Tcho !