我非要捅穿這 Neutron(二)上層資源模型篇

目錄

Neutron 的資源模型

Neutron 資源模型官方文檔:https://developer.openstack.org/api-ref/network/v2/index.htmlhtml

Network

Network 是 Network Connectivity as s Service 的 「根」 操做對象,是 Neutron 對二層網絡的抽象,包含了用戶對「大二層」網絡的一切想象,支持 Local、 Flat、 VLAN、 VXLAN、 GRE、 Geneve 等多種網絡類型並不斷擴充。若是將 Network 映射到現實世界的話,它就至關於一個巨大的交換機:從介質的角度,它擁有許多端口;從網絡的角度,它劃分廣播域;從功能的角度,它提供了 「隔離」 和 「轉發」。linux

在《Networking 基本術語/概念》一文中,咱們已經記錄了關於大二層網絡的介紹,這裏再也不贅述。
在這裏插入圖片描述
在這裏插入圖片描述
Neutron 網絡概念的定義和類型花樣之多,使人眼花繚亂。咱們姑且從幾個不一樣的方面對 「網絡」 作一個分類概括,區分理解。web

從網絡實現模型分層的角度看數據庫

  • 本地網絡(br-int)
  • 租戶網絡(br-ethX/br-tun)
  • 外部網絡(br-ex)

從網絡實現技術的角度看api

  • 非隧道網絡(Local、Flat、VLAN)
  • 隧道網絡(VxLAN、GRE、Geneve)

從網絡從屬關係的角度看數組

  • 運營商(物理)網絡
  • 租戶網絡

從 Neutron 所追求的「多租戶隔離多平面網絡」實現來看,咱們能夠感覺到每個 Network 都應該具備如下 3 個核心要素,我將其稱之爲 「核心三要素」:安全

  • Network Type
  • VID Range
  • Physical Network Mapping

接下來的內容,主要就是圍繞這核心三要素展開的。服務器

運營商網絡和租戶網絡

本章節咱們要使用的就是第三種視角。所謂 「從屬」 便是 「屬於誰」:由 Neutron 建立的,屬於 Neutron 管理範疇的網絡,咱們稱之爲租戶網絡(Tenant Network);屬於運營商(e.g. OpenStack 平臺的運營者)原有的,Neutron 沒法管理、只能映射(記錄)的網絡,稱之爲運營商網絡( Provider Network)。兩種網絡都採用了 Network 資源模型,從模型的角度來看,二者的區別在因而否會持久化下列三個字段屬性值(運營商網絡具備,租戶網絡反之):網絡

  • provider:network_ type (string) — The type of physical network that this network is mapped to.
  • provider:physical_network (string) — The physical network where this network is implemented.
  • provider:segmentation_id (int) — The ID of the isolated segment on the physical network.

首先須要提出的問題是:爲何要區分租戶網絡和運營商網絡?這是由於單存的 Neutron(虛擬網絡)自己只是一座孤島,若是但願與外界取得聯繫,就須要依賴於公共設施(e.g. 移動通訊網絡、橋樑)的支持。雖然 Neutron 對這些公共設施沒有控制權,但卻可使用(e.g. 購買手機號碼)它們,因此 Neutron 要建立一個 Network 資源對象來存儲這些(運營商網絡)信息。而存儲的地方就是上述的 3 個字段了。架構

舉例說明運營商網絡的應用場景:用戶但願 OpenStack 中的 VMs 與產品部(VLAN 10=100)的若干臺我的電腦處於同一個二層網絡,那麼用戶能夠爲這些 VMs 建立一個 VLAN ID=100 的運營商網絡 p-network1,而且填入下列字段的值。

  • provider:network_ type ==> VLAN
  • provider:physical_network ==> 「產品部網絡」
  • provider:segmentation_id ==> 100

NOTE:以上只是舉例,實際這三個字段的值並不是如此。
在這裏插入圖片描述
須要注意的是,上述例子中不管是 VLAN 類型的網絡,仍是 VLAN ID=100 都並不是是 Neutron 建立的,而是運營商原本就已經存在的網絡,Neutron 只是建立了一個 Network 資源類型並記錄下這些運營商網絡的信息,繼而進一步使用這個運營商網絡的特性而已
在這裏插入圖片描述
簡而言之,運營商網絡的做用就是爲了讓 Neutron 內部的虛擬網絡能夠與物理網絡鏈接起來。運營商網絡是運營商的某個物理網絡在 Neutron 上的延伸,Neutron 租戶沒法管理這個物理網絡的生命週期

建立運營商網絡

建立運營商網絡有兩個要點:一是隻能有管理員建立,二是須要手動填寫 「核心三要素」。
在這裏插入圖片描述
上圖爲經過 admin 建立一個 VLAN 類型的運營商網絡,填寫的三個表單就對應了 「核心三要素」 並存儲到數據庫中 networks 表的 provider:network_ type、provider:physical_network、provider:segmentation_id 字段中。「核心三要素」 中的 Network Type 和 VID Range 很好理解,二者描述了運營商網絡的特徵。但 Physical Network Mapping 的意義你或許會感到迷惑,其實它描述的是 Neutron Network 應該如何接入運營商實際的物理網絡。直白的說,就是 Neutron Network 要經過哪一張 「網卡」 來接入到實際的運營商網絡中。

但爲何上圖 「物理網絡」 表單項填寫的 「網卡」 名稱是 「public」 呢?其實 「public」 是 Neutron 實現的一種 Label 機制,本質是一個 Key-Value Mapping,將便於人類理解的 Label Name 與實際的 Physical Network 隱射起來,這就是所謂的 Physical Network Mapping。幾乎全部的物理網絡是使用 Label 的方式來標的。而這個 Physical Network Mapping 就定義在 Neutron OvS(本文以 OvS 爲例)的配置文件中。e.g.

# /etc/neutron/plugins/ml2/openvswitch_agent.ini

bridge_mappings = physnet1:br-ethx1, physnet2:br-ethx2

那爲何 Lable 「public」 對應的 Physical Network 是 「br-ethx1」 而不是一張具體的物理網卡名稱(e.g. eth0)呢?這個問題其實咱們在 Neutron 的網絡實現模型已經提到過,由於 OvS Bridge 會將物理服務器上的物理網卡掛載到 OvS br-ethX(IP 地址也在此),因此 Physical Network 填入的是 br-ethX 而不是 eth0。

在這裏插入圖片描述
上圖爲建立一個 VxLAN 類型的運營商網絡。奇怪的是爲何沒有了 「物理網絡」 這個表單項呢?這仍是要說到 Physical Network Mapping(provider:physical_network)描述的是 Neutron Network 應該如何接入到實際的運營商網絡,因爲 Flat、VLAN 等非隧道網絡類型本質是一個二層網絡,因此須要指定 「網卡」 才能讓二層的數據幀流入運營商網絡。但像 VxLAN、GRE 之類的隧道網絡類型的實現原理是基於三層 IP 協議的,因此隧道網絡可否與運營商網絡互通的關鍵是隧道外層封裝 IP 地址是否填寫正確,而非一張 「網卡」。

再次強調一下建立運營商網絡的要點:

  • 只能由管理員建立
  • 須要手動填寫 「核心三要素」,並持久化到數據庫中
    • 非隧道網絡類型:須要傳入 Physical Network Mapping(provider:physical_network)字段值,描述 Neutron Network 接入運營商網絡的 「網卡(br-ethX)」。
    • 隧道網絡類型:只須要保證節點上已經定義好了與運營商網絡對應的 Tunnel 端口 IP 地址而且三層網絡通訊無障礙便可。

在這裏插入圖片描述
上圖爲建立一個 Flat 類型的運營商網絡。Flat(扁平)類型網絡是沒有 VID 的,不想 VLAN 類型在網絡包從 Bridge 發出前仍是打 VLAN tag 並進行內外 VID 轉換。Flat 的網絡包就是一個 Untag 網絡包,直接接入物理網絡,只須要告訴它用哪一張 「網卡(物理網絡)」 便可。

建立租戶網絡

在這裏插入圖片描述
上圖爲建立一個租戶網絡,可見並無填入 「核心三要素」 的表單項。這是由於 Neutron 認爲:對於租戶建立本身的網絡而言,租戶但願獲得的只是一個二層網絡,至於這個二層網絡是由 VLAN 提供的或由 VxLAN 提供的並沒有所謂,VID 是多少也並沒有所謂,Physical Network 是怎麼 Mapping 的就更無所謂了。

一言以蔽之,租戶無需關心這個 Network(二層網絡)的底層實現細節。Neutron 只有作到了這一點,才能稱之爲 「服務」!這不由讓我想起柯達公司的一句經典廣告語 —— 你只管快門,其他我來!

但話又說回來,租戶天然是沒必要操心這些細節,但云平臺管理員可不行。雲平臺管理員是能夠經過 Neutron 的配置文件來聲明定義 「核心三要素」 的。e.g.

[ml2]
...
tenant_network_types = vlan,vxlan
mechanism_drivers = openvswitch,linuxbridge

[securitygroup]
firewall_driver = openvswitch

[ovs]
datapath_type = system
bridge_mappings = public:br-ex,
tunnel_bridge = br-tun
local_ip = 172.18.22.200

[ml2_type_flat]
flat_networks = public,

[ml2_type_vlan]
network_vlan_ranges = public:3001:4000,

[ml2_type_geneve]
vni_ranges = 1:1000

[ml2_type_gre]
tunnel_id_ranges = 1:1000

[ml2_type_vxlan]
vni_ranges = 1:1000

因此,就租戶網絡而言,其 「核心三要素」 是不須要持久化到數據庫中的,也就不具備 provider:network_ type、provider:physical_network、provider:segmentation_id 這三個字段值了。再一個就是從配置中能夠看出,只有非隧道類型是須要填寫 「物理網絡」 Lable 的。

建立外部網絡

建立外部網絡(可以訪問公網的網絡)屬於運營商網絡的另外一種應用場景。

在這裏插入圖片描述

實際上所謂的外部網絡和運營商網絡的底層實現並沒有區別,不一樣之處在於外部網絡具備分配 Floating IP 的功能,即外部網絡在網絡節點的 qrouter-XXX namespace 中配置的 iptables 的 NAT(SNAT/DNAT)功能,使得內部虛擬機得以訪問到外部公網。

Network 小結

Network 是 Neutron 的二層網絡資源模型,對外提供二層網絡的服務接口(建立、刪除二層網絡)。租戶能夠經過這個接口來建立專屬於本身的租戶網絡,管理員能夠經過這個接口來建立(對接)Neutron 所沒法管理的運營商的物理網絡與外部網絡。

從資源的角度來看,租戶網絡和運營商網絡的主要區別在於「可控性」:租戶網絡是 Neutron 徹底可控的,基於 Neutron 網絡實現模型支撐的多租戶隔離多平面網絡需求,租戶網絡的 「核心三要素」 由雲管人員經過 Neutron 的 ML2 配置文件定義;而運營商網絡倒是 Neutron 所沒法管理的運營商的物理網絡在 Neutron 上的一種延伸,其 「核心三要素」 只是對運營商物理網絡信息的一個記錄,並沒有管理性質。

運營商網絡的兩種典型應用場景:一是打通 Neutron Network 與運營商內部的物理網絡,使得二者之間的虛擬機能夠互相通訊;再一個是打通 Neutron Network 與 Internet,使得 Neutron Network 中的虛擬機能夠訪問公網。第二種應用場景也就是 Neutron 的 Floating IP 功能,是經過網絡節點上 qrouter-XXX 中的 iptables NAT 實現的。

Subnet

Subnet 下屬於 Network,是 Neutron 對三層子網的抽象,它做爲一個具體網段(CIDR)的 IP 地址池,同時爲使用接入到該子網的 VMs 提供 IP 核心網絡服務,還負責保證三層網絡之間的路由互通。在一般狀況下,Subnet 符合人們對 「子網」 的常規理解。

  • IP VERSION
  • IPADDR
  • NETMASK
  • GATEWAY
  • ROUTE
  • DNS
  • DHCP
  • IPAM

NOTE:在某些特定的應用場景中(e.g. Multi-Segments),Subnet 被賦予了更加深厚的含義,這裏咱們暫且不談。
在這裏插入圖片描述
在這裏插入圖片描述

IP 核心網絡服務

IP 核心網絡服務(IP CoreNetwork Services),又稱 DDI(DNS、DHCP、IPAM)服務。

  • DNS — Domain Name System,域名系統
  • DHCP — Dynamic Host Configuration Protocol,動態主機設置協議
  • IPAM — IP Address Manager,IP 地址管理

DNS 和 DHCP 相信你們不會陌生,這裏介紹一下 IPAM 服務。IPAM 用於發現、監視、審覈和管理企業網絡上使用的 IP 地址空間,IPAM 還能夠對運行的 DHCP 和 DNS 服務器進行管理和監視。簡而言之,IPAM 就是一種 IP 地址的管理手段,目的是讓 IP 地址的分配、使用更加便利

Subnet 資源模型中與 DDI 服務相關的字段
在這裏插入圖片描述
enable_dhcp:布爾類型,表示是否爲 Subnet 啓用 DHCP 服務,若啓用,再會在 qdhcp-XXX namesapce 中啓動 dnsmasq 服務進程。

allocation_pools:是一個數組,表示 DHCP 服務可分配的 IP 地址池列表,每一個地址池的格式爲 [start IP, end IP]。若沒有配置則以 Subnet 的 cidr 做爲地址池。

dns_nameservers:是一個數組,用於指定一批 DNS Server 的地址。這裏僅記錄地址,實際的 DNS 服務並不禁 Neutron 提供。

subnetpool_id:是 tables subnetpools 的外鍵,指向 SubnetPools 資源模型。

NOTE:SubnetPools 與 allocation_pools 是兩個 「同類不一樣源」 的功能。二者均爲 IP 地址的資源池,而前者是對後者的一種優化,在 Kilo 版本引入,爲了更好的管理子網網絡資源池(e.g. 提供訪問接口)。

