超詳細的 Vagrant 上手指南

DavyCloud發表於2020-09-28

搭建 Linux 虛擬機器,別再用 VirtualBox 從 .iso 檔案安裝了。

概述

2020 年了,也許你已經習慣了 docker,習慣了在 XX 雲上快速建立雲主機,但是如果你想在個人電腦上安裝虛擬機器來搭建開發/測試環境,Vagrant 仍然不失最佳選擇。

文末有和 docker 的對比說明。

安裝軟體

安裝 VirtualBox

進入 VirtualBox 的主頁,點選大大的下載按鈕,即可進入下載頁面。

VirtualBox 是一個跨平臺的虛擬化工具,支援多個作業系統,根據自己的情況選擇對應的版本下載即可。

注意,除了主程式,還要把對應的擴充套件包程式也一併下載了。有些高階特性,比如 USB 3.0 等需要擴充套件包的支援。

download_virtualbox
download_virtualbox

在安裝完主程式後,直接雙擊擴充套件包檔案即可安裝擴充套件包。

下載頁面先別關,後面還要用到。

安裝 Vagrant

Vagant 網站下載最新的版本,根據自己的作業系統選擇對應的版本下載即可。

注意,Vagrant 是沒有圖形介面的,所以安裝完成後也沒有桌面快捷方式。具體使用方法,接下來會詳細說明。

Vagrant 的安裝程式會自動把安裝路徑加入到 PATH 環境變數,所以,這時候可以通過命令列執行 vagrant version 檢查是否安裝成功:

> vagrant version
Installed Version: 2.2.7
Latest Version: 2.2.8

配置虛機存放位置

建立虛擬機器會佔用較多的磁碟空間,在 Windows 系統下預設的虛機建立位置是在 C 盤,所以最好配置到其它地方。

配置 VirtualBox

啟動 VirtualBox 後,通過選單 管理 -> 全域性設定,或者按下快捷鍵 Ctrl + g,在全域性設定對話方塊中,修改 預設虛擬電腦位置,指定一個容量較大的磁碟。

VirtualBox 預設虛機位置
VirtualBox 預設虛機位置

配置 Vagrant

通過 Vagrant 建立虛機需要先匯入映象檔案,也就是 box,它們預設儲存的位置在使用者目錄下的 .vagrant.d 目錄下,對於 Windows 系統來說,就是 C:\Users\使用者名稱\.vagrant.d

如果後續可能會用到較多映象,或者你的 C 盤空間比較緊缺,可以通過設定環境變數 VAGRANT_HOME 來設定該目錄。

在 Windows 系統中,可以這樣操作:新建系統環境變數,環境變數名為 VAGRANT_HOME,變數值為 E:\VirtualBox\.vagrant.d

Vagrant_Home 環境變數
Vagrant_Home 環境變數

注意,最後這個 .vagrant.d 目錄名稱不是必須的,但是建議保持一致,這樣一眼看上去就能知道這個目錄是做什麼用處的了。

下載虛機映象

使用 Vagrant 建立虛機時,需要指定一個映象,也就是 box。開始這個 box 不存在,所以 Vagrant 會先從網上下載,然後快取在本地目錄中。

Vagrant 有一個映象網站,裡面列出了都有哪些映象可以用,並且提供了操作文件。

但是這裡預設下載往往會比較慢,所以下面我會介紹如何在其它地方下載到基礎映象,然後按照自己的需要重置。如果網速較好,下載順利的朋友可以選擇性地跳過部分內容。

下面我給出最常用的兩個 Linux 作業系統映象的下載地址:

CentOS

CentOS 的映象下載網站是: http://cloud.centos.org/centos/

在其中選擇自己想要下載的版本,列表中有一個 vagrant 目錄,裡面是專門為 vagrant 構建的映象。選擇其中的 .box 字尾的檔案下載即可。這裡可以使用下載工具,以較快的速度下載下來。

這裡我們選擇下載的是 CentOS 7 的最新版本

Ubuntu

Ubuntu 的映象下載網站是: http://cloud-images.ubuntu.com/

同樣先選擇想要的版本,然後選擇針對 vagrant 的 .box 檔案即可。

如果這裡官網的速度較慢,還可以從 清華大學的映象站 下載。

下面的例子以 CentOS 7 為例,使用其它版本作業系統的也可以參考。

新增 box

接下來我們需要將下載後的 .box 檔案新增到 vagrant 中。

Vagrant 沒有 GUI,只能從命令列訪問,先啟動一個命令列,然後執行:

$ vagrant box list
There are no installed boxes! Use `vagrant box add` to add some.

提示現在還沒有 box。如果這是第一次執行,此時 VAGRANT_HOME 目錄下會自動生成若干的檔案和資料夾,其中有一個 boxes 資料夾,這就是要存放 box 檔案的地方。

執行 vagrant box add 命令新增 box:

$ vagrant box add e:\Downloads\CentOS-7.box --name centos-7
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'centos-7' (v0) for provider:
    box: Unpacking necessary files from: file:///e:/Downloads/CentOS-7.box
    box:
==> box: Successfully added box 'centos-7' (v0) for 'virtualbox'!

命令後面跟著的是下載的檔案路徑,並且通過 --name centos-7 為這個 box 指定一個名字。

後面建立虛機都需要指定這個名字,所以儘量把名字取得簡短一點,同時也要能標識出這個映象的資訊(我們後面會定製自己的基礎映象,所以這裡可以簡單點)。

再次查詢,可以看到有了一個 box:

$ vagrant box list
centos-7 (virtualbox, 0)

Vagrant 基本操作

新建虛機

建立一個目錄,先執行 vagrant init

$ mkdir demo
$ cd demo
$ vagrant init centos-7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

其中的 centos-7 就是我們要使用的 box 名字。

這個命令只是為我們生成一個 Vagrantfile,所以,這裡的名字沒指定或者寫錯了都沒關係,後面會介紹如何編輯這個 Vagrantfile 來修改。

啟動虛機

我們等會再來細看這個檔案,現在直接按照提示執行 vagrant up

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos-7'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: demo_default_1588406874156_65036
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key

正常的情況下,不到一分鐘應該就能啟動成功了。

這裡我遇到點問題,2222 埠轉發出現未知錯誤,造成 vagrant 的啟動超時。檢查了這個埠並沒被佔用,同時更改大一點的埠可以轉發成功,所以應該是系統哪裡有點問題。在後面我介紹瞭如何處理,如果你也遇到和我一樣的情況,可先跳到下面檢視。

注意到這裡包含的資訊:

  • 虛機名稱:demo_default_1588406874156_65036(想改?最後有提)
  • 網路卡:Adapter 1: nat,第一塊網路卡,NAT 模式,這是固定的
  • 埠轉發:22 (guest) => 2222 (host) (adapter 1),把虛機的 22 埠,對映到宿主機的 2222 埠上,這樣就可以通過 127.0.0.1:2222 訪問虛擬機器了
  • SSH 使用者名稱:vagrant,這裡使用 private key 登入

密碼也是 vagrant,但是密碼方式僅供直接登入,是不能通過 SSH 登入的。

檢視虛機狀態

執行下面的命令可以檢視虛機的狀態:

vagrant status

Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

該命令還提示瞭如何操作虛機,我們繼續一一介紹

連線虛機

如果啟動沒問題,接下來執行 vagrant ssh 就能以 vagrant 使用者直接登入虛機中。

root 使用者沒有預設密碼,也不能直接登入。需要 root 許可權的命令可以通過在命令前新增 sudo 來執行,也可以執行 sudo -i 直接切換到 root 使用者。

這時候開啟 VirtualBox 程式,可以看到自動建立的虛機:

Vagrant 建立的虛機
Vagrant 建立的虛機

我們也可以在 VirtualBox 的終端上登入系統,預設的登入使用者名稱和密碼都是 vagrant

