亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

GitHub Actions:在Kubernetes中運行自托管Runner的那些事兒

我们使用 GitHub Actions 的 Runners(运行器)进行部署,后来觉得把它们放到我们自己的 Kubernetes 集群上会更好,因为:

  • 自托管的 GitHub 运行器更省钱——实际上,你只需支付运行作业的服务器费用
  • 我们需要在 AWS RDS 上运行 SQL 迁移,该 RDS 位于 AWS VPC 的私有子网中
  • 性能不受限制——我们可以使用任何类型的 AWS EC2 以及各种磁盘类型,不受 CPU/内存和 IOPS 的限制

关于 — 关于自托管运行器的介绍

GitHub 有一个单独的控制器用于这个 — Actions Runner Controller (ARC),我们将会使用它 (ARC)。

我们使用AWS Elastic Kubernetes Service v1.30,Karpenter v1.0进行EC2自动扩展,并用AWS Elastic Container Registry存储Docker镜像。

自托管运行器有一些Usage limits,但我们不太可能碰到这些问题。

今天我们要做什么呢:

  • 使用 Helm 安装 Runner Controller Actions,并设置一个带有 Runners 的 Scale Set,以便在测试仓库中查看整体操作
  • 将创建一个带有 taints 的 Karpenter NodePool,以确保 Runners 在专用的 Kubernetes 工作节点上运行
  • 尝试实际构建和部署我们的后端 API,看看会发生什么错误
  • 将创建我们自己的 Docker 镜像以用于 Runners
  • 尝试 Docker in Docker 模式,看看效果如何
  • 创建一个具有高 IOPS 的独立 Kubernetes 存储类,并观察它如何影响构建和部署的速度

首先,我们先手动做所有事情,然后尝试真正运行构建并部署 GitHub Actions 流程。

GitHub 授权

文档 — 使用个人访问令牌的传统身份验证方法 (GitHub API 身份验证)

这里有两种选择——一是通过 GitHub 应用来更为正式地设置生产环境,二是使用个人访问令牌。

GitHub 应用程序似乎是个更好的选择,但我们是个小公司,使用个人令牌会更方便,所以我们现在先这么干,如果以后真的有必要的话,到时候我们再正规地做。

进入你的个人资料页面,点击设置,然后找到开发者设置,再点击个人访问令牌,点击生成新令牌。

目前暂时,我们只为一个仓库使用自托管运行程序,仅将权限设置为 repo

最好设置一个到期日期,但这只是一个 PoC(之后会像往常一样投入正式使用),所以暂时就这样——那就让它一直有效吧。

为 runner 创建一个 Kubernetes 命名空间:

    $ kk create ns ops-github-runners-ns  
    命名空间/ops-github-runners-ns 创建完毕

创建一个带有 token 的 Kubernetes Secret:

    $ kk -n ops-github-runners-ns create secret generic gh-runners-token --from-literal=github_token='ghp_FMT***5av'  
    secret/gh-runners-token 创建完成

看看这个:

运行命令来获取名为ops-github-runners-ns的命名空间中的secret信息,输出格式为yaml。

$ kk -n ops-github-runners-ns get secret -o yaml
items:
- data:
    github_token: Z2h***hdg==
  kind: Secret
...
《通过 Helm 来运行 Actions Runner 控制器》

动作运行控制器由两部分组成。

  • [gha-runner-scale-set-controller](https://github.com/actions/actions-runner-controller/tree/master/charts/gha-runner-scale-set-controller):控制器本身,它的 Helm 图表会创建所需的 Kubernetes 自定义资源定义(CRDs),并运行控制器 Pod,负责管理。
  • [gha-runner-scale-set](https://github.com/actions/actions-runner-controller/tree/master/charts/gha-runner-scale-set):负责运行带有 GitHub Actions 运行器的 Kubernetes Pod,

还有一个老版本——[actions-runner-controller](https://github.com/actions/actions-runner-controller/tree/master/charts/actions-runner-controller),但不会使用它。

尽管 Scale Sets Controller 在文档中也被称为 Actions Runner Controller,并且还有一个旧版本的 Actions Runner Controller……这种情况有点令人困惑,因此请记得,谷歌上找到的一些例子或文档可能是关于旧版本的。

文档 — 快速入门:Actions Runner 控制器,或完整版本 — 使用 Actions Runner 控制器部署运行器规模集

安装缩放集控制工具(如何进行)

为控制器单独创建一个 Kubernetes 命名空间:

    $ kk create ns ops-github-controller-ns  
    namespace/ops-github-controller-ns 创建成功

安装图表文件——它的配置文件很简单,非常简单,无需更新。它的配置文件无需更新。

$ helm -n ops-github-controller-ns upgrade --install github-runners-controller > oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

检查Pods:

    $ kk -n ops-github-controller-ns get pod  
    NAME                                                           就绪状态   状态     重启次数   存活时间  
    github-runners-controller-gha-rs-controller-5d6c6b587d-fv8bz   1/1     运行   0          2m26s

查看新CRDs:

$ kk get crd | grep github  # 获取与github相关的自定义资源定义 (Fetch the custom resource definitions related to github)
autoscalinglisteners.actions.github.com                     2024-09-17T10:41:28Z  # 标准ISO 8601格式时间戳 (Standard ISO 8601 formatted timestamp)
autoscalingrunnersets.actions.github.com                    2024-09-17T10:41:29Z  
ephemeralrunners.actions.github.com                         2024-09-17T10:41:29Z  
ephemeralrunnersets.actions.github.com                      2024-09-17T10:41:30Z
安装 GitHub 托管机

注:如果需要解释“Scale Set for GitHub Runners”,可以将其翻译为“GitHub 托管机群组”或“GitHub 托管机集合”,以保持与原文概念的一致性。如果上下文中需要进一步说明,可以在正文或注释部分详细说明。

每个 Scale Set(AutoscalingRunnerSet 资源)负责特定的运行器,这些运行器会在工作流文件的 runs-on 中被使用。

设置两个环境变量,之后我们会在这自己的值文件中传递这些变量。

  • INSTALLATION_NAME: 运行器的名字(可在 values 中通过设置 runnerScaleSetName 参数来指定)
  • GITHUB_CONFIG_URL: 格式为 https://github.com/<ORG_NAME>/<REPO_NAME> 的 GitHub 组织或仓库
    $ INSTALLATION_NAME="test-runners"  # 安装名称 "test-runners"
    $ GITHUB_CONFIG_URL="https://github.com/***/atlas-test"  # GitHub 配置 URL "https://github.com/***/atlas-test"

安装图表资源并传递 githubConfigUrlgithubConfigSecret - 在此我们已经创建了一个 Kubernetes Secret,因此使用该Secret。

$ helm --namespace ops-github-runners-ns 升级 --安装 test-runners \  
> --set githubConfigUrl="${GITHUB_CONFIG_URL}" \  
> --set githubConfigSecret=gh-runners-token \  
> oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

再次检查控制器命名空间中的Pod,应该发现新增了一个名为 test-runners-*-listener 的Pod——它将负责为 test-runners 池启动包含Runner的Pod。

    $ kk -n ops-github-controller-ns 获取 pod  
    NAME                                                           READY   STATUS    RESTARTS   存活时间  
    github-runners-controller-gha-rs-controller-5d6c6b587d-fv8bz   1/1     Running   0          8m38s  
    test-runners-694c8c97-listener                                 1/1     Running   0          40秒

它是从 AutoscalingListeners 资源创建的。

    $ kk -n ops-github-controller-ns get autoscalinglisteners   
    名称(NAME)                             GitHub 配置的网址                            自动伸缩运行器集命名空间   自动伸缩运行器集名称  
    test-runners-694c8c97-listener   https://github.com/***/atlas-test   ops-github-runners-ns            测试运行器

检查运行者命名空间中的Pods,它现在还是空的。

    $ kk -n ops-github-runners-ns get pod  
    没有找到任何资源在 ops-github-runners-ns 命名空间中。

实际上,这已经足够让你开始尝试运行作业。

使用 GitHub Actions 工作流进行测试人员的测试

让我们试着运行一个最小化的构建过程,确认配置没问题。

在测试用的代码仓库中,新建一个 .github/workflows/test-gh-runners.yml 文件。

runs-on 中指定我们的 runner 池名为 test-runners

    name: "测试GitHub运行器实例"  
    concurrency:  
      group: github-test  
      cancel-in-progress: 不取消正在进行的任务  
    on:  
      workflow_dispatch:  

    permissions:  
      # 允许步骤读取仓库内容  
      contents: read  
    jobs:  
      aws-test:  
        name: 测试EKS运行器  
        runs-on: test-runners  
        steps:  
          - name: 测试运行器  
            run: echo $HOSTNAME # 输出主机名

推送更改到仓库,运行构建,等上一分钟,你就能看到Runner的名字:

在 Kubernetes 中查看 Pods:

    $ kk -n ops-github-runners-ns get pod  
    名称                              就绪   状态    重启   年龄  
    test-runners-p7j9h-runner-xhb94   1/1     运行中   0          6s

同一运行器及其相应的运行器规模设置将显示在设置 > 操作 > 运行器部分。

工作完成了

行了,搞定。接下来做什么?

  • 我们需要为 GitHub Runners 创建一个专门用于 GitHub Runners 的 Karpenter NodePool
  • 需要为 Runner Pods 配置 requests
  • 看看如何在 Runners 中构建 Docker 镜像
创建 Karpenter 节点池

让我们创建一个带有 taints 标签的专用 Karpenter 节点池,以便只有带有 GitHub 运行器的 Pod 可以在这些 EC2 上运行(参考 Kubernetes:Pods 和工作节点 – 控制 Pod 在节点上的调度):

apiVersion: karpenter.sh/v1  
kind: NodePool  
metadata:  
  name: github1abgt  
spec:  
  重量: 20  
  模板:  
    metadata:  
      labels:  
        created-by: karpenter  
        component: devops  
    spec:  
      污点:  
        - key: GitHubOnly  
          operator: Exists  
          effect: NoSchedule  
      节点类引用:  
        group: karpenter.k8s.aws  
        kind: EC2NodeClass  
        name: defaultv1a  
      要求:  
        - key: karpenter.k8s.aws/instance-family  
          operator: In  
          values: ["c5"]  
        - key: karpenter.k8s.aws/instance-size  
          operator: In  
          values: ["large", "xlarge"]  
        - key: topology.kubernetes.io/zone  
          operator: In  
          values: ["us-east-1a"]  
        - key: karpenter.sh/capacity-type  
          operator: In  
          values: ["spot", "on-demand"]  
  # total cluster limits  
  限制:  
    cpu: 1000  
    memory: 1000Gi  
  中断:  
    整合政策: WhenEmptyOrUnderutilized  
    整合等待时间: 600s  
    预算:  
      - nodes: "1"

我没有在这里调整实例类型,只是复制了我们为后端API使用的NodePool配置,然后我们再看看runners在工作中使用了多少资源,再根据实际情况调整EC2实例类型。

调整 disruption 参数也很合理,例如,如果所有开发人员都在同一时区工作,您可以在夜间执行 WhenEmptyOrUnderutilized,并且只在白天通过 WhenEmpty 删除节点。此外,您可以设置更高的 consolidateAfter 值,这样新的 GitHub Actions 作业就不会等待太久来创建 EC2 实例。参见Karpenter: 关于 Disruption Budgets 的介绍

helm和scale集部署自动化

这里有几个选择:

  • 可以使用 Terraform 的 [resource helm_release](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release)
  • 可以创建自己的 helm chart,并通过Helm 依赖项来安装 GitHub Runner charts

或者我们可以更简单地做——创建一个包含配置和值的代码仓库,添加一个 Makefile,暂时手动部署。

我可能会把这两样东西混起来。

  • 控制器本身将从一个部署整个Kubernetes集群的Terraform脚本中安装,因为我们还有其他控制器如ExternalDNS、ALB Ingress Controller等已安装在那里
  • 为了为每个仓库创建带有Runner池的Scale Sets,我将在一个单独的仓库中创建一个专门的Helm图表,并在其中添加每个Runner池的配置文件
  • 但在还是PoC阶段时,将通过执行helm install -f values.yamlMakefile来安装Scale Sets

现在我们创建自己的 values.yaml 文件,将 runnerScaleSetNamerequeststolerances 设置为 NodePool 的 tains

    githubConfigUrl: "https://github.com/***/atlas-test"  
    githubConfigSecret: "github配置密钥"  

    runnerScaleSetName: "runner 规模设置名称"  
    template:  
      spec:  
        容器:  
          - 名称: runner  
            镜像: ghcr.io/actions/actions-runner:latest  
            命令: ["/home/runner/run.sh"]      
            资源:  
              请求:  
                cpu: 1  
                memory: 1Gi  
        容忍:  
          - key: "GitHubOnly"  
            效应: "NoSchedule"    
            操作符: "Exists"

添加一个简单的 Makefile 文件:

deploy-helm-runners-test:  
  helm -n ops-github-runners-ns upgrade --install test-eks-runners -f test-github-runners-values.yaml oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set (从 GHCR 获取的 Helm 图表)

部署图表如下:

    $ make deploy-helm-runners-test

运行部署Helm Runner的测试命令。

看看这个跑者池的设置有没有变?

    $ kk -n ops-github-runners-ns describe autoscalingrunnerset test-runners  
    名称:         test-runners  
    命名空间:    ops-github-runners-ns  
    ...  
    API 版本:  v1alpha1  
    类型:         AutoscalingRunnerSet  
    ...  
    配置:  
      GitHub 配置密钥:   gh-runners-token  
      GitHub 配置 URL:      https://github.com/***/atlas-test  
      RunnerSet 名称:  test-runners  
      模板规格:  
        配置:  
          容器:  
            命令行:  
              /home/runner/run.sh  
            镜像:  ghcr.io/actions/actions-runner:latest  
            名称:   runner  
            资源需求:  
              请求:  
                CPU:             1  
                内存:          1Gi  
          重启策略:        Never  
          服务账户:  test-runners-gha-rs-no-permission  
          容忍列表:  
            效果:    NoSchedule  
            键:       GitHubOnly  
            操作符:  Exists

运行一个测试构建,并检查节点声明,以确认新 Karpenter NodePool 是否会被采用。

    $ kk get nodeclaim | grep git  
    github1abgt-dq8v5    c5.large    spot       us-east-1a                                 未知状态   0s

好的,实例已经创建好了,我们现在有了一个新的Runner:

$ kk -n ops-github-runners-ns get pod  
名称:                              就绪:              状态:              重启次数:   年龄:  
test-runners-6s8nd-runner-2s47n   0/1     Container 正在创建   0          45s

这里一切正常。

# 构建一个真正的后端 API 工作

首先,让我们试一试用真实的代码和 GitHub Actions 工作流来运行构建并部署我们后端的流程。

为新的跑者群体创建一个新的值文件:
githubConfigUrl: "https://github.com/***/kraken"  
githubConfigSecret: gh-runners-token  

runnerScaleSetName: "kraken-eks-runners"  
...

部署新的规模设置:

$ helm -n ops-github-runners-ns: 升级并安装 kraken-eks-runners \

-f kraken-github-runners-values.yaml \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

编辑项目的流程 — 将 runs-on: ubuntu-latest 修改为 runs-on: kraken-eks-runners

    ...  
    jobs:  
      eks_build_deploy:  
        name: "构建或部署后端"  
        runs-on: kraken-eks-runners  
    ...

一旦开始构建,一个新的 Pod 就会被创建出来:

    $ kk -n ops-github-runners-ns get pod  
    NAME                                    READY   STATUS     RESTARTS   AGE  
    kraken-eks-runners-pg29x-runner-xwjxx   1/1     运行中      0          11s
    (Pod正在运行,未重启,已经运行了11秒)

构建正在跑:

但随后马上就因为找不到一些必需的工具,如 makegit 而出错。

GitHub Runner 镜像及“git: 命令未找到”

让我们手动验证一下,执行 ghcr.io/actions/actions-runner:latest Docker 图像以进行本地运行。

    $ docker run -ti ghcr.io/actions/actions-runner:latest bash  
    可以使用 "sudo <command>" 以管理员(用户 "root")身份运行命令。  
    有关详细信息,请参阅 "man sudo_root"。  

    runner@c8aa7e25c76c:~ $ make  
    bash: make: 命令未找到 (未安装该工具)  
    runner@c8aa7e25c76c:~ $ git  
    bash: git: 命令未找到 (未安装该工具)

没有 make 这是可以理解的,但为什么 git 在 GitHub 运行器上默认没有安装呢?

在這個GitHub Issue中,人们也对这个做法感到意外。

不过好吧……我们有了这些。我们可以基于 ghcr.io/actions/actions-runner 创建自己的镜像,并安装我们需要的所有工具来让我们开心。

请参阅在 ARC runner 图像中安装的软件。还有其他非 GitHub 的 Runners 镜像版,但本人未尝试过这些镜像。

我们使用的 GitHub Runners 镜像环境是 Ubuntu 22.04,因此我们可以使用 apt 安装所有必需的软件包。

创建一个 Dockerfile 文件,我已经添加了 AWS CLI 和几个 Python 相关的包。

    FROM ghcr.io/actions/actions-runner:latest  

    RUN sudo curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash  
    RUN sudo apt update && apt -y install git make python3-pip awscli python3-venv # 更新包列表并安装必要的工具

另外,还可能看到这种警告:

警告:脚本 gunicorn 已安装在 ‘/home/runner/.local/bin’,该目录未包含在 PATH 中。
考虑将该目录添加到 PATH,或者如果你希望忽略此警告,可以使用 — no-warn-script-location 选项.

因此,我也在Dockerfile中添加了PATH变量定义。

    FROM ghcr.io/actions/actions-runner:latest  

    ENV PATH="$PATH:/home/runner/.local/bin"  
    RUN sudo curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash  
    RUN sudo apt update && \  
      sudo apt -y install git make python3-pip awscli python3-venv

在 AWS ECR 上创建一个镜像仓库:

制作镜像:

在终端里输入以下命令(构建并标记镜像):
$ docker build -t 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken -f Dockerfile.kraken .

登录到ECR:

$ aws --profile work ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 492***148.dkr.ecr.us-east-1.amazonaws.com
# 该命令使用 AWS 工作配置文件获取 ECR 的登录密码,并使用 Docker 使用该密码登录.

推送图片:

运行这条命令来推送镜像:$ docker push 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken

更改你的image设置在“值”中。

    ...  
    runnerScaleSetName: "kraken-eks-runners"  

    template:  
      spec:  
        containers:  
          - name: runner  
            image: 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken:latest  
            command: ["/home/runner/run.sh"]    
    ...

部署它,运行构建,我们就遇到了新状况。

无法连接到 Docker 守护程序的错误,以及 Scale Set 的 containerMode 错误: "Docker in Docker"

现在Docker出了点问题:

Docker:无法连接到 Docker 守护进程 unix:///var/run/docker.sock。Docker 守护进程是不是没有运行?

在构建我们后端的过程中,会启动一个 Docker 容器来生成 OpenAPI 文档。

因此,在咱们的情况下,咱们得用Docker in Docker(尽管我真是不太喜欢这种方法,Docker in Docker即在一个Docker容器中运行另一个Docker容器,尽管我非常不喜欢这种方法)。

GitHub 文档中的 — 使用 Docker-in-Docker

在缩放集(Scale Sets)中,有一个专门的参数用于这种情况下的配置:[containerMode.type=dind](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set/values.yaml#L112C11-L112C29)

把这个加到数值里

...
runnerScaleSetName: "kraken-eks-runners"  # 运行器缩放集名称

containerMode:  
  type: "dind"  # 容器模式
template:  
  spec:  # 规范
...

部署 Helm 图表后,我们现在有两个容器在 Runner Pod 中 — 一个是 runner 本身,另一个是 dind

    ==> 新容器实例 [kraken-eks-runners-trb9h-runner-klfxk:dind]  
    ==> 新容器实例 [kraken-eks-runners-trb9h-runner-klfxk:runner]

再试一次构建,然后……我们又遇到了一个新的错误。

大家好,DinD 和 Docker 数据卷。

错误看起来是这样的:

错误信息:ENOENT: 找不到文件或目录 '/app/openapi.yml'

Docker 中的 Docker,和 Docker 卷

问题出现是因为后端 API 的代码在 /tmp 目录中创建了一个新目录,并在此目录中生成了一个 openapi.yml 文件。接着,从这个文件生成了一个包含文档的 HTML 文件。

def 生成OpenAPI_HTML定义(yml_path: Path, html_path: Path):  
    print("运行Docker生成HTML")  

    app_volume_path = Path(tempfile.mkdtemp())  
    (app_volume_path / "openapi.yml").write_text(yml_path.read_text())  

if subprocess.call(  
        [  
            "docker",  
            "run",  
            "-v",  
            f"{app_volume_path}:/app",  
            "--platform",  
            "linux/amd64",  
            "-e",  
            "yaml_path=/app/openapi.yml",  
            "-e",  
            "html_path=/app/openapi.html",  
            "492***148.dkr.ecr.us-east-1.amazonaws.com/openapi-generator:latest",  
        ]  
    ):  
...

这里 Path(tempfile.mkdtemp()) 会在 /tmp 目录中创建一个新目录,但这个操作在 kraken-eks-runners-trb9h-runner-klfxk:runner 容器中执行,而 docker run -v f"{app_volume_path}:/app" 这个操作则在 kraken-eks-runners-trb9h-runner-klfxk:dind 容器中执行。

我们先来看看 pod 的清单文件:

    $ kk -n ops-github-runners-ns describe autoscalingrunnerset kraken-eks-runners  
    ......  
      模板信息:  
        规格说明:  
          容器列表:  
            ......  
            镜像:    492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken:0.8  
            名称:    runner  
            ......  
            卷挂载信息:  
              挂载路径: /home/runner/_work  
              名称:      work  
            ......  
            镜像为 docker:dind,名称为 dind  
            ......  
            卷挂载信息:  
              挂载路径: /home/runner/_work  
              名称:      work  
            ......

也就是说,这两个容器在 /home/runner/_work 这个共同目录上达成一致,该目录在主机(如 EC2 实例)上创建,然后挂载到它们所在的 Kubernetes Pod 中。

runner 容器中的 /tmp 目录对它来说是“本地”的,而不能被 dind 容器访问。

只需在 /home/runner/_work 目录下为 openapi.yml 文件创建一个新目录即可。

    ...
        # 获取 $HOME,如果获取失败则默认使用 '/home/runner'
        home = os.environ.get('HOME', '/home/runner')
        # 将 app_volume_path 设置为 '/home/runner/_work/tmp/'
        app_volume_path = Path(home) / "_work/tmp/"

        # 递归创建目录,如果已存在则忽略
        app_volume_path.mkdir(parents=True, exist_ok=True)
        (app_volume_path / "openapi.yml").write_text(yml_path.read_text())
    ...

或者做得更好——如果构建过程将在 GitHub 主办的运行器上运行,请查看作业运行在哪一个运行器上,并根据情况选择创建目录的位置。

在我们的 Scale Set 中加入一个叫做 RUNNER_EKS 的变量。

...  
模板:  
  规范:  
    containers:  
      - 名称: runner  
        image: 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken:0.8  
        command: ["/home/runner/run.sh"]  
        env:  
        - 名称: RUNNER_EKS  
          值: "true"  
...

在代码里,检查一下这个变量,根据它的结果来设置 app_volume_path 目录。

        # 我们的程序将具有 'RUNNER_EKS=true'  
        if os.environ.get('RUNNER_EKS', '').lower() == 'true':  
            # 获取 $HOME,回退到 '/home/runner'  
            home = os.environ.get('HOME', '/home/runner')  
            # 设置 app_volume_path 为 '/home/runner/_work/tmp/'  
            app_volume_path = Path(home) / "_work/tmp/"  
            # 递归创建文件夹,如果已存在则跳过  
            app_volume_path.mkdir(parents=True, exist_ok=True)  
        # 否则,如果是没有 'RUNNER_EKS' 的 GitHub 主机上的程序,使用旧的代码  
        else:  
            app_volume_path = Path(tempfile.mkdtemp())  
        # 将 yml_path 读取的文本写入到 (app_volume_path / "openapi.yml")  
        (app_volume_path / "openapi.yml").write_text(yml_path.read_text())  
    ...

再运行一遍构建,现在一切都能正常运行了。

“访问路径‘/home/runner/_work/_temp/_github_home/.kube/cache’拒绝访问”的错误

有时在构建部署的最后阶段,任务会以“错误:操作已取消”的提示结束。

原因在于,可以在运行日志中找到这个问题——它不能删除 _github_home/.kube/cache 目录,

    ...  
    kraken-eks-runners-wwn6k-runner-zlw7s:runner [WORKER 2024-09-20 10:55:23Z 信息] TempDirectoryManager: 正在清理运行器的临时文件夹:/home/runner/_work/_temp  
    kraken-eks-runners-wwn6k-runner-zlw7s:runner [WORKER 2024-09-20 10:55:23Z 错误] TempDirectoryManager: 出现一个或多个错误。 (访问路径 '/home/runner/_work/_temp/_github_home/.kube/cache' 被拒绝。)  
    kraken-eks-runners-wwn6k-runner-zlw7s:runner [WORKER 2024-09-20 10:55:23Z 错误] TempDirectoryManager: 系统未经授权访问异常,访问路径 '/home/runner/_work/_temp/_github_home/.kube/cache' 被拒绝。  
    kraken-eks-runners-wwn6k-runner-zlw7s:runner [WORKER 2024-09-20 10:55:23Z 错误] TempDirectoryManager: 系统IO异常,权限被拒绝  
    ...

实际上,如果我们从 runner 容器中检查 /home/runner/_work/_temp/_github_home/,它无法访问。

runner@kraken-eks-runners-7pd5d-runner-frbbb:~$ ls -l /home/runner/_work/_temp/_github_home/.kube/cache  
ls: 无法打开目录 '/home/runner/_work/_temp/_github_home/.kube/cache': 没有权限

但是可以通过该目录创建的带有 dind 的容器进行访问。

    / # ls -l /home/runner/_work/_temp/_github_home/.kube/cache  
    0 个文件  
    drwxr-x---    3 root     root            78 Sep 24 08:36 discovery  
    drwxr-x---    3 root     root           313 Sep 24 08:36 http

在这种情形下,由root创建,尽管其他目录是由用户1001创建。

    / # ls -l /home/runner/_work/_temp/  
    总计 40K  
    -rw-r--r--    1 1001     1001            71 9月24 08:36 79b35fe7-ba51-47fc-b5a2-4e4cdf227076.sh  
    drwxr-xr-x    2 1001     1001            24 9月24 08:31 _github_workflow  
    ...

1001 是 runner 容器中的 runner 用户:

runner@kraken-eks-runners-7pd5d-runner-frbbb:~$ id runner
运行命令`id runner`,显示用户runner的UID、GID和所属的用户组。UID为1001(runner),GID为1001(runner),用户组包括1001(runner),27(sudo),123(docker)。

有意思的是,错误时有时无,虽然工作流程没有改变过。

.kube/config 是由 bitovi/github-actions-deploy-eks-helm 动作生成的,该动作在其自己的 Docker 容器中运行 aws eks update-kubeconfig,并且以 root 权限运行,因为它是在 Docker 中运行的另一个 Docker 容器。

为解决这个问题,想到有两个选项:

  • 可以在部署的末尾简单地增加一个额外的命令 chown -r 1001:1001 /home/runner/_work/_temp/_github_home/.kube/cache(虽然也可以使用相同的命令来简单地删除该目录)
  • 或者将 GITHUB_HOME 更改为另一个目录,然后 aws eks update-kubeconfig 会将该目录创建在另一个位置,带有 runner 标签的容器将能够执行 清理 runner 临时文件夹 操作

然而,我还是不明白为什么“清理运行者临时文件夹”每次都不起作用,因此它是一个“不稳定的bug”。让我们看看将来它会如何表现。

连接一个高IOPS的卷(IOPS是指每秒输入输出操作次数)

我们想切换到自托管运行器,原因之一是为了加快构建和部署过程。

构建过程中耗时最长的部分通常是像 docker loaddocker save 这样的命令。

所以,我想尝试连接一个具有高 IOPS 的 AWS EBS,因为默认的 gp2 每 GB 有 100 IOPS — 请参阅 Amazon EBS 卷类型

创建一个新的 Kubernetes 存储类。

    apiVersion: storage.k8s.io/v1  
    kind: StorageClass  
    metadata:  
      name: gp3-iops  
    provisioner: kubernetes.io/aws-ebs  
    parameters:  
      type: gp3  
      iopsPerGB: "16000"  
      throughput: "1000"  
    allowVolumeExpansion: true  
    volumeBindingMode: WaitForFirstConsumer

在我们 Runner 池的配置值里,添加 volumes 块,其中我们覆盖了默认由 [emptyDir: {}] 创建的 work 磁盘的参数。

在这里设置一个新的 storageClassName,例如:

    githubConfigUrl: "https://github.com/***/kraken"  
    githubConfigSecret: "GitHub 配置密钥"  # gh-runners-token  

    runnerScaleSetName: "运行器规模集名称"  # kraken-eks-runners  
    containerMode:  
      type: "容器模式类型"  # dind  # Docker in Docker 实现  
    template:  
      spec:   
        容器:  
          - name: runner  
            image: 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken:0.9  
            命令: ["/home/runner/run.sh"]  
            环境变量:  
            - name: RUNNER_EKS  
              value: "true"  
            资源请求:  
              请求:  
                CPU: 2  
                内存: 4Gi  
        卷:  
          - name: work  
            临时卷模板:  
              规格:  
                访问模式: [ "读写一次" ]  
                存储类名称: "gp3-iops"  
                存储资源请求:  
                  请求:  
                    存储容量: 10Gi

部署这些更改到AutoscalingRunnerSet,执行部署,带有Runners的Pod会被创建,但会立即被终止,任务本身也会宣告失败。

访问该路径“/home/runner/_work/_tool”被拒绝时的错误

再次检查运行日志,看看:

_kraken-eks-runners-gz866-runner-nx89n:runner [RUNNER 2024–09–24 10:15:40Z JobDispatcher 错误] 未经授权的访问异常: 无法访问路径 "/home/runner/_work/tool"

我已经查看了关于错误“访问路径/home/runner/_work/_tool受到拒绝”的文档(错误:访问路径/home/runner/_work/_tool受到拒绝),当时我在寻找解决上述“访问路径‘/home/runner/_work/_temp/_github_home/.kube/cache’受到拒绝”错误的方法,这正是我所需要的。

要解决这个问题,添加另一个initContainer、并运行chown命令:

    ...  
    模板:  
      规范:  
        初始化容器:  
        - name: kube-init  
          image: ghcr.io/actions/actions-runner:latest  
          command: ["sudo", "chown", "-R", "1001:123", "/home/runner/_work"]  
          卷挂载:  
            - name: work  
              挂载路径: /home/runner/_work    
        容器:  
          - name: runner  
            image: 492***148.dkr.ecr.us-east-1.amazonaws.com/github-runners/kraken:0.9  
            command: ["/home/runner/run.sh"]  
    ...

现在一切都好了。

咱们比一比看看结果。

“后端的构建和部署”环节大约花了9分钟:

就这样变成了6分钟了

基本上,就这样吧。

虽然一开始可能需要稍微调整一下,但总的来说它是可以工作的。

原发布于 RTFM:Linux、DevOps 和系统管理

注:原文中的下划线表示斜体。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消