Vagrant 虚拟机管理

发布时间 2024-01-08 17:44:55作者: 浮沉丶随心

一 、 安装

  • Vagrant 2.4.0

下载地址:https://developer.hashicorp.com/vagrant/downloads

  • VirtualBox 7.0.12

下载地址:https://www.virtualbox.org/wiki/Downloads

二 、 类比docker

image.png

Vagrant Box

image.png

Box是Vagrant创建虚拟机使用的包格式,它是一个预先配置好的运行环境。

Box File实际上是一个压缩文件(tar,zip,tar.gz),它通常包含了虚拟机镜像、元数据、描述信息、配置文件。

。不同的虚拟化平台需要使用不同的Box。如果在ARM架构下创建虚拟机,需要使用arm64架构的Box文件。

我们可以从Vagrant Cloud网站上找到支持不同虚拟化平台的Box文件。

Vagrant Cloud不像Docker Hub,没有完整的镜像站。

国内源

https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cloud-images/jammy/20230929/jammy-server-cloudimg-amd64-vagrant.box

image.png

三 、 使用

3.1 简单使用

比如我们要创建ubuntu 22.04的虚拟机,有以下两种方式:

命令行

vagrant init ubuntu/jammy64
vagrant up

配置文件(Vagrantfile)

创建Vagrantfile文件,然后使用Vagrant up命令启动。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
end

3.2 创建过程

image.png

创建和启动虚拟机主要有下面几个步骤:

  1. 配置网络
  2. 转发SSH端口
  3. 配置ssh用户和密钥对
  4. 挂载共享目录

配置网络

vagrant总是会将虚拟机的第一个网卡设置为NAT。NAT只能用于虚拟机向外访问,通常用来访问互联网,主机不能访问虚拟机,虚拟机与虚拟机之间也不能通信

转发SSH端口

NAT无法从主机访问虚拟机,为什么可以使用vagrant ssh连接到虚拟机呢?

这是因为vagrant将虚拟机的ssh端口22转发到宿主机2222端口。如果有多个虚拟机,每个虚拟机会转发到不同的端口。

通过vagrant ssh连接到虚拟机跟使用下列命令是一样的。

ssh vagrant@localhost -p 2222

配置SSH用户

vagrant默认为每个虚拟机创建一个vagrant用户,密码为vagrant

这个用户拥有sudo权限,默认情况下不启动root用户。

配置SSH密钥对

vagrant ssh命令不需要输入密码,通过密钥对免密登录虚拟机。

所有的虚拟机Box打包的时候都使用同一个公开的密钥对。这样子显然是不安全的。

内置的密钥对仅用于第一次创建虚拟机的时候,vagrant会通过这个密钥对登录进虚拟机。然后生成一个新的密钥对,并替换公开的密钥对。

挂载共享目录

最后,vagrant会将当前目录设置为共享目录,挂载到虚拟机的/vagrant目录下。

更新Guest Additions

如果要去掉guest additions版本不匹配的提示信息,可以手动更新

vagrant plugin install vagrant-vbguest

3.3 Vagrantfile文件

用于配置和管理虚拟机文件,它使用ruby语法,用于定义虚拟机的配置选项,例如虚拟机的操作系统、网络设置、共享文件夹等。

运行vagrant init命令时,会在当前目录生成Vagrantfile配置文件,默认的配置文件如下:

Vagrant.configure("2") do |config|
	config.vm.box = "ubuntu/jammy64"
end

在线

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
  config.vm.box_url = "https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cloud-images/jammy/20230929/jammy-server-cloudimg-amd64-vagrant.box"
end

离线

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
  config.vm.box_url = "file://tmp/ubuntu-2204.box"
end

配置环境变量

ENV["LC_ALL"] = "en_US.UTF-8"

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
end  

3.4 Provider和Provision

image.png

Vagrantfile主要用来配置Provider(虚拟化提供者)和Provision(配置管理器)

Vagrant支持不同的虚拟化平台,默认使用VirtualBox,同时官方也支持Vmware(需要安装插件)、Hyper-V和Docker。

