この記事の目的
- クラウドネイティブなシステムを作るのに、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公式から引用
GitHubリポジトリに配置したワークフローを、PR発行時やmainブランチへのマージ時など、任意のタイミングで実行・結果の出力をしてくれるツールとなります。
GitHub Self-Hosted Runnerとは
このGitHub Actionsは、実行主体となるRunnerの稼働場所として、2種類の選択肢があります。
-
GitHub-hosted Runner
- GitHub上にホストされた仮想マシン上でのRunnerを稼働させる
- GitHubにおけるデフォルト構成であり、特に何も考えなくてもワークフローのファイルを置けばRunnerが実行される
-
Self-hosted Runner
- 自身の環境に専用Runnerを構築する
- GitHubリポジトリと認証連携を行うことで、ワークフローのファイルをRunnerで実行することが可能
GitHub-hosted Runnerは、素早いワークフロー構築やRunner性能を気にしなくていい点など非常に便利ですが、以下点が懸念になります。
・Runnerがインターネット側にあり、かつGitHubで利用しているグローバルIPが流動的なため、自身の環境への接続が必要な場合はインターネットからの通信許可を大きく開ける必要がある
・Runnerが利用しているコンテナイメージのカスタマイズができない
パブリッククラウドを利用するとしても、企業のシステムを構築する以上、オンプレで作る場合と同様にセキュリティ面を考慮したり、リソース管理を行いたいという要件が発生すると思うので、その場合はSelf-hosted Runnerを利用することになります。
Actions Runner Controller
Self-hosted Runnerは、EC2インスタンスなどの稼働マシンで稼働させることもできますが、せっかくクラウドネイティブな環境を作っているのに不要な仮想マシンは増やしたくありません。
その場合、EKSクラスタ上にPodとしてRunnerを起動できる、Actions Runner Controllerを利用します。
Actions Runner Controller公式
Actions Runner Controllerはコンテナ環境でのGitHub ActionsのRunner管理を行う機能を提供するリソースで、OSSとして公開されています。
大まかに、以下の機能を提供しています。
- リポジトリもしくはOrgaizationsに対するRunnerのセッティング
- Runnerのスケーリング
- Runnerへの権限付与
Actions Runner Controllerによって、Runnerがデプロイされますが、RunnerはDeployment
もしくはReplicaSet
の形でデプロイが可能です。今回はRunnerDeployment
でデプロイします。
EKSにRunnerを立ててみる
という事で、EKSクラスタにAtcions 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プロバイダの作成
以下2つの用途で、OpenIDConnect(OIDC)プロバイダを作成する必要があります。
-
EKSクラスタ
-
GitHub Actions
Self-hosted 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"
}
また、先ほど作成したOIDCプロバイダとの信頼関係を設定しています。
Runner用のサービスアカウントと、Runnerを稼働するリポジトリを対象に設定します。
{
"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とサブネットを作成する簡単コードを書いています。planを実行するためだけなので実際にデプロイはされません。
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の作成
Self-hosted RunnerがGitHubリポジトリのRunnerとして稼働するために、GitHubに対する認証が必要になります。
GitHubアカウントのPAT(Personal Account Token)を発行する方法がありますが、セキュリティ的にあまりよろしくないので、今回はGitHub Appsを利用します。
No. | 項目 | 必須 | 値 |
---|---|---|---|
1 | GitHub App name | 〇 | GitHub Appsの名前(任意) |
2 | Description | - | GitHub Appsの説明 |
3 | Homepage URL | 〇 | GitHub AppsのURL ※今回は利用しないので適当な名前でOK |
4 | WebHook | - | 今回は利用しないので、チェックを外す |
- 設定時に併せてリポジトリに対する権限を付与します。
Repository permissions
項目 | 権限 |
---|---|
Actions | Read-Only |
Administration | |
Checks | Read-Only |
Metadata | Read-Only |
Organizarion permissions
項目 | 権限 |
---|---|
Self-hosted runners | Read and Write |
- 作成されたGitHub Appsで
Private Keys
を発行し手元に保管します。(この後利用します。)
以上で、GitHubリポジトリの準備が完了です。
Actions Runner ControllerおよびRunnerのデプロイ
事前準備が完了したので、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が利用するリソース割り当て設計
など、ちゃんとシステム構築に利用するものとする場合に考えることはまだあります。
より知りたい方は、DeepL片手にActions Runner ControllerのGitHubを覗いてみてください。
Action Runner Controller公式