天天看點

Ansible 在知乎大資料的實踐

作者:閃念基因

本文主要講解運維架構的設計、選型、以及 Ansible 在知乎大資料實踐。

1 背景

目前的大資料運維大緻分為了三個方向:

  • 購買商業産品,例如 CDH。
  • 繼續維護曆史遺留的 Apache Ambari (社群停止維護,繼續維護成本較高)。
  • 使用運維架構 Ansible、Chef、Puppet 來開發運維平台,或自研。

知乎大資料運維的發展分為以下三個階段,單點、半自動、工程化。目前正處于工程化階段。

  1. 單點階段:由半自動化大量 Shell 腳本 + 零散 Salt 組成。此階段主要問題是:
    1. 擴縮容、配置變更等大多數變更都成為了一次性的工作,無法實作幂等。
    2. 沒有工程化,個人風格代碼很多,他人很難參與共同維護,容易出現單點問題。
  2. 半自動階段:開始使用 Ansible 按照原來曆史遺留的 Shell 進行重構。這樣解決了幂等問題,但又出現了部署效率慢、架構設計不合理等衍生問題;
  3. 工程化階段:實作了工程與效率兼并。因為大資料 Hadoop 生态維護都是側重配置與服務,急需一個工程化,多人協作的架構,既能解決運維效率問題、又能解決單點問題、還能滿足知乎的專屬大資料需求。

2 目标

知乎在工程化階段的目标如下:

  • 快速相容:短時間相容大資料 Apache Hadoop 生态運維;
  • 曆史接管:接管遺留腳本;
  • 工程化:友善配置管理和多人協作;
  • 降低運維心智:開發和使用的複雜度不能高于遺留 Shell 腳本。

3 大資料運維架構

3.1 架構概述

運維自動化是我們的長期目标,但是一味地追求自動化,容易忽略真正的業務訴求,比如業務後端架構或大資料架構自動化部署、配置變更、服務變更、跨機房遷移等場景的易用與穩定。

業務正是考驗運維品質的試金石。什麼樣的架構設計是對開發和運維友好的、對業務負責的呢?我們總結了以下幾個關鍵點:

  • 架構獨立:優秀的運維系統需要具備獨立部署、獨立測試、依賴解耦的能力;
  • 部署友好:實作部署友好需要深入了解應用和業務的關系,評估潛在風險,對環境和依賴都需要慎重選擇;
  • 降低運維心智成本:運維開發時需要額外考慮運維時的心智負擔,盡量多做幂等性、保護性、可協作性開發,投入大量的穩定性開發工作才能換來更安全地變更;
  • 資料可觀測:所有系統的設計之初,一定需要做到資料可觀測,才能有效的疊代下去。資料可觀測包括但不限于監控性、穩定性、擴充性、協作性。

3.2 架構選型

基于以上,我們需要在短時間内相容以往的大資料運維系統,解決曆史遺留問題,并保證工程化、多人協作和效率,同時降低運維時的心智成本。

我們進行了以下運維工具的對比:

- Puppet Chef Ansible CM&CDH Ambari
開發語言 Ruby Ruby Python Python&混合 Java
二次開發 支援 支援 支援 商業支援 支援(社群維護不穩定)
運維DSL Puppet DSL Chef DSL YAML WebUI WebUI
安裝複雜度
架構 主從+代理 主從+代理 主從+無代理 主從+代理 主從+代理
通信與加密 HTTP、SSL HTTP、SSL SSH、OpenSSH HTTP、SSL HTTP、SSL
配置方式 PULL PULL PUSH & PULL PUSH & PULL PUSH & PULL
社群&文檔 豐富 豐富 豐富 商業支援 社群維護不穩定
學習成本 中(商業支援) 高(社群維護不穩定)

(注:2023 年初社群啟動了 Ambari 複活計劃)

對比發現 Ansible 學習成本較低,滿足低成本快速相容的需求,且它的 Agentless (SSH) 架構天生具備快速運維的優勢。是以,最終我們選擇 Ansible 進行大資料自動化運維管理。

3.3 Ansible 介紹

Ansible 是一種用 Python 編寫的開源自動化工具,具有非常強大的擴充性和可定制性,設計理念如下:

  • 提供了 Inventory 主機清單的功能,可以将需要維護的主機以及關鍵變量存放在此,友善管理;
  • 提供了子產品 Modules 友善開發具體運維能力,例如複制、解壓、配置更新等;
  • 提供了角色 Roles 功能,可以将多個 Modules 串聯起來,友善依賴或重用;
  • 提供了劇本 Playbook 以實作複雜的工作流編排;
  • 提供了快速調試 Adhoc 與 Playbook 兩種執行方式,可以實作多流程的一鍵自動化,也可以快速調試;
  • 所有功能自帶幂等性,簡單的調用即可實作幂等操作;
  • 學習需求極低,無需程式設計語言基礎,隻需要管理語言 YAML 即可;
  • 不需要在被管理的節點上安裝任何代理程式;
  • 提供了豐富的内置優化政策,雖然比不過 Agent based 的工具,但是能夠輕松勝任數千台規模的叢集;
  • 支援幾乎所有的作業系統和雲平台。

3.4 Ansible 架構設計

架構分為四個部分:

  • Inventory 設計;
  • 合理使用子產品 Modules 與插件 Plugins,進行功能擴充;
  • 遵循一鍵原則的劇本 Playbook,同時搭配标簽 Tags 能夠靈活組織流程;
  • 原子化、依賴化設計角色 Roles。
Ansible 在知乎大資料的實踐

3.4.1 Inventory 設計

Inventory 一般是用于管理主機清單的功能。但它不僅僅是一個簡單的清單,它可以上升到一個叢集的 “文字拓撲描述”,使用該思路配置後,能清晰地了解叢集中的程式、版本、關鍵變量配置等資訊。

社群一般推薦使用動靜結合模式(與 CMDB 對接),并可以配置多個 Inventory。但是我們目前的規模,使用靜态模式足以支撐。社群還貼心地提供了靈活的 Inventory 對接模式,友善以後切換。

Inventory 的内部設計分為兩部分,全局關鍵配置部分和主機組部分。

  1. 全局關鍵配置部分:存放一些核心全局關鍵變量,比如 Hadoop、Hive、Spark 等的版本資訊、關鍵配置資訊、關鍵目錄資訊,以及一些工程設計上的重要模式變量。
  2. 主機組部分:按照逐級放量的方式配置設定數量,比如 Hadoop DataNode 和 NodeManager 會定義多個組,組内的機器數量由1、10、500、數千組成,這樣的好處是在變更的時候可以分組進行放量 (這會造成配置備援,對接動态模式能緩解此問題),并且主機組還會搭配專屬的主機組變量。

通過全局關鍵配置部分和主機組部分,以及全局關鍵變量和主機組變量的配合使用,能夠快速了解叢集拓撲現狀,降低了拓撲環境學習的成本,通過對 Inventory 修改即可實作大多數變更,例如擴縮容、關鍵配置調整和版本更新。

3.4.2 功能擴充

Ansible 提供了 Modules 和 Plugins 來完成功能補充與擴充。

Modules 是用來補充 Ansible 内置子產品的功能,使得使用者可以通過自定義的 Modules 實作一些特定功能,比如管理檔案、使用者、安裝軟體、建立雲資源等等。而且 Modules 的實作不限于特定語言,可以使用 Python、Bash、Perl、PowerShell 腳本等等。

Plugins 則是用來擴充其功能的元件,包括子產品、連接配接器、變量插件、回調插件等等。Plugins 是可插入的代碼單元,可以更改 Ansible 的行為和擴充其功能。Plugins 常用類型分為三種:Inventory Plugins、Connection Plugins、和 Callback Plugins。Ansible 插件通常用于實作一些進階自動化任務,比如使用插件來在部署過程中自定義通知、從不同的資料源中提取資訊等。(注:Ansible 提供了10種以上的插件,此處隻介紹了常用的3個)。

這裡可以補充一下,該如何區分什麼時候用 Modules、什麼時候用 Plugins 呢?

Ansible 内置了 copy、template、unarchive 等高效子產品,比如在使用這些子產品功能後,需要調用外部接口獲得相關資料,或者實作臨時通知發送,此時可以使用 Modules 去補充。

整體劇本執行完想進行一些資料收集、資料統計、或者回調其他操作時,可以使用 Plugins 去擴充。

是以在此部分我們企業微信通知、特定服務接口資料擷取等其他功能全用 Modules 來實作,而 Plugins 隻使用了 Callback 部分。Var、Lookup、Action 等 Plugins 這些插件的使用門檻較高,使用不當容易适得其反。

3.4.3 一鍵操作

Playbook 的編排是運維核心設計的第一道考驗。它是一種将系統配置視為目标狀态的方法,而不是一系列必須執行的操作。換句話說,我們隻需要描述系統的最終狀态,而不是告訴 Ansible 需要執行哪些指令。

Playbook 提倡子產品化和可重用性。每個 Playbook 可以包含一個或多個角色或任務,每個角色又包含多個子產品,這種層級關系極大地提高了靈活性。子產品可以使用标簽進行區分,不但可以一鍵 Playbook 完成所有操作,還可以實作流程細節控制。

但是,在這麼多關系和嵌套的前提下,很容易出現過度設計或備援設計。例如,我們在部署 Hadoop 的 DataNode 和 NodeManager 時,經常會把同類型的操作放在一個 YAML 檔案中,導緻在實際執行時會出現很多幹擾資訊,出現大量的跳過,增加心智負擔。是以我們重新思考了最開始的那句話,“我們隻需要描述系統的最終狀态,而不是具體要執行哪些指令”。

是以我們早期在 Playbook 裡開發的建立目錄、配置變量、服務啟停等功能,發現不應該放在這裡,而是應該使用 Roles 封裝起來。 例如我們需要 “jdk、jmx_agent、execute_account、hadoop_pkg、datanode_conf、serv_state_check” 等相關 Roles,可以将對應的 Roles 組織起來,也可以把它們想象成軟體開發中的方法來使用。

最後,Playbook 的設計将會如同程式設計語言開發一樣,使用各種函數去編排邏輯。

3.4.4 角色設計

Roles 的設計核心思想是将系統配置分解為可重用的元件,以提高代碼的可維護性和可複用性。通過将複雜的系統配置任務分解為多個獨立的可重用角色,每個角色專注于完成特定的功能。

同時也因為 Roles 的開發是動态腳本,不像 Java 開發中 Spring Boot 這類重工業架構有着大量的約定和限制,而且每個 Roles 包含一組相關的任務和變量,在這種多重環境下,容易出現循環重用的問題,如何避免這樣的問題呢?

  1. 确定 Role 的作用範圍:在設計 Role 時,要明确其作用範圍,并盡可能将其限制在特定的任務或功能領域内。這樣可以確定 Role 隻包含必要的任務和變量,避免不必要的可重用。
  2. 避免過于抽象的命名:為了使 Role 更加通用和可重用,有時會采用過于抽象的命名方式,例如 common 或 base。然而,這種抽象命名方式可能會導緻 Roles 變得複雜和難以了解。是以,最好使用更具體和明确的命名方式,以便更好地表達 Roles 的作用範圍和目的。
  3. 擅用依賴:有時候,會有一些通用的任務和變量,它們可以被多個 Role 共享。為了避免重複編寫這些代碼,可以将這些通用的任務和變量單獨放在一個 Role 中,并将它們作為其他 Role 的依賴。
  4. 靈活使用 Role 的繼承和包含特性:Ansible 提供了 Role 的繼承和包含特性,這些特性可以幫助規避循環重用。通過繼承和包含,可以将一些常用的任務和變量抽象為一個基礎 Role,然後在其他 Role 中包含或繼承這個基礎 Role,避免重複編寫代碼。但是,在使用這些特性時,需要確定基礎 Role 的作用範圍清晰明确,以免引入不必要的複雜性。

在知乎,我們前後疊代了三類 Roles 的設計思路,從 “直白指令思路”、到 “類型劃分思路”、最後到 “原子方法思路”,介紹如下:

3.4.4.1 直白指令思路

早期采用的是 “直白指令思路”,比如以下代碼是 HDFS Router 進行的直接操作,這種角色的複用性、依賴性和相容性都存在很多問題。因為這種簡單直白的邏輯,都會使用許多外部變量來實作主要的邏輯,如果外部依賴發生了變化,Roles 将會失效。這種直白的場景通常是為了滿足當時的需求而完成的,沒有做更多的設計考量。

# 直白指令思路
├── hdfs
│   └── router
│       ├── files
│       │   ├── ***
│       ├── tasks
│       │   └── main.yml
│       └── templates
│           ├── ***
│           └── ***           

3.4.4.2 類型劃分思路

中期的 “類型劃分思路” 為 Roles 的子產品化提供了一種架構思路,通過變量區分安裝包、配置、服務的部署,進而提高了一定的複用性。但實際上并沒有真正解決問題,反而增加了一些流程變量,加大了功能管理的複雜度。

如果按照此類思路設計,将無法相容更為複雜的邏輯。而且會錯誤地認為使用 A 邏輯變量對應安裝包,B 邏輯變量對應配置,在 Playbook 中随意使用邏輯變量,容易遇到 Ansible 大型工程中的另一個尴尬問題:“變量重複替換”。

# 類型劃分思路
grafana
├── tasks
│   ├── cfg_file_deploy.yml
│   ├── main.yml (when cfg, when pkg, when serv)
│   ├── pkg_deploy.yml
│   └── serv_make.yml
└── templates
    ├── ***
    └── ***           

這個 “變量重複替換” 問題在知乎大資料運維核心的疊代曆程中困擾了我們很久。因為在正常的開發中比如 Java 或 Go,在某段邏輯給某個變量進行了重新指派後,新值可以在目前作用域生效。可是在 Ansible 中,如果一個變量有多次指派,它不會區分作用域,而會取最後一次指派的内容導緻出現錯誤部署。

是以,我們總結了以下幾點

  1. 規範 Roles :可以按照程式設計中的方法來了解,所有的方法都需要入參和出參,那麼 Roles 中是所需要的變量都按照入參來定義即可。比如預設值、入參檢測、參數類型、以及傳遞後可以用不同的變量名來接收;
  2. 規範作用域:雖然可以使用變量作用域來控制變量的生命周期,避免變量被重複替換,比如 hostvars 或 group_vars 來定義變量的作用域,這樣每個主機或組都有自己獨立的變量空間;
  3. 依賴表示:如果一個變量依賴于另一個變量的值,應該在 Role 中明确指出這種依賴關系。可以使用 Ansible 的變量依賴關系來實作這一點,而不是直接依賴指派,否則容易出現一些未知變量不存在的問題。

3.4.4.3 原子方法思路

現在的 “原子方法思路”,犧牲了一點工程便捷性,提升了一些工程目錄複雜度,換來的大量的原子 Roles,簡單的了解就是我們把需要做的細節進行原子拆分,然後它們彼此之前可以使用參數傳遞,通過 Roles 級别的依賴進行限制,在 Playbook 中則隻需要組合 Roles 即可,不需要去關心 Roles 的實作細節。是以此時我們再來了解 “我們隻需要描述系統的最終狀态,而不是具體要執行哪些指令” 會有煥然一新的感覺。

# 原子方法思路
alluxio
├── alluxio_conf
│   └── ***
├── alluxio_master_serv
│   └── ***
├── alluxio_pkg
│   └── ***
├── alluxio_worker_cache
│   └── ***
└── alluxio_worker_serv
    └── ***           

3.5 Hadoop 生态運維執行個體

3.5.1 Hadoop 運維概述

知乎大資料生态使用的是 Apache Hadoop。在前期單點 Shell 階段,雖然可以快速應對簡單的場景,但是遇到大規模複雜場景時相對吃力,難以保證自身穩定性。

Apache Hadoop 生态的運維工作相對複雜,主要是因為大資料生态系統由多個元件構成,每個元件之間有着複雜的依賴關系和配置參數,不同的企業群組織在使用大資料生态時,往往會面臨不同的應用場景和業務需求,需要根據具體的情況進行定制化配置和管理。在知乎,大資料相關元件已經全部上雲 (裸金屬一類),在部署層面有大量定制性需求,是以我們面臨着以下難題:

  1. 部署複雜:Apache Hadoop 叢集基礎安裝就已經涉及到多個元件的配置管理,而且知乎存儲和計算都實作了聯邦的架構,部署難度大幅度增加;
  2. 配置複雜多樣:知乎的大資料叢集有多套環境和跨機房的場景,在面對測試、灰階以及多叢集配置時複雜度上升到了一個難以維護的層面;
  3. 資料存儲和管理難度大:Apache Hadoop 生态系統支援海量資料的存儲和管理,但也帶來了資料存儲和管理的複雜度。因為需要對接多個不同形态的業務,多個存儲主節點導緻壓力已經超過社群推薦值,為運維變更以及穩定性帶來了極大的難題;
  4. 變更周期長:因為知乎大資料使用的 Apache Hadoop 生态,HDFS、YARN、Spark、Flink、Presto、Hive、排程、同步等等元件都有定制開發,在版本更新、功能擴充、Bug 修複等場景帶來了極大的難度,比較嚴重是變更周期長;
  5. 業務連續性和可靠性問題:Apache Hadoop 生态系統中的資料處理和分析任務往往涉及到關鍵業務,需要保證任務的連續性和可靠性,早期的腳本很難達到業務無感覺的情況下變更,極端情況需要短暫停業務才能變更。

3.5.2 Hadoop 運維實踐對比

接下來将在 Hadoop 運維中列舉幾個部署、配置、協作場景,來對比知乎早期腳本和現在的 Ansible 工程化,以及商業 CDH 在開發和運維層面的差別。

3.5.2.1 曆史遺留 Shell 運維方式

通過向曆史腳本傳參進行部署操作,問題如下:

  • 部署流程無法控制範圍和服務,一開始操作則是全重來一遍;
  • 無法滾動部署;
  • 部署後的服務觀測難度大。
Ansible 在知乎大資料的實踐

3.5.2.2 工程化 Ansible 運維方式

通過向工程送出 Merge Request 進行運維變更操作,流程如下:

  • 選擇已經設計好的角色 Roles 和 劇本 Playbook;
  • 變更 Inventory 配置新增的節點數量;
  • MR review 以及等待代碼合并後,執行 Playbook 即完成擴容。

如下列圖檔,我們的變更是在 Inventory 中進行,而此次變更會搭配 Git 進行版本控制和 Review 管理。友善他人審查或者對接其他 GitOps,能夠清晰的知道我們的變更内容是什麼。以及變更的風險性。

Ansible 在知乎大資料的實踐

運維開發者的角度,我們可以看到有大量相關的 Hadoop Roles,和指定的 Playbook,像前面說的“我們隻需要描述系統的最終狀态,而不是具體要執行哪些指令”。

最後的執行,就是一條符合邏輯的指令,比如要做什麼,在什麼叢集,轉換為 Ansible 指令就是 “ansible-playbook hadoop_worker_deploy.yml -i inventory_abc.ini”。

Ansible 在知乎大資料的實踐
Ansible 在知乎大資料的實踐

需要注意的是,變更内容都是以代碼的形式存在,我們是通過 Code Review 的方式進行運維協作開發。

同樣我們也具備一些 WebUI,如果暫時不想關心細節,隻需選中需要做的内容點選 RUN 即可,由 Ansible 和目前的架構設計來為你保證穩定。

Ansible 在知乎大資料的實踐
Ansible 在知乎大資料的實踐

注意,你沒看錯,這個擴容流程就是 Hadoop Worker (DataNode,NodeManager,***) 的部署流程,它會根據我們的變更内容和機器分組,對指定的機器進行相關操作。因為預先幂等性的設計,隻會對狀态不同的内容變更,而已存在的内容則會進行一些預檢測,然後根據需要執行。很多的維護工作都是同樣執行邏輯,雖然在執行指令上會有細微差別,但是可以在 WebUI 上都封裝成一個按鈕。

3.5.2.3 商業 CDH 運維方式

CDH 管理工作全由 CM 來完成,它可以友善地進行 Hadoop 叢集管理、配置更新、監控、擴容等工作。使用 CDH 進行 Hadoop HDFS 擴容,需要進行以下步驟:

  • 在 CDH 管理界面上選擇 Add New Hosts,将新節點添加到叢集中;
  • 在 NameNode 上運作 hdfs dfsadmin -refreshNodes;
  • 在新增節點上啟動 DataNode 服務。可以通過在 CM 管理界面上選擇新增節點并啟動服務來完成。

下面例舉了常見的擴容流程。可以看出 CM 的設計,以及管理功能已經很豐富了,隻需簡單的 Web 操作,就可以完成所有工作。

Ansible 在知乎大資料的實踐
Ansible 在知乎大資料的實踐
Ansible 在知乎大資料的實踐

3.5.3 Hadoop 運維對比總結

根據以上基礎的運維功能對比,可以明顯的看到:

  • 曆史的 Shell 腳本方式功能單一且無法勝任複雜的場景,并且變更心智成本要求較高;
  • Ansible 的變更流程,因為架構設計使它的基礎運維子產品開發、生産服務變更都是以 Code Review 的方式出現。雖然與腳本對比,同樣也有需要了解,才敢變更的相似性,但是這是一個工程化的架構,是多人協作開發與測試産出的結果,在變更穩定性上是得過驗證的,是以它是具有自動一鍵完成的能力;
  • 商業産品 CDH 可以看到截圖中,把所有流程都以可觀測性的方式展現出來,這對變更非常有幫助。但這是優點也是缺點,如果其中出現問題,可能需要商業支援,或者商業教育訓練以及專人駐場。

最後,相對于曆史的 Shell 腳本方式,工程化的實踐是質的飛躍。再比較 CDH,在産品設計理念層面比如功能穩定性、WebUI,可能相較甚遠,但是面向公司内的高度定制化,Ansible 能夠以低成本的方式高效完成工作,這也是我們為什麼會選擇 Ansible 的原因。

4 Ansible 調優

4.1 高效幂等

Ansible 另一個最大的優點是它自帶的幂等性。因為隻需要簡單調用内置方法,即能實作需求又能達到幂等操作,且可以重複運作,避免多次運作不一緻。

幂等性的原理是基于每個子產品内部的狀态檢查機制來完成的。舉例幾個源碼中的實作:

file 子產品的幂等:

1. 首先使用 os.stat 函數擷取指定檔案的狀态資訊,包括檔案的大小、權限模式、所有者、組等屬性;
2. 然後根據參數中指定的屬性資訊,檢查檔案的狀态和屬性是否滿足要求。如果不滿足,則需要修改檔案的狀态和屬性;
3. 根據參數中指定的屬性資訊,使用 os.chmod、os.chown 和 os.utime 等函數,修改檔案的權限模式、所有者、組和時間戳等屬性,使其滿足要求;
4. 最後,該方法會傳回一個布爾值,表示檔案的狀态和屬性是否已經滿足要求。           

user 子產品的幂等:

1. 如果使用者或組已經存在,則不執行任何操作,否則會建立使用者或組;
2. 對于已存在的使用者或組,Ansible 會檢查其屬性(例如使用者名、UID、GID 等),并根據需要修改這些屬性以保持一緻性;
3. 對于需要建立的使用者或組,Ansible 會使用系統指令來建立使用者或組,并設定其屬性。           

它們的實作都有一個執行前後多重檢查的共性。

可是這裡有個問題,假設需要重複建立 100 個目錄、或者 100 個使用者時,繼續使用預設的幂等方式是低效的。首先檢測代碼是多重的調用棧 “ansible yaml -> python -> c”,因為子產品的不同,檢查的數量也不同,子產品要執行的任務較多時,檢查的耗時是較高的。雖然社群的建議可以使用批處理子產品 batch、異步操作 async+poll、循環 loop 去提速,但是都沒有從根源解決 “合理避免備援的幂等性檢測” 的問題。是以當工程規模達到一定程度時,可以根據需求做一些合理的預檢測來避免備援的幂等性檢測。

比如前面舉例需要建立 100 個使用者場景,如果使用社群的一些優化建議會複雜化問題。其實可以根據需求做一些重型邏輯前的預檢測,首先獲得目前主機的使用者資訊,将其與需要建立的使用者清單進行 diff,根據 diff 的增量内容傳入後續的邏輯,這樣的方式可以大幅度提升重複跑時的效率。其他的子產品也同理,甚至還可以實作特殊需求的幂等。

4.2 減少運維心智成本

相對于其他運維工具,當項目體積龐大了之後,很容易陷入一個複雜地獄,導緻變更會具有極高的心智成本。

什麼樣的設計理念,可以減少運維心智成本呢?

應該是達到一鍵完成的運維變更操作,才能有效的減少運維心智成本。在 IaC(基礎設施即代碼)概念中有一個架構 Terraform,它的設計理念優雅地達到了這個狀态。

簡單介紹一下 Terraform 的設計理念,運維部署的所有結果 (叢集、服務、變量、配置等等) 都儲存了一個狀态,每次的變更都會進行狀态 diff (叢集節點數量增加,增加了兩個配置檔案此類), 而且這些 diff 都會展現在狀态檔案中,最後隻用變更狀态不同的服務或主機即可,其他的容錯、幂等都有架構去保證,這也是 Ansible 指令式與 Terraform 聲明式最大的差別,Terraform 隻需要聲明我的最終架構狀态,其他的全由工具去完成。

Terraform 在實際運維操作時所有的變更 (資源開通、銷毀、變更) 都是一個指令,因為使用了狀态 diff 檢測,即使多次執行也不用擔心出錯,真正解放了運維時的心智成本。

反觀目前的 Ansible 運維架構設計,确實無法像 Terraform 靠狀态 diff 去做增量變更 (工具之間指令式與聲明式的差別)。但是 Ansible 搭配好了架構設計,規劃了變量布局,統一了 Roles 的開發規範,換一個角度想,實際運維變更時也是執行某個 Playbook,是否可以把這個 Playbook 當做一個一鍵完成呢?或者說朝着那個方向努力呢?

這确實這是一個好的方向,我們要走的路還很長,需要疊代的東西還很多,比如在劇本執行時中斷了,如何使用 retry 機制合理的自動重試。Inventory 的動靜模式結合如何合理規劃叢集。幂等如何避免大量備援檢查。以上都應該是朝着一鍵完成的角度去進步。

4.3 效率突破

常見的優化比如并發執行、減少 SSH 連接配接 (ssh_args、pipelining、SSH ControlMaster)、搭配緩存插件 (cache_plugin)、異步執行 (async、poll)。如何使用在這就不做過多介紹,因為這類資料很常見,是以我們分享一些能起到大幅度效率突破的内容。

  • 工程獨立核心配置:可以在工程中放置一個自定義的 ansible.cfg 檔案,來自定義一些優化參數,執行的預設 host、SSH 調優、插件路徑、關鍵變量等都可以通過工程自定義 cfg 完成,而且也建議使用自定義,這樣可以更好的了解工程;
  • 統一 Python 環境:pipelining 在使用的時候最好能統一 Python 環境和運維賬号的 SSH 權限,經過測試 SSH 的連接配接量級至少能減少 30% 以上;
  • SSH 會話保持:建議使用 SSH 的 control_path 來儲存 SSH 的 socket 狀态檔案,原理也是減少 SSH 的連接配接成本,需要注意的是存放 socket 的目錄應提前建立好;
  • 慎用 vars_plugins:分享一個踩到的大坑,使用 vars_plugins 目的是為了快速擷取變量,但是這裡有個問題是自定義實作的 vars_plugins 會與 playbook 的主線流程有對接,源碼中的主線流程将異常隐藏起來,當 vars_plugins 自定義實作處理不當時,會影響每次的 playbook 執行,嚴重的話就會出現丢邏輯;
Ansible 在知乎大資料的實踐
  • 回調插件:建議在 callback_plugins 中實作任務執行時間統計功能,這樣可以在每次執行完時檢視效率情況,友善定位慢邏輯問題,達到資料可觀測性;
Ansible 在知乎大資料的實踐
  • 資訊收集:gather_subset 收集系統資訊時,最好是可選性收集,比如隻收集網絡或者硬體,預設的收集特别耗時間。下面給出一個參考:
- name: Gather hardware info
  setup:
    gather_subset:
      - '!all'
      - '!any'
      - hardware           

5 成果與展望

經過工程化階段的疊代與實踐,成果如下:

  • 支援跨機房運維;
  • 1 小時内完成 PoC 規模 (10 ~ 100台) 的大資料叢集部署;
  • 不影響 (或極低影響) 業務的情況下,4~7 天滾動變更完所有叢集;
  • 純配置更新 5 分鐘數千台;
  • 全流程部署 1 台/70~100s;
  • 半天文檔閱讀即可上手開發;
  • 70+ 功能角色子產品 Roles、30+ 固定、10+ 動态流程劇本 Playbook。

未來展望:

本文主要是講了運維架構核心的設計和效率提升,後續将繼續提升易用性和穩定性,簡化 Web UI 操作,朝着一鍵完成持續努力。

作者:陳大炮

出處:https://zhuanlan.zhihu.com/p/617731670

繼續閱讀