Vagrant (三) - 網路配置

hedzr發表於2019-02-27

這個章節脫胎於我早期的blog文章,訂正了一下句法和準確性。

原始的文字在 VAGRANT 網路配置

整個Vagrant系列,已經有四篇文章了:

  1. Vagrant (一) - 基本知識
  2. Vagrant (二) - 日常操作
  3. Vagrant (三) - 網路配置
  4. Vagrant (四) - Box的用法

網路配置

Vagrant可以指定三種網路:埠轉發(Forwarded Ports),私有網路(Private Network),公有網路(Public Network)。他們和多數虛擬機器提供的網路是對應的。

Vagrant 網路模型

埠轉發:

    config.vm.network :forwarded_port, guest: 80, host: 8080
複製程式碼

將虛擬機器(被稱作guest)的埠80對映為宿主機的埠8080。

埠轉發隱含著一個provider的NAT網路將被首先建立。 所以,如果你單獨定義一條埠轉發的配置語句的話,VM將會自動建立NAT網路環境。

私有網路:

    config.vm.network :private_network, ip: "192.168.1.104"
複製程式碼

你可以從宿主機自由訪問虛擬機器,但LAN網路中的其他人不需要也無法訪問虛擬機器。

值得注意的是,ip地址“192.168.1.104”不是隨便指定的。 首先你可以不指定,這表示虛機啟動時會DHCP到一個可用的IP地址(例如:192.168.33.101),這是vagrant通過virtualbox私有網路的DHCP機制獲得的。 如果你要自行指定明確的IP地址,要保證該地址是在恰當的網段中,例如192.168.33.71。

多臺虛擬機器在私有網路模式下也可以互相訪問,只要設定為相同的網段就可以。

本質上說,這是使用provider的HostOnly模式。

公有網路:

公有網路實際上是表示將虛擬機器暴露為LAN(例如你的宿主機所在的辦公室網路)中的一臺主機。

例如使用LAN的DHCP自動獲得IP地址:

    config.vm.network :public_network
複製程式碼

也可以指定LAN網段中的一個可用的地址,但需要注意不要和LAN中已有的主機或者保留的IP地址相沖突。

本質上說,這是使用provider的橋接網路模式。

Provider的網路模式

對於vagrant的provider,例如VirtualBox來說,網路模式區分的更細,但vagrant並不能使用全部vbox網路模型。

VirtualBox 的典型網路模型:NAT,Hostonly,Bridge以及Internal。

這些模式的細節我們不再列舉。

借用一張表格來歸納:

NAT Bridged Internal Hostonly
vm -> host × ×
host -> vm × × ×
vm -> others hosts × ×
others hosts => vm × × ×
vm <-> vm × same subnet

這張表格描述了virtualbox的網路模型。

實用的網路配置

一般來說,埠轉發足以滿足開發需要了。

但對於特殊的需要來說,你可能需要一臺完全“真實”的虛機,這臺虛機可以被穩定地從宿主機訪問,並且可以訪問LAN中的其他資源。這樣的需求實際上可以通過配置多塊網路卡來解決問題,例如一塊配置為私有網路模式,一塊配置為公有網路模式。

vagrant通過配置檔案能夠支援virtualbox的NAT,Bridge以及Hostonly網路模型。

預設情況

預設情況下,我們已經知道一個最簡的流程來啟動vagrant:

    mkdir /dev
    cd /dev
    vagrant box add ubuntu/trusty64
    vagrant init ubuntu/trusty64
    vagrant up
    vagrant ssh
    vagrant halt
複製程式碼

這樣的步驟,可以得到一臺ubuntu 14.04的虛擬機器,採用Provider的NAT網路模式,在虛擬機器中可以訪問宿主機,也可以使用宿主機的外網路由上網。

觀察它生成的預設的Vagranfile,其網路配置是未指定的。

此時,vagrant建立的vm具有一個NAT網路卡。

橋接網路

當採用如下配置語句時,vagrant建立的vm具有一個Bridged網路:

# 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"
複製程式碼

此時,vm在宿主機所在的LAN中等價於一臺物理機器。假如你具有LAN Router的管理權的話,這是很簡單的一種開發模型:通過路由器的mac繫結為vm保留一個固定的dhcp地址,這樣vm無論何時啟動都會獲取到相同的IP地址,你的開發和除錯將會很順利很簡單。

私有網路

當採用如下配置語句時,vagrant建立的vm具有兩個hostonly網路:

config.vm.network "private_network", ip: "192.168.9.10"
config.vm.network "private_network", ip: "192.168.33.10"
複製程式碼

識別符號“private_network”總是被對映為virtualbox的hostonly模型。

私有網路模型是允許宿主機訪問的,為了便於宿主機訪問,我們也可以明確指定一個適當網段的地址。要知道適當的網段是多少,可以這樣查證:

  • 通過進入虛機顯示網路卡資訊和IP地址來了解網段。
  • 開啟 VirtualBox 的網路配置,檢查HostOnly網路的DHCP網段。

混合網路

當採用如下配置語句時,vagrant建立的vm具有一個NAT和一個hostonly網路:

config.vm.network "private_network", ip: "192.168.33.10"
複製程式碼

識別符號“private_network”總是被對映為virtualbox的hostonly模型。

注意 NAT 網路將被隱含地建立。 vagrant在建立網路卡時,如果配置檔案僅配置了一個private_network,則vagrant自動建立NAT網路卡,然後在建立配置檔案所描述的網路;而如果配置檔案指定了兩個以上的private_network的話,vagrant不再自動建立 NAT 網路卡了。

混合網路非常適合開發和測試環境,你可以通過NAT和Internet相通,然後多個vm之間也能相互通訊。

內外網路

內外網路 只是我隨便命名的,也就是從vm的角度出發既有內網(VM的私有網路),又有外網(宿主機所在的區域網)。

當採用如下配置語句時,vagrant建立的vm具有一個bridged和一個hostonly網路:

config.vm.network "public_network"
config.vm.network "private_network", ip: "192.168.33.10"
複製程式碼

這是比較通用的配置模式,vm既有host主機所在區域網的ip,又有一個私有網路的ip地址,因此這些vm之間具有全連通性。

不過,一般來說開發和測試使用較為封閉的網路模型是比較好的方式,通常不建議vm配置有 public_network 的網路卡。

小結

任何

config.vm.network "private_network", ip: "192.168.33.10"
複製程式碼

語句都可以改為:

config.vm.network "private_network", type: "dhcp"
複製程式碼

這時virtualbox的閘道器負責dhcp應答和分配IP。

例項

nginx服務

  1. 建立一個新的工作目錄 sample-nginx
  2. 使用 vagrant init ubuntu/xenial64 進行初始化
  3. 修改 Vagrantfile 加入必要的宣告 (見後)
  4. 使用 vagrant up 啟動該虛擬機器,然後可以SSH進入或者 curl -i http://localhost:8080/ 來嘗試訪問它

完整的 Vagrantfile 如下:

# -*- 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 = "ubuntu/xenial64"
  
  # 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
  # Puppet, Chef, Ansible, Salt, and Docker 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 nginx
  SHELL
end
複製程式碼

這是一個相當簡單的例項。更復雜的初始化,可以考慮使用一個獨立的指令碼檔案來完成,而不是直接放在 Vagrantfile 中。

Updates

有時候,可能遇到網路配置失敗的情況。例如:

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/bionic64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ubuntu/bionic64' is up to date...
==> default: Setting the name of the VM: setup_bionic_default_1511875921207_73707
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> 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
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Connection reset. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default: 
    default: Guest Additions Version: 5.1.28_Ubuntu r117968
    default: VirtualBox Version: 5.2
==> default: Configuring and enabling network interfaces...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

/sbin/ifdown 'enp0s8' || true
/sbin/ip addr flush dev 'enp0s8'
# Remove any previous network modifications from the interfaces file
sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre
sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tac | sed -e '/^#VAGRANT-END/,$ d' | tac > /tmp/vagrant-network-interfaces.post

cat \
  /tmp/vagrant-network-interfaces.pre \
  /tmp/vagrant-network-entry \
  /tmp/vagrant-network-interfaces.post \
  > /etc/network/interfaces

rm -f /tmp/vagrant-network-interfaces.pre
rm -f /tmp/vagrant-network-entry
rm -f /tmp/vagrant-network-interfaces.post

/sbin/ifup 'enp0s8'

Stdout from the command:



Stderr from the command:

bash: line 4: /sbin/ifdown: No such file or directory
sed: can't read /etc/network/interfaces: No such file or directory
sed: can't read /etc/network/interfaces: No such file or directory
bash: line 20: /sbin/ifup: No such file or directory
複製程式碼

這是由於 vagrant 使用 ifupdown 軟體包來管理虛擬機器的網路配置問題,然而某些新版本的作業系統,例如 Ubuntu 18+ 已經放棄了 ifupdown,因而 vagrant 指令碼會失敗。

解決的辦法是兩次啟動虛擬機器,並且在其間自行安裝 ifupdown:

$ vagrant up   # 啟動虛擬機器,會報錯網路配置不成功
$ vagrant ssh  # 不理睬錯誤直接登入到虛擬機器中
ubuntu@node1$ sudo apt install ifupdown; exit
$ vagrant halt # 關閉虛擬機器
$ vargant up && vagrant ssh # 然後重新啟動虛擬機器,網路配置會再次被應用,並且應該會一切正常了
複製程式碼

References

相關文章