我喜歡的一條(還有很多)關(guān)于Salt的事情是它本身并沒有一種模糊的語言:正如我一直想提到的,,只需要YAML和Jinja,每個3條規(guī)則,,你就可以開始自動化,。例如,當你需要一個簡單迭代時,,并不需要閱讀全部文檔并查看“在列表中迭代的特定指令”,,僅僅是一個簡單而直接的Jinja循環(huán),{%- for element in list %},。然而,,在某些特殊情況下,Jinja本身可能是不夠的,,或者是它可能會變得過于復雜和不可讀,。當我需要處理一個復雜的任務(wù)時,我有時會覺得“我最好用Python來寫這個,,而不是Jinja(或者兩者的結(jié)合)”,。 眾所周知,Salt是一個非常靈活的框架,,這是因為它的內(nèi)部結(jié)構(gòu)簡單:一個密集的核心,,允許添加可插拔的接口,。比如說,如果您查看GitHub上的官方存儲庫,,你會注意到一個相當長的目錄列表(還有許多其他的目錄): 純Python渲染器 其中一個可插入的接口稱為渲染器:這是一個促進低級別輸入-輸出交互的子系統(tǒng),。對于Salt來說,如果你的文件結(jié)構(gòu)是YAML,、JSON或TOML等等,,那么這些數(shù)據(jù)都是用Python對象來表示:您使用的是數(shù)據(jù),而不是文本塊,! 多虧了這個聰明的方法,,(大約6年前)添加一個純Python渲染器當然是最自然的事情了。毫不夸張的說,,這就是為什么你今天能夠用純Python來寫東西,。但是不要隨便相信我的話,耐心等待,,我將會證明的,。 Python支柱 如果您是Salt新手,SLS(SaLtStack)是Salt所使用的文件格式,。默認情況下,,SLS是Jinja+YAML的結(jié)合(首先渲染Jinja),然后生成的YAML被翻譯成Python對象,,并加載到內(nèi)存中,。讓我們考慮以下SLS示例: 在這個簡單的例子中,我們先定義一個IP地址列表,,就像普通的YAML一樣,。但是,如果我們希望有一個更長的地址列表,,并且我們想要避免手動輸入每個地址,,會發(fā)生什么呢?如前所述,SLS默認是YAML和Jinja的巧妙組合,,因此您可以重寫等價的SLS,,如下所述: 盡管在這個特殊例子中,,人們可能會爭辯說它似乎并沒有大規(guī)模地縮小尺寸,但在處理大量數(shù)據(jù)時確實如此,。 請記住:您可以在任何SLS文件中應(yīng)用完全相同的邏輯,,無論是支柱、狀態(tài),、反應(yīng)堆還是top.sls文件,,等等。 在繼續(xù)之前,,我想澄清的是,Jinja和YAML都屬于我之前提到的渲染器界面,。換句話說,,SLS在默認情況下使用這兩個渲染器。 您可以選擇任何對您有效的渲染器組合,,并且可以輕易地做到,,只需在文件的頂部添加一個釋伴(#!),并命名您想要使用的渲染器,,由管道符(|)分隔,。有了這些,默認的標題是#!jinja|yaml,。 需要選擇Python渲染器的釋伴是!#py,。在這里惟一的約束條件是,你需要一個名為run的函數(shù)來返回所需的數(shù)據(jù),。例如,,上面的例子的等價SLS如下: /etc/salt/pillar/ip_addresses.sls 就是看上去那樣簡單:我們編寫的是純Python,可以使用它作為我們系統(tǒng)的輸入數(shù)據(jù),。然后讓我們這樣做:將這些內(nèi)容保存到/etc/salt/pillar/ip_addresses.sls,,并在柱頂文件(/etc/salt/pillar/top.sls -正如在pillar_roots中主目錄中配置的),以這樣一種方式,,任何被控端都可以讀取內(nèi)容(正是由于 * -想獲得更多細節(jié)請查看頂部文件文檔): /etc/salt/pillar/top.sls 在刷新支柱后(使用saltutil.refresh_pillar執(zhí)行功能),,我們可以驗證數(shù)據(jù)是否可用: 但是等一下:我將柱頂文件定義為默認的SLS文件,純YAML格式,。即使在這個微不足道的示例中,,它也有點太過了,如果想要頂部文件可以根據(jù)需要動態(tài)地生成,,是有很好的生產(chǎn)案例,,因此我們就有可能動態(tài)地將支柱綁定到被控端(或公式,用于狀態(tài)頭部文件): /etc/salt/pillar/top.sls 通過以上演示,,我們已經(jīng)確認了完全能夠只用Python將數(shù)據(jù)引入到系統(tǒng)中,。盡管我目前提供的示例是很微小的,,但它們完全可以擴展到更復雜的實現(xiàn),就像解決問題所需要的一樣,。 我最喜歡舉的一個例子是從外部系統(tǒng)加載支柱的數(shù)據(jù),從HTTP API訪問https:///api.json(提供數(shù)據(jù)格式化為JSON): /etc/salt/pillar/example.sls 通過5行SLS使用Python渲染器,,我們可以直接將數(shù)據(jù)從遠程端點引入到Salt中。當然,,在實現(xiàn)之前,,您需要評估安全性和其他注意事項。此外,,當需要處理非常復雜的問題時,,你需要從多個角度來看待問題,應(yīng)該考慮使用外部支柱或外部頂部系統(tǒng),,因為它們是處理輸入數(shù)據(jù)的另一種很好的方式,。而且,它們還提供了在常規(guī)支柱之前或之后加載的靈活性(使用 Python SLS公式 類似地,,我們可以完全用Python來編寫SLS公式,。例如,以下狀態(tài)SLS(從napalm-ntp-formula中提取): 可以使用py渲染器來重寫: 同樣,,在這個特殊的例子中和它的默認行為相比,,Python渲染器并沒有能夠帶來更多的好處,但是它是一個很好的機會,,可以看到__salt__(雙下劃線),,它是所有可用的執(zhí)行函數(shù)的散列。當我們的狀態(tài)需要非常復雜的決策時,,編寫Python公式被證明是極其有用的,。 但是,我更喜歡(而且推薦)將復雜性轉(zhuǎn)移到執(zhí)行函數(shù)中,,就像在編寫執(zhí)行模塊部分中所描述的那樣,。 Python模板 是的,你看的沒錯,,不必過分熱心,。同樣在有些情況下,Jinja或其他模板引擎是不夠的,。比如,,當我需要生成包含unicode字符的文本時,我使用它是因為Jinja表現(xiàn)真的很差勁,,或者說過于復雜,,而在Python中,,它就像在文件的頂部添加# -*- coding: utf-8 -*-一樣簡單。 如果考慮一下,,在一天結(jié)束時,,給定一組輸入變量,模板引擎卻只返回一組純文本,。使用Python實現(xiàn)這個幾乎是毫無價值的,。還有另一種特定于Salt py的渲染器是需要注意的:在哪里找到輸入變量。Salt注入一個名為context的全局變量,,它是一個包含您發(fā)送給模板的所有變量的字典,。 考慮下面的模板(文件擴展名實際上并不重要,但是最好保持一致,,這樣您和您的文本編輯器就可以知道文件格式): /etc/salt/templates/example.py 這個Python模板可以作為任何其他的模板使用:我們只需要告訴Salt通過py引擎生成文本,。我們可以使用net.load_template執(zhí)行功能從命令行中驗證和加載生成的內(nèi)容(minion1是瞻博網(wǎng)絡(luò)設(shè)備): 或通過狀態(tài)系統(tǒng): /etc/salt/states/example.sls 當執(zhí)行example.sls公式,它將生成/tmp/ntp_peers.cfg文本文件,處理salt://templates/example.py模板(請記住,,salt://指向Salt文件服務(wù)器的位置,在本例中是/etc/salt,,通過file_roots配置)通過py接口,。執(zhí)行$ sudo salt 'minion1' state.sls example,它將生成以下內(nèi)容: 編寫執(zhí)行模塊 執(zhí)行模塊是Salt中最靈活的子系統(tǒng),,這并不是秘密,,因為它們允許您重用代碼,正是由于它們可以在其他不同的子系統(tǒng)中使用,,包括:渲染器(以及隱式的,、模板)、狀態(tài)模塊,、引擎,、重圖器、指向標等等,?;旧希坏┠憔帉懥艘粋€執(zhí)行模塊,,它就可以立即在Salt的命名部分中找到,。 編寫一個執(zhí)行模塊所需的內(nèi)容全部都是關(guān)于Python的基本知識,通過閱讀 然后將一個文件保存在一個名為 /etc/salt/_modules/ip_addresses.py 提示 有大量的輔助函數(shù)庫可以重用,??梢栽趗tils目錄中找到?;ㄒ恍r間瀏覽這個目錄和它的文件,。別擔心,以我的經(jīng)驗來看,,要想避免重新發(fā)明車輪,,你可能需要花上幾個月或幾年的時間來了解你需要的確切功能。去到那里,,完成任務(wù),,得到一件“車輪再發(fā)明者”t恤。:-) 此外,,請記住,,您可以從其他執(zhí)行函數(shù)調(diào)用執(zhí)行函數(shù),如下所述,。 要讓Salt知道還有另一個執(zhí)行模塊要加載,,您必須運行saltutil.sync_modules,只要執(zhí)行新定義的函數(shù)(語法是 注意在最后一個示例中,鍵值參數(shù)長度是從命令行傳遞到生成函數(shù),,之前我們在Python模塊中定義了這個名稱,。 請注意 默認情況下,執(zhí)行模塊的名稱只是Python模塊(文件)的名稱,。要使用不同的名稱,,可以使用 到這里,,我們有了一個為我們自己的環(huán)境定義的新函數(shù),。它可以從命令行調(diào)用,如我們所見,,但也可以在不同的區(qū)域中使用,,如下所述。 在模板中調(diào)用執(zhí)行模塊 新ip_addresses.generate執(zhí)行函數(shù)可以從Salt支持的任何模板語言中調(diào)用,,例如Jinja: 上面的模板將被呈現(xiàn)為: 在支柱SLS中調(diào)用執(zhí)行模塊 使用ip_addresses.generate函數(shù),,我們可將上面例子支柱重寫/etc/salt/pillar/ip_addresses.sls。 可以完全相同的方式在公式中調(diào)用執(zhí)行模塊。 從其他Salt模塊調(diào)用執(zhí)行模塊 一般來說,,我們可以使用__salt__(雙下劃線)來從不同的Salt模塊中執(zhí)行一個功能,。 例如,我們可以定義下面的執(zhí)行模塊,,該模塊將調(diào)用ip_address.generate功能: /etc/salt/_modules/ixp_interfaces.py 注意,,在上面的例子中,擴展參數(shù)不再是鍵值對,,在執(zhí)行該函數(shù)時,,我們總是需要傳遞一個值: 類似地,我們可以重用在其他子系統(tǒng)中生成的ip_addresses.generate代碼,,如指向標,、引擎、跑者或支柱等,。 這樣做的另一個好處是您可以控制更簡單的各種參數(shù),。例如,在我們設(shè)計生成函數(shù)的方式中,,它從10.10.10.0網(wǎng)絡(luò)返回IP地址; 假設(shè)在某一時刻,,我們決定從172.17.19.0網(wǎng)絡(luò)生成地址,我們只有一個地方可以進行調(diào)整,。進一步說,,如果這經(jīng)常發(fā)生變化,我們可以將基礎(chǔ)移動到另一個鍵值對,,或者在一個配置選項中: IP網(wǎng)絡(luò)作為形參: /etc/salt/_modules/ip_addresses.py IP網(wǎng)絡(luò)作為配置選項: 在第二種方法中,選擇__opts__(雙下劃線)是擁有被控端配置選項的字典(從配置文件中讀取/etc/salt/minion,,用于普通的被控端,,或用于代理被控端的/etc/salt/proxy),并與支柱和谷物數(shù)據(jù)合并,。 如前所述,,在您的系統(tǒng)中傳播一個變更,只需要調(diào)整(代理)被控端配置文件,,例如:
請注意 在定義您自己的配置選項之前,,檢查它是否已被定義,避免最終的沖突:配置Salt被控端,。 結(jié)論 一如既往,,在Salt里沒有“最佳規(guī)則”:Salt是非常靈活的,你的環(huán)境決定了什么對你最有意義,。Salt不僅向你展示了Python的強大功能,,而且從這個角度來看,它也表現(xiàn)得更像Python,并為你提供了以各種方式解決任何問題的方法,,而且沒有嚴格的限制,。 這就是為什么你總是要評估和決定哪種方法最適合你。 我的建議是嘗試將復雜性轉(zhuǎn)移到執(zhí)行模塊中,。是的,,在你自己的環(huán)境中編寫更多的擴展模塊(對于社區(qū)來說,開源那些與您的業(yè)務(wù)邏輯耦合不高的源代碼也是非常不錯的),。使用執(zhí)行函數(shù)簡化復雜的Jinja模板,。為你的團隊多寫輔助類。保持SLS文件簡明扼要,。 當SLS(或SLS的邏輯部分),,或這模板的長度超過5-10行,這時應(yīng)該開始產(chǎn)生疑問,,并找到優(yōu)化和使代碼可重用的方法,。 但是,當您不能將復雜性分解開,,或是沒有必要轉(zhuǎn)移到其他地方時,,你就會再次被覆蓋掉,你可以通過使用常規(guī)的Python渲染器的強大功能來避免復雜的yaml/jinja(順便說一下,,這無疑是我最愛的接口),。 英文原文:https:///2017-12-19-salt-pure-python/ |
|