聲明性基礎設施與Jsonnet模板語言
這篇文章是我們一係列的一部分內部工程博客磚平台,基礎設施管理、集成、工具、監視和配置。Beplay体育安卓版本
在磚工程,我們的瘋狂粉絲Kubernetes。我們的平台基礎設施KubBeplay体育安卓版本ernetes內運行,是否在AWS雲更規範的環境。
然而,我們發現,Kubernetes獨自管理一個複雜的服務基礎設施是不夠的,這可能包括Kubernetes中創建的資源(例如,豆莢,服務等)和外部資源我的角色。管理的複雜性來自於(1)需要對當前狀態的可見性的基礎設施(2)推理如何更改基礎設施。
幫助降低管理複雜性,基礎設施是最理想的聲明(即。,described by a set of configuration files or templates). The first advantage is that one can easily inspect the target state of infrastructure by reading the configuration files. Second, since the infrastructure is entirely described by the files, changes can be proposed, reviewed, and applied as part of a standard software development workflow.
缺失的是什麼?
KubernetesYAML的配置文件已經在Kubernetes實現聲明對象的更新。用戶隻需要編輯一個對象的YAML文件,然後運行美元- f theobject.yaml kubectl適用
Kubernetes同步更改。這些YAML文件可能簽入到源代碼控製係統,如果需要,用戶可以查詢Kubernetes檢查現場版本的區別和本地文件。
然而,一遇到痛點當試圖將這種方法應用於一個更大的生產環境:
- 常見的結構不能跨YAML文件共享。您通常設置服務不止一次,而是好幾次,也許在{dev、過渡刺激}環境中,在不同的地理區域,如{於美國東部,亞太},和雲提供商{AWS, Azure,全球教育運動}。
YAML文件等必須經常參考關於外部定義的元數據實體關係數據庫、網絡配置,或SSL證書。
複雜的多層服務部署可能需要許多不同的組合資源或YAML文件。更新部署由許多這樣的文件變得繁瑣。
在這篇文章中,我們描述我們如何使用穀歌Jsonnet配置語言要解決這些問題,走過的一個例子使用Jsonnet templatizing服務部署,並提出一個Jsonnet風格指南基礎設施的用例模板。我們發現Jsonnet很容易開始,尺度複雜的用例。
Jsonnet用法在磚
2015年我們開始嚐試Jsonnet作為努力的一部分磚社區版,我們的免費Apache引發教育服務。從那時起,Jsonnet磚工程內很流行,1000年有超過40000行Jsonnet +不同的文件檢查到我們的主要開發庫。這些模板擴展到成千上萬行一旦物化生YAML。
團隊在磚目前使用Jsonnet Kubernetes資源管理配置上運行的服務(包括內部配置Kubernetes),AWS CloudFormation,起程拓殖,磚的工作,也等任務TLS證書管理和定義普羅米修斯警報。在過去的兩年裏,Jsonnet已經成為在工程的實際標準配置語言。
Jsonnet基礎知識
Jsonnet是一種配置語言,可以幫助你定義JSON數據。基本的想法是,一些JSON字段可能離開變量或表達式,在編譯時間評估。例如,JSON對象{“計數”:4}
可以表示為{數:2 + 2}
在Jsonnet。您還可以聲明隱藏字段用“::”可以在編譯過程中引用,如{x:: 2, y:: 2,數:美元。x + $ .y}
也評估{“計數”:4}
。
Jsonnet對象可能是從它派生出子類通過連接(“+”)對象覆蓋字段值,例如,假設我們定義以下。
當地基礎= {x::錯誤“你必須定義“x”的價值”,y::2,/ /這個領域有一個默認值數:美元。x+$.y};
然後,Jsonnet表達式(基礎+ {x:: 10})
將編譯{“計數”:12
}。事實上,Jsonnet編譯器需要重寫x從默認值提出了一個錯誤。因此,可以認為基地Jsonnet定義一個抽象基類。
Jsonnet編譯是完全確定的,不能執行外部I / O,使它適合定義配置。我們發現Jsonnet罷工的權利限製和靈活性之間的平衡——以前我們使用Scala代碼生成的配置,這錯太多的靈活性,導致許多頭痛。
在磚,我們擴展我們的kubectl工具(稱為kubecfg),這樣就可以直接Jsonnet文件作為參數。在內部它編譯Jsonnet平原JSON / YAML Kubernetes在發送數據之前。Kubernetes然後根據需要創建或更新對象基於上傳配置。
創作與Jsonnet Kubernetes對象
為了更好地理解如何使用Jsonnet Kubernetes,讓我們考慮的任務部署一個理想化的[1]單租戶“磚平台”為企業客戶。Beplay体育安卓版本這裏我們需要創建兩個截然不同但相關的服務部署:Webapp(用於交互式工作區)和集群管理器(用於管理引發集群)。此外,需要訪問的服務AWS RDS數據庫存儲持久數據。
[1]:請注意,我們實際上並不為每個客戶創造單個Jsonnet文件在現實中,將創建一個可怕的Jsonnet文件和本身將是一個問題。
模板定義Kubernetes部署
對於這些示例,我們將使用service-deployment.jsonnet.TEMPLATE,我們的一個簡化版本內部基礎模板,定義一個Kubernetes服務和部署作為一對在一起。實例化服務和部署在一起構成一個獨立的“生產服務”在Kubernetes可以接收網絡流量。注意模板有兩個必需的參數除了幾個可選參數,包括可選配置傳遞到服務二進製本身:
service-deployment.jsonnet.TEMPLATE:
/ /模板Kubernetes(服務、部署)。{/ /這個模板所需要的參數名::錯誤“必須指定名”,dockerImage::錯誤“必須指定dockerImage”,/ /可選參數為該模板。serviceConf::{}…}
示例1:一個文件為每個服務部署
service-deployment.jsonnet。我們有模板,最簡單的選擇是創建單獨的Jsonnet Webapp和集群管理器文件。在每個文件我們必須導入基礎模板,子類,並填寫必需的參數。
我們指定的服務名稱,碼頭工人形象包含二進製的服務,和一些服務特定的配置包括RDS的地址。在這個例子中,RDS地址是硬編碼的,但是以後我們會展示如何導入元數據如運行CloudFormation腳本的輸出文件。
以下文件描述了管理服務(服務需要在其標準的意義,我們會參考Kubernetes服務顯式等)。我們還在Jsonnet模板通過構建特定於服務的配置serviceConf:字段
,模板通過艙作為一個環境變量。我們發現它有用的統一服務和Kubernetes配置:
當地serviceDeployment =進口“. . / service-deployment.jsonnet.TEMPLATE”;/ / foocorp集群管理器部署。serviceDeployment + {名::“foocorp-manager”,dockerImage::“經理:2.42 rc1”,serviceConf::{customerName:“foocorp”,數據庫:“用戶- db.databricks.us -西方- 2. - rds.amazonaws.com”,},}
應用服務創建集群,它必須指定集群管理器的Kubernetes DNS地址,可以決定提前從Kubernetes服務名稱:
當地serviceDeployment =進口“. . / service-deployment.jsonnet.TEMPLATE”;/ / webapp foocorp部署。serviceDeployment + {名::“foocorp-webapp”,dockerImage::“webapp: 2.42 rc1”,serviceConf::{customerName:“foocorp”,數據庫:“用戶- db.databricks.us -西方- 2. - rds.amazonaws.com”,managerAddress:“foocorp-manager.prod.svc.cluster.local”,},}
如果你有示例代碼回購克隆,您可以查看這些模板的物化輸出通過運行jsonnet編譯器上的文件,例如:
美元/磚/簡單/ foocorp-webapp.jsonnet jsonnet例子
所以我們得到了什麼?我們設法至少刪除一些標準的Kubernetes樣板定義服務,減少每個部署定義從大約10 100行。然而,我們可以做得更好,還有重複的參數將使這種模式很難維持,如果有許多不同的客戶或更多的服務每個客戶的要求。beplay体育app下载地址
示例2:組合在一起部署在單個文件中
因為Webapp和集群管理器作為一個單元或服務部署在一起碎片每個客戶,每個客戶隻應該有一個文件描述其獨特的要求。這的確是可以定義一個模板,shard.jsonnet.TEMPLATE一起,組成Webapp和集群管理器部署沒有重複的參數。
訣竅是模板定義了一個Kubernetes“列表”對象,其中可以包括多個Kubernetes資源在一個JSON對象。我們合並產生的列表的服務部署使用Jsonnet模板標準庫函數std.flattenArrays
:
shard-v1 / shard.jsonnet.TEMPLATE:
當地serviceDeployment =進口“. . / service-deployment.jsonnet.TEMPLATE”;
{/ /需要的參數customerName::錯誤“customerName必須定義”,釋放::錯誤必須定義“釋放”,/ /可選參數commonConf::{customerName:$ .customerName數據庫:“用戶- db.databricks.us -西方- 2. - rds.amazonaws.com”,},
當地webapp = serviceDeployment +……當地經理= serviceDeployment +……類:“列表”,項目經理:std.flattenArrays ([webapp]),}
現在我們可以方便地定義FooCorp整個部署沒有任何重複的數據,並使用隻有一個文件:
shard-v1 / foocorp-shard.jsonnet:
當地的shardTemplate=導入“shard.jsonnet.TEMPLATE”;shardTemplate+{customerName::“foocorp”,釋放::“2.42 rc1”,}
示例3:子類化模板為多個環境
看到Jsonnet的靈活性,考慮如何子類中定義的模板上麵的示例創建碎片,在開發環境中運行。在開發,部署應該使用數據庫開發而不是生產,我們還想要運行的最新前沿碼頭工人的圖像。要做到這一點,我們可以進一步shard.jsonnet子類。模板,不同的環境。這使它可以創建特定的生產或開發碎片從這些專業模板:
shard.jsonnet公開的接口。模板更明確的,而不是出口對象,我們將出口的一個函數newShard(名稱、版本、env)
,它可以被稱為構造碎片對象。env對象封裝環境之間的差異(如數據庫URL)。
shard-v2 / shard.jsonnet.TEMPLATE:
當地的serviceDeployment=導入“. . / service-deployment.jsonnet.TEMPLATE”;當地的newShard (customerName釋放env)={當地的commonConf={customerName: customerName,數據庫:env.database,},當地的webapp=serviceDeployment+{名:customerName+“應用”,dockerImage::“webapp:“+釋放,serviceConf: commonConf+{managerAddress: customerName+“-manager.prod.svc.cluster.local”,},},當地的經理=serviceDeployment+{名:customerName+“經理”,dockerImage::“經理:”+釋放,serviceConf:: commonConf,},
apiVersion:“v1”,:“名單”,項目:std.flattenArrays ([webapp。項目,manager.items]),};//導出函數作為一個構造函數為碎片{newShard:: newShard,}
dev碎片模板現在隻需要填寫所需env dev和指定默認使用各種版本。它獲得env通過導入一個簡單的JSON文件包含所需的數據庫元數據:
shard-v2 / dev-shard.jsonnet.TEMPLATE:
當地的shardTemplate=導入“shard.jsonnet.TEMPLATE”;當地的devEnv=導入“dev-env.json”;當地的newDevShard (shardName釋放=“尖端”)=(shardTemplate.newShard (shardName釋放,env=devEnv));
{newDevShard:: newDevShard,}
完整的示例代碼的刺激和dev碎片模板可以在這裏找到:https://github.com/databricks/jsonnet-style-guide/tree/master/examples/databricks/shard-v2
注意子類化的使用功能是可選的,我們可能相反dev-shard.jsonnet.TEMPLATE和prod-shard.jsonnet.TEMPLATE隻使用領域覆蓋。然而,在我們的經驗中,使用特別的領域覆蓋,雖然強大,往往會導致更多的脆性模板。我們發現它有用建立最佳實踐限製使用這樣的結構在較大的模板。
Jsonnet風格指南
在上麵的例子中,我們看到了如何使用Jsonnet模板移除Kubernetes對象之間的重複配置數據,組成多個部署在一起,和外部實體引用。這大大簡化了基礎設施管理。然而,在大型項目模板本身可能成為一種複雜性。我們發現這是可控的一些最佳實踐和投資在Jsonnet工具:
這裏有一些Jsonnet最佳實踐我們發現:
- 對於大型模板(> 10參數),避免直接子類化時首要的內部字段。相反,為使用Jsonnet函數模板定義顯式的構造函數,這有助於與可讀性和鼓勵模塊化。
- 檢查你Jsonnet配置到源代碼控製。
- 添加導向模板編譯測試,以確保所有檢查。這可以避免無意的破壞,當普通模板更新。您還可以創建使用“單元測試”Jsonnet斷言表達式斷言不變量在模板變量。
- 考慮也檢查Jsonnet物化JSON / YAML代碼評審期間改變更為明顯。因為共同Jsonnet模板可以由許多進口文件,更改一個文件會影響很多的輸出。幸運的是,意想不到的變化可以被探測到在編譯時如果還包括物化YAML Jsonnet變化。
- 重構通常——沒有破碎險。自Jsonnet模板編譯混凝土JSON / YAML文件,可以檢查之前和之後的輸出,確保重構是正確的(即零淨物化diff)。
- 把盡可能多的配置Jsonnet。它可能容易引入邏輯服務基於一些環境標誌(如dev vs刺激)。我們發現這是一個反模式,應該在模板編譯時確定這樣的配置,而不是在運行時。這一原則的密封的配置有助於防止意外當服務部署到新的環境。
更詳細的建議,看看我們新發布Jsonnet風格指南。我們也想從社區歡迎任何評論或貢獻。
結論
在這篇文章裏,我們分享我們的經驗在磚使用Jsonnet簡化基礎設施管理。作為一個統一的模板語言,使跨廣泛的基礎設施和服務組成,Jsonnet帶給我們更近一步一個完全聲明性基礎設施。我們發現Jsonnet易於使用,並具有足夠的靈活性來作為我們的主要配置語言,並建議你也試試!
如果你發現這個話題很有趣,當心未來博客的工具我們圍繞Jsonnet Kubernetes。我們也招聘對於雲平台、基礎設施Beplay体育安卓版本和磚Serverless。