當然還可以使用其它的 SSH 連線工具例如 XShell,SecureCRT 連線,但是這裡預設網路卡使用的是 NAT 模式,沒有指定 IP,實際應用並不方便,我們在後面介紹網路配置時再詳細介紹如何連線虛機。

停止虛機

執行下面的命令可以關閉虛機:

vagrant halt

直接在 VirtualBox 上關閉虛機,或者直接在虛機內部執行 poweroff 命令也都是可以的。

暫停虛機

執行下面的命令可以暫停虛機:

vagrant suspend

恢復虛機

執行下面的命令把暫停狀態的虛機恢復執行:

vagrant resume

注意: 不管虛機是關閉還是暫停狀態,甚至是 error 狀態,都可以執行 vagrant up 來讓虛機恢復執行。

過載虛機

執行下面的命令會重啟虛機,並且重新載入 Vagrantfile 中的配置資訊:

vagrant reload

刪除虛機

最後,執行下面的命令可以徹底刪除虛機,包括整個虛機檔案:

vagrant destroy

注意: 在當前這個小例子中,上面所有的 vagrant 命令都需要在 Vagrantfile 所在的目錄下執行。

初識 Vagrantfile

先來認識一下預設的 Vagrantfile 檔案,使用帶語法高亮的文字編輯器(例如 VSCode) 開啟:

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  config.vm.box = "centos-7"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via 127.0.0.1 to disable public access
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Enable provisioning with a shell script. Additional provisioners such as
  # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL
end

這是一個 Ruby 語法的檔案,因為 Vagrant 就是用 Ruby 編寫的。如果編輯器沒有語法高亮可以手動設定檔案型別為 Ruby。

這個預設檔案內容幾乎都是註釋,提示有哪些配置項可以修改,我們不需要去學 Ruby 程式設計也可以照葫蘆畫瓢的完成基本的配置。

當然,如果會 Ruby 程式設計的可以在此實現更高階的作用,但是絕大多數人用不著。

刨除註釋,這個檔案的實際生效內容實際只有 3 行:

Vagrant.configure("2") do |config|
  config.vm.box = "centos-7"
end

首尾兩行組成一個程式碼塊結構,不要去動它,除非你知道自己在幹什麼。我們平常只需要編輯這其中的配置項。

這裡的 config.vm.box 對應的就是虛機的映象,也就是 box 檔案,這是唯一必填的配置項。

特別提醒,Vagrantfile 檔名是固定的寫法,大小寫也要完全一樣,修改了就不認識了。

自定義配置 Vagrantfile

下面我將針對這份預設的 Vagrantfile 內容,逐個講解其中的配置含義和如何根據實際情況修改。

配置埠轉發

埠轉發(Port forward)又叫埠對映,就是把虛機的某個埠,對映到宿主機的埠上。這樣就能在宿主機上訪問到虛擬機器中的服務。

例如啟動虛機時,預設的 22 (guest) => 2222 (host) (adapter 1) 就是把虛機的 SSH 服務埠(22)對映到宿主機的 2222 埠,這樣直接在宿主機通過 ssh 客戶端訪問 127.0.0.1:2222 埠就等價於訪問虛擬機器的 22 埠。

下面這兩段配置就是教我們如何配置額外的埠轉發規則,例如把 Web 服務也對映出來:

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via 127.0.0.1 to disable public access
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

實際上設定埠轉發這個功能並不實用,一個很明顯的問題就是如果啟動多個虛機,很容易就出現宿主機上埠衝突的問題。即使沒有埠衝突,使用起來也不方便,我個人不推薦使用的,可以把這部分配置直接刪掉。直接使用下面的私有網路。

這個功能是虛擬機器軟體提供的,可以在虛機的網路卡設定中展開高階選項,找到相關的配置:

還有個地方需要注意,預設的 SSH 埠對映在這裡沒法直接修改。比如像我這樣,2222 埠出現莫名問題,如果想要把 22 埠轉發到其它埠如 22222,直接新增下面這樣的配置是沒用的:

  config.vm.network "forwarded_port", guest: 22, host: 22222