小結一下,IP 核心網絡服務(DNS、DHCP、IPAM)是 Subnet 資源模型提供的服務,而服務的對象是使用該 Subnet 的 VMs。可見,Subnet 不只僅是一個抽象資源接口,其具備必定的管理功能。這一點很是重要,由於咱們在啓動虛擬機時,時常選擇的是一個 Network 而非 Subnet。e.g.

$ openstack server create -h
...
[--nic <net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,port-id=port-uuid,auto,none>]

這會讓咱們產生一種錯覺,虛擬機網絡來自於 Network,從表面看並無問題,也是 Neutron 有意爲之(對用戶隱藏複雜細節的表現)。但對於開發者而言,若是帶着這種認知去閱讀源碼就不免會產生疑惑,IP 核心網絡服務的代碼邏輯處理對象實際是 Subnet 而非 Network,這一點徹底契合 Subnet 的資源模型設計。因此再次強調,Network 是 NaaS 的根操做對象,表示一個二層網絡;而 Subnet 下屬於 Network,爲 Network 中的 VMs 提供 IP 核心服務

SubnetPools 資源模型

你是否考慮過,用戶真的須要關心一個 Subnet 的網段是 192.X、仍是 172.X、仍是 10.X 嗎?用戶要關心的僅僅是這個 Subnet 能夠提供多少個 IP 地址而已!出於這樣的需求,Network 引入了 SubnetPools 資源模型,經過 subnetpool_id 字段與 Subnet 關聯。SubnetPools 和 Sunbet 的關係就至關於:前者定義了一個大的(不重複的)網段,後者從中分配了一個小的網段。e.g.

$ openstack subnet create -h
...
[--subnet-pool <subnet-pool> | --use-prefix-delegation USE_PREFIX_DELEGATION | --use-default-subnet-pool]

NOTE:在較新的版本(Rocky)中,配合 OpenStack Placement Project,SubnetPools 還能夠被一個第三方的網絡軟件提供的外部 IP 資源池代替。

在這裏插入圖片描述

# 記錄了一個 SubnetPool 的子網網段劃分規則
MariaDB [neutron]>  select * from subnetpools\G;
*************************** 1. row ***************************
       project_id: 031cec3e2df143259d302aa1993fd410
               id: 5bbd5cab-c6a0-4851-b2d7-1137742e6adf
             name: shared-default-subnetpool-v4
       ip_version: 4
default_prefixlen: 26
    min_prefixlen: 8
    max_prefixlen: 32
           shared: 1
    default_quota: NULL
             hash: 27e49575-31f7-4012-b57b-55c990cb6d82
 address_scope_id: NULL
       is_default: 1
 standard_attr_id: 1
 
# 記錄了一個 SubnetPool 的子網網段信息
MariaDB [neutron]> select * from  subnetpoolprefixes;
+----------------+--------------------------------------+
| cidr           | subnetpool_id                        |
+----------------+--------------------------------------+
| 192.168.1.0/24 | 5bbd5cab-c6a0-4851-b2d7-1137742e6adf |
+----------------+--------------------------------------+

Multi-Segments

在上文中咱們反覆提到,Network 是一個二層網絡的抽象,Subnet 是一個三層子網的抽象。這是大多數 SDN 軟件設計的常規定義,建立一個 Network 就至關於得到了一個廣播域,建立一個 Subnet 就至關於得到了一個三層 IP 地址池。對用戶的理解很是友好。

不過在實際的生產需求中 Neutron 面臨一個問題:若是一個 Network 內的 VMs/Hosts 數量太多,就會出現諸如二層廣播風暴、網絡內交換機 ARP 表項容量不足等狀況。簡單來講,這就是 Network 做爲雲計算平臺中的大二層網絡卻沒法支撐起 「無限」 多個 VMs 的問題。爲了知足這一需求 Neutron 引入了 Multi-Segments 設計。

Multi-Segments 解決這一問題的辦法就是將 Network 的定義進一步升級爲 「多個二層網絡的容器」,同時也將 Subnet 的定義進一步升級爲 「一個二層網絡以及基於此的三層子網」,Subnet 之間經過三層路由互通。

在這裏插入圖片描述
在這裏插入圖片描述
Multi-Segments 顧名思義是一個 Network 具備多個 Segments,而一個 Segment 實際上是一個運營商網絡,這一點能夠從 Segment 資源模型看出(每條 Segment 記錄都包含一個運營商網絡的 「核心三要素」)。
在這裏插入圖片描述
顯然,天下沒有免費的午飯,Multi-Segments 的設計實際上具備很是大的侷限性 —— 須要規劃整個物理、虛擬網絡拓撲。它是一種經過 「物理手段」 來完成的 「Network 升級」。好比:Routed Network 應用場景。
在這裏插入圖片描述
所謂 Routed Network,就是一個 Network 內的 Subnet(對應運營商的物理網絡)之間使用物理路由器連通,Subnet 的 gateway_ip 指向物理路由器直連網口的 IP 地址。在現實機房中,一個機櫃接入物理路由器的網口基本固定,網口的 IP 地址也不會輕易改動,這意味着 Subnet 的三層子網須要與該物理路由器網口的 IP 處於同一個網段,不然沒法通訊。也就是說,使用 Routed Network 要求管理員提早作好物理、虛擬網絡、每一個機櫃的網關 IP、DHCP Server 的全盤規劃。

Multi-Segments 的應用場景除了 Routed Network 還有「VTEP 位於 TOR 交換機上」這一場景,暫不做介紹。能夠感覺到 Multi-Segments 的應用大多須要結合實際的物理網絡拓撲對機房網絡進行全盤規劃,雖然都並不是是一種通用的方案,但 Multi-Segments 的存在也的確讓 Neutron 落地物理機房多了一些更加靈活的配置選項。不過,很惋惜的是,至今爲止筆者依然沒有遇見過在生產環境中落地的案例,有時候不免讓人懷疑 Multi-Segments 存在的必要性,這是一個你們都對其 「諱莫如深」 的話題。在本章節中提出也只是爲了做一個瞭解而已。

Routed provider networks 官方示例文檔:https://docs.openstack.org/newton/networking-guide/config-routed-networks.html

NOTE:有趣的是,通過上述文檔的實驗發現 Multi-Segments 沒法實現預期的 Segments(Subnets)之間的二層隔離,這是由於 VMs 雖然在不一樣 Segments(Subnets)上,但 VMs 鏈接到同一個(計算節點的)OvS br-int 上的 qvo-XXX 卻具備相同的 Tag ID,天然就沒法進行隔離了。因爲 Multi-Segments 的場景實在少見,筆者也就再也不深究了,只是作出提醒,瞭解就好。

建立 Subnet

在這裏插入圖片描述
建立 Subnet 能夠手動輸入你預期的網段,也能夠應用 SubnetPools 資源模型,直接選擇一個 IP 地址資源池。顯而後者要來的更加簡便一些,只須要經過 「網絡掩碼」 表單來描述你想要的 IP 地址數量便可(e.g. NETMASK=24 即 2**8 == 256 個 IP 地址)。
在這裏插入圖片描述
建立 Subnet 時你還能夠 「手動輸入網絡地址」,該表單對應 Subnet 資源模型的 allocation_pools 屬性,因此 「手動輸入網絡地址」 是沒法和 SubnetPools 共存的,只有經過手動指定網段的方式纔可使用。「主機路由」 對應 host_routes 屬性,格式正如上文所說:[目標網絡, 下一跳 IP 地址]。「DNS 服務器」 對應 dns_nameservers 屬性,是一個數值類型,填入 DNS Server 的 IP 地址。

NOTE:同一個 Network 不能夠同時具備從資源池中分配(SubnetPools)和手動輸入網絡地址(allocation_pools)兩種類型的 Subnet — Subnets hosted on the same network must be allocated from the same subnet pool.

Network 與 Subnet 的一對多關係

Network 和 Subnet 是一對多的關係,這跟應用了 Multi-Segments 與否無關。Network 下屬的 Subnets 能夠有不一樣的 IP 網段(CIDR)且不能重疊,但不一樣 Networks 的 Subnets 之間能夠具備相同的 CIDR。這得益於 Neutron L3 Router 應用了 qrouter-XXX network namespace,解決了 Networks 之間 CIDR 衝突的問題。對於不一樣 Networks 的 Subnets 之間具備相同 CIDR 時,會有如下兩種狀況:

  1. 若兩個 Subnets 經過同一個 Router 路由,根據 Router 的配置,只有指定的一個 Subnet 可被路由。
  2. 若兩個 Subnets 經過不一樣的 Router 路由,由於 Router 的路由表隔離,因此兩個 Subnets 均可以被路由。

咱們知道常規的 Network 就是二層廣播域,那麼你是否想過,爲何 Network 與 Subnet 的聚合關係被設計成一對多?爲何 Neutron 容許在同一個二層廣播域之上能夠配置多個不一樣的子網?其實這並不是出於什麼特殊的意圖,只是一對多的關係使得 Neutron 的網絡應用更加靈活。一般狀況下,用戶大機率只會在一個 Network 下建立一個 Subnet,讓它們儘量維護一個 「表面上」 的一比一關係。但總有特殊的狀況,例如:一個 Subnet 的 IP 地址用完了;例如:我要啓用 Multi-Segments。不管從操做層面仍是從架構設計層面上看,Network 與 Subnet 的一對多關係都讓 Neutron 變得更加靈活。

那麼選定一個具備多個 Subnets 的 Network 來啓動一個虛擬機時 Neutron 會怎麼處理呢(Nova 不支持選定 Subnet 來啓動虛擬機)?Neutron 會按照 Subnets 的順序選定第一個可用的 Subnet 來給虛擬機使用,知道該 Subnet 的 IP 被分配完畢爲止。由此能夠感覺到,若是不是有特殊需求,仍是儘可能讓 Network 與 Subnet 維持 「表面上」 的一比一關係吧。
在這裏插入圖片描述

NOTE:雖然 Network 下屬有多個 Subnet,但爲整個 Network 服務的 DHCP 仍然只有一個。e.g.
在這裏插入圖片描述

Port

Network 是二層網絡的抽象,Subnet 是三層子網的抽象,Port 就是網卡(NIC)的抽象,是鏈接 Nova 虛擬機與 Neutron Subnet 的橋樑,也是 Subnet 接入 L3 Router 的橋樑。

直白的說,Port 就是一個虛擬網卡(vNIC),Port 的關鍵屬性就是 IP 地址和 MAC 地址。虛擬機須要綁定 Port,路由器也要綁定 Port。
在這裏插入圖片描述
在這裏插入圖片描述
Port 與 Subnet 同樣下屬於 Network,Port 與 Subnet 的關係是水平的,爲多對多。Port 的 IP 地址來源於 Subnet,也就是說一個 Port 能夠具備多個 IP 地址。不過一般狀況下,Port 有且只有一個 MAC 地址,對應 Port 資源模型的 mac_address 屬性。

Port 與 Network、Subnet 的關係以下圖
在這裏插入圖片描述

Neutron 安全組(Security Group)

Neutron 提供了 Security Group 和 FWaaS 兩種安全機制,前者的做用域是單個虛擬機或 Port,後者的做用域是一個具體的網絡。而 Security Group 又分爲 Nova Security Group 和 Neutron Security Group,這裏咱們主要討論的是 Neutron(Port)Security Group。

在這裏插入圖片描述

當用戶將一個 Port 掛載到虛擬機時,底層邏輯會在該虛擬機所處的計算節點上建立一個 Tap 設備並將 Port 的特徵信息隱射到這個 Tap 設備,從而實現了爲虛擬機添上一張虛擬網卡(vNIC)。咱們在 Neutron 網絡實現模型的章節中提到過,虛擬機的 vNIC(Tap 設備)並不是是直連到 OvS br-int(綜合網橋)的,之間還存在一個安全網橋(Linux Bridge qbr-XXX)層。e.g.
在這裏插入圖片描述
如上圖,Linux Bridge qbr-XXX 就是 Neutron Security Group 的底層支撐。當用戶爲指定 Port 設定一系列 Security Group Rules 時,Neutron 其實是經過 CLI 方式調用操做系統的 iptables 指令爲 qbr-xxx 上的 Tap 設備配置了相應的 iptables rules。這就是所謂的 「安全層」。

那麼問題來了:爲何不能直接在 OvS br-int 上爲虛擬機 Port(Tap 設備)設定安全組規則?非得莫名的增長一個安全層?這是由於 OvS Bridge 自己不支持對鏈接到其自身的 Tap 設備使用 iptables。

直到,OvS 2.5 提出了 「基於流表」 的 Security Group 特性,它應用 Linux 內核的 conntrack(CT)模塊實現了對網絡鏈接狀態識別和處理。OpenStack 則從 M 版開始應用 OvS 這一新特性,支持 openvswitch securitygroup driver,支持使用 OvS 來實現 「有狀態防火牆」 功能。能夠經過修改 Neutron 的 ML2 配置文件來選擇 Neutron Security Group 的驅動類型。

# /etc/neutron/plugins/ml2/ml2_conf.ini

[securitygroup]
firewall_driver = openvswitch

固然了,若是選擇了 openvswitch securitygroup driver,那麼 Neutron 的網絡實現模型就再也不須要 qbr-XXX 安全層了。相應了少了一層轉發以後,網絡性能也會有所提升,尤爲在安全組規則數量巨大的時候。
在這裏插入圖片描述
在這裏插入圖片描述
關於 OvS Security Group 的實現細節,這裏暫不做過多討論,感興趣的小夥伴能夠瀏覽《OVS實現安全組,你須要知道這些!

可用地址對(Allowed address pairs)

Neutron Security Group 不只僅只會一味的按照安全組規則來對虛擬機進行保護,Security Group 自己的存在(啓用)就已經設定了一些安全策略,例如:爲了防止虛擬機被 ARP Spoofing(ARP 欺詐)、 DHCP Spoofing 的 Port IP/MAC 地址綁定安全策略。由此,默認狀況下,一個 Port 的 IP 地址和 MAC 地址是一一對應且綁定,也就是說對這個 Port 的 IP 地址進行添加、修改、刪除都會被 Neutron Security Group 斷定爲非法行爲。這也是之因此在 Neutron 網絡環境中沒法直接使用 Keepalived 來作虛擬機高可用的緣由,這是一個無處安放的 VIP 的問題。
在這裏插入圖片描述
NOTE:ARP 反欺詐有不少方案,將 IP/MAC 地址一一綁定是最直接簡單的辦法。

但就像上文提到的 Keepalived 例子,一個 Port 具備多個 IP 地址的狀況老是存在需求的,Port 資源模型的 fixed_ips 屬性就被設計爲一個數組類型(Port 可具備多個 IP 地址,1 個 MAC 地址)。針對這樣的場景通常有兩個辦法:關閉 Port Security(port_security_enabled,不建議使用)或使用 Allowed address pairs(allowed_address_pairs)機制。
在這裏插入圖片描述
每一個 Port 都具備本身的 allowed_address_pairs 數組,它記錄了若干對合法的 IP/MAC 地址映射,以此來支持容許多個 IP 與 Port 連通,從而知足一個 Port 具備多個 IP 地址的需求。

更多可用地址對的詳情請瀏覽官方介紹:https://docs.openstack.org/developer/dragonflow/specs/allowed_address_pairs.html

建立一個 Port

NOTE:Port 是依託於 Network 的,因此首先須要建立一個 Network。
在這裏插入圖片描述
上圖建立了一個簡單的 Port:

  • Port 的 Fixed IP 地址會從 Subnet1 的 IP 地址池中隨機分配,對應 fixed_ips 屬性。
  • 開啓了 Port Security,如今 Port 的 IP/MAC 地址一一綁定,對應 port_security_enabled 屬性。
  • VNIC 類型爲正常(normal),對應 binding:vnic_type 屬性。
  • 沒有指定 「設備 ID(device_id)」 和 「設備所屬者(device_owner)」,因此如今設備的 binding_vif_type 狀態爲 unbound。binding:vif_type 除了用來標識 Port 的綁定狀態(unbound,bound_failed)以外,還用於標識這個 Port 的 Mechanism 類型(e.g. ovs、bridge、macvtap)。
  • 在建立 Port 的同時能夠指定這個 Port 的安全組規則
[root@localhost ~]# openstack port show Port1
+-------------------------+-----------------------------------------------------------------------------+
| Field                   | Value                                                                       |
+-------------------------+-----------------------------------------------------------------------------+
| admin_state_up          | UP                                                                          |
| allowed_address_pairs   |                                                                             |
| binding_host_id         |                                                                             |
| binding_profile         |                                                                             |
| binding_vif_details     |                                                                             |
| binding_vif_type        | unbound                                                                     |
| binding_vnic_type       | normal                                                                      |
| created_at              | 2019-03-08T03:17:23Z                                                        |
| data_plane_status       | None                                                                        |
| description             |                                                                             |
| device_id               |                                                                             |
| device_owner            |                                                                             |
| dns_assignment          | None                                                                        |
| dns_domain              | None                                                                        |
| dns_name                | None                                                                        |
| extra_dhcp_opts         |                                                                             |
| fixed_ips               | ip_address='192.168.1.27', subnet_id='96f33568-70cd-47a2-a0b5-a32a853caa11' |
| id                      | 07995f4e-b6b2-493f-9ce5-b1d945a13807                                        |
| location                | None                                                                        |
| mac_address             | fa:16:3e:0f:ff:74                                                           |
| name                    | Port1                                                                       |
| network_id              | e28bd712-352f-439d-88ea-35a994a4a765                                        |
| port_security_enabled   | True                                                                        |
| project_id              | 031cec3e2df143259d302aa1993fd410                                            |
| propagate_uplink_status | None                                                                        |
| qos_policy_id           | None                                                                        |
| resource_request        | None                                                                        |
| revision_number         | 1                                                                           |
| security_group_ids      | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad                                        |
| status                  | DOWN                                                                        |
| tags                    |                                                                             |
| trunk_details           | None                                                                        |
| updated_at              | 2019-03-08T03:17:23Z                                                        |
+-------------------------+-----------------------------------------------------------------------------+

這裏強調一下 「設備 ID(device_id)」 和 「設備所屬者(device_owner)」 兩個字段,它們共同標識了 Port 的綁定實體。好比:Port 綁定到了一臺虛擬機,那麼 device_id=<instance_uuid>device_owner=compute:nova。常見的 device_owner 還有:

  • network:dhcp — Neutron DHCP Agent 使用的端口,爲 Network 提供 DHCP 服務
  • network:router_interface — Neutron L3 Router Agent 使用的端口,將 Subnet 接入路由器
  • network:router_gateway — Neutron L3 Router Agent 使用的接口,外部網絡接入路由器的網管接口

還須要強調一下的是 Port 的類型,Neutron 支持多種類型的 Port,不一樣類型的 Port 底層可能由不一樣的 Agent 實現(e.g. ovs-agent、sriov-agent)。
在這裏插入圖片描述
只須要提供用戶預期的 IP/MAC 地址,便可爲 Port 添加可用地址對。在可用地址對清單中的 IP/MAC 均可以經過自動或手動的方式應用到這個 Port 所對應的 vNIC(Tap 設備)上。
在這裏插入圖片描述

還須要注意一點,fixe_ips 屬性是一個數值類型,就是說加入一個 Network 下屬具備多個 Subnet,那麼這個 Port 就能夠從多個 Subnets 中獲取多個 IP 地址。e.g.

[root@localhost ~]# openstack port create --network Net2 --fixed-ip subnet=Subnet2-1 --fixed-ip subnet=Subnet2-2 --enable-port-security Port2-1
+-------------------------+--------------------------------------------------------------------------------+
| Field                   | Value                                                                          |
+-------------------------+--------------------------------------------------------------------------------+
| admin_state_up          | UP                                                                             |
| allowed_address_pairs   |                                                                                |
| binding_host_id         |                                                                                |
| binding_profile         |                                                                                |
| binding_vif_details     |                                                                                |
| binding_vif_type        | unbound                                                                        |
| binding_vnic_type       | normal                                                                         |
| created_at              | 2019-03-08T04:21:56Z                                                           |
| data_plane_status       | None                                                                           |
| description             |                                                                                |
| device_id               |                                                                                |
| device_owner            |                                                                                |
| dns_assignment          | None                                                                           |
| dns_domain              | None                                                                           |
| dns_name                | None                                                                           |
| extra_dhcp_opts         |                                                                                |
| fixed_ips               | ip_address='172.16.100.38', subnet_id='0f15d289-26f2-4c83-9538-fae158bf3153'   |
|                         | ip_address='192.168.100.125', subnet_id='7a2fa4b5-c8ca-48e9-94fb-c74f5e59510f' |
| id                      | 51383a86-56b5-4907-8bd2-80801351fc1b                                           |
| location                | None                                                                           |
| mac_address             | fa:16:3e:df:03:8c                                                              |
| name                    | Port2-1                                                                        |
| network_id              | 4472e95b-f2e1-4ff5-8bce-429e1997f3cf                                           |
| port_security_enabled   | True                                                                           |
| project_id              | 031cec3e2df143259d302aa1993fd410                                               |
| propagate_uplink_status | None                                                                           |
| qos_policy_id           | None                                                                           |
| resource_request        | None                                                                           |
| revision_number         | 1                                                                              |
| security_group_ids      | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad                                           |
| status                  | DOWN                                                                           |
| tags                    |                                                                                |
| trunk_details           | None                                                                           |
| updated_at              | 2019-03-08T04:21:57Z                                                           |
+-------------------------+--------------------------------------------------------------------------------+

掛載一個 Port

使用 Port 來啓動一個虛擬機

openstack server create --flavor cirros256 --image cirros-0.3.4-x86_64-disk --port Port1 VM1

在這裏插入圖片描述
須要注意的是,即使 Port 具備多個可用地址對,但虛擬機網卡原生的 IP/MAC 依舊是 Port 的原生 fixed_ips/mac_address。可用地址對的含義是能夠被 Port 使用的 IP/MAC,而非必定會被使用的 IP/MAC。

Port 掛載到虛擬機以後,其自身的信息也會被更新

[root@localhost ~]# openstack port show Port1
+-------------------------+-------------------------------------------------------------------------------------------+
| Field                   | Value                                                                                     |
+-------------------------+-------------------------------------------------------------------------------------------+
| admin_state_up          | UP                                                                                        |
| allowed_address_pairs   | ip_address='192.168.1.28', mac_address='fa:16:3e:0f:ff:28'                                |
|                         | ip_address='192.168.1.29', mac_address='fa:16:3e:0f:ff:29'                                |
| binding_host_id         | localhost.localdomain                                                                     |
| binding_profile         |                                                                                           |
| binding_vif_details     | bridge_name='br-int', datapath_type='system', ovs_hybrid_plug='False', port_filter='True' |
| binding_vif_type        | ovs                                                                                       |
| binding_vnic_type       | normal                                                                                    |
| created_at              | 2019-03-08T03:17:23Z                                                                      |
| data_plane_status       | None                                                                                      |
| description             |                                                                                           |
| device_id               | 6da255cb-402a-4273-844f-5aad549d65e7                                                      |
| device_owner            | compute:nova                                                                              |
| dns_assignment          | None                                                                                      |
| dns_domain              | None                                                                                      |
| dns_name                | None                                                                                      |
| extra_dhcp_opts         |                                                                                           |
| fixed_ips               | ip_address='192.168.1.27', subnet_id='96f33568-70cd-47a2-a0b5-a32a853caa11'               |
| id                      | 07995f4e-b6b2-493f-9ce5-b1d945a13807                                                      |
| location                | None                                                                                      |
| mac_address             | fa:16:3e:0f:ff:74                                                                         |
| name                    | Port1                                                                                     |
| network_id              | e28bd712-352f-439d-88ea-35a994a4a765                                                      |
| port_security_enabled   | True                                                                                      |
| project_id              | 031cec3e2df143259d302aa1993fd410                                                          |
| propagate_uplink_status | None                                                                                      |
| qos_policy_id           | None                                                                                      |
| resource_request        | None                                                                                      |
| revision_number         | 6                                                                                         |
| security_group_ids      | 12519ba0-d1f1-46e3-a0b2-48e6639ee8ad                                                      |
| status                  | ACTIVE                                                                                    |
| tags                    |                                                                                           |
| trunk_details           | None                                                                                      |
| updated_at              | 2019-03-08T03:51:05Z                                                                      |
+-------------------------+-------------------------------------------------------------------------------------------+
  • binding_vif_type — 從 unbound 變成 ovs,表示底層使用的是 OvS Mechanism Driver。
  • binding_host_id — Port 對應的 Tap 設備所在的主機,也就是 VM 所在的計算節點。
  • binding_vif_details — 記錄了一些 Port 掛載後的信息,例如 Tap 設備加入的 OvS Bridge 名稱,OvS Bridge 的類型。
  • device_id & device_owner — 指定綁定 Port 的實體對象(虛擬機)和類型(compute:nova)。

NOTE:從上述信息能夠看出,Port 對應的 Tap 設備實際上實在 「綁定」 以後再建立的,Tap 設備的命名規則爲 tap[port-uuid]。e.g.

[root@localhost ~]# ovs-vsctl show
ccb13b70-cffb-4a64-97b1-734bf9040abc
    Manager "ptcp:6640:127.0.0.1"
        is_connected: true
...
    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
...
        Port "tap07995f4e-b6"
            tag: 5
            Interface "tap07995f4e-b6"
...            
    ovs_version: "2.9.0"

使用具備多個 fixed_ips 的 Port 來啓動一個虛擬機

[root@localhost ~]# openstack server create --flavor cirros256 --image cirros-0.3.4-x86_64-disk --port Port2-1 VM2

在這裏插入圖片描述
在這裏插入圖片描述
奇怪爲何 Port 和虛擬機明明有兩個 IP 地址,但 GuestOS 起來後卻只看見了一個?其實虛擬機啓動獲取 IP 地址時是隨機挑選一個的,不會把 Port 的全部 IP 都配置上去,固然你也能夠手動的將兩個 IP 地址配置上去。總的來講這種作法和應用可用地址對的效果差很少,若是你只是單純的但願虛擬機具備多個 IP,仍是建議你使用可用地址對。但若是你但願虛擬機能夠實現跨網段中斷,那麼就可使用具備多個 Subnet IP 的 Port。

Router

Router 是 Neutron 實現的路由器抽象。

須要注意的是,Router 只是 Neutron 對 Linux 服務器路由功能的抽象,而非 Neutron 實現了一個完整的 vRouter 軟件。這之間有着巨大的區別。Neutron Router 對 Linux 路由功能進行了封裝,並由 L3 Agent 服務進程負責處理。一般的,L3 Agent 只運行在網絡節點中,因此全部的跨網段訪問流量、公網訪問流量都會流經網絡節點。由此,網絡節點成爲了 Neutron 三層網絡的性能瓶頸,爲了解決這個問題,Neutron 提出了 DVR(分佈式路由器)機制,讓每一個計算節點都具備三層網絡功能。

簡而言之,對於上層應用而言,只須要關注 Router 的 端口網關路由表 便可。

NOTE:關於 Linux 路由功能在《Linux 的路由功能》一文中已經介紹過,這裏再也不贅述。

在這裏插入圖片描述
在這裏插入圖片描述

外部網關

Neutron 語義環境中的 「外部網關」 有兩重含義:

  • 實際的外部網關(下文稱爲物理外部網關),在 Neutron 的管理範疇以外。以下圖的 Router_2 上的 Port2。
  • Neutron 的外部網關(下文成爲 Neutron 外部網關,以做區別),在 Neutron 的管理範疇以內。以下圖 Router_1 上的 Port1。

咱們思考一個問題:爲何 Neutron 內部網絡須要經過 Router_1 再與 Router_2 進行鏈接?而不將內部網絡直連 Router_2 而後訪問公網呢?首先明確一點,下圖中 「Neutron 管理的內部網絡」 指的是租戶網絡而非運營商網絡。運營商網絡固然能夠映射到一個直連物理外部網關的物理網絡,這是由於雲管人員手動的爲運營商網絡提供了物理網絡的 「核心三要素」。同理,若是租戶網絡但願直連到物理外部網關,那麼就須要雲管人員提供鏈接到物理外部網關的 「核心要素(e.g. 外部網關的 IP 地址)」。可是,讓雲管人員爲每一個租戶網絡都手動的 「填入」(實際上並不是填入,而是獲取) 「核心要素」 顯然是不明智的,再者物理路由器極可能沒有足夠的接口數量供租戶網絡使用。

在這裏插入圖片描述
出於這樣的前提,Neutron 在租戶網絡和外部網關之間引入了一個內部路由層。每一個租戶均可以建立專屬的內部路由層,包含若干個 L3 Router 實例對象。租戶能夠隨意的將租戶網絡接入內部路由層,而後再由內部路由層統一對接物理外部網關。這樣物理路由器的端口就能獲得更高效的利用。
在這裏插入圖片描述
NOTE:建立多個 L3 Router 實例對象並不是是說須要多個 Linux 服務器,Neutron 經過 qrouter-XXX network namespace 在網絡節點上隔離出了多個 「虛擬路由器」。每建立一個 Router,運行在網絡節點的 L3 Agent 就會新建一個 qrouter-XXX。因爲 qrouter-XXX 具備本身獨立的路由表,因此同一租戶下不一樣的 Network 之間容許具備相同 IP 網段(CIDR)的 Subnet,只要這些 Subnet 只要不鏈接到同一個 Router 上就不會出現 IP 地址重疊的問題。

如上圖,Router_1 對接 Router_2 的方式很簡單,只需在 Router_1 添加一條路由表項便可。e.g.

destination          next_hop     out_interface
104.20.110.0/24  182.24.4.1   Port1(182.21.4.6)

這條路由表項的關鍵信息有三:物理外部網關 IP(182.24.4.1)、Neutron 外部網關 IP(182.21.4.6)以及隱藏信息 —— 運營商網絡(外部網絡)182.24.4.0/24。

NOTE:此處的運營商網絡(外部網絡)182.24.4.0/24 不是上圖中的外部網絡(公網),而是勾選了 「外部網絡」 的運營商網絡。

在這裏插入圖片描述

可見,Neutron 內部網絡要想鏈接公網,關鍵在於如何獲取上述 3 個關鍵信息。辦法很簡單,只須要建立一個運營商網絡(外部網絡)便可,例如:182.24.4.0/24。物理外部網關 IP(182.24.4.1)就是運營商網絡(外部網絡)對應的 Subnet 的 gateway_ip 屬性,而 Neutron 外部網關 IP(182.21.4.6)就是該 Subnet 分配的一個子網 IP 地址。存放這些信息的 Router 資源模型屬性就是 external_gateway_info:

{
	"external_gateway_info": {
		"enable_snat": true,
		"external_fixed_ips": [{
			"ip_address": "182.24.4.6",
			"subnet_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
		}],
		"network_id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3"
	}
}
  • network_id — 運營商網絡(外部網絡)
  • subnet_id — 運營商網絡(外部網絡)的 Subnet(其 gateway_ip 就是物理外部網關 IP 地址)
  • ip_address — Neutron 外部網關 IP 地址
  • enable_snat — Neutron 外部網關端口是否開啓 SNAT 功能

若是開啓了 SNAT,Neutron 就會在 qroute-XXX namespace 中添加這樣的一條 iptables rule,全部從 qg-426c2bcd-f4(如上圖 Port 1)出去的數據包,不管你來自哪裏(源 IP 地址)或者要去哪裏(目的 IP 地址),數據包的源 IP 地址都會被轉換爲 172.18.22.210。

[root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f iptables -nvL -t nat
...

Chain neutron-l3-agent-snat (1 references)
 pkts bytes target     prot opt in     out     source               destination
    2   142 neutron-l3-agent-float-snat  all  --  *      *       0.0.0.0/0            0.0.0.0/0
    2   142 SNAT       all  --  *      qg-426c2bcd-f4  0.0.0.0/0            0.0.0.0/0            to:172.18.22.210
    0     0 SNAT       all  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match ! 0x2/0xffff ctstate DNAT to:172.18.22.210

NOTE:添加這條 SNAT 的緣由是,Linux 做爲路由器時僅僅開啓了內核的路由轉發功能是不夠的,還須要添加這麼一條 SNAT。那爲何不須要添加 DNAT 呢?由於做爲路由器而言,全部的數據包都只是 SNAT(送出去)而已。

新建 Router 和外部網關端口

從上文可一直,當咱們但願 Neutron 內部網絡連通公網時,就建立一個運營商網絡(外部網絡),而後使用 L3 Router 將內部網絡與運營商網絡(外部網絡)直連起來便可。e.g.

  • 新建 Router。若是此時用戶選擇了 「外部網絡」,那麼 Neutron 會自動的建立 Router1 與 public 直連的外部網關端口。須要注意的是,只有這一種狀況下 Neutron 會自動建立路由端口,不然都須要用戶手動建立。
    在這裏插入圖片描述
  • 內部網絡 Net1 和運營商網絡(外部網絡)public 經過 Router 直連
    在這裏插入圖片描述
  • public 有一個端口鏈接到 Router,類型爲 network:router_gateway,IP 爲 172.18.22.210(Neutron 外部網關 IP)。
    在這裏插入圖片描述
  • public 的 Subnet public-subnet 具備物理外部網關 IP 地址 172.18.22.1。
[root@localhost ~]# openstack subnet show public-subnet
+-------------------+--------------------------------------+
| Field             | Value                                |
+-------------------+--------------------------------------+
| allocation_pools  | 172.18.22.201-172.18.22.210          |
| cidr              | 172.18.22.0/24                       |
| created_at        | 2019-02-13T03:41:09Z                 |
| description       |                                      |
| dns_nameservers   |                                      |
| enable_dhcp       | False                                |
| gateway_ip        | 172.18.22.1                          |
| host_routes       |                                      |
| id                | 99749410-a6b0-418b-9ebc-fd060e1a746e |
| ip_version        | 4                                    |
| ipv6_address_mode | None                                 |
| ipv6_ra_mode      | None                                 |
| location          | None                                 |
| name              | public-subnet                        |
| network_id        | 282e146e-6948-436f-992c-f2d50588e357 |
| project_id        | 031cec3e2df143259d302aa1993fd410     |
| revision_number   | 0                                    |
| segment_id        | None                                 |
| service_types     |                                      |
| subnetpool_id     | None                                 |
| tags              |                                      |
| updated_at        | 2019-02-13T03:41:09Z                 |
+-------------------+--------------------------------------+
  • Router 具備兩個端口,一個鏈接 Net1,IP 地址是 Net1 的網關 IP 192.168.1.1;另外一個鏈接 public,IP 是 Neutron 外部網關 IP 172.18.22.210。
    在這裏插入圖片描述

  • Router1 的路由表和網絡設備:

[root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

qg-426c2bcd-f4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.22.210  netmask 255.255.255.0  broadcast 172.18.22.255
        inet6 fe80::f816:3eff:fe0b:c722  prefixlen 64  scopeid 0x20<link>
        ether fa:16:3e:0b:c7:22  txqueuelen 1000  (Ethernet)
        RX packets 765265  bytes 36064217 (34.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 90  bytes 5316 (5.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

qr-cf018461-bc: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.1  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::f816:3eff:fea5:b2bb  prefixlen 64  scopeid 0x20<link>
        ether fa:16:3e:a5:b2:bb  txqueuelen 1000  (Ethernet)
        RX packets 216  bytes 18796 (18.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 139  bytes 13870 (13.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f route -nne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         172.18.22.1     0.0.0.0         UG        0 0          0 qg-426c2bcd-f4
172.18.22.0     0.0.0.0         255.255.255.0   U         0 0          0 qg-426c2bcd-f4
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0          0 qr-cf018461-bc

其中 qr-XXX 和 qg-YYY 在 qroute-XXX namespace 中用於鏈接 br-int 和 br-ex,從第2、三條路由表項是由鏈路層協議發現並建立的,分別是 Net1 和 public 子網的直連路由,由設備 qr-XXX 和 qg-YYY 轉發。而第一條路由表項,則是由 Neutron 添加的默認靜態路由,Neutron 從 public 的 gateway_ip 獲取到物理外部網關 IP 地址,全部非直連路由的子網訪問流量,都會使用默認靜態路由進行轉發。

Router 的路由表

咱們知道路由器的路由表項分配靜態路由和動態路由,Neutron 中全部的路由表項均爲靜態路由(不遵循動態路由協議),又細分爲:

  • 靜態路由
  • 靜態默認路由
  • 直連路由

其中只有 「靜態路由」 一種類型會被 Neutron 記錄在 Router 資源模型的 routes(數值類型)屬性中。

MariaDB [neutron]> desc routerroutes;
+-------------+-------------+------+-----+---------+-------+
| Field       | Type        | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| destination | varchar(64) | NO   | PRI | NULL    |       |
| nexthop     | varchar(64) | NO   | PRI | NULL    |       |    # 目的網段( CIDR) 
| router_id   | varchar(36) | NO   | PRI | NULL    |       |    # 下 一 跳 IP(下一個路由器的端口 IP)
+-------------+-------------+------+-----+---------+-------+

後面兩種路由表項類型爲何不被持久化呢?由於靜態默認路由的信息來自外部網絡的 gateway_ip 屬性,而直連路由則由鏈路層協議自動發現、建立。因此不須要額外的持久化。

那麼靜態路由又是什麼路由,爲何要持久化呢?
在這裏插入圖片描述

+----------------+-------------+--------------------------------------+
| destination    | nexthop     | router_id                            |
+----------------+-------------+--------------------------------------+
| 192.168.1.2/32 | 172.18.22.1 | a1adb970-dba9-49f0-ba4b-4294f0d07f6f |
+----------------+-------------+--------------------------------------+

如上,Router 的靜態路由其實就是常規的主機路由或網絡路由,使用戶自定義的路由表項。但須要注意的是,「下一跳」 表單不能隨意亂填,而是限制在 Router 夠得着的子網 IP 地址。不然就會 the nexthop is not connected with router

NOTE:在講述 Subnet 資源模型的章節中你或許會對 Subnet 的 gateway_ip 和 host_routes 屬性感到疑惑,通本章的舉例,或許你可以更好的理解這些字段所具備的功能和含義。

Floating IP

從功能的角度能夠將 Neutron 的 IP 分爲:

  • Fixed IP:由租戶網絡 Subnet 分配,用於虛擬機的內部通信,建立虛擬機時自動分配。
  • Floating IP:有外部網絡分配,用於虛擬機訪問公網,須要手動分配。

前文中咱們提到過,通常的運營商網絡和勾選了 「外部網絡」 的運營商網絡的主要區別在於後者能夠分配 Floating IP,實現的原理就是內部網絡與外部網絡之間的 Router(qroute-XXX)的 NAT(SNAT&DNAT)功能。
在這裏插入圖片描述
上圖可見,外部網絡 public 成爲了一個 Floating IP 地址池。建立一個 Floating IP 會相應的建立一條數據庫記錄:

MariaDB [neutron]> select * from floatingips\G;
*************************** 1. row ***************************
          project_id: 031cec3e2df143259d302aa1993fd410
                  id: 67272b76-493a-4a60-b63a-22e5aefebfc0
 floating_ip_address: 172.18.22.204
 floating_network_id: 282e146e-6948-436f-992c-f2d50588e357
    floating_port_id: 04aceef8-a3b2-46e4-a815-3104a2031ed7
       fixed_port_id: NULL
    fixed_ip_address: NULL
           router_id: NULL
last_known_router_id: NULL
              status: DOWN
    standard_attr_id: 82
1 row in set (0.01 sec)

在這裏插入圖片描述
在這裏插入圖片描述
爲虛擬機綁定這個 Floating IP 以後,數據庫記錄會被更改:

MariaDB [neutron]> select * from floatingips\G;
*************************** 1. row ***************************
          project_id: 031cec3e2df143259d302aa1993fd410
                  id: 67272b76-493a-4a60-b63a-22e5aefebfc0
 floating_ip_address: 172.18.22.204
 floating_network_id: 282e146e-6948-436f-992c-f2d50588e357
    floating_port_id: 04aceef8-a3b2-46e4-a815-3104a2031ed7
       fixed_port_id: 07995f4e-b6b2-493f-9ce5-b1d945a13807
    fixed_ip_address: 192.168.1.27
           router_id: a1adb970-dba9-49f0-ba4b-4294f0d07f6f
last_known_router_id: NULL
              status: ACTIVE
    standard_attr_id: 82
1 row in set (0.00 sec)

qroute-XXX 中的 iptables rules 也會相同的添加 SNAT、DNAT 規則:

[root@localhost ~]# ip netns exec qrouter-a1adb970-dba9-49f0-ba4b-4294f0d07f6f iptables -nvL -t nat
...

# 到目的 IP 地址 172.18.22.204 的數據包 DNAT 至 192.168.1.27
Chain neutron-l3-agent-PREROUTING (1 references)
 pkts bytes target     prot opt in     out     source               destination
    ...
    0     0 DNAT       all  --  *      *       0.0.0.0/0            172.18.22.204        to:192.168.1.27
...

# 從源 IP 地址 192.168.1.27 來的數據包 SNAT 至 172.18.22.204
Chain neutron-l3-agent-float-snat (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 SNAT       all  --  *      *       192.168.1.27         0.0.0.0/0            to:172.18.22.204
...

這一切都發生在 qroute-XXX network namespace 中,實現了不一樣 Router 之間的網絡隔離。

相關文章
相關標籤/搜索