Provider用来适配不同的虚拟化平台。在不同平台下创建虚拟机需要使用不同的Provider,不同的Provider功能有些差异,一些配置选项也有所不同。

Provision是配置管理器,用于在虚拟机创建完成后,使用shell脚本或自动化运维工具配置环境、安装软件等。不同的配置工具需要使用不同的配置管理器。

Vagrant Provider 虚拟化提供者

primary disk默认是40G,硬盘容量不能比默认值小。

Vagrant.configure("2") do |config|
	config.vm.box = "unbuntu/jammy64"
	
	# 规格配置
	config.vm.provider : virtualbox do |box|
		vbox.name	= "ubuntu-22.04"
		vbox.cpus	= 2
		vbox.memory = 2048
		vbox.customize ['modifyvm', :id, '--graphicscontroller', 'vmsvga']
	end
	
	config.vm.hostname ="ubuntu-1"
	config.vm.disk :disk, size:"40GB",primary: true
	config.vm.disk :disk, size:"20GB",name: "extra_disk"
end

常用命令

  • vagrant reload 重新配置虚拟机
  • vagrant halt 关闭虚拟机
  • vagrant destroy 删除虚拟机
  • vagrant suspend 休眠
  • vagrant resume 唤醒

VMware

vagrant plugin install vagrant-vmware-desktop
config.vm.provider "vmware_desktop" do |v|
  v.vmx["memsize"] = "1024"
  v.vmx["numvcpus"] = "2"
end

Vagrant.configure("2") do |config|

  config.vm.box = "ubuntu/jammy64"

  config.vm.provider :virtualbox do |vbox|
    vbox.cpus    = 2
    vbox.memory  = 2048
  end

  config.vm.disk :disk, size: "40GB"

end

配置默认的provider

export VAGRANT_DEFAULT_PROVIDER=vmware_desktop

Vagrant Provision 配置管理器

虚拟机创建完成后,通常无法直接满足我们的需求。

Provision配置管理器可以在创建虚拟机的过程中修改配置、自动安装软件等。

这个是自动化的,不需要人工干预,只需一条命令就可以创建一个开箱即用的虚拟环境。

配置管理器支持Chef、Ansible、Puppet、Salt等自动化工具,通常我们使用shell脚本就够了

修改密码

vagrant用户默认的密码是vagrant,这是不安全的,因此vagrant默认不启动SSH密码登录。

通过provision修改密码并开启SSH密码登录。

Vagrant.configure("2") do |config|
  
  config.vm.box = "ubuntu/jammy64"
  
  # 规格配置
  config.vm.provider :virtualbox do |vbox|
    vbox.name    = "ubuntu-22.04"
    vbox.cpus    = 2
    vbox.memory  = 2048
    vbox.customize ['modifyvm', :id, '--graphicscontroller', 'vmsvga']
  end

  # 修改hostname,更新/etc/hosts
  config.vm.hostname = "ubuntu-1"
  # 设置系统盘容量
	config.vm.disk :disk, size: "40GB", primary: true
  # 新建一个硬盘
  config.vm.disk :disk, size: "20GB", name: "extra_disk"

  # 修改vagrant用户密码
  config.vm.provision "shell", inline: "echo vagrant:Abc123 | sudo chpasswd"
  
end

['modifyvm', :id, '--graphicscontroller', 'vmsvga']:这是传递给 vbox.customize 命令的参数数组,它指定了要进行的具体定制化操作。

  • 'modifyvm':这是 VirtualBox 的一个命令行工具 VBoxManage 的命令,用于修改已存在的虚拟机的设置。
  • :id:这是一个特殊的占位符,Vagrant 在执行时会自动将其替换为当前虚拟机的 ID。
  • '--graphicscontroller':这是 VBoxManage modifyvm 命令的一个选项,用于指定虚拟机使用的图形控制器类型。
  • 'vmsvga':这是 --graphicscontroller 选项的参数,指定使用 VMWare SVGA 图形控制器。这通常用于改善虚拟机的图形性能,特别是在需要更好的 3D 加速时。

