在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来进行部署。
尝试在EKS上创建一个Runner
那么,我们将在EKS集群上部署Actions Runner Controller,并实际运行工作流程来尝试一下。
架构如下所示。
-
- 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
- ローカル環境から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代码中使用。
创建OIDC提供者。
必须在以下两个方面中创建OpenID Connect (OIDC) 提供者。
创建用于自托管 Runner 的 IAM 角色。
在运行Runner时创建IAM角色。
为了执行terraform plan,将以下权限授予IAM角色。
{
"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"
}
{
"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。
※今回は利用しないので適当な名前でOK4WebHook-今回は利用しないので、チェックを外す
-
- 設定時に併せてリポジトリに対する権限を付与します。
- Repository permissions
ChecksRead-OnlyMetadataRead-Only
组织权限
- 作成されたGitHub AppsでPrivate Keysを発行し手元に保管します。(この後利用します。)
以上就是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
现在,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
总结
我使用 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的官方网页。