Skip to content
Published on

GitHub Actions CI/CD完全攻略:Goビルドからブランチ戦略、自動デプロイまで

Authors

Part 1: GitHub Actionsアーキテクチャ

1.1 コアコンセプト

GitHub Actionsは、GitHubに深(ふか)く統合(とうごう)されたCI/CDプラットフォームです。外部(がいぶ)CIツールとは異(こと)なり、Pull Request、Issue、Release、そしてGitHubエコシステム全体(ぜんたい)へのネイティブアクセスを持(も)ちます。

アーキテクチャは5つの基本(きほん)プリミティブで構成(こうせい)されています。

  • Workflow(ワークフロー) -- .github/workflows/配下(はいか)のYAMLファイルで、自動化(じどうか)プロセスを定義(ていぎ)する
  • Event(イベント) -- ワークフローを起動(きどう)するトリガー(push、pull_request、schedule、workflow_dispatchなど)
  • Job(ジョブ) -- 同(おな)じランナー上(じょう)で実行(じっこう)されるステップの集合(しゅうごう)、デフォルトで並列(へいれつ)実行
  • Step(ステップ) -- ジョブ内(ない)の個々(ここ)のタスク、シェルコマンドまたはアクション
  • Runner(ランナー) -- ジョブが実行される仮想(かそう)または物理(ぶつり)マシン
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

concurrency:
  group: ci-BRANCH_REF
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: echo "Running tests..."

1.2 イベントタイプとトリガー

GitHub Actionsは35以上(いじょう)のイベントタイプをサポートしています。最(もっと)もよく使(つか)われるものは以下の通りです。

イベント説明ユースケース
pushブランチへのコードプッシュmain/developでのCIビルド
pull_requestPRのオープン/更新/クローズコードレビュー自動化
scheduleCronベースのトリガーナイトリービルド、クリーンアップ
workflow_dispatch手動トリガーオンデマンドデプロイ
releaseリリース公開本番デプロイメント
repository_dispatch外部Webhookクロスリポジトリトリガー
workflow_call別ワークフローからの呼び出し再利用可能ワークフロー

パスとブランチによるイベントフィルタリング:

on:
  push:
    branches:
      - main
      - 'release/**'
    paths:
      - 'src/**'
      - 'go.mod'
      - 'go.sum'
    paths-ignore:
      - '**.md'
      - 'docs/**'

Cron式(しき)によるスケジュール:

on:
  schedule:
    # 平日のUTC 2:00に実行
    - cron: '0 2 * * 1-5'

1.3 並行制御(へいこうせいぎょ)

Concurrencyグループは重複(ちょうふく)するワークフロー実行を防止(ぼうし)し、コンピューティングリソースを節約(せつやく)します。

concurrency:
  group: deploy-production
  cancel-in-progress: false  # 進行中のデプロイはキャンセルしない

# PRビルドの場合、同じPRの前回の実行をキャンセル
concurrency:
  group: pr-build-PR_NUMBER
  cancel-in-progress: true

1.4 ランナータイプ

ランナーvCPURAMストレージコスト(分)
ubuntu-latest416 GB14 GB SSD$0.008
ubuntu-latest-xl (4x)1664 GB150 GB SSD$0.032
macos-latest3 (M1)7 GB14 GB SSD$0.08
windows-latest27 GB14 GB SSD$0.016
self-hostedカスタムカスタムカスタム無料(自前インフラ)

1.5 必須(ひっす)アクション Top 20

最も有用(ゆうよう)なコミュニティおよび公式(こうしき)アクション:

# 1. コードのチェックアウト
- uses: actions/checkout@v4

# 2. Goのセットアップ
- uses: actions/setup-go@v5
  with:
    go-version: '1.22'
    cache: true

# 3. 依存関係のキャッシュ
- uses: actions/cache@v4
  with:
    path: ~/go/pkg/mod
    key: go-mod-HASH_OF_GO_SUM

# 4. アーティファクトのアップロード
- uses: actions/upload-artifact@v4
  with:
    name: binary
    path: ./bin/

# 5. アーティファクトのダウンロード
- uses: actions/download-artifact@v4

# 6. Dockerビルドとプッシュ
- uses: docker/build-push-action@v6
  with:
    push: true
    tags: myapp:latest

# 7. コンテナレジストリへのログイン
- uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: GITHUB_ACTOR
    password: GITHUB_TOKEN_SECRET

# 8. Docker Buildxのセットアップ
- uses: docker/setup-buildx-action@v3

# 9. GitHubリリース
- uses: softprops/action-gh-release@v2

# 10. Slack通知
- uses: slackapi/slack-github-action@v2

# 11. Node.jsのセットアップ
- uses: actions/setup-node@v4

# 12. Pythonのセットアップ
- uses: actions/setup-python@v5

# 13. golangci-lint
- uses: golangci/golangci-lint-action@v6

# 14. CodeQL分析
- uses: github/codeql-action/analyze@v3

# 15. K8sへのデプロイ
- uses: azure/k8s-deploy@v5

# 16. AWS認証情報
- uses: aws-actions/configure-aws-credentials@v4

