Le but de ce how-to est de monter un cluster qui hébergera des machines virtuelles. Il utilisera deux serveurs de chez Online (on a pas d'action chez eux, c'est juste que leurs offres sont très agressives ^^) en tant qu'hyperviseurs. Il doit être possible de:
La première étape est d'installer une CentOS 6 (6.4 étant la dernière version actuellement) en édition x86_64. CentOS n'étant plus supportée par l'installateur d'Online 1), il faut d'abord installer n'importe quel système (une debian par exemple), puis, une fois installé, on peut accéder à l'interface iLO (ou iDRAC selon la marque du serveur, iLO pour les IBM, iDRAC pour les Dell). On peut ensuite charger un applet java (oui, beurk, mais c'est toujours mieux que de l'activeX ) pour prendre la main sur la console du serveur (une sorte de KVM sur IP) ainsi qu'un lecteur CD virtuel. Il faut simplement connecter une ISO netinstall de CentOS dans ce lecteur virtuel et redémarrer le serveur, qui va s'amorcer dessus. L'amorce peut prendre du temps selon la vitesse de votre connexion, puisque l'image netinstall sera streamée depuis votre poste.
La suite de ce how-to considère que:
Certains services ne sont pas nécessaires et sont donc désactivés maintenant.
chkconfig ip6tables off chkconfig iptables off /etc/init.d/iptables stop /etc/init.d/ip6tables stop
Pour éviter des chutes de performances, il faut éviter à tout prix de swapper, quitte à exploiter moins de cache. On désactive aussi l'IPv6 puisqu'on ne s'en servira pas dans notre cas
echo "vm.swappiness=0" >> /etc/sysctl.conf echo "vm.vfs_cache_pressure=10000" >> /etc/sysctl.conf echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf sysctl -p
Avec cette configuration, le swap ne devrait être utilisé que pour éviter un OOM (dans la pratique, il semble que le swap soit toujours un peu utilisé, même si moins qu'avant. La version 6.4 de CentOS semble utiliser beaucoup plus facilement le swap, espérons que ce comportement soit corrigé dans la 6.5)
Dans cette étape, nous allons, sur chacun des hyperviseurs, créer quelques fichiers texte simples, qui contiennent des variables. Ça simplifiera la suite
mkdir /etc/install chmod 700 /etc/install echo hyp1 > /etc/install/hostname echo hyp2 > /etc/install/peer_hostname echo firewall-services.com > /etc/install/dnsdomainname echo 10.25.9.46 > /etc/install/rpn_ip echo B4:B5:2F:51:9B:61 > /etc/install/rpn_mac echo 10.25.8.47 > /etc/install/rpn_peer_ip echo 1 > /etc/install/host_id echo 37651 > /etc/install/online_sdx echo xxxxxxxxxxxxxxxxxxxxx > /etc/install/api_key
Faites la même chose sur hyp2, en adaptant les valeurs.
Gardez cette clé secrète, elle permet de gérer à distance vos serveurs, y compris les redémarrer
Il va falloir ajouter quelques dépôts, certains composants logiciels n'étant pas disponibles dans les dépôts de base, ou leur version étant trop ancienne
Le dépôt EPEL contient de nombreux logiciels, et sera utilisé pour openvpn, ainsi que quelques outils de base
rpm -Uvh http://fr2.rpmfind.net/linux/epel/6/i386/epel-release-6-8.noarch.rpm
Maintenant que le dépôt EPEL est configuré, on peut en profiter pour installer quelques outils de base que je juge indispensables
yum install htop screen strace pbzip2 iftop iptraf man rsync bash-completion wget openssh-clients vim acpid tcpdump pciutils patch sysstat
La version de GlusterFS dans les dépôts de base est un peu vieillotte, et surtout, ne permet pas un recovery pendant que les fichiers sont ouverts. On va donc utiliser le dépôt RHEL fourni par gluster.org pour avoir une version plus récente.
wget -P /etc/yum.repos.d/ \ http://download.gluster.org/pub/gluster/glusterfs/3.4/LATEST/RHEL/glusterfs-epel.repo
Il faut maintenant installer les outils de base pour virtualiser tout notre petit monde
yum install libvirt qemu-kvm \
bridge-utils policycoreutils-python
On va appliquer une configuration minimale, mais fonctionnelle de libvirt. Le but est de:
KSM 2) est un mécanisme permettant de mettre en commun des pages mémoire identiques (y compris entre plusieurs invités). En utilisant un peu de CPU, on peut donc récupérer de la mémoire. Au plus vous faites tourner des systèmes identiques ou proche, au plus vous avez de chance d'voir des pages identiques, et donc d’économiser de la RAM. Le démon ksmtuned permet quand à lui de surveiller la pertinence et KSM (démarrage quand la mémoire vient à manquer, ajustement de l'agressivité du scan en fonction de la pression sur la RAM, désactivation quand ce n'est plus nécessaire etc…)
chkconfig ksm on chkconfig ksmtuned on mv /etc/ksmtuned.conf /etc/ksmtuned.conf.default cat <<'EOF' > /etc/ksmtuned.conf KSM_MONITOR_INTERVAL=10 KSM_SLEEP_MSEC=5 KSM_NPAGES_BOOST=600 KSM_NPAGES_DECAY=-200 KSM_NPAGES_MIN=64 KSM_NPAGES_MAX=4094 KSM_THRES_COEF=45 KSM_THRES_CONST=9144 LOGFILE=/var/log/ksmtuned.log DEBUG=1 EOF cat <<'EOF' > /etc/logrotate.d/ksmtuned /var/log/ksmtuned.log { weekly missingok rotate 4 compress delaycompress copytruncate minsize 100k } EOF /etc/init.d/ksm start /etc/init.d/ksmtuned start
mv /etc/libvirt/libvirtd.conf \ /etc/libvirt/libvirtd.conf.default cat <<EOF > /etc/libvirt/libvirtd.conf listen_tls = 0 listen_tcp = 0 mdns_adv = 0 unix_sock_group = "libvirt" unix_sock_rw_perms = "0770" auth_unix_ro = "none" auth_unix_rw = "none" EOF
On va aussi empêcher le marquage des machines virtuelles pour un démarrage automatique. Si pour un hyperviseur simple c'est une fonction intéressante, dans le cas d'un cluster, c'est plutôt dangereux, il vaut mieux maîtriser cette phase.
mkdir -p /etc/libvirt/qemu/autostart/ chattr +i /etc/libvirt/qemu/autostart/
useradd fws
passwd fws
groupadd libvirt usermod -a -G libvirt fws
Une variable SELinux doit être activée pour certains types de sauvegardes utilisant rsync.
setsebool -P rsync_export_all_ro=1
Sur les deux hyperviseurs, il faut maintenant configurer l'interface privée (celle connectée sur le réseau RPN, normalement eth1):
cat <<'EOF' > /etc/sysconfig/network-scripts/ifcfg-eth1 DEVICE=eth1 HWADDR=__MAC__ ONBOOT=yes NM_CONTROLLED=no TYPE=Ethernet BOOTPROTO=dhcp MTU=9000 EOF sed -i -e "s/__MAC__/$(cat /etc/install/rpn_mac)/g" \ /etc/sysconfig/network-scripts/ifcfg-eth1 /etc/init.d/network restart
Si tout se passe bien, vous devriez pouvoir pinguer les deux hyperviseurs entre eux via le réseau RPN. Ça ne sert à rien d'aller plus loin tant que ça n'est pas le cas.
Il faut que les deux hyperviseurs puissent toujours se joindre, même en cas de problème de DNS, le plus simple est donc de rajouter quelques alias dans /etc/hosts
cat <<'EOF' > /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 __HOSTNAME__ __HOSTNAME__.__DOMAINNAME__ ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 __PEER_IP__ peer __PEER_HOSTNAME__ __PEER_HOSTNAME__.__DOMAINNAME__ EOF sed -i -e "s/__HOSTNAME__/$(cat /etc/install/hostname)/g" \ -e "s/__DOMAINNAME__/$(cat /etc/install/dnsdomainname)/g" \ -e "s/__PEER_IP__/$(cat /etc/install/rpn_peer_ip)/g" \ -e "s/__PEER_HOSTNAME__/$(cat /etc/install/peer_hostname)/g" /etc/hosts
GlusterFS sera utilisé pour fournir l'espace de stockage accessible depuis les deux hyperviseurs en simultané.
yum install glusterfs-fuse glusterfs \
glusterfs-server
Les données exportées par GlusterFS seront stockées sur un volume LVM sur chaque hyperviseur. Dans cet exemple, on va créer un volume de 800Go. Vous pouvez ajuster en fonction de l'espace disponible sur vos serveurs.
lvcreate -L 800G -n vmstore main
Red Hat recommande l'utilisation du système de fichier XFS pour l'utilisation de Gluster. Ext4 fonctionne aussi, à vous de voir celui que vous préférez
yum install xfsprogs
mkfs.xfs -f -i size=512 -L VMSTORE /dev/main/vmstore
Les volumes qui stockeront les données Gluster seront montés dans /mnt/bricks/<nom du volume>
mkdir -p /mnt/bricks/vmstore
echo "/dev/main/vmstore /mnt/bricks/vmstore xfs noatime 0 0" \ >> /etc/fstab mount -a mount
chkconfig glusterd on /etc/init.d/glusterd start
Depuis un hyperviseur (peu importe lequel), on va vérifier la connectivité avec l'hyperviseur distants, via un probe
gluster peer probe hyp2
gluster volume create vmstore replica 2 \ hyp1:/mnt/bricks/vmstore hyp2:/mnt/bricks/vmstore
gluster volume start vmstore
On peut faire quelques réglages sur ce volume pour améliorer les performances
gluster volume set vmstore nfs.disable on gluster volume set vmstore network.ping-timeout 5 gluster volume set vmstore network.frame-timeout 300
Autres réglages recommandés pour optimiser les performances
gluster volume set vmstore performance.stat-prefetch off gluster volume set vmstore performance.io-cache off gluster volume set vmstore performance.read-ahead off gluster volume set vmstore remote-dio on gluster volume set vmstore cluster.eager-lock on
Ce nouveau volume GlusterFS sera monté dans /var/lib/libvirt/images, on pourra ensuite définir plusieurs pool de stockage:
echo "localhost:/vmstore /var/lib/libvirt/images glusterfs defaults,_netdev 0 0" \ >> /etc/fstab chkconfig netfs on mount -a
GlusterFS étant basé sur FUSE, il faut autoriser les VM à accéder aux systèmes de fichiers FUSE (SELinux le bloque par défaut):
setsebool -P virt_use_fusefs=1
le démon sanlock va permettre de poser des verrous sur les images disques, et ainsi d'empêcher la même machine virtuelle de démarrer en même temps sur les deux hyperviseurs (ce qui corromprait les données à coup sûre)
yum install libvirt-lock-sanlock
wdmd agit comme un watchdog et forcera l'hyperviseur à redémarrer en cas de problème
chkconfig wdmd on /etc/init.d/wdmd start
sanlock doit pouvoir écrire sur un répertoire commun entre les deux hyperviseurs. Le plus simple est donc de créer un nouveau volume GlusterFS pour le stockage des verrous.
lvcreate -L 10G -n sanlock main mkfs.xfs -f -i size=512 -L SANLOCK /dev/main/sanlock mkdir -p /mnt/bricks/sanlock chown :sanlock /mnt/bricks/sanlock chmod 770 /mnt/bricks/sanlock echo "/dev/main/sanlock /mnt/bricks/sanlock xfs noatime 0 0" >> /etc/fstab mount -a
gluster volume create sanlock replica 2 \ hyp1:/mnt/bricks/sanlock hyp2:/mnt/bricks/sanlock gluster volume start sanlock gluster volume set sanlock nfs.disable on gluster volume set sanlock network.ping-timeout 5 gluster volume set sanlock network.frame-timeout 10
echo "localhost:/sanlock /var/lib/libvirt/sanlock glusterfs defaults,_netdev 0 0" \ >> /etc/fstab mount -a
chkconfig sanlock on /etc/init.d/sanlock start
mv /etc/libvirt/qemu-sanlock.conf \ /etc/libvirt/qemu-sanlock.conf.default cat <<'EOF' > /etc/libvirt/qemu-sanlock.conf auto_disk_leases = 1 host_id = __HOST_ID__ user= "sanlock" group = "sanlock" EOF sed -i -e "s/__HOST_ID__/$(cat /etc/install/host_id)/g" \ /etc/libvirt/qemu-sanlock.conf
mv /etc/libvirt/qemu.conf \ /etc/libvirt/qemu.conf.default cat <<'EOF' > /etc/libvirt/qemu.conf lock_manager = "sanlock" EOF
setsebool -P virt_use_sanlock=1 setsebool -P sanlock_use_fusefs=1
chkconfig libvirtd on /etc/init.d/libvirtd restart
À partir de maintenant, vous devriez pouvoir ajouter les connexions vers hyp1 et hyp2 dans virt-manager. Ne créez pas de machine virtuelle pour l'instant, vérifier juste que la connexion est OK (avec l'URI qemu+ssh://fws@hyp1/system)
Dans certains cas, un verrou peut se perdre (crash d'une VM, crash du démon sanlock etc…), on va donc mettre en place une tâche cron qui va nettoyer les verrous qui ne devraient plus être là. Ces cas sont suffisamment rares pour que la tâche ne s'exécute qu'une fois par jour (une fois par semaine serait même probablement suffisant)
cat <<'EOF' > /etc/cron.daily/sanlock-cleanup #!/bin/bash # Sleep between 0 second and 30 minutes # So hypervisors do not cleanup at the same time sleep $[ $RANDOM % 1800 ] /usr/sbin/virt-sanlock-cleanup >/dev/null 2>&1 EOF chmod +x /etc/cron.daily/sanlock-cleanup
On peut maintenant définir un premier pool de stockage pour les images ISO
mkdir -p /var/lib/libvirt/images/iso cat <<'EOF' > iso.xml <pool type='dir'> <name>iso</name> <source> </source> <target> <path>/var/lib/libvirt/images/iso</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </pool> EOF virsh pool-define iso.xml virsh pool-start iso virsh pool-autostart iso
On va aussi créer un second pool pour le stockage des images des VM. Vous pouvez créer autant de pool que nécessaire, par exemple, un pool (aka répertoire dans le volume Gluster) par client
mkdir -p /var/lib/libvirt/images/firewallservices cat <<'EOF' > firewallservices.xml <pool type='dir'> <name>firewallservices</name> <source> </source> <target> <path>/var/lib/libvirt/images/firewallservices</path> <permissions> <mode>0755</mode> <owner>0</owner> <group>0</group> </permissions> </target> </pool> EOF virsh pool-define firewallservices.xml virsh pool-start firewallservices virsh pool-autostart firewallservices
Libvirt peut utiliser une fonction de sauvegarde sur disque pour suspendre un invité en sauvegardant son état dans un fichier sur disque. Par défaut, ces dumps mémoire sont stockés dans /var/lib/libvirt/qemu/save. L'idéal est donc de créer un volume partagé entre les deux hyperviseurs (ainsi une VM suspendue sur l'un peu être réveillée sur l'autre).
lvcreate -L50G -n save main mkfs.xfs -L QEMU -i size=512 -f /dev/main/save mkdir -p /mnt/bricks/save echo "/dev/main/save /mnt/bricks/save xfs noatime 0 0" \ >> /etc/fstab mount -a
Maintenant, on crée un volume gluster
gluster volume create save replica 2 hyp1:/mnt/bricks/save hyp2:/mnt/bricks/save gluster volume start save gluster volume set save nfs.disable on gluster volume set save network.ping-timeout 5 gluster volume set save network.frame-timeout 300
Puis on monte ce volume
echo "localhost:/save /var/lib/libvirt/qemu/save glusterfs defaults,_netdev 0 0" \ >> /etc/fstab mount -a
On va maintenant créer deux réseaux pour les VM. Le premier, nommé wan sera utilisé pour connecter les VM à Internet (avec une IP publique), l'autre, nommé lan sera isolé et permettra de disposer d'un réseau privé. Les hyperviseurs n'ont pas d'accès à ces réseau, le routage entre le lan et le wan sera donc à charge d'une VM (qui aura une interface dans chaque réseau). Ces réseaux seront reliés à leur homologue entre les deux hyperviseurs via des tunnels OpenVPN, permettant de migrer une VM d'un hyperviseur à l'autre sans perte de connexion. Tout le trafic entre les deux hyperviseurs se faisant sur le réseau privé (RPN), OpenVPN sera utilisé sans aucune authentification, ni chiffrement, afin d'économiser des ressources CPU.
Les réseau par défaut ne seront pas utilisés:
virsh net-destroy default virsh net-undefine default
On crée un bridge nommé brwan sur les hyperviseurs. Ce bridge n'aura pour l'instant aucune interface esclave:
cat <<'EOF' > /etc/sysconfig/network-scripts/ifcfg-brwan DEVICE=brwan TYPE=Bridge ONBOOT=yes BOOTPROTO=none IPV6INIT=no EOF
Même manipulation, pour le réseau lan avec un bridge brlan
cat <<'EOF' > /etc/sysconfig/network-scripts/ifcfg-brlan DEVICE=brlan TYPE=Bridge ONBOOT=yes BOOTPROTO=none IPV6INIT=no IPV6_AUTOCONF=no EOF
/etc/init.d/network restart
Il faut maintenant créer des tunnels entre les deux hyperviseurs pour relier les deux brwan entre eux, ainsi que les deux brlan (mais les deux réseau lan et wan resteront parfaitement étanches)
Ce script sera appelé par OpenVPN au démarrage de chaque tunnel, et il se chargera d'ajouter l'interface tap du tunnel au bridge correspondant (par exemple taplan sera ajouté automatiquement à brlan)
mkdir -p /etc/openvpn/bin cat <<'EOF' > /etc/openvpn/bin/up #!/bin/bash sleep 2 TAP=$1 ID=${TAP#tap} /sbin/ifconfig $1 0.0.0.0 up promisc /usr/sbin/brctl addif br$ID $TAP EOF chmod +x /etc/openvpn/bin/up restorecon -Rv /etc/openvpn
Par défaut, SELinux va empêcher OpenVPN de faire appel à brctl, il faut donc créer une politique de sécurité personnalisée.
cat <<'EOF' > mycluster.te module mycluster 1.0; require { type openvpn_t; type brctl_exec_t; class file { read execute open getattr execute_no_trans }; } #============= openvpn_t ============== allow openvpn_t brctl_exec_t:file { read execute open getattr execute_no_trans }; EOF checkmodule -M -m -o mycluster.mod mycluster.te semodule_package -o mycluster.pp -m mycluster.mod mkdir -p /usr/share/selinux/packages/mycluster/ mv mycluster.pp /usr/share/selinux/packages/mycluster/ semodule -i /usr/share/selinux/packages/mycluster/mycluster.pp
Même si les deux hyperviseurs seront parfaitement équivalent, pour OpenVPN, il faut en définir un qui jouera le rôle de serveur, et l'autre de client. Dans cet exemple, hyp1 sera le serveur, et hyp2 le client
yum install openvpn
cat <<'EOF' > /etc/openvpn/wan.conf lport 1195 proto udp dev tapwan user openvpn group openvpn up bin/up persist-key persist-tun cipher none ping 5 ping-restart 20 EOF cat <<'EOF' > /etc/openvpn/lan.conf lport 1194 proto udp dev taplan user openvpn group openvpn up bin/up persist-key persist-tun cipher none ping 5 ping-restart 20 EOF semanage port -a -t openvpn_port_t -p udp 1195
cat <<'EOF' > /etc/openvpn/wan.conf rport 1195 proto udp dev tapwan nobind remote __PEER_IP__ up bin/up user openvpn group openvpn chroot /etc/openvpn/ persist-key persist-tun cipher none ping 5 ping-restart 60 EOF cat <<'EOF' > /etc/openvpn/lan.conf rport 1194 proto udp dev taplan nobind remote __PEER_IP__ up bin/up user openvpn group openvpn chroot /etc/openvpn/ persist-key persist-tun cipher none ping 5 ping-restart 60 EOF sed -i -e "s/__PEER_IP__/$(cat /etc/install/rpn_peer_ip)/g" \ /etc/openvpn/wan.conf sed -i -e "s/__PEER_IP__/$(cat /etc/install/rpn_peer_ip)/g" \ /etc/openvpn/lan.conf
chkconfig openvpn on /etc/init.d/openvpn start
Si tout se passe comme prévu, les deux tunnels se monteront (vous pouvez vérifier dans /var/log/messages), et les deux bridges brwan et brlan auront une interface esclave chacune: respectivement tapwan et taplan
Cette étape est optionnelle, puisque toutes les migrations, créations, etc… pourront se faire depuis l'outil graphique virt-manager. Cependant, si vous voulez aussi pouvoir gérer les migrations en ligne de commande, le mieux est de générer quelques clés SSH et de les échanger entre les différents hyperviseurs. Sur chaque hyperviseur, il faut:
Maintenant que la majorité est en place, il est temps de se préoccuper du par feu sur les hyperviseurs. Étant le cœur de l'infrastructure, inutile de préciser que leur sécurité est primordial. L'accès ne doit être possible que depuis des IP de confiances, et uniquement par SSH. Shorewall sera utilisé pour définir les règles de par feu.
yum install shorewall
Shorewall utilise la notion de zones pour établir des règles de filtrage, en général, une zone correspond à une réseau accessible depuis une interface donnée. Dans notre configuration, chaque hyperviseur à 3 zones sur lesquels un filtrage doit/peut être appliqué:
Contenu du fichier /etc/shorewall/interfaces:
# # Shorewall version 4 - Interfaces File # # For information about entries in this file, type "man shorewall-interfaces" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-interfaces.html # ############################################################################### FORMAT 2 ############################################################################### #ZONE INTERFACE OPTIONS wan eth0 rpn eth1 pwan brwan bridge
Il faut maintenant définir le type de chaque zone. Dans notre cas assez simple, nous ne voulons que du trafic IPv4, nous allons donc éditer le fichier /etc/shorewall/zones:
# # Shorewall version 4 - Zones File # # For information about this file, type "man shorewall-zones" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-zones.html # ############################################################################### #ZONE TYPE OPTIONS IN OUT # OPTIONS OPTIONS fw firewall wan ipv4 rpn ipv4 pwan ipv4
Le fichier /etc/shorewall/policy permet de définir l'action du par feu si aucune règle définie ne correspond au paquet:
# # Shorewall version 4 - Policy File # # For information about entries in this file, type "man shorewall-policy" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-policy.html # ############################################################################### #SOURCE DEST POLICY LOG LIMIT: CONNLIMIT: # LEVEL BURST MASK $FW rpn ACCEPT rpn $FW ACCEPT wan pwan ACCEPT pwan wan ACCEPT all all DROP info
Ici:
Le fichier /etc/shorewall/params permet de définir des variables, qui peuvent ensuite être utilisées dans les règles de filtrage. Ça facilite la maintenance des règles. Dans ce fichier, on peut y placer les IP de confiances qui seront les seules à pouvoir se connecter en SSH aux hyperviseurs par exemple
# # Shorewall version 4 - Params File # # /etc/shorewall/params # # Assign any variables that you need here. # # It is suggested that variable names begin with an upper case letter # to distinguish them from variables used internally within the # Shorewall programs # # Example: # # NET_IF=eth0 # NET_BCAST=130.252.100.255 # NET_OPTIONS=routefilter,norfc1918 # # Example (/etc/shorewall/interfaces record): # # net $NET_IF $NET_BCAST $NET_OPTIONS # # The result will be the same as if the record had been written # # net eth0 130.252.100.255 routefilter,norfc1918 # ############################################################################### IP_BDX=11.12.13.14 IP_MRS=74.5.236.77,198.20.21.22 IP_RBX=57.26.2945 IP_TRUST=$IP_BDX,$IP_MRS,$IP_RBX IP_ZBX=62.33.89.127 #LAST LINE -- DO NOT REMOVE
Le fichier le plus important est probablement /etc/shorewall/rules. C'est dans ce fichier qu'on va définir nos règles spécifiques de filtrage. Il va falloir adapter ce fichier à votre environnement, voici un exemple:
# # Shorewall version 4 - Rules File # # For information on the settings in this file, type "man shorewall-rules" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-rules.html # ###################################################################################################################################################################################### #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH # PORT PORT(S) DEST LIMIT GROUP #SECTION ALL #SECTION ESTABLISHED #SECTION RELATED SECTION NEW # OUTPUT ACCEPT $FW wan UDP 53 ACCEPT $FW wan UDP 123 ACCEPT $FW wan TCP 80,443,20,21 ACCEPT $FW all ICMP 8 # INPUT ACCEPT any $FW ICMP 8 ACCEPT wan:$IP_TRUST $FW TCP 22 ACCEPT wan:$IP_ZBX $FW TCP 10050
Dans cet exemple:
Avec une installation par défaut, shorewall refusera de démarrer, il faut éditer le fichier /etc/shorewall/shorewall.conf et remplacer STARTUP_ENABLED=No par STARTUP_ENABLED=Yes
sed -i -e "s/STARTUP_ENABLED=.*/STARTUP_ENABLED=Yes/g" \ /etc/shorewall/shorewall.conf
Attention, roulement de tambours, on va maintenant appliquer nos règles de filtrage.
sleep 300 && reboot
puis activez le par feu depuis une autre fenêtre. Si tout est OK, vous pouvez annuler la commande de redémarrage, mais si vous vous êtes coupé l'accès, le serveur redémarrera au bout de 5 minutes (et les règles de par feu ne s'appliqueront pas automatiquement puisqu'on a pas encore activé le démarrage de shorewall)
/etc/init.d/shorewall start
Si tout se passe bien, shorewall ne devrait loguer aucune erreur, et commencer son filtrage. Vous pouvez suivre les paquets bloqués dans /var/log/messages.
Vous pouvez maintenant faire exactement la même chose sur le deuxième hyperviseur.
Une fois que vous vous êtes assuré que les règles de filtrages sont fonctionnelles, il ne reste plus qu'à activer shorewall au démarrage:
chkconfig shorewall on
On y est presque, il ne manque plus qu'à définir les règles de proxy ARP. Ce n'est pas du par feu à proprement parler, mais Shorewall permet de simplifier cette mise en place (activation du mode proxy sur l'interface brwan, ajout des règles de routage etc…) alors pourquoi s'en priver
Les hyperviseurs ne feront pas de routage du trafic, excepté entre les réseaux wan et pwan. L'interface physique des hyperviseurs (eth0) recevra les trames à destinations des IP publiques des VM (les IP failovers), et les transmettra sur le réseau pwan (via le pont brwan) grâce à une configuration en proxy ARP. Cette technique présente plusieurs avantages par rapport à du bridging simple:
Dans le fichier /etc/shorewall/proxyarp, nous allons ajouter une ligne par IP Failover qui sera attribuée à une machine virtuelle:
# # Shorewall version 4 - Proxyarp File # # For information about entries in this file, type "man shorewall-proxyarp" # # See http://shorewall.net/ProxyARP.htm for additional information. # ############################################################################### #ADDRESS INTERFACE EXTERNAL HAVEROUTE PERSISTENT 88.190.23.99 brwan eth0 No Yes 88.191.89.16 brwan eth0 No Yes
Dans cet exemple, 88.190.23.99 et 88.191.89.16 sont deux IP failovers redirigées vers un des deux hyperviseurs (et la beauté de tout ce système, c'est qu'on s'en fou sur lequel des deux l'IP est redirigée).
Ajoutez sur chaque hyperviseurs uniquement les adresses IP Failovers qui lui sont redirigées dessus.
On vient de voir comment mettre en place les règles de proxy ARP, mais ça serait quand même plus classe si nos deux hyperviseurs pouvaient mettre à jour automatiquement ces règles: ainsi plus besoin de toucher aux hyperviseurs, on ajoute une IP failover sur un des deux, et il modifiera sa configuration automatiquement en conséquence. Non seulement ça serait plus classe, mais en plus, c'est possible
En cas de changement d'une IP failover d'un hyperviseur vers l'autre, il fat re-configurer les deux interfaces eth0 et brwan. Plutôt que de relancer toute la stack réseau (qui couperait l'accès aux volumes gluster), on va créer un script qui ne re-configure que les deux interfaces nécessaires:
cp -a /etc/init.d/network /etc/init.d/wan cat <<'EOF' > wan.patch --- /etc/init.d/network 2013-01-09 12:13:29.000000000 +0100 +++ /etc/init.d/wan 2013-07-21 16:13:06.526176211 +0200 @@ -40,12 +40,7 @@ # find all the interfaces besides loopback. # ignore aliases, alternative configurations, and editor backup files -interfaces=$(ls ifcfg* | \ - LANG=C sed -e "$__sed_discard_ignored_files" \ - -e '/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d' \ - -e '/ifcfg-[A-Za-z0-9#\._-]\+$/ { s/^ifcfg-//g;s/[0-9]/ &/}' | \ - LANG=C sort -k 1,1 -k 2n | \ - LANG=C sed 's/ //') +interfaces="brwan eth0" rc=0 # See how we were called. @@ -60,9 +55,6 @@ apply_sysctl - # bring up loopback interface - action $"Bringing up loopback interface: " ./ifup ifcfg-lo - case "$VLAN" in yes) if [ ! -d /proc/net/vlan ] && ! modprobe 8021q >/dev/null 2>&1 ; then @@ -213,10 +205,6 @@ ) done - action $"Shutting down loopback interface: " ./ifdown ifcfg-lo - - sysctl -w net.ipv4.ip_forward=0 > /dev/null 2>&1 - # IPv6 hook (post IPv4 stop) if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then /etc/sysconfig/network-scripts/init.ipv6-global stop post EOF patch /etc/init.d/wan wan.patch
On va créer un petit script perl sur chaque hyperviseur. Ce script demandera à intervalles régulières quelles IP Failovers lui sont redirigées dessus, et il pourra ainsi mettre à jour la configuration de shorewall.
Placez ce script dans /usr/local/bin/failover_updater.pl
#!/usr/bin/perl -w use strict; use LWP; use JSON; use Getopt::Long; use File::Compare; use File::Copy; my ($key, $id) = ''; GetOptions( "key=s" => \$key, "id=s" => \$id ); if ($key eq '' || $id eq ''){ print "Usage: $0 --key=<online api key> --id=<server id>\n"; exit(1); } # Used to hide api key $0 = 'failover updater'; my $c = LWP::UserAgent->new; my $r = $c->get("https://api.online.net/api/v1/server/info/$id", "Authorization" => "Authorization: Bearer $key", ); unless ($r->is_success){ print "an error occured while querying the API" . "The error is: " . $r->status_line; exit(2); } my $data = from_json($r->content); open TMP, ">/tmp/proxyarp" || die "Cannot open /tmp/proxyarp"; # Print the file header print TMP <<"EOF"; ############################################################################### ##ADDRESS\t\tINTERFACE\tEXTERNAL\tHAVEROUTE\tPERSISTENT EOF # Now add one arp proxy rule per failover IP # redirected to ourself foreach my $ip ( @{$data->{'network'}->{'ipfo'}} ){ print TMP "$ip\t\tbrwan\t\teth0\t\tNo\t\tYes\n"; } close TMP; if (compare("/etc/shorewall/proxyarp", "/tmp/proxyarp") != 0){ # Looks like IP Failover changed, we need to update shorewall # # config, reload it and flush the ARP cache print "Updating ARP Proxy config and flushing cache...\n"; move ("/tmp/proxyarp", "/etc/shorewall/proxyarp"); system("/sbin/restorecon -R /etc/shorewall >/dev/null 2>&1"); # Wait a few second for the new routing rules to take place # It will result in more down time if we update too early sleep(90); system("/etc/init.d/wan restart >/dev/null 2>&1"); system("/etc/init.d/shorewall reload >/dev/null 2>&1"); } else{ unlink "/tmp/proxyarp"; }
Donnez les droits d'exécution:
chmod +x /usr/local/bin/failover_updater.pl
Installez les dépendances perl:
yum install perl-JSON perl-libwww-perl
Ajoutez une tâche cron:
cat <<'EOF' > /etc/cron.d/failover_updater * * * * * root /usr/local/bin/failover_updater.pl --key=__API_KEY__ --id=__ONLINE_ID__ EOF chmod 640 /etc/cron.d/failover_updater sed -i -e "s/__API_KEY__/$(cat /etc/install/api_key)/g" \ -e "s/__ONLINE_ID__/$(cat /etc/install/online_sdx)/g" \ /etc/cron.d/failover_updater
Et voilà, tout est prêt. Toutes les minutes, les hyperviseurs vont demander quelles IP Failover leur sont attribuées, et re-configurer ce qui est nécessaire. En cas de bascule d'une IP Failover d'un hyperviseur vers l'autre (pour maintenance par exemple), il faut compter entre 1 et 2 minutes de coupure le temps que le nouveau routage se mette en place, que les hyperviseur récupèrent la nouvelle configuration et re-configure le nécessaire.
Pour finir, on va mettre un petit script sur les deux hyperviseurs. Il va permettre de migrer automatiquement les VM sur le deuxième hyperviseur pendant l'arrêt du serveur. Si le deuxième hyperviseur n'est pas joignable, alors on va simplement sauvegarder l'état des VM (virsh managedsave)
#!/usr/bin/perl -w use strict; use Sys::Virt; my ($local,$remote) = undef; eval { $local = Sys::Virt->new( uri => 'qemu:///system' ); }; if ($@){ # No connection to the local libvirt # We can't do anything die 'Error connecting to libvirt on URI: qemu:///system: '. $@; } eval { $remote = Sys::Virt->new( uri => 'qemu+ssh://fws@peer/system' ); }; if ($@){ # No connection to the remote server # Let's try to managed save all running domain foreach my $dom ($local->list_all_domains()){ next unless $dom->is_active; $dom->managed_save(); } } else{ # We have a connection to both the local and # the remote libvirt. Lets try to migrate # all our running domain to our friend foreach my $dom ($local->list_all_domains()){ next unless $dom->is_active; $dom->migrate($remote, Sys::Virt::Domain::MIGRATE_LIVE | Sys::Virt::Domain::MIGRATE_PEER2PEER | Sys::Virt::Domain::MIGRATE_TUNNELLED); } }
chmod +x /usr/local/bin/migrate_all.pl
cat <<'EOF' > /etc/init.d/rc.shutdown #! /bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin case "$1" in stop) if [ -x /etc/rc.shutdown ]; then logger "Running local shutdown scripts (/etc/rc.shutdown)" /etc/rc.shutdown fi ;; start) # nothing to do ;; *) echo "Usage: $0 start|stop" >&2 exit 3 ;; esac EOF chmod +x /etc/init.d/rc.shutdown ln -sf /etc/rc.d/init.d/rc.shutdown /etc/rc0.d/K00rc.shutdown ln -sf /etc/rc.d/init.d/rc.shutdown /etc/rc6.d/K00rc.shutdown
cat <<'EOF' > /etc/rc.shutdown #!/bin/sh /usr/local/bin/migrate_all.pl EOF chmod +x /etc/rc.shutdown
chkconfig --del libvirt-guests
On peut maintenant s'attaquer à la création d'un invité sur notre nouveau cluster tout neuf. La création est assez simple, pensez simplement à utiliser le pool iso pour stocker les ISO d'installation, et un autre pool pour les VM (pas obligatoire, mais plus propre)
Pour donner une IP publique à un invité, et donc faire en sorte qu'il soit directement connecté à Internet (sans aucun NAT ou filtrage), il faut:
ip route add 88.190.47.1 dev eth1
En cas de crash important, nécessitant une ré-installation complète d'un des deux hyperviseurs, la plupart de ce tuto peut être suivit sans actions particulière, sauf pour la partie GlusterFS. Voilà les étapes à suivre pour re-synchroniser les 3 volumes:
UUID=34de250b-fba2-462f-8f68-e19cdee31111 operating-version=2
gluster peer probe hyp2
gluster volume sync hyp1 all
rsync -dvPt --xattrs /mnt/bricks/ root@hyp2:/mnt/bricks/
find /var/lib/libvirt/