它會在原來的基礎上新加一個埠轉發規則,而不是替代原來的,必須要先強制關閉掉預設的那條規則:

  config.vm.network "forwarded_port", guest: 22, host: 2222, id: "ssh", disabled: "true"
  config.vm.network "forwarded_port", guest: 22, host: 22222

配置私有網路

下面這段配置用來配置私有網路,實際上對應的是 VirtualBox 的主機網路,也就是 HostOnly 網路。

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

取消註釋最下面一行,就可以為虛機設定指定的私有網路地址:

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

如果這個網段的主機網路在 VirtualBox 中不存在,Vagrant 會在啟動虛機時自動建立。所以,如果你想要利用已有的網路,請檢視現有主機網路配置:

檢視主機網路
檢視主機網路

最好這個網路也不要啟用 DHCP,完全由自己來分配地址,這樣更加清楚。

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

修改完成後,執行 vagrant reload 命令重建虛機,就能看到多出來的網路卡了。

私有網路實際也可以直接使用 DHCP,但是並不推薦:

  config.vm.network "private_network", type: "dhcp"

配置公共網路

下面這條配置用來配置公共網路:

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

正如註釋所說,這裡通常對應的就是橋接網路。實際開發場景下,我們極少會需要把虛機暴露到公共網路上,這樣既不安全,也沒有必要。

預設所起的第 1 個 NAT 網路已經保證了虛機可以上網際網路,而私有網路保證了宿主機和虛機,以及虛機和虛機之間的通訊。如果有對外暴露服務的需求,還可以使用埠轉發。我實在想不出什麼情況下是必須要用橋接網路的。

所以這部分配置可以直接刪除,如確有使用的,可以參考 官方文件

配置同步資料夾

下面的配置項用來配置同步資料夾:

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

在啟動虛機的時候,我們可以看到這樣的提示:

==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default:
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Configuring and enabling network interfaces...
==> default: Rsyncing folder: /cygdrive/c/Users/Davy/demo/ => /vagrant

先注意最後一行的提示:Rsyncing folder: /cygdrive/c/Users/Davy/demo/ => /vagrant

  • /cygdrive/c/Users/Davy/demo/ 這是宿主機的本地目錄,也就是 Vagrantfile 所在的目錄。
  • /vagrant 是虛擬機器內部的路徑
  • Rsyncing 表示同步的方式是 Rsync

宿主機目錄中出現 /cygdrive 是因為 Vagrant 程式用到了 Cygwin,它是在 Windows 系統中相容 Linux/POSIX 的模擬層。可以把 /cygdrive 看成是虛擬的根目錄。

這是 Vagrant 預設的同步資料夾設定,別忘了 Vagrant 的作用是用來搭建開發環境的。所以它假定了當前目錄是我們的開發專案所在目錄,自動把本地的專案目錄同步到虛機中,就可以快速的開始開發除錯工作了。

  1. demo 目錄下建立一些檔案,例如 hello.py
  2. 執行 vagrant reload,重啟虛機
  3. 在虛機啟動完成後登入到虛機內,操作如下:
$ vagrant ssh
Last login: Sat May  2 16:25:00 2020 from 10.0.2.2
[vagrant@localhost ~]$ cd /vagrant/
[vagrant@localhost vagrant]$ ls
hello.py  Vagrantfile
[vagrant@localhost vagrant]$ python hello.py
helloworld

這種同步方式在大多數情況下都能提供便利,不過也有不足之處:

  • 同步是一次性的,即只有啟動虛機的時候執行,也就是說改了程式碼必須要重啟一次虛機
  • 單向的,即只能從宿主機同步到虛擬機器,也就是說在虛機內的改動不會同步到外面
  • 需要拷貝檔案,如果要同步的檔案數量較多,會佔用更多的磁碟空間

讓我們按照預設配置的提示來新加一條同步資料夾配置試試:

config.vm.synced_folder "../data", "/vagrant_data"

注意,別忘了先在宿主機上建立 data 資料夾,重啟虛機可能看到下面的錯誤提示:

==> default: Rsyncing folder: /cygdrive/c/Users/Davy/demo/ => /vagrant
==> default: Mounting shared folders...
    default: /vagrant_data => C:/Users/Davy/data
Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:

mount -t vboxsf -o uid=1000,gid=1000 vagrant_data /vagrant_data

The error output from the command was:

mount: unknown filesystem type 'vboxsf'

這是因為 Vagrant 提供了多種同步方式,在使用 VirtualBox 的時候,預設同步型別是 vboxsf 掛載檔案系統,它需要在虛擬機器內部安裝客戶機增強包,也就是 VirtualBox Guest Additions(輸出資訊中也提示了)。

如何在虛機系統中安裝 guest additions 要分作業系統而定,有點小坑,後面會細說,現在修改一下配置,明確指定同步型別是 rsync

config.vm.synced_folder "../data", "/vagrant_data", type: "rsync"

這樣表示仍然使用 rsync 來單向同步。

更改虛機規格

VirtualBox 等虛擬機器軟體在 Vagrant 中被稱為 Provider,虛機的規格等配置是和 Provider 相關的。因為 VirtualBox 用的最多,所以預設的配置提示是以 VirtualBox 舉例。

如果想要了解其它 Provider 的配置,請參考 文件

把中間那一段取消註釋,其它的可以刪掉:

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

使用 VSCode 時,選中它們,然後按下快捷鍵 Ctrl + / 即可:

  config.vm.provider "virtualbox" do |vb|
    # Display the VirtualBox GUI when booting the machine
    vb.gui = true

    # Customize the amount of memory on the VM:
    vb.memory = "1024"
  end

vb.gui = true 是在虛機啟動時自動開啟 VirtualBox 的圖形介面,這對伺服器來說沒什麼用,直接刪掉。

新增 CPU 的配置,同時修改記憶體大小:

  config.vm.provider "virtualbox" do |vb|
    vb.cpus = 2
    vb.memory = 2048
  end

注意到,記憶體的大小單位是 MB,值是數字,預設的示例中有引號,實際也可以不加。

特別提醒一下,在 說明文件 裡給的例子,其中的變數名是 v,這其實是在雙豎線中定義的,直接拷貝的時候要看清楚。

config.vm.provider "virtualbox" do |v|
  v.memory = 1024
  v.cpus = 2
end

Provision

Provision 是指在虛機初次建立的時候,Vagrant 自動去執行的構造任務,比如安裝軟體,更新系統配置等。

因為 box 往往只提供基礎的系統(雖然我們可以自定義 box,但是並不是每次都要這麼做,而且這樣做會喪失一部分靈活性),有些東西仍然需要在建立虛機的時候完成。

  # Enable provisioning with a shell script. Additional provisioners such as
  # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL

因為這部分完全是個開放的內容,所以我們這裡不過多討論,來看一下什麼情況下會觸發 provision 的操作:

  • 某個環境初次執行 vagrant up 的時候
  • 執行 vagrant provision 命令
  • 重啟的時候 vagrant reload --provision,帶上 --provision 選項

除了上面這些預設提示給出的配置項,Vagrantfile 還支援其它很多配置,具體請 檢視文件

由於 Vagrantfile 本身是 Ruby 指令碼,所以它並不僅僅是靜態的配置檔案,而且可以包含程式邏輯,例如在 如何建立多個虛機 中就有應用,有興趣的可以自行研究。

定製帶客戶機增強的 box

下載 Guest Addition

VirtualBox 的下載頁面並沒有直接給出 Guest Addtion 的下載連結,我們先在 VirtualBox 的任一下載連結上右鍵,複製連結地址,例如得到 https://download.virtualbox.org/virtualbox/6.1.6/VirtualBox-6.1.6-137129-Win.exe,去掉最後的檔名,把其中的路徑 https://download.virtualbox.org/virtualbox/6.1.6/ 在瀏覽器中開啟,就能看到所有可下載的版本,在其中找到 VBoxGuestAdditions_6.1.6.iso 直接下載。

下載客戶機增強
下載客戶機增強

安裝 Guest Addition

重新使用 Vagrant 從原始的映象啟動一個乾淨的虛機。

VBoxGuestAdditions_6.1.6.iso 需要以光碟的形式掛載到虛機上,但是預設啟動的這個虛機是沒有光碟機的。新增虛擬光碟機需要先將虛機關閉。

然後在 VirtualBox 介面上操作,開啟 設定,選擇 儲存,點選 新增虛擬光碟機,點選 控制器: IDE,選擇 VBoxGuestAdditions_6.1.6.iso,點選 OK

直接在 VirtualBox 上啟動虛機,如果你在虛機選單上選擇 裝置 - 安裝增強功能,大概率是會遇到下面這樣的錯誤,別管它,我們手動來安裝。

VirtualBoxVM_安裝增強
VirtualBoxVM_安裝增強
VirtualBoxVM_安裝增強4
VirtualBoxVM_安裝增強4

使用 vagrant/vagrant 登入到虛機內,切換到 root 使用者,檢視虛擬光碟是否已經掛載上來了:

$ sudo -i
# lsblk
NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda      8:0    0  40G  0 disk
└─sda1   8:1    0  40G  0 part /
sr0     11:0    1  57M  0 rom

下面的 sr0 就是光碟裝置,要把它掛載到檔案系統中

# mount /dev/sr0 /mnt/
mount: /dev/sr0 is write-protected, mounting read-only
# cd /mnt/
# ls
AUTORUN.INF  cert  OS2           TRANS.TBL                VBoxDarwinAdditionsUninstall.tool  VBoxSolarisAdditions.pkg        VBoxWindowsAdditions.exe
autorun.sh   NT3x  runasroot.sh  VBoxDarwinAdditions.pkg  VBoxLinuxAdditions.run             VBoxWindowsAdditions-amd64.exe  VBoxWindowsAdditions-x86.exe

在 Linux 系統中要執行 VBoxLinuxAdditions.run,這時候直接執行仍然會報錯:

VirtualBoxVM_安裝增強4
VirtualBoxVM_安裝增強4

大致意思是要它需要核心的標頭檔案來構建。

標頭檔案通過安裝 kernel-devel 就可以了,但是直接安裝的版本是最新版本的,可能會當前的核心版本不一致,仍然是沒用的。例如:

[root@localhost mnt]# uname -r
3.10.0-1062.12.1.el7.x86_64
[root@localhost mnt]# yum info kernel-devel
Installed Packages
Name        : kernel-devel
Arch        : x86_64
Version     : 3.10.0
Release     : 1127.el7
Size        : 38 M
Repo        : installed
From repo   : base

這裡 kernel-develRelease 版本號和系統核心版本不一致,所以仍然不行。

需要先更新一把,然後再安裝(也可以先安裝再更新):

# yum update -y
# yum install -y gcc kernel-devel

這裡選擇的是更新到最新版本,如果你的開發環境需要特定的核心版本,你也可以根據情況安裝指定的版本,只要保證核心版本和標頭檔案版本完全匹配

Ubuntu 系統的安裝方式有所不同,可以參考 Vagrant 的文件

升級核心後需要重啟虛機,然後再次嘗試安裝 Additions:

[root@localhost mnt]# ./VBoxLinuxAdditions.run
Verifying archive integrity... All good.
Uncompressing VirtualBox 6.1.6 Guest Additions for Linux........
VirtualBox Guest Additions installer
Removing installed version 6.1.6 of VirtualBox Guest Additions...
Copying additional installer modules ...
Installing additional modules ...
VirtualBox Guest Additions: Starting.
VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel
modules.  This may take a while.
VirtualBox Guest Additions: To build modules for other installed kernels, run
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup <version>
VirtualBox Guest Additions: or
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup all
VirtualBox Guest Additions: Building the modules for kernel
3.10.0-1127.el7.x86_64.
VirtualBox Guest Additions: Running kernel modules will not be replaced until
the system is restarted

