Google Cloudコスト増減の定期監視とアドバイスを行うCostChecker-Agentの紹介

Sreake事業部

2024.1.25

1. はじめに

はじめまして、Sreake事業部インターン生の井上です。私はSreake事業部にてSRE技術の調査と研究を行う目的で2023年3月6日から長期インターン生として参加しています。

この記事では、”PaLM API for textで作るGoogle Cloudコストチェッカー“を基に開発した「CostChecker-Agent」について紹介します。「CostChecker-Agent」はGoogle Cloudのコストの増減について定期的に監視を行い、LLMがレポートを作成します。

2. CostChecker-Agentとは?

上記はCostChecker-Agentのコンセプト図です。CostChecker-Agent自体はDocker imageを利用して構築しています。Github Actionを利用する事で、Google Cloudのコストの増減について定期的に監視を行い、LLMがレポートを作成します。Github Actionを利用する事で、レポートが定期的に取得出来ます。更にローカルでの実行もサポートしています。2023年11月7日現在、CostChecker-Agentで使用するLLMに、OpenAI社のChatGPTと、Google CloudのVertex AI(PaLM2)をサポートしています。

3. CostChecker-Agentの動作

CostChecker-Agentのコードや仕組みは、”PaLM API for textで作るGoogle Cloudコストチェッカー“で説明されているので詳細を割愛します。CostChecker-Agent自体はDocker Imageで構築されています。CostChecker-Agentは、Big Queryからコスト情報を抽出して、LLMを使い、コスト増減に対するアドバイスを生成しています。また、GItHub Actionには、Docker imageを動作させる機能や、GitHub Container Registry(ghcr.io)が備わっています。これらの機能を利用する事で、ユーザは自身のリポジトリにGitHub Actionの定義ファイル(.yaml)を配置と環境変数を設定する事で利用できます。

以下はcostchecker-agentをGitHub Action上で動作させるためのyamlです。こちらから取得できます。costchecker-agentのimageの最新版を3-shakeのリポジトリから取得する他、エラーについてもissueに投稿されます。実行時にRepository secretsを設定する必要があります。また、Settings > Actions > General > Workflow permissionsのRead repository contents permissionをRead and write permissionsに設定する必要があります。

name: costchecker-agent

on:
  workflow_dispatch:
  schedule:
    # 毎週月曜
    - cron: '0 0 * * 1'


jobs:
  run_and_report:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Login to GHCR
        env:
          GHCR_PAT: ${{ secrets.GHCR_PAT }}
        run: |
          if [ ! -z "$GHCR_PAT" ]; then
            echo "$GHCR_PAT" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          fi

      - name: Pull Docker Image
        run: docker pull ghcr.io/3-shake/costchecker-agent/costchecker-agent:latest

      - name: Run Docker Image
        continue-on-error: true
        run: |
          docker run --env GCP_SA_JSON_KEY='${{ secrets.GCP_SA_JSON_KEY }}' \
                      --env GCP_PROJECT_NAME="${{ secrets.GCP_PROJECT_NAME }}" \
                      --env GCP_TABLE_NAME="${{ secrets.GCP_TABLE_NAME }}" \
                      --env GCP_DATASET="${{ secrets.GCP_DATASET }}" \
                      --env OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY }}" \
                      --env OPENAI_ORGANIZATION_ID="${{ secrets.OPENAI_ORGANIZATION_ID }}" \
                      --env LANGUAGE="${{ secrets.LANGUAGE }}" \
                      --env LLM_MODEL="${{ secrets.LLM_MODEL }}" \
                      ghcr.io/3-shake/costchecker-agent/costchecker-agent:latest > output.txt 2>error.txt

      - name: Create issue
        run: |

          REPO_OWNER=$(echo ${{ github.repository }} | cut -d'/' -f1)
          REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f2)
          API_URL="https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/issues"

          if [[ ! -s error.txt ]]; then
            ISSUE_TITLE="Weekly Report for Week $(date +'%U') of $(date +'%B %Y')"
            ISSUE_BODY=$(<output.txt)
          else
            ISSUE_TITLE="ERROR: $(sed '2!d' error.txt)"
            ISSUE_BODY=$(<error.txt)
          fi

          ISSUE_DATA=$(jq -n \
                        --arg title "$ISSUE_TITLE" \
                        --arg body "$ISSUE_BODY" \
                        '{title: $title, body: $body}')

          curl -X POST \
              -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
              -H "Accept: application/vnd.github.v3+json" \
              $API_URL \
              -d "$ISSUE_DATA"

      - name: Check for Errors
        run: |
          if [[ -s error.txt ]]; then
            echo "Error detected in costchecker-agent."
            cat error.txt
            exit 1
          fi

Repository secrets

    GCP_SA_JSON_KEY=<サービスアカウントのキーファイルの内容を張り付ける>
    GCP_PROJECT_NAME=<YOUR ENV>
    GCP_DATASET=<YOUR ENV>
    GCP_TABLE_NAME=<YOUR ENV>
    OPENAI_API_KEY=<YOUR ENV>
    OPENAI_ORGANIZATION_ID=<YOUR ENV>
    LLM_MODEL=PaLM2
    LANGUAGE=Japanese

以下はローカルで実行するためのDockerコマンドです。

docker run --rm \
--name costchecker-agent \
-v $(pwd)/key.json:/root/key.json \
-e GOOGLE_APPLICATION_CREDENTIALS=/root/key.json \
-e GCP_PROJECT_NAME=<YOUR ENV> \
-e GCP_DATASET=<YOUR ENV> \
-e GCP_TABLE_NAME=<YOUR ENV> \
-e OPENAI_API_KEY=<YOUR ENV> \
-e OPENAI_ORGANIZATION_ID=<YOUR ENV> \
-e LLM_MODEL=PaLM2 \
-e LANGUAGE=Japanese \
ghcr.io/3-shake/costchecker-agent/costchecker-agent:latest > output.txt 2>&1

勿論、Docker Composeでも利用可能です。

services:
costchecker-agent:
    image: ghcr.io/3-shake/costchecker-agent/costchecker-agent:latest
    volumes:
		# サービスアカウントのキーファイル
    - ./key.json:/root/key.json
    environment:
    - GOOGLE_APPLICATION_CREDENTIALS=/root/key.json
    - GCP_PROJECT_NAME=<YOUR ENV>
    - GCP_DATASET=<YOUR ENV>
    - GCP_TABLE_NAME=<YOUR ENV>
    - OPENAI_API_KEY=<YOUR ENV>
    - OPENAI_ORGANIZATION_ID=<YOUR ENV>
    - LLM_MODEL=PaLM2
    - LANGUAGE=Japanese

4. GitHub Actionで考慮すべき事

costchecker-agentの開発中、GitHub Actionのスケジュール機能を使う上で、”最後にリポジトリに活動を行った60日後に処理が停止する”という問題がある事が分かりました。この問題を解決するため、月一で無駄なコミットを送るというYAMLを作成しました。

monthly-timestamp-update.yml 初回実行時は”GithubActionTimeStamp”という空のブランチを作成して、timestamp.txtにタイムスタンプを書き込みます。二回目以降は、タイムスタンプの書き込みのみが行われます。

name: Monthly Timestamp Update

on:
  workflow_dispatch:
  schedule:
    - cron: '0 0 1 * *'

jobs:
  check-and-update-timestamp:
    runs-on: ubuntu-latest

    steps:
      - name: Check out the repository
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Set up Git identity
        run: |
          git config --global user.name 'github-actions-bot'
          git config --global user.email 'github-actions-bot@users.noreply.github.com'

      - name: Check for GithubActionTimeStamp branch
        run: |
          if git branch --list GithubActionTimeStamp; then
            echo "GithubActionTimeStamp branch already exists."
          else
            echo "Creating GithubActionTimeStamp branch."
            git checkout --orphan GithubActionTimeStamp
            git rm -rf .
            touch .gitkeep
            git add .gitkeep
            git commit -m "Initial commit on GithubActionTimeStamp branch"
            git push origin GithubActionTimeStamp
          fi

      - name: Update timestamp
        run: |
          git fetch --all
          git checkout GithubActionTimeStamp || git checkout -b GithubActionTimeStamp
          date +'%Y-%m-%d %H:%M:%S' > timestamp.txt
          git add timestamp.txt
          git commit -m "Update timestamp"
          git push origin GithubActionTimeStamp

5. 実際に動作させた結果

GitHub Actionを利用すると以下のようなレポートが作成されます。

7. おまけ(CI/CD)

costchecker-agentの開発で、GitHub Container Registry(ghcr.io)を利用しました。以下は開発で使用したGitHub ActionのYAMLです。mainブランチにプロジェクトがpushされるとDockerfileを基にイメージをBuildしてPushします。

name: Build and Push Docker Image

on:
    workflow_dispatch:
    push:
        branches:
        - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v3
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ghcr.io/${{ github.repository }}/costchecker-agent:latest

6. まとめ

今回の開発では、costchecker-agentの開発を通して、GitHub Actionを利用した定期実行という新しい概念を学ぶ事が出来ました。また、mainブランチにpushされたプロジェクトを、GitHub Actionを利用してGitHub Container Registry(ghcr.io)にPushするCI/CDの初歩も実践的に学ぶ事が出来ました。

7. おわりに

本記事では、”PaLM API for textで作るGoogle Cloudコストチェッカー“を基に開発した「CostChecker-Agent」について紹介しましました。また、GitHub Actionのスケジュールを長期的に使用する上での憂慮事項や、得られたナレッジについても説明しました。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