在EKS上创建GitHub自托管的运行器

这篇文章的目标是什么?

    • クラウドネイティブなシステムを作るのに、CI/CDは欠かせない仕組みです。

 

    • CIパイプラインを動かすツールとして有名なGitHub Actionsを、Self-hosted RunnerとしてEKSクラスタ上で動かすため設計の注意点や導入方法について記載しています。

 

    公式が英語で、DeepLにコピるのが面倒な方の一助になれば幸いです。

這篇文章讓人了解到的事情。

    • EKSでのGitHub Self-hosted Runnerの立て方

 

    設計上の注意ポイント

在这篇文章中不理解的事情。

    • GitHub Actionsでのワークフローの作り方

動作確認のため簡単なワークフローは動かしていますが、GitHub Acitonsのワークフローに関する設定について深く言及しません。

AzureやGCP特有の設定

AWS上で動かすことにフォーカスしています。k8sではあるもののパブリッククラウドごとに微妙に制約が変わってくると思います。

Terraformについて

テスト用にTerraformコードを動かしていますが、内容について深く言及しません。

GitHub Actions是什么?

可能有些对这篇文章感兴趣的读者可能不需要,但为了保险起见,我还是从官方引用了GitHub Actions的说明。

让我们在存储库中自动化、自定义和执行软件开发工作流程,使用 GitHub Actions。您可以找到、创建和共享包含喜欢的 CI/CD 作业的动作,并在完全定制的工作流程中组合这些动作。这是引用自 GitHub Actions 官方网站。

这个工具可以在任意时间点执行工作流程并输出结果,例如在发布PR或将其合并到主分支时,工作流程可以在GitHub存储库中进行配置。

GitHub自托管运行程序是什么?

在GitHub Actions中,Runner的运行位置有两种选择。

GitHub-hosted Runner

GitHub上にホストされた仮想マシン上でのRunnerを稼働させる
GitHubにおけるデフォルト構成であり、特に何も考えなくてもワークフローのファイルを置けばRunnerが実行される

Self-hosted Runner

自身の環境に専用Runnerを構築する
GitHubリポジトリと認証連携を行うことで、ワークフローのファイルをRunnerで実行することが可能

GitHub托管的Runner非常方便,可以快速构建工作流并无需担心Runner的性能问题,但以下几点令人担忧:
・Runner位于互联网上,并且使用的全球IP地址在GitHub上是变动的,因此如果需要连接到自己的环境,则需要大幅开放与互联网的通信权限。
・无法定制Runner使用的容器镜像。

即使使用公共云,我认为如果构建企业系统,仍然需要考虑安全性,并且需要满足资源管理的要求,就像在本地构建一样。在这种情况下,可以使用自托管 Runner。

行动执行控制器

自己托管的Runner可以在EC2实例等运行机器上运行,但是既然已经创建了云原生环境,就不想增加不必要的虚拟机。
在这种情况下,可以使用Actions Runner Controller在EKS集群上将Runner作为Pod启动。
Actions Runner Controller官方

Actions Runner Controller是在容器环境中管理GitHub Actions Runner的功能资源,作为开源软件进行公开发布。
大致提供以下功能:

    • リポジトリもしくはOrgaizationsに対するRunnerのセッティング

 

    • Runnerのスケーリング

 

    Runnerへの権限付与

通过 Actions Runner Controller,Runner将被部署,但是Runner可以以Deployment或者ReplicaSet的形式进行部署。这次我们将使用RunnerDeployment来进行部署。

image.png

尝试在EKS上创建一个Runner

那么,我们将在EKS集群上部署Actions Runner Controller,并实际运行工作流程来尝试一下。
架构如下所示。

image.png
    • AWS上に必要なリソースを作成します。

EKSクラスタ
Terraform用S3バケット、DynamoDB

tfstateの配置、排他制御に利用

ServiceAccount用IAMロール

AWS全体のReadOnlyAccess権限、S3/DynamoDBへの接続権限
Runner実行時の権限を付与

GitHubにテスト用のTerraformリポジトリを用意します。
Actions Runner Controllerをデプロイし、リポジトリ専用のRunnerをデプロイします。

サービスアカウントを利用して、IAMロールを紐づけ

Runnerの挙動確認をするため、terrafom planコマンドを実行するワークフローを実行します。

其他版本和相关信息如下所示。

AWSリージョン:東京(ap-northeast-1)
EKSクラスタ:1.24
Actions Runner Controller : 0.21.1
Terraform : 1.3.0

准备AWS资源

我們將事先建立所需的AWS資源。建立資源需要授權給IAM用戶和設定存取密鑰等,但在這裡我們假設這些都已經設定完成,我們將繼續進行。

EKS集群

    EKSクラスタが簡単に構築できるeksctlコマンドを利用します。
$  eksctl create cluster \
--name test-cluster \
--region ap-northeast-1 \
--timeout 40m \
--version 1.24 \
--nodegroup-name test-node \
--node-type t3.large
image.png
    ローカル環境からEKSクラスタに接続できるようにしておきます。
# 作成したEKSクラスタをローカルに登録
$ aws eks --region ap-northeast-1 update-kubeconfig --name test-cluster
Added new context arn:aws:eks:ap-northeast-1:XXXXXXXXXX:cluster/test-cluster to C:\Users\xxxxxx\.kube\config

# 接続確認
$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   60m

创建S3存储桶和DynamoDB

从管理控制台创建。
创建的资源名称将在随后的IAM角色或Terraform代码中使用。

DynamoDB.png

创建OIDC提供者。

必须在以下两个方面中创建OpenID Connect (OIDC) 提供者。

OIDCプロバイダ(GitHubアクション).png

创建用于自托管 Runner 的 IAM 角色。

在运行Runner时创建IAM角色。
为了执行terraform plan,将以下权限授予IAM角色。

IAMロール.png
{
    "Statement": [
        {
            "Action": [
                "dynamodb:PutItem",
                "dynamodb:GetItem",
                "dynamodb:DeleteItem"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:dynamodb:ap-northeast-1:xxxxxxx:table/<作成したDynamoDBの名前>",
            "Sid": ""
        }
    ],
    "Version": "2012-10-17"
}
IAMロール_信頼関係.png
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::XXXXXXX:oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/<乱数>"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.ap-northeast-1.amazonaws.com/id/<乱数>:sub": "system:serviceaccount:<Runnerのnamespace>:<RunnerのServiceAccout名>"
                }
            }
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::XXXXXXX:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:<Runnerを稼働するリポジトリ名>:*"
                }
            }
        }
    ]
}
    OIDCプロバイダの設定とIAMロールの設定を行うことで、GitHub Actionsに対してシークレットキーなどの秘匿情報を保管する必要がなくなります。

现在,AWS资源已经准备就绪。

准备GitHub仓库

进行GitHub仓库的准备工作。

    • リポジトリと実行コードの作成

 

    GitHub Appsの作成

创建存储库和执行代码

创建一个合适的Terraform存储库,并放置用于执行terrafom plan命令的测试代码。
我正在编写一个简单的代码来创建VPC和子网。这只是为了执行计划,不会实际部署。

terraform {
  # Terraformバージョン指定
  required_version = "~> 1.3.0" 

  # AWSプロバイダのバージョン指定
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.17.0"
    }
  }

  # tfstateファイルの配置場所をS3バケットに指定
  backend "s3" {
    bucket         = "qiita-terraform-tfstate" # S3バケット名
    key            = "test_vpc" # tfstateに格納する際のキー名 
    region         = "ap-northeast-1"
    # S3への処理を排他制御するDynamoDBを指定
    dynamodb_table = "Qiita-dynamoDB" # 作成したDynamoDB名
  }
}

# VPCを作成するコード
resource "aws_vpc" "main" {
  cidr_block                       = "192.168.0.0/16"
}

# サブネットを作成するコード
resource "aws_subnet" "main" {
  vpc_id                           = aws_vpc.main.id
  cidr_block                       = "192.168.100.0/24"
  availability_zone                = "ap-northeast-1a"
}

创建GitHub Apps

为了使自托管Runner能够作为GitHub仓库的Runner运行,需要对GitHub进行身份验证。
虽然有一种方法可以生成GitHub帐户的PAT(个人帐户令牌),但从安全角度来看并不是很好,因此我们将使用GitHub Apps。

GitHub Apps.png
No.項目必須値1GitHub App name〇GitHub Appsの名前(任意)2Description-GitHub Appsの説明3Homepage URL〇GitHub AppsのURL
※今回は利用しないので適当な名前でOK4WebHook-今回は利用しないので、チェックを外す
    • 設定時に併せてリポジトリに対する権限を付与します。

 

    Repository permissions
項目権限ActionsRead-OnlyAdministration
ChecksRead-OnlyMetadataRead-Only

组织权限

項目権限Self-hosted runnersRead and Write
    作成されたGitHub AppsでPrivate Keysを発行し手元に保管します。(この後利用します。)
Github PrivateKey.png
GitHub Apps インストール_2.png

以上就是GitHub仓库准备完成。

操作运行控制器和运行器的部署

由于事前准备工作已经完成,现在开始部署Actions Runner Controller和Runner。

    認証用に作成したGitHub Appsの情報をsecretとしてEKSにデプロイします。
apiVersion: v1
kind: Secret
metadata:
  name: github-actions-runner-secret
  namespace: default
data: # すべてbase64でエンコード
  github_app_id: <GitHub Appsのapp ID>
  github_app_installation_id: <リポジトリにインストールした際のID(URLから取得)>
  github_app_private_key: <発行したPrivate Keys>
$  kubectl apply -f github-apps-secret.yaml 
secret/github-actions-runner-secret created

$  kubectl get secret github-actions-runner-secret
NAME                           TYPE     DATA   AGE
github-actions-runner-secret   Opaque   3      15s
    GitHub ActionsのCA管理に利用する、cert-managerをHelmチャートを利用してデプロイします。
# Helmリポジトリの取得
$ helm repo add cert-manager https://charts.jetstack.io
$ helm repo upgrade 

# CRDのインストール
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.1/cert-manager.crds.yaml
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created

# cert-managerインストール(Helm)
$ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.10.1
NAME: cert-manager
LAST DEPLOYED: Tue Dec 13 21:16:05 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.10.1 has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.io/docs/configuration/

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.io/docs/usage/ingress/

# 稼働確認
$ kubectl get pod -n cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-5fdbd97fb5-82kj4              1/1     Running   0          59s
cert-manager-cainjector-7c44879bc4-gtfhn   1/1     Running   0          59s
cert-manager-webhook-5db84854c8-98pp4      1/1     Running   0          59s
    Actions Runner ControllerをHelmでデプロイします。
# Helmリポジトリの取得
$ helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller
$ helm repo upgrade 

# Actions Runner Controllerのデプロイ(Helm)
$ helm install -f values.yaml --wait --namespace default actions-runner-controller actions-runner-controller/actions-runner-controller
NAME: actions-runner-controller
LAST DEPLOYED: Tue Dec 13 21:23:42 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=actions-runner-controller,app.kubernetes.io/instance=actions-runner-controller" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

# 稼働確認
$ kubectl get pod
NAME                                         READY   STATUS    RESTARTS   AGE
actions-runner-controller-6dcc598777-nkkrb   2/2     Running   0          35s
# Actions Runner Controllerのレプリカ数
replicaCount: 1

# GitHub Apps認証に利用するSecretの指定(先ほど作成)
authSecret:
  enabled: true
  create: false
  name: "github-actions-runner-secret"

# RBACの有効化
rbac:
  allowGrantingKubernetesContainerModePermissions: true

# Webhookは利用しないため無効化
githubWebhookServer:
  enabled: false
    Runnerが利用するサービスアカウントをデプロイします。
apiVersion: v1
kind: ServiceAccount
metadata:
  # IAMロール作成時に指定したサービスアカウント名と合わせる
  name: terraform-runner
  namespace: default
  annotations:
    # 作成したIAMロールを割り当てる(ARN)
    eks.amazonaws.com/role-arn: arn:aws:iam::234159168154:role/Qiita-terraform-Github
$ kubectl apply -f serviceaccount.yaml
serviceaccount/terraform-runner created
    Terraformリポジトリ専用のRunnerDeploymentをデプロイします。
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: terraform-runner
  namespace: default
spec:
  # Runnerのレプリカ数
  replicas: 1
  template:
    spec:
      # GitHubリポジトリ名を指定
      repository: aokiYosuke/terraform-test
      # 利用するサービスアカウント名
      serviceAccountName: terraform-runner
      securityContext:
        fsGroup: 1000
$ kubectl apply -f runnerdeployment.yaml
runnerdeployment.actions.summerwind.dev/terraform-runner created
$ kubectl get pod | grep terraform
NAME                                         READY   STATUS    RESTARTS   AGE
terraform-runner-kftwb-pzkzr                 2/2     Running   0          37m
image.png

现在,Self-hosted Runner的部署已经完成了。

确认工作流程的行为

为了确认Self-hosted Runner是否正常运行,我们将运行一个测试用工作流程。

    GitHubリポジトリにワークフローファイルを配置します。
name: 'Terraform'

on:
  push:
    paths:
      - .github/workflows/terraform-ci.yml
  pull_request:

permissions:
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    # Self-hosted Runnerを利用するように明示的に指定
    runs-on: self-hosted
    environment: Qiita-test

    defaults:
      run:
        shell: bash

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    # Terraformコマンドのインストール
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2
      with:
       terraform_version: 1.3.0
    # NodeJSのインストール(Terraformコマンド実行に必要)
    - name: Setup Nodejs
      uses: actions/setup-node@v1

    # terraform fmtコマンド実行
    - name: Terraform fmt
      id: fmt
      run: terraform fmt -check
      continue-on-error: true

    # terraform initコマンド実行
    - name: Terraform Init
      id: init
      run: terraform init

    # terraform validateコマンド実行 
    - name: Terraform Validate
      id: validate
      run: terraform validate -no-color

    # terraform planコマンド実行
    - name: Terraform Plan
      id: plan
      run: terraform plan -no-color
      continue-on-error: true
image.png

总结

我使用 Actions Runner Controller,在 EKS 集群上部署了 Self-hosted Runner 的方法进行了简要的确认。通过这种方式,

    • リポジトリに対して専用のSelf-hosted Runnerデプロイ

 

    認証情報をGitHubに渡さなくてもIAM権限を利用したRunnerの実行

附近已经实现了。

除了这个之外,

    • Runnerをリポジトリ専用にするか?Organizations全体で共有するか?

 

    • Runnerのスケーリング方式(Horizonal Runner Autoscaler)

 

    Runnerが利用するリソース割り当て設計

如果考虑到使用在系统建设中的合适选项,还有其他方面需要思考。
如果想了解更多信息,请查看GitHub上的DeepL片手与Actions Runner Controller的官方网页。