GitHub Actions の Self-hosted runners を使って Google Cloud 環境に Terraform を実行する方法

記事タイトルとURLをコピーする

G-gen の武井です。 当記事では GitHub Actions の Self-hosted runners (セルフホストランナー) を使って Google Cloud (旧称 GCP) 環境に Terraform を実行する方法を紹介します。

GitHub Actions

はじめに

GitHub Actions の概要や実行方法については以下の記事で解説しています。

一読いただくと後述の理解が深まりますで是非ご参照ください。

blog.g-gen.co.jp

実行環境について

概要

GitHub Actions では、ワークフローの実行環境を ランナー と呼びます。

ランナーの種類

ランナーは以下の2種類から選択可能です。

# 種類 特徴
1 GitHub-hosted runners GitHub 側で用意される実行環境。ユーザーによる管理は不要だが条件次第で課金が発生
2 Self-hosted runners ユーザーが用意する実行環境。管理は必要だが利用料金は不要で HW/SW 構成も自由

ランナーの特徴

各ランナーの特徴について整理します。

項目 GitHub-hosted Self-hosted
管理 GitHub が管理 ユーザーが管理
構成自由度 小さい (契約プランに依存) 大きい
料金 パブリックリポジトリ:無料
プライベートリポジトリ:従量課金型 (無料枠あり)
無料

管理

前者の場合、ランナーの管理は GitHub 側で行われますが、後者の場合はユーザー側に責任が生じるため管理に伴う工数やコストが発生します。

参考:GitHub ホステッド ランナーの概要
参考:セルフホステッド ランナーの概要

構成自由度

前者の場合、ハードウェアやソフトウェア仕様の選択は契約プランに依存しますが、後者の場合はありません。ジョブの規模などに応じて柔軟な調整が可能です。

参考:サポートされているランナーとハードウェアリソース

料金

前者の場合、パブリックリポジトリにおけるランナーの利用は無料ですが、プライベートリポジトリにおけるランナーの利用については契約プランに応じた無料の使用時間 (分) とストレージ (サイズ) が各アカウントに付与されます。無料枠を超過した場合に使用時間による従量課金が発生します。

ランナーの使用時間は毎月リセットされますが、ストレージはリセットされません。契約プランごとの無料枠や費用計算、その他詳細は以下をご確認ください。

参考:GitHub Actions の課金について
参考:GitHub 料金計算ツール

その他

上記の他にも両者の仕様上の違いがありますので詳細は以下をご確認ください。

参考:セルフホスト ランナーと GitHub ホステッド ランナーの違い

セルフホストランナーを使った Terraform の実行方法

では実際にセルフホストランナーを使用して Google Cloud 環境に Terraform を実行する方法をご紹介していきます。

セルフホストランナー用 VM マシンの準備

以下は Terraform から VM マシン (OS : Debian11) と関連リソースをデプロイする場合のソースです。ご利用される場合、リソース名や変数をご自身の環境に合わせて置き換えてください。

# vpc
resource "google_compute_network" "runner_vpc" {
  project                 = var.terraform_project_id
  name                    = "runner-vpc"
  auto_create_subnetworks = false
  mtu                     = 1460
}
  
# subnet
resource "google_compute_subnetwork" "runner_subnet" {
  name          = "runner-subnet"
  ip_cidr_range = "192.168.10.0/24"
  region        = "asia-northeast1"
  network       = google_compute_network.runner_vpc.id
  project       = var.terraform_project_id
  private_ip_google_access  = true
  
  log_config {
    aggregation_interval = "INTERVAL_5_MIN"
    flow_sampling        = 0.5
    metadata             = "EXCLUDE_ALL_METADATA"
  }
}
  
# firewall
resource "google_compute_firewall" "allow_ssh {
  name        = "allow-ssh"
  network     = google_compute_network.runner_vpc.name
  project     = var.terraform_project_id
  priority    = "100"
  direction   = "INGRESS"
  description = "Cloud IAP の SSH 通信を許可"

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }

  log_config {
    metadata = "EXCLUDE_ALL_METADATA"
  }

  source_ranges = ["35.235.240.0/20"]
}
  
# compute engine
resource "google_compute_instance" "runner" {
    name            = "runner-01"
    machine_type    = "e2-micro"
    zone            = "asia-northeast1-b"
    project         = var.terraform_project_id
        
    boot_disk {
        initialize_params {
            image   = "debian-cloud/debian-11"
            size    = "10"
            type    = "pd-balanced"
            labels  = {
                instance = "runner-01"   
            }
        }
    }  
  
    network_interface {
        subnetwork          = google_compute_subnetwork.runner_subnet.name
        subnetwork_project  = var.terraform_project_id
        network_ip          = "192.168.10.11"
        access_config {}
    }  
  
    service_account {
        email  = "${TERRAFORM_SERVICE_ACCOUNT}" // サービスアカウント
        scopes = ["cloud-platform"]
    }
}

参考:

セルフホストランナー用 VM マシンのセットアップ

設定用コマンドの取得

以下の手順でコマンドを取得します。

  1. GitHub リポジトリ にログイン
  2. Settings > Actions > Runners の順に画面を遷移
  3. [New self-hosted runner] をクリック
  4. [Runner image]Linux[Architecture]x64 に選択、表示されるコマンドを取得

上記手順を実行して表示される設定用コマンド

参考:リポジトリへのセルフホストランナーの追加

設定用コマンドの実行

一般ユーザで実行する場合

先程取得した設定用コマンドを VM マシンにログインして OS 上で実行します。

config.sh の実行コマンドで以下のエラーが表示された場合、エラーメッセージ内の依存関係解消用のコマンド (sudo ./bin/installdependencies.sh) を実行した後にリトライしてください。

$ ./config.sh --url https://github.com/{OWNER_NAME}/{REPOSITORY_NAME} --token {VALUE}
  
Libicu's dependencies is missing for Dotnet Core 6.0
Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.

セルフホストランナーが GitHub に紐付きプロセスが正常に起動すると Connected to GitHub と表示されます。

ランナープロセスが起動した状態

また、GitHub の画面には登録されたセルフホストランナーのマシン名が表示されます。

GitHub 側の画面でも確認可能

root で実行する場合

root にスイッチ (sudo su -) した後に先程取得した設定コマンドを実行しても config.sh の実行コマンドがエラーとなります。

以下のように変数 (RUNNER_ALLOW_RUNASROOT) を設定しつつコマンドを実行します。その際、依存関係を示すエラーが表示された場合は前述と同じように対応します。

# RUNNER_ALLOW_RUNASROOT="1" ./config.sh --url https://github.com/{OWNER_NAME}/{REPOSITORY_NAME} --token {VALUE}

変数を設定する理由としては、config.sh を実行したユーザの UID が "0" (root の UID は "0") の場合はプログラムが終了する作りになっているためです。

#!/bin/bash
  
user_id=`id -u`
  
# we want to snapshot the environment of the config user
if [ $user_id -eq 0 -a -z "$RUNNER_ALLOW_RUNASROOT" ]; then
    echo "Must not run with sudo"
    exit 1
fi

プロセスの自動起動設定

取得したコマンドではセルフホストランナーのプロセスを ./run.sh コマンドで手動起動する前提になっています。

プロセスを自動起動させたい場合は以下のリンク先に記載の設定を実行してください。

参考:セルフホストランナーアプリケーションをサービスとして設定する

定義ファイルの作成

セルフホストランナーを使用する場合、15行目に記載のある通りジョブ定義の中で runs-on: self-hosted と指定するだけです。

name: terraform
  
# main ブランチへの Pull request と Merge
on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main
  
# ジョブ (Self-hosted runners で実行)
jobs:
  terraform-workflow:
    runs-on: self-hosted
  
    permissions:
      id-token: write
      contents: read
      pull-requests: write
  
    # main.tf のあるディレクトリを指定
    strategy:
      matrix:
        tf_working_dir:
          - ./env/yutakei
  
    steps:
      - uses: actions/checkout@v3
        name: Checkout
        id: checkout
  
      # https://github.com/marketplace/actions/setup-tfcmt      
      - uses: shmokmt/actions-setup-tfcmt@v2
        name: Setup tfcmt
  
      # https://github.com/marketplace/actions/setup-github-comment
      - uses: shmokmt/actions-setup-github-comment@v2
        name: Setup github-comment
        
      # https://github.com/actions/setup-node
      # https://github.com/hashicorp/setup-terraform/issues/84
      - uses: actions/setup-node@v3
        with:
          node-version: '16'
  
      - uses: hashicorp/setup-terraform@v2
        name: Setup terraform
  
      - name: Terraform fmt
        id: fmt
        run: |
          cd ${{ matrix.tf_working_dir }}
          terraform fmt -recursive
        continue-on-error: true
      
      - name: Terraform Init
        id: init
        run: |
          cd ${{ matrix.tf_working_dir }}
          terraform init -upgrade
  
      - name: Terraform Validate
        id: validate
        run: |
          cd ${{ matrix.tf_working_dir }}
          terraform validate
  
      # main ブランチへの pull request した際のみ terraform plan を実行
      - name: Terraform Plan
        id: plan
        if: github.event_name == 'pull_request'
        run: |
          cd ${{ matrix.tf_working_dir }}
          export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
          tfcmt -var target:${{ matrix.tf_working_dir }} plan -- terraform plan --parallelism=50
          github-comment hide -condition 'Comment.Body contains "No changes."'
        continue-on-error: true
  
      # terraform status で失敗した際に workflow を停止
      - name: Terraform Plan Status
        id: status
        if: steps.plan.outcome == 'failure'
        run: exit 1
  
      # main ブランチへの push した際のみ terraform apply を実行
      - name: Terraform Apply
        id: apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: |
          cd ${{ matrix.tf_working_dir }}
          export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
          tfcmt -var target:${{ matrix.tf_working_dir }} apply -- terraform apply -auto-approve -input=false --parallelism=50
  

ワークフローの実行

定義ファイルに記載のトリガー条件を満たすとセルフホストランナーによってワークフローが実行されます。

  • 所定のブランチに Pull request を実行すると tarraform plan が実行
  • 所定のブランチに Merge を実行すると tarraform apply が実行

Pull request (terraform plan) と Merge (terraform apply) に関するワークフロー

セルフホストランナーにて実行されたことがわかる

武井 祐介 (記事一覧)

クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア

Google Cloud Partner Top Engineer 2024 に選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。

趣味はロードバイク、ロードレースやサッカー観戦です。