关于云计算:在-Kubernetes-上执行-GitHub-Actions-流水线作业

4次阅读

共计 8539 个字符,预计需要花费 22 分钟才能阅读完成。

GitHub Actions 是一个功能强大、“收费”的 CI(继续集成)工具。

与之前介绍的 Tekton 相似,GitHub Actions 的外围也是 Pipeline as Code 也就是所谓的流水线即代码。二者不同的是,GitHub Actions 自身就是一个 CI 平台,用户能够应用代码来定义流水线并在平台上运行;而 Tekton 自身是一个用于构建 CI/CD 平台的开源框架。

Pipeline as Code,既然与代码扯上了关系。那流水线的定义就可繁可简了,齐全看需要。小到一个 GitHub Pages,大到流程简单的我的项目都能够应用 GitHub Actions 来构建。

本篇文章不会介绍如何应用 GitHub Actions 的,如果还未用过的同学能够浏览下官网的文档。明天次要来分享下如何在 Kubernetes 上的自托管资源来执行流水线作业。

0x01 背景

在介绍 GitHub Actions 的时候,收费带上了引号,为何?其作为一个 CI 工具,容许用户定义流水线并在平台上运行,须要耗费计算、存储、网络等资源,这些运行流水线的机器称为 Runner。GitHub 为不同类(等)型(级)的用户每月提供了不同的收费额度(额度用完后,每分钟 0.008 美元。),见下图。不同类型的主机,分钟数的耗费倍数也不同:Linux 为 1、macOS 为 10、Windows 为 2。

拿收费用户来看,每月 2000 分钟看似也不少。比方笔者集体就是拿来构建下博客动态页面,以及几个简略的利用,每个月也用不了太多。但对于企业或者组织来说,尤其是当流水线的触发频繁(每次代码提交触发)、或者我的项目的单元测试耗时很长(bug 引起的或者我的项目自身的复杂度所致),千里之行; 始于足下也会变成一笔不小的开销。

那有没有方法应用本人的资源来运行流水线呢?有。GitHub Actions Runner 分为两种:Github 托管的 Runner 和自托管的 Runner。咱们能够将本人的资源作为自托管的 Runner 来运行流水线,而且还能够借助 Kubernetes 的能力来治理这些 Runner。

同时,自托管 Runner 也适宜那些对 CI 有更高要求的用户,比方更高性能、更多类型的计算资源等等。

0x02 筹备工作

Kubernetes 集群

咱们应用 k3s 疾速创立一个单节点的集群。

export INSTALL_K3S_VERSION=v1.22.11+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

应用 Github Actions 构建的我的项目

这里应用之前做的一个 graalvm+maven 的根底镜像仓库来进行测试:https://github.com/addozhang/…。

GitHub Access Token

参考文档,创立 Access Token。

留神:鉴于本演示的须要,调配齐全的 repo 拜访权限。

0x03 创立 Runner

GitHub Actions Runner 的程序源码是开源的,GitHub 托管的 Runner 也是应用该程序运行的。

Runner 能够是某个仓库应用,也能够由组织下的所有仓库共享。鉴于这里演示用的我的项目,咱们为下面提到的我的项目仓库创立 Runner。

Runner 在启动时,会通过 GitHub API 将本人注册到 GitHub Actions;而后一直发送申请到 GitHub 查看是否调配了流水线作业,如有则会执行流水线作业;Runner 进行运行时,须要进行登记的操作。这里的一系列操作,都会用到下面创立的 Access Token。

要在 Kubernetes 上运行,咱们要将 Runner 利用以及下面的逻辑打成镜像,并以 Deployment 的形式部署到 Kubernetes 中。而后通过 workflow 作业 webhook 查看排队的作业数来决定是否减少或者缩小 Deployment 的正本数。

听起来就有些简单,但的确是实现的根本形式。这里咱们应用一个 Kubernetes controller actions-runner-controller/actions-runner-controller 来实现所有的流程。

actions-runner-controller 提供了三种 CRD:

  • Runner:能够了解为 Pod,该 Runner 只能执行一次作业。
  • RunnerDeployments:能够了解为 Deployment,能够设置要创立的 Runner 数量。Runner 在执行完作业后会销毁,而后 Controller 会创立新的 Runner 期待作业调度。
  • RunnerSets:能够了解为 StatefulSet,也是基于 StatefulSet 来创立 Pod(即 Runner),提供 StatefulSet 的个性。

更多用法,能够参考 actions-runner-controller 官网文档。

部署 Cert Manager

actions-runner-controller 的运行,须要应用 cert-mananger。通过上面的命令来部署:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.yaml

查看 pod 是否胜利启动:

kubectl get pods -n cert-manager

NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-66b646d76-7b9fz               1/1     Running   0          18s
cert-manager-cainjector-59dc9659c7-rzlrl   1/1     Running   0          18s
cert-manager-webhook-7d8f555998-6zkqp      1/1     Running   0          18s

启动 Controller

接下来就是启动 controller,本文公布时的最新版本是 v0.25.0。应用上面的命令部署 controller:

kubectl create -f https://github.com/actions-runner-controller/actions-runner-controller/releases/download/v0.25.0/actions-runner-controller.yaml

注:这里应用的 create 而非 apply。应用 apply 会报相似上面的谬误:
Error from server (Invalid): error when creating “https://github.com/actions-runner-controller/actions-runner-controller/releases/download/v0.25.0/actions-runner-controller.yaml”: CustomResourceDefinition.apiextensions.k8s.io “runnerdeployments.actions.summerwind.dev” is invalid: metadata.annotations: Too long: must have at most 262144 bytes

此时,pod 无奈启动,还须要为其创立 Secret 来提供 GitHub Access Token:

export GITHUB_TOKEN=<TOKEN_HERE>
kubectl create secret generic controller-manager \
    -n actions-runner-system \
    --from-literal=github_token=${GITHUB_TOKEN}

当初能够看到 pod 能够胜利运行:

kubectl get pods -n actions-runner-system
NAME                                  READY   STATUS    RESTARTS   AGE
controller-manager-58c598f64d-27xn6   2/2     Running   0          40s

创立 Runner

执行上面的命令,创立一个名为 building-runnerRunner CR。

kubectl apply -f - <<EOF
apiVersion: actions.summerwind.dev/v1alpha1
kind: Runner
metadata:
  name: building-runner
spec:
  repository: addozhang/docker-graalvm-maven
  env: []
EOF

此时,会发现 controller 创立了一个同名的 pod:

kubectl get pods -l actions-runner="" -n actions-runner-system
NAME              READY   STATUS    RESTARTS   AGE
building-runner   2/2     Running   0          16s

在仓库的 Settings/Actions/Runners 中能够看到同名的 Runner,处于 idle 状态。

如果此时启动流水线作业,会发现作业并没有调度该 Runner 上。这是因为还没有将作业 Runner 的 label 设置为该 runner 的 label:self-hostedLinuxX64

批改流水线定义,将 runs-on 指定为 [self-hosted, linux, X64]

而后能够查看作业胜利调度到该 runner 上运行:

当初再去看 pod 的状态,其中的 runner 容器是 Completed

kubectl get pods -l actions-runner="" -n actions-runner-system
NAME              READY   STATUS     RESTARTS   AGE
building-runner   1/2     Running   0          3m53s

此时,再去触发流水线执行,作业会始终期待可用的 runner 来执行:

runner 无奈重复使用,怎么办?

0x04 可重用 Runner

RunnerDeployment 要派上用场了,后面提到能够将 RunnerDeployments 了解为 Deployment,能够设置要创立的 Runner 数量。Runner 在执行完作业后会销毁,而后 Controller 会创立新的 Runner 期待作业调度。

kubectl apply -f - <<EOF
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: building-runner
spec:
  template:
    spec:
      repository: addozhang/docker-graalvm-maven
      env: []
EOF

应用下面的命令创立 RunnerDeployment 之后,controller 会创立新的 pod(runner)并失去作业的调度;实现作业的执行后,pod building-runner-9k4cj-ml46v 被销毁,新的 pod building-runner-9k4cj-f8twz 被创立并期待作业的调度:

