天天看點

使用 Prow 實作 GitOps 與 ChatOps

本篇文章将對 Prow 進行基本的介紹,并根據架構對其中的工作原理逐一講解。之後再引導大家如何在 Kubernetes 環境中部署使用 Prow。

Prow 介紹

Prow 是基于 Kubernetes 的 CI/CD 系統。它能夠對各種類型的事件進行觸發,并向不同的服務報告狀态。與此同時,它還為 GitHub 自動化實作了政策增強、ChatOps(如 /foo 風格的指令)以及自動化合并 PR等功能。

目前 Kubernetes、Istio、Prometheus 等 CNCF 項目都使用 Prow 進行開源協同。另外一款雲原生的 CI/CD 平台 —— Jenkins X 也将 Prow 作為關鍵元件內建到項目中以實作 Serverless。

Prow 項目還是作為 kubernetes/test-infra 項目下的子目錄而存在。

  • Kubernetes 生态的 Prow 系統:https://prow.k8s.io/
  • Prow 開源倉庫:https://github.com/kubernetes/test-infra/
  • Prow 官方文檔:https://github.com/kubernetes/test-infra/blob/master/prow/README.md

Prow 架構

下圖是 Prow 的微服務結構,每個服務元件在 Kubernetes 中都以無狀态的 Deployment 進行部署。

使用 Prow 實作 GitOps 與 ChatOps

核心元件

  • Hook

    是整個系統中最重要的一塊。這是一個無狀态服務,用于監聽 GitHub Webhooks 并分發事件到相應的 Plugins。這些 Plugin 能夠實作觸發 Jobs、處理 /foo 風格的指令、發送資訊到 Slack 等功能。 參閱 prow/plugins 以擷取更多關于 Plugin 的資訊。
  • Plank

    是 Prow 系統的 controller,用于管理運作在 Kubernetes Pods 中的 jobs 的執行與生命周期。
  • Deck

    是 Prow 系統的可視化界面,用于展示 Jobs 狀态、PR 狀态、自動化合并的狀态和曆史記錄、指令和插件等幫助資訊。
  • Horologium

    用于觸發定時任務 (periodic jobs)。
  • Sinker

    用于清理舊的任務和 pods。
  • Crier

    用于報告 Prowjob 的狀态。目前支援的 reporter 有 Gerrit、Pubsub、GitHub、Slack。
  • Tide

    用于自動重新測試 PR,并根據預先設定好的合并條件,對 PR 進行自動化合并。

ProwJob

Prow 的 job 是通過 ProwJob 進行定義的。ProwJob 以 Kubernetes CustomResourceDefinition 實作。

Prowjob 類型主要分為以下三種:

  • Periodic

    針對于定時任務。
  • Postsubmit

    針對于 PR 合并之後的任務。
  • Presubmits

    針對于 PR 代碼的任務。

參閱 ProwJob docs 以擷取更多關于 Prowjob 的資訊。

Prow 工作流程

使用 Prow 實作 GitOps 與 ChatOps

上圖為 Prow 對于 PR 的互動序列圖,能夠清楚的了解 Prow 的基本用法與互動流程。

Prow Hook 監聽來自 GitHub 的 http 請求,并将其轉化為 “GitHub event objects”,随後 Hook 将 GitHub event 廣播到 Prow Plugins,根據 GitHub event 觸發相應的 Prow Plugin 來處理事件。就 /ok-to-test 而言,Prow Hook 會為此建立一個 “Issue Comment Event”

當 “hook” 發送用于表達 /test all 指令的 event 到 Trigger plugin,Trigger plugin 在運作測試之前會檢查該 PR 是否符合要求,要求例如有 “該 PR 送出者是否為 GitHub Organization 成員”、“該 PR 是否存在

ok-to-test

标簽”等。驗證通過後,Trigger plugin 會根據 ProwJob 的類型和相關資訊告知 Kubernetes API server 建立 ProwJob Custom Resource

ProwJob 的 yaml 表現形式可能如下:

apiVersion: prow.k8s.io/v1
kind: ProwJob
metadata:
  name: 32456927-35d9-11e7-8d95-0a580a6c1504
spec:
  job: pull-test-infra-bazel
  decorate: true
  pod_spec:
    containers:
    - image: gcr.io/k8s-testimages/bazelbuild:0.11
  refs:
    base_ref: master
    base_sha: 064678510782db5b382df478bb374aaa32e577ea
    org: kubernetes
    pulls:
    - author: ixdy
      number: 2716
      sha: dc32ccc9ea3672ccc523b7cbaa8b00360b4183cd
    repo: test-infra
  type: presubmit
status:
  startTime: 2017-05-10T23:34:22.567457715Z
  state: triggered
           

Plank 根據預先設定好的周期(每隔30秒)運作

Sync

操作,通過 syncKubernetesJob 同步每個 ProwJob 和 Pod,維護其生命周期和狀态,并且可以更新 GitHub status。

最後,Sinker 會根據預先設定好的周期,定期向 Kubernetes API server 清理舊的 job 和 pod。

部署 Prow

準備環境

Prow 需要運作在 Kubernetes 環境,最好在有公網 IP 位址的 K8s 叢集中部署,這可以免去 Webhook 轉發的麻煩。我選用的環境是 Azure 提供的 AKS 叢集環境進行部署。

建立 GitHub bot 賬号

在部署 Prow 之前,請確定已建立 GitHub rebot 提供給到 Prow 使用。Prow 将忽略此賬号生成的大多數 GitHub event,是以對于區分 Prow 的自動化操作和任何使用者對于 Prow 的互動操作是非常重要的。即是你隻是針對于個人存儲庫做操作,你仍然需要這麼做。

  1. 確定 bot 賬号擁有以下權限:
  • 對于 GitHub repository 來說需要有寫入權限
  • 對于 GitHub organization 來說需要有所有者或者組織成員權限(請注意,如果沒有此權限,則可以處理 organization 中特定的 repository)
  1. 為 GitHub bot 賬戶建立 personal access token ,并添加以下權限:
  • 必須擁有

    public_repo

    repo:status

    權限
  • 如果你打算作用于私有倉庫,請添加

    repo

    權限
  • 如果你打算作用于 GitHub Organization,請添加

    admin:org_hook

    權限
    使用 Prow 實作 GitOps 與 ChatOps
  1. 添加成功後會生成令牌,将其寫入

    oauth-token

    檔案,根據此檔案生成

    oauth-token

    Kubernetes secret。
$ echo "f192f1277d47aff3eb60e99530b1cdc6639fda3c" > oauth-token
$ kubectl create secret generic oauth-token --from-file=oauth=./oauth-token
           
  1. 生成一個用于 GitHub Webhook 認證的 hmac 令牌。
$ openssl rand -hex 20 > hmac-token
$ kubectl create secret generic hmac-token --from-file=hmac=./hmac-token
           

部署系統

$ git clone https://github.com/jeffoverflow/prow-deploy.git
# 部署 Prow 叢集,預設部署到 default 命名空間
$ kubectl apply -f prow-deploy/
# 等待所有 pods 狀态都變成 Running
$ kubectl get pods
# 檢視 Prow 預設部署的 ingress
$ kubectl get ingress
# 檢視 deck 和 hook 的 Service
$ kubectl get svc
           

Deck 和 Hook 的 Service 情況如下:

NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)           AGE
deck         ClusterIP    10.0.136.52    <none>        80/TCP       1d
ghproxy      ClusterIP   10.0.112.192   <none>        80/TCP,9090/TCP     23h
hook         ClusterIP    10.0.92.16     <none>        8888/TCP,9090/TCP      1d
kubernetes   ClusterIP   10.0.0.1       <none>        443/TCP            1d
tide         ClusterIP    10.0.23.4      <none>        80/TCP       1d
           

目前我們需要将 Prow 叢集接入公網,通過配置 Public Standard Load Balancer (SLB) 将流量轉發到

Deck

Hook

,應該得到類似這樣的網址:

deck: http://prow.example.com/
hook: http://hook.example.com/
           

目前 K8s 叢集需要有 Ingress controller, 可以修改 Prow 預設的 Ingress 來配置通路位址,這裡我們選用了 Nginx Ingress controller:

$ kubectl get ingress
NAME   CLASS  HOSTS   ADDRESS   PORTS   AGE
ing    <none> *                 80, 443 21h

$ kubectl describe ingress ing
Name:             ing
Namespace:        default
Address:
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *
        /       deck:80 (10.244.0.32:8080,10.244.0.33:8080)
        /hook   hook:8888 (10.244.0.16:8888)

# 如果 ingress 需要綁定域名,比如 prow.example.com
$ vim prow-deploy/tls-ing_ingress.yaml

        backend:
          serviceName: hook
          servicePort: 8888
+    host: prow.example.com

$ kubectl apply -f prow-deploy/tls-ing_ingress.yaml
# 驗證 ingress 中域名設定
$ kubectl describe ingress ing
Name:             ing
Namespace:        default
Address:
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host              Path  Backends
  ----              ----  --------
  prow.example.com
                    /       deck:80 (10.244.0.32:8080,10.244.0.33:8080)
                    /hook   hook:8888 (10.244.0.16:8888)
           

那麼通過ingress通路時的網址應該類似這樣:

deck: http://prow.example.com/
hook: http://prow.example.com/hook/
           

通路 Prow 界面

打開 deck 的通路位址 (比如 http://prow.example.com/) 即可看到 Prow Status 界面

使用 Prow 實作 GitOps 與 ChatOps

添加 GitHub Webhook

我以

jeffoverflow/prow-test

倉庫為例,在項目 Setting ——> Webhooks 配置中點選

Add webhook

按鈕,按照如下的提示填寫相關資訊并儲存 webhook:

Payload URL: http://prow.example.com/hook/  # 填寫 hook 位址
Content type: application/json
Secret: `68f58d2dd9aa4ecd5fae8c0c0ac8cb8057e123456` # 通過 `cat hmac-token` 指令檢視 hmac-token 的值
Which events would you like to trigger this webhook?: Send me everything.
           
使用 Prow 實作 GitOps 與 ChatOps

添加 webhook 成功後,新增的 webhook 位址前面會顯示一個圖示用來顯示 webhook 接收端狀态是否有效,有效狀态顯示為綠色

如果狀态為

×

則說明 webhook 位址配置不正确,點選 webhook 位址的 edit 按鈕進入 webhook 管理界面,下方

Recent Deliveries

會顯示最近事件清單,點選事件 UUID 可以檢視目前事件的推送請求和傳回結果。在每個請求的

Reponse

标簽中能夠檢視 webhook 請求的傳回狀态碼和傳回内容,有助于定位問題。

這裡還可以通過點選

Redeliver

按鈕重複推送這個事件,測試目前 webhook 接收端是否正常接收事件。

使用 Prow 實作 GitOps 與 ChatOps

啟用 Plugins

Prow 提供了豐富的插件,我們可以點選 Prow Dashboard 左上角的菜單清單展示菜單選項,點選

Plugins

菜單欄。預設界面展示了所有 repos 能夠使用的 Plugin,點選對應 Plugin 的

DETAILS

能夠檢視插件的較長的描述、使用場景、互動指令等細節。

預設部署是沒有啟用任何 Plugin 的,需要去修改

plugins.yaml

部署檔案實作為指定代碼倉庫配置所需的插件。

建立一個名為

plugins.yaml

配置檔案并參照如下内容配置需要的插件。

$ vim ./plugins.yaml

plugins:
  jeffoverflow/prow-test:
  - trigger
  - size
  - welcome
  - owners-label
  - wip
  - cat # /meow
  - help # /help
  - label # /key value, /kind bug, /language zh
  - skip # /skip
  - lgtm # /lgtm
  - assign # /assign @user, /cc @user
  - lifecycle
  - hold
  - milestone
  - milestonestatus
  - project
  - stage
  - approve # /approve
           

官方建議通過

checkconfig

指令來驗證配置檔案是否正确。如果你沒有發生大的改動,可以跳過。

# 安裝 checkconfig
$ go get k8s.io/test-infra/prow/cmd/checkconfig
# 檢查配置是否有文法錯誤
$ checkconfig --plugin-config=./plugins.yaml
           

儲存

plugins.yaml

并部署到 Kubernetes:

$ kubectl create configmap plugins \
  --from-file=plugins.yaml=./plugins.yaml --dry-run -o yaml \
  | kubectl replace configmap plugins -f -
# 驗證配置是否儲存成功
$ kubectl get cm plugins -o yaml
           

插件配置部署成功後,要過一會才能在

Prow Plugin Catalog

界面看到配置生效

使用 Prow 實作 GitOps 與 ChatOps

測試 Prow

插件配置生效以後就可以測試 Prow 效果了,在 Github 倉庫中發起一個PR,稍後就會看到 size,approve, trigger 這幾個插件的效果。

注意:如果沒有建立機器人賬号,直接在個人賬号下添加的 Github Token,那麼 Prow 的響應都會顯示為個人賬号的動作。
使用 Prow 實作 GitOps 與 ChatOps

在 PR 中回複指令即可觸發相關插件,比如回複 /meow 會自動插入一張貓的圖檔。

使用 Prow 實作 GitOps 與 ChatOps

配置 Prow Job

Prow 預設部署了一個

echo-test

測試任務,每隔10分鐘顯示目前時間。

通過檢視 k8s 中

config

配置的

config.yaml

檔案即可看到這個Job的配置内容:

$ kubectl get configmap config -o yaml
           

我們把

config.yaml

檔案的内容提取出來,通過修改這個檔案并應用到 k8s 叢集來配置 Prow Job:

prowjob_namespace: default
pod_namespace: test-pods
periodics:
- interval: 10m
  agent: kubernetes
  name: echo-test
  spec:
    containers:
    - image: alpine
      command: ["/bin/date"]
           

如果暫時不需要 Prow Job,可以把

periodics

配置清空,比如:

$ vim ./config.yaml
prowjob_namespace: default
pod_namespace: test-pods
periodics:

           

官方建議通過 checkconfig 指令來驗證配置檔案是否正确,暫時可以跳過。

$ checkconfig --config-path=./config.yaml
           

儲存 config 配置到 K8s 讓 Prow Job 生效:

$ kubectl create configmap config \
  --from-file=config.yaml=./config.yaml --dry-run -o yaml \
  | kubectl replace configmap config -f -
           

Prow Job 應用後可以在

Prow Status

界面檢視到執行記錄:

使用 Prow 實作 GitOps 與 ChatOps

關于

config.yaml

的具體配置,參見 Kubernetes Prow Job Configs

啟動 Prow PR Status 功能

Prow PR Status 功能可以在 Prow 中檢視個人相關的 PR 處于什麼狀态。Prow Starter叢集預設沒有啟用 PR Status 功能,Prow 左側看不到相關菜單,必須啟用以後才會出現 PR Status 菜單。

由于 PR Status 是針對項目的所有貢獻者的,是以當貢獻者通路 Prow 的 PR Status 功能時,浏覽器會跳轉到 Github 使用 OAuth 認證獲得使用者的授權後,浏覽器會自動跳轉回 PR Status 界面顯示該貢獻者相關的 PR 清單。

注意:Prow 必須使用 https 通路才能正常使用 PR Status 功能。

配置 https 通路

# 申請證書過程省略,這裡我們使用 Nginx 證書來建立對應域名的 Kubernetes secret
$ kubectl create secret tls prow-secret --key 4428169_prow.example.com.key --cert 4428169_prow.example.com.pem

# 修改 ingress 配置
$ vim prow-deploy/tls-ing_ingress.yaml

        backend:
          serviceName: hook
          servicePort: 8888
    host: prow.example.com
+  tls:
+  - hosts:
+    - prow.example.com
+    secretName: prow-secret

$ kubectl apply -f prow-deploy/tls-ing_ingress.yaml
# 驗證 ingress 中域名證書設定
$ kubectl describe ingress ing

Name:             ing
Namespace:        default
Address:
Default backend:  default-http-backend:80 (<none>)
TLS:
  prow-secret terminates prow.example.com
Rules:
  Host              Path  Backends
  ----              ----  --------
  prow.example.com
                    /       deck:80 (10.244.0.32:8080,10.244.0.33:8080)
                    /hook   hook:8888 (10.244.0.16:8888)
           

在 Github 個人賬号 Settings → Developer settings → OAuth Apps 中點選

New OAuth App

,參照如下資訊添加一個 OAuth App。

Application name: Prow PR Status
Homepage URL: https://prow.example.com/
Authorization callback URL: https://prow.example.com/
           

OAuth App 儲存成功後,把生成的用戶端ID和密鑰填入 github-oauth-config.yaml 檔案.

client_id: e6403c9b594929axxxxx
client_secret: 933edc0ed4f91db12ebc26165f40cd49b02xxxxx
redirect_url: https://prow.example.com/github-login/redirect
final_redirect_url: https://prow.example.com/pr
           

再建立一個用于儲存 cookie 的

cookie.txt

檔案。

把以上 2 個配置檔案儲存到 K8s 叢集。

$ kubectl create secret generic github-oauth-config --from-file=secret=github-oauth-config.yaml
$ kubectl create secret generic cookie --from-file=secret=cookie.txt
           

修改目前

Deck

部署配置以便啟動

PR Status

功能。

$ vim prow-deploy/deck_deployment.yaml
           

args

中添加 3 個參數,務必注意縮進:

- --oauth-url=/github-login
       - --github-oauth-config-file=/etc/oauth/secret
       - --cookie-secret=/etc/cookie/secret
           

volumeMounts

下面添加 2 個挂載路徑,務必注意縮進:

- name: oauth-config
         mountPath: /etc/oauth
         readOnly: true
       - name: cookie-secret
         mountPath: /etc/cookie
         readOnly: true
           

volumes

下面添加 2 個卷引用,務必注意縮進:

- name: oauth-config
        secret:
          secretName: github-oauth-config
      - name: cookie-secret
        secret:
          secretName: cookie
           

在 K8s 叢集中更新 Deck 部署:

$ kubectl apply -f prow-deploy/deck_deployment.yaml
# 檢查 deck 的 pod 狀态,直到所有 pod 都是 Running
$ kubectl get po
           

等 Deck 的所有 pod 都重建以後,重新整理

Prow Status

界面,左側菜單會多出一個

PR Status

菜單。

點選這個菜單會執行 OAuth 認證過程,跳轉到 Github 中根據提示授權 Prow 讀取 Github 的資訊,授權成功後會回到

PR Status

界面顯示目前使用者開啟的 PR 清單及其狀态。

使用 Pod utilities

Pod utilities 是需要在 plank 中配置用來增強 Prow Jobs 在整個 CI 基礎設施內建的便利性,使得測試代碼能夠以正确的版本進行內建,確定測試指令能夠在适當的環境下運,并将測試的輸出(以狀态、日志和歸檔産物的形式)正确上傳至雲端。

這些 Pod utilities 以 InitContainers 和 sidecar Container 的形式添加到使用者提供的 PodSpec 中,通過覆寫使用者提供的測試容器的 container entrypoint 以整合到目前測試運作中。目前已存在以下 utilities:

  • clonerefs

    用于克隆需要測試的源代碼。
  • initupload

    用于在雲環境下記錄開始測試時,測試狀态和報告克隆操作的狀态。
  • entrypoint

    用于注入到被測試的 container 中,以捕獲日志和退出狀态。
  • sidecar

    與測試的 container 一起運作,待測試完成後,将狀态、日志和歸檔産物上傳至雲端。
注意:目前隻支援 GCS 作為 Prow job 的日志存儲

關于 Pod utilities 的更多資訊,參見 Pod Utilities

詳細配置 Plank 的文檔見 Plank README

參考資料

[1] Prow 快速入門向導: https://www.servicemesher.com/blog/prow-quick-start-guide/

[2] kubernetes/test-infra/prow: https://github.com/kubernetes/test-infra/tree/master/prow

繼續閱讀