Puppet内部原理
轉載位址:http://junqili.com/puppet/puppet-internals/
puppet關于内部原理的官方的文檔:https://docs.puppetlabs.com/guides/puppet_internals.html
本文檔目标是描述Puppet的 manifest在系統上是如何工作。此過程相當複雜,但是一般也不需要了解其中太多的細節。整理此文檔也正是為了那些想了解更多關于Puppet内部機制人更友善更直接了解。
在介紹之前,我們先看兩個關于Puppet的經典圖。一方面希望這兩幅圖能幫助了解,另一方面也希望下面的解釋能幫助了解這兩幅圖。
Puppet架構圖
Puppet工作流程圖
High Level
粗略來看,Puppet有三個主要的執行階段--編譯、執行個體化和配置
編譯
這階段描述如何将一個基于文本的manifest轉變為一個針對特定節點規範(node-specific specification)。任何與此主機無關的代碼都會被忽略,而任何與此主機相關的代碼都會被插入,意味着變量擴充并且所有的結果都是文字字元串。
編譯階段與Puppet庫資源類型的唯一聯系就是所有作為結果的資源規範都會被驗證:也就是驗證指定的類型是有效的并且該類型所有特定屬性都是有效的。這時候沒有對屬性值的驗證。
在網絡安裝環境中,這階段完全發生在服務端。此階段的輸出是一個類似于哈希的簡單資源集合。
執行個體化
這階段将簡單的哈希和數組(編譯階段的輸出)轉變為Puppet庫對象。因為這階段需要依賴用戶端的很多資訊才能正常工作(比如,使用什麼類型的包,什麼類型的服務等),這階段完全發生在用戶端。
從簡單的格式轉換為文字的Puppet對象允許這些對象對輸入做更強大的驗證,這是大部分輸入驗證發生的地方。如果你指定有效的屬性但卻賦予無效的值,在這裡你可以發現它。意味着你将在用戶端執行個體化配置時驗證這類值錯誤,而不是在服務端。
這階段的輸出是全部的機器配置,而它能夠改變本地系統。
配置
這階段Puppet真正改變系統。每個資源執行個體比較自身特定狀态與機器狀态并且去做任何需要更改的變動。如果機器狀态恰好與指定狀态一緻,則不需要做任何變動。
Lower Level
上述三個階段每個都可以分解為更多步驟。
編譯階段1:解析
輸入:使用Puppet語言編寫的Manifests
輸出:解析樹(AST對象執行個體)
入口:Puppet::Parser::Parser#parse
這時候,所有Puppet manifests開始為文本檔案,解析器的工作就是了解這些檔案。解析器隻做了很少的工作-它将文本轉換為一種文本直接映射回文本的格式,建立解析樹本質上等價于(建立)文本自身。這時的驗證僅僅是文法的驗證。
這階段無論是否使用節點,無論使用stand-alone Puppet解釋器或者用戶端/服務端系統。一旦啟動Puppet,解析過程就會發生。
編譯階段2:解釋
輸入:解析樹(AST對象執行個體)和用戶端資訊(由Facter收集的facts)
輸出:Trees of TransObject and TransBucket instances
入口:Puppet::Parser::AST#evaluate
出口:Puppet::Parser::Scope#to_trans
許多配置需要依賴用戶端資訊來決定。當Puppet用戶端開始運作,它會加載Facter庫,收集所有能夠收集的facts,并且将這些facts傳給解釋器。當你通過網絡使用Puppet,這些facts會經過網絡傳輸到服務端,然後服務端會利用這些資訊來編譯用戶端配置。
這階段傳遞給服務端的資訊(像作業系統和硬體體系結構等),既讓服務端能夠對用戶端配置做出決定,同時它也能讓服務端向配置中插入用戶端資訊,像IP位址和MAC位址這些資訊。
解釋器将解析樹和用戶端資訊合并為一個簡單可傳輸對象樹。這個可傳輸對象映射manifeasts定義的配置,它仍是一個樹,但是它是一個類樹,而資源包含在這些類中。
節點和無節點
當你使用Puppet時,有選項選擇是否使用節點資源。如果不使用節點資源,每次一個用戶端連接配接都需要解釋整個配置,自頂向下解釋解析樹。在這種情況下,你必須有某種明确的選擇機制來指定哪個代碼适用哪個節點。
如果你使用節點資源,那麼,解釋器就會預編譯所有與特定節點無關的代碼。當一個節點連接配接時,解釋器就會尋找此節點名(從Facter facts中檢索)相關的代碼然後僅僅編譯這些相關代碼。
配置傳輸
輸入:可傳輸對象
輸出:可傳輸對象
入口:Puppet::Network::Server::Master#getconfig
出口:Puppet::Network::Client::Master#getconfig
如果使用stand-alone Puppet,那麼就沒有配置傳輸,因為用戶端和服務端是同一程序。如果使用基于網絡的Puppetd用戶端和Puppetmasterd服務端,那麼這些配置一旦編譯完畢,就必須發送到用戶端。
Puppet目前是将這些可傳輸對象轉變為YAML,它經過CGI轉義然後使用XMLRPC通過HTTPS發送。用戶端接收配置,反轉義,緩存到磁盤以免下次服務端不可用,然後使用YAML再将它轉換為普通ruby可傳輸對象。
執行個體化階段
輸入:可傳輸對象
輸出: Puppet::Type instances
入口:Puppet::Network::Client::Master#run
出口:Puppet::Transaction#initialize
為了建立Puppet 庫對象(Puppet::Type子類執行個體),在可傳輸對象頂層調用to_trans。所有的容器對象轉換為Puppet::Type::Component執行個體,所有普通對象轉變為Puppet資源類型執行個體。
這是所有輸入驗證發生地而且經常在這裡值轉換為更可用形式。例如,檔案系統始終傳回使用者IDs,而不是使用者名,是以Puppet對象将它們适當轉換。(順便提及下,有時候Puppet會在對檔案chown時建立使用者,意味着隻要有可能Puppet會忽略驗證錯誤直到最後一刻)。
一旦所有資源建立在一個類似圖的元件和資源樹時,這個樹轉變為一個GRATR圖像。然後這個圖像傳遞給一個新的事務執行個體。
配置階段
輸入:GRATR圖
輸出:事物報告
入口:Puppet::Transaction#evaluate
出口:Puppet::Transaction#generate_report
在這個階段,所有工作的完成,由一個事務控制。
資源生成
一些資源管理其他資源執行個體,例如遞歸檔案操作。在這個階段,任何靜态生成的資源都被生成。然後這些生成的資源被添加到資源圖。
關系
配置程序的下一階段建立一個圖來為資源依賴模組化。Puppet語言的目标之一就是使檔案順序問題盡可能少;這意味着一個需要依賴其他資源的Puppet資源在manifest中會被列在它依賴的資源之後,也就是說被依賴的資源将會在它依賴的資源之後被執行個體化。最後依賴關系圖會和原始資源圖進行合并來建立一個完整的有着所有資源和所有關系的圖。
評估
配置事務會對最終關系圖做一個拓撲排序并且周遊結果清單,按順序評價每個資源。每個資源的每個非同步屬性都會生成一個Puppet::StateChange對象。事務使用該對象嚴格控制這些資源需要做哪些改變和在什麼時候改變,當然也會確定提供日志。
報告 随着事務的進行,它會收集日志和度量名額。在評估的最後,将這些資訊轉變為報告,發送給服務端。
總結
這是Puppet Manifest轉變為一個完整配置的完整流程。對于Puppet系統,還有更多,例如FileBuckets,但是這些更多是支援角色而不是主要魅力所在。