kubectl get events --sort-by='metadata.creationTimestamp'
...
3m7s        Normal    PodCreated                 runner/building-runner-9k4cj-ml46v         Created pod 'building-runner-9k4cj-ml46v'
3m6s        Normal    Scheduled                  pod/building-runner-9k4cj-ml46v            Successfully assigned actions-runner-system/building-runner-9k4cj-ml46v to ubuntu-dev1
3m7s        Normal    RegistrationTokenUpdated   runner/building-runner-9k4cj-ml46v         Successfully update registration token
3m6s        Normal    Pulling                    pod/building-runner-9k4cj-ml46v            Pulling image "summerwind/actions-runner:latest"
3m3s        Normal    Started                    pod/building-runner-9k4cj-ml46v            Started container docker
3m3s        Normal    Pulled                     pod/building-runner-9k4cj-ml46v            Successfully pulled image "summerwind/actions-runner:latest" in 3.06531539s
3m3s        Normal    Created                    pod/building-runner-9k4cj-ml46v            Created container runner
3m3s        Normal    Started                    pod/building-runner-9k4cj-ml46v            Started container runner
3m3s        Normal    Created                    pod/building-runner-9k4cj-ml46v            Created container docker
3m3s        Normal    Pulled                     pod/building-runner-9k4cj-ml46v            Container image "docker:dind" already present on machine
2m6s        Normal    RegistrationTokenUpdated   runner/building-runner-9k4cj-f8twz         Successfully update registration token
2m6s        Normal    PodCreated                 runner/building-runner-9k4cj-f8twz         Created pod 'building-runner-9k4cj-f8twz'
2m5s        Normal    Scheduled                  pod/building-runner-9k4cj-f8twz            Successfully assigned actions-runner-system/building-runner-9k4cj-f8twz to ubuntu-dev1
2m5s        Normal    Pulling                    pod/building-runner-9k4cj-f8twz            Pulling image "summerwind/actions-runner:latest"
2m5s        Normal    Killing                    pod/building-runner-9k4cj-ml46v            Stopping container docker
2m3s        Normal    Pulled                     pod/building-runner-9k4cj-f8twz            Successfully pulled image "summerwind/actions-runner:latest" in 2.50468863s
2m3s        Normal    Created                    pod/building-runner-9k4cj-f8twz            Created container runner
2m3s        Normal    Started                    pod/building-runner-9k4cj-f8twz            Started container runner
2m3s        Normal    Pulled                     pod/building-runner-9k4cj-f8twz            Container image "docker:dind" already present on machine
2m3s        Normal    Created                    pod/building-runner-9k4cj-f8twz            Created container docker
2m3s        Normal    Started                    pod/building-runner-9k4cj-f8twz            Started container docker

后面创立 RunnerDeployment 的时候没有指定正本数,也就是 runner 的数量,controller 只创立了一个 Runner。假如这个 RunnerDeployment 是某个组织下多个我的项目共用的,多个我的项目同时执行作业会怎么?

这里我从新运行 3 个曾经实现的作业,模仿作业的并发:

curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/addozhang/docker-graalvm-maven/actions/runs/2643982502/rerun
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/addozhang/docker-graalvm-maven/actions/runs/2643982274/rerun
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/addozhang/docker-graalvm-maven/actions/runs/2643982274/rerun

或者 push 几个空的提交到仓库(不举荐,会有 commit 历史。测试项目随便):

git commit --allow-empty -m "trigger action"
git push

去 Actions 列表会发现,只有一个作业在运行,其余两个都是 queued 的期待状态。后面的作业执行实现后,后一个作业才会被执行。

不反对并发?既然是相似 Deployment 的形式运行 runner,那是否有相似 HPA(程度 pod 主动扩缩容)的性能?

0x05 主动伸缩

actions-runner-controller 在 3 个 Runner 的 CRD 之外,还提供了相似 HPA 的 CRD HorizontalRunnerAutoscaler,简称 HRA。

HRA 能够依据指标 PercentageRunnersBusy 或者 TotalNumberOfQueuedAndInProgressWorkflowRuns 来对 runner 进行扩缩容,或者基于 GitHub Events(webhook)来进行扩缩容。这两种都各有优缺点,前者指标是通过 GitHub API 轮训期待的作业数,实现简略,但时效性差;后者基于事件触发时效性更佳,然而实现简单,须要对外裸露拜访端点接管 GitHub Event。

这里为了演示,应用基于指标的形式进行扩缩容。

kubectl apply -f - <<EOF
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
  name: building-runner-autoscaler
spec:
  scaleDownDelaySecondsAfterScaleOut: 30
  scaleTargetRef:
    name: building-runner
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: PercentageRunnersBusy
    scaleUpThreshold: '0.75'
    scaleDownThreshold: '0.25'
    scaleUpFactor: '2'
    scaleDownFactor: '0.5'
EOF

还是通过 API 触发作业模仿并发执行,因为轮训距离比拟长(默认 1 分钟),主动扩容须要期待一段时间:

kubectl get pods -l actions-runner="" -n actions-runner-system
NAME                          READY   STATUS    RESTARTS   AGE
building-runner-k6jlc-dk7gc   2/2     Running   0          1m34s
building-runner-k6jlc-pbwnz   2/2     Running   0          54s
building-runner-k6jlc-nlptp   2/2     Running   0          54s

0x06 总结

GitHub Actions 是个很弱小的 CI 工具,联合自托管的 Runner 能够在付出较低成本的根底上有更好的体验。本文也只是从满足需要的登程,对“Runner on Kubernetes”进行了摸索。对一个工具从会用到用好,还有很长的路要走。但满足以后需要,已是足矣。

有趣味的同学能够更进一步思考:

  • 如何限度 Runner 运行时的资源占用
  • 长久化如何实现,比方 Maven 构建时的本地库,Nodejs 的 node_mudules 如何防止反复下载
  • 主动扩缩容是否更加高效

    文章对立公布在公众号 云原生指北

正文完
 0