启动SSH密码登录

provision也可以执行shell脚步文件

# 启用ssh密码认证
echo "==> Enable ssh password authentication"
sed -i 's/^PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl reload sshd
# 修改vagrant用户密码
config.vm.provision "shell", path: "bootstrap.sh"

什么时候执行

  • 配置管理器只在第一次使用vagrant up创建环境时执行。
  • 如果虚拟机已经创建,vagrant upvagrant reload不会再次运行配置管理器,需要显式调用
vagrant up --provision
vagrant reload --provision

--手动执行
vagrant provision

3.5 网络

Vagrant 网络:

  • NAT(默认)
  • 私有网络
  • 共有网络

NAT 网络地址转换(默认)

Vagrant总是将虚拟机的第一个网卡设置为NAT(网络地址转发),。

NAT模式下,虚拟机只能单向访问外部网络(通常用来访问互联网),虚拟机对外部互联网是不可见和无法访问的,虚拟机之间也是相互隔离的。

端口转发

在NAT模式下,如果想访问虚拟机中的端口,可以使用端口转发。

这种方式虽然可以访问虚拟机内的服务,但是不适合搭建分布式集群。

config.vm.network "forwarded_port", guest: 80, host: 8080

私有网络

私有网络可以实现虚拟机之间的网络访问,在Virtual Box下,Vagrant将网卡设置为仅主机访问(Host-only)模式。

Host-only顾名思义,只能从宿主机访问虚拟机,无法从外部主机访问虚拟机。

config.vm.network "private_network", ip: "192.168.56.20"

VirtualBox默认将192.168.56.0/21范围内的地址分配给虚拟机。

查看IP地址或者DHCP自动获取

VBoxManage list hostonlynets
config.vm.network "private_network", type: "dhcp"

外部网络

如果要从别的主机进行访问,也可以设置公有网络。

在VirutualBox中,使用bridge网络实现。Bridge网络将虚拟机等同为网络中的真实主机,可以像访问其他主机一样访问虚拟机。

查看桥接网络接口:

VBoxManage list bridgedifs
## bridge的名称中不能包含中文,Vagrant无法识别,需要修改网络的名称。
config.vm.network "public_network", ip: "10.150.36.191", bridge: "en4: USB-eth"

image-20231228182029565

3.6 Vagrant SSH连接虚拟机

Vagrant会为每个虚拟机都生产一对密钥,这个存放在当前的.vagrant目录下。

在Windows下,ssh可能会报错或者假死,通常第一次创建虚拟机时会出现。

出现这种情况,在virtualbox界面删除虚拟机,重新创建虚拟机即可。

  • 与Docker Desktop有冲突

还有一种情况,Vagrant可能会与Docker Desktop有冲突,需要先卸载Docker Desktop,虚拟机创建完成后,再安装Docker Desktop。

免密登录

ssh vagrant@localhost -p 2200 -i .vagrant/machines/default/virtualbox/private_key

3.7 Vagrant 批量创建虚拟机

Vagrant.configure("2") do |config|

  config.vm.box = "ubuntu/jammy64"
  
  (1..3).each do |i|
    config.vm.define "node-#{i}" do |node|
      node.vm.provision "shell",
      inline: "echo hello from node #{i}"
    end
  end
 
end
vm_list = [
  { # hash map
    "name" => "node-1",
    "cpu" => "2",
    "mem" => "2048",
    "ip_addr" => "192.168.56.10"
  },
  {
    "name" => "node-2",
    "cpu" => "1",
    "mem" => "1024",
    "ip_addr" => "192.168.56.11"
  },
  {
    "name" => "node-3",
    "cpu" => "1",
    "mem" => "1024",
    "ip_addr" => "192.168.56.12"
  }
]

