Projekt

Allgemein

Profil

Setup kvm » Historie » Version 4

Jeremias Keihsler, 22.09.2021 15:57

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
<pre><code class="bash">
118
systemctl enable libvirt-guests
119
systemctl start libvirt-guests
120
</code></pre>
121
122
all settings are to be done in @/etc/sysconfig/libvirt-guests@
123
124
h2. install
125
126
127
<pre><code class="bash">
128
yum install virt-manager
129
</code></pre>
130
131
<pre><code class="bash">
132
usermod -a -G libvirt username
133
</code></pre>
134
135
h2. rename KVM-guest
136
137
taken from http://www.taitclarridge.com/techlog/2011/01/rename-kvm-virtual-machine-with-virsh.html
138
139
Power off the virtual machine and export the machine's XML configuration file:
140
141
<pre><code class="bash">
142
virsh dumpxml name_of_vm > name_of_vm.xml
143
</code></pre>
144
145
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'>.
146
147
Save the XML file and undefine the old VM name with:
148
149
<pre><code class="bash">
150
virsh undefine name_of_vm
151
</code></pre>
152
153
Now just import the edited XML file to define the VM:
154
155
<pre><code class="bash">
156
virsh define name_of_vm.xml
157
</code></pre>
158
159
And that should be it! You can now start up your vm either in the Virtual Machine Manager or with virsh using:
160
161
<pre><code class="bash">
162
virsh start name_of_vm
163
</code></pre>
164
165
h2. set fixed IP-adr via DHCP (default-network)
166
167
taken from https://wiki.libvirt.org/page/Networking
168
169
<pre><code class="bash">
170
virsh edit <guest>
171
</code></pre>
172
173
where <guest> is the name or uuid of the guest. Add the following snippet of XML to the config file: 
174
175
<pre><code class="bash">
176
<interface type='network'>
177
  <source network='default'/>
178
  <mac address='00:16:3e:1a:b3:4a'/>
179
</interface>
180
</code></pre>
181
182
Applying modifications to the network
183
184
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.
185
virsh net-update
186
187
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: 
188
189
<pre><code class="bash">
190
virsh net-update default add ip-dhcp-host \
191
          "<host mac='52:54:00:00:00:01' \
192
           name='bob' ip='192.168.122.45' />" \
193
           --live --config
194
</code></pre>
195
196
h2. forwarding incoming connections
197
198
taken from https://wiki.libvirt.org/page/Networking
199
200
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.
201
202
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:
203
204
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".
205
206
(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.)
207
208
2) Stop the guest if it's running.
209
210
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):
211
212
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): 
213
214
<pre>
215
#!/bin/bash
216
# used some from advanced script to have multiple ports: use an equal number of guest and host ports
217
218
# Update the following variables to fit your setup
219
Guest_name=GUEST_NAME
220
Guest_ipaddr=GUEST_IP
221
Host_ipaddr=HOST_IP
222
Host_port=(  'HOST_PORT1' 'HOST_PORT2' )
223
Guest_port=( 'GUEST_PORT1' 'GUEST_PORT2' )
224
225
length=$(( ${#Host_port[@]} - 1 ))
226
if [ "${1}" = "${Guest_name}" ]; then
227
   if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
228
       for i in `seq 0 $length`; do
229
               iptables -t nat -D PREROUTING -d ${Host_ipaddr} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
230
               iptables -D FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
231
       done
232
   fi
233
   if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
234
       for i in `seq 0 $length`; do
235
               iptables -t nat -A PREROUTING -d ${Host_ipaddr} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
236
               iptables -I FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
237
       done
238
   fi
239
fi
240
</pre>
241
4) chmod +x /etc/libvirt/hooks/qemu
242
243
5) Restart the libvirtd service.
244
245
6) Start the guest.
246
247
(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). 
248
249
h2. wrapper script for virsh
250
251
<pre>
252
#! /bin/sh
253
# kvm_control   Startup script for KVM Virtual Machines
254
#
255
# description: Manages KVM VMs
256
# processname: kvm_control.sh
257
#
258
# pidfile: /var/run/kvm_control/kvm_control.pid
259
#
260
### BEGIN INIT INFO
261
#
262
### END INIT INFO
263
#
264
# Version 20171103 by Jeremias Keihsler added ionice prio 'idle'
265
# Version 20161228 by Jeremias Keihsler based on:
266
# virsh-specific parts are taken from:
267
#  https://github.com/kumina/shutdown-kvm-guests/blob/master/shutdown-kvm-guests.sh
268
# Version 20110509 by Jeremias Keihsler (vboxcontrol) based on:
269
# Version 20090301 by Kevin Swanson <kswan.info> based on:
270
# Version 2008051100 by Jochem Kossen <jochem.kossen@gmail.com>
271
# http://farfewertoes.com
272
#
273
# Released in the public domain
274
#
275
# This file came with a README file containing the instructions on how
276
# to use this script.
277
# 
278
# this is no more to be used as an init.d-script (vboxcontrol was an init.d-script)
279
#
280
281
################################################################################
282
# INITIAL CONFIGURATION
283
284
export PATH="${PATH:+$PATH:}/bin:/usr/bin:/usr/sbin:/sbin"
285
286
VIRSH=/usr/bin/virsh
287
TIMEOUT=300
288
289
declare -i VM_isrunning
290
291
################################################################################
292
# FUNCTIONS
293
294
log_failure_msg() {
295
echo $1
296
}
297
298
log_action_msg() {
299
echo $1
300
}
301
302
# list running domains
303
list_running_domains() {
304
  $VIRSH list | grep running | awk '{ print $2}'
305
}
306
307
# Check for running machines every few seconds; return when all machines are
308
# down
309
wait_for_closing_machines() {
310
RUNNING_MACHINES=`list_running_domains | wc -l`
311
if [ $RUNNING_MACHINES != 0 ]; then
312
  log_action_msg "machines running: "$RUNNING_MACHINES
313
  sleep 2
314
315
  wait_for_closing_machines
316
fi
317
}
318
319
################################################################################
320
# RUN
321
case "$1" in
322
  start)
323
    if [ -f /etc/kvm_box/machines_enabled_start ]; then
324
325
      cat /etc/kvm_box/machines_enabled_start | while read VM; do
326
        log_action_msg "Starting VM: $VM ..."
327
        $VIRSH start $VM
328
        sleep 20
329
        RETVAL=$?
330
      done
331
      touch /tmp/kvm_control
332
    fi
333
  ;;
334
  stop)
335
    # NOTE: this stops first the listed VMs in the given order
336
    # and later all running VM's. 
337
    # After the defined timeout all remaining VMs are killed
338
339
    # Create some sort of semaphore.
340
    touch /tmp/shutdown-kvm-guests
341
342
    echo "Try to cleanly shut down all listed KVM domains..."
343
    # Try to shutdown each listed domain, one by one.
344
    if [ -f /etc/kvm_box/machines_enabled_stop ]; then
345
      cat /etc/kvm_box/machines_enabled_stop | while read VM; do
346
        log_action_msg "Shutting down VM: $VM ..."
347
        $VIRSH shutdown $VM --mode acpi
348
        sleep 10
349
        RETVAL=$?
350
      done
351
    fi
352
    sleep 10
353
354
    echo "give still running machines some more time..."
355
    # wait 20s per still running machine
356
    list_running_domains | while read VM; do
357
      log_action_msg "waiting 20s ... for: $VM ..."
358
      sleep 20
359
    done
360
361
    echo "Try to cleanly shut down all running KVM domains..."
362
    # Try to shutdown each remaining domain, one by one.
363
    list_running_domains | while read VM; do
364
      log_action_msg "Shutting down VM: $VM ..."
365
      $VIRSH shutdown $VM --mode acpi
366
      sleep 10
367
    done
368
369
    # Wait until all domains are shut down or timeout has reached.
370
    END_TIME=$(date -d "$TIMEOUT seconds" +%s)
371
372
    while [ $(date +%s) -lt $END_TIME ]; do
373
      # Break while loop when no domains are left.
374
      test -z "$(list_running_domains)" && break
375
      # Wait a litte, we don't want to DoS libvirt.
376
      sleep 2
377
    done
378
379
    # Clean up left over domains, one by one.
380
    list_running_domains | while read DOMAIN; do
381
      # Try to shutdown given domain.
382
      $VIRSH destroy $DOMAIN
383
      # Give libvirt some time for killing off the domain.
384
      sleep 10
385
    done
386
387
    wait_for_closing_machines
388
    rm -f /tmp/shutdown-kvm-guests
389
    rm -f /tmp/kvm_control
390
  ;;
391
  export)
392
    JKE_DATE=$(date +%F)
393
    if [ -f /etc/kvm_box/machines_enabled_export ]; then
394
      cat /etc/kvm_box/machines_enabled_export  | while read VM; do
395
        rm -f /tmp/kvm_control_VM_isrunning
396
        VM_isrunning=0
397
        list_running_domains | while read RVM; do
398
          #echo "VM list -$VM- : -$RVM-"
399
          if [[ "$VM" ==  "$RVM" ]]; then
400
            #echo "VM found running..."
401
            touch /tmp/kvm_control_VM_isrunning
402
            VM_isrunning=1
403
            #echo "$VM_isrunning"
404
            break
405
          fi
406
          #echo "$VM_isrunning"
407
        done
408
409
        # took me a while to figure out that the above 'while'-loop 
410
        # runs in a separate process ... let's use the 'file' as a 
411
        # kind of interprocess-communication :-) JKE 20161229
412
        if [ -f /tmp/kvm_control_VM_isrunning ]; then
413
          VM_isrunning=1
414
        fi
415
        rm -f /tmp/kvm_control_VM_isrunning
416
417
        #echo "VM status $VM_isrunning"
418
        if [ "$VM_isrunning" -ne 0 ]; then
419
          log_failure_msg "Exporting VM: $VM is not possible, it's running ..."
420
        else
421
          log_action_msg "Exporting VM: $VM ..."
422
          VM_BAK_DIR="$VM"_"$JKE_DATE"
423
          mkdir "$VM_BAK_DIR"
424
          $VIRSH dumpxml $VM > ./$VM_BAK_DIR/$VM.xml
425
          $VIRSH -q domblklist $VM | awk '{ print$2}' | while read VMHDD; do
426
            echo "$VM hdd=$VMHDD"
427
            if [ -f "$VMHDD" ]; then
428
              ionice -c 3 rsync --progress $VMHDD ./$VM_BAK_DIR/`basename $VMHDD`
429
            else
430
              log_failure_msg "Exporting VM: $VM image-file $VMHDD not found ..."
431
            fi
432
          done
433
        fi
434
      done
435
    else
436
      log_action_msg "export-list not found"
437
    fi
438
  ;;
439
  start-vm)
440
    log_action_msg "Starting VM: $2 ..."
441
    $VIRSH start $2
442
    RETVAL=$?
443
  ;;
444
  stop-vm)
445
    log_action_msg "Stopping VM: $2 ..."
446
    $VIRSH shutdown $2 --mode acpi
447
    RETVAL=$?
448
  ;;
449
  poweroff-vm)
450
    log_action_msg "Powering off VM: $2 ..."
451
    $VIRSH destroy $2
452
    RETVAL=$?
453
  ;;
454
  export-vm)
455
    # NOTE: this exports the given VM
456
    log_action_msg "Exporting VM: $2 ..."
457
    rm -f /tmp/kvm_control_VM_isrunning
458
    VM_isrunning=0
459
    JKE_DATE=$(date +%F)
460
    list_running_domains | while read RVM; do
461
      #echo "VM list -$VM- : -$RVM-"
462
      if [[ "$2" ==  "$RVM" ]]; then
463
        #echo "VM found running..."
464
        touch /tmp/kvm_control_VM_isrunning
465
        VM_isrunning=1
466
        #echo "$VM_isrunning"
467
        break
468
      fi
469
      #echo "$VM_isrunning"
470
    done
471
472
    # took me a while to figure out that the above 'while'-loop 
473
    # runs in a separate process ... let's use the 'file' as a 
474
    # kind of interprocess-communication :-) JKE 20161229
475
    if [ -f /tmp/kvm_control_VM_isrunning ]; then
476
      VM_isrunning=1
477
    fi
478
    rm -f /tmp/kvm_control_VM_isrunning
479
480
    #echo "VM status $VM_isrunning"
481
    if [ "$VM_isrunning" -ne 0 ]; then
482
      log_failure_msg "Exporting VM: $VM is not possible, it's running ..."
483
    else
484
      log_action_msg "Exporting VM: $VM ..."
485
      VM_BAK_DIR="$2"_"$JKE_DATE"
486
      mkdir "$VM_BAK_DIR"
487
      $VIRSH dumpxml $2 > ./$VM_BAK_DIR/$2.xml
488
      $VIRSH -q domblklist $2 | awk '{ print$2}' | while read VMHDD; do
489
        echo "$2 hdd=$VMHDD"
490
        if [ -f "$VMHDD" ]; then
491
          ionice -c 3 rsync --progress $VMHDD ./$VM_BAK_DIR/`basename $VMHDD`
492
        else
493
          log_failure_msg "Exporting VM: $2 image-file $VMHDD not found ..."
494
        fi
495
      done
496
    fi
497
  ;;
498
  status)
499
    echo "The following virtual machines are currently running:"
500
    list_running_domains | while read VM; do
501
      echo -n "  $VM"
502
      echo " ... is running"
503
    done
504
  ;;
505
506
  *)
507
    echo "Usage: $0 {start|stop|status|export|start-vm <VM name>|stop-vm <VM name>|poweroff-vm <VM name>}|export-vm <VMname>"
508
    echo "  start      start all VMs listed in '/etc/kvm_box/machines_enabled_start'"
509
    echo "  stop       1st step: acpi-shutdown all VMs listed in '/etc/kvm_box/machines_enabled_stop'"
510
    echo "             2nd step: wait 20s for each still running machine to give a chance to shut-down on their own"
511
    echo "             3rd step: acpi-shutdown all running VMs"
512
    echo "             4th step: wait for all machines shutdown or $TIMEOUT s"
513
    echo "             5th step: destroy all sitting VMs"
514
    echo "  status     list all running VMs"
515
    echo "  export     export all VMs listed in '/etc/kvm_box/machines_enabled_export' to the current directory"
516
    echo "  start-vm <VM name>     start the given VM"
517
    echo "  stop-vm <VM name>      acpi-shutdown the given VM"
518
    echo "  poweroff-vm <VM name>  poweroff the given VM"
519
    echo "  export-vm <VM name>    export the given VM to the current directory"
520
    exit 3
521
esac
522
523
exit 0
524
525
</pre>
526
527
h2. restore 'exported' kvm-machines
528
529
<pre><code class="shell">
530
tar xvf mach-name_202x-01-01.tar.gz 
531
</code></pre>
532
533
* copy the image-files to @/var/lib/libvirt/images/@
534
535
set ownership
536
<pre><code class="shell">
537
chown qemu:qemu /var/lib/libvirt/images/*
538
</code></pre>
539
540
541
define the machine by
542
543
<pre><code class="shell">
544
virsh define mach-name.xml
545
</code></pre>