我对这个主题还很陌生,我花了好几天的时间才弄清楚如何通过 KVM 主机为 KVM 客户机分配多个公共 IP 地址。我找到了大量示例,了解如何通过 1 个公共 IP 运行此类设置。
以下是我的设置:服务器只有一个 NIC/MAC,运行 2 个 KVM-Guest,带有 apache(和其他东西)。两个客户环境都是 ubuntu server 11.10,必须在单独的 VM 中运行。5 个公共 IP 地址用于处理 SSL 证书和其他东西。第一个 VM 应该使用 5 个地址/证书中的 3 个。第二个 VM 使用其余的。apache 东西配置正确。
我尝试了多种不同的方式通过 iptables 将流量从主机 NIC 路由到客户机 NIC。尽管其中一种方法是正确的,但只是实施错误,但我没有透露细节,以免让您感到困惑。问题是:理想的做法是什么?
应满足以下条件:
- Apache 必须获取访问者的原始 IP 地址
- Apache 必须知道使用了哪个公共 IP 地址才能使用正确的 ssl-vhost
- 流量不能通过主机上的(反向)代理进行路由,因为其他 VM 客户机上还有另外 2 个非 http 服务,应该可以从公共网络访问。并且:只有 sshd 应该直接在主机上监听 - 没有其他东西
- 每个虚拟机都应该能够直接访问互联网。
- 数据中心的网络是基于 MAC 交换的。据我所知,与互联网通信的唯一方式是通过 eth0 及其 MAC 地址。
如果我丢弃所有虚拟化内容,这将非常容易,因为 apache 直接从特定的 ip 地址获取请求。
我愿意接受任何可行的解决方案。
答案1
在您的 dom0(例如 KVM 主机)WAN 接口上使用桥接器。这需要安装bridge-utils
包。由于这是基于 Debian 的发行版,您可以在以下位置进行配置/etc/network/interfaces
:
iface eth0 inet manual
auto br_wan
iface br_wan inet dhcp
# Assuming DHCP to get address, otherwise migrate all WAN connection options here
#address 192.168.122.0
bridge_ports eth0 tap_guest1
bridge_stp off
bridge_maxwait 0
bridge_fd 0
pre-up ip tuntap add dev tap_guest1 user guest1 mode tap
# This command is required if your ISP allocates static IPs depending on MAC address
# You shouldn't use this but might be handy some time
#pre-up sysctl -q -w net/ipv4/conf/tap_guest1/proxy_arp=1
post-down ip tuntap del tap_guest1 mode tap
预备命令设置 TAP 接口以将您的 KVM 客户端连接到桥接器。请注意,此设置允许从非特权用户 guest1 运行 kvm。请注意,net.ipv4.ip_forward = 1
使用 sysctl 进行设置也可能有用。
我使用了包ip tuntap
中的命令iproute2
。Debian 包中尚未记录该命令,但很快就会在上游的手册页中提供。由于此包安装在每个基于 Debian 的服务器上,因此您无需安装uml-utilities
或openvpn
打包即可创建这些接口。
这种方法在管理大量 tap 接口时确实缺乏一些优雅性,因为您需要创建与tap_guest1
接口类似的 pre-up 和 post-down 线路。这可以通过在/etc/network/pre-up.d
和中编写额外的脚本来解决/etc/network/post-down.d
。如果您想br_wan
在 KVM 客户机仍在运行时使用 ifdown/ifup 脚本重新配置接口,这也是一个问题 — 您需要删除除桥接eth0
配置之外的所有接口并手动将它们从桥接中分离(然后不要忘记在桥接重新配置后将它们重新连接起来)或关闭桥接上运行的所有 KVM 实例。
另一种方法,也许更简洁,是为 KVM 本身编写自定义 ifup 脚本,并将其用作script
NIC 的选项。您可以在 中找到一个示例/etc/qemu-ifup
。请参阅kvm 手册页了解详情。
然后你可以像这样运行你的 KVM 盒:
kvm -net nic,model=virtio,macaddr=12:34:56:78:9a:bc \
-net tap,ifname=tap_guest1,script=no,downscript=no \
-boot c -nographic -display none -daemonize \
guest1-drive.qcow2
可以使用命令手动为 KVM 客户机在一个接口上设置多个 IP 地址
ip address add aaa.bbb.ccc.101/24 dev eth0
或者永久地像/etc/network/interfaces
这样:
auto eth0 eth0:1
iface eth0 inet static
address aaa.bbb.ccc.100
network aaa.bbb.ccc.0
netmask 255.255.255.0
broadcast aaa.bbb.ccc.255
gateway aaa.bbb.ccc.1
iface eth0:1 inet static
address aaa.bbb.ccc.101
network aaa.bbb.ccc.0
netmask 255.255.255.0
broadcast aaa.bbb.ccc.255
gateway aaa.bbb.ccc.1
请注意,如果您的数据中心/提供商不希望您在同一网络上显示其他盒子,他可能不会配置它们,并且它们将不可用。在这种情况下,您可能需要创建内部桥接并使用 iptables 使用 DNAT 和 SNAT 在您的 WAN 接口和此桥接之间转发数据包。假设您的本地虚拟桥接网络是 10.0.0.0/8,您的 guest1 是 10.0.0.2,您将需要以下内容:
iptables -t nat -A PREROUTING --dst aaa.bbb.ccc.100 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2
iptables -t nat -A PREROUTING --dst aaa.bbb.ccc.101 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2
...
iptables -t nat -A POSTROUTING -p tcp --dst 10.0.0.2 -j SNAT --to-source aaa.bbb.ccc.100
请注意,您需要为每个 KVM 客户端设置与外部 IP 数量相同的 DNAT 命令,但只需一条 SNAT 规则即可访问互联网。此外,您还可以通过仅允许所需端口来仅允许 HTTP/HTTPS/SSH 流量。如果您省略该--dport
语句,则所有端口都将被转发。除非您愿意托管 DHCP 服务器,否则您的 KVM 客户端应具有静态网络设置,并以 KVM 主机作为默认网关。
答案2
您使用网桥而不是使用 iptables 转发数据包。启动客户机后,您只需将 IP 配置到客户机上的 NIC(如果您有多个虚拟 NIC)即可。
参考此文档。
答案3
@jollyroger:非常感谢您的全面指南。遗憾的是,就我而言,它不起作用。但您的指南帮助我更好地了解了 linux-networking/bridging 与 KVM 结合使用的功能。
所以我恢复了所有内容,并直接从托管公司获得了一些帮助。该场景非常简单,只需使用虚拟管理器和维尔什用于添加自定义网络配置。以下是最终对我有用的解决方案:
我有一个 /29 子网(+主机上用于 ssh 的另一个不同 IP)。数据中心的网络是基于 mac 交换的,因此所有流量都必须通过本地网关(据我所知)。我的提供商是德国的 Hetzner。我使用了http://www.subnet-calculator.com/subnet.php?net_class=A计算网络配置 xml 的所有必要设置:
<network>
<name>{some systemwide unique name. e.g.: subnet1}</name>
<uuid>{> call uuidgen from shell to generate a uuid}</uuid>
<forward dev="{interface e.g.: eth0}" mode="route" />
<bridge name="{name of a _new_ virtual interface e.g.: virbr2}" stp="off" forwardDelay="0" />
<ip address="{subnet-calc: Host Address Range Start Address}" netmask="subnet-calc: Subnet Mask">
<dhcp>
<range start="{subnet-calc: Host Address Range Start Address+1}" end="{subnet-calc: Host Address Range End Address}" />
</dhcp>
</ip>
</network>
我保存了这个 xml 数据到类似/tmp/subnet1.xml并通过以下方式应用这个新的虚拟网络:
sudo virsh net-define /tmp/subnet1.xml
我用虚拟管理器0.9.0(通过我的 Windows 机器上的 Putty 通过 x11 转发)创建和编辑所有虚拟机。因此,我的 2 个公共虚拟机都获得了这个新的虚拟网络。在每个虚拟机中,我为每个公共 IP 创建了一个新虚拟网络接口。我已记下哪个 MAC 地址用于哪个公共 IP。因此,如果我想将公共 IP 地址移动到另一个虚拟机,只需删除虚拟机 A 中的虚拟 NIC 并使用相同的 MAC 地址在虚拟机 B 中创建它即可。
另外,如果您尝试重现此场景:在启动虚拟机之前,请确保虚拟网络“子网 1”(sa)处于活动状态。在 virt-manager 的主窗口中,转到编辑 > 连接详细信息并跳转到 Tab虚拟网络。如果网络“subnet1”呈灰色,只需标记它并单击播放按钮。
现在启动虚拟机并输入是否配置. 就我而言,一切都直接起作用。
如果我在本报告中遗漏了任何内容,请直接询问。如果有人有更好的解决方案,请随时在此处添加。