Vagrant.configure(2) do |config|

   config.vm.box = "ubuntu/jammy64"

   vm_list.each do |item|
        config.vm.define item["name"] do |node|

            node.vm.provider "virtualbox" do |vbox|
              vbox.name = item["name"];  # 虚拟机名称
              vbox.memory = item["mem"]; # 内存
              vbox.cpus = item["cpu"];   # CPU
            end
            # 设置hostanme
            node.vm.hostname = item["name"]
            # 设置IP
            node.vm.network "private_network", ip: item["ip_addr"]

        end
    end

end

命令后面加虚拟机的名字,可以单独操作虚拟机:

vagrant up node-1
vagrant halt node-1
vagrant reload node-1
vagrant destroy node-1
vagrant provision node-1

SSH的私钥也分别存放在.vagrant虚拟机名对应的目录下

ssh vagrant@192.168.56.10 -i .vagrant/machines/k3s-server/virtualbox/private_key

四 、 例子

Vagrant 自动部署k3s集群

1.集群规划

主机名 IP地址 类型
k3s-server 192.168.56.10 Control Plane 控制平面
k3s-agent1 192.168.56.11 工作节点
k3s-agent2 192.168.56.12 工作节点

2.Vagrantfile 配置文件

vm_list = [
  {
    "name" => "k3s-server",
    "cpu" => "2",
    "mem" => "2048",
    "ip_addr" => "192.168.56.10"
  },
  {
    "name" => "k3s-agent1",
    "cpu" => "1",
    "mem" => "1024",
    "ip_addr" => "192.168.56.11"
  },
  {
    "name" => "k3s-agent2",
    "cpu" => "1",
    "mem" => "1024",
    "ip_addr" => "192.168.56.12"
  }
]

Vagrant.configure(2) do |config|

   config.vm.box = "ubuntu/jammy64"

   vm_list.each do |item|
        config.vm.define item["name"] do |node|
            node.vm.provider "virtualbox" do |vbox|
              vbox.name = item["name"];  #虚拟机名称
              vbox.memory = item["mem"]; #内存
              vbox.cpus = item["cpu"];   #CPU
            end
            # 设置hostname
            node.vm.hostname = item["name"]
            # 设置IP
            node.vm.network "private_network", ip: item["ip_addr"]

            # 执行shell脚本
            node.vm.provision "shell" do |script|
              script.path = "k3s-install.sh"   #脚本路径
              script.args = [ item["name"] ]   #传递参数
            end
        end
    end

end

3.安装脚本

在安装脚本中,我们通过传入的参数,也就是虚拟机的名字,来执行不同的命令。

环境变量

自动创建K3s集群,需要配置下面两个环境变量:

K3S_TOKEN:预先约定好token,工作节点自动加入

K3S_URL:主节点API server的访问地址

由于预先分配了IP,所以可以确定API server的访问地址。

Flannel网络

K3s内置了flannel作为默认的网络插件(CNI)。

flannel默认是选择第一个网卡进行网络通信,由于Vagrant虚拟机的第一个网卡是NAT模式,虚拟机之间无法互相访问。因此,在安装k3s时,需要使用--flannel-iface将第二个网卡enp0s8设置为默认网络。

echo "==> k3s cluster settings:"
K3S_TOKEN=NEIwQ0IxMEUtNTA2MS00RE
K3S_URL=https://192.168.56.10:6443
FLANNEL_IFACE="enp0s8"
echo "    TOKEN: ${TOKEN}"
echo "    API_SERVER:${API_SERVER}"
echo "    FLANNEL_IFACE:${FLANNEL_IFACE}"

echo "==> disable firewall"
ufw disable

if [ $1 == "k3s-server" ]; then
    echo "==> install $1"
    curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
         INSTALL_K3S_MIRROR=cn \
         K3S_TOKEN=${TOKEN} \
         sh -s - server --flannel-iface ${FLANNEL_IFACE}
else
    echo "==> install $1"
    curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
         INSTALL_K3S_MIRROR=cn \
         K3S_URL=https://${API_SERVER}:6443 \
         K3S_TOKEN=${TOKEN} \
         sh -s - --flannel-iface ${FLANNEL_IFACE}
fi