介紹了這麼多 Pulumi 的用法,都還沒詳細說明 Pulumi 到底怎麼運作,如何支援多語言的運作。今天就透過這篇文章來說明清楚 Pulumi 怎麼運作的。了解 Pulumi 如何運作,會對之後介紹如何做測試有幫助。
Pulumi 的架構大致如下圖,其中 Pulumi CLI、Pulumi Resource Plugin、Pulumi Language Runtime 這三組元件都是使用 Go 語言撰寫。使用 Go 語言撰寫有很多好處,可以輕鬆的跨平台、且不須額外安裝 Runtime 就可以執行程式。
Pulumi CLI 與 Pulumi Language Runtime 會在一開始安裝 Pulumi 的時候一起安裝。至於 Resource Plugin 則是在使用到需要的 Resource Provider 時,才會下載。可以透過 pulumi plugin ls
指令查詢目前安裝的 plugin。
接著分別介紹每個元件的作用。
Pulumi CLI 為最主要的 CLI 介面,之前使用的所有 Pulumi 指令都是由這個 CLI 提供。包含:pulumi up
、pulumi stack
、pulumi state
等。
Pulumi CLI 也負責協調 Language Runtime 與 Resource Plugin 的溝通,這些元件之間的溝通都是透過 gRPC 進行溝通。
Language Runtime 主要負責執行專案的 IaC 程式碼、並與 CLI 溝通所需要建立、更新或刪除的資源。
這邊的 Language Runtime 不是一個程式,是一組程式。每個 Pulumi 支援的程式語言都有一個 Runtime 執行檔。檔名會是pulumi-language-<language name>
,例如 pulumi-language-nodejs
、pulumi-language-python
等。每個語言的 runtime 會知道該語言的 Pulumi 專案程式碼要怎麼執行、並交換資訊。也就是說,通常情況下執行 Pulumi 的方法與一般執行程式語言的方式不同。例如在 Python 中,不能直接使用 python __main__.py
執行 Pulumi 程式。一定得讓 Pulumi CLI 透過 pulumi-language-python
執行 __main__.py
。
Pulumi 是使用 Plugin 架構支援不同的 Resource Provider。在 Pulumi 中,Resource Provider 分為兩種。
圖上所畫的 Resource Plugin 為第一種跨程式語言的 Resource Provider。
以下說明一下這兩種 Provider 的區別:
在 Pulumi 中,一個 Provider 要支援多個程式語言的話,就必須使用 Go 語言撰寫,並搭配 Pulumi SDK 撰寫 Plugin。這個 plugin 執行起來,會啟動一個 gRPC server,讓 CLI 可以與之溝通需要處理的資源。
另外還需要為各個要支援的程式語言撰寫相關的套件。為了各種程式語言所撰寫的套件主要目的是讓該程式語言知道 Resource Provider 支援多少 Resource、參數與設定有哪些。通常這些都可以透過產生器產生。這些各個程式語言的套件,並不會直接處理 Resource 的建立、更新與刪除。
這邊來看一個範例:Pulumi Command Resource Provider
這是一個跨程式語言的 Provider,可以透過這個 Provider 在 local 或是 remote 執行指令。
可以看到 SDK 資料夾中,分別有 dotnet
、go
、java
、nodejs
、python
資料夾。這就是為了讓不同的程式語言可以認得這個 Resource 所建立的套件。
另外一種 Resource Provider 為僅限單一程式語言的 Dynamic Provider。這種是直接在 IaC Source 的專案中,使用該程式語言撰寫資源建立、更新與刪除的邏輯。因為是使用特定程式語言撰寫,不太具備跨語言的移植能力,且功能也受限較多。例如說在 nodejs 中的 Dynamic Provider (使用 TypeScript 或 JavaScript 撰寫),並不支援 import resource。每個不同的語言在 Dynamic Provider 所支援的功能都不同。使用時需要參考 Pulumi 的 Dynamic Provider 文件。
這邊一樣提供一個 Dynamic Resource Provider 的範例:aws-py-dynamicresource
這是 Pulumi 官方的範例集中的其中一個範例,可以在程式碼中看到,直接在 Python 程式碼中撰寫 Provider。並提供了 create
、delete
、diff
、update
等資源處理的方法。
以下節錄範例中的 Dynamic Provider 程式碼內容供參考。
class SchemaProvider(ResourceProvider):
def create(self, args):
// ......
def delete(self, id, args):
// ......
def diff(self, id, old_inputs, new_inputs):
// ......
def update(self, id, old_inputs, new_inputs):
// ......
今天的文章介紹了 Pulumi 的架構與運作原理,主要是要為之後撰寫測試的文章鋪墊。因為知道這些流程,才知道測試的時候如果需要做隔離,要如何隔離。