研究開発部 Architectグループ ML PlatformチームのKAZYこと新井です。
名古屋にある中部支店に所属しています。
KubernetesのCronJobからJobの作成と実行をGitHub Actionsでできるようにした話を紹介します。
なお、本記事は【R&D DevOps通信】という連載記事のひとつです。
目次
研究開発部とEKSの背景
研究開発部ではアプリケーション基盤としてEKS(Circuitと呼んでいます)を導入し、アプリケーション開発者への利用を推進中です。
推進活動の一環として、Kubernetesの経験が浅い方でも気軽にはじめられるような取り組みをしています。
こういった取り組みの甲斐もあり、研究開発部の方々が続々とバッチ処理やAPIサーバを作成してくれるようになったのでした。
CronJobの手動実行が必要になった経緯
定期バッチ処理*1は失敗や失敗や失敗やアルゴリズムの改修等で手動での再実行がしたくなるものです。
アプリケーション開発者自身で再実行が出来るようにしたいなと思うものの、基盤の整備状況などの観点から、これまでは基盤を運用しているArchitectグループのエンジニアがやっていました*2。
しかし、Circuitに乗せるアプリケーションが多くなったのでその運用にも限界が来ました。
そのため簡単に定期バッチの手動実行できる仕組みを作ることにしました。
GitHub ActionsからCronJobの手動実行ができる仕組み
CronJobをJobとしてGitHub Actionsから実行できるようにしました。
kubectlのバージョンなどの実行環境をユーザに気にさせなくて済むためです*3。
CronJobからJobを作成する方法
kubectlコマンドにあるCronJobからJobを作成するコマンドを使います。
kubectl create job --from=cronjob/<CronJob名> <Job名> -n <名前空間>
Jobマニフェストを作成してapplyすることもできますが、
- 簡潔にワンライナーで表現できること
- 1回実行したら不要になることが多いこと
から上記の方法にしました。
実装の方針
- 既存のCronJobからのみJobを作成できるようにする(任意のJob作成はNG)
- なるべくシンプルにする(使い方がわからないという問い合わせを0にしたい)
出来上がった仕組みの構成図
人間がGitHub Actionsのworkflow dispatchを用いて、フォームに必要事項を記入します。 そして実行ボタンをポチっとすると、EKSにCronJobをもとにJobを実行するものとしました。
作成したワークフロー
EKSをGitHub Actionsから操作できるようにして、kubectlコマンドを叩くだけです。
必要事項は
- 実行環境
- 名前空間
- CronJobの名前
です。
name: Execute Job Manually on: workflow_dispatch: inputs: environment: type: choice description: Environment options: - development - staging namespace: type: choice description: Namespace options: - namespace1 - namespace2 cronjob_name: description: CronJob Name jobs: create-manifest: name: "[${{ github.event.inputs.environment }}] Execute Job Manually." runs-on: ubuntu-latest environment: ${{ github.event.inputs.environment }} permissions: id-token: write timeout-minutes: 5 steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: ${{ vars.AWS_ASSUME_ROLE_ARN }} aws-region: ap-northeast-1 - name: Create kubeconfig file run: aws eks update-kubeconfig --name ${{ vars.CLUSTER_NAME }} - name: Install kubectl run: | curl -LO https://storage.googleapis.com/kubernetes-release/release/${{ vars.K8S_VERSION }}/bin/linux/amd64/kubectl chmod +x ./kubectl ./kubectl version # やりたいのはコレ - name: Create Job from CronJob run: | kubectl create job --from=cronjob/${{ github.event.inputs.cronjob_name }} ${{ github.event.inputs.cronjob_name }}-by-gha-$(date +'%Y%m%d%H%M%S') -n ${{ github.event.inputs.namespace }}
GitHub Actionsからkubectlを使うためのAWS側の設定
- OIDCプロバイダを用いた認証
- EKSへの権限付与
を行いました。
OIDCプロバイダを用いた認証はGitHub ActionsからAWSリソースへアクセスする方法と同様です。
Configuring OpenID Connect in Amazon Web Services - GitHub Docs
本番環境ではmainブランチからのみIAM Roleの使用権限を与えました*4。任意のブランチからワークフローの実行ができると、ワークフローを書き換えて、任意のJobをレビュー無しで作成可能になってしまうのためです*5。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456123456:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringLike": { "token.actions.githubusercontent.com:sub": "repo:octo-org/octo-repo:main" # mainに限定している }, "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" } } } ] }
IAM RoleへはEKSにアクセスできるよう以下のようなポリシーを付与しました。CircuitではBlue/Greenデプロイによるクラスター更新*6を行うため、一部ワイルドカードを利用して、アップデート後のクラスター名との差分を吸収できるようにしました。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": "eks:DescribeCluster", "Resource": "arn:aws:eks:ap-northeast-1:123456789012:cluster/<EKS クラスター名>" } ] }
これでEKS自体にはアクセスできるようになります。
GitHub ActionsからJobを作成するためのKubernetes側の設定
クラスターに対する IAM プリンシパルのアクセスの有効化 - Amazon EKSを参考にして、IAM Roleにクラスター内のリソースを操作する権限を与えてます。
IAM Role(sample-role)をgithub-actionsというユーザ名でgithub-actionsというグループに紐付けます*7。
apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: arn:aws:iam::123456789012:role/sample-role username: github-actions groups: - github-actions
CronJobの読み込みと、Jobの作成ができるRoleを作成します*8。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: create-job-from-cronjob rules: - apiGroups: - "batch" resources: - cronjobs verbs: - get - apiGroups: - "batch" resources: - jobs verbs: - create
IAM Rroleと紐付けたグループ(github-actions)とClusterRoleを紐付けます。
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: github-actions roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: create-job-from-cronjob subjects: - kind: Group name: github-actions apiGroup: rbac.authorization.k8s.io
これでCronJobからJobの作成ができるIAM Roleになりました。
注意したい点
ワークフロー実行が可能な人
どんなユーザがワークフローを実行できるかは一度確認すると良いでしょう。
ちなみにワークフロー実行者の履歴は残ります。
Job名の文字数
あとJob名は64文字未満の制限があるのでご注意ください。
おわりに
アプリケーション開発者自身で再実行できるようになったため、アプリケーション開発者と基盤開発者双方が幸せになりましたとさ。
めでたしめでたし。
求人
私の所属するML Platformチームを含む、研究開発部Architectグループでは一緒に働く仲間を募集しています。
R&D MLOps/DevOpsエンジニア / Sansan株式会社
*1:ここではCronJobリソースを指します。
*2:kubectlコマンドをゴニョゴニョと叩いていました。
*3:kubectlのバージョンは、クラスターのマイナーバージョンとの差分が1つ以内でなければならないとドキュメントには記載されています。 https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/
*4:参考 https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services
*5:mainブランチへのコミットはレビューを必須にしています。
*6:新しいクラスタを作成して更新を行う方法です。
*7:グループ名とユーザ名は同じである必要はありません。
*8:名前空間を超えて使いたかったのでClusterRoleにしました。