# 檢查模組是否已經載入了
[root@localhost mnt]# lsmod |grep vbox
vboxvideo              35867  1
ttm                    96673  1 vboxvideo
drm_kms_helper        186531  1 vboxvideo
drm                   456166  4 ttm,drm_kms_helper,vboxvideo
vboxguest             349038  1
[root@localhost mnt]#

能夠看到 vboxguest 就代表安裝成功了。

測試

在 Vagrantfile 中新增同步資料夾設定,這次不再指定同步型別

config.vm.synced_folder "../data", "/vagrant_data"

然後執行 vagrant reload

$ vagrant reload
...
==> default: Checking for guest additions in VM...
==> default: Rsyncing folder: /cygdrive/c/Users/Davy/demo/ => /vagrant
==> default: Mounting shared folders...
    default: /vagrant_data => C:/Users/Davy/data
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

可以看到關於 additions 的提示資訊沒有了,新的同步資料夾也能正常同步了,這次的同步是雙向的。我們可以先去同步資料夾隨便建立一個檔案:

$ vagrant ssh
Last login: Tue May  5 12:16:39 2020 from 10.0.2.2
[vagrant@localhost ~]$ cd /vagrant_data/
[vagrant@localhost vagrant_data]$ ls
data.txt
[vagrant@localhost vagrant_data]$ touch vm.txt   # 新建一個檔案
[vagrant@localhost vagrant_data]$ ls
data.txt  vm.txt

回到 Windows 宿主機上,data 資料夾下面也能看到 vm.txt 檔案,

Davy@Davy-Desktop MINGW64 ~/data
$ ls
data.txt  vm.txt

這樣,我們的 Guest Addition 就安裝成功了。

清理磁碟

接下來我們來把這個好不容易才裝上了增強包的虛機儲存為新的映象。

這裡你可能還想安裝點其它軟體,但是作為基礎映象,最好還是保持乾淨一點,不宜安裝太多東西。後續可以在這個基礎之上,再次構建其它特定的映象。

為了使做出來的映象檔案大小緊湊點,我們把剛才安裝過程中的快取也刪掉:

yum clean all

我在打包過程中還遇到過 swapfile 佔用磁碟空間的問題,導致映象檔案過大,可以這樣檢查:

du /swapfile   # 檢視是否有佔用
swapoff -a     # 關閉 SWAP
rm -f /swapfile

使用 df -h 命令檢視磁碟佔用情況,像我這次操作,磁碟根目錄不到 2GB 的空間佔用:

# df -h
/dev/sda1        40G  1.5G   39G   4% /

打包為 box

執行 vagrant package 就可以把當前環境打包生成新的 box 檔案:

$ vagrant  package
    ==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: C:/Users/Davy/demo/package.box

生成的檔案就在 Dockerfile 所在的目錄,檔名預設是 package.box。大小不到 600MB。

注意:,因為預設啟動虛機時本地目錄會 rsync 到虛機中,package.box 檔案也會同步(拷貝)到虛機中,佔用虛機磁碟。這時候如果二次執行打包,生成的檔案大小會翻倍。

新增到 vagrant 中

繼續使用下面的命令把新建的 box 新增到 vagrant 中:

$ vagrant box add package.box --name davy/centos-7-base

為了區分這是個人建立的基礎映象,加了個人使用者名稱作為字首,同時加了 base 字尾。

新增成功後,本地的 package.box 就可以刪除了。

後續再建立虛機,使用下面的命令就可以了:

$ vagrant init davy/centos-7-base

使用 SSH 客戶端

vagrant ssh 命令雖然很方便,但是在 Windows 環境下,因為預設的命令列終端不太好用,所以往往還需要使用更專業的 SSH 客戶端例如 XShell 或 SecureCRT。

預設的映象只支援 private_key 的方式登入,vagrant/vagrant 可以在 VirtualBox 上登入系統,但是如果用來登入 SSH,會被拒絕。

當然你可以在製作映象的時候修改 ssh 服務的配置,讓它能夠用密碼登入,但是實際上用金鑰更加方便。

先使用 vagrant ssh-config 命令可以看到 SSH 的配置:

$ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 22222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile E:/VirtualBox/.vagrant.d/boxes/davy-VAGRANTSLASH-centos-7-base/0/virtualbox/vagrant_private_key
  IdentitiesOnly yes
  LogLevel FATAL

可以看到其中的 IdentityFile 就是私鑰檔案。

發現這個自定義 box 啟動的虛機的金鑰檔案是固定在 VAGRANT_HOME 下的相關目錄下。那麼就好辦了,直接在 SSH 客戶端軟體上匯入這個私鑰檔案就可以了。

以 SecureCRT 為例:

SecureCRT_金鑰
SecureCRT_金鑰

使用模板檔案

vagrant init 命令只是用來生成 Vagrantfile 檔案,但是預設的配置選項每次都要修改也很麻煩。該命令提供了 --template 選項,可以指定一個模板檔案,我們可以在自定義自己的模板檔案。

這個模板檔案的格式 ERBRuby 的模板語法,如果有 Ruby on Rails 開發經驗的可能會比較熟悉。但是我們不用去學習這些細節。

可以從 這裡 拷貝一份原檔案,還能在 Vagrant 的安裝位置 Vagrant\embedded\gems\2.2.7\gems\vagrant-2.2.7\templates\commands\init 底下找到,並且有一個 Vagrantfile.min.erb 是去掉所有註釋的:

Vagrant.configure("2") do |config|
  config.vm.box = "<%= box_name %>"
  <% if box_version -%>
  config.vm.box_version = "<%= box_version %>"
  <% end -%>
  <% if box_url -%>
  config.vm.box_url = "<%= box_url %>"
  <% end -%>
end

可以看到其中是怎麼配置 config.vm.box 的。 像 <% 這樣的語法有興趣可以自己去了解,這裡我們只要把自己想要的配置項原樣寫上去就行了。

下面是我按照自己的需要寫的:

Vagrant.configure("2") do |config|
  config.vm.box = "<%= box_name %>"
  config.vm.network "forwarded_port", guest: 22, host: 2222, id: "ssh", disabled: "true"
  config.vm.network "forwarded_port", guest: 22, host: 22222

  # config.vm.network "private_network", ip: "192.168.56.10"
  # config.vm.synced_folder "../data", "/data"

  config.vm.provider "virtualbox" do |vb|
    # vb.name = "give me a better name"
    vb.memory = "1024"
  end
end

不要有中文,不然會遇到編碼的麻煩。

其中像私有網路和同步資料夾配置,幾乎每次基本都要,但是又不好固定,所以仍然以註釋的形式保留,每次稍微改一下也很方便。

把這個檔案找個目錄,儲存為 vagrant.erb

接著在使用 vagrant init 的時候通過 --tempate 指定它就可以了,例如:

vagrant init davy/centos-7-base --tempate "C:\Users\Davy\vagrant.erb"

顯然,每次要記住並且輸入這個模板檔案也很麻煩的。可以通過設定環境變數 VAGRANT_DEFAULT_TEMPLATE 來一勞永逸地解決這個問題。

尾聲

費了莫大的力氣,終於可以比較愉快地玩耍了。雖然也只是剛把基礎映象搞定了,後面可能還要針對不同用途的環境編寫更加複雜的 Vagrantfile。

現在很多人剛認識到 Vagrant 之後都會問,Vagrant 和 Docker 的區別是什麼?

在容器流行之前,Vagrant 就是用來編排虛機和自動部署開發環境的,有了 Docker/Kubernetes 之後,直接用容器來編排應用確實更香。但是還有一些工作,例如容器平臺自身的安裝,多節點叢集的部署測試等,更方便用虛機解決。

此外,現在 Windows 中還可以通過 WSL 使用 Linux 系統,但是使用場景上還是有所不同。Vagrant 更多地用於快速搭建可重用的開發環境,從這個角度看,Vagrant 其實好比 IaaS 雲平臺,只不過規模侷限在個人電腦上。


感謝您的閱讀,請繼續關注「雲端計算實驗室」。

本文使用 mdnice 排版

相關文章