Site cover image

Site icon image MEGUMI SHIMABUKURO

This blog is all about things I've tried and found out.

Go製のCLIツールを定期実行させたい

はじめに

Go で書いた CLI ツールを定期実行させたかったので、普段よく使ってるGCPの環境で試してみた。Docker で CLI を実行するイメージを作って、CloudRun Job で実行させるようにする。定期実行のタイミングは Github Actions のスケジュールのイベントで指定する。といった感じ。

CLI はサイトのデータをスクレイピングして DB に保存するツールで、この CLI を毎週自動で実行するようにしたかったのがモチベーション。

📝 やること

  1. CLI ツールを Docker 化する(Dockerfile を用意する)
  2. Docker Image をリポジトリ(artifact registry)に登録する
  3. ここまでの操作をCI で自動化する(1 と 2 を CI で自動化する)
  4. Cloud Run Job を使って Docker 化した CLI を定期実行する

1️⃣ CLI ツールを Docker 化する

Dockerfile を用意する。

# Use an official Golang runtime as a parent image
FROM golang:alpine

# Set the working directory inside the container
WORKDIR /app

# Copy the local package files to the container's workspace
COPY . /app

# Build the Go application inside the container
RUN go build -o ageage-collector cmd/ageage-collector/main.go

# Define the command to run your application
ENTRYPOINT ["./ageage-collector"]

ビルドする。

# ビルドする
$ docker build -t shimabukuromeg/ageage-collector:latest .
$ docker images | grep ageage
shimabukuromeg/ageage-collector                                                         latest           f47a90783376   About a minute ago   669MB
# 手元で実行してみる(envの指定はサンプルのCLIの都合)
$ docker run --env DSN="postgresql://<your user name>:<your password>@<your endpoint>:<your port>/<your db name>" shimabukuromeg/ageage-collector

2️⃣ Docker Image をリポジトリ(Artifact Registry)に登録する

なければ、Artifact Registry を作る。

$ gcloud artifacts repositories create myrepo --location=asia-northeast1 --repository-format=docker

ビルドする。

$ docker build ./ -t asia-northeast1-docker.pkg.dev/${PROJECT_ID}/myrepo/ageage-collector

プッシュする。

$ docker push asia-northeast1-docker.pkg.dev/${PROJECT_ID}/myrepo/ageage-collector

作られた。

Image in a image block

3️⃣ ここまでの操作をCI で自動化する

手元のローカルの環境でビルドとプッシュを試したが、CI で自動でできるようにする。

name: Docker Image Build

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:

permissions:
  contents: "read"
  id-token: "write"

env:
  # 例. projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<プールID>/providers/github
  # projects/999999999999/locations/global/workloadIdentityPools/my-pool-id/providers/github
  WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
  SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
  IMAGE_HOST: "asia-northeast1-docker.pkg.dev"
  IMAGE_TAG_BASE: ${{ secrets.IMAGE_TAG_BASE }} # 例. プロジェクトID/artifact_registry名/イメージ名

jobs:
  build-push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - id: "auth"
        name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ env.SERVICE_ACCOUNT }}

      - name: "Set up Cloud SDK"
        uses: "google-github-actions/setup-gcloud@v1"

      - name: Docker Setup
        id: buildx
        uses: docker/setup-buildx-action@v2

      - name: Authorize Docker push
        id: authorize-docker-push
        shell: bash
        run: gcloud auth configure-docker ${{ env.IMAGE_HOST }}

      - name: Docker Image Build and Push
        id: docker-build
        uses: docker/build-push-action@v4
        with:
          push: true
          provenance: false
          tags: "${{ env.IMAGE_HOST }}/${{ env.IMAGE_TAG_BASE }}:latest"
          build-args: |
            APP_VERSION=${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

secrets の秘匿情報を github 側に追加して、workflowの定義から読み込めるようにする。

Image in a image block

CI を実行して成功したらOK

Image in a image block

4️⃣ Cloud Run Job を使って CLI を定期実行する

.github/workflows/exec-ageage-collector.yml を作成してワークフローを定義する。

name: Exec Ageage Collector

on:
  push:
    tags:
      - "v*"
  workflow_dispatch: # 手動実行用
  schedule:
    - cron: "0 23 * * SUN" # 日曜日の23時に実行

permissions:
  contents: "read"
  id-token: "write"

env:
  # 例. projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<プールID>/providers/github
  # projects/999999999999/locations/global/workloadIdentityPools/my-pool-id/providers/github
  WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
  SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
  RUN_SERVICE_ACCOUNT: ${{ secrets.RUN_SERVICE_ACCOUNT }}
  IMAGE_HOST: "asia-northeast1-docker.pkg.dev"
  IMAGE_TAG_BASE: ${{ secrets.IMAGE_TAG_BASE }} # 例. プロジェクトID/artifact_registry名/イメージ名
  GCP_REGION: asia-northeast1

jobs:
  exec-cli:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - id: "auth"
        name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ env.SERVICE_ACCOUNT }}

      - name: "Set up Cloud SDK"
        uses: "google-github-actions/setup-gcloud@v1"

      - name: Exec Ageage Collector
        id: ageage-collector
        run: |
          gcloud config set run/region $GCP_REGION
          set +e
          gcloud run jobs describe ageagecli --quiet >/dev/null
          job_exists_check=$?
          set -e
          if [ $job_exists_check -eq 0 ]; then
          jobs_cmd="update"
          else
          jobs_cmd="create"
          fi
          echo ": $jobs_cmd a job"
          gcloud run jobs $jobs_cmd ageagecli --image="${{ env.IMAGE_HOST }}/${{ env.IMAGE_TAG_BASE }}:latest" --max-retries=1 --service-account="${{ env.RUN_SERVICE_ACCOUNT }}" --set-secrets="DSN=DATABASE_URL:latest"
          gcloud run jobs execute ageagecli --wait --format=json

成功

Image in a image block

参考