圖資料庫驅動的基礎設施運維實操

NebulaGraph發表於2023-04-12

本文系圖技術在大型、複雜基礎設施之中 SRE/DevOps 的實踐參考,並以 OpenStack 系統之上的圖資料庫增強的運維案例為例,揭示圖資料庫、圖演演算法在智慧運維上的應用。本文所有示例程式碼開源。

最近,有些尚未使用過圖技術、DevOps/Infra 領域的工程師在 NebulaGraph 社群詢問是否有「圖技術在運維的應用」相關案例參考。於是,我又可以“借題發揮”來實踐下如何利用圖的能力與優勢去幫助運維工程師們基於複雜基礎設施上構建輔助運維繫統。如果你對本文有任何看法,歡迎評論區或者來論壇和我交流下,非常感謝。

通常,我們說的複雜的基礎設施運維環境指的是資源(manifest)繁多且分佈在不同層面的系統。為了讓實踐更加真實、貼近實際的運維情況,讓運維問題複雜又可控,這裡我選擇了用一個基礎設施平臺:OpenStack。在 OpenStack 系統上,我分別利用 Push 和 Pull 兩種模式將資源在圖模型中對應點、邊資訊載入到 NebulaGraph 的 Graph ETL 管道的路徑中。

在我們基於運維資源構建的圖譜,會做如下用例圖探索:

  • 告警、狀態的推理與傳導;
  • 網路直連與互聯關係;
  • 映象、雲盤、快照血緣管理;
  • 高相關性虛機預警;
  • 秘鑰洩漏的圖上風控分析;
  • 映象、雲盤漏洞範圍分析;
  • 宿主機逃離影響範圍分析;
  • 脆弱依賴資源檢測;

實驗環境搭建

背景知識

OpenStack 是一個開源的雲端計算平臺,提供了類似於 AWS 的雲服務。它提供了一組可插拔的模組,包括了計算,儲存和網路等功能,可以幫助使用者構建和管理雲環境。OpenStack 採用分散式架構,支援多種作業系統和硬體平臺,可以在企業級和服務提供企業級環境中使用。

OpenStack-overview-diagram-new

最初,OpenStack 是由 NASARackspace Inc. 發起的 Nova(虛擬化計算專案)和 Swift(相容 S3 的物件儲存)專案組成。隨著專案的發展,OpenStack 現在已經有非常多不同的子專案:

openstack-map-v20221001

本次實踐中涉及到 OpenStack 的主要專案有:

  • Nova 是 OpenStack 的計算服務,用於管理虛擬機器;
  • Cinder 是 OpenStack 的塊儲存服務,用於管理雲端儲存;
  • Neutron 是 OpenStack 的網路服務,用於管理雲網路;
  • Glance 是 OpenStack 的映象服務,用於管理雲映象;
  • Horizon 是 OpenStack 的視覺化控制檯服務。

除此之外,我還引入了 Vitrage 專案輔助我們收集部分資源資料:

  • Vitrage 是 OpenStack 的一個高階分析和視覺化工具,用於分析和視覺化 OpenStack 環境中的資源和事件。它可以彙集來自 OpenStack 各個服務的資料,並以視覺化的方式呈現。Vitrage 能發現和診斷問題,提高 OpenStack 環境的可用性和可維護性。

得益於 OpenStack Decouple 的設計理念,Vitrage 可以很容易、無侵入式(只用修改要收集的服務的兩行配置)就可以在 OpenStack 的訊息佇列中訂閱資源資訊的 Push 訊息。

不過,介於 Vitrage 許久沒有大更新,且維護維艱,比如:在 zed 裡 Vitrage Dashboard 作為 Horizon 外掛已經無法正常工作了。所以,本實踐只利用它的資源收集能力。

環境準備搭建

NebulaGraph 叢集

快速試玩 NebulaGraph 的話,安裝有這麼幾個選項:

OpenStack 叢集

本文需要的 OpenStack 叢集是一個多機的環境,因此我在 Linux Server 上用 Libvirt 和 Linux Bridge 搭建了多個虛擬機器來模擬 OpenStack 的物理機。得益於 CPU 的巢狀虛擬化和 QEMU,我們完全可以在虛擬機器搭建的實驗環境中模擬可正常工作的 OpenStack Nova instance 虛機。

虛擬機器搭建完成後,還需要模擬真實的多資源 Infra 環境。這邊略去具體的操作步驟,感興趣的小夥伴可以閱讀本文的參考文獻,當中有詳細的實踐過程。

完成 OpenStack 環境的搭建後,我們透過 Horizon Dashboard 檢視叢集和資源:

虛擬機器情況:

nova_instance

網盤情況,其中四個掛載在不同的虛擬機器上

cinder_volume

叢集租戶的網路拓撲:

neutron_topology

透過 OpenStack Vitrage 的 API/CLI 可獲得部分主要資源的拓撲:

source openrc admin admin
vitrage topology show --all-tenants

這個結果是一個 JSON,資料已經是邊(links)和點(nodes)的序列化圖結構了。

{
  "directed": true,
  "graph": {},
  "links": [
    {
      "vitrage_is_deleted": false,
      "relationship_type": "contains",
      "source": 0,
      "target": 11,
      "key": "contains"
    },
    {
      "vitrage_is_deleted": false,
      "relationship_type": "contains",
      "source": 0,
      "target": 13,
      "key": "contains"
    },
...
    {
      "vitrage_is_deleted": false,
      "relationship_type": "attached",
      "source": 27,
      "target": 28,
      "key": "attached"
    }
  ],
  "multigraph": true,
  "nodes": [
    {
      "id": "node0",
      "vitrage_type": "nova.host",
      "vitrage_category": "RESOURCE",
      "vitrage_is_deleted": false,
      "update_timestamp": "2023-01-13T08:06:48Z",
      "vitrage_sample_timestamp": "2023-01-13T08:06:49Z",
      "vitrage_is_placeholder": false,
      "vitrage_id": "630b4c2c-5347-4073-91a3-255ec18dadfc",
      "name": "node0",
      "vitrage_cached_id": "d043d278a6a712909e30e50ca8ec2364",
      "is_real_vitrage_id": true,
      "vitrage_aggregated_state": "AVAILABLE",
      "vitrage_operational_state": "OK",
      "vitrage_datasource_name": "nova.host",
      "state": "available",
      "graph_index": 0
    },
    {
      "id": "nova",
      "vitrage_type": "nova.zone",
      "vitrage_category": "RESOURCE",
      "vitrage_is_deleted": false,
      "vitrage_sample_timestamp": "2023-01-12T03:06:48Z",
      "vitrage_is_placeholder": false,
      "vitrage_id": "a1e9c808-dac8-4b59-8f80-f21a90e9869d",
      "vitrage_cached_id": "125f1d8c4451a6385cc2cfa2b0ba45be",
      "is_real_vitrage_id": true,
      "vitrage_aggregated_state": "AVAILABLE",
      "vitrage_operational_state": "OK",
      "state": "available",
      "update_timestamp": "2023-01-12T03:06:48Z",
      "name": "nova",
      "vitrage_datasource_name": "nova.zone",
      "graph_index": 1
    },
...
  "raw": true
}

圖譜建模

將資源對映成圖譜:

  • nova instance 是 Nova 服務中的虛擬機器例項,每個 nova instance 都有自己的配置資訊(如 CPU、記憶體、磁碟等),有時候我們就叫它 server 或者 VM、虛機。
  • nova host 是 Nova 服務中的物理主機,是 nova instance 執行的物理環境。nova host 上面會執行 nova-compute 服務,這個服務負責管理和排程 nova instance。nova host 上面還可能執行其他服務,如網路服務等。
  • nova keypair 是 Nova 服務中的金鑰對,用於訪問 nova instance。
  • cinder volume 是 Cinder 服務中的雲端儲存卷,可以 attach 到 nova instance 上做為硬碟。
  • cinder snapshot 是 Cinder 服務中的雲端儲存快照,可以在 cinder volume 上做快照。
  • glance image 是 Glance 服務中的映象,可以作為建立 nova instance 時候的啟動硬碟。
  • neutron network 是 Neutron 服務中的網路,可以用於配置 nova instance 的網路連線。
  • neutron port 是 Neutron 服務中的埠,用來連線 nova instance 和 neutron network 之間。在 nova instance 虛擬機器上,如果不是 trunk port 的話,一個 port 常常對應一個網路卡。

它們之間的關係如下:

schema_draft

基礎設施圖 ETL

接下來我們解決從基礎設施中抽取資源後設資料的問題:

push 模式

這裡的 push 指的是以基礎設施為出發點,透過事件驅動主動地發出資源變動的資訊。它的好處是實時掌握資源情況,壞處是過於依賴基礎設施,很多非常瘦的、軟體定義/可程式設計程度不高的元件、某些硬體裝置是沒有 push 機制的。比如:有些年份的軟體系統不一定能存在 push 的介面,改造起來有侵入性。

前面提及過,OpenStack 自身是存在 Push Hook 機制的,它的子專案 vitrage 就利用這個機制很優雅地收集系統資源、告警等資訊進入圖中,類似的機制在其他平臺中也是可以實現的。

本實驗中我們就利用 vitrage 的機制去收集一部分圖譜中的資源資訊,如下圖,可以看到 vitrage 會在 OpenStack message bus 中訂閱 nova/cinder/neutron 等服務中的資源時間,把事件傳入 Entity Queue,經過處理,儲存到 Entity Graph 中。

在此之上,我們可以透過 vitrage API 獲取圖譜的拓撲,來消費它。

注意:實際上 Vitrage 服務還提供了推理告警、推理狀態、定義決策事件的能力,這裡我們並沒有採用,後邊我們在圖上做的一些事情甚至還和它的能力有一些重疊。

vitrage_arch

這裡我只是用它來展示 push 模式的工作機制,如果沒有 Virtrage 這個專案存在,我們也可以比較容易透過 OpenStack 的 oslo.messaging 這個庫很容易寫出在 Message Bus(可能是 Kafka, RabbitMQ 等不同底層實現)上訂閱資源時間的應用,然後把事件透過 Flink/ Kafka/ Pulsar 等方式接駁 NebulaGraph。

因為 Vitrage 的存在,我就偷懶不用去實現這部分邏輯,只消寫一小部分程式碼呼叫 Vitrage API 取這個資料就可以了,諷刺的是,從這個角度來看,這其實是一種 pull 的模式了,不用拘泥它本質上算是哪一種方式,至少在資源發起測,我們把它當做 push 模式的例子看待吧。

這部分從 Vitrage 抓取的程式碼我放在 https://github.com/wey-gu/openstack-graph/blob/main/utils/vitrage_to_graph.py 了,呼叫方式很簡單,在有 OpenStack 客戶端的環境中,執行它就可以了,比如:

# 連到 node0 上
ssh stack@node0_ip

# 進入 devstack 目錄
cd devstack

# 下載 vitrage 中圖資料,解析為 NeublaGraph DML/DQL 的工具
wget https://raw.githubusercontent.com/wey-gu/openstack-graph/main/utils/vitrage_to_graph.py

# 執行它
python3 vitrage_to_graph.py

執行之後,會生成如下檔案:

  • schema.ngql 圖資料的 Schema 定義
  • vertices/ 點資料的資料夾
  • edges/ 邊資料的資料夾

pull 模式

反過來,pull 模式是從資源外部定期或者事件驅動地拉取資源,存入圖譜的方式。剛好,本實驗中 Vitrage 抓取的資源是有限,部分額外的資源單獨寫了 Python 的程式碼來主動全量抓取。pull 模式的好處是對資源方沒有任何侵入性,只需要呼叫它的介面獲取資訊就可以,壞處則是有的系統不太容易獲得增量變化,可能只能全量去取。

這部分我抓取的關係如下:

  • glance_used_by:image -[:used_by]-> instance (get from instance)
  • glance_created_from:image -[:created_from]-> volume (get from image)
  • nova_keypair_used_by:keypair -[:used_by]-> instance (get from instance)
  • cinder_snapshot_created_from:volume snapshot -[:created_from]-> volume (get from snapshot)
  • cinder_volume_created_from:volume -[:created_from]-> volume snapshot (get from volume)
  • cinder_volume_created_from:volume -[:created_from]-> image (get from volume)

程式碼在 https://github.com/wey-gu/openstack-graph/blob/main/utils/pull_resources_to_graph.py。在真實場景下,我們可能會用 Apache Airflow、Dagster 甚至是 Cron Job 等方式定期執行它。

手動執行的方式也很簡單:

# 連到 node0 上
ssh stack@node0_ip

# 進入 devstack 目錄
cd devstack

# 下載抓取 OpenStack 資源,生成 NeublaGraph DML/DQL 的工具
wget https://raw.githubusercontent.com/wey-gu/openstack-graph/main/utils/pull_resources_to_graph.py.py

# 執行它
python3 pull_resources_to_graph.py

執行之後,會生成點、邊的 nGQL 語句在兩個資料夾下:

  • vertices/ 點資料的資料夾
  • edges/ 邊資料的資料夾

載入資料到 NebulaGraph

我們只需要在 NebulaGraph Studio Console,Explorer Console 或者 NebulaGraph 命令列 Console 中執行上邊生成的 .ngql 檔案就好了:

# DDL from vitrage
cat schema.ngql

# DDL and DML for both push and pull mode data
cat edges/*.ngql
cat vertices/*.ngql

之後,在 NebulaGraph 中我們會有一個叫做 openstack 的圖空間,用這個查詢可以查到所有資料:

MATCH (n) WITH n LIMIT 1000
OPTIONAL MATCH p=(n)--()
RETURN p, n

在 NebulaGraph Explorer 中渲染,手動設定一下資料的圖示,就可以看到 OpenStack 叢集裡的所有租戶的資源圖了:

all_graph_view

接下來,我們終於可以在圖上看看有意思的洞察了!

基於圖譜的基礎設施運維示例

作為非 SRE、DevOps 人員,我嘗試藉由自己在 OpenStack 和圖技術的理解模擬出以下例項,希望對你有所幫助。

告警、狀態的推理與傳導

受啟發於 Vitrage 專案,我們可以藉助資源圖譜實時圖查詢、圖計算甚至圖視覺化能力,在圖上推理、傳導資訊,把重要的事件藉由圖上組織好的知識分發給需要收到通知的人、組織、系統。

一個簡單的例子,我們在 nova host(虛擬機器的宿主機、Hypervisor 機器,以下簡稱宿主機)中獲得了一個告警、事件的時候,可能是網路卡失敗、物理硬碟預警、CPU 佔用過高之類的告警。藉助圖譜查詢獲得所有相關聯的虛機後,再把 WARN 級別的告警發出去或者設定它們為亞健康 unhealthy 狀態。

獲得通知的物件,往往是一些使用者系統,它們可以根據預先定義好的策略做些自動化運維,或者通知 Hook:

  • 收到“宿主機 CPU 過高”的告警情況,根據使用者自己設定的不同策略把虛機遷移走,或者採用更高階、複雜的撤離方式,像是開始不接受新流量,建立新的替代 workload,再優雅地關閉這個 workload;
  • “控制面網路故障”告警情況,這時候往往無法成功進行主機的撤離、遷移,故可以考慮觸發備份主機、啟動新 workload、關機;
  • 其他“亞健康狀態”,可以作為負載層面出問題的根因分析 RCA 依據。

這裡給出一個在圖譜上進行告警、狀態傳遞的查詢例子。假設 vid 為 node0 的宿主機出現了高 CPU 告警,下面這個查詢可以得到所有該宿主機上的虛機,獲得時間、告警通知列表:

MATCH (vm:nova_instance)<-[:`contains`]-(host_CPU_high:nova_host)
    WHERE id(host_CPU_high) == "node0"
RETURN vm.nova_instance.name AS VM_to_raise_CPU_alarms

這條語句的查詢圖模式是從 host_CPU_high 這個 nova_host 向外經由 contains 這個關係指向 vm 這個 nova_instance 的。

(vm:nova_instance)<-[:`contains`]-(host_CPU_high:nova_host)

它的結果是:

VM_to_raise_CPU_alarms
server-4
server-3
server-1
server-0

如果我們把查詢改動一下,選擇輸出全路徑,則可以看到這個資訊傳遞的方向:

MATCH p=(vm:nova_instance)<-[:`contains`]-(host_CPU_high:nova_host)
    WHERE id(host_CPU_high) == "node0"
RETURN p

我們在 Explorer 中渲染下,點選 N 跳檢測:

all_graph_view

這個例子比較簡單,甚至用不到圖能力。因為一跳查詢在表結構中也能很輕鬆地用一、兩個 nova API call 搞定。實際上,圖上是可以做很多更 Graphy(具有圖屬性的)、複雜、獨特的工作的,我們慢慢來看:

網路可達檢測

考慮下這樣的場景,在 OpenStack 中,不同的主機可以連線到相同的子網 VPC,主機也可以連線到多個子網之中。這樣,主機之間的網路連通性資訊、與網路聯通相關的推理、傳導都可以在圖上進行。

在真實世界中,這裡可能還要考慮 Security Group、Router、Switch 等因素。本示例中我們用到的 OpenStack 是比較簡化的 L2 only Setup。

我們要獲得與虛機 server_a 同一 VPC 的所有其他虛機很容易表達:

MATCH (server_a)--(:neutron_port)--(:neutron_network)--(:neutron_port)--(server_b:`nova_instance`)
    WHERE id(server_a) == "server-0"
RETURN server_b.nova_instance.name AS L2_connected_server

結果如下:

L2_connected_server
server-1

看起來很初級,接下來我們再查詢與虛機 server_a 同一 VPC、有可能透過跨網路虛機而互聯的主機的所有其他虛機。這時候,我們除了共享 neutron network(VPC) 的情況,還要查詢所有二層直連的虛機可能透過其他 VPC 連出去的的虛機。下面的例子,我們用到了 OPTIONAL MATCH 的表達,表示可能匹配到的模式:

MATCH (server_a)--(:neutron_port)--(net:neutron_network)--(:neutron_port)--(server_b:`nova_instance`)
    WHERE id(server_a) == "server-0"
OPTIONAL MATCH (server_b)--()--(other_net:neutron_network)--(:neutron_port)--(server_c:`nova_instance`)
    WITH server_a, server_b AS same_subnet_machines, server_c AS routeable_machines WHERE routeable_machines != server_a

RETURN same_subnet_machines.nova_instance.name AS L2_connected_server,
       routeable_machines.nova_instance.name AS cross_vpc_server

可以看到結果裡,跨網路潛在的相連主機還有 server-3:

L2_connected_servercross_vpc_server
server-1server-3

視覺化下,同樣,我們修改輸出為路徑 pp1

MATCH p=(server_a)--(:neutron_port)--(net:neutron_network)--(:neutron_port)--(server_b:`nova_instance`)
    WHERE id(server_a) == "server-0"
OPTIONAL MATCH p1=(server_b)--()--(other_net:neutron_network)--(:neutron_port)--(server_c:`nova_instance`)
RETURN p, p1

它可能的連線路徑一目瞭然了:

cross_vpc_vm

有了獲得這些資訊的能力,我們可以可程式設計地連線告警、狀態、安全風控、網路等方方面面系統。當然這不是本文的重點,就不加以贅述了,你有相關的實踐想要分享的話,記得來 NebulaGraph 社群

下面,我們來看看儲存相關的例子。

映象、雲盤、快照的血緣

在基礎設施中,雲盤(iSCSI、Ceph、NFS)、映象、快照之間有多重複雜的關係,比如:

  • 一個系統映象可能從某一個虛擬機器掛載的雲盤或者一個快照建立
  • 一個雲盤可能是從一個系統映象、一個快照或者另一個雲盤建立
  • 一個快照是從一個雲盤建立的

這種血緣資訊的識別和管理是很有必要的。下面的查詢可以獲得指定虛機 server-0 的所有儲存血緣:

MATCH p=(server_a)-[:`attached`|created_from|used_by]-(step1)
    WHERE id(server_a) == "server-0"
OPTIONAL MATCH p1=(step1)-[:created_from*1..5]-(step2)
    RETURN p, p1

我們可以看到結果中:

  • server-0 的啟動映象(這裡它是從本地盤啟動的,沒有掛載雲盤)是從 volume-1 建立的;
  • volume-1 是從 cirros-0.5.2-x86_64-disk 這個映象建立的;

此外,還有其他有分叉關係的儲存資源和它們也息息相關:

storage_lineage_0

下面,我們不只考慮儲存資源,看下涉及雲盤 cinder_volume 掛載 attached 這層關係下的血緣關係:

MATCH p=(server_a)-[:`attached`|created_from|used_by]-(step1)
    WHERE id(server_a) == "server-4"
OPTIONAL MATCH p1=(step1)-[:created_from|attached*1..5]-(step2)
    RETURN p, p1

storage_lineage_1

我們可以從渲染圖中讀出這樣的洞察:

  • server-4 的啟動映象(這裡它是從本地盤啟動的)是從 volume-1 建立的

    • volume-1 現在掛載在 server-6
    • volume-1 是從 cirros-0.5.2-x86_64-disk 這個映象建立的
    • 同樣 cirros-0.5.2-x86_64-disk 映象被很多其他虛機在採用
  • server-4 同時掛載了資料盤 volume-2

    • volume-2 是一個多掛載的盤,它同時掛載在 server-3 之上
    • server-3 的系統啟動盤是從快照 snapshot-202301111800-volume-1 克隆建立的
    • 快照 snapshot-202301111800-volume-1 是曾經從 volume-1 建立的
    • volume-1 現在掛載在 server-6 上,快照不一定是從 server-6 而來,因為映象可能被重新掛載過。而這些血緣資訊可以被用在資源生命週期管理、根因分析、安全告警、狀態傳遞上,這裡不加以贅述。

高相關性虛機預警

下面這個例子,會給出一個節點相似度的應用。在全圖或者子圖上,利用圖演演算法找到與指定虛機圖拓撲結構最相似的其他虛機,並在這種相關性基礎上增加新的關係,做風險事件預警。

本次實踐,我們會按照一個典型的從「快速子圖驗證」到「全圖生產應用」的工作流。

子圖快速驗證:瀏覽器內演演算法

server-0 的三度子圖上做演演算法的驗證:

GET SUBGRAPH 3 STEPS FROM "server-0"
YIELD VERTICES AS nodes, EDGES AS relationships;

將結果渲染在畫布上,我們可以看到子圖中包含了其他幾個虛機:

server_subgraph

我們利用 Explorer 中的瀏覽器內圖演演算法,可以非常方便地驗證我們的想法。這裡,我們使用 Jaccard Similarity 相似性演演算法,讓 server-0server-1,server-3,server-4,server-6 迭代分別得到相似性:

jacc_sim_browser

可以看出,在 3 步子圖內,和 server-0 最近接的虛機是 server-4。進一步,我們可以簡單在子圖上看看兩者之間的路徑作為相似性的解釋:

sim_explain

在這個可解釋結果中,我們知道 server-0server-4 相似的原因可能是:

  • 坐落在同一個宿主機:node-0
  • 使用同一個映象:cirros_mod_from_volume-1

因此,我們最終落地的預警機制可能是,當 server-0 出現某一問題、告警時候,給相似的 server-4 也設定預警,預警理由就是它們在同樣主機、同樣映象。

全圖生產應用

有了上面的快速實驗,藉助 Workflow + NebulaGraph Analytics 把它落地為全圖上的演演算法,利用 Analytics 分散式能力去執行。

在生產上,我們利用 Workflow 的 DAG 編排能力,建立兩個前後相連的任務:

  • 取臨近虛機
  • 全圖算相似度

第一個任務如下,實時從指定虛機出發給出其他虛機 vid。這裡查詢語句寫死了 server-0,但是在 Workflow 裡可以引數化,並封裝任務為可被 API 觸發的非同步服務:

MATCH (n)-[*1..5]-(m:`nova_instance`)
    WHERE id(n) == "server-0" AND n != m
RETURN distinct id(m)

query_sim_server

而在 Jaccard Similarity Job 中,我們選擇 ids1 為 server-0,ids2 從上游(上面的 Query Job)取,選擇在 OpenStack 全圖掃描所有邊型別。

jacc_sim_workflow

儲存、執行。結果如下,這次它運算了更多的目標虛機,並且迭代作用範圍是全圖而非一個子圖。可以看到同上次的結果是一樣,因為子圖上關聯度大的點和相近的邊在 Jaccard 演演算法裡起到了更主要的作用。

jacc_result

安全相關場景

基礎設施資源中的關聯關係和金融、內容系統、電商領域的風控場景有相似的地方,很多場景本質上利用到了圖譜關係中的知識,在相簿上實時獲取這些複雜但又天然可解釋的安全洞察。

秘鑰洩漏風控分析

先看一個秘鑰洩漏的場景:假設 key-0 被安全部門確定被洩漏了,我們可以在毫秒內獲得如下查詢:

  • 直接使用該金鑰的虛機
  • 與使用該秘鑰的虛機網路直連的機器
  • 與使用該秘鑰的虛機跨網路相連的機器
MATCH (key_leaked)-[:`used_by`]->(involved_server:nova_instance)--(:neutron_port)--(net:neutron_network)--(:neutron_port)--(server_b:nova_instance)
       WHERE id(key_leaked) == "key-0"
OPTIONAL MATCH (server_b)--()--(other_net:neutron_network)--(:neutron_port)--(server_c:nova_instance)
    WITH involved_server, server_b AS same_subnet_machines, server_c AS cross_net_machines
        WHERE cross_net_machines != involved_server
RETURN involved_server.nova_instance.name AS with_key,
        same_subnet_machines.nova_instance.name AS l2_vms,
        cross_net_machines.nova_instance.name AS cross_vpc_vms

貼一下部分結果,我們知道 server-4 採用了這個 keypair,並且 server-6 和它在同一個網路。同時,有一定機率透過 server-6、server-1、server-2、server-0、server-5 也受到了影響。針對這種情況,相關的機器可以設定不同告警級別來降低安全事故的影響。

with_keyl2_vmscross_vpc_vms
server-4server-6server-1
server-4server-6server-2
server-4server-6server-0
server-4server-6server-5

這個查詢改造為視覺化結果:

MATCH p=(key_leaked)-[:`used_by`]->(involved_server:nova_instance)--(:neutron_port)--(net:neutron_network)--(:neutron_port)--(server_b:nova_instance)
    WHERE id(key_leaked) == "key-0"
OPTIONAL MATCH p1=(server_b)--()--(other_net:neutron_network)--(:neutron_port)--(server_c:nova_instance)
RETURN p,p1

在 Explorer 中應用 Dagre-LR 的佈局,相關的關聯關係可以很清晰地被展示出來。介於視覺化展示的直觀性,我們可以考慮把這個圖放入安全報告,隨同其他安全資訊一同分發給虛機租戶。

key_leaked

映象、雲盤漏洞範圍分析

類似的,一個映象被掃出漏洞,我們可以瞬間查到波及的資源,並做出相應應對之策。

映象檔案有漏洞:

MATCH p=(image_risky)-[:`created_from`]-(step1)
    WHERE id(image_risky) == "cirros-0.5.2-x86_64-disk"
OPTIONAL MATCH p1=(step1)-[:created_from*1..5]-(step2)
RETURN p, p1

image_vulnerability

某個雲盤有漏洞:

MATCH p=(volume_risky)-[:`created_from`]-(step1)
    WHERE id(volume_risky) == "volume-1"
OPTIONAL MATCH p1=(step1)-[:created_from*1..5]-(step2)
RETURN p, p1

volume_vulnerability

潛在宿主機逃離影響範圍分析

最後,我們討論一個嚴重的安全問題:宿主機逃離。

在極端的情況下,server-0 發生了有可能影響宿主機的安全事件,此時僅僅關閉這個宿主機是不夠的,因為受影響的範圍可能已經擴大。但我們又不能因為這樣不知影響範圍多廣的安全事件來關閉整個機房。所以,利用圖譜輔助找出受影響範圍就非常有用了。

下面的查詢模式是:

  • 找出可能被影響的子網(VPC),標記最高階別風險子網為後續定位做準備
  • 找到可能被控制了的宿主機
  • 從宿主機觸發,找出同主機的其他虛機
  • 從其他虛機觸發,找到它們的子網(VPC)
  • 從其他虛機觸發,找到可能已經被影響的網盤。這是為了防止被掛載到其他機器,這會擴大影響。
MATCH (server_escaping_hypervisor)<-[:`contains`]-(hypervisor_compromised:nova_host)
    WHERE id(server_escaping_hypervisor) == "server-0"
OPTIONAL MATCH (server_escaping_hypervisor)<-[:attached]-(:neutron_port)<-[:contains]-(impacted_subnet_high:neutron_network)
OPTIONAL MATCH (hypervisor_compromised)-[:`contains`]->(server_same_host:nova_instance)
OPTIONAL MATCH (server_same_host)<-[:attached]-(:neutron_port)<-[:contains]-(impacted_subnet:neutron_network)
OPTIONAL MATCH (server_same_host)<-[:attached]-(impacted_volume:cinder_volume)

RETURN impacted_subnet_high.neutron_network.name AS impacted_subnet_high,
       hypervisor_compromised.nova_host.name AS hypervisor_compromised,
       impacted_subnet.neutron_network.name AS impacted_subnet,
       [server_same_host.nova_instance.name, server_same_host.nova_instance.instance_name] AS server_same_host,
       impacted_volume.cinder_volume.name AS impacted_volume

下面的結果集中,列出了 server-0 被控制之後,考慮宿主機逃離的情況下可能受影響的擴散範圍。

impacted_subnet_highhypervisor_compromisedimpacted_subnetserver_same_hostimpacted_volume
sharednode0shared["server-0", "instance-00000001"]Empty
sharednode0shared["server-1", "instance-00000002"]ffaeb199-47f4-4d95-89b2-97fba3c1bcfe
sharednode0private["server-1", "instance-00000002"]ffaeb199-47f4-4d95-89b2-97fba3c1bcfe
sharednode0private["server-3", "instance-00000005"]c9db7c2e-c712-49d6-8019-14b82de8542d
sharednode0private["server-3", "instance-00000005"]volume-2
sharednode0public["server-4", "instance-00000006"]volume-2

我們們再看看它的視覺化結果。

MATCH p=(server_escaping_hypervisor)<-[:`contains`]-(hypervisor_compromised:nova_host)
    WHERE id(server_escaping_hypervisor) == "server-0"
OPTIONAL MATCH p0=(server_escaping_hypervisor)<-[:attached]-(:neutron_port)<-[:contains]-(impacted_subnet_high:neutron_network)
OPTIONAL MATCH p1=(hypervisor_compromised)-[:`contains`]->(server_same_host:nova_instance)
OPTIONAL MATCH p2=(server_same_host)<-[:attached]-(:neutron_port)<-[:contains]-(impacted_subnet:neutron_network)
OPTIONAL MATCH p3=(server_same_host)<-[:attached]-(impacted_volume:cinder_volume)
RETURN p,p0,p1,p2,p3

還是和之前一樣,我們在視覺化圖探索工具 Explorer 中選擇 Dagre 佈局,它能比較清晰看出影響資源的範圍。從這些可能受影響的虛機、網路、網盤出發,可以進一步採取需要的安全措施了。

hypervisor_escape

重點關注資源檢測

最後,利用 Betweenness Centrality 演演算法,我們可以獲得基礎設施中影響面大的那些”脆弱環節“。這些資源不一定真的處在危險的狀態,只是說,它們處在了比較重要的資源之間的交匯處,一旦它們出問題,出問題的代價可能會非常大。

因此,識別關鍵資源後,我們可以考慮下面的安全機制:

  • 有針對性採用更激進、昂貴的健康檢查策略;
  • 設定更高的支援、關注級別;
  • 主動遷移相關聯的資源,以降低”脆弱環節“對整體基礎設施可用性的影響範圍;

在這裡,我們只在瀏覽器內部的子圖上做演演算法流程驗證。機智的你,可以自己試著利用開源的 NebulaGraph Algorithm 或者付費的 NebulaGraph Workflow + Analytics 做全圖上的等價操作。

首先,我們用之前的方式去掃描圖上 1,000 個點,並且從它們出發,跳一跳,獲得一個比較隨機的子圖。實際上,由於我們的資料集並不是很大,這個操作是撈取了全圖的資料:

OPTIONAL MATCH p=(n)--()
RETURN p, n

隨機子圖搞定之後,我們執行 Betweenness Centrality 演演算法,得到 node0 是分值最大的”脆弱一環“。的確,它是我們當前實驗中負載最大的宿主機,可以想象它確實是故障之後全域性影響最大的一個資源。

bwteeness_centrality

總結

在海量資料、企業雲、混合雲的複雜基礎設施運維場景下,利用圖資料庫圖演演算法的能力做高效的輔助運維工作是一個十分值得的嘗試與技術投資。

NebulaGraph 作為高效能、開源、分散式的新一代雲原生圖資料庫,是一個很值得考慮的圖基礎設施選型目標。

歡迎大家在文末留言討論,本文的可復現環境和示例的 ETL 管道的程式碼、示例資料全都在 https://github.com/wey-gu/openstack-graph/ 開源,歡迎大家來一起完善。

本文用到的視覺化圖探索工具為 NebulaGraph Explorer,目前可以免費試用 30 天

此外,我把本實驗中的圖譜放在了 NebulaGraph Studio/Explorer 的示例資料集中,大家也可以在裡邊下載試玩。

參考文獻


謝謝你讀完本文 (///▽///)

NebulaGraph Desktop,Windows 和 macOS 使用者安裝圖資料庫的綠色通道,10s 拉起搞定海量資料的圖服務。通道傳送門:http://c.nxw.so/7CcNV

想看原始碼的小夥伴可以前往 GitHub 閱讀、使用、(^з^)-☆ star 它 -> GitHub;和其他的 NebulaGraph 使用者一起交流圖資料庫技術和應用技能,留下「你的名片」一起玩耍呢~

相關文章