天天看点

使用 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

继续阅读