# 使用Vagrant部署kvm虚拟化(libVirt)

## 说明

Vagrant非常适合支持桌面级虚拟化VirtualBox，不过在生产环境中，通常会部署KVM或Xen环境。Vagrant通过libvirt支持不同的虚拟化环境，同样也包括了KVM／Qemu。Vagrant提供了易于部署和管理的包装，以便快速部署和方便管理VM。

> 本文是为了准备Ceph分布式存储系统实验环境，参考「Ceph Cookbook」部署的3节点Vagrant环境，采用KVM作为虚拟化平台。
>
> CentOS操作系统中安装KVM环境请参考 [在CentOS 7中部署KVM](https://github.com/huataihuang/cloud-atlas-draft/tree/6f3204fffc11cf006abd394631e2598d98b415c3/virtual/kvm/deployment_and_administration/deploy_kvm_on_centos/README.md)
>
> `vagrant-libvirt`插件

## 安装

* 安装vagrant

直接访问[Vargant官方下载](https://www.vagrantup.com/downloads.html)安装Vargant

```
wget https://releases.hashicorp.com/vagrant/1.8.7/vagrant_1.8.7_x86_64.rpm
sudo rpm -ivh vagrant_1.8.7_x86_64.rpm
```

如果是ubuntu，同样也需要从官网下载安装deb包安装（发行版包过于陈旧）

### 安装Vagrant Plugins

* `vagrant-libvirt`插件用于支持libvirt

```
vagrant plugin install vagrant-libvirt
```

> 需要安装好`libvirt-devel`才能安装`vagrant-libvirt`插件，否则提示以下错误

```
extconf.rb:73:in `<main>': libvirt library not found in default locations (RuntimeError)
```

* `vagrant-mutate`插件用于将官方的Vagrant guest box转换成KVM格式

```
vagrant plugin install vagrant-mutate
```

> 在[Vagrant官方镜像](https://atlas.hashicorp.com/boxes/search)中，也有部分发行版直接提供了`libvirt`版本

* `vagrant-rekey-ssh` - 由于官方的Vagrant boxes使用了内建的非安全的SSH key，所以我们可以使用`vagrant-rekey-ssh`插件来重新生成新的SSH key

```
vagrant plugin install vagrant-rekey-ssh
```

## 下载和安装虚拟机镜像

[Vagrant官方镜像](https://atlas.hashicorp.com/boxes/search) 提供了常用的主流发行版

### 使用vagrant-libvirt安装CentOS 7 box

* 初始化centos 7 box（CentOS提供了支持 vmware\_fusion, vmware\_desktop , virtualbox, libvirt 4种版本的镜像 [boxes  centos/7  box versions](https://atlas.hashicorp.com/centos/boxes/7)）

```
vagrant init centos/7
```

* Vagrant会尝试使用一个名为`default`的存储池，如果这个`default`存储池不存在就会尝试在`/var/lib/libvirt/images`上创建这个`defualt`存储池。CentOS7默认安装libvirt环境，已经在`/var/lib/libvirt/images`目录上创建了名为`images`的存储池，所以需要修改`Vagrantfile`配置文件中定义`provider`。

```
Vagrant.configure("2") do |config|
  ...
  config.vm.provider :libvirt do |libvirt|
    libvirt.storage_pool_name = "images"
  end
  ...
end
```

* 为告知Vagrant主机使用的provider是`vagrant-libvirt`而不是默认的`virtualbox`，设置环境变量（这样`vagrant up`命令就不需要加上`--provider libvirt`参数）

```
export VAGRANT_DEFAULT_PROVIDER=libvirt
```

* 启动安装

```
vagrant up
```

### 使用vagrant-libvirt安装CentOS 7 box的排查过程（供参考）

* CentOS提供了支持 vmware\_fusion, vmware\_desktop , virtualbox, libvirt 4种版本的镜像 [boxes  centos/7  box versions](https://atlas.hashicorp.com/centos/boxes/7)

以下为下载安装CentOS 7

```
vagrant init centos/7; vagrant up --provider libvirt
```

> 参考 [vagrant-libvirt/vagrant-libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt) 可以通过以下设置环境变量方法使得Vagrant将默认的VirtulBox更改为Libvirt（这样就不必每次都使用`--provider=libvirt`参数了）

```
export VAGRANT_DEFAULT_PROVIDER=libvirt
```

在CentOS 7上使用vagrant会提示一个报错

```
There was error while creating libvirt storage pool: Call to virStoragePoolDefineXML failed: operation failed: Storage source conflict with pool: 'images'
```

参考 [ERROR: Call to virStoragePoolDefineXML failed: operation failed: pool 'default' already exists](https://github.com/vagrant-libvirt/vagrant-libvirt/issues/536)和 [vagrant up - always returns virStoragePoolCreateXML failed: storage conflict](https://github.com/vagrant-libvirt/vagrant-libvirt/issues/184) :

> Vagrant会尝试使用一个名为`default`的存储池，如果这个`default`存储池不存在就会尝试在`/var/lib/libvirt/images`上创建这个`defualt`存储池。
>
> [CentOS 7部署kvm](https://github.com/huataihuang/cloud-atlas-draft/tree/6f3204fffc11cf006abd394631e2598d98b415c3/virtual/vagrant/deploy_kvm_on_centos/README.md)使用SELinux增强模式，不能使用非默认的目录来创建VM镜像。

使用 `virsh pool-list --all` 可以看到当前系统的存储池

```
 Name                 State      Autostart
-------------------------------------------
 huatai               active     yes
 images               active     yes
 root                 active     yes
```

[error when default storage pool is missing](https://github.com/simon3z/virt-deploy/issues/8)提供了一个解决的方法

* 首先检查`virsh pool-list` 确保已经定义了

```
 Name                 State      Autostart 
-------------------------------------------
 default              active     yes
```

可以尝试定义一个名为`default`的存储池并启动这个`default`存储池

```
virsh pool-define /dev/stdin <<EOF
<pool type='dir'>
  <name>default</name>
  <target>
    <path>/var/lib/libvirt/images</path>
  </target>
</pool>
EOF

virsh pool-start default
virsh pool-autostart default
```

这里我发现确实不能重复定义，在执行第一个`virsh pool-define /dev/stdin <<EOF`就提示相同错误（表明Vagrant确实在启动时如果找不到名为`default`存储池就会尝试在`/var/lib/libvirt/images`目录创建该存储池）

```
error: Failed to define pool from /dev/stdin
error: operation failed: Storage source conflict with pool: 'images'
```

通过 `virsh help | grep pool` 可以看到`virsh`提供了`pool-dumpxml`可以用来检查各个存储池的定义。我发现原来系统为每个用户（这里是`root`用户和`huatai`用户）各自定义了一个存储池位于其`HOME`目录。例如`huatai`用户定义的存储池 `virsh pool-dumpxml huatai` 显示如下

```
<pool type='dir'>
  <name>huatai</name>
  <uuid>d367d5bd-9a5c-42ad-b376-78f034eae621</uuid>
  <capacity unit='bytes'>52710469632</capacity>
  <allocation unit='bytes'>18440990720</allocation>
  <available unit='bytes'>34269478912</available>
  <source>
  </source>
  <target>
    <path>/home/huatai</path>
    <permissions>
      <mode>0700</mode>
      <owner>501</owner>
      <group>20</group>
    </permissions>
  </target>
</pool>
```

在 [error when default storage pool is missing](https://github.com/simon3z/virt-deploy/issues/8) rrasouli 提供了一个解决方法，就是删除掉`images`存储池，然后重新执行Vagrant部署，此时Vagrant就会去创建这个`default`存储池并且位于`/var/lib/libvirt/images`目录。

```
virsh pool-destroy images
virsh pool-undefine images
```

> 我检查了先前已经在运行的2个vm，发现原先创建的vm的`dumpxml`显示是直接使用`<source file='/var/lib/libvirt/images/centos7.img'/>`，所以理论上删除掉`images`这个存储池定义应该没有问题。但是，有没有方法直接定义Vagrant的存储池使用`images`而不是使用`default`呢？

仔细看了[vagrant-libvirt/vagrant-libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt)开源项目的文档说明（其实很多时候开源项目的文档仔细阅读能够解决绝大多数问题，而是自己猜测或者直接论坛提问），在`Libvirt`配置中，有关`Porvider Options`中可以设置多个连接选项，其中有一个连接无关的选项是`storage_pool_name`定义了Libvirt存储池名字，也就是box image和instance snapshoot存储位置，参考说明文档案例并结合当前`Vagrantfile`中有关`provider`配置注释，在这个`Vagrantfile`中添加配置

```
Vagrant.configure("2") do |config|
  ...
  config.vm.provider :libvirt do |libvirt|
    libvirt.storage_pool_name = "images"
  end
  ...
end
```

然后再次执行命令 `vagrant up --provider libvirt` 就可以完成基于[vagrant-libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt)的[CentOS 7 box](https://atlas.hashicorp.com/centos/boxes/7)安装。

## Vagrant网络

前面解决了Vagrant的存储问题，但是Vagrant的虚拟机启动后会遇到网络连接问题

* 首先遇到`vagrant up`启动设置防火墙报错

```
Error while activating network: Call to virNetworkCreate failed: internal error: Failed to apply firewall rules /usr/sbin/ip6tables --table filter --insert FORWARD --in-interface virbr1 --jump REJECT: ip6tables v1.4.21: can't initialize ip6tables table `filter': Address family not supported by protocol
Perhaps ip6tables or your kernel needs to be upgraded.
```

参考 [libvirt via virt-manager virtual network start failed](https://bbs.archlinux.org/viewtopic.php?id=198744) 和 [libvirt: “Failed to initialize a valid firewall backend”](http://superuser.com/questions/1063240/libvirt-failed-to-initialize-a-valid-firewall-backend) ，需要执行以下两个动作：

* 安装并启动 `firewalld` （实际系统已经安装但是没有启动，需要通过`systemctl status firewalld`确认）

```
yum install firewalld
systemctl start firewalld
```

* 重启一次 `libvirtd` （重启以后才能识别firewalld）

```
systemctl restart libvirtd
```

> 此时再起启动 `vagrant up` 则可以看到启动后进入网络连接

```
#vagrant up
Bringing machine 'default' up with 'libvirt' provider...
==> default: Starting domain.
==> default: Waiting for domain to get an IP address...
```

但是最后无法获得IP地址退出，显示如下

```
==> default: Removing domain...
/root/.vagrant.d/gems/gems/fog-core-1.43.0/lib/fog/core/wait_for.rb:9:in `block in wait_for': The specified wait_for timeout (2 seconds) was exceeded (Fog::Errors::TimeoutError)
```

在这个过程中(即显示`default: Waiting for domain to get an IP address...`时)，使用 `virsh list` 可以看到服务器上有运行 `centos7_default` 虚拟机

```
#virsh list
 Id    Name                           State
----------------------------------------------------
 ...
 5     centos7_default                running
```

此时使用 `virsh console centos7_default` 连接这个Vagrant启动的虚拟机，可以通过默认密码登陆，但是我发现这个虚拟机内部没有虚拟网卡，只有 `lo` 回环接口

```
[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
[root@localhost ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```

此外，在物理服务器上查看，可以看到显示Vagrant启动的虚拟机连接到了 `virbr1` 这个虚拟网桥接口`virbr1-nic`接口（另外两个网桥接口 `docker0` 是 docker 服务使用，`virbr0` 是现前设置kvm使用），看来还需要排查Vagrant网络

```
#brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242019a7083    no
virbr0        8000.525400fa6cfe    yes        virbr0-nic
                            vnet0
                            vnet1
virbr1        8000.52540045088a    yes        virbr1-nic
```

## 参考

* [Vagrant and libVirt(KVM/Qemu) - Setting up boxes the easy way](http://www.lucainvernizzi.net/blog/2014/12/03/vagrant-and-libvirt-kvm-qemu-setting-up-boxes-the-easy-way/)
* [how to create custom vagrant box from libvirt/kvm instance?](http://unix.stackexchange.com/questions/222427/how-to-create-custom-vagrant-box-from-libvirt-kvm-instance)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huataihuang.gitbook.io/cloud-atlas-draft/virtual/vagrant/vagrant_libvirt_kvm.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