# 17. GCP認証
- uses: google-github-actions/auth@v2

# 18. Terraform
- uses: hashicorp/setup-terraform@v3

# 19. PRコメント作成
- uses: peter-evans/create-or-update-comment@v4

# 20. PRラベル
- uses: actions/labeler@v5

1.6 シークレット管理(かんり)とOIDC

リポジトリシークレットと環境(かんきょう)シークレット:

シークレットは暗号化(あんごうか)され、ワークフローにのみ公開(こうかい)されます。ログでは自動的(じどうてき)にマスクされます。

jobs:
  deploy:
    environment: production
    steps:
      - name: Deploy
        env:
          API_KEY: SECRETS_API_KEY_REF
          DB_PASSWORD: SECRETS_DB_PASSWORD_REF
        run: ./deploy.sh

注:上記(じょうき)のYAMLでは、SECRETS_API_KEY_REFSECRETS_DB_PASSWORD_REFはリポジトリ設定(せってい)で構成されたGitHubシークレットへの参照(さんしょう)を表します。

クラウドプロバイダー認証(にんしょう)のためのOIDC(静的認証情報不要):

jobs:
  deploy-aws:
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions
          aws-region: us-east-1

OIDCはGitHubとクラウドプロバイダー間(かん)の信頼(しんらい)関係を確立(かくりつ)することで、長期間有効(ゆうこう)な認証情報を排除(はいじょ)します。ワークフローは自動的にスコープされ、ローテーションされる短期トークンを要求(ようきゅう)します。

1.7 環境とデプロイメント保護(ほご)

環境を使用すると、デプロイメントの保護ルールを定義できます。

jobs:
  deploy-staging:
    environment: staging
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh staging

  deploy-production:
    needs: deploy-staging
    environment:
      name: production
      url: https://myapp.example.com
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh production

環境保護ルールには以下が含(ふく)まれます:

  • 必須レビュアー(最大6名)
  • 待機(たいき)タイマー(デプロイ前の遅延)
  • ブランチ制限(mainのみproductionにデプロイ可能)
  • カスタムデプロイメントブランチポリシー

Part 2: GoプロジェクトCI/CD

2.1 Goビルドの基礎(きそ)

Goはランタイム依存関係(いぞんかんけい)のない静的(せいてき)バイナリにコンパイルされるため、コンテナ化されたデプロイメントに最適(さいてき)です。本番(ほんばん)ビルドの主要(しゅよう)フラグ:

# すべての最適化を有効にした本番ビルド
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
  go build \
  -ldflags="-s -w -X main.version=1.2.3 -X main.commit=abc123 -X main.buildTime=2026-03-23T10:00:00Z" \
  -trimpath \
  -o ./bin/myapp \
  ./cmd/server/

ビルドフラグの説明(せつめい):

フラグ目的
CGO_ENABLED=0完全な静的バイナリのためcgoを無効化
-ldflags="-s -w"デバッグ情報を削除、バイナリサイズ約30%削減
-ldflags="-X main.version=..."ビルド時にバージョン情報を注入
-trimpathバイナリからローカルファイルシステムパスを除去
-raceレースディテクタ有効化(テスト専用、本番には使用しない)
-coverカバレッジ計測付きビルド

2.2 クロスコンパイルマトリックス

Goはクロスコンパイルに優(すぐ)れています。単一(たんいつ)のCIジョブで複数(ふくすう)プラットフォーム向けにビルドできます。

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        goos: [linux, darwin, windows]
        goarch: [amd64, arm64]
        exclude:
          - goos: windows
            goarch: arm64
      fail-fast: false

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'

      - name: Build
        env:
          GOOS: matrix-goos-value
          GOARCH: matrix-goarch-value
        run: |
          BINARY_NAME="myapp-matrix-goos-value-matrix-goarch-value"
          if [ "matrix-goos-value" = "windows" ]; then
            BINARY_NAME="myapp-matrix-goos-value-matrix-goarch-value.exe"
          fi
          go build -ldflags="-s -w" -trimpath -o "./dist/BINARY_NAME_VAR" ./cmd/server/

      - uses: actions/upload-artifact@v4
        with:
          name: binary-matrix-goos-value-matrix-goarch-value
          path: ./dist/

注:上記のマトリックスビルドでは、matrix-goos-valuematrix-goarch-valueはstrategy matrix設定から動的(どうてき)に解決(かいけつ)される値を表しています。

2.3 テスト戦略(せんりゃく)

包括的(ほうかつてき)なGoテストパイプラインにはユニットテスト、統合テスト、ベンチマークが含まれます。

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'

      - name: カバレッジ付きユニットテスト
        run: |
          go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
          go tool cover -func=coverage.out

      - name: 統合テスト
        env:
          DATABASE_URL: postgres://test:test@localhost:5432/testdb?sslmode=disable
        run: |
          go test -v -tags=integration -count=1 ./tests/integration/...

      - name: ベンチマークテスト
        run: |
          go test -bench=. -benchmem -run=^$ ./... | tee benchmark.txt

      - name: カバレッジアップロード
        uses: codecov/codecov-action@v4
        with:
          files: ./coverage.out
          token: CODECOV_TOKEN_REF

2.4 リンティングと静的解析(かいせき)

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'

      - name: golangci-lint
        uses: golangci/golangci-lint-action@v6
        with:
          version: v1.57
          args: --timeout=5m

      - name: go vet
        run: go vet ./...

      - name: staticcheck
        uses: dominikh/staticcheck-action@v1
        with:
          version: '2024.1'

      - name: govulncheck
        run: |
          go install golang.org/x/vuln/cmd/govulncheck@latest
          govulncheck ./...

golangci-lint設定(.golangci.yml):

run:
  timeout: 5m
  go: '1.22'

linters:
  enable:
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - unused
    - bodyclose
    - gocritic
    - gofumpt
    - gosec
    - misspell
    - prealloc
    - revive
    - unconvert

linters-settings:
  gocritic:
    enabled-checks:
      - nestingReduce
      - truncateCmp
      - unnamedResult
  gosec:
    excludes:
      - G104 # 未処理エラー
  revive:
    rules:
      - name: unexported-return
        disabled: true

issues:
  exclude-rules:
    - path: _test\.go
      linters:
        - gosec
        - errcheck

2.5 Goのキャッシュ戦略

効果的(こうかてき)なキャッシュにより、Goビルド時間を60-80%短縮(たんしゅく)できます。

steps:
  - uses: actions/setup-go@v5
    with:
      go-version: '1.22'
      cache: true # 組み込みモジュールキャッシュ

  # 高速コンパイルのための追加ビルドキャッシュ
  - uses: actions/cache@v4
    with:
      path: |
        ~/.cache/go-build
        ~/go/pkg/mod
      key: go-build-OS-HASH_OF_ALL_GO_FILES
      restore-keys: |
        go-build-OS-

キャッシュサイズ比較(ひかく):

キャッシュタイプ一般的なサイズ節約時間
Goモジュールキャッシュ100-500 MB30-60秒
Goビルドキャッシュ200-800 MB45-120秒
Dockerレイヤーキャッシュ500 MB-2 GB60-300秒
golangci-lintキャッシュ50-200 MB20-40秒

2.6 マルチステージDockerビルド

# ステージ1: ビルド
FROM golang:1.22-alpine AS builder

RUN apk add --no-cache git ca-certificates tzdata

WORKDIR /app

# 依存関係のキャッシュ
COPY go.mod go.sum ./
RUN go mod download && go mod verify

# ビルド
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build \
    -ldflags="-s -w -X main.version=VERSION_ARG -X main.commit=COMMIT_ARG" \
    -trimpath \
    -o /app/server \
    ./cmd/server/

# ステージ2: ランタイム
FROM scratch

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /app/server /server

EXPOSE 8080

ENTRYPOINT ["/server"]

注:上記のDockerfileでは、VERSION_ARGCOMMIT_ARGはCIパイプラインで実際のバージョンとコミットハッシュに置換(ちかん)されるプレースホルダーです。

Dockerビルドワークフロー:

jobs:
  docker:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: GITHUB_ACTOR_REF
          password: GITHUB_TOKEN_REF

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ghcr.io/OWNER/myapp
          tags: |
            type=sha
            type=ref,event=branch
            type=semver,pattern=v{{version}}
            type=semver,pattern=v{{major}}.{{minor}}

      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: steps-meta-outputs-tags
          labels: steps-meta-outputs-labels
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            VERSION=v1.2.3
            COMMIT=GITHUB_SHA_REF

2.7 GoReleaser統合(とうごう)

GoReleaserはリリースプロセス全体(ビルド、パッケージング、公開)を自動化します。

# .goreleaser.yml
version: 2

before:
  hooks:
    - go mod tidy
    - go generate ./...

builds:
  - id: server
    main: ./cmd/server/
    binary: myapp
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
      - windows
    goarch:
      - amd64
      - arm64
    ldflags:
      - -s -w
      - -X main.version={{.Version}}
      - -X main.commit={{.Commit}}
      - -X main.date={{.Date}}

archives:
  - format: tar.gz
    name_template: 'myapp_{{.Version}}_{{.Os}}_{{.Arch}}'
    format_overrides:
      - goos: windows
        format: zip

dockers:
  - image_templates:
      - 'ghcr.io/OWNER/myapp:{{.Version}}'
      - 'ghcr.io/OWNER/myapp:latest'
    dockerfile: Dockerfile.goreleaser
    build_flag_templates:
      - '--label=org.opencontainers.image.version={{.Version}}'

changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'
      - '^ci:'

release:
  github:
    owner: myorg
    name: myapp

GoReleaserのワークフロー:

name: Release
on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write
  packages: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'

      - uses: goreleaser/goreleaser-action@v6
        with:
          version: '~> v2'
          args: release --clean
        env:
          GITHUB_TOKEN: GITHUB_TOKEN_SECRET_REF

Part 3: ブランチ戦略

3.1 Git Flow

Git Flowは、スケジュールされたリリースを持つプロジェクトに適(てき)した包括的なブランチモデルです。

ブランチ構成:

  • main -- 本番対応(たいおう)コード、バージョン番号(ばんごう)でタグ付け
  • develop -- フィーチャーの統合ブランチ
  • feature/xxx -- 新機能(きのう)のための短期(たんき)ブランチ
  • release/x.y.z -- リリース準備(じゅんび)と安定化(あんていか)
  • hotfix/xxx -- 本番の緊急(きんきゅう)修正(しゅうせい)

フロー:

  1. developからフィーチャーブランチを作成
  2. フィーチャーを開発・テスト
  3. PRを通じてフィーチャーをdevelopにマージ
  4. developからリリースブランチを作成
  5. 安定化、バージョンバンプ、mainとdevelopにマージ
  6. mainブランチにバージョンタグを付与(ふよ)
  7. ホットフィックス:mainからブランチ作成、修正、mainとdevelopにマージ

メリット: 明確(めいかく)な構造、並列開発、リリース管理 デメリット: 複雑、ブランチが多い、高速プロジェクトには遅い

3.2 GitHub Flow

GitHub Flowは継続的(けいぞくてき)デリバリーに焦点(しょうてん)を当てたシンプルなモデルです。

ブランチ構成:

  • main -- 常(つね)にデプロイ可能
  • feature/xxx -- すべての作業はフィーチャーブランチで行う

フロー:

  1. mainからブランチ作成
  2. コミットを追加(ついか)
  3. Pull Requestをオープン
  4. コードレビューとディスカッション
  5. デプロイとテスト(オプションでステージングへ)
  6. mainにマージ

メリット: シンプル、高速、小さなPRを促進(そくしん) デメリット: リリース管理なし、すべてがmainに入る

3.3 Trunk-Based Development

Trunk-Based Developmentはブランチの存続(そんぞく)期間(きかん)を最小化(さいしょうか)し、継続的インテグレーションを重視(じゅうし)します。

ブランチ構成:

  • main(トランク) -- 全員がここにコミット、常にリリース可能
  • 短期フィーチャーブランチ(1-2日以内)
  • オプションのリリースブランチ(修正のチェリーピック用)

フロー:

  1. mainから最新を取得(しゅとく)
  2. 小さな増分(ぞうぶん)的変更を加える
  3. ローカルですべてのテストを実行
  4. mainに直接プッシュ(または非常に短期のPR)
  5. CIが即座(そくざ)に検証(けんしょう)
  6. フィーチャーフラグを通じて継続的にデプロイ

メリット: 最速のフィードバック、マージコンフリクト最小、最もシンプルなモデル デメリット: 強力なCIが必要、フィーチャーフラグが必須、規律(きりつ)あるチームが必要

3.4 戦略比較

観点(かんてん)Git FlowGitHub FlowTrunk-Based
複雑さ高い低い非常に低い
ブランチ寿命数日〜数週間数時間〜数日数時間
リリース頻度スケジュール制継続的継続的
チームサイズ大規模小〜中規模任意のサイズ
CI/CD成熟度要件
フィーチャーフラグ不要オプション必須
マージコンフリクト頻繁時々
最適な用途エンタープライズ、バージョン管理SaaS、Webアプリ高パフォーマンス
デプロイ頻度週次/月次日次1日複数回

3.5 ブランチ保護ルール

# GitHub API/設定によるブランチ保護

# mainブランチの保護
branches:
  main:
    protection:
      required_status_checks:
        strict: true
        contexts:
          - 'ci/lint'
          - 'ci/test'
          - 'ci/build'
      required_pull_request_reviews:
        required_approving_review_count: 2
        dismiss_stale_reviews: true
        require_code_owner_reviews: true
      restrictions:
        users: []
        teams: ['core-maintainers']
      enforce_admins: true
      required_linear_history: true
      allow_force_pushes: false
      allow_deletions: false

3.6 CODEOWNERS

CODEOWNERSファイルは、レビューが自動的にリクエストされる担当者(たんとうしゃ)を定義します。

# .github/CODEOWNERS

# すべてのデフォルトオーナー
* @myorg/backend-team

# Goソースコード
/cmd/ @myorg/go-team
/internal/ @myorg/go-team
/pkg/ @myorg/go-team

# インフラストラクチャ
/.github/ @myorg/devops-team
/deploy/ @myorg/devops-team
/terraform/ @myorg/devops-team
Dockerfile @myorg/devops-team

# ドキュメント
/docs/ @myorg/docs-team
*.md @myorg/docs-team

# API定義
/api/ @myorg/api-team @myorg/backend-team

3.7 リポジトリルールセット

ルールセット(2023年導入(どうにゅう))は従来(じゅうらい)のブランチ保護ルールよりも柔軟(じゅうなん)な保護を提供します。

  • パターンマッチングによる複数ブランチ/タグへの適用
  • 階層化(かいそうか)されたルール(組織レベル + リポジトリレベル)
  • 特定(とくてい)のロールに対するバイパス権限(けんげん)
  • タグ保護
  • 署名(しょめい)付きプッシュ制限
  • メタデータ制限(コミットメッセージ形式、作者メール)
ルールセット設定例:
  名前: production-protection
  対象: "main" および "release/**" に一致するブランチ
  ルール:
    - マージ前にPull Requestを要求(2件の承認)
    - ステータスチェック必須: lint, test, build
    - 署名付きコミットを要求
    - 強制プッシュをブロック
    - リニア履歴を要求
  バイパス: 緊急時のリポジトリ管理者

Part 4: フルパイプライン -- Goマイクロサービス

4.1 プロジェクト構造(こうぞう)

myapp/
  cmd/
    server/
      main.go
  internal/
    handler/
    service/
    repository/
  pkg/
    middleware/
  api/
    openapi.yaml
  deploy/
    k8s/
      deployment.yaml
      service.yaml
      ingress.yaml
    docker/
      Dockerfile
  .github/
    workflows/
      ci.yml
      deploy.yml
      release.yml
    CODEOWNERS
  .golangci.yml
  .goreleaser.yml
  go.mod
  go.sum
  Makefile

4.2 完全なCIワークフロー

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
    paths-ignore:
      - '**.md'
      - 'docs/**'
  pull_request:
    branches: [main]

concurrency:
  group: ci-REF_NAME
  cancel-in-progress: true

env:
  GO_VERSION: '1.22'
  GOLANGCI_LINT_VERSION: v1.57
  REGISTRY: ghcr.io
  IMAGE_NAME: myorg/myapp

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: GO_VERSION_ENV

      - uses: golangci/golangci-lint-action@v6
        with:
          version: GOLANGCI_LINT_VERSION_ENV

      - name: フォーマットチェック
        run: |
          if [ -n "$(gofmt -l .)" ]; then
            echo "フォーマットされていないファイル:"
            gofmt -l .
            exit 1
          fi

      - name: 依存関係の検証
        run: |
          go mod verify
          go mod tidy
          git diff --exit-code go.mod go.sum

  test:
    name: Test
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: GO_VERSION_ENV

      - name: ユニットテスト
        run: |
          go test -v -race -count=1 \
            -coverprofile=coverage.out \
            -covermode=atomic \
            ./...

      - name: 統合テスト
        env:
          DATABASE_URL: postgres://test:test@localhost:5432/testdb?sslmode=disable
          REDIS_URL: redis://localhost:6379
        run: |
          go test -v -tags=integration -count=1 ./tests/integration/...

      - name: カバレッジレポート
        run: |
          COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}')
          echo "合計カバレッジ: COVERAGE_VAR"

      - uses: codecov/codecov-action@v4
        with:
          files: ./coverage.out

  security:
    name: Security Scan
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: GO_VERSION_ENV

      - name: 脆弱性チェック
        run: |
          go install golang.org/x/vuln/cmd/govulncheck@latest
          govulncheck ./...

      - name: ライセンスチェック
        run: |
          go install github.com/google/go-licenses@latest
          go-licenses check ./...

  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [test, security]
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: GITHUB_ACTOR_REF
          password: GITHUB_TOKEN_REF

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: REGISTRY_ENV/IMAGE_NAME_ENV
          tags: |
            type=sha,prefix=
            type=ref,event=branch
            type=ref,event=pr

      - uses: docker/build-push-action@v6
        with:
          context: .
          file: ./deploy/docker/Dockerfile
          push: PUSH_CONDITION
          tags: steps-meta-outputs-tags
          labels: steps-meta-outputs-labels
          cache-from: type=gha
          cache-to: type=gha,mode=max

4.3 デプロイメントワークフロー

# .github/workflows/deploy.yml
name: Deploy

on:
  workflow_run:
    workflows: ['CI']
    branches: [main]
    types: [completed]

concurrency:
  group: deploy-ENVIRONMENT
  cancel-in-progress: false

jobs:
  deploy-staging:
    if: github.event.workflow_run.conclusion == 'success'
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.myapp.example.com
    steps:
      - uses: actions/checkout@v4

      - name: kubectlの設定
        uses: azure/setup-kubectl@v4

      - name: K8sコンテキストの設定
        uses: azure/k8s-set-context@v4
        with:
          method: kubeconfig
          kubeconfig: SECRETS_KUBECONFIG_STAGING_REF

      - name: ステージングへのデプロイ
        run: |
          kubectl set image deployment/myapp \
            myapp=ghcr.io/myorg/myapp:COMMIT_SHA \
            -n staging
          kubectl rollout status deployment/myapp -n staging --timeout=300s

      - name: スモークテスト実行
        run: |
          sleep 10
          curl -sf https://staging.myapp.example.com/healthz || exit 1

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.example.com
    steps:
      - uses: actions/checkout@v4

      - name: kubectlの設定
        uses: azure/setup-kubectl@v4

      - name: K8sコンテキストの設定
        uses: azure/k8s-set-context@v4
        with:
          method: kubeconfig
          kubeconfig: SECRETS_KUBECONFIG_PRODUCTION_REF

      - name: 本番へのデプロイ
        run: |
          kubectl set image deployment/myapp \
            myapp=ghcr.io/myorg/myapp:COMMIT_SHA \
            -n production
          kubectl rollout status deployment/myapp -n production --timeout=300s

      - name: デプロイメントの検証
        run: |
          curl -sf https://myapp.example.com/healthz || exit 1

      - name: 失敗時の通知
        if: failure()
        uses: slackapi/slack-github-action@v2
        with:
          webhook: SECRETS_SLACK_WEBHOOK_REF
          webhook-type: incoming-webhook
          payload: |
            {
              "text": "本番デプロイメントが失敗しました: COMMIT_SHA_SHORT"
            }

4.4 Kubernetesマニフェスト

# deploy/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: ghcr.io/myorg/myapp:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /readyz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          env:
            - name: LOG_LEVEL
              value: 'info'
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: database-url
# deploy/k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  type: ClusterIP
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP

4.5 再利用可能(さいりようかのう)ワークフロー

リポジトリ間でCI/CDロジックを共有(きょうゆう)するための再利用可能ワークフローを作成します。

# .github/workflows/reusable-go-ci.yml
name: Reusable Go CI

on:
  workflow_call:
    inputs:
      go-version:
        description: '使用するGoバージョン'
        required: false
        type: string
        default: '1.22'
      coverage-threshold:
        description: '最小カバレッジ率'
        required: false
        type: number
        default: 70
    secrets:
      CODECOV_TOKEN:
        required: false

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: inputs-go-version
      - run: go test -race -coverprofile=coverage.out ./...
      - run: go vet ./...

再利用可能ワークフローの呼(よ)び出(だ)し:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  go-ci:
    uses: myorg/.github/.github/workflows/reusable-go-ci.yml@main
    with:
      go-version: '1.22'
      coverage-threshold: 80
    secrets:
      CODECOV_TOKEN: SECRETS_CODECOV_TOKEN_REF

4.6 パスベースのトリガー

モノレポでは、関連(かんれん)ファイルが変更された場合(ばあい)にのみワークフローをトリガーします。

on:
  push:
    paths:
      - 'services/api/**'
      - 'shared/pkg/**'
      - 'go.mod'
      - 'go.sum'
    paths-ignore:
      - '**.md'
      - 'docs/**'

モノレポでの変更検出(けんしゅつ):

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      api: steps-filter-outputs-api
      web: steps-filter-outputs-web
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            api:
              - 'services/api/**'
              - 'shared/**'
            web:
              - 'services/web/**'
              - 'shared/**'

  build-api:
    needs: changes
    if: needs.changes.outputs.api == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: echo "APIサービスをビルド中"

  build-web:
    needs: changes
    if: needs.changes.outputs.web == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: echo "Webサービスをビルド中"

4.7 ロールバック戦略

# .github/workflows/rollback.yml
name: Rollback

on:
  workflow_dispatch:
    inputs:
      environment:
        description: '対象環境'
        required: true
        type: choice
        options:
          - staging
          - production
      revision:
        description: 'ロールバック先のリビジョン(空欄で前回に戻る)'
        required: false
        type: string

jobs:
  rollback:
    runs-on: ubuntu-latest
    environment: inputs-environment
    steps:
      - name: kubectlの設定
        uses: azure/setup-kubectl@v4

      - name: デプロイメントのロールバック
        run: |
          if [ -z "REVISION_INPUT" ]; then
            kubectl rollout undo deployment/myapp -n TARGET_ENV
          else
            kubectl rollout undo deployment/myapp \
              --to-revision=REVISION_INPUT -n TARGET_ENV
          fi
          kubectl rollout status deployment/myapp -n TARGET_ENV --timeout=300s

      - name: ロールバックの検証
        run: |
          curl -sf https://TARGET_ENV.myapp.example.com/healthz || exit 1

      - name: 通知
        uses: slackapi/slack-github-action@v2
        with:
          webhook: SECRETS_SLACK_WEBHOOK_REF
          webhook-type: incoming-webhook
          payload: |
            {
              "text": "TARGET_ENV環境のロールバックが完了しました"
            }

Part 5: パフォーマンスとコスト最適化

5.1 キャッシュ戦略の深掘(ふかぼ)り

レイヤー1: Goモジュールキャッシュ(actions/setup-go組み込み)

setup-goアクションはGoモジュールディレクトリを自動的にキャッシュします。追加設定は不要です。

レイヤー2: Goビルドキャッシュ

- uses: actions/cache@v4
  with:
    path: ~/.cache/go-build
    key: go-build-OS-HASH_OF_ALL_GO_FILES
    restore-keys: |
      go-build-OS-

レイヤー3: GitHub Actionsキャッシュバックエンドを使ったDockerレイヤーキャッシュ

- uses: docker/build-push-action@v6
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

レイヤー4: golangci-lintキャッシュ

golangci-lint-actionは独自(どくじ)のキャッシュを自動的に処理(しょり)します。キャッシュキーはGoバージョンとgolangci-lint設定に基(もと)づきます。

キャッシュ効率(こうりつ)の監視(かんし):

ワークフローログでキャッシュヒット率を追跡(ついせき)しましょう。健全(けんぜん)なパイプラインでは、モジュールとビルドキャッシュのヒット率が80%以上であるべきです。

5.2 ジョブの並列実行

最大の並列性(へいれつせい)を得るためにワークフローDAGを構造化します。

  lint ------+
             |
  test ------+--> build --> deploy-staging --> deploy-production
             |
  security --+
jobs:
  lint:
    runs-on: ubuntu-latest
    # 依存なし、即座に実行

  test:
    runs-on: ubuntu-latest
    # 依存なし、lintと並列実行

  security:
    runs-on: ubuntu-latest
    # 依存なし、lintとtestと並列実行

  build:
    needs: [lint, test, security]
    # 3つすべての完了を待機

  deploy-staging:
    needs: build

  deploy-production:
    needs: deploy-staging

5.3 セルフホストランナー

コストの高い、または特殊(とくしゅ)なワークロードには、セルフホストランナーが分単位(ぶんたんい)の課金(かきん)を排除します。

jobs:
  build:
    runs-on: [self-hosted, linux, x64, gpu]
    steps:
      - uses: actions/checkout@v4
      - run: nvidia-smi # セルフホストでのGPUアクセス

セルフホストランナーを使うべき場合:

シナリオ推奨(すいしょう)
ビルド時間10分未満GitHub-hosted
ビルド時間10-30分セルフホスト検討
ビルド時間30分超セルフホスト強く推奨
GPU/専用ハードウェアセルフホスト(必須)
コンプライアンス(データローカリティ)セルフホスト(必須)
内部リソースへのネットワークアクセスセルフホスト(必須)

Dockerによるセルフホストランナーのセットアップ:

# セルフホストランナー用docker-compose.yml
version: '3.8'
services:
  runner:
    image: myoung34/github-runner:latest
    environment:
      REPO_URL: https://github.com/myorg/myrepo
      RUNNER_TOKEN: your-registration-token
      RUNNER_NAME: custom-runner-1
      RUNNER_WORKDIR: /tmp/github-runner
      LABELS: self-hosted,linux,x64
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

5.4 課金とコスト計算(けいさん)

GitHub Actionsの料金(りょうきん)(2026年現在):

プラン含まれる分数ストレージ追加分あたり(Linux)
Free2,000/月500 MBN/A
Team3,000/月2 GB$0.008
Enterprise50,000/月50 GB$0.008

OS乗数(じょうすう):

OS乗数
Linux1x
Windows2x
macOS10x

コスト最適化のヒント:

  1. concurrencyグループで冗長(じょうちょう)なワークフロー実行をキャンセル
  2. パスフィルタで不要なビルドをスキップ
  3. キャッシュを最大限活用(モジュール、ビルド、Dockerレイヤー)
  4. 絶対に必要な場合を除きmacOSランナーを避ける(10倍のコスト)
  5. マトリックス戦略を賢明(けんめい)に使用 -- 各エントリは別のジョブ
  6. 大量のリポジトリにはセルフホスト
  7. timeout-minutesで暴走(ぼうそう)ワークフローを防止
  8. 小さなジョブを結合してジョブ起動オーバーヘッドを削減(約15-30秒/ジョブ)

5.5 ワークフローパフォーマンスの監視

ボトルネックを特定するための主要メトリクスを追跡します。

# 主要ステップにタイミングを追加
- name: タイミング付きビルド
  run: |
    START_TIME=$(date +%s)
    go build -o ./bin/myapp ./cmd/server/
    END_TIME=$(date +%s)
    DURATION=$((END_TIME - START_TIME))
    echo "ビルドにDURATION_VAR秒かかりました"

追跡すべき主要メトリクス:

  • ワークフロー全体の所要時間
  • 個々のジョブとステップの所要時間
  • キャッシュヒット/ミス率
  • キュー待機時間(ランナーがジョブを取得するまでの時間)
  • ジョブタイプ別の失敗率
  • ワークフロー実行あたりのコスト

5.6 actによるローカルテスト

プッシュ前にGitHub Actionsをローカルでテストします。

# actのインストール
brew install act

# デフォルトイベント(push)の実行
act

# 特定のワークフローを実行
act -W .github/workflows/ci.yml

# 特定のジョブを実行
act -j test

# 特定のイベントを使用
act pull_request

# シークレットを渡す
act -s GITHUB_TOKEN=your-token

# より大きなランナーイメージを使用
act -P ubuntu-latest=catthehacker/ubuntu:full-latest

actの制限事項(せいげんじこう):

  • servicesのサポートなし(データベースのモックが必要)
  • 一部のGitHubコンテキスト変数が利用できない場合がある
  • Docker-in-Dockerが問題になることがある
  • 一部のアクションがGitHub-hostedランナーと同一に動作(どうさ)しない場合がある

クイズ

Q1: GitHub Actionsワークフローにおけるneedsusesの違いは何ですか?

回答: needsはジョブの依存関係を定義します。現在のジョブが実行される前に正常に完了する必要があるジョブを指定します。例えば、needs: [lint, test]は、lintとtestの両方が完了するまでジョブが待機することを意味します。usesはアクションまたは再利用可能ワークフローを参照します。ステップ内では、uses: actions/checkout@v4がcheckoutアクションを実行します。ジョブレベルでは、uses: org/repo/.github/workflows/ci.yml@mainが再利用可能ワークフローを呼び出します。

Q2: Go DockerビルドでCGO_ENABLED=0が重要な理由は何ですか?

回答: CGO_ENABLED=0を設定すると、C言語ライブラリへの依存がない完全に静的にリンクされたバイナリが生成されます。これは、glibcを含まないscratchdistrolessのような最小限のベースイメージを使用する場合に重要です。CGO_ENABLED=0なしでは、バイナリはlibcに動的リンクし、これらの最小限のコンテナでは実行に失敗します。また、Cクロスコンパイラツールチェーンが不要になるため、クロスコンパイルが簡単になります。

Q3: Git FlowよりもTrunk-Based Developmentを選ぶべきなのはいつですか?

回答: Trunk-Based Developmentは、1日に複数回のデプロイが必要で、チームが自動テストを含む強力なCI/CDプラクティスを持ち、未完成の機能を管理するためにフィーチャーフラグを使用できる場合に最適です。Git Flowは、正式なリリース管理が必要な場合、複数バージョンの同時サポートが必要な場合、またはCI/CDインフラが十分に成熟していない場合に適しています。重要な判断基準はデプロイ頻度です。1日に複数回デプロイするならTrunk-Basedが優れています。バージョン番号付きで月次リリースするなら、Git Flowが必要な構造を提供します。

Q4: OIDCはクラウドプロバイダー認証において従来のシークレットよりどのようにセキュリティを向上させますか?

回答: OIDC(OpenID Connect)は、GitHubとクラウドプロバイダー間のフェデレーション信頼関係を確立することで、長期間有効な静的認証情報(アクセスキー)を排除します。恒久的なアクセスキーをリポジトリシークレットとして保存する代わりに、ワークフローは特定のリポジトリ、ブランチ、環境に自動的にスコープされる短期トークンを要求します。これらのトークンは数分で期限切れとなるため、漏洩時の影響範囲が縮小されます。OIDCはどのワークフローがどの権限を要求したかの監査証跡も提供し、ローテーションや偶発的な漏洩のリスクがある認証情報が存在しません。

Q5: Go CIパイプラインにおいて、コスト対パフォーマンス比が最も高いキャッシュ戦略は何ですか?

回答: 効果の高い順にキャッシュレイヤーを並べると:(1) actions/setup-go組み込みキャッシュによるGoモジュールキャッシュ -- 依存関係のダウンロードを排除し、通常30-60秒節約。(2) actions/cacheを介したGoビルドキャッシュ -- 変更されていないパッケージの再コンパイルを回避し、45-120秒節約。(3) GitHub Actionsキャッシュバックエンド(type=gha)を使用したDockerレイヤーキャッシュ -- 変更されていないDockerレイヤーの再ビルドを回避し、60-300秒節約。(4) golangci-lintキャッシュ(アクションにより自動処理)。これらを組み合わせると、10分のパイプラインを3分未満に短縮できます。モジュールキャッシュはsetup-goに組み込まれているため無料であり、キャッシュサイズに対して最も多くの時間を節約するため、コスト対パフォーマンス比が最も高くなります。


参考文献(さんこうぶんけん)

  1. GitHub Actions Documentation -- https://docs.github.com/en/actions
  2. GitHub Actions Workflow Syntax -- https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
  3. GitHub Actions Contexts and Expressions -- https://docs.github.com/en/actions/learn-github-actions/contexts
  4. GitHub Actions Environments -- https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment
  5. GitHub OIDC with Cloud Providers -- https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
  6. Go Build Documentation -- https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies
  7. Go Cross-Compilation -- https://go.dev/doc/install/source#environment
  8. golangci-lint -- https://golangci-lint.run/
  9. GoReleaser Documentation -- https://goreleaser.com/
  10. Docker Multi-Stage Builds -- https://docs.docker.com/build/building/multi-stage/
  11. Docker Build Push Action -- https://github.com/docker/build-push-action
  12. Git Flow by Vincent Driessen -- https://nvie.com/posts/a-successful-git-branching-model/
  13. GitHub Flow -- https://docs.github.com/en/get-started/using-github/github-flow
  14. Trunk-Based Development -- https://trunkbaseddevelopment.com/
  15. Kubernetes Deployment Strategies -- https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  16. GitHub Actions Caching -- https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows
  17. GitHub Actions Self-Hosted Runners -- https://docs.github.com/en/actions/hosting-your-own-runners
  18. GitHub Actions Reusable Workflows -- https://docs.github.com/en/actions/using-workflows/reusing-workflows
  19. GitHub Repository Rulesets -- https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets
  20. GitHub Actions Billing -- https://docs.github.com/en/billing/managing-billing-for-github-actions
  21. act - Run GitHub Actions Locally -- https://github.com/nektos/act
  22. CODEOWNERS Documentation -- https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
  23. Codecov GitHub Action -- https://github.com/codecov/codecov-action
  24. dorny/paths-filter -- https://github.com/dorny/paths-filter