KVM 是Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中,KVM目前已成为学术界的主流 VMM (virtual machine monitor,虚拟机监视器,也称为hypervisor)之一。
可参考红帽官方对kvm的定义:https://www.redhat.com/zh/topics/virtualization/what-is-KVM
KVM的虚拟化需要硬件支持(如Intel VT技术或者AMD V技术),是基于硬件的完全虚拟化,而Xen早期则是基于软件模拟的半虚拟化,新版本则是支持基于硬件支持的完全虚拟化,但Xen本身有自己的进程调度器,存储管理模块等,所以代码较为庞大,广为流传的商业系统虚拟化软件VMware ESXI系列是Full-Virtualization,(IBM文档:http://www.ibm.com/developerworks/cn/linux/l-using-kvm/ )
简单地说,KVM就是基于硬件支持实现,将单台主机,分隔成多个互不干扰的虚拟主机的技术,不受困于底层操作系统,可以为每个主机提供单独的、所需的的运行环境,如下图所示:

如果每个节点都是用共享存储如NAS,则可实现跨主机的虚拟机快速迁移,这也是我们在生产环境中经常采用的架构。本文将以下面架构,来具体展示KVM在实际生产中的应用。
三个主机,其中两个主机node1和node2上运行KVM,另一台是NAS共享存储服务器,通过10.0.0.0/16网段内网连接。在node1上的用KVM构建虚拟机H1和W1分别运行haproxy+keepalived服务和nginx服务,其中H1桥接方式到node1的两块网卡上,W1桥接到内网网卡eth1上。node2节点中也是如此。于是,将Client端请求通过haproxy反向代理只内网的web服务集群中,保证负载均衡和高可用,哪怕一个物理节点服务器down掉也不影响客户服务访问。
NAS服务器的搭建很容易,这里就先不详细介绍了,本文之后主要介绍KVM服务的配置及虚拟机集群的搭建。
宿主机环境准备:
KVM需要宿主机CPU必须支持虚拟化功能,因此如果是在vmware workstation上使用虚拟机做宿主机,那么必须要在虚拟机配置界面的处理器选项中开启虚拟机化功能(VMware支持嵌套虚拟化,而KVM不支持,大部分云服务器如阿里云就是基于KVM技术二次研发制作的,所以云服务器都不支持从中再建虚拟机)。
KVM模块因为已经被集成到linux内核之中,所以我们只需要安装KVM管理工具就可以直接使用KVM创建虚拟机了。
可用命令验证是否开启虚拟化(也可通过lscpu命令,grep -o选项只看匹配词本身,也可以用egrep代替grep -E)
| [root@localhost ~]# grep -Eo "vmx|svm" /proc/cpuinfo | wc -l |
| vmx |
| vmx |
KVM软件包安装
在Ubuntu 18.04中:
可参考官网https://ubuntu.com/server/docs/virtualization-libvirt
| |
| apt install qemu-kvm virt-manager libvirt-daemon-system |
| kvm-ok |
| INFO: /dev/kvm exists |
| KVM acceleration can be used |
CentOS 7.X:
yum install qemu-kvm qemu-kvm-tools libvirt libvirt-client virt-manager virt-install
| systemctl start libvirtd |
| systemctl enable libvirtd |
网络环境配置
为了让三个节点间虚拟机可以直接相互之间通信,需要将KVM上的虚拟机以桥接模式与宿主机的网卡连接,这就需要我们提前在宿主机上配置好网桥。
若宿主机为Ubuntu18.04:
sudo vim /etc/netplan/01-netcfg.yaml
| |
| |
| network: |
| version: 2 |
| renderer: networkd |
| ethernets: |
| eth0: |
| dhcp4: no |
| dhcp6: no |
| eth1: |
| dhcp4: no |
| dhcp6: no |
| bridges: |
| br0: |
| dhcp4: no |
| dhcp6: no |
| addresses: [172.18.0.75/16] |
| gateway4: 172.18.0.1 |
| nameservers: |
| addresses: [180.76.76.76] |
| interfaces: |
| - eth0 |
| br1: |
| dhcp4: no |
| dhcp6: no |
| addresses: [10.0.0.75/16] |
| interfaces: |
| - eth1 |
修改网卡配置后,使用netplan apply命令使配置文件的修改生效
netplan apply
若宿主机为CentOS:
cd /etc/sysconfig/network-scripts/
vim ifcfg-eh0
| TYPE=Ethernet |
| BOOTPROTO=static |
| NAME=eth0 |
| DEVICE=eth0 |
| ONBOOT=yes |
| BRIDGE=br0 |
vim ifcfg-eh1
| TYPE=Ethernet |
| BOOTPROTO=static |
| NAME=eth1 |
| DEVICE=eth1 |
| ONBOOT=yes |
| BRIDGE=br1 |
vim ifcfg-br0
| TYPE=Bridge |
| BOOTPROTO=static |
| NAME=br0 |
| DEVICE=br0 |
| ONBOOT=yes |
| IPADDR=172.18.32.75 |
| NETMASK=255.255.0.0 |
| GATEWAY=172.18.0.1 |
| DNS1=180.76.76.76 |
vim ifcfg-br1
| TYPE=Bridge |
| BOOTPROTO=static |
| NAME=br1 |
| DEVICE=br1 |
| ONBOOT=yes |
| IPADDR=10.0.0.75 |
| NETMASK=255.255.0.0 |
重启网络服务,生效配置
systemctl restart network
ip a命令或ifconfig查看桥接网卡是否生效
KVM虚拟机创建
创建虚拟磁盘
| [root@localhost ~]# ll /var/lib/libvirt/images/ #默认保存虚拟机磁盘的路径 |
| total 0 |
| [root@localhost ~]# |
使用qemu-img create命令可以创建磁盘,如果创建raw格式磁盘文件,则理解占据实际大小,若创建qcow2稀疏格式磁盘,则磁盘文件会随着使用的增大而增大
qemu-img create -f raw /var/lib/libvirt/images/CentOS7.raw 10G建磁盘
| ll -h /var/lib/libvirt/images/CentOS7.raw |
| -rw-r--r-- 1 root root 10G Nov 29 16:34 /var/lib/libvirt/images/centos.raw |
qemu-img create -f qcow2 /var/lib/libvirt/images/CentOS7.qcow2 10G
| ll -h /var/lib/libvirt/images/CentOS7.qcow2 |
| -rw-r--r-- 1 root root 193K Nov 29 16:36 /var/lib/libvirt/images/centos.qcow2 |
我们选用qcow2格式的磁盘,先创建H1虚拟机,
qemu-img create -f qcow2 /var/lib/libvirt/images/H1.qcow2 10G
创建虚拟机
先导入ISO光盘镜像文件,可以使用共享存储上的ISO文件,也可本机导入,本机上传的话一般习惯性放到/usr/local/src/目录下
使用virt-install命令创建虚拟机,参数可virt-install --help查看帮助信息
virt-install
| usage: virt-install |
| 使用指定安装介质新建虚拟机。 |
| optional arguments: |
| -h, |
| |
| |
| 通用选项: |
| -n NAME, |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 安装方法选项: |
| |
| -l LOCATION, |
| 安装源(例如:nfs:host:/path、http://host/path |
| ftp://host/path) |
| |
| |
| |
| -x EXTRA_ARGS, |
| 附加到使用 |
| |
| 使用 |
| 添加给定文件 |
| |
| 在其中安装 OS 变体的虚拟机,比如 |
| 'fedora18'、'rhel6'、'winxp' 等等。 |
| |
| |
| |
| |
| |
| 设备选项: |
| |
| |
| |
| |
| |
| -w NETWORK, |
| 配置虚拟机网络接口。例如: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 配置虚拟机控制程序设备。例如: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 主机设备配置为与虚拟机共享 |
| |
| 将主机目录传递给虚拟机。例如: |
| |
| |
| |
| |
| |
| |
| 配置虚拟机智能卡设备。例如: |
| |
| |
| |
| |
| 配置虚拟机 memballoon 设备。例如: |
| |
| |
| |
| |
| |
| |
| |
| 虚拟机配置选项: |
| |
| |
| |
| |
| 为域进程调整 blkio 策略。 |
| |
| 为域进程设置内存后备策略。例如: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 虚拟化平台选项: |
| -v, |
| -p, |
| |
| |
| 等等) |
| |
| |
| 其它选项: |
| |
| |
| |
| |
| |
| 输出所生成域 XML,而不是创建虚拟机。 |
| |
| 虚拟机。 |
| |
| |
| |
| -q, |
| -d, |
我们使用命令创建虚拟机(网卡配置选择default的话默认是nat模式)
输入virt-manager可开启xshellmanager的终端窗口如下图
双击图标打开,可以看到已经开始自动从光盘启动安装了,配置完毕,手动安装CentOS7系统。
如果输入virt-manage不能启动xmanager的窗口,检查一下项目
- 确认已经安装xmanager
- xshell文件—属性—连接—隧道—设置转发x11到xmanager
- ssh服务配置文件是否开启X11Forwarding选项 设置为yes
以上都没问题,建议重装xmanager,我就是配置都对,但是输入virt-manage命令没反应,重装了一下xmanager和xshell之后就可以正常使用了。
安装好CentOS7系统重启后,在vrit-manager的窗口中点击虚拟机info,给H1虚拟机添加一块网卡,并将两块网卡分别选择为主机的桥接网卡br0和br1,如下图所示

在虚拟机中配置两个网卡的IP,配置分别如下
| vi /etc/sysconfig/network-scripts/ifcfg-eth0 |
| TYPE="Ethernet" |
| BOOTPROTO="static" |
| IPADDR="172.18.32.85" |
| GATEWAY="172.18.0.1" |
| DNS1="180.76.76.76" |
| DEFROUTE="yes" |
| NAME="eth0" |
| DEVICE="eth0" |
| ONBOOT="yes" |
| vi /etc/sysconfig/network-scripts/ifcfg-eth1 |
| TYPE="Ethernet" |
| BOOTPROTO="static" |
| IPADDR="10.0.0.85" |
| DEFROUTE="no" |
| NAME="eth1" |
| DEVICE="eth1" |
| ONBOOT="yes" |
此时H1的基本配置就算完成了。
查看/var/lib/libvirt/images/目录下,看到已经有一个镜像了了
| [root@localhost ~]# ll /var/lib/libvirt/images/ |
| total 1594308 |
| -rw-r--r-- 1 root root 1632632832 Nov 29 18:54 H1.qcow2 |
可以直接将此文件cp一份来当web1的磁盘文件
| cd /var/lib/libvirt/images/ |
| cp H1.qcow2 W1.qcow2 |
也可以在node2节点上的H2和web2虚拟机的磁盘文件。
我们直接拷贝改名后,执行virt-install命令,创建另外三个虚拟机。
然后将W1强制终止,因为第一次是默认从光驱启动的,我们已经有系统了,不需要重装,重启后就发现已经是装好的系统,与H1一模一样的。
然后修改主机名为web1,将网卡桥接在eth1上,修改网卡配置。
| TYPE="Ethernet" |
| BOOTPROTO="static" |
| IPADDR="10.0.0.175" |
| PREFIX="16" |
| DEFROUTE="yes" |
| NAME="eth0" |
| DEVICE="eth0" |
| ONBOOT="yes" |
对node2上的两个虚拟机采用同样方式创建。
之后分别通过脚本装HAProxy+keepadlived服务和nginx+PHP服务。
修改完几个服务的配置文件(可参考之前文章企业级应用:负载均衡层——haproxy(一))之后,就可以将通过客户机就可以访问,后端web服务器了。
附一键安装脚本
HAProxy一键安装脚本
| [root@HAProxy1 haproxy]# tree |
| . |
| ├── haproxy-2.0.8.tar.gz |
| ├── haproxy84.cfg |
| ├── haproxy.cfg |
| ├── haproxy.service |
| ├── haproxy.sh |
| └── lua-5.3.5.tar.gz |
| |
| 0 directories, 6 files |
| [root@HAProxy1 haproxy]# bash haproxy.sh |
| #!/bin/bash |
| DST="/apps" |
| [ -a haproxy.cfg ] || { echo ' the absence of haproxy.conf' ;exit 1;} |
| [ -a haproxy-2.0.8.tar.gz ] || { echo ' the absence of haproxy-2.0.8.tar.gz' ;exit 1;} |
| [ -a haproxy.service ] || { echo ' the absence of haproxy.service' ;exit 2;} |
| [ -a /usr/lib/systemd/system/haproxy.service ] && { echo ' haproxy is aready installed' ;exit 3;} |
| id haproxy &>/dev/null && { echo 'user haproxy is exist' ; exit 4;} || useradd -r -s /sbin/nologin -u 79 haproxy |
| yum install -y libtermcap-devel ncurses-devel libevent-devel readline-devel gcc make |
| [ -a lua-5.3.5.tar.gz ] || wget http://www.lua.org/ftp/lua-5.3.5.tar.gz |
| |
| tar xvf lua-5.3.5.tar.gz |
| cd lua-5.3.5 |
| make linux test |
| |
| yum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel -y |
| mkdir -p $DST/haproxy |
| |
| cd .. |
| tar xvf haproxy-2.0.8.tar.gz |
| sleep 1 |
| cd haproxy-2.0.8 |
| |
| make ARCH=x86_64 \ |
| TARGET=linux-glibc USE_PCRE=1 \ |
| USE_OPENSSL=1 \ |
| USE_ZLIB=1 \ |
| USE_SYSTEMD=1 \ |
| USE_CPU_AFFINITY=1 \ |
| USE_LUA=1 \ |
| LUA_INC=../lua-5.3.5/src/ \ |
| LUA_LIB=../lua-5.3.5/src/ \ |
| PREFIX=$DST/haproxy |
| make install PREFIX=$DST/haproxy |
| |
| cat > /usr/lib/systemd/system/haproxy.service << "END" |
| [Unit] |
| Description=HAProxy Load Balancer |
| After=syslog.target network.target |
| [Service] |
| ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q |
| ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid |
| ExecReload=/bin/kill -USR2 $MAINPID |
| [Install] |
| WantedBy=multi-user.target\ |
| END |
| sed -i "s@/usr@$DST/haproxy@" /usr/lib/systemd/system/haproxy.service |
| |
| mkdir -p /etc/haproxy |
| cd .. |
| cp haproxy.cfg /etc/haproxy/ |
| sed -i "s@chroot /apps/haproxy@chroot $DST/haproxy@" /etc/haproxy/haproxy.cfg |
| mkdir -p /var/lib/haproxy |
| chown 99.99 /var/lib/haproxy/ -R |
| |
| cat >> /etc/sysctl.conf << END |
| net.ipv4.ip_forward = 1 |
| net.ipv4.ip_nonlocal_bind = 1 |
| END |
| sysctl -p |
| |
| systemctl enable --now haproxy |
| systemctl status haproxy |
Nginx+PHP一键安装脚本
| [root@Web1 web]# tree |
| . |
| ├── nginx |
| │ ├── nginx-1.16.1.tar.gz |
| │ ├── nginx.conf |
| │ ├── nginx.service |
| │ └── nginx.sh |
| ├── php-fpm |
| │ ├── php-7.3.10.tar.xz |
| │ └── php-fpm.sh |
| └── web.sh |
| |
| 2 directories, 7 files |
| [root@Web1 web]# bash web.sh |
| vim web.sh |
| #!/bin/bash |
| cd nginx |
| bash nginx.sh |
| cd ../php-fpm |
| bash php-fpm.sh |
| vim nginx/nginx.sh |
| #!/bin/bash |
| DST="/apps" |
| [ -a nginx.conf ] || { echo ' the absence of nginx.conf' ;exit 1;} |
| [ -a nginx.service ] || { echo ' the absence of nginx.service' ;exit 2;} |
| [ -a /usr/lib/systemd/system/nginx.service ] && { echo ' nginx is aready installed' ;exit 3;} |
| id nginx &>/dev/null && { echo 'user nginx is exist' ; exit 4;} || useradd -r -s /sbin/nologin -u 80 nginx |
| yum install -y gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools iotop bc zip unzip zlib-devel bash-completion nfs-utils automake libxml2 libxml2-devel libxslt libxslt-devel perl perl-ExtUtils-Embed vim lrzsz tree psmisc wget || { echo 'Dependencies is not installed';exit 5;} |
| |
| mkdir -p /data/apps/nginx |
| ln -s /data/apps/ /apps |
| [ -a nginx-1.16.1.tar.gz ] || wget https://nginx.org/download/nginx-1.16.1.tar.gz |
| tar xvf nginx-1.16.1.tar.gz |
| cd nginx-1.16.1 |
| ./configure --prefix=$DST/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --with-select_module --with-file-aio |
| make -j 4 && make install |
| cp ../nginx.conf $DST/nginx/conf/ |
| cp ../nginx.service /usr/lib/systemd/system/ |
| systemctl enable --now nginx |
| systemctl status nginx |
| vim nginx/nginx.service |
| [Unit] |
| Description=The nginx HTTP and reverse proxy server |
| After=network.target remote-fs.target nss-lookup.target |
| [Service] |
| Type=forking |
| PIDFile=/apps/nginx/logs/nginx.pid |
| |
| |
| |
| ExecStartPre=/usr/bin/rm -f /apps/nginx/logs/nginx.pid |
| ExecStartPre=/apps/nginx/sbin/nginx -t |
| ExecStart=/apps/nginx/sbin/nginx |
| ExecReload=/bin/kill -s HUP $MAINPID |
| KillSignal=SIGQUIT |
| TimeoutStopSec=5 |
| KillMode=process |
| PrivateTmp=true |
| [Install] |
| WantedBy=multi-user.target |
| vim nginx/nginx.conf |
| user nginx; |
| worker_processes 2; |
| worker_cpu_affinity 0001 0010; |
| worker_priority -10; |
| error_log logs/error.log info; |
| error_log /apps/nginx/logs/error.log error; |
| events { |
| worker_connections 65536; |
| use epoll; |
| accept_mutex on; |
| multi_accept on; |
| } |
| http { |
| include mime.types; |
| default_type application/octet-stream; |
| log_format access_json '{"@timestamp":"$time_iso8601",' |
| '"host":"$server_addr",' |
| '"clientip":"$remote_addr",' |
| '"size":$body_bytes_sent,' |
| '"responsetime":$request_time,' |
| '"upstreamtime":"$upstream_response_time",' |
| '"upstreamhost":"$upstream_addr",' |
| '"http_host":"$host",' |
| '"uri":"$uri",' |
| '"domain":"$host",' |
| '"xff":"$http_x_forwarded_for",' |
| '"referer":"$http_referer",' |
| '"tcp_xff":"$proxy_protocol_addr",' |
| '"http_user_agent":"$http_user_agent",' |
| '"status":"$status"}'; |
| access_log /apps/nginx/logs/access_json.log access_json; |
| sendfile on; |
| keepalive_timeout 65 65; |
| server_tokens off; |
| charset utf-8; |
| gzip on; |
| server { |
| listen 80; |
| server_name www.example.net; |
| location = / { |
| root html; |
| index index.html index.htm; |
| } |
| error_page 500 502 503 504 /50x.html; |
| location = /50x.html { |
| root html; |
| } |
| location ~ ^/(status|ping)$ { |
| include fastcgi_params; |
| fastcgi_pass 127.0.0.1:9000; |
| fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name; |
| fastcgi_hide_header X-Powered-By; |
| } |
| location ~ \.php$ { |
| root html; |
| fastcgi_pass 127.0.0.1:9000; |
| fastcgi_index index.php; |
| fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; |
| include fastcgi_params; |
| } |
| } |
| } |
| vim php-fpm/php-fpm.sh |
| #!/bin/bash |
| DST="/apps" |
| [ -a php-*.tar.* ] || { echo ' the absence of php-7.3.10.tar.xz' ;exit 1;} |
| [ -a /usr/lib/systemd/system/php-fpm.service ] && { echo ' php-fpm is aready installed' ;exit 2;} |
| [ -a $DST/php* ] && { echo ' php is aready installed' ;exit 2;} |
| mkdir -p $DST/php |
| yum install libxml2-devel bzip2-devel libmcrypt-devel -y || { echo 'Dependencies is not installed';exit 5;} |
| |
| |
| tar xvf php-*.tar.* |
| cd php-7.3.10 |
| |
| ./configure \ |
| --prefix=$DST/php \ |
| --enable-mysqlnd \ |
| --with-mysqli=mysqlnd \ |
| --with-pdo-mysql=mysqlnd \ |
| --with-openssl \ |
| --with-freetype-dir \ |
| --with-jpeg-dir \ |
| --with-png-dir \ |
| --with-zlib \ |
| --with-libxml-dir=/usr \ |
| --with-config-file-path=/etc \ |
| --with-config-file-scan-dir=/etc/php.d \ |
| --enable-mbstring \ |
| --enable-xml \ |
| --enable-sockets \ |
| --enable-fpm \ |
| --enable-maintainer-zts \ |
| --disable-fileinfo |
| |
| make -j 4 && make install |
| |
| cp php.ini-production /etc/php.ini |
| cp sapi/fpm/php-fpm.service /usr/lib/systemd/system/ |
| |
| cp $DST/php/etc/php-fpm.conf.default $DST/php/etc/php-fpm.conf |
| cp $DST/php/etc/php-fpm.d/www.conf.default $DST/php/etc/php-fpm.d/www.conf |
| |
| sed -i 's@nobody@nginx@g' $DST/php/etc/php-fpm.d/www.conf |
| sed -i '/#/!s@index index.html index.htm@index index.php index.html index.htm@' $DST/nginx/conf/nginx.conf |
| sed -i 's@/scripts$fastcgi_script_name@$document_root$fastcgi_script_name@g' $DST/nginx/conf/nginx.conf |
| $DST/nginx/sbin/nginx -s reload |
| systemctl enable --now php-fpm |