Devbox KVM+Libvirt perfect setup.
Dans le cadre de mon boulot chez Euro-Web j’ai été amené à monter une plate forme un peu particulière pour mon client Zenexity. Leur principale activité est le développement d’applications web basées sur une technologie maison open source ( framework Play! ).
J’ai donc demandé l’autorisation de faire une documentation publique sur cette installation, car je la trouve intéressante, et je n’ai rien trouvé de similaire pour le moment sur la toile.
Le setup part d’un gros besoin en machines virtuelles :
- Les développeurs ont besoin de tester leurs applications sous toutes les coutures et sont plus plutôt allergiques à certains systèmes d’exploitation, même sous forme de machines virtuelles.
- Certaines plates formes déjà en production ont besoin d’environnement de préproduction facilement ré-initialisables. Les développeurs ont besoin d’images/VM “template” afin de pouvoir réaliser leurs tests facilement.
- Les architectes ont besoin de valider des setups en mode pré-production sur des technologies émergentes
Préambule
- Le setup d’origine ayant été réalisé sous Debian 6.0 la documentation présenté tel qu’elle est ici, est bien sur basé sur la machine de production Debian 6.0. Il est à noter que j’ai réalisé très facilement un clone de cette machine sous gentoo, je vous donnerai en annexe les options de compilation des différents programmes utilisés.
- L’installation + la configuration du serveur OpenVPN ne fait pas l’objet de cet article, vous pouvez donc pour cela vous reporter aux autres articles de Geekfault traitant du VPN.
- En fait, réaliser ce setup, est chose plutôt aisé pour tout adminsys chevronné, (chose qui vous vous en doutez je suis :p), le plus compliqué a été de trouver des contournements pour les multitudes de soucis rencontrés.
Cahier des charges
Comme expliqué plus haut le système de VM est un système très utilisé chez mon client. J’avais dans le passé monté un serveur XEN pour eux, mais la flexibilité ne convenait plus, notamment pour les grid de test Windows.
Il fallait donc un nouveau serveur de VM. By design, les guest Windows ne seront pas accessible depuis l’internet, et n’auront donc pas d’IP publique. En outre, les développeurs devront pouvoir accéder de façon transparente à leur Windows de test, si possible de manière sécurisée.
Les guests GNU/Linux, quand à eux, pourront être accessibles via une IP publique, via VPN ou les deux à la fois.
La création / les duplications / la maintenance / le démarrage / l’arrêt des VM devra être très simple. (Le client souhaite garder la main sur cela, et être maitre de la situation en matière de création de VM).
Choix de la technologie
Nous avons finalement retenu l’idée d’un serveur étant capable de faire cela :
- N’importe qui doit pouvoir ré-amorcer un système clean : toutes les VM tourneront sur des fichiers .img stockés dans /var
- Les gestion des ressources réseau est automatisée au maximum : les VM devront obtenir leurs IP privées ou publiques automatiquement, par DHCP.
- La technologie de virtualisation sera Qemu/Kvm, épaulée par Libvirt.
- Certaines VM ne seront accessible que au travers d’un VPN, alors que d’autres devront pouvoir faire office de serveur de préproduction et devront avoir des IP publiques.
- Ce serveur devra donc être “hub” VPN et permettre la communication entre les clients du VPN, et les VM.
- Chaque VM qui ne sera que dans le LAN VPN, devra quand même pouvoir accéder à l’internet. Nous pensons donc utiliser un bridge ethernet sur eth0 ainsi que sur l’interface VPN.
- Chaque VM devra donc être clonable et les developpeurs pourront s’y connecter à travers le VPN, à l’aide de ssh, ou VNC / rdesktop pour les systèmes d’exploitation de seconde zone.
- Ce serveur devra pouvoir faire tourner des VM : Gentoo / Debian 5.0 / Debian 6.0 / CentOS / Windows (avec différentes version d’Internet Explorer, nerf de la guerre des tests d’applications web sous plate-forme windows).
Prérequis Kernel
Nous avons finalement utilisé le noyau 2.6.32.27. En effet les noyaux 2.6.36 présentent un bug dans le support KVM qui fait freezer la machine à la coupure des VM. (Nous sommes à priori victimes d’une régression dans le kernel Linux).
http://comments.gmane.org/gmane.comp.emulators.kvm.devel/65852
Nous avons donc upgradé sur le 2.6.37 mais, nous nous sommes heurtés à un nouveau bug kernel.
Bug kernel avec le 2.6.37
[76529.274129] rmap_remove: ffff88017deb37f8 1->BUG
[76529.274162] ------------[ cut here ]------------
[76529.274189] kernel BUG at arch/x86/kvm/mmu.c:700!
[76529.274217] invalid opcode: 0000 [#1] SMP
[76529.274247] last sysfs file: /sys/devices/virtual/net/lo/operstate
[76529.274276] CPU 2
[76529.274302] Pid: 27193, comm: kvm Not tainted 2.6.37 #1 01V648/PowerEdge R410
[76529.274333] RIP: 0010:[] [] drop_spte+0xb8/0x179
[76529.274389] RSP: 0018:ffff88080d987b28 EFLAGS: 00010296
[76529.274417] RAX: 000000000000003b RBX: ffff88017deb37f8 RCX: ffff8800000bc380
[76529.274449] RDX: 000000000000c3c3 RSI: 0000000000000046 RDI: ffffffff81918a88
[76529.274480] RBP: ffff88080d987b38 R08: 0000000000000000 R09: 000000000000000a
[76529.274512] R10: ffff88102f819400 R11: ffff88102f819400 R12: ffff880fe7cb0000
[76529.274544] R13: ffff88080d987b98 R14: 0000000000000000 R15: ffff88017deb37f8
[76529.274576] FS: 0000000000000000(0000) GS:ffff88102fc20000(0000) knlGS:0000000000000000
[76529.274623] CS: 0010 DS: 002b ES: 002b CR0: 000000008005003b
[76529.274652] CR2: 00007fb91807f000 CR3: 000000100aa0d000 CR4: 00000000000026e0
[76529.274684] DR0: 0000000000000090 DR1: 00000000000000a4 DR2: 00000000000000ff
[76529.274715] DR3: 000000000000000f DR6: 00000000ffff0ff0 DR7: 0000000000000400
[76529.274747] Process kvm (pid: 27193, threadinfo ffff88080d986000, task ffff8805a1d82630)
[76529.274794] Stack:
[76529.274815] ffff88080da10090 ffff880fe7cb0000 ffff88080d987b88 ffffffff8100ef8f
[76529.274870] ffff8805a1d82630 000000ff8109702a ffff88080d987c08 ffff880fe7cb0000
[76529.274925] ffff88080da10950 ffff88080d987b98 ffff880fe7cb2320 0000000000020d93
[76529.274981] Call Trace:
[76529.275006] [] kvm_mmu_prepare_zap_page+0x74/0x23c
[76529.275038] [] kvm_mmu_zap_all+0x41/0x6b
[76529.275069] [] kvm_arch_flush_shadow+0x11/0x1e
[76529.275101] [] kvm_mmu_notifier_release+0x2c/0x3f
[76529.275134] [] __mmu_notifier_release+0x49/0x74
[76529.275166] [] exit_mmap+0x27/0x147
[76529.275198] [] mmput+0x28/0xb4
[76529.275226] [] exit_mm+0x124/0x131
[76529.275254] [] do_exit+0x20f/0x6a7
[76529.275283] [] do_group_exit+0x71/0x99
[76529.275313] [] get_signal_to_deliver+0x2fa/0x315
[76529.275346] [] do_signal+0x6d/0x673
[76529.275375] [] ? kill_pid_info+0x3a/0x47
[76529.275404] [] ? sys_kill+0x82/0x161
[76529.275433] [] do_notify_resume+0x27/0x51
[76529.275464] [] int_signal+0x12/0x17
[76529.275491] Code: 48 d9 71 81 31 c0 e8 2b 7f 5c 00 0f 0b eb fe 40 f6 c6 01 75 26 48 39 f3 74 15 48 89 de 48 c7 c7 63 d9 71 81 31 c0 e8 0b 7f 5c 00 <0f> 0b eb fe 48 c7 00 00 00 00 00 e9 ac 00 00 00 48 83 e6 fe 31
[76529.275720] RIP [] drop_spte+0xb8/0x179
[76529.275752] RSP
[76529.276109] ---[ end trace 2d814c5436296e34 ]---
[76529.276177] Fixing recursive fault but reboot is needed!
Je vous renvoie à la mailing list de kernel.org à ce sujet. Ça ressemble beaucoup, le gars a le même problème sur un 2.6.36. Le mainteneur des kernel 2.4.x a posté un patch pour 2.6.36 sur le thread.
Voici ce qu’il faut activer dans le noyau pour que notre setup fonctionne :
- Networking support
-- Networking options
--- 802.1d Ethernet Bridging
--- Network Packet Filtering framework (netfilter)
---- Core Netfilter configuration (les options dont vous aurez besoin pour vos propres régles de firewalling)
---- IP: Netfilter Configuration
----- IP tables support
----- IPv4 Connection tracking support (required for NAT)
----- Full NAT
----- MASQUERADE target support
(à compléter, j’ai certainement du oublier un truc).
Installation des composants indispensables
Voici la liste des composants indispensables à installer avec votre package manager (apt-get sous Debian; emerge sous Gentoo) :
- libvirt-bin
- libvirt0
- virt-top
- virtinst
- isc-dhcp-server
- openvpn
- iproute
- iptables
- ifupdown
- kvm
- build-essential + libncurses5-dev
- qemu-kvm
- rsync
- munin-libvirt-plugins
Use flags sous Gentoo
/etc/portage/package.use
app-emulation/libvirt libvirtd qemu lvm network nfs virt-network
Configuration du bridge (debian ways)
/etc/network/interfaces
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet static
address 91.x.x.x
netmask 255.255.255.0
network 91.x.x.0
broadcast 91.x.x.255
gateway 91.x.x.1
auto br0
iface br0 inet static
address 91.x.x.x
netmask 255.255.255.0
network 91.x.x.0
broadcast 91.x.x.255
gateway 91.x.x.1
bridge_ports eth0
bridge_stp off
bridge_maxwait 5
auto br1
iface br1 inet static
address 172.16.20.1
netmask 255.255.255.0
network 172.16.20.0
broadcast 172.16.20.255
bridge_ports tap0
bridge_stp off
bridge_maxwait 5
pre-up iptables-restore -c /etc/network/iptables
auto eth1
iface eth1 inet static
address 10.191.78.4
netmask 255.255.0.0
network 10.191.0.0
broadcast 10.191.0.255
libvirt setup
/etc/default/libvirt-bin
start_libvirtd="yes"
libvirtd_opts="-d"
Creation du fichier de l’image :# qemu-img create /var/VM/gentoo-x86_64.img 10G
/etc/sysctl.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 0
net.ipv4.conf.all.bootp_relay = 1
Attention à l’isolation reseau des VM : https://bugzilla.redhat.com/show_bug.cgi?id=512206
Le DHCP ne doit pas envoyer de requêtes sur le reseau :# iptables -t filter -A FORWARD -p udp --sport 68 --dport 67 -j DROP
Sécuriser le VNC de qemu/kvm en le forçant sur l’interface sécurisée
/etc/libvirt/qemu.conf
vnc_listen '172.16.20.1'
Problème de keymap bug libvirt
http://www.arnebrodowski.de/blog/keymap-problems-with-virt-manager.html
Iptables avec le bridge et la libvirt
http://ddevnet.net/wiki/index.php/How_KVM_or_libVirt_IPtables_work
Mise en place du dhcpd
/etc/dhcp/dhcpd.conf
ddns-update-style none;
option domain-name "zenexity.fr";
option domain-name-servers 81.93.xxx.xxx, 81.93.xx.xx;
default-lease-time 3600;
max-lease-time 7200;
log-facility local7;
subnet 91.xxx.xx.0 netmask 255.255.255.0 {
range 91.xxx.xx.20 91.xxx.xx.50;
option routers 91.xxx.xx.1;
}
subnet 172.16.20.0 netmask 255.255.255.0 {
range 172.16.20.128 172.16.20.254;
option routers 172.16.20.1
}
Exemple de création de VM
- Une machine virtuelle gentoo n’ayant qu’une seule interface réseau sur le VPN only
# virt-install -n gentoo -r 512 -vcpus=1 -f /var/VM/gentoo-x86_64.img -b br1 --vnc --accelerate -v -c /var/iso/gentoo.iso --os-type=linux
- Une machine virtuelle debian, ayant une interface reseau publique et une interface VPN
# virt-install -n debian -r 512 -vcpus=1 -f /var/VM/debian-x86_64.img -b br0 -b br1 --vnc --accelerate -v -c /var/iso/debian-5.iso --os-type=linux
libvirt stocke ses fichiers de description de VM dans /etc/libvirt/qemu. Voici un exemple de fichier xml généré par libvirt :/etc/libvirt/qemu/debian-50.xml
Attention, si vous changez à la main un fichier domain.xml vous devez lancer la commande ;# virsh define domain.xml
Internet pour les VM n’ayant été bind que sur le bridge du VPN
# iptables -t filter -A FORWARD -i br1 -j MARK --set-mark 1
# iptables -t nat -A POSTROUTING -m mark ! --mark 1 -o br0 -j MASQUERADE
Ou, moins fin :# iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE
Commandes libvirt qui vous serviront avec KVM
- virsh start domain : Start le domaine spécifié
- virsh shutdown domain : Shutdown le domaine spécifié
- virsh reboot domain : just reboot a domain
- virsh destroy domain : Terminate a domain
- virsh autostart domain : Autostart a domain
- virsh list : List domains
Ordre de boot des services
Cette partie est la pierre angulaire de ce setup :
- Il faut que dhcpd soit demarré après openvpn ! en effet sinon vous ne serai pas capable de diffuser des adresses ip sur votre interface br1 (bridgé avec l’interface tap0 du vpn) si le dhcpd ne demarre pas après openvpn.
- Il faut que la libvirt soit demarré après dhcpd, sinon vous ne serez pas capable de donner des ip à vos VM si celles-ci sont en auto start.
/etc/init.d/.depend.start
TARGETS = rsyslog qemu-kvm killprocs openvpn apache2 isc-dhcp-server cron acpid ssh rsync dbus exim4 libvirt-bin bootlogs munin-node single rc.local stop-bootlogd rmnologin
INTERACTIVE = openvpn apache2
openvpn: rsyslog
apache2: rsyslog
isc-dhcp-server: rsyslog
cron: rsyslog
acpid: rsyslog
ssh: rsyslog
rsync: rsyslog
dbus: rsyslog
exim4: rsyslog
libvirt-bin: rsyslog openvpn
munin-node: rsyslog isc-dhcp-server qemu-kvm openvpn libvirt-bin apache2 bootlogs cron acpid ssh rsync dbus exim4
single: killprocs bootlogs
rc.local: rsyslog isc-dhcp-server qemu-kvm openvpn libvirt-bin apache2 bootlogs cron acpid ssh rsync dbus exim4
stop-bootlogd: rsyslog isc-dhcp-server qemu-kvm openvpn libvirt-bin apache2 bootlogs cron acpid ssh rsync dbus exim4
rmnologin: rsyslog isc-dhcp-server qemu-kvm openvpn libvirt-bin apache2 bootlogs cron acpid ssh rsync dbus exim4 - /etc/network/interfaces sur Debian aura toujours tendance à se lancer AVANT la création de tap0 par le service openvpn, or votre interface br1 doit être bridge sur tap0, il est donc nécéssaire de lancer ce petit script (J’ai rajouté ce script au script d’init de openvpn, ainsi celui est lancé juste après que le vpn soit lancé, ainsi le bridge entre br1 et tap0 est fonctionnel lorsque la libvirt se lancera) :
/etc/zenexity/fixebr1
sleep 10
ifdown br1 && echo 'bridge down' >> /var/tmp/bridge
sleep 1
ifup br1 && echo 'bridge up' >> /var/tmp/bridge
sleep 1
/etc/init.d/isc-dhcp-server restart
Plugin munin pour libvirt + KVM
Je vous renvoie au site du plugin LibVirt pour Munin.