Platform Engineering を支える GitHub Actions の小ネタ集
これは GLOBIS Advent Calendar 2024 9日目の記事です。
はじめに
Platform Engineering とは、開発者の認知負荷を下げて、効率的に作業できる基盤を構築・運用する取り組みのことで、SRE や DevOps の文脈で近年特に注目を集めています。
しかし、我々が運用している Terraform や Kubernetes といったツールは特にアプリケーション開発者にとって馴染みが薄く、認知負荷が大きいという課題があります。
こういった IaC 領域の開発およびデリバリープロセスは GitHub を起点として展開されているため、これらの課題に対するアプローチとして GitHub Actions を活用することが有効ではないかと考えています。
弊社の SRE チームでは GitHub Actions を様々なユースケースで活用しており、今回は、その中で得た Tips をいくつか紹介します。
GitHub Apps の活用
GITHUB_TOKEN
の限界
GitHub Actions でデフォルトで提供される GITHUB_TOKEN
は気軽に使えてとても便利ですが、いくつかの制約があります。特に、自動化を進めようとすると以下のような問題に直面しがちではないでしょうか。
- 自分自身のリポジトリに対するアクセス権限に限定されているため、複数のリポジトリにまたがる操作ができないこと。
- 再帰的なワークフローの実行に制限があるため、ワークフロー内で commit を push すると、別のワークフローが意図せずトリガーされないこと。
GitHub Apps での認証
この問題を解決するためには GitHub Apps を利用する必要があります。GitHub Apps を使うことで、より柔軟な権限管理が可能になります。
具体的な手順はドキュメントに任せますが、GITHUB_TOKEN
ではなく GitHub Apps を通じて発行されるトークンを使用することで、必要なリポジトリに対するアクセス権限を取得することができます。
以下の公式アクションを使うことで安全にトークンを発行できるので積極的に利用しています。
- name: Create a token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.GITHUB_APP_ID }}
private-key: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}
- name: Checkout private tools
uses: actions/checkout@v4
with:
repository: my-org/private-tools
token: ${{ steps.app-token.outputs.token }}
GitHub Apps 経由で git コマンドを使う
GITHUB_TOKEN
を使っている場合は、git config コマンドでユーザー名とメールアドレスを以下のように設定することで、git commit を行ったときのユーザーが適切に表示されます。
- name: Set git user and email
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
マジックナンバーのように見える固定値 41898282
は実は GitHub Actions 自体のユーザー ID であり、noreply
用のメールアドレス形式 ID+USERNAME@users.noreply.github.com
に従って設定しているだけです。
GitHub Apps を利用している場合は少し特殊で、ユーザー名の値は <app_name>[bot]
ですが、メールアドレスを求めるには GitHub API を使って GitHub App 自体のユーザー ID を取得する必要があります。
# gh コマンドを使ってローカルから取得する方法
❯ gh api 'users/<app_name>[bot]' --jq '.id'
# 試しに github-actions[bot] の ID を取得してみると上記の固定値と一致することがわかる
❯ gh api 'users/github-actions[bot]' --jq '.id'
41898282
あとは ID+USERNAME@users.noreply.github.com
の形式に従うことで設定するべきメールアドレスがわかります。
GitHub Apps 経由で GitHub CLI を使う
さらに、GitHub Apps 経由で認証を通して GitHub CLI を使うことも可能です。特別な手順は不要で、環境変数 GH_TOKEN
を設定するだけで利用できます。
- name: Use GitHub CLI
run: gh pr list
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
Monorepo で変更差分を取得する
paths
や paths-ignore
の限界
複数のコンポーネントを1つのリポジトリで管理する monorepo 運用では、特定のディレクトリやファイルの変更をトリガーとして動的にワークフローを実行することが必要になります。弊社の場合は以下のようなケースで monorepo を採用しています。
- 複数環境の Terraform や共通 module をディレクトリ単位で分割する
- Argo CD Application の source としてディレクトリごとに Helm chart や Kustomize を定義する
しかし、GitHub Actions の paths
や paths-ignore
オプションだけでは柔軟に制御できない場合があります。
例えば、変更があったディレクトリの分だけ動的に matrix を定義することはできませんし、個別にワークフローを定義しようとするとファイル数が増えて認知負荷に繋がる恐れがあります。
tj-actions/changed-files
を使う
この問題を解決するために、tj-actions/changed-files
アクションを利用しています。このアクションを使うことで、より柔軟に変更差分を検知し、必要十分なコンポーネントにのみ後続の処理を実行することができます。
アクションの共通化
Reusable Workflow と Composite Action の使い分け
GitHub Actions では Reusable Workflow や Composite Action を使って処理を共通化することができます。
機能的にはどちらでもほぼ同じことが実現できますが、Platform Engineering 観点では複数のプロダクト(≒リポジトリ)から呼び出されることを考慮して、以下の基準で使い分けています。
Reusable Workflow
- プロダクトごとの固有の文脈をプロダクト内で共通化したいとき
- ex. デプロイ用のワークフローを複数環境に展開する
- トリガーやデプロイ先の情報しか変わらないので処理をワークフローごと共通化できる
Composite Action
- プロダクト横断で抽象化できる処理をステップ単位で共通化したいとき
- ex. Kubernetes マニフェストや Terraform コードの静的解析を実行する
- プロダクトのディレクトリ構成によらない形で切り出せる
- 呼び出し側にプロダクト固有のロジックを寄せられるので責務を分離できる
カスタムアクションを利用する
シェルや actions/github-script
アクションを用いたスクリプトでは記述しにくいほど処理が複雑な場合は、独自の TypeScript カスタムアクションを作成して運用しています。
TypeScript や Node.js のエコシステムに関する知識が必要になりますが、使いこなせると非常に強力なツールとなるので学習するメリットは大きいと考えています。
公式から以下のテンプレートリポジトリが公開されているので、最初は clone するだけで使い始めることができます。
また、SRE チームでは複数のカスタムアクションや Composite Action を運用しており、それぞれ独立したリポジトリを作ると認知負荷につながってしまうため、npm workspaces を使って1つのリポジトリに集約しています。
sre-actions
├── codeowners-validator
├── manifest-analyzer
├── terraform-lockfile-checker
├── ...
├── package.json
├── tsconfig.json
└── ...
事例紹介
最後にこれらの Tips を活用した弊社 SRE チームの事例をいくつか紹介します。
terraform-lockfile-checker
Terraform の lockfile (.terraform.lock.hcl
)をコミットしているかチェックするためのアクションです。変更があったディレクトリを tj-actions/changed-files
アクションで取得し、その中に lockfile が含まれていない場合はコメントで警告を出しています。
codeowners-validator
CODEOWNERS に指定できるのはユーザーまたはチームであり、GitHub App を Bot ユーザーとして指定することはできません。
また、Renovate による automerge 機能は renovate-approve
という GitHub App で自動的にレビューを通すことで実現されています。
これらの仕様により、GitHub 側で CODEOWNERS によるレビューを必須にすると、Renovate の automerge が機能しなくなるという課題があります。
このアクションは上記の課題を解決するために、CODEOWNERS に GitHub App も指定できるようにして renovate-approve
がレビューを行ったかどうか検証します。これを必須のステータスチェックとすることで Renovate の automerge が機能するようにしています。
実装としては、CODEOWNERS ファイルの解析や判定ロジックを記述するために TypeScript でカスタムアクションを定義しています。
manifest-analyzer
こちらは Kubernetes マニフェスト用のアクションです。Kustomize を展開して差分を可視化しつつ、各種静的解析を実行しています。
詳細については以下の記事にまとめていますが、複数のリポジトリから呼び出されることを前提に Composite Action として作られています。
まとめ
今後は GitHub Actions や GitHub Apps をさらに活用して、開発者にゴールデンパスを提供する Bot を開発したり、静的解析をさらに充実させたりしていきたいと考えています。
ここまで GitHub Actions を活用した取り組みを紹介しましたが、少しでも参考になることがあれば幸いです。
Discussion