乐趣区

Vagrant (三) – 网络配置

这个章节脱胎于我早期的 blog 文章,订正了一下句法和准确性。
原始的文字在 VAGRANT 网络配置 (HIDE)
网络配置
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 服务

建立一个新的工作目录 sample-nginx

使用 vagrant init ubuntu/xenial64 进行初始化
修改 Vagrantfile 加入必要的声明 (见后)
使用 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

https://www.virtualbox.org/ma…
https://dev.to/isabolic99/how…
https://www.vagrantup.com/doc…

退出移动版