Projekt

Allgemein

Profil

Setup kvm » Historie » Version 5

Jeremias Keihsler, 23.09.2024 10:38

1 1 Jeremias Keihsler
h1. KVM
2
3 2 Jeremias Keihsler
this is for a vanilla CentOS 8 minimal installation,
4 1 Jeremias Keihsler
largely based on @kvm_virtualization_in_rhel_7_made_easy.pdf@
5
6
good information is also found at http://virtuallyhyper.com/2013/06/migrate-from-libvirt-kvm-to-virtualbox/
7
8 3 Jeremias Keihsler
br0 -sources:
9
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/networking_guide/sec-configuring_ip_networking_with_nmcli
10
https://www.tecmint.com/create-network-bridge-in-rhel-centos-8/
11
https://www.cyberciti.biz/faq/how-to-add-network-bridge-with-nmcli-networkmanager-on-linux/
12
https://extravm.com/billing/knowledgebase/114/CentOS-8-ifup-unknown-connection---Add-Second-IP.html
13
14
15 1 Jeremias Keihsler
h2. basic updates/installs
16
17
<pre><code class="bash">
18
yum update
19
yum install wget
20
yum install vim
21
reboot
22
</code></pre>
23
24
h2. check machine capability
25
26
<pre><code class="bash">
27
grep -E 'svm|vmx' /proc/cpuinfo
28
</code></pre>
29
30
vmx ... Intel
31
svm ... AMD
32
33
h2. install KVM on CentOS minimal
34
35
<pre><code class="bash">
36
yum install qemu-kvm libvirt libguestfs-tools virt-install
37
systemctl enable libvirtd && systemctl start libvirtd
38
</code></pre>
39
40
verify the following kernel modules are loaded
41
<pre><code class="bash">
42
lsmod | grep kvm
43
</code></pre>
44
45
<pre><code class="bash">
46
kvm
47
kvm_intel
48
</code></pre>
49
<pre><code class="bash">
50
kvm
51
kvm_amd
52
</code></pre>
53 4 Jeremias Keihsler
54 1 Jeremias Keihsler
h2. setup networking
55
56
add to the network controller configuration file @/etc/sysconfig/network-scripts/ifcfg-em1@
57
<pre>
58
...
59
BRIDGE=br0
60
</pre>
61
62
add following new file @/etc/sysconfig/network-scripts/ifcfg-br0@
63
<pre>
64
DEVICE="br0"
65
# BOOTPROTO is up to you. If you prefer “static”, you will need to
66
# specify the IP address, netmask, gateway and DNS information.
67
BOOTPROTO="dhcp"
68
IPV6INIT="yes"
69
IPV6_AUTOCONF="yes"
70
ONBOOT="yes"
71
TYPE="Bridge"
72
DELAY="0"
73
</pre>
74
75
enable network forwarding @/etc/sysctl.conf@
76
<pre>
77
...
78
net.ipv4.ip_forward = 1
79
</pre>
80
81
read the file and restart NetworkManager
82
<pre><code class="bash">
83
sysctl -p /etc/sysctl.conf
84
systemctl restart NetworkManager
85
</code></pre>
86
87
h2. can KVM and Virtualbox coexist
88
89
http://www.dedoimedo.com/computers/kvm-virtualbox.html
90
91
h2. convert Virtualbox to KVM
92
93
h3. uninstall Virtualbox-guest-additions
94
95
<pre><code class="bash">
96
opt/[VboxAddonsFolder]/uninstall.sh
97
</code></pre>
98
99
some people had to remove @/etc/X11/xorg.conf@
100
101
h3. convert image from Virtualbox to KWM
102
103
<pre><code class="bash">
104
VBoxManage clonehd --format RAW Virt_Image.vdi Virt_Image.img
105
</code></pre>
106
107
RAW-Datei nach qcow konvertieren
108
<pre><code class="bash">
109
qemu-img convert -f raw Virt_Image.img -O qcow2 Virt_Image.qcow
110
</code></pre>
111
112
h2. automatic start/shutdown of VMs with Host
113
114
taken from https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Virtualization_Administration_Guide/sub-sect-Shutting_down_rebooting_and_force_shutdown_of_a_guest_virtual_machine-Manipulating_the_libvirt_guests_configuration_settings.html
115
116
h3. enable libvirt-guests service
117 5 Jeremias Keihsler
118 1 Jeremias Keihsler
<pre><code class="bash">
119
systemctl enable libvirt-guests
120
systemctl start libvirt-guests
121
</code></pre>
122
123
all settings are to be done in @/etc/sysconfig/libvirt-guests@
124
125
h2. install
126
127
128
<pre><code class="bash">
129
yum install virt-manager
130
</code></pre>
131
132
<pre><code class="bash">
133
usermod -a -G libvirt username
134
</code></pre>
135
136
h2. rename KVM-guest
137
138
taken from http://www.taitclarridge.com/techlog/2011/01/rename-kvm-virtual-machine-with-virsh.html
139
140
Power off the virtual machine and export the machine's XML configuration file:
141
142
<pre><code class="bash">
143
virsh dumpxml name_of_vm > name_of_vm.xml
144
</code></pre>
145
146
Next, edit the XML file and change the name between the <name></name> tags (should be right near the top). As an added step you could also rename the disk file to reflect the change of the name and change the name of it in the <devices> section under <source file='/path/to/name_of_vm.img'>.
147
148
Save the XML file and undefine the old VM name with:
149
150
<pre><code class="bash">
151
virsh undefine name_of_vm
152
</code></pre>
153
154
Now just import the edited XML file to define the VM:
155
156
<pre><code class="bash">
157
virsh define name_of_vm.xml
158
</code></pre>
159
160
And that should be it! You can now start up your vm either in the Virtual Machine Manager or with virsh using:
161
162
<pre><code class="bash">
163
virsh start name_of_vm
164
</code></pre>
165
166
h2. set fixed IP-adr via DHCP (default-network)
167
168
taken from https://wiki.libvirt.org/page/Networking
169
170
<pre><code class="bash">
171
virsh edit <guest>
172
</code></pre>
173
174
where <guest> is the name or uuid of the guest. Add the following snippet of XML to the config file: 
175
176
<pre><code class="bash">
177
<interface type='network'>
178
  <source network='default'/>
