首頁 >後端開發 >Golang >我們如何使用 Kubernetes 作業來擴充 OpenSSF Scorecard

我們如何使用 Kubernetes 作業來擴充 OpenSSF Scorecard

WBOY
WBOY原創
2024-08-09 10:20:521157瀏覽

How we use Kubernetes jobs to scale OpenSSF Scorecard

我們最近在 OpenSauced 平台上發布了與 OpenSSF Scorecard 的整合。 OpenSSF Scorecard 是一個功能強大的 Go 命令列介面,任何人都可以使用它來開始了解其專案和依賴項的安全狀況。它會對危險的工作流程、CICD 最佳實踐、專案是否仍在維護等等進行多項檢查。這使得軟體開發者和消費者能夠了解他們的整體安全狀況,推斷專案是否可以安全使用,以及需要對安全實踐進行改進。

但我們將 OpenSSF 記分卡整合到 OpenSauced 平台的目標之一是使其可供更廣泛的開源生態系統使用。如果它是 GitHub 上的儲存庫,我們希望能夠顯示它的分數。這意味著將 Scorecard CLI 擴展到 GitHub 上的幾乎所有儲存庫。說起來容易做起來難!

在這篇文章中,讓我們深入了解如何使用 Kubernetes 做到這一點,以及我們在實現此整合時做出了哪些技術決策。

我們知道我們需要建立一個 cron 類型的微服務,它可以頻繁地更新無數儲存庫中的分數:真正的問題是我們如何做到這一點。臨時運行記分卡 CLI 是沒有意義的:該平台很容易被淹沒,我們希望能夠對整個開源生態系統的分數進行更深入的分析,即使 OpenSauced 存儲庫頁面尚未被訪問最近訪問過。最初,我們考慮使用 Scorecard Go 函式庫作為直接依賴程式碼,並在單一整體微服務中執行記分卡檢查。我們還考慮使用無伺服器作業來運行一次性記分卡容器,該容器將傳回各個儲存庫的結果。

我們最終採用的方法結合了簡單性、靈活性和強大功能,是大規模使用 Kubernetes 作業,所有這些都由「調度程式」Kubernetes 控制器微服務管理。執行一個 Kubernetes 作業無需與記分卡建立更深層的程式碼集成,它為我們提供了與使用無伺服器方法相同的好處,但成本更低,因為我們直接在 Kubernetes 叢集上管理這一切。作業的運作方式也提供了很大的靈活性:它們可以有很長的超時時間,可以使用磁碟,並且像任何其他 Kubernetes 範例一樣,它們可以讓多個 pod 執行不同的任務。

讓我們分解這個系統的各個組件,看看它們是如何深入工作的:

這個系統的第一個也是最大的部分是「scorecard-k8s-scheduler」;類似 Kubernetes 控制器的微服務,可在叢集上啟動新作業。雖然此微服務遵循建置傳統 Kubernetes 控制器或操作器時使用的許多原則、模式和方法,但它不會監視或改變叢集上的自訂資源。它的功能是簡單地啟動運行 Scorecard CLI 的 Kubernetes 作業並收集完成的作業結果。

我們先來看看Go程式碼中的主控制迴圈。此微服務使用 Kubernetes Client-Go 函式庫直接與執行微服務的叢集進行互動:這通常稱為叢集上配置和客戶端。在程式碼中,引導叢集上的客戶端後,我們輪詢資料庫中需要更新的儲存庫。一旦找到一些儲存庫,我們就會在各個工作「執行緒」上啟動 Kubernetes 作業,這些作業將等待每個作業完成。

// buffered channel, sort of like semaphores, for threaded working
sem := make(chan bool, numConcurrentJobs)

// continuous control loop
for {
    // blocks on getting semaphore off buffered channel
    sem <- true

    go func() {
        // release the hold on the channel for this Go routine when done
        defer func() {
            <-sem
        }()

        // grab repo needing update, start scorecard Kubernetes Job on-cluster,
        // wait for results, etc. etc.

        // sleep the configured amount of time to relieve backpressure
        time.Sleep(backoff)
    }()
}

這種具有緩衝通道的「無限控制循環」方法是 Go 中連續執行某些操作但僅使用配置數量的執行緒的常見方法。在任何給定時間運行的並發 Go 函數的數量取決於「numConcurrentJobs」變數的配置值。這將緩衝通道設定為充當工作池或信號量,表示在任何給定時間運行的並發 Go 函數的數量。由於緩衝通道是所有執行緒都可以使用和檢查的共享資源,因此我經常將其視為信號量:一種資源,很像互斥體,多個執行緒可以嘗試鎖定和存取。在我們的生產環境中,我們擴展了該調度程序中同時運行的執行緒數量。由於實際的調度程序的計算量不是很大,只會啟動作業並等待結果最終浮出水面,因此我們可以突破該調度程序可以管理的範圍。我們還有一個內建的退避系統,可以在需要時嘗試緩解壓力:如果出現錯誤或沒有找到可以計算分數的儲存庫,該系統將增加配置的「退避」值。這確保了我們不會不斷地用查詢猛擊我們的資料庫,並且記分卡調度程序本身可以保持在“等待”狀態,而不佔用集群上寶貴的計算資源。

在控制循環中,我們做了一些事情:首先,我們在資料庫中查詢需要更新記分卡的儲存庫。這是一個簡單的資料庫查詢,它基於我們監視的一些時間戳元資料並具有索引。自從計算儲存庫的最後一個分數以來,一旦經過配置的時間量,它將冒泡並由運行記分卡 CLI 的 Kubernetes 作業進行處理。

接下來,一旦我們有了可以取得分數的儲存庫,我們就使用「gcr.io/openssf/scorecard」鏡像啟動 Kubernetes 作業。使用 Client-Go 在 Go 程式碼中引導這項工作看起來與使用 yaml 非常相似,只需使用透過「k8s.io」導入提供的各種函式庫和 API 並以程式設計方式執行:

// defines the Kubernetes Job and its spec
job := &batchv1.Job{
    // structs and details for the actual Job
    // including metav1.ObjectMeta and batchv1.JobSpec
}

// create the actual Job on cluster
// using the in-cluster config and client
return s.clientset.BatchV1().Jobs(ScorecardNamespace).Create(ctx, job, metav1.CreateOptions{})

作業建立後,我們等待它發出已完成或出錯的訊號。與 kubectl 非常相似,Client-Go 提供了一種有用的方式來「監視」資源並觀察它們發生變化時的狀態:

// watch selector for the job name on cluster
watch, err := s.clientset.BatchV1().Jobs(ScorecardNamespace).Watch(ctx, metav1.ListOptions{
    FieldSelector: "metadata.name=" + jobName,
})

// continuously pop off the watch results channel for job status
for event := range watch.ResultChan() {
        // wait for job success, error, or other states
}

最後,一旦我們成功完成作業,我們就可以從作業的 pod 日誌中取得結果,其中將包含來自記分卡 CLI 的實際 json 結果!獲得這些結果後,我們可以將分數更新插入資料庫並更改任何必要的元數據,以向我們的其他微服務或 OpenSauced API 發出訊號,表明有一個新分數!

如前所述,scorecard-k8s-scheduler 可以同時執行任意數量的並發作業:在我們的生產環境中,我們同時運行大量作業,所有作業均由該微服務管理。目的是能夠每兩週更新一次 GitHub 上所有儲存庫的分數。憑藉這種規模,我們希望能夠為任何開源維護者或消費者提供強大的工具和見解!

「調度程式」微服務最終成為整個系統的一小部分:任何熟悉 Kubernetes 控制器的人都知道,系統運作還需要額外的 Kubernetes 基礎架構。在我們的例子中,我們需要一些基於角色的存取控制(RBAC)來使我們的微服務能夠在叢集上建立作業。

首先,我們需要一個服務帳戶:這是調度程序將使用的帳戶,並具有與其綁定的存取控制:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: scorecard-sa
  namespace: scorecard-ns

我們將此服務帳戶放置在所有這些運行的「scorecard-ns」命名空間中。

接下來,我們需要為服務帳戶進行角色和角色綁定。這包括實際的存取控制(包括能夠建立作業、查看 Pod 日誌等)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: scorecard-scheduler-role
  namespace: scorecard-ns
rules:
- apiGroups: ["batch"]
  resources: ["jobs"]
  verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]

—

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: scorecard-scheduler-role-binding
  namespace: scorecard-ns
subjects:
- kind: ServiceAccount
  name: scorecard-sa
  namespace: scorecard-ns
roleRef:
  kind: Role
  name: scorecard-scheduler-role
  apiGroup: rbac.authorization.k8s.io

您可能會問自己「為什麼我需要授予此服務帳戶存取權限才能取得 pod 和 pod 日誌?這不是存取控制的過度延伸嗎?」記住!作業有 pod,為了取得包含記分卡 CLI 實際結果的 pod 日誌,我們必須能夠列出作業中的 pod,然後讀取它們的日誌!

第二部分“RoleBinding”,是我們實際將角色附加到服務帳戶的地方。然後可以在叢集上啟動新作業時使用此服務帳戶。

向 Alex Ellis 和他出色的運行作業控制器致敬:這是正確使用 Client-Go 與 Jobs 的巨大啟發和參考!

大家保持俏皮!

以上是我們如何使用 Kubernetes 作業來擴充 OpenSSF Scorecard的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn