guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

branch master updated: website: Add draft of a Ganeti cluster post.


From: Marius Bakke
Subject: branch master updated: website: Add draft of a Ganeti cluster post.
Date: Thu, 16 Jul 2020 16:28:08 -0400

This is an automated email from the git hooks/post-receive script.

mbakke pushed a commit to branch master
in repository guix-artwork.

The following commit(s) were added to refs/heads/master by this push:
     new 6a477cd  website: Add draft of a Ganeti cluster post.
6a477cd is described below

commit 6a477cd9529560df25a21a67433be5b2bf6a7470
Author: Marius Bakke <mbakke@fastmail.com>
AuthorDate: Wed Jul 8 11:48:17 2020 +0200

    website: Add draft of a Ganeti cluster post.
    
    * website/drafts/ganeti-on-guix.md: New file.
---
 website/drafts/ganeti-on-guix.md | 469 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 469 insertions(+)

diff --git a/website/drafts/ganeti-on-guix.md b/website/drafts/ganeti-on-guix.md
new file mode 100644
index 0000000..89f175d
--- /dev/null
+++ b/website/drafts/ganeti-on-guix.md
@@ -0,0 +1,469 @@
+title: Running a Ganeti cluster on Guix
+date: 2020-07-17 12:00
+author: Marius Bakke
+tags: Virtualization, Ganeti
+---
+The latest addition to Guix's ever-growing list of services is a little-known
+virtualization toolkit called [Ganeti](http://www.ganeti.org/).  Ganeti is
+designed to keep virtual machines running on a cluster of servers even in the
+event of hardware failures, and to make maintenance and recovery tasks easy.
+
+It is comparable to tools such as
+[Proxmox](https://www.proxmox.com/en/proxmox-ve) or
+[oVirt](https://www.ovirt.org/), but has some distinctive features.  One is
+that there is no GUI: [third](https://github.com/osuosl/ganeti_webmgr)
+[party](https://github.com/grnet/ganetimgr)
+[ones](https://github.com/sipgate/ganeti-control-center) exist, but are not
+currently packaged in Guix, so you are left with a rich command-line client
+and a fully featured
+[remote API](http://docs.ganeti.org/ganeti/master/html/rapi.html).
+
+Another interesting feature is that installing Ganeti on its own leaves you
+no way to actually deploy any virtual machines.  That probably sounds crazy,
+but stems from the fact that Ganeti is designed to be API-driven and automated,
+thus it comes with a
+[OS 
API](http://docs.ganeti.org/ganeti/master/html/man-ganeti-os-interface.html)
+and users need to install one or more *OS providers* in addition to Ganeti.
+OS providers offer a declarative way to deploy virtual machine variants and
+should feel natural to Guix users.  At the time of writing, the providers
+available in Guix are 
[debootstrap](https://github.com/ganeti/instance-debootstrap)
+for provisioning Debian- and Ubuntu-based VMs, and of course a
+[Guix](https://github.com/mbakke/ganeti-instance-guix) provider.
+
+Finally Ganeti comes with a sophisticated _instance allocation framework_ that
+efficiently packs virtual machines across a cluster while maintaining N+1
+redundancy in case of a failover scenario.  It can also make informed 
scheduling
+decisions based on various cluster tags, such as ensuring primary and secondary
+nodes are on different power distribution lines.
+
+(Note: if you are looking for a way to run just a few virtual machines on
+your local computer, you are probably better off using
+[libvirt](https://guix.gnu.org/manual/en/guix.html#index-libvirt) or even a
+[Childhurd](https://guix.gnu.org/manual/devel/en/guix.html#index-hurd_002dvm_002dservice_002dtype),
+as Ganeti is fairly heavyweight and requires a complicated networking setup.)
+
+
+# Preparing the configuration
+
+With introductions out of the way, let's see how we can deploy a Ganeti
+cluster using Guix.  For this tutorial we will create a two-node cluster
+and connect instances to the local network using an
+[Open vSwitch](https://www.openvswitch.org/) bridge with no VLANs.  We assume
+that each node has a single network interface named `eth0` connected to the
+same network, and that a dedicated partition `/dev/sdz3` is available for
+virtual machine storage.  It is possible to store VMs on a number of other
+storage backends, but a dedicated drive (or rather LVM volume group) is
+necessary to use the [DRBD](https://www.linbit.com/drbd/) integration to
+replicate VM disks.
+
+We'll start off by defining a few helper services to create the Open vSwitch
+bridge and ensure the physical network interface is in the "up" state.  Since
+Open vSwich stores the configuration in a database, you might as well run the
+equivalent `ovs-vsctl` commands on the host once and be done with it, but we
+do it through the configuration system to ensure we don't forget it in the
+future when adding or reinstalling nodes.
+
+```
+(use-modules (gnu)
+             (gnu packages linux)
+             (gnu packages networking)
+             (gnu services shepherd))
+
+(define (start-interface if)
+  #~(let ((ip #$(file-append iproute "/sbin/ip")))
+      (invoke/quiet ip "link" "set" #$if "up")))
+
+(define (stop-interface if)
+  #~(let ((ip #$(file-append iproute "/sbin/ip")))
+      (invoke/quiet ip "link" "set" #$if "down")))
+
+;; This service is necessary to ensure eth0 is in the "up" state on boot
+;; since it is otherwise unmanaged from Guix PoV.
+(define (ifup-service if)
+  (let ((name (string-append "ifup-" if)))
+    (simple-service name shepherd-root-service-type
+                    (list (shepherd-service
+                           (provision (list (string->symbol name)))
+                           (start #~(lambda ()
+                                      #$(start-interface if)))
+                           (stop #~(lambda (_)
+                                     #$(stop-interface if)))
+                           (respawn? #f))))))
+
+;; Note: Remove vlan_mode to use tagged VLANs.
+(define (create-openvswitch-bridge bridge uplink)
+  #~(let ((ovs-vsctl (lambda (cmd)
+                       (apply invoke/quiet
+                              #$(file-append openvswitch "/bin/ovs-vsctl")
+                              (string-tokenize cmd)))))
+      (and (ovs-vsctl (string-append "--may-exist add-br " #$bridge))
+           (ovs-vsctl (string-append "--may-exist add-port " #$bridge " "
+                                     #$uplink
+                                     " vlan_mode=native-untagged")))))
+
+(define (create-openvswitch-internal-port bridge port)
+  #~(invoke/quiet #$(file-append openvswitch "/bin/ovs-vsctl")
+                  "--may-exist" "add-port" #$bridge #$port
+                  "vlan_mode=native-untagged"
+                  "--" "set" "Interface" #$port "type=internal"))
+
+(define %openvswitch-configuration-service
+  (simple-service 'openvswitch-configuration shepherd-root-service-type
+                  (list (shepherd-service
+                         (provision '(openvswitch-configuration))
+                         (requirement '(vswitchd))
+                         (start #~(lambda ()
+                                    #$(create-openvswitch-bridge
+                                       "br0" "eth0")
+                                    #$(create-openvswitch-internal-port
+                                       "br0" "gnt0")))
+                         (respawn? #f)))))
+```
+
+This defines a `openvswitch-configuration` service object that creates a
+logical switch `br0`, connects `eth0` as the "uplink", and creates a logical
+port `gnt0` that we will use later as the main network interface for this
+system.  We also create an `ifup` service that can bring network interfaces
+up and down.  By themselves these variables do nothing, we also have to add
+them to our `operating-system` configuration below.
+
+Such a configuration might be suitable for a small home network.  In most
+"real world" deployments you would use tagged VLANs, and maybe a traditional
+Linux bridge instead of Open vSwitch.  You can also forego bridging altogether
+with a `routed` networking setup, or do any combination of the three.
+
+With this in place, we can start creating the `operating-system` configuration
+that we will use for the Ganeti servers:
+
+```
+[continued from the above configuration snippet]
+
+(use-service-modules base ganeti linux networking ssh)
+
+(operating-system
+  (host-name "node1")
+  [...]
+  ;; Ganeti requires that each node and the cluster name resolves to an
+  ;; IP address.  The easiest way to achieve this is by adding everything
+  ;; to the hosts file.
+  (hosts-file (plain-file "hosts" "
+127.0.0.1       localhost
+::1             localhost
+
+192.168.1.200   ganeti.lan
+192.168.1.201   node1
+192.168.1.202   node2
+"))
+  (kernel-arguments
+   (append %default-kernel-arguments
+           '(;; Disable DRBDs usermode helper, as Ganeti is the only entity
+             ;; that should manage DRBD.
+             "drbd.usermode_helper=/run/current-system/profile/bin/true")))
+
+  (packages (append (map specification->package
+                         '("qemu" "drbd-utils" "lvm2"
+                           "ganeti-instance-guix"
+                           "ganeti-instance-debootstrap"))
+                    %base-packages))
+
+  (services (cons* (service ganeti-service-type
+                            (ganeti-configuration
+                             (file-storage-paths '("/srv/ganeti/file-storage"))
+                             (os
+                              (list (guix-os %default-guix-variants)
+                                    (debootstrap-os
+                                     (list (debootstrap-variant
+                                            "buster"
+                                            (debootstrap-configuration
+                                             (suite "buster")))
+                                           (debootstrap-variant
+                                            "testing+contrib+paravirtualized"
+                                            (debootstrap-configuration
+                                             (suite "testing")
+                                             (hooks
+                                              (local-file
+                                               "paravirt-hooks"
+                                               #:recursive? #t))
+                                             (extra-pkgs
+                                              (delete "linux-image-amd64"
+                                                      
%default-debootstrap-extra-pkgs))
+                                             (components '("main" 
"contrib"))))))))))
+
+                   ;; Ensure the DRBD kernel module is loaded.
+                   (service kernel-module-loader-service-type
+                            '("drbd"))
+
+                   ;; Create a static IP on the "gnt0" Open vSwitch interface.
+                   (service openvswitch-service-type)
+                   %openvswitch-configuration-service
+                   (ifup-service "eth0")
+                   (static-networking-service "gnt0" "192.168.1.201"
+                                              #:netmask "255.255.255.0"
+                                              #:gateway "192.168.1.1"
+                                              #:requirement 
'(openvswitch-configuration)
+                                              #:name-servers '("192.168.1.1"))
+
+                   (service openssh-service-type
+                            (openssh-configuration
+                             (permit-root-login 'without-password)))
+                   %base-services)))
+```
+
+Here we declare two OS "variants" for the debootstrap provider.  Debootstrap
+variants rely on a set of scripts (known as "hooks") in the installation 
process
+to do things like configure networking, install bootloader, create users, etc.
+In the example above, the "buster" variant uses the default hooks provided by
+Guix which configures network and GRUB, whereas the 
"testing+contrib+paravirtualized"
+variant uses a local directory next to the configuration file named 
"paravirt-hooks"
+(it is copied into the final system closure).
+
+We also declare a default `guix-os` variant provided by Guix's Ganeti service.
+
+Ganeti veterans may be surprised that each OS variant has its own hooks.  The
+Ganeti deployments I've seen use a single set of hooks for all variants,
+sometimes with additional logic inside the script based on the variant.  Guix
+offers a powerful abstraction that makes it trivial to create per-variant 
hooks,
+obsoleting the need for a big `/etc/ganeti/instance-debootstrap/hooks` 
directory.
+Of course you can still create it if you wish and set the `hooks` property of 
the
+variants to `#f`.
+
+Not all Ganeti options are exposed in the configuration system yet.  If you
+find it limiting, you can add custom files using `extra-special-file`, or
+ideally extend the `<ganeti-configuration>` data type to suite your needs.
+You can also use `gnt-cluster copyfile` and `gnt-cluster command` to distribute
+files or run executables, but undeclared changes in `/etc` may be lost on the
+next reboot or reconfigure.
+
+# Initializing a cluster
+
+At this stage, you should run `guix system reconfigure` with the new
+configuration on all nodes that will participate in the cluster.  If you
+do this over SSH or with
+[guix 
deploy](https://guix.gnu.org/blog/2019/managing-servers-with-gnu-guix-a-tutorial/),
+beware that `eth0` will lose network connectivity once it is "plugged in to"
+the virtual switch, and you need to add any IP configuration to `gnt0`.
+
+The Guix configuration system does not currently support declaring LVM
+volume groups, so we will create these manually on each node.  We could
+write our own declarative configuration like the 
`%openvswitch-configuration-service`,
+but for brevity and safety reasons we'll do it "by hand":
+
+```
+pvcreate /dev/sdz3
+vgcreate ganetivg /dev/sdz3
+```
+
+On the node that will act as the "master node", run the init command:
+
+**Warning**: this will create new SSH keypairs, both host keys and for the root
+user!  You can prevent that by adding `--no-ssh-init`, but then you will need
+to distribute `/var/lib/ganeti/known_hosts` to all hosts, and authorize the
+Ganeti key for the root user in `openssh-configuration`.  Here we let Ganeti
+manage the keys for simplicity.  As a bonus, we can automatically rotate the
+cluster keys in the future using `gnt-cluster renew-crypto --new-ssh-keys`.
+
+```
+gnt-cluster init \
+    --master-netdev=gnt0 \
+    --vg-name=ganetivg \
+    --enabled-disk-templates=file,plain,drbd \
+    --drbd-usermode-helper=/run/current-system/profile/bin/true \
+    --enabled-hypervisors=kvm \
+    --hypervisor-parameters=kvm:kvm_flag=enabled \
+    --nic-parameters=mode=openvswitch,link=br0 \
+    --no-etc-hosts \
+    ganeti.lan
+```
+
+`--no-etc-hosts` prevents Ganeti from automatically updating the `/etc/hosts`
+file when nodes are added or removed, which makes little sense on Guix because
+it is recreated every reboot/reconfigure.
+
+See the [gnt-cluster 
manual](http://docs.ganeti.org/ganeti/master/man/gnt-cluster.html)
+for information on the available options.  Most can be changed at runtime with
+`gnt-cluster modify`.
+
+If all goes well, the command returns no output and you should have the
+`ganeti.lan` IP address visible on `gnt0`.  You can run `gnt-cluster verify`
+to check that the cluster is in good shape.  Most likely it complains about
+something:
+
+```
+root@node1 ~# gnt-cluster verify
+Submitted jobs 3, 4
+Waiting for job 3 ...
+Thu Jul 16 18:26:34 2020 * Verifying cluster config
+Thu Jul 16 18:26:34 2020 * Verifying cluster certificate files
+Thu Jul 16 18:26:34 2020 * Verifying hypervisor parameters
+Thu Jul 16 18:26:34 2020 * Verifying all nodes belong to an existing group
+Waiting for job 4 ...
+Thu Jul 16 18:26:34 2020 * Verifying group 'default'
+Thu Jul 16 18:26:34 2020 * Gathering data (1 nodes)
+Thu Jul 16 18:26:34 2020 * Gathering information about nodes (1 nodes)
+Thu Jul 16 18:26:35 2020 * Gathering disk information (1 nodes)
+Thu Jul 16 18:26:35 2020 * Verifying configuration file consistency
+Thu Jul 16 18:26:35 2020 * Verifying node status
+Thu Jul 16 18:26:35 2020   - ERROR: node node1: hypervisor kvm parameter 
verify failure (source cluster): Parameter 'kernel_path' fails validation: not 
found or not a file (current value: '/boot/vmlinuz-3-kvmU')
+Thu Jul 16 18:26:35 2020 * Verifying instance status
+Thu Jul 16 18:26:35 2020 * Verifying orphan volumes
+Thu Jul 16 18:26:35 2020 * Verifying N+1 Memory redundancy
+Thu Jul 16 18:26:35 2020 * Other Notes
+Thu Jul 16 18:26:35 2020 * Hooks Results
+```
+
+When using the KVM hypervisor, Ganeti expects to find a dedicated kernel
+image for virtual machines in `/boot`.  For this tutorial we only use fully
+virtualized instances (meaning each VM runs its own kernel), so we can set
+`kernel_path` to an empty string to make the warning disappear:
+
+```
+gnt-cluster modify -H kvm:kernel_path=
+```
+
+Now let's add our other machine to the cluster:
+
+```
+gnt-node add node2
+```
+
+Ganeti will log into the node, copy the cluster configuration and start the
+relevant Shepherd services.  You may need to authorize node1's SSH key first.
+Run `gnt-cluster verify` again to check that everything is in order:
+
+```
+gnt-cluster verify
+```
+
+If you used `--no-ssh-init` earlier you will likely get SSH host key warnings 
here.
+In that case you should update `/var/lib/ganeti/known_hosts` with the new node
+information, and distribute it with `gnt-cluster copyfile` or by adding it to 
the
+OS configuration.
+
+The above configuration will make three operating systems available:
+
+```
+# gnt-os list
+Name
+debootstrap+buster
+debootstrap+testing+contrib+paravirtualized
+guix+default
+```
+
+Let's try them out.  But first we'll make Ganeti aware of our network
+so it can choose a static IP for the virtual machines.
+
+```
+# gnt-network add --network=192.168.1.0/24 --gateway=192.168.1.1 lan
+# gnt-network connect -N mode=openvswitch,link=br0 lan
+```
+
+Now we can add an instance:
+
+```
+root@node1 ~# gnt-instance add --no-name-check --no-ip-check \
+    -o debootstrap+buster -t drbd --disk 0:size=5G \
+    --net 0:network=lan,ip=pool bustervm1
+Thu Jul 16 18:28:58 2020  - INFO: Selected nodes for instance bustervm1 via 
iallocator hail: node1, node2
+Thu Jul 16 18:28:58 2020  - INFO: NIC/0 inherits netparams ['br0', 
'openvswitch', '']
+Thu Jul 16 18:28:58 2020  - INFO: Chose IP 192.168.1.2 from network lan
+Thu Jul 16 18:28:58 2020 * creating instance disks...
+Thu Jul 16 18:29:03 2020 adding instance bustervm1 to cluster config
+Thu Jul 16 18:29:03 2020 adding disks to cluster config
+Thu Jul 16 18:29:03 2020  - INFO: Waiting for instance bustervm1 to sync disks
+Thu Jul 16 18:29:03 2020  - INFO: - device disk/0:  0.60% done, 5m 26s 
remaining (estimated)
+[...]
+Thu Jul 16 18:31:08 2020  - INFO: - device disk/0: 100.00% done, 0s remaining 
(estimated)
+Thu Jul 16 18:31:08 2020  - INFO: Instance bustervm1's disks are in sync
+Thu Jul 16 18:31:08 2020  - INFO: Waiting for instance bustervm1 to sync disks
+Thu Jul 16 18:31:08 2020  - INFO: Instance bustervm1's disks are in sync
+Thu Jul 16 18:31:08 2020 * running the instance OS create scripts...
+Thu Jul 16 18:32:09 2020 * starting instance...
+```
+
+Ganeti will automatically select the optimal primary and secondary node
+for this VM based on available cluster resources.  You can manually
+specify primary and secondary nodes with the `-n` and `-s` options.
+
+By default Ganeti assumes that the new instance is already configured in DNS,
+so we need `--no-name-check` and `--no-ip-check` to bypass some sanity tests.
+
+Try adding another instance, now using the Guix OS provider with the 'plain'
+(LVM, not redundant) disk backend:
+
+```
+gnt-instance add --no-name-check --no-ip-check -o guix+default \
+    -t plain --disk 0:size=5G -B memory=1G,vcpus=2 \
+    --net 0:network=lan,ip=pool \
+    guix1
+```
+
+The `guix+default` variant has a configuration that starts an SSH server and
+authorizes the hosts SSH key, and configures static networking based on 
information
+from Ganeti.  To use other configuration files, you should declare variants 
with
+the config file as the `configuration` property.  The Guix provider also 
supports
+"OS parameters" that lets you specify a specific Guix commit or branch:
+
+```
+gnt-instance add --no-name-check --no-ip-check \
+    -o guix+gnome -O "commit=<commit>" \
+    -H kvm:spice_bind=0.0.0.0,cpu_type=host \
+    -t file --file-storage-dir=/srv/ganeti/file-storage \
+    --disk 0:size=20G -B minmem=1G,maxmem=6G,vcpus=3 \
+    --net 0:network=lan,ip=pool -n node1 \
+    guix2
+```
+
+You can connect to a VM serial console using `gnt-instance console <instance>`.
+For this last VM we used a hypothetical 'guix+gnome' variant, and added a
+graphical [SPICE](https://spice-space.org/) console that you can connect to
+remotely using the `spicy` command.
+
+If you are new to Ganeti, the next steps is to familiarize yourself with the
+`gnt-` family commands.  Fun stuff to do include `gnt-instance migrate` to move
+VMs between hosts, `gnt-node evacuate` to migrate _all_ VMs off a node, and
+`gnt-cluster master-failover` to move the master role to a different node.
+
+If you wish to start over for any reason, you can use `gnt-cluster destroy`.
+
+
+# Final remarks
+
+The declarative nature of Guix maps well to Ganetis OS API.  OS variants can be
+composed and inherit from each other, something that is not easily achieved 
with
+traditional configuration management tools.  This convenience does however make
+me wish other parts of Ganeti could be made declarative, such as instance and
+cluster configuration.  Nevertheless I'm happy and excited to finally be able
+to use Guix as a Ganeti host OS.
+
+Like most services in Guix, Ganeti comes with a
+[system test](https://guix.gnu.org/blog/2016/guixsd-system-tests/)
+that runs in a VM and ensures that things like initializing a cluster work.
+The continuous integration system
+[runs this automatically](https://ci.guix.gnu.org/search?query=ganeti) whenever
+a dependency is updated, and provides comfort that both the package and service
+is in a good shape.  Currently it has
+[rudimentary service 
tests](https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/tests/ganeti.scm#n117),
+but it can conceivably be extended to provision a real cluster inside Ganeti
+and try things like master-failover and live migration.
+
+So far only the KVM hypervisor has been tested.  If you use LXC or Xen with
+Ganeti, please reach out to `guix-devel@gnu.org` and share your experience.
+
+#### About GNU Guix
+
+[GNU Guix](https://guix.gnu.org) is a transactional package
+manager and an advanced distribution of the GNU system that [respects
+user
+freedom](https://www.gnu.org/distros/free-system-distribution-guidelines.html).
+Guix can be used on top of any system running the kernel Linux, or it
+can be used as a standalone operating system distribution for i686,
+x86_64, ARMv7, and AArch64 machines.
+
+In addition to standard package management features, Guix supports
+transactional upgrades and roll-backs, unprivileged package management,
+per-user profiles, and garbage collection.  When used as a standalone
+GNU/Linux distribution, Guix offers a declarative, stateless approach to
+operating system configuration management.  Guix is highly customizable
+and hackable through [Guile](https://www.gnu.org/software/guile)
+programming interfaces and extensions to the
+[Scheme](http://schemers.org) language.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]