GitHub Actions Workflow を定期実行できる環境を AWS CDK で作成する方法
実現したいこと
定刻にジョブが実行される CI/CD 環境から GitHub Actions への移行する際に、GitHub Actions の定期実行ジョブの遅延実行が問題になりました。Lambda から GitHub Actions workflow_dispatch を API 経由で実行することにより課題を解消します。
GitHub Actions Workflow を 5 分ごと 正しい間隔(定期実行) でスケジュール実行したいが、GitHub Actions の Cron( schedule トリガー ) は遅延が発生[1]する仕様です。この課題を AWS Lambda + GitHub Actions API を利用して解決[2]します。AWS 環境構築には AWS CDK を利用します。
前提条件
AWS CDK を利用して、AWS Lambda から GitHub Actions の workflow_dispatch を定期実行する AWS 環境を実現します。
- AWS を利用します
- Node.js を利用します
- AWS CDK を利用します
- GitHub Apps を利用します
- GitHub App の private key を
AWS Secrets Manager
に登録します- 本記事では
AWS Secrets Manager
を利用[3]していますが、AWS Parameter Store
でも要件を満たします。
- 本記事では
- workflow_dispatch ワークフロートリガーが設定されている GitHub Actions workflow を API で呼び出します
成果物(結論)
AWS CDK で AWS 環境を構築し、Lambda から GitHub Actions Workflow を実行する成果物は、上記リポジトリを参照してください。全てここにあります。
やっていく
AWS 環境構築 + AWS Lambda 関数実装を AWS CDK を利用して行います。
今回作成したプロジェクトは https://github.com/naotama2002/cron-github-actions-workflow-from-lambda.git を参照ください。ポイントかもしれない部分だけ紹介していきます。
準備
プロジェクト作成
npx aws-cdk@2 init app --language typescript
ブートストラップ
npm run cdk bootstrap
esbuild インストール
NodejsFunction
が esbuild を利用しているため、インストールします。
npm install esbuild -D
deploy
npm run cdk deploy
AWS コンソールで Lambda 関数をみると CronGithubActionsWorkflow-WorkflowDispatchxxxxxx-xxxxxxx
という名前で Lambda 関数が作成されています。
5分ごとに実行するスケジュール登録
EventBridge 定義
Cron 形式で 5 分ごとに実行する EventBridge のルールを定義します。
export class CronGithubActionsWorkflowFromLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
:
:
// 5分ごとに実行
new events.Rule(this, "WorkflowDispatchRule", {
schedule: events.Schedule.expression("cron(0/5 * * * ? *)"),
targets: [new targets.LambdaFunction(WorkflowDispatch,
{
retryAttempts: 0
}
)],
});
実行される GitHub Actions Workflow 定義
この Workflow が 5 分ごとに実行されることがゴール。
name: Workflow dispatch test
on:
workflow_dispatch:
jobs:
step:
runs-on: ubuntu-latest
steps:
- run: echo Run workflow dispatch!
Lambda から GitHub Actions workflow_dispatch を実行する
準備
GitHub App を作成する
外部から workflow_dispatch を API 経由で実行するために、GitHub Apps を利用します。
必要なのは Repository Actions Read and write
権限を持ち、対象のリポジトリにインストールされていることです。
GitHub App 作成時の、デフォルトからの変更点を記載します。
- Expire user authorization tokens : OFF
- Permissions
- Repository permissions
- Actions : Read and write
- Repository permissions
- Webhook Active : OFF
GitHub App をインストールします。
- Only select repositories を選択して
-
naotama2002-org/workflow-dispatch-zenn
を選択
-
GitHub App インストール結果
GitHub App 情報を AWS Secrets Manager に登録する
GitHub App インストールとして認証で必要な情報を登録します。Lambda から利用し、GitHub App から Token を取得するために利用します。
登録するのは下記の 3 つです。
- GITHUB_APP_ID : GitHub App の ID
- GITHUB_SECRET_KEY : GitHub App の private key
- GITHUB_APP_INSTALLATION_ID : GitHub App の installation ID
実行コード
Lambda 関数で利用する GitHub App の情報を Secrets Manager から 得る
シークレット取得コードはここを見ていただくとして、Lambda 関数に必要な権限を付与します。
export class CronGithubActionsWorkflowFromLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// .env ファイル読み込み
dotenv.config();
// Secrets Manager の APN
const stringSecretArn = process.env.AWS_SECRETS_MANAGER_APN;
:
:
// AWS Secrets Manager への権限付与
// Secrets Manager の該当 APN の Read権限
const smResource = Secret.fromSecretCompleteArn(this, "SecretsManager", stringSecretArn);
smResource.grantRead(WorkflowDispatch);
.env ファイルから APN を読み込んで、権限を付与しています。AWS CDK で書くと直感的で良いですね。
workflow_dispatch を実行
GitHub Actions API を叩く部分は https://github.com/octokit/octokit.js を利用しています。
octokit/auth-app で GitHub App から Token を取得します。
export const triggerWorkflowDispatch = async ({
secrets,
}: triggerWorkflowDispatchParams): Promise<void> => {
const octokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: secrets.GITHUB_APP_ID,
privateKey: secrets.GITHUB_SECRET_KEY,
installationId: secrets.GITHUB_APP_INSTALLATION_ID,
},
});
octokit/rest で workflow_dispatch を実行します。
await octokit.rest.actions
.createWorkflowDispatch({
owner: WORKFLOW_OWNER,
repo: WORKFLOW_REPO,
workflow_id: WORKFLOW_ID,
ref: WORKFLOW_REF,
})
.then((_) => {
console.log(
`success: ${WORKFLOW_OWNER}/${WORKFLOW_REPO}/${WORKFLOW_ID}:${WORKFLOW_REF} workflow_dispatch`,
);
})
.catch((error) => {
console.log(
`error: ${WORKFLOW_OWNER}/${WORKFLOW_REPO}/${WORKFLOW_ID}:${WORKFLOW_REF} workflow_dispatch`,
);
throw error;
}
);
実行結果
5 分ごとに実行されていることが確認できます。
あとがき
実務では Serverless framework V3.x で実装したのですが、V4 New Model に合わせ、移行先を検討しておくかーということで、今回は AWS CDK を検証してみました。
Discussion