2018年1月29日

Vagrant

Vagrant 是一個管理 Virtual Machine 的 command line utility 工具。最初支援 VirtualBox,但現在也支援了 docker、Parallels、VMWare。

安裝

先安裝 VirtualBox: Download VirtualBox

然後在 Download Vagrant,根據不同 OS 下載 binary 套件直接安裝就好了。

vagrant CLI

vagrant 本身是一個 CLI tool,目的是為了能在 script 直接操作並使用 VM,能夠讓系統安裝及測試自動化。

如果需要一個的 CentOS 7 VM 可以用以下指令處理,vagrant 有個集中分享的 Vagrant Cloud repository,我們可以直接搜尋centos7 找到適合與最多人使用的 VM。

我們使用 geerlingguy/centos7 Vagrant box VM

mkdir vagrant_centos7
cd vagrant_centos7

# 以 init 指令下載 centos 7 Vagrantfile
vagrant init geerlingguy/centos7

# 啟動 VM
vagrant up

# VM 關機
vagrant halt

# 刪除 VM
vagrant destroy

# 查詢 VM 狀態
vagrant status

# 查看 ssh 資訊
vagrant ssh-config

其他常用的指令

# 列出所有 vagrant VMs
vagrant box list

# guest OS 及 host OS 的 port 對應表
vagrant port
    22 (guest) => 2222 (host)

# 以 ssh 登入 VM
vagrant ssh

Vagrantfile

剛剛使用的 CentOS 7 VM Vagranfile source code 也是透過定義 Vagrantfile 再分享到 Cloud。

而我們的 Vagrantfile 只需要這樣的設定,就能夠引用 geerlingguy 的 centos7

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

Vagrant.configure("2") do |config|
  config.vm.box = "geerlingguy/centos7"
end

被引用的 Vagratfile 定義會被下載到 ~/.vagrant.d 這個目錄中,可在 boxes 目錄找到相關資訊

~/.vagrant.d/boxes/geerlingguy-VAGRANTSLASH-centos7

Vagrantfile 是採用 ruby 語法,當我們在某個目錄 (/Users/user/VirtualBoxVMs/vagrant_centos7/) 執行 vagrant 指令時,他會依照以下順序尋找 Vagrantfile

/Users/user/VirtualBoxVMs/vagrant_centos7/Vagrantfile
/Users/user/VirtualBoxVMs/Vagrantfile
/Users/user/Vagrantfile
/Users/Vagrantfile
/Vagrantfile

  • Configuration Version

Vagrant.configure 後面的數字,代表不同版本的 vagrant 語法,

  1. "1" 表示為 1.0.x 的語法
  2. "2" 表示為 1.1+ 到 2.0 版的語法
Vagrant.configure("2") do |config|
  # ...
end

  • Networking

VM 的網路設定決定了 VM 跟 host machine 的溝通介面,網路部分分為三種,Forwarded Ports、Private Network及 Public Network。

Forwarded Ports: forwarded_port

可開放 guest machine 的某個 Port,並轉換到 host machine 的另一個 Port,TCP 或 UDP 都可以。

guest machine 啟動了一個 web server,運作在 TCP Port 80,可 forward 到 host machine 的 TCP 8080

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

跟上面一樣,將 TCP 80 forward 到 TCP 8080,同時限制只能用 127.0.0.1 存取 8080 這個 forwarded port 設定,也可以加上 TCP/UDP 的 protocol 限制,預設為 TCP。

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

config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1", protocol: "tcp"

Private Network

可透過 Internet 無法使用的 IP 存取 guest machine,在 VM 的網路設定通常稱為 NAT

  1. DHCP

    config.vm.network "private_network", type: "dhcp"
  2. Static IP

    config.vm.network "private_network", ip: "192.168.50.4"
  3. IPv6

config.vm.network "private_network", ip: "fde4:8dba:82e1::c4"

# 加上 netmask
config.vm.network "private_network",
    ip: "fde4:8dba:82e1::c4",
    netmask: "96"

Public Network

在 VM 的網路設定通常稱為 Bridge network,就是讓 VM 直接取得可讓其他機器存取的網路設定。

  1. DHCP

    config.vm.network "public_network"
    
    # 使用 DHCP 的設定作為 default route
    config.vm.network "public_network",
      use_dhcp_assigned_default_route: true
  2. Static IP

    config.vm.network "public_network", ip: "192.168.0.17"

    如果 host machine 有多個網路介面,在啟動 vagrant VM 時,會詢問要使用哪一個網路介面,可以在設定 Public Netork 時,直接決定是用哪一個網路介面。

config.vm.network "public_network", bridge: "en1: Wi-Fi (AirPort)"

# 可指定多個網路介面
config.vm.network "public_network", bridge: [
  "en1: Wi-Fi (AirPort)",
  "en6: Broadcom NetXtreme Gigabit Ethernet Controller",
]

可利用 shell 指令設定 ip

Vagrant.configure("2") do |config|
  config.vm.network "public_network", auto_config: false

  # manual ip
  config.vm.provision "shell",
    run: "always",
    inline: "ifconfig eth1 192.168.0.17 netmask 255.255.255.0 up"

  # manual ipv6
  config.vm.provision "shell",
    run: "always",
    inline: "ifconfig eth1 inet6 add fc00::17/7"
end

設定固定 IP 以及 default route

Vagrant.configure("2") do |config|
  config.vm.network "public_network", ip: "192.168.0.17"

  # default router
  config.vm.provision "shell",
    run: "always",
    inline: "route add default gw 192.168.0.1"

  # default router ipv6
  config.vm.provision "shell",
    run: "always",
    inline: "route -A inet6 add default gw fc00::1 eth1"

  # delete default gw on eth0
  config.vm.provision "shell",
    run: "always",
    inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
end

  • Synced Folders

可在 guest 及 host machine 之間共用資料夾,vagrant 預設會分享 project 目錄,也就是存放 Vagrantfile 的目錄到 /vagrant。

# 前面是 host machine folder,後面是 guest machine path
config.vm.synced_folder "src/", "/srv/website"

# disable default /vagrant shared folder
config.vm.synced_folder ".", "/vagrant", disabled: true

# 修改 folder owner, group
config.vm.synced_folder "src/", "/srv/website",
  owner: "root", group: "root"

也可以使用 NFSRSync或是 SMB 這三種 protocol


  • Provisioning

這是在啟動 VM 時,自動安裝軟體、修改設定的功能。當 VM box 需要微調時,可以利用這個功能進行調整。Provisioning 可使用 shell script, ansible, chef, puppet, salt, docker 等指令,比較基本的是直接用 shell scripts

Provisioning 會在這三個時間點發生作用

  1. 第一次以 vagrant up 啟動 VM,Provisioning 會有作用,但如果是已經啟動過的 VM,就不會執行 provisioning,但可用 --provision 強制執行。

  2. 在 VM 運作中,執行 vagrant provision

  3. 執行 vagrant reload --provision


Vagrant Shell provisioner 可在 guest machine 執行某個 script。

Inline Scripts

直接在 Vagrantfile 撰寫 scipt command

Vagrant.configure("2") do |config|
  config.vm.provision "shell",
    inline: "echo Hello, World"
end

在上面定義 script,然後在 config.vm.provision 中執行

$script = <<SCRIPT
echo I am provisioning...
date > /etc/vagrant_provisioned_at
SCRIPT

Vagrant.configure("2") do |config|
  config.vm.provision "shell", inline: $script
end

External Script

可執行 host machine 的某一個 shell script,也可以用某個網址下載 script

Vagrant.configure("2") do |config|
  config.vm.provision "shell", path: "script.sh"
end

Vagrant.configure("2") do |config|
  config.vm.provision "shell", path: "https://example.com/provisioner.sh"
end

如果要執行 guest machine 的 script

Vagrant.configure("2") do |config|
  config.vm.provision "shell",
    inline: "/bin/sh /path/to/the/script/already/on/the/guest.sh"
end

Script Arguments

利用 args 指定 script 參數

Vagrant.configure("2") do |config|
  config.vm.provision "shell" do |s|
    s.inline = "echo $1"
    s.args   = "'hello, world!'"
  end
end

Vagrant.configure("2") do |config|
  config.vm.provision "shell" do |s|
    s.inline = "echo $1"
    s.args   = ["hello, world!"]
  end
end

  • Multi-Machines

Multi-Machines 功能可在一個 Vagrantfile 中定義多個 guest machines。

定義兩個機器,一台是 web,一台是 db

Vagrant.configure("2") do |config|
  config.vm.provision "shell", inline: "echo Hello"

  config.vm.define "web" do |web|
    web.vm.box = "apache"
  end

  config.vm.define "db" do |db|
    db.vm.box = "mysql"
  end
end

執行 vagrant up 會同啟動 web 及 db,也可以用 vagrant up web 只啟動 web

Reference

Vagrant Tutorial(1)雲端研發人員,你也需要虛擬機!

Vagrant Tutorial(2)跟著流浪漢把玩虛擬機

Vagrant Tutorial(3)細說虛擬機生滅狀態

Vagrant Tutorial(4)虛擬機,若即若離的國中之國

Vagrant Tutorial(5)客製化虛擬機內容的幾種方法

使用Vagrant進行伺服器環境部署