本文首發於 Nebula Graph 公眾號 NebulaGraphCommunity,Follow & 看大廠圖資料庫技術實踐
背景
在 Nebula-Graph 的日常測試中,我們會經常在伺服器上部署 Nebula-Graph。為了提高效率,我們需要一種工具,能幫我們做到快速部署,主要的需求:
- 可以使用非 root 賬戶部署 Nebula Graph,這樣我們可以針對這個使用者設定 cgroup 做資源限制。
- 可以在操作機上更改配置檔案,然後分發到部署的叢集上,方便我們做各種調參的測試。
- 可以使用指令碼呼叫,方便以後我們繼承在測試的平臺或工具上。
工具選擇上,早期有 Fabric 和 Puppet,比較新的工具有 Ansible 和 SaltStack。
Ansible 在 GitHub 上有 40K+ star, 而且在 2015年被 Red Hat 收購,社群比較活躍。很多開源專案都提供了 Ansible 的部署方式,比如 Kubernetes 中的 kubespray和 TiDB 中的 tidb-ansible。
綜合下來,我們使用 Ansible 來部署 Nebula Graph。
Ansible 介紹
特點
Ansible 是開源的,自動化部署工具(Ansible Tower 是商業的)。具有以下的幾個特點:
- 預設協議是基於 SSH,相比於 SaltStack不 需要額外部署 agent。
- 使用 playbook, role, module 來定義部署過程,比較靈活。
- 操作行為冪等。
- 模組化開發,模組比較豐富。
優缺點比較明顯
- 使用 SSH 協議,優點是大多數機器預設只要有賬號密碼就可以通過 Ansible 完成部署,而缺點效能上會差一些。
- 使用 playbook 來定義部署過程,Python 的 Jinja2 作為模板渲染引擎,對於熟悉的人來說會比較方便,而對於沒有使用過的人,會增加學習成本。
綜上,適用於小批量機器的批量部署,不需要關心額外部署 agent 的場景,和我們的需求比較匹配。
部署邏輯
通常為了離線部署,可以把機器分為 3種角色。
- Ansible 執行機:執行 Ansible 的機器,需要能通過 SSH 連到所有機器。
- 有外網的資源機:執行需要連線外網的任務,比如下載 RPM 包。
- 伺服器:即執行服務的伺服器,可以網路隔離,通過執行機來部署
任務邏輯
Ansible 中,主要有三種層次的任務:
- Module
- Role
- Playbook
Module 分為 CoreModule 和 CustomerModule,是 Ansible 任務的基本單元。
在執行任務的時候,首先 Ansible 會根據 module 的程式碼,將引數代入,生成一個新的 Python 檔案,通過 SSH 放到遠端的 tmp 資料夾,然後通過 SSH 遠端執行 Python 將輸出結果返回,最後把遠端目錄刪除。
# 設定不刪除 tmp 檔案
export ANSIBLE_KEEP_REMOTE_FILES=1
# -vvv 檢視 debug 資訊
ansible -m ping all -vvv
<192.168.8.147> SSH: EXEC ssh -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="nebula"' -o ConnectTimeout=10 -o ControlPath=/home/vesoft/.ansible/cp/d94660cf0d -tt 192.168.8.147 '/bin/sh -c '"'"'/usr/bin/python /home/nebula/.ansible/tmp/ansible-tmp-1618982672.9659252-5332-61577192045877/AnsiballZ_ping.py && sleep 0'"'"''
可以看到有這樣的日誌輸出,AnsiballZ_ping.py 就是根據 module 生成的 Python 檔案,可以登入到那臺機器,執行 Python 語句看一下結果。
python3 AnsiballZ_ping.py
#{"ping": "pong", "invocation": {"module_args": {"data": "pong"}}}
返回了執行 Python 檔案的標準輸出,然後 Ansible 再對返回的結果做額外處理。
Role 是串聯 module 的一系列任務,可以通過 register 來傳遞上下文引數。
典型例子:
- 建立目錄
- 如果建立目錄成功,繼續安裝,否則退出整個部署工程。
Playbook 是組織部署機器和 role 之間的關聯。
通過在 inventory 對不同機器進行分組,對不同分組使用不同的 role 來部署,完成非常靈活的安裝部署任務。
當 playbook 定義好之後,不同的環境,只要變更 inventory 中的機器配置,就可以完成一樣的部署過程。
模組定製
自定義 filter
Ansible 使用 Jinja2 作為模板渲染引擎,可以用 Jinja2 自帶的 filter ,比如
# 使用 default filter,預設輸出 5
ansible -m debug -a 'msg={{ hello | default(5) }}' all
有時候,我們會需要自定義的 filter 來操作變數,典型的場景就是 nebula-metad 的 地址 --meta_server_addrs
。
- 當只有 1 個 metad 的時候,格式是
metad1:9559
, - 當有 3 個 metad 的時候,格式是
metad1:9559,metad2:9559,metad3:9559
在 ansible playbook 的工程下,新建 filter_plugins 目錄,建立一個 map_fomat.py Python檔案,檔案內容:
# -*- encoding: utf-8 -*-
from jinja2.utils import soft_unicode
def map_format(value, pattern):
"""
e.g.
"{{ groups['metad']|map('map_format', '%s:9559')|join(',') }}"
"""
return soft_unicode(pattern) % (value)
class FilterModule(object):
""" jinja2 filters """
def filters(self):
return {
'map_format': map_format,
}
{{ groups['metad']|map('map_format', '%s:9559')|join(',') }}
即為我們想要的值。
自定義 module
自定義 module 需要符合 Ansible 框架的格式,包括獲取引數,標準返回,錯誤返回等。
寫好的自定義 module,需要在 ansible.cfg 中配置 ANSIBLE_LIBRARY,讓 ansible 能夠獲取到。
具體參考官網:https://ansible-docs.readthedocs.io/zh/stable-2.0/rst/developing_modules.html
Nebula Graph 的 Ansible 實踐
因為 Nebula Graph 本身啟動並不複雜,使用 Ansible 來完成 Nebula-Graph 的部署十分簡單。
- 下載 RPM 包。
- 複製 RPM 包到部署機,解壓後,放到目的資料夾。
- 更新配置檔案。
- 通過 shell 啟動。
使用通用的 role
Nebula Graph 有三個元件,graphd、metad、storaged,三個元件的命名和啟動使用一樣的格式,可以使用通用的 role,graphd、metad、storaged 分別引用通用的 role。
一方面更容易維護,另一方面部署的服務更有細粒度。比如 A B C 機器部署 storaged, 只有 C 機器部署 graphd,那 A B 機器上,就不會有 graphd 的配置檔案。
# 通用的 role, 使用變數 install/task/main.yml
- name: config {{ module }}.conf
template:
src: "{{ playbook_dir}}/templates/{{ module }}.conf.j2"
dest: "{{ deploy_dir }}/etc/{{ module }}.conf"
# graphd role,將變數傳進來 nebula-graphd/task/main.yml
- name: install graphd
include_role:
name: install
vars:
module: nebula-graphd
在 playbook 中,graphd 的機器組來執行 graphd 的 role,如果 A B 不在 graphd 的機器組,就不會將 graphd 的配置檔案上傳。
這樣部署後,就不能使用 Nebula-Graph 的 nebula.service start all 來全部啟動,因為有的機器上會沒有 nebula-graphd.conf 的配置檔案。類似的,可以在 playbook 中,通過引數,來指定不同的機器組,傳不同的引數。
# playbook start.yml
- hosts: metad
roles:
- op
vars:
- module: metad
- op: start
- hosts: storaged
roles:
- op
vars:
- module: storaged
- op: start
- hosts: graphd
roles:
- op
vars:
- module: graphd
- op: start
這樣會相當於多次 ssh 去執行啟動指令碼,雖然執行效率沒有 start all 更好,但是服務的啟停會更為靈活。
使用 vars_prompt 結束 playbook
當只想更新二進位制,不想刪除資料目錄的時候,
可以在 remove 的 playbook 中,新增 vars_prompt 二次確認,如果二次確認了,才會刪除資料,否則會退出 playbook。
# playbook remove.yml
- hosts: all
vars_prompt:
- name: confirmed
prompt: "Are you sure you want to remove the Nebula-Graph? Will delete binary only (yes/no)"
roles:
- remove
而在 role 裡,會校驗二次確認的值
# remove/task/main.yml
---
- name: Information
debug:
msg: "Must input 'yes', abort the playbook "
when:
- confirmed != 'yes'
- meta: end_play
when:
- confirmed != 'yes
效果如圖,刪除時可以二次確認,如果不為 yes,就會取消執行這次的 playbook,這樣可以只刪除二進位制,而不刪除 nebula 叢集的資料。
交流圖資料庫技術?加入 Nebula 交流群請先填寫下你的 Nebulae 名片,Nebula 小助手會拉你進群~~