179
  <mac address='00:16:3e:1a:b3:4a'/>
180
</interface>
181
</code></pre>
182
183
Applying modifications to the network
184
185
Sometimes, one needs to edit the network definition and apply the changes on the fly. The most common scenario for this is adding new static MAC+IP mappings for the network's DHCP server. If you edit the network with "virsh net-edit", any changes you make won't take effect until the network is destroyed and re-started, which unfortunately will cause a all guests to lose network connectivity with the host until their network interfaces are explicitly re-attached.
186
virsh net-update
187
188
Fortunately, many changes to the network configuration (including the aforementioned addition of a static MAC+IP mapping for DHCP) can be done with "virsh net-update", which can be told to enact the changes immediately. For example, to add a DHCP static host entry to the network named "default" mapping MAC address 53:54:00:00:01 to IP address 192.168.122.45 and hostname "bob", you could use this command: 
189
190
<pre><code class="bash">
191
virsh net-update default add ip-dhcp-host \
192
          "<host mac='52:54:00:00:00:01' \
193
           name='bob' ip='192.168.122.45' />" \
194
           --live --config
195
</code></pre>
196
197
h2. forwarding incoming connections
198
199
taken from https://wiki.libvirt.org/page/Networking
200
201
By default, guests that are connected via a virtual network with <forward mode='nat'/> can make any outgoing network connection they like. Incoming connections are allowed from the host, and from other guests connected to the same libvirt network, but all other incoming connections are blocked by iptables rules.
202
203
If you would like to make a service that is on a guest behind a NATed virtual network publicly available, you can setup libvirt's "hook" script for qemu to install the necessary iptables rules to forward incoming connections to the host on any given port HP to port GP on the guest GNAME:
204
205
1) Determine a) the name of the guest "G" (as defined in the libvirt domain XML), b) the IP address of the guest "I", c) the port on the guest that will receive the connections "GP", and d) the port on the host that will be forwarded to the guest "HP".
206
207
(To assure that the guest's IP address remains unchanged, you can either configure the guest OS with static ip information, or add a <host> element inside the <dhcp> element of the network that is used by your guest. See the libvirt network XML documentation address section for defails and an example.)
208
209
2) Stop the guest if it's running.
210
211
3) Create the file /etc/libvirt/hooks/qemu (or add the following to an already existing hook script), with contents similar to the following (replace GNAME, IP, GP, and HP appropriately for your setup):
212
213
Use the basic script below or see an "advanced" version, which can handle several different machines and port mappings here (improvements are welcome) or here's a python script which does a similar thing and is easy to understand and configure (improvements are welcome): 
214
215
<pre>
216
#!/bin/bash
217
# used some from advanced script to have multiple ports: use an equal number of guest and host ports
218
219
# Update the following variables to fit your setup
220
Guest_name=GUEST_NAME
221
Guest_ipaddr=GUEST_IP
222
Host_ipaddr=HOST_IP
223
Host_port=(  'HOST_PORT1' 'HOST_PORT2' )
224
Guest_port=( 'GUEST_PORT1' 'GUEST_PORT2' )
225
226
length=$(( ${#Host_port[@]} - 1 ))
227
if [ "${1}" = "${Guest_name}" ]; then
228
   if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
229
       for i in `seq 0 $length`; do
230
               iptables -t nat -D PREROUTING -d ${Host_ipaddr} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
231
               iptables -D FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
232
       done
233
   fi
234
   if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
235
       for i in `seq 0 $length`; do
236
               iptables -t nat -A PREROUTING -d ${Host_ipaddr} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
237
               iptables -I FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
238
       done
239
   fi
240
fi
241
</pre>
242
4) chmod +x /etc/libvirt/hooks/qemu
243
244
5) Restart the libvirtd service.
245
246
6) Start the guest.
247
248
(NB: This method is a hack, and has one annoying flaw in versions of libvirt prior to 0.9.13 - if libvirtd is restarted while the guest is running, all of the standard iptables rules to support virtual networks that were added by libvirtd will be reloaded, thus changing the order of the above FORWARD rule relative to a reject rule for the network, hence rendering this setup non-working until the guest is stopped and restarted. Thanks to the new "reconnect" hook in libvirt-0.9.13 and newer (which is used by the above script if available), this flaw is not present in newer versions of libvirt (however, this hook script should still be considered a hack). 
249
250
h2. wrapper script for virsh
251
252
<pre>
253
#! /bin/sh
254
# kvm_control   Startup script for KVM Virtual Machines
255
#
256
# description: Manages KVM VMs
257
# processname: kvm_control.sh
258
#
259
# pidfile: /var/run/kvm_control/kvm_control.pid
260
#
261
### BEGIN INIT INFO
262
#
263
### END INIT INFO
264
#
265
# Version 20171103 by Jeremias Keihsler added ionice prio 'idle'
266
# Version 20161228 by Jeremias Keihsler based on:
267
# virsh-specific parts are taken from:
268
#  https://github.com/kumina/shutdown-kvm-guests/blob/master/shutdown-kvm-guests.sh
269
# Version 20110509 by Jeremias Keihsler (vboxcontrol) based on:
270
# Version 20090301 by Kevin Swanson <kswan.info> based on:
271
# Version 2008051100 by Jochem Kossen <jochem.kossen@gmail.com>
272
# http://farfewertoes.com
273
#
274
# Released in the public domain
275
#
276
# This file came with a README file containing the instructions on how
277
# to use this script.
278
# 
279
# this is no more to be used as an init.d-script (vboxcontrol was an init.d-script)
280
#
281
282
################################################################################
283
# INITIAL CONFIGURATION
284
285
export PATH="${PATH:+$PATH:}/bin:/usr/bin:/usr/sbin:/sbin"
286
287
VIRSH=/usr/bin/virsh
288
TIMEOUT=300
289
290
declare -i VM_isrunning
291
292
################################################################################
293
# FUNCTIONS
294
295
log_failure_msg() {
296
echo $1
297
}
298
299
log_action_msg() {
300
echo $1
301
}
302
303
# list running domains
304
list_running_domains() {
305
  $VIRSH list | grep running | awk '{ print $2}'
306
}
307
308
# Check for running machines every few seconds; return when all machines are
309
# down
310
wait_for_closing_machines() {
311
RUNNING_MACHINES=`list_running_domains | wc -l`
312
if [ $RUNNING_MACHINES != 0 ]; then
313
  log_action_msg "machines running: "$RUNNING_MACHINES
314
  sleep 2
315
316
  wait_for_closing_machines
317
fi
318
}
319
320
################################################################################
321
# RUN
322
case "$1" in
323
  start)
324
    if [ -f /etc/kvm_box/machines_enabled_start ]; then
325
326
      cat /etc/kvm_box/machines_enabled_start | while read VM; do
327
        log_action_msg "Starting VM: $VM ..."
328
        $VIRSH start $VM
329
        sleep 20
330
        RETVAL=$?
331
      done
332
      touch /tmp/kvm_control
333
    fi
334
  ;;
335
  stop)
336
    # NOTE: this stops first the listed VMs in the given order
337
    # and later all running VM's. 
338
    # After the defined timeout all remaining VMs are killed
339
340
    # Create some sort of semaphore.
341
    touch /tmp/shutdown-kvm-guests
342
343
    echo "Try to cleanly shut down all listed KVM domains..."
344
    # Try to shutdown each listed domain, one by one.
345
    if [ -f /etc/kvm_box/machines_enabled_stop ]; then
346
      cat /etc/kvm_box/machines_enabled_stop | while read VM; do
347
        log_action_msg "Shutting down VM: $VM ..."
348
        $VIRSH shutdown $VM --mode acpi
349
        sleep 10
350
        RETVAL=$?
351
      done
352
    fi
353
    sleep 10
354
355
    echo "give still running machines some more time..."
356
    # wait 20s per still running machine
357
    list_running_domains | while read VM; do
358
      log_action_msg "waiting 20s ... for: $VM ..."
359
      sleep 20
360
    done
361
362
    echo "Try to cleanly shut down all running KVM domains..."
363
    # Try to shutdown each remaining domain, one by one.
364
    list_running_domains | while read VM; do
365
      log_action_msg "Shutting down VM: $VM ..."
366
      $VIRSH shutdown $VM --mode acpi
367
      sleep 10
368
    done
369
370
    # Wait until all domains are shut down or timeout has reached.
371
    END_TIME=$(date -d "$TIMEOUT seconds" +%s)
372
373
    while [ $(date +%s) -lt $END_TIME ]; do
374
      # Break while loop when no domains are left.
375
      test -z "$(list_running_domains)" && break
376
      # Wait a litte, we don't want to DoS libvirt.
377
      sleep 2
378
    done
379
380
    # Clean up left over domains, one by one.
381
    list_running_domains | while read DOMAIN; do
382
      # Try to shutdown given domain.
383
      $VIRSH destroy $DOMAIN
384
      # Give libvirt some time for killing off the domain.
385
      sleep 10
386
    done
387
388
    wait_for_closing_machines
389
    rm -f /tmp/shutdown-kvm-guests
390
    rm -f /tmp/kvm_control
391
  ;;
392
  export)
393
    JKE_DATE=$(date +%F)
394
    if [ -f /etc/kvm_box/machines_enabled_export ]; then
395
      cat /etc/kvm_box/machines_enabled_export  | while read VM; do
396
        rm -f /tmp/kvm_control_VM_isrunning
397
        VM_isrunning=0
398
        list_running_domains | while read RVM; do
399
          #echo "VM list -$VM- : -$RVM-"
400
          if [[ "$VM" ==  "$RVM" ]]; then
401
            #echo "VM found running..."
402
            touch /tmp/kvm_control_VM_isrunning
403
            VM_isrunning=1
404
            #echo "$VM_isrunning"
405
            break
406
          fi
407
          #echo "$VM_isrunning"
408
        done
409
410
        # took me a while to figure out that the above 'while'-loop 
411
        # runs in a separate process ... let's use the 'file' as a 
412
        # kind of interprocess-communication :-) JKE 20161229
413
        if [ -f /tmp/kvm_control_VM_isrunning ]; then
414
          VM_isrunning=1
415
        fi
416
        rm -f /tmp/kvm_control_VM_isrunning
417
418
        #echo "VM status $VM_isrunning"
419
        if [ "$VM_isrunning" -ne 0 ]; then
420
          log_failure_msg "Exporting VM: $VM is not possible, it's running ..."
421
        else
422
          log_action_msg "Exporting VM: $VM ..."
423
          VM_BAK_DIR="$VM"_"$JKE_DATE"
424
          mkdir "$VM_BAK_DIR"
425
          $VIRSH dumpxml $VM > ./$VM_BAK_DIR/$VM.xml
426
          $VIRSH -q domblklist $VM | awk '{ print$2}' | while read VMHDD; do
427
            echo "$VM hdd=$VMHDD"
428
            if [ -f "$VMHDD" ]; then
429
              ionice -c 3 rsync --progress $VMHDD ./$VM_BAK_DIR/`basename $VMHDD`
430
            else
431
              log_failure_msg "Exporting VM: $VM image-file $VMHDD not found ..."
432
            fi
433
          done
434
        fi
435
      done
436
    else
437
      log_action_msg "export-list not found"
438
    fi
439
  ;;
440
  start-vm)
441
    log_action_msg "Starting VM: $2 ..."
442
    $VIRSH start $2
443
    RETVAL=$?
444
  ;;
445
  stop-vm)
446
    log_action_msg "Stopping VM: $2 ..."
447
    $VIRSH shutdown $2 --mode acpi
448
    RETVAL=$?
449
  ;;
450
  poweroff-vm)
451
    log_action_msg "Powering off VM: $2 ..."
452
    $VIRSH destroy $2
453
    RETVAL=$?
454
  ;;
455
  export-vm)
456
    # NOTE: this exports the given VM
457
    log_action_msg "Exporting VM: $2 ..."
458
    rm -f /tmp/kvm_control_VM_isrunning
459
    VM_isrunning=0
460
    JKE_DATE=$(date +%F)
461
    list_running_domains | while read RVM; do
462
      #echo "VM list -$VM- : -$RVM-"
463
      if [[ "$2" ==  "$RVM" ]]; then
464
        #echo "VM found running..."
465
        touch /tmp/kvm_control_VM_isrunning
466
        VM_isrunning=1
467
        #echo "$VM_isrunning"
468
        break
469
      fi
470
      #echo "$VM_isrunning"
471
    done
472
473
    # took me a while to figure out that the above 'while'-loop 
474
    # runs in a separate process ... let's use the 'file' as a 
475
    # kind of interprocess-communication :-) JKE 20161229
476
    if [ -f /tmp/kvm_control_VM_isrunning ]; then
477
      VM_isrunning=1
478
    fi
479
    rm -f /tmp/kvm_control_VM_isrunning
480
481
    #echo "VM status $VM_isrunning"
482
    if [ "$VM_isrunning" -ne 0 ]; then
483
      log_failure_msg "Exporting VM: $VM is not possible, it's running ..."
484
    else
485
      log_action_msg "Exporting VM: $VM ..."
486
      VM_BAK_DIR="$2"_"$JKE_DATE"
487
      mkdir "$VM_BAK_DIR"
488
      $VIRSH dumpxml $2 > ./$VM_BAK_DIR/$2.xml
489
      $VIRSH -q domblklist $2 | awk '{ print$2}' | while read VMHDD; do
490
        echo "$2 hdd=$VMHDD"
491
        if [ -f "$VMHDD" ]; then
492
          ionice -c 3 rsync --progress $VMHDD ./$VM_BAK_DIR/`basename $VMHDD`
493
        else
494
          log_failure_msg "Exporting VM: $2 image-file $VMHDD not found ..."
495
        fi
496
      done
497
    fi
498
  ;;
499
  status)
500
    echo "The following virtual machines are currently running:"
501
    list_running_domains | while read VM; do
502
      echo -n "  $VM"
503
      echo " ... is running"
504
    done
505
  ;;
506
507
  *)
508
    echo "Usage: $0 {start|stop|status|export|start-vm <VM name>|stop-vm <VM name>|poweroff-vm <VM name>}|export-vm <VMname>"
509
    echo "  start      start all VMs listed in '/etc/kvm_box/machines_enabled_start'"
510
    echo "  stop       1st step: acpi-shutdown all VMs listed in '/etc/kvm_box/machines_enabled_stop'"
511
    echo "             2nd step: wait 20s for each still running machine to give a chance to shut-down on their own"
512
    echo "             3rd step: acpi-shutdown all running VMs"
513
    echo "             4th step: wait for all machines shutdown or $TIMEOUT s"
514
    echo "             5th step: destroy all sitting VMs"
515
    echo "  status     list all running VMs"
516
    echo "  export     export all VMs listed in '/etc/kvm_box/machines_enabled_export' to the current directory"
517
    echo "  start-vm <VM name>     start the given VM"
518
    echo "  stop-vm <VM name>      acpi-shutdown the given VM"
519
    echo "  poweroff-vm <VM name>  poweroff the given VM"
520
    echo "  export-vm <VM name>    export the given VM to the current directory"
521
    exit 3
522
esac
523
524
exit 0
525
526
</pre>
527
528
h2. restore 'exported' kvm-machines
529
530
<pre><code class="shell">
531
tar xvf mach-name_202x-01-01.tar.gz 
532
</code></pre>
533
534
* copy the image-files to @/var/lib/libvirt/images/@
535
536
set ownership
537
<pre><code class="shell">
538
chown qemu:qemu /var/lib/libvirt/images/*
539
</code></pre>
540
541
542
define the machine by
543
544
<pre><code class="shell">
545
virsh define mach-name.xml
546
</code></pre>