聽過 Configuration as Data 嗎?它在 Kubernetes 中如何實踐?

近幾年,以「雲原生」為基礎的基礎架構和應用框架出現了爆炸性的成長。現代化的基礎架構平台;從 Kubernetes 這類的容器編排工具,到用於快速開發應用程式的無伺服器平台,應有盡有。同時,系統管理員用來部署、配置、管理這些平台的腳本 (shell script),也演變成所謂的「基礎架構即代碼」(Infrastructure as Code, IaC),並透過 Python 或 Ruby 等高階語言或 HashiCorp 的 HCL(通過 Terraform)等專用語言來進行撰寫。

雖然 IaC 已被廣泛使用,但它依然存在嚴重缺陷:「程式碼並沒有為開發者的目的和執行操作之間進行關聯。」關聯性是提供穩定、安全、高速的 IT 環境的基礎,但是,每次修改程式碼時,你都需要執行驗證工具來確定符合開發者的目的。

這就產生了一個問題,首先,為什麼系統管理員會需要使用程式語言?其次,為什麼這一切如此複雜?在許多方面上來說,它試圖將不可預測的事情進行自動化,但是大多數的基礎架構都較為鬆散,需要很多步驟,來模仿系統管理員登入時的操作。

此外,雖然配置基礎架構很重要,但配置完成後,IT 維運人員仍需要部署及管理整個基礎架構和應用程式,以維持正常營運。在理想狀況下,你可以使用相同的管理工具來全面部署和管理基礎架構和應用程式。

在 Kubernetes 的世界裡怎麼做

有了 Kubernetes,情況就不一樣了!

Kubernetes 沒有採取命令式或程序化的方法,而是依賴 “Configuration as Data” 的概念,採取宣告式的方法來部署和管理基礎架構以及應用程式,宣告你所期望的狀態,而不用指定其中的精確動作或步驟。每一個 Kubernetes 資源都是由 Configuration as Data  所定義,使用 YAML 和 JSON 文件來表示。建立部署?定義服務?設置策略?以上都是 Configuration as Data。在過去的六年裡,只有 Kubernetes 使用者一直有在了解這個含意。

想深入了解 CaD 的含意嗎?以下我們舉一個簡單的 Kubernetes 例子:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go 
  namespace: default
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-samples/helloworld-go

只需簡單透過 10 行 YAML,你就可以定義你的應用程式所屬的代理服務,建立 Route、Ingress、Service 和負載均衡器以設置網路,而且,它還會根據流量自動調整。

Configuration as Data (CaD) 是如何運作的?在 Kubernetes API Server 中,有一組控制器,負責確保現有基礎架構與你所表達的宣告狀態是否一致。舉例來說,Kubernetes 控制器可能會確保建立一個負載均衡器和代理服務,連結到相對應的 Pod,並且處理好所有必要配置及後續維護,以實現你所宣告的狀態。控制器會永遠維持該配置狀態,直到你明確更新或刪除。

比較少人知道的是,原來只為容器化應用提供支援的 Kubernetes Resource Model (KRM),也可以管理「非 Kubernetes」資源,包括其他基礎架構、平台和應用服務。例如你可以使用 KRM 來部署、管理雲端數據庫、儲存空間、網路等等。一些 Google Cloud 的客戶也會使用開源工具,透過內部開發的 Kubernetes 控制器來管理他們的應用程式和服務。

那麼,你要如何開始利用 KRM 來管理 Google Cloud 資源?去年,Google Cloud 發布了 Config Connector,為 Google Cloud 資源提供內建的控制器。 Config Connector 可以讓你用管理 Kubernetes 的方法來管理 Google Cloud 的基礎架構,將「基礎架構配置定義為數據」,從而降低整個團隊的維運複雜性和架構認知差異。

按照上面的範例,假設我們想部署一個 Google Cloud Redis 作為我們服務的備份儲存。我們可以藉由撰寫一個簡單的 YAML 來使用 KRM,而這樣的表示方式與我們應用程式的表示方式完全一樣:

apiVersion: redis.cnrm.cloud.google.com/v1beta1
kind: RedisInstance
metadata:
  name: redisinstance-sample
  namespace: default
spec:
  displayName: Sample Redis Instance
  region: us-west1
  tier: basic
  memorySizeGb: 16

我們可以透過 KRM 和 Config Connector 來建立 Redis:

 kubectl apply -f ./redis-example.yaml

CaD 與傳統 IaC 工具的結合

這麼說,是否意味著你不再需要 Terraform 這種的傳統 IaC 工具?

不一定,因為你還是需要協調系統之間的配置,例如需要收集服務 IP 以及更新外部 DNS 來源,這些就是傳統 IaC 工具的用途。使用 Config Connector 來管理 Google Cloud 資源的好處是,兩者的關係會更加牢固。這種模式也提供了更好的整合,並明確劃分了資源「配置」和資源「管理」的責任。下面是一個使用 Terraform 的例子

resource "google_compute_network" "demo_network" {
    name = "demo-network"
}

resource "kubernetes_manifest" "redis_instance" {
  provider = kubernetes-alpha

  depends_on = [
     google_compute_network.demo_network,
  ]

  manifest = {
    "apiVersion" = "redis.cnrm.cloud.google.com/v1beta1"
    "kind" = "RedisInstance"
    "metadata" = {
      "name" = "redisinstance-sample"
      "namespace" = "default"
     }
     "spec" = {
 "displayName" = "Sample Redis Instance"
 "region" = "us-west1"
 "tier" = "BASIC"
 "memorySizeGb" = 16
 "authorizedNetworkRef" = {
    "external" = google_compute_network.demo_network.self_link
 }
      }
    }
  }

這個例子中,我們參考 terraform-provider-google 創建了一個名為 “demo_network” 的Google Cloud 網路,並以 Terraform Kubernetes provider 以及 KRM 建立一個 Google Cloud Redis,與之連結。從表層來看,Terraform 和這兩個提供者之間的協議看起來一樣,但其實他們負責不同的工作。

Terraform-provider-google 直接調用 Google Cloud API 來建立網路資源,如果你想使用其他配置工具,你需要建立一套新的 Google Cloud API 集成。此外,你還可以在 Kubernetes 和 Terraform 之間來回,以查看在每個介面中分別創建的資源。

另一方面,Terraform Kubernetes provider 是由執行在 Kubernetes 中的控制器所支援,該控制器提供用於配置 Redis 的 KRM 接口。當 Terraform 以資料的形式將配置提交給 Kubernetes API Server 的時候,資源就會被建立起來,並由 Kubernetes 主動管理。「配置即數據 (CaD)」在工具和接口之間建立了一個強大的關係,以保證兩者獲得一致的結果。你可以在 Kubernetes 接口中,共同管理資源和應用程式,Kubernetes API Server 會不斷地將現有的 Google Cloud 狀態與你使用 KRM 在 Terraform 中建立的狀態進行協調。CaD 可說是對 Terraform 的補強,Terraform 的執行間隔可能相隔數小時、數天或數週,但都具有一致性。

總而言之,CaD 是一種基礎架構和應用程式的管理方法,它可以使本地資源和配置工具(例如 IaC 和命令列)之間互動流暢,同時這也是一個正在快速發展的領域。您可以在 Google Cloud 專案中試用 Config Connector,或諮詢 iKala Cloud 以了解更多 Google 的開發工具!

(本文翻譯改編自 Google Cloud。)