Mon petit PRA (plan de reprise d’activité) d’auto-hébergement chez CloudWatt, avec OpenStack

J’ai mis en place un petit PRA (https://fr.wikipedia.org/wiki/Plan_de_reprise_d’activité) pour mes serveurs.

C’est rigolo d’appliquer pour l’auto-hébergement les mêmes procédures qu’en entreprise. Les besoins et la mise en œuvre sont finalement assez proches.

Et ce n’est pas juste pour l’exercice : ces petits serveurs (Sheevaplug et Olinuxino A20) sont fragiles. Les filesystems sont sur des cartes SD, l’alimentation non redondée, le matériel non protégé etc. Le risque de panne est donc réel. L’idée est d’avoir un plan de secours en cas de gros problème, pour remettre en ligne le nécessaire le plus vite possible.

Et c’est chez CloudWatt que j’ai décidé de mettre mon instance de secours, en utilisant les APIs OpenStack

cloudwatt   OpenStack

Avant de basculer en PRA

Le PRA est en général un dernier recours.

En cas de panne, il faut d’abord essayer de refaire marcher l’ensemble sur site. Pour ça, on s’appuie sur du classique :

  • sauvegardes régulières (et testées de temps en temps)
  • du matériel de spare (si possible exactement le même : ça peut permettre d’y déplacer directement le filesystem)
  • pouvoir (sans trop transpirer) déplacer des services d’une machine à une autre
  • rendre les services (et serveurs) aussi indépendants les uns des autres que possible : si l’un est indisponible, il faut éviter que ça bloque les autres

Objectifs du PRA dans mon contexte d’auto-hébergement

D’abord identifier les services les plus critiques. Dans mon cas, c’est essentiellement ce blog. J’y rajouterai probablement d’autres services par la suite.

Ensuite définir les objectifs :

Ici, une « perte de données maximale » de 1j me parait largement raisonnable. Le contenu (articles + commentaires) ne bouge pas très souvent donc il y a de grandes chances de ne perdre aucune donnée en restaurant une sauvegarde à J-1.

Quant à la « durée d’interruption maximale », je ne pourrai pas faire mieux qu’1j, à moins que le problème se produise quand je suis chez moi.

Quel site externe?

L’idée est de pouvoir basculer rapidement le blog sur un autre serveur en cas de gros problème technique. Mais où ça?

Pour faire tourner le blog, les besoins sont assez minimalistes, et il y a plein de solutions.

Mes exigences étaient que cela soit gratuit ou très peu cher, et que je puisse facilement faire basculer dessus la résolution DNS.

Assez vite, je me suis tourné vers des clouds avec tarification à l’usage.

En effet, 99% du temps, je n’ai pas besoin que ma machine de PRA soit en ligne. Je n’en ai besoin que dans 2 cas de figure :

  • quand je veux mettre à jour les données dessus (quelques minutes chaque jour)
  • quand je veux basculer dessus (ce qui n’arrivera que très rarement, je l’espère)

Le reste du temps, il faut éviter de payer pour rien.

Pourquoi CloudWatt?

CloudWatt a quelques particularités qui m’ont plu :

  • Basé sur OpenStack : je préfère ne pas me lier à un cloud particulier via des APIs propriétaires. Et ce que j’apprendrai sur les APIs OpenStack pourra me servir sur d’autres clouds
  • Apparemment engagé dans le logiciel libre
  • Hébergé en France

cloudwatt

Pour ne pas payer inutilement, il faut re-créer l’instance (la VM) à chaque fois qu’on en a besoin. Évidemment, ça peut se faire programmatiquement via l’API. Et les données peuvent être persistées dans des volumes, images et snapshots (tarifés suivant l’espace disque utilisé)

Coût prévisionnel

Stockage bloc (image, snapshot ou volume) : 3Go => 0.12€HT/mois

Instance tiny (la moins puissante) : quelques minutes par jour pour la mise à jour. 6 minutes/j = 3h/mois => 0.03€HT/mois

Trafic réseau : minimum=1Go => 0.089€HT/mois

Au total : 0.239€HT/mois = un peu moins de 30 centimes TTC/mois => moins de 3€50/an. Ça parait tout à fait raisonnable.

Et, si j’utilise l’instance de PRA (ce qui devrait être très rare, et ne pas durer longtemps), ça coûte un peu moins de 30 centimes TTC par jour. Là aussi, ce n’est franchement pas exorbitant.

De plus, tout ça est gratuit jusqu’au 24 mars 2015, ce qui permet de tester sans frais.

Pourquoi pas un serveur similaire chez un autre auto-hébergé?

Ce serait une solution radicalement différente : plutôt que de mettre son PRA dans un cloud, le mettre chez un autre auto-hébergé.

Je trouve que cette solution serait assez fun.

Mais c’était l’occasion de jouer avec CloudWatt/OpenStack, donc pas pour cette fois.

Peut-être pour la v2? :-)

Implémentation et méthode

Besoin : synchroniser automatiquement les données vers le serveur externe, une fois par jour.

Principe et persistance des données

Comme déjà évoqué, je n’ai pas besoin d’une instance qui tourne 24h/24, mais uniquement quelques minutes par jour (le temps d’y mettre à jour les données).

Mais comment avoir des données persistantes, et ne pas repartir à zéro à chaque fois?

Au départ, j’avais pensé à mettre tout le filesystem (OS compris) dans un volume persistant. C’est le plus simple à mettre en œuvre mais je suis tombé sur une limitation : impossible de créer un volume persistant (basé sur Debian) de moins de 20 Go. Or je n’ai pas envie de payer pour tout cet espace alors que j’en utiliserai 10 fois moins.

Donc, solution alternative :

  • un snapshot du filesystem pour l’OS et la configuration d’Apache/MySQL : 1.3Go. Je n’aurai besoin de refaire ce snapshot que pour les mises à jour du système ou de la configuration
  • un volume persistant pour les données (avec des liens symboliques qui pointent dessus pour /var/www et  /var/lib/mysql) : 1Go. C’est là-dessus que les données seront mises à jour quotidiennement

Petit inconvénient de cette solution : si on peut attacher un volume au démarrage via l’API, on ne peut pas le faire depuis la console de gestion web. Si on n’a pas d’accès SSH/console à la machine, il faut donc créer l’instance, puis attacher l’IP et le volume, puis la rebooter.

Préparation du serveur distant

sudo apt-get install apache2 libapache2-mod-php5 php5-mysql mysql-server rsync bash-completion
sudo a2enmod rewrite ssl

+ Création du ou des sites Apache2

+ Création du user et de la base de données MySQL

Script de sauvegarde

Création/lancement de la VM

Télécharger le fichier openrc.sh depuis votre compte CloudWatt (qui contient les identifiants), et installer le client Openstack en ligne de commande (j’ai utilisé la version en Python).

OpenStack

La VM peut être lancée par script comme suit :

#!/bin/bash
source openstack-env/bin/activate
source openrc.sh
echo "Création de l'instance"
nova boot --image bb6785de-81de-48c0-8418-f20f48321f4b --flavor 16 instance_pra --block-device id=1fdffab7-1298-4e7b-9261-43bfb67a7605,source=volume,dest=volume
echo "Attente 30s de la préparation de l'instance..."
sleep 30
echo "Affectation de l'IP publique"
nova add-floating-ip instance_pra x.x.x.x
echo "Attente 90s du démarrage d'OpenSSH..."
sleep 90

Synchronisation des données

echo "Synchronisation des données PHP"
rsync -av --delete /var/www/wordpress/ cloud@x.x.x.x:/var/www/wordpress
echo "Export de la base MySQL"
mysqldump -u wordpress --password=xxx wordpress >export_wordpress.sql
echo "Transfert du dump de la base MySQL"
scp export_wordpress.sql cloud@x.x.x.x:/home/cloud
echo "Adaptation des données pour la VM, en SSH"
scp adaptation_donnees_pra.sh cloud@x.x.x.x:/home/cloud
ssh cloud@x.x.x.x 'chmod +x /home/cloud/adaptation_donnees_pra.sh;/home/cloud/adaptation_donnees_pra.sh'

echo "Attente 30s avant suppression de l'instance, par précaution"
sleep 30
echo "Suppression de l'instance"
nova delete instance_pra

Adaptation des données pour la VM

Le fichier adaptation_donnees_pra.sh est exécuté sur le serveur distant :

#!/bin/bash
echo "Import des données MySQL"
mysql -u wordpress --password=xxx -D wordpress < /home/cloud/export_wordpress.sql
# Pratique pour les tests, si l'URL n'est pas la même
#echo "update wp_options set option_value='http://y.y.y.y' where option_name='siteurl' or option_name='home';" | mysql -u root -p -D wordpress
echo "Désactivation de WP SuperCache"
sudo sed -i "/^# BEGIN WPSuperCache/,/^# END WPSuperCache/d" /var/www/wordpress/.htaccess
sudo rm -rf /var/www/wordpress/wp-content/cache/*
sudo rm -rf /var/www/wordpress/wp-content/plugins/wp-super-cache
sudo chown -R www-data:www-data /var/www/wordpress

Et on met tout ça dans un cron pour que ça s’exécute chaque nuit.

Problème de clé SSH regénérée pour chaque instance CloudWatt

En l’état, les scripts ci-dessus échouent à cause d’un problème de clé SSH : à chaque création d’une nouvelle instance chez CloudWatt, la clé SSH est écrasée par une nouvelle. Donc le client SSH plante en disant qu’il ne reconnaît pas la clé correspondant à l’IP.

Première (mauvaise) solution : rajouter des options à ssh/scp/rsync pour qu’ils ignorent le problème

-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=error

Pour rsync, cela consiste à rajouter l’option :

-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=error"

(le LogLevel est nécessaire pour qu’il n’y ait pas de « Warning: Permanently added ‘x.x.x.x’ to the list of known hosts » dans la sortie d’erreur, qui génère en envoi de mail quand c’est lancé en cron)

Ca fonctionne bien, mais ouvre une belle faille de sécurité.

Meilleure solution : donner des clés SSH fixes à la création de l’instance en Customization Script / User Data

Avec OpenStack, il est possible de passer ce qu’ils appellent un « Customization Script » (autrement appelé « User Data ») à la création d’une instance.  Entre autres choses, cela permet de lui dire quelles clés SSH utiliser (au lieu qu’il les génère à la création).

Dans la VM, c’est exécuté au premier démarrage via le package cloud-init (au moins sur Debian et Ubuntu, je ne sais pas pour les autres OS)

A partir des exemples de http://cloudinit.readthedocs.org/en/latest/topics/examples.html, j’ai pu créer un fichier cloud-config.txt qui ressemble à ça (les clés SSH sont des exemples) :

#cloud-config

# Send pre-generated ssh private keys to the server
# If these are present, they will be written to /etc/ssh and
# new random keys will not be generated
ssh_keys:
  rsa_private: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIBxwIBAAJhAKD0YSHy73nUgysO13XsJmd4fHiFyQ+00R7VVu2iV9Qcon2LZS/x
    1cydPZ4pQpfjEha6WxZ6o8ci/Ea/w0n+0HGPwaxlEG2Z9inNtj3pgFrYcRztfECb
    1j6HCibZbAzYtwIBIwJgO8h72WjcmvcpZ8OvHSvTwAguO2TkR6mPgHsgSaKy6GJo
    PUJnaZRWuba/HX0KGyhz19nPzLpzG5f0fYahlMJAyc13FV7K6kMBPXTRR6FxgHEg
    L0MPC7cdqAwOVNcPY6A7AjEA1bNaIjOzFN2sfZX0j7OMhQuc4zP7r80zaGc5oy6W
    p58hRAncFKEvnEq2CeL3vtuZAjEAwNBHpbNsBYTRPCHM7rZuG/iBtwp8Rxhc9I5w
    ixvzMgi+HpGLWzUIBS+P/XhekIjPAjA285rVmEP+DR255Ls65QbgYhJmTzIXQ2T9
    luLvcmFBC6l35Uc4gTgg4ALsmXLn71MCMGMpSWspEvuGInayTCL+vEjmNBT+FAdO
    W7D4zCpI43jRS9U06JVOeSc9CDk2lwiA3wIwCTB/6uc8Cq85D9YqpM10FuHjKpnP
    REPPOyrAspdeOAV+6VKRavstea7+2DZmSUgE
    -----END RSA PRIVATE KEY-----

  rsa_public: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEAoPRhIfLvedSDKw7XdewmZ3h8eIXJD7TRHtVW7aJX1ByifYtlL/HVzJ09nilCl+MSFrpbFnqjxyL8Rr/DSf7QcY/BrGUQbZn2Kc22PemAWthxHO18QJvWPocKJtlsDNi3 smoser@localhost

  dsa_private: |
    -----BEGIN DSA PRIVATE KEY-----
    MIIBuwIBAAKBgQDP2HLu7pTExL89USyM0264RCyWX/CMLmukxX0Jdbm29ax8FBJT
    pLrO8TIXVY5rPAJm1dTHnpuyJhOvU9G7M8tPUABtzSJh4GVSHlwaCfycwcpLv9TX
    DgWIpSj+6EiHCyaRlB1/CBp9RiaB+10QcFbm+lapuET+/Au6vSDp9IRtlQIVAIMR
    8KucvUYbOEI+yv+5LW9u3z/BAoGBAI0q6JP+JvJmwZFaeCMMVxXUbqiSko/P1lsa
    LNNBHZ5/8MOUIm8rB2FC6ziidfueJpqTMqeQmSAlEBCwnwreUnGfRrKoJpyPNENY
    d15MG6N5J+z81sEcHFeprryZ+D3Ge9VjPq3Tf3NhKKwCDQ0240aPezbnjPeFm4mH
    bYxxcZ9GAoGAXmLIFSQgiAPu459rCKxT46tHJtM0QfnNiEnQLbFluefZ/yiI4DI3
    8UzTCOXLhUA7ybmZha+D/csj15Y9/BNFuO7unzVhikCQV9DTeXX46pG4s1o23JKC
    /QaYWNMZ7kTRv+wWow9MhGiVdML4ZN4XnifuO5krqAybngIy66PMEoQCFEIsKKWv
    99iziAH0KBMVbxy03Trz
    -----END DSA PRIVATE KEY-----

  dsa_public: ssh-dss AAAAB3NzaC1kc3MAAACBAM/Ycu7ulMTEvz1RLIzTbrhELJZf8Iwua6TFfQl1ubb1rHwUElOkus7xMhdVjms8AmbV1Meem7ImE69T0bszy09QAG3NImHgZVIeXBoJ/JzByku/1NcOBYilKP7oSIcLJpGUHX8IGn1GJoH7XRBwVub6Vqm4RP78C7q9IOn0hG2VAAAAFQCDEfCrnL1GGzhCPsr/uS1vbt8/wQAAAIEAjSrok/4m8mbBkVp4IwxXFdRuqJKSj8/WWxos00Ednn/ww5QibysHYULrOKJ1+54mmpMyp5CZICUQELCfCt5ScZ9GsqgmnI80Q1h3Xkwbo3kn7PzWwRwcV6muvJn4PcZ71WM+rdN/c2EorAINDTbjRo97NueM94WbiYdtjHFxn0YAAACAXmLIFSQgiAPu459rCKxT46tHJtM0QfnNiEnQLbFluefZ/yiI4DI38UzTCOXLhUA7ybmZha+D/csj15Y9/BNFuO7unzVhikCQV9DTeXX46pG4s1o23JKC/QaYWNMZ7kTRv+wWow9MhGiVdML4ZN4XnifuO5krqAybngIy66PMEoQ= smoser@localhost

Il suffit ensuite, lors de la création de l’instance avec nova boot, de rajouter l’option :

--user-data cloud-config.txt

Cf http://docs.openstack.org/user-guide/content/inserting_userdata.html

Baptême par le feu et enseignements

Le 1er janvier 2015, coupure d’accès Internet chez mon FAI (défaillance du modem). Pas de bol. J’ai dû me lancer dans le PRA à marche forcée.

C’était à peu près prêt, sauf que la mise à jour des données n’était pas encore automatisée. J’ai donc dû mettre à jour les données à la main, mais en-dehors de ça, ça s’est bien déroulé.

Quels enseignements de ce premier test ?

  • Il faut prévoir comment faire la bascule en n’étant pas chez soi. D’où nécessité d’avoir accès aux identifiants/clés pour tout faire (en l’occurrence se connecter à CloudWatt et Gandi + si possible une possibilité de se logger sur la machine : clé SSH ou user/mot de passe). Mais attention à les conserver de manière sécurisée…
  • Si on bascule en PRA, il faut ensuite penser à bloquer la mise à jour quotidienne des données (sinon cela écraserait les données modifiées dans la journée)
  • Prévoir la procédure de bascule retour (fin du PRA). En l’occurrence, il s’agit simplement de re-transférer le site PHP et la base MySQL dans l’autre sens, et refaire la bascule DNS.

Contraintes

Une fois la procédure mise en place, la mise à jour se fait tous les jours, et tout est prêt pour un éventuel PRA.

Seules contraintes qui restent :

  • Tester de temps en temps une bascule, ne serait-ce que pour vérifier que les données sont à jour, que la procédure fonctionne toujours etc
  • Tenir à jour la configuration d’Apache sur l’instance de PRA, quand elle change sur sur le serveur principal

2 réflexions sur « Mon petit PRA (plan de reprise d’activité) d’auto-hébergement chez CloudWatt, avec OpenStack »

  1. Bonjour,

    Bravo et merci beaucoup pour ce tutoriel :)

    Au niveau du déploiement de vos instances, vous pourriez à la place de votre script bash utiliser notre outil d’orchestration (Heat) :

    http://support.cloudwatt.com/debuter/heat-index.html
    ou en ligne de commande :
    http://support.cloudwatt.com/debuter/cli-heat-1-utiliser.html

    Dans les templates d’orchestration, il est également possible d’utiliser les User Data.

    Il existe plusieurs exemples pour déployer du WordPress dont vous pourriez vous inspirer :

    https://github.com/openstack/heat-templates/tree/master/hot/software-config/example-templates/wordpress

    Vous pouvez également faire l’inverse et utiliser Flame pour transformer une structure déployée en template d’orchestration :

    http://dev.cloudwatt.com/fr/blog/introduction-a-flame-le-generateur-de-template-automatique-pour-heat.html

    Petite nuance, Flame ne génère pas les meta data, du coup il vous faudra quand même modifier le template généré pour les insérer.

    N’hésitez pas à me contacter si vous avez des questions.

    Cordialement,

    François

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *