8.8 OpenStack 如何使用DHCP?

image0

8.8.1 为何要使用dhcp?

在创建虚拟机时,虚拟机的ip获取一般有两种方式:

  1. 静态IP:通过 ConfigDrive 注入虚拟机,由 Cloud-Init 负责配置。

  2. 动态IP:使用DHCP方式动态获取,由 NetworkManager 负责配置。

相比静态 ip ,动态IP更有利于管理。比如需要对已分配的虚拟机的ip进行更换。

若是使用 静态ip(写在配置文件里),更换 ip 需要在平台层面将虚拟机的旧ip改成新ip(通过挂卸网卡实现),手动登陆到虚拟机内部修改配置,并重启网络。

若是使用 动态ip(通过DHCP),可以做到修改IP地址无需重启虚拟机,动态生效。

8.8.2 集群网络拓扑结构

如下图所示

image1

红色部分是虚拟机内部需要增加的网卡设备。

br-int :为openvswitch-agent自动创建的ovs网桥,其主要的作用是供neutron-dhcp-agent挂设备的。

dchp-ovs:连接物理网卡与br-int之间的ovs桥,作用是打通宿主机与虚拟机内部的二层网络

tap : veth pair的tap设备端,主要作用是连接Linux namespace 下的dhcp server网卡设备与ovs网桥。一个network一个tap设备。

8.8.3 集群环境部署支持

8.8.3.1 网卡与网络

在控制节点上新增一个网卡,通过xml动态增加。

<interface type='bridge'>
     <source bridge='br0-ovs'/>
     <virtualport type='openvswitch'>
     </virtualport>
     <target dev='vnet4'/>
     <model type='virtio'/>
     <alias name='net1'/>
</interface>

到tmp.xml所在目录里执行命令

virsh attach-device ws_controller01 ./tmp.xml --persistent --live

网卡配置文件:/etc/sysconfig/network-scripts/ifcfg-eth2

DEVICE=eth2
TYPE=OVSPort
ONBOOT=yes
BOOTPROTO=none
OVS_BRIDGE=dchp-ovs
DEVICETYPE=ovs

/etc/sysconfig/network-scripts/ifcfg-dhcp-ovs

BOOTPROTO=static
DEVICE=dhcp-ovs
ONBOOT=yes
TYPE=OVSBridge

重启网络

8.8.3.2 依赖包与配置

确保控制节点都安装了 neutron-openvswitch-agentneutron-dhcp-agent 等几个包

image2

确保如下几个配置无误

1、/etc/neutron/dhcp_agent.ini

[DEFAULT]
ovs_integration_bridge = br-int
ovs_use_veth = True
interface_driver = openvswitch

[OVS]
ovsdb_interface=vsctl

2、/etc/neutron/plugins/ml2/openvswitch_agent.ini

[ovs]
ovsdb_interface = vsctl
bridge_mappings = phynet0:dhcp-ovs

启动服务

systemctl enable neutron-dhcp-agent
systemctl enable neutron-openvswitch-agent
systemctl restart neutron-dhcp-agent
systemctl restart neutron-openvswitch-agent

通过 ovs-vsctl show 检查环境是否正常

image3

8.8.3.3 镜像支持

使用 dhcp 创建虚拟机时,cloudinit 检查到是 dhcp 模式,会跳过网卡配置文件的配置,而后启动的 NetworkManager (centos 是 NetworkManager, ubuntu 是network-manager)才负责获取ip。

所以为了支持DHCP,需要在镜像内部安装 NetworkManager ,并设置开机自启,关闭 network 服务并取消开机自启,以免冲突。

8.8.4 重要:场景验证

  1. 启用DHCP后,dhcp server 会占用一个allocation_pools中的ip。

  2. 子网启用了dhcp,ConfigDrive不会携带静态的ip地址(类型为ipv4,左图),而是携带了将类型变更为ipv4_dhcp(右图),虚拟机启动时发送广播消息DHCP discover从dhcp server获得真正的ip地址。 image4

  3. 若子网启用了dhcp,并且创建了虚拟机,此时 disable dhcp,只是表象上 dhcp 被关了,而实际 dhcp port 被是占用着,因为 dhcp server 还要给之前创建的虚拟机提供服务。如果再用这个子网创建的虚拟机会使用dhcp ip而不是使用 static ip,而当有删除(新增)其他子网的动作时,这个dhcp-port 又会被删除。如果一个子网开启了dhcp,并且这个子网下没有虚拟机,更新子网 disable dhcp,dhcp-port会立即删除。再用这个子网创建的虚拟机会使用static ip。如果一个子网开启了dhcp,并且这个子网下有虚拟机,更新子网 disable dhcp,dhcp-port不会立即删除。再用这个子网创建的虚拟机会使用dhcp获取ip。

  4. dnsmasq进程是由dhcp-agent 服务管理的。如果停用 dhcp-agent 时,dnsmasq 进程并不会关闭。如果同一个网络下有多个dnsmasq ,不会影响正常的dhcp获取ip。

  5. 如果三个控制节点上的 dhcp-agent 都是关闭状态,此时创建虚拟机时,ip仍然会正常分配、port会正常创建,但由于nova-compute等不到neutron的port plugged事件,过一段时间就超时导致创建虚拟机失败。而如果在超时范围内将dhcp-agent启动起来,就可以立即创建成功。

  6. 如果一台节点上的 dhcp-agent 关闭了,neutron-server 会等待150s(agent_down_time*2)后再重新调度,将负责这个节点上的dnsmasq进程在另一台上启动起来。

  7. 虚拟机通过dhcp获取ip,和用config drive 注入静态ip配置的时间差不多,经验证从创建到ping通,dhcp花了22s,静态ip花了23s

  8. 如果一个子网没有配置 dns,那么用这个子网创建虚拟机,虚拟机内部会将这个子网的dhcp server 的ip拿来做dns配在 /etc/resolv.conf 里,而且在排在最上面,可能会导致虚拟机上不了网。

  9. 使用 dhcp 的模式,cloudinit 从 configure drive 中知道是dhcp后就不会去刷新配置文件将static 改为dhcp(使用的是NetworkManager自动获取ip,这在开机启动时 NetworkManager就会先于cloudinit 去做),所以如果这个镜像原网卡配置文件里是静态ip,那么使用这个镜像创建dhcp 的虚拟机,就会暴露旧ip,但是这对于配置ip没有影响,NetworkManager 配置ip的顺序是先dhcp,获取不到再从配置文件读。

  10. 如果一个子网只有dhcp port,子网可以被删除,如果有其他port,则子网不能删除。

  11. 一个network一个ns,一个ns下面会有多个dhcp server的ip,正常情况下每个subnet一个dhcp server,若一个network下有多个开启了dhcp server 的子网,只关闭其中一个子网的dhcp,并不会立马删除这个dhcp port,因为这个port还有其他人占用(感觉这块neutron处理不好,应该更新一个port的fixed_ip,把关闭dhcp 的子网的ip给删除掉),当一个network里没有开启dhcp的子网且没有虚拟机使用dhcp的情况下,neutron才会删除这个dhcp port。

8.8.5 镜像问题排查

CentOS 7.x 中,所有的服务都是通过systemd 管理的,服务的启动依赖也可以在 Service 中声明,会开机时 systemd 做为第一个服务启动,会将所有的依赖关系都理出来,按顺序去启动。

比如 local 阶段作为第一个执行的。

image5

而后的几个服务(如init阶段),都需要保证 后于 local 已经启动过才能运行。

image6

CentOS 6.x 中,由于系统过于古老并没有 systemd ,它的服务启动顺序是由 /etc/rc.d/rc3.d 目录进行管理的,按照编号进行从小到大执行。

image7

这个启动顺序相当重要,在 CentOS6.x 上会因此出现问题

假如我们要创建 一个CentOS 6.5 的虚拟机,而我们希望这个虚拟机的 ip 是静态ip(写在配置文件里),当你在虚拟机里使用的是NetworkManager(而非 network)时,会发现 ip 并不会自动配置上。而如果创建的是DHCP ip 的虚拟机时,就能正常配置ip。

在解释这个问题之前,先要理解 cloudinit 在local 阶段做了啥事。通过阅读源代码可知,在local阶段它会从 datasource 里读取网络配置信息,如果发现使用的是静态ip,cloudinit就会将网络信息(ip,dns,gateway等) 写入ifcfg配置文件。而如果发现使用的是 DHCP,cloudinit 并不会创建刷新网卡配置文件,配置ip的工作就交由 NetworkManager 会去自动获取。

从以信息可知,如果创建静态ip的虚拟机,NetworkManager 这个服务必须在 cloudinit-local 之后启动才可正常从配置文件中读取 ip 并配置。而当你在镜像里安装 NetworkManager后,默认情况下它的启动顺序是会在 cloudinit-local 之前的。

img

img

解决方法也很简单,将 S23NetworkManager 重命名为 S54NetworkManager 即可。

8.8.6 源码解读

发生dhcp-agent DOWN后会触发重新调度将dnsmasq迁到另一台,对应函数:reschedule_resources_from_down_agents,这个函数里默认会等待150s:wait_down_agents

dhcp-port 是如何被创建出来的?

从 neutron:raw-latex:neutron-0.0.1.dev2:raw-latex:neutron:raw-latex:`\agent`:raw-latex:`\linux`:raw-latex:`\dhcp`.py: setup() 开始

再进入 setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序:existint_dhcp-port -> reserved-dhcp-port -> setup_dhcp_port()


image8