3-4. CI 基礎 - GitHub Actions でテスト・ビルド・デプロイを自動化
所要時間: 50-70分 ゴール: Go アプリの test → build → Docker image push → VPS デプロイを GitHub Actions で自動化できる コミット内容:
~/learn/infra/ci/に動作する.github/workflows/*.yml一式
このレッスンのゴール
- CI と CD の責務の違いを語れる
- GitHub Actions の用語(Workflow / Job / Step / Action)を区別できる
-
push時にテスト → ビルド → イメージ push の3段で動かせる - OIDC 経由で AWS / GCP に short-lived 認証できる
-
concurrencyでデプロイ多重実行を防止できる
なぜ学ぶか(実務悩みベース)
- 「自分の手元では動いた」病を撲滅する
- マージから本番反映を分単位に短縮する
- 脆弱性スキャン / シークレットスキャンを自動化する
- 採用面接の頻出質問「CI 書けますか?」に即答できる
前章とのつながり
3-3 までで「動かせる本番環境」が揃った。今回はそこに 自動でコードを流し込む 仕組み。Level 1 のイメージビルド、Level 2 の Compose、Level 3 の VPS、全てが CI/CD で1本に繋がる。
大前提: CI/CD は「自動テスト + 自動デプロイ」の総称
- CI (Continuous Integration): コード変更ごとにテスト・lint・ビルドを自動実行し、壊れていないことを担保する
- CD (Continuous Delivery / Deployment): テストが通ったコードを自動でステージングや本番に届ける
なぜ必須か:
- 品質: PR 時に「テストが通ること」が機械的に保証される。レビュワーは「正しさ」より「設計」を見られる
- 速度: 「動作確認のためのマニュアル手順書」が消える。マージ → 自動デプロイで数分後に本番反映
- 再現性: 「自分の手元では動いた」が消滅。CI 環境で動かなければマージできない仕組み
- セキュリティ: 脆弱性スキャン、シークレットスキャン、SBOM 生成を自動化できる
- 採用: GitHub Actions / GitLab CI / CircleCI が読めない/書けないバックエンドエンジニアは2026年では珍しい
GitHub の OSS 文化と直結している GitHub Actions を学べば、CI/CD の本質はすべて掴める。他の CI ツール(GitLab CI, CircleCI, Buildkite)の概念モデルもほぼ同じ。
セッション①: GitHub Actions の構造(20-25分)
1. 用語の階層
[Workflow] .github/workflows/ci.yml に書く 1 ファイル
↓ 含む
[Job] 独立した実行単位。並列で複数走らせられる
↓ 含む
[Step] コマンド or アクションの 1 単位
↓ 使う
[Action] 再利用可能な処理(公式 / マーケットプレイス / 自作)最小例:
# .github/workflows/hello.yml
name: hello-world
on: push # トリガー
jobs:
greeting: # ジョブ名
runs-on: ubuntu-latest
steps:
- name: echo
run: echo "Hello, $GITHUB_ACTOR"
runs-onで指定する実行環境GitHub がホストするランナー(仮想マシン)の OS を指定する:
ラベル 中身 ubuntu-latestUbuntu 24.04 (2026 時点) ubuntu-22.04バージョン固定 macos-latestmacOS(iOS ビルド等) windows-latestWindows Server self-hosted自前で立てたランナー 基本は
ubuntu-latest。macOS は実行時間課金が約 10 倍、Windows は 2 倍。-latestは将来 OS が上がるので、再現性が必要ならubuntu-24.04のように固定する流儀もある。
2. トリガー on:
on:
push:
branches: [main, develop]
paths:
- 'src/**'
- 'go.mod'
pull_request:
branches: [main]
schedule:
- cron: '0 3 * * *' # 毎日 03:00 UTC
workflow_dispatch: # 手動実行ボタン
inputs:
env:
description: 'デプロイ環境'
required: true
default: 'staging'
type: choice
options: [staging, production]各トリガーの実務用途
- push: 通常の CI(main マージ時、各 PR ブランチへの push)
- pull_request: PR 単位での CI。fork からの PR でも安全に動く(secrets が読めない制限が入る)
- schedule: 定期実行。脆弱性スキャン、データ集計、デッドリンクチェック
- workflow_dispatch: 手動実行。デプロイボタン、ロールバック、ホットフィックス
- release: GitHub Release 作成時。バイナリビルド・配布
- workflow_run: 別の workflow 完了時。「テストが通ったらデプロイ」のチェーン
pull_requestとpull_request_targetの違い(重要なセキュリティ)
pull_request: fork からの PR では secrets が空、書き込み権限なし。安全pull_request_target: 親リポの権限で実行。secrets も触れる、書き込みも可能。fork からの悪意あるコードがランナー上で動く危険マーケットプレイスの
pull_request_targetを使うアクションは要監査。基本はpull_requestを使う。
3. ジョブとステップ
name: ci
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- run: go vet ./...
- run: go install honnef.co/go/tools/cmd/staticcheck@latest
- run: staticcheck ./...
test:
runs-on: ubuntu-latest
needs: lint # lint が成功した後に走る
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- run: go test -race -cover ./...
build:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- run: go build -o app ./cmd/server
- uses: actions/upload-artifact@v4
with:
name: app-binary
path: app
needs:でジョブの依存関係デフォルトで複数 job は並列実行。
needs: [lint]を書くと「lint 成功後に開始」になる。並列 vs 直列の選択:
- lint と test は内容が独立 → 並列が速い
- build は test 通過後 → 直列
- deploy は build 通過後 → 直列
「lint と test は並列、両方終わったら build」のような複雑な依存も
needs: [lint, test]で書ける。
uses:とrun:の違い
run:: シェルコマンドを直接実行。run: go test ./...uses:: 既存の Action を再利用。uses: actions/checkout@v4Action はマーケットプレイス(marketplace.github.com)で検索できる。「Slack 通知」「Docker ビルド」「AWS デプロイ」など何でもある。
4. 環境変数とシークレット
env:
GO_VERSION: '1.23'
REGISTRY: ghcr.io
jobs:
deploy:
runs-on: ubuntu-latest
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
steps:
- name: SSH デプロイ
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
echo "$SSH_KEY" > /tmp/key
chmod 600 /tmp/key
ssh -i /tmp/key deploy@vps.example.com "/opt/app/deploy.sh"secrets の管理
リポジトリの Settings → Secrets and variables → Actions で登録する。
- Repository secrets: そのリポでのみ使える
- Environment secrets: 環境(staging/production)単位、approval workflow と組み合わせられる
- Organization secrets: 組織内の複数リポで共有
シークレットは workflow ログでも
***でマスクされる。
secret を echo して漏らす事故
# NG - run: echo "API_KEY is ${{ secrets.API_KEY }}"ログには
API_KEY is ***と出るのでマスクされているように見える。ただしマスクは「完全一致」のみ。危険な書き方:
# NG: 部分文字列を経由して漏らす - run: | KEY="${{ secrets.API_KEY }}" echo "${KEY:0:5}..." # 先頭 5 文字を出力 → マスク回避 echo "$KEY" | base64 # エンコードしてマスク回避 echo "$KEY" >> /tmp/key # ファイルに書く → 直後のステップで cat /tmp/key みたいな攻撃: PR で workflow ファイルを書き換えて secrets を取り出す手口が実在。対策は fork からの PR では
pull_requestを使う(pull_request_targetを使わない)、信頼できないコードのレビューでは workflow 変更を要警戒。
5. マトリックスビルド
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # 1 つ落ちても他を続ける
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.22', '1.23']
exclude:
- os: windows-latest
go: '1.22' # この組み合わせはスキップ
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- run: go test ./...マトリックスビルドの典型用途
- 多バージョン対応のライブラリ: Go 1.22 / 1.23、Python 3.10 / 3.11 / 3.12
- 多 OS 対応の CLI: Linux / macOS / Windows でビルド
- DB 種別: MySQL 8.0 / PostgreSQL 16 / SQLite で同じテストを回す
上の例だと 3 OS × 2 バージョン = 6 ジョブが並列実行。Go の OSS で典型的なパターン。
fail-fast: falseデフォルトでは 1 ジョブ失敗で他もキャンセル。トラブル時に「全部の組み合わせの状況を見たい」場合は
falseに。trueの方が CI 時間と費用を抑えられる。
6. キャッシュ
- uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true # go modules を自動キャッシュ
# 手動でキャッシュ管理する場合
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-キャッシュで CI を高速化
依存ダウンロードに毎回数分かかると、コミットごとに数分浪費する。キャッシュで CI 時間を 30-70% 削減できる。
キャッシュキー設計のコツ:
hashFiles('go.sum')のように依存ファイルのハッシュを含める → 依存が変わったら自動的に新キャッシュrestore-keysでフォールバック(前のキャッシュを部分的に使う)キャッシュ容量: リポジトリあたり 10GB まで。古いキャッシュは 7 日で自動削除。
セッション②: Go アプリの実用 CI(20-25分)
7. Go アプリ向けの workflow 完成版
# .github/workflows/ci.yml
name: ci
on:
push:
branches: [main]
pull_request:
permissions:
contents: read # 最小権限
env:
GO_VERSION: '1.23'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- run: gofmt -l . | tee /tmp/fmt.log
- run: test ! -s /tmp/fmt.log # gofmt 違反があれば失敗
- run: go vet ./...
- uses: dominikh/staticcheck-action@v1
with:
version: latest
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: testpass
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: ${{ env.GO_VERSION }}
cache: true
- name: テスト実行(カバレッジ付き)
env:
DATABASE_URL: postgres://postgres:testpass@localhost:5432/postgres?sslmode=disable
run: go test -race -coverprofile=coverage.out ./...
- name: カバレッジ報告
run: go tool cover -func=coverage.out
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.out
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
services:で DB 等のサイドカージョブ内で PostgreSQL / MySQL / Redis などを起動できる。統合テストに不可欠。
servicesは Docker コンテナとして起動し、ジョブ内からlocalhost:<ポート>で接続できる。health-cmdで起動完了を待つ仕組み。
govulncheck で脆弱性スキャン
Go 公式の脆弱性スキャナ。
go.sumの依存だけでなく、実際にコード中で呼ばれている関数のみ で判定する。「依存はあるが脆弱な関数は使っていない」を区別できるのが強み。他に
nancy,osv-scanner等もある。現代の Go プロジェクトには 1 つは入れる。
8. Docker イメージのビルドと push
# .github/workflows/release.yml
name: release
on:
push:
branches: [main]
tags: ['v*']
permissions:
contents: read
packages: write # ghcr.io への push に必要
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: GitHub Container Registry にログイン
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: タグ生成
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=,format=short
type=raw,value=latest,enable={{is_default_branch}}
- name: ビルドして push
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
platforms: linux/amd64,linux/arm64ghcr.io vs Docker Hub
項目 ghcr.io Docker Hub 認証 GITHUB_TOKEN自動DOCKERHUB_TOKEN手動登録料金 プライベートも無料 無料枠の制限あり レイテンシ GitHub と同居で速い 別ホスト 連携 GitHub Repo と密結合 独立 2026 年現在、個人・小規模は ghcr.io 一択。大規模・組織横断的は ECR / Artifact Registry / Docker Hub Team が選択肢。
GITHUB_TOKENの自動発行各 workflow 実行時に GitHub が自動で発行するトークン。
secrets.GITHUB_TOKENで参照できる。デフォルトの権限: read(リポジトリ設定で変更可能)。最小権限の原則に従い、各 workflow / job の
permissions:ブロックで必要なものだけ宣言する。permissions: contents: read packages: write # コンテナレジストリへの push に必要 pull-requests: write # PR コメント投稿に必要
マルチアーキビルド
platforms: linux/amd64,linux/arm64で AMD64 と ARM64 の両方をビルド。Apple Silicon Mac、AWS Graviton、Raspberry Pi で動くようになる。ARM ランナーは Mac でないと遅い →
docker/setup-buildx-actionが QEMU エミュレーションで対応。ビルド時間が2倍近くなる。必要な時だけ有効化。
セッション③: VPS への自動デプロイ(20-25分)
9. VPS にデプロイする workflow
# .github/workflows/deploy.yml
name: deploy
on:
workflow_run:
workflows: ['release']
types: [completed]
branches: [main]
workflow_dispatch: # 手動実行用
permissions:
contents: read
concurrency:
group: production-deploy
cancel-in-progress: false # デプロイは並行させない
jobs:
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- name: SSH 鍵を準備
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: VPS でデプロイスクリプト実行
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} bash <<'EOF'
set -euo pipefail
cd /opt/app
docker pull ghcr.io/myorg/myapp:latest
docker compose up -d --no-deps app
docker image prune -f
EOF
- name: ヘルスチェック
run: |
for i in 1 2 3 4 5; do
if curl -fs https://example.com/healthz; then
echo "healthcheck ok"
exit 0
fi
sleep 5
done
echo "healthcheck failed"
exit 1
concurrency:で多重実行を制御同じデプロイが同時に 2 本走ると壊れることがある(DB マイグレーションが二重実行されるなど)。
concurrency: group: production-deploy cancel-in-progress: false # 走っている方を待つ
cancel-in-progress: trueにすると「最新だけ実行、古いのはキャンセル」。新しいコミットが連続で来た時に CI 時間を節約できる(テスト用途)。デプロイではfalse推奨。
environment:で承認フローを挟む
environment: productionを指定すると、GitHub の Settings → Environments で 「承認者を必須にする」「特定ブランチからのみデプロイ可」「待機時間 N 分」 といった保護ルールを設定できる。本番デプロイには「2 人の承認が必要」のようなガードが鉄板。事故防止+監査証跡。
ssh-keyscan で known_hosts を作る
ssh-keyscan -H <host>でホスト鍵を取得し~/.ssh/known_hostsに登録。これをしないと SSH 接続時に「初めての接続です(yes/no)」が出てスクリプトが止まる、またはStrictHostKeyChecking=noで危険な設定にしがち。
デプロイ鍵の権限を絞る
DEPLOY_SSH_KEYの秘密鍵に対応する公開鍵を VPS のdeployユーザーに置く。ただしauthorized_keysで:command="/opt/app/deploy.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... github-actions-deployのように command 制約 を付け、デプロイ専用に。万一漏洩しても他用途には使えない。
10. ロールバック workflow
# .github/workflows/rollback.yml
name: rollback
on:
workflow_dispatch:
inputs:
version:
description: '戻すバージョン(タグまたは SHA)'
required: true
type: string
permissions:
contents: read
jobs:
rollback:
runs-on: ubuntu-latest
environment:
name: production
steps:
- name: SSH 準備
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: 指定バージョンに戻す
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} \
"cd /opt/app && \
IMAGE_TAG=${{ inputs.version }} docker compose up -d --no-deps app"ロールバックの心構え
「本番デプロイ → 不具合発覚 → 戻したい」の時に Git revert からの再デプロイは遅い。直前イメージにタグ切替で即戻しが現実的な手法。
前提:
- イメージタグに コミット SHA とブランチ名 を両方含めている
- 過去 N 個のイメージはレジストリに残っている
- VPS 側に
IMAGE_TAGを受け取ってdocker composeが起動する仕組みがある
アンチパターン総まとめ
secret を echo / cat で出力
上述の通り、partial output や encode を経由した漏洩が起きる。secrets は ファイルにも保存しない、ログにも出さない。
mainブランチでだけ動く CIon: push: branches: [main]PR の CI が動かないので、main にマージするまでテストが回らない。
pull_requestトリガーも必須。
テストを skip するための CI
- run: go test ./... || true # 失敗を握りつぶす「CI が落ちて止まるから」と握りつぶすと、CI の意味がない。落ちたら直す。
サードパーティ Action をバージョン無指定で使う
- uses: shady-action/do-things@main # NG: 信頼できる commit にピン留めしていないリポオーナーが悪意あるコードを混ぜると、secrets が抜かれる。
対策:
@v1.2.3のようなセマンティックタグ、または commit SHA でピン留め:- uses: shady-action/do-things@abc1234abc1234abc1234abc1234abc1234abc12Dependabot でアクション更新も自動化可能。
デプロイ前にヘルスチェックなし
- run: ssh deploy@vps "docker compose up -d app" # → ここで workflow は成功扱い # → アプリは起動失敗していてもユーザーには気付かれないデプロイ後に
/healthzを叩いて 5xx でないことを確認してから「成功」とする。
permissions:を書かず write 権限デフォルトで動かす古い GitHub Actions のデフォルトは
write-all。workflow ファイル冒頭でpermissions:を最小権限に絞るのが現代の作法。
セキュリティ補強
Action のセキュリティ
- 公式アクションを優先:
actions/checkout,actions/setup-goなど。GitHub 公式は監査されている- マーケットプレイスは作者を確認: スター数、メンテ頻度、所有者の信頼性
- OIDC で短命クレデンシャル: AWS/GCP/Azure には
permissions: id-token: writeで短命の OIDC トークンを発行し、永続クレデンシャルを secrets に置かない方式が現代の推奨
OIDC によるパスワードレス AWS デプロイ例
permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy aws-region: ap-northeast-1 - run: aws s3 sync ./build s3://my-bucket/AWS 側に IAM ロールと OIDC プロバイダ設定が必要だが、アクセスキーを GitHub に置かなくて済む。漏洩時の被害が激減。
secret scanning と Dependabot
リポジトリ Settings で:
- Secret scanning: AWS キーや GitHub トークンが誤コミットされた瞬間に検知して通知
- Dependabot: 依存ライブラリの脆弱性を検知、自動 PR で更新
- Dependabot for GitHub Actions: アクションのバージョン更新も自動 PR
全部 ON にする。コスト無料。
実例: テスト → イメージビルド → VPS デプロイの完全フロー
# .github/workflows/full-pipeline.yml
name: full-pipeline
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
- run: go vet ./...
- run: go test -race -cover ./...
build:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=,format=short
type=raw,value=latest
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
concurrency:
group: production-deploy
cancel-in-progress: false
steps:
- name: SSH 鍵設置
env:
KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
mkdir -p ~/.ssh
echo "$KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: デプロイ
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} bash <<EOF
set -euo pipefail
cd /opt/app
export IMAGE_TAG=${{ needs.build.outputs.image-tag }}
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:\$IMAGE_TAG
docker compose up -d --no-deps app
docker image prune -f
EOF
- name: ヘルスチェック(最大 30 秒)
run: |
for i in $(seq 1 6); do
if curl -fs https://example.com/healthz; then
echo "OK"; exit 0
fi
sleep 5
done
echo "FAILED"; exit 1練習課題
- 小さな Go プロジェクト(
hello.goレベル)を GitHub に push .github/workflows/ci.ymlでgo vet+go testを走らせる- PR を作って Actions タブで実行を観察、緑チェックマークを確認
.github/workflows/release.ymlで ghcr.io に Docker イメージを push- ghcr.io のリポジトリページで自分のイメージが見えるか確認
- VPS の
deployユーザー向けの SSH 鍵を作り、GitHub Secrets に登録 .github/workflows/deploy.ymlで VPS にデプロイ- main に新しいコミットを push して、テスト → ビルド → デプロイが連鎖することを確認
- ヘルスチェック失敗時の挙動を試す(わざと 500 を返すコードで)
- ロールバック workflow も作って手動実行できることを確認
締め: git で証跡を残す
cd ~/learn/infra/ci
# workflow ファイル一式を別途学習用にコピー
cp -r ~/myapp/.github .
git add .github/ README.md
git commit -m "feat(ci): GitHub Actions の test/build/deploy フルパイプライン構築"チェックリスト
- workflow, job, step, action の階層関係を説明できる
-
on:で push / pull_request / schedule / workflow_dispatch を使い分けられる - secrets と環境変数の違いを説明できる
- マトリックスビルドで多バージョンテストできる
-
needs:でジョブ依存を制御できる - ghcr.io に Docker イメージを push できる
- VPS に SSH で自動デプロイできる
-
permissions:を最小権限で書ける - サードパーティ Action のリスクと SHA ピン留めを意識している
- OIDC でパスワードレス AWS デプロイの方法を知っている
詰まった時のチートシート
| やりたいこと | 書き方 |
|---|---|
| push と PR で動かす | on: [push, pull_request] |
| main のみ | on: push: branches: [main] |
| 手動実行 | on: workflow_dispatch: |
| 毎日定時 | on: schedule: - cron: '0 3 * * *' |
| ジョブ依存 | needs: [test, lint] |
| 条件付き実行 | if: github.ref == 'refs/heads/main' |
| secret 参照 | ${{ secrets.NAME }} |
| env 参照 | ${{ env.NAME }} |
| 出力受け渡し | outputs: + ${{ needs.xxx.outputs.yyy }} |
| Go セットアップ | uses: actions/setup-go@v5 |
| キャッシュ | cache: true(setup-* で) |
| Docker ビルド | uses: docker/build-push-action@v6 |
| 並行制御 | concurrency: group: ... |
対比表: デプロイ戦略の選択肢
| 戦略 | 仕組み | メリット | デメリット |
|---|---|---|---|
| 手動 SSH デプロイ | git pull + restart | 単純 | 再現性ゼロ、ヒューマンエラー多発 |
| GitHub Actions → SSH | ssh で本番にコマンド | 自動化、再現性 | SSH 鍵管理 |
| GitHub Actions → イメージ push → 本番 pull | レジストリ経由 | ロールバック容易 | レジストリ運用 |
| GitOps (ArgoCD等) | リポジトリ状態 = 本番状態 | 完全宣言的 | 学習コスト、k8s 前提が多い |
対比表: 認証方式
| 方式 | 内容 | 推奨度 |
|---|---|---|
| 長期 secret (Access Key) | AWS_ACCESS_KEY_ID 等 | △ (漏えいリスク) |
| OIDC (id-token) | GitHub → AWS/GCP に short-lived 認証 | ◎ (推奨) |
| SSH 鍵 + GitHub secret | appleboy/ssh-action 等で SSH | ○ (VPS デプロイ) |
「実務OK」基準
- Go の test/lint/build/security を CI で組める
- Docker イメージを ghcr.io に push できる
- VPS へ自動デプロイの workflow が書ける
- secrets を漏らさず使える
permissions:で最小権限を宣言できる- マトリックスビルドで複数バージョン同時テストできる
concurrencyでデプロイ多重実行を防げる- OIDC / 環境保護ルール / Dependabot を含む現代的なセキュリティ運用を理解している
自己評価チェックリスト
知識レベル
- CI と CD の責務の違いを語れる
- OIDC が「長期 secret 漏洩リスク」を解決する仕組みを言える
-
concurrencyがなぜ必要かを即答できる
実行レベル
- test → build → image push の Workflow を書いた
- secrets を
${{ secrets.X }}で参照する書き方を実践した -
permissions:を最小権限で宣言した
メタ認知
- 自分のリポジトリで「手動デプロイ」が残っていないか棚卸しした
- CI が「ただの飾り」になっていないか(テスト失敗が無視されていないか)チェックした
さらに深掘りするなら
- 公式: GitHub Actions Documentation
- 公式: Security hardening for GitHub Actions
- 公式: Workflow syntax reference
- マーケットプレイス: github.com/marketplace?type=actions
- 書籍: 『GitHub CI/CD 実践ガイド』(野村 真矢, 技術評論社)
- OSS 事例: kubernetes/kubernetes, golang/go の
.github/workflows/を読む
次のレッスン
3-5_ログ収集と監視.md で、デプロイされたアプリの動きを把握する監視・ロギングの世界へ。