🚀 STEP 23: CI/CD統合(GitHub Actions)
自動ビルド・テスト・デプロイのパイプライン構築
📋 このステップで学ぶこと
- CI/CDとは何か
- GitHub ActionsでDockerイメージビルド
- Docker Hubへの自動プッシュ
- タグ戦略(latest、バージョン番号)
- セキュリティスキャン(Trivy)
- 実践演習:完全なCI/CDパイプライン構築
🔧 0. このステップの前提知識
📚 これまでの学習の復習
- Dockerfile:イメージのビルド(STEP 11-14)
- Docker Hub:イメージの取得とプッシュ(STEP 7)
- 環境変数:シークレット管理(STEP 21)
- Git基礎:コミット、プッシュ、ブランチ
0-1. 作業ディレクトリの準備
mkdir -p ~/docker-practice/step23
cd ~/docker-practice/step23
pwd
0-2. CI/CDパイプラインの全体像
【CI/CDパイプライン フロー】
┌─────────────────────────────────────────────────────────────────────┐
│ 開発者のローカル環境 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ コード編集 │───→│ テスト実行 │───→│ git push │ │
│ └──────────┘ └──────────┘ └─────┬────┘ │
└──────────────────────────────────────────┼──────────────────────────┘
│
↓ トリガー
┌─────────────────────────────────────────────────────────────────────┐
│ GitHub Actions (CI) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ checkout │───→│ test │───→│ build │───→│ scan │ │
│ │ コード │ │ 自動テスト│ │イメージ構築│ │脆弱性検査 │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────┬────┘ │
└────────────────────────────────────────────────────────┼────────────┘
│
↓ 成功時
┌─────────────────────────────────────────────────────────────────────┐
│ GitHub Actions (CD) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ push │───→│ deploy │───→│ notify │ │
│ │Docker Hub│ │ 本番反映 │ │ Slack通知│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
0-3. GitHub Actionsの構成要素
| 要素 | 説明 | 例 |
|---|---|---|
| Workflow | 自動化プロセス全体 | .github/workflows/ci.yml |
| Event | ワークフローを起動するトリガー | push, pull_request, schedule |
| Job | ワークフロー内の処理単位 | build, test, deploy |
| Step | ジョブ内の個別タスク | checkout, run, uses |
| Action | 再利用可能な処理 | actions/checkout@v3 |
| Runner | ワークフローを実行するサーバー | ubuntu-latest |
0-4. 必要なアカウント
🔑 事前に必要なもの
- GitHubアカウント:リポジトリとActions
- Docker Hubアカウント:イメージの保存先
- Docker Hubアクセストークン:自動プッシュ用
🎯 1. CI/CDとは
1-1. CI(Continuous Integration)継続的インテグレーション
【CIの流れ】
開発者A ───┐
│
開発者B ───┼───→ リポジトリ ───→ 自動ビルド ───→ 自動テスト
│ (main) ↓ ↓
開発者C ───┘ 成功/失敗 成功/失敗
↓ ↓
即座に通知 即座に通知
【CIの利点】
✅ コードの品質を維持
✅ バグの早期発見
✅ チーム開発の効率化
✅ マージ前に問題を検出
1-2. CD(Continuous Deployment)継続的デプロイ
【CDの流れ】
テスト成功 ───→ ステージング環境 ───→ 承認 ───→ 本番環境
にデプロイ (自動/手動) にデプロイ
【CDの利点】
✅ デプロイの自動化
✅ リリース頻度の向上
✅ 人的ミスの削減
✅ ロールバックの容易化
1-3. GitHub Actionsの特徴
💰 無料枠あり
パブリックリポジトリは無料
プライベートは月2,000分まで無料
⚡ 簡単設定
YAMLファイル1つで設定
GitHub完全統合
🔧 豊富なアクション
マーケットプレイスで
数千のアクションを利用可能
🔗 GitHub統合
PR、Issue、リリースと連携
ステータスチェック
🔧 2. GitHub Actionsの基本
2-1. プロジェクト構造
# ディレクトリ構造を作成
mkdir -p .github/workflows
# 構造
# myproject/
# ├── .github/
# │ └── workflows/
# │ ├── docker-build.yml # ビルドワークフロー
# │ └── docker-publish.yml # プッシュワークフロー
# ├── Dockerfile
# ├── requirements.txt
# └── app.py
2-2. 最小構成のワークフロー
# ワークフローファイルを作成
cat > .github/workflows/docker-build.yml << ‘EOF’
# ワークフローの名前
name: Docker Build
# トリガー:いつ実行するか
on:
push:
branches: [ main ] # mainブランチへのプッシュ時
pull_request:
branches: [ main ] # mainへのPR作成時
# ジョブ:実行する処理
jobs:
build:
runs-on: ubuntu-latest # 実行環境
steps:
# ステップ1: コードをチェックアウト
– name: Checkout code
uses: actions/checkout@v3
# ステップ2: Dockerイメージをビルド
– name: Build Docker image
run: |
docker build -t myapp:test .
# ステップ3: 簡単なテスト
– name: Test Docker image
run: |
docker run –rm myapp:test python -c “print(‘Hello CI/CD!’)”
EOF
cat .github/workflows/docker-build.yml
2-3. ワークフローの構成要素
【ワークフローの構造】
name: ワークフロー名 ← GitHubのActionsタブに表示
│
on: トリガー ← いつ実行するか
│ ├── push: プッシュ時
│ ├── pull_request: PR時
│ ├── schedule: 定期実行(cron形式)
│ └── workflow_dispatch: 手動実行
│
jobs: ← 実行する処理群
│
└── build: ← ジョブ名
│
├── runs-on: ubuntu-latest ← 実行環境
│
└── steps: ← 個別の処理
├── uses: アクションを使用
└── run: シェルコマンドを実行
2-4. よく使うトリガー
| トリガー | 説明 | 例 |
|---|---|---|
push | プッシュ時 | branches: [main] |
pull_request | PR作成/更新時 | branches: [main] |
schedule | 定期実行 | cron: '0 0 * * *' |
workflow_dispatch | 手動実行 | ボタンクリックで実行 |
release | リリース時 | types: [published] |
📦 3. Docker Hubへの自動プッシュ
3-1. シークレットの設定
🔐 GitHubのシークレット設定手順
- GitHubリポジトリの「Settings」タブを開く
- 左メニューの「Secrets and variables」→「Actions」を選択
- 「New repository secret」をクリック
- 以下の2つを追加:
DOCKERHUB_USERNAME:Docker Hubのユーザー名DOCKERHUB_TOKEN:Docker Hubのアクセストークン
⚠️ Docker Hubアクセストークンの取得方法
- Docker Hub(https://hub.docker.com)にログイン
- 右上のアカウント → Account Settings
- Security → New Access Token
- 名前を入力(例:github-actions)
- 「Generate」をクリック
- 表示されたトークンをコピー(一度しか表示されない!)
3-2. Docker Hub自動プッシュワークフロー
cat > .github/workflows/docker-publish.yml << ‘EOF’
name: Docker Publish
on:
push:
branches: [ main ]
tags:
– ‘v*’ # v1.0.0 のようなタグ
env:
IMAGE_NAME: myapp
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
# コードをチェックアウト
– name: Checkout code
uses: actions/checkout@v3
# Docker Buildxをセットアップ(マルチプラットフォーム対応)
– name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Docker Hubにログイン
– name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# メタデータを抽出(タグ生成)
– name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
# ビルドしてプッシュ
– name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
EOF
3-3. タグ戦略
【自動生成されるタグ】
┌─────────────────────────────────────────────────────────────────────┐
│ トリガー 生成されるタグ │
├─────────────────────────────────────────────────────────────────────┤
│ git push origin main → username/myapp:main │
│ → username/myapp:sha-abc1234 │
├─────────────────────────────────────────────────────────────────────┤
│ git tag v1.2.3 → username/myapp:1.2.3 │
│ git push –tags → username/myapp:1.2 │
│ → username/myapp:latest │
├─────────────────────────────────────────────────────────────────────┤
│ Pull Request #123 → username/myapp:pr-123 │
└─────────────────────────────────────────────────────────────────────┘
🔒 4. セキュリティスキャン(Trivy)
4-1. Trivyとは
Trivyは、コンテナイメージの脆弱性をスキャンするオープンソースツールです。 OSパッケージやアプリケーション依存関係の脆弱性を検出します。
【Trivyの検出対象】
┌─────────────────────────────────────────────────────────────────────┐
│ Dockerイメージ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ アプリケーション層 │ │
│ │ └─ Python/Node.js/Java等の依存関係 ← 脆弱性チェック │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ OSパッケージ層 │ │
│ │ └─ apt/yum等でインストールしたパッケージ ← 脆弱性チェック │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ ベースイメージ │ │
│ │ └─ Ubuntu/Alpine等のOS ← 脆弱性チェック │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
4-2. Trivyスキャンワークフロー
cat > .github/workflows/security-scan.yml << ‘EOF’
name: Security Scan
on:
push:
branches: [ main ]
schedule:
– cron: ‘0 0 * * 0’ # 毎週日曜日の0時(UTC)
jobs:
scan:
runs-on: ubuntu-latest
steps:
– name: Checkout code
uses: actions/checkout@v3
– name: Build image for scanning
run: docker build -t myapp:scan .
– name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ‘myapp:scan’
format: ‘table’
exit-code: ‘1’ # 脆弱性発見時は失敗
ignore-unfixed: true # 修正不可なものは無視
severity: ‘CRITICAL,HIGH’ # 重大度の高いもののみ
– name: Run Trivy and output SARIF
uses: aquasecurity/trivy-action@master
if: always()
with:
image-ref: ‘myapp:scan’
format: ‘sarif’
output: ‘trivy-results.sarif’
– name: Upload scan results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: ‘trivy-results.sarif’
EOF
4-3. Trivyの設定オプション
| オプション | 説明 | 推奨値 |
|---|---|---|
exit-code | 脆弱性発見時の終了コード | ‘1’(失敗させる) |
severity | チェックする重大度 | ‘CRITICAL,HIGH’ |
ignore-unfixed | 修正不可能な脆弱性を無視 | true |
format | 出力形式 | ‘table’ or ‘sarif’ |
🛠️ 5. 完全なCI/CDパイプライン
5-1. サンプルアプリケーションの作成
# Flaskアプリを作成
cat > app.py << ‘EOF’
from flask import Flask, jsonify
app = Flask(__name__)
@app.route(‘/’)
def hello():
return jsonify({‘message’: ‘Hello CI/CD!’, ‘status’: ‘ok’})
@app.route(‘/health’)
def health():
return jsonify({‘status’: ‘healthy’})
if __name__ == ‘__main__’:
app.run(host=’0.0.0.0′, port=5000)
EOF
# 依存関係ファイル
cat > requirements.txt << ‘EOF’
flask==2.3.0
pytest==7.4.0
EOF
# テストファイル
mkdir -p tests
cat > tests/test_app.py << ‘EOF’
import pytest
from app import app
@pytest.fixture
def client():
app.config[‘TESTING’] = True
with app.test_client() as client:
yield client
def test_hello(client):
response = client.get(‘/’)
assert response.status_code == 200
assert b’Hello CI/CD!’ in response.data
def test_health(client):
response = client.get(‘/health’)
assert response.status_code == 200
assert b’healthy’ in response.data
EOF
5-2. Dockerfileの作成
cat > Dockerfile << ‘EOF’
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install –no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
HEALTHCHECK –interval=30s –timeout=3s \
CMD curl -f http://localhost:5000/health || exit 1
CMD [“python”, “app.py”]
EOF
5-3. 統合CI/CDワークフロー
cat > .github/workflows/ci-cd.yml << ‘EOF’
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
release:
types: [ published ]
env:
DOCKER_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/myapp
jobs:
# ジョブ1: テスト
test:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ‘3.9’
– name: Install dependencies
run: |
pip install -r requirements.txt
– name: Run tests
run: pytest tests/ -v
# ジョブ2: ビルドとスキャン
build-and-scan:
needs: test
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
– name: Build image
uses: docker/build-push-action@v4
with:
context: .
load: true
tags: ${{ env.DOCKER_IMAGE }}:test
– name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.DOCKER_IMAGE }}:test
format: ‘table’
exit-code: ‘0’
severity: ‘CRITICAL,HIGH’
# ジョブ3: プッシュ(mainブランチのみ)
push:
needs: build-and-scan
if: github.ref == ‘refs/heads/main’
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
– name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
– name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=sha
type=raw,value=latest
– name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ジョブ4: デプロイ通知
notify:
needs: push
if: success()
runs-on: ubuntu-latest
steps:
– name: Send notification
run: |
echo “✅ Deployment successful!”
echo “Image: ${{ env.DOCKER_IMAGE }}:latest”
EOF
5-4. 実行の流れ
# 1. ローカルでテスト
pytest tests/ -v
# 2. コードをコミット・プッシュ
git add .
git commit -m “Add CI/CD pipeline”
git push origin main
# 3. GitHubでActionsタブを確認
# → テスト → ビルド → スキャン → プッシュ の順で実行される
# 4. Docker Hubでイメージを確認
# https://hub.docker.com/r/username/myapp/tags
🔧 6. トラブルシューティング
6-1. よくある問題と対処法
問題1: ワークフローが実行されない
- ファイルパスを確認:
.github/workflows/に配置されているか - YAML構文エラーがないか確認
- ブランチ名が
on:で指定したものと一致しているか
問題2: Docker Hubへのプッシュが失敗
- シークレットが正しく設定されているか確認
DOCKERHUB_USERNAMEとDOCKERHUB_TOKENの名前を確認- Docker Hubのアクセストークンが有効か確認
# シークレットの確認方法
# Settings → Secrets and variables → Actions
# シークレット名に typo がないか確認
問題3: テストが失敗する
- ローカルでテストが通るか確認
- 依存関係がすべて
requirements.txtに含まれているか - Pythonバージョンが一致しているか
6-2. デバッグ方法
# ワークフローにデバッグステップを追加
– name: Debug info
run: |
echo “Branch: ${{ github.ref }}”
echo “Event: ${{ github.event_name }}”
echo “SHA: ${{ github.sha }}”
ls -la
cat Dockerfile
💪 7. 練習問題
練習問題 1
基礎
CI/CDの利点を3つ挙げてください。
- 品質向上:自動テストでバグを早期発見
- 効率化:手動作業の削減、デプロイの自動化
- リスク軽減:小さな変更を頻繁にデプロイ、問題の切り分けが容易
練習問題 2
基礎
GitHub Actionsのワークフローファイルはどこに配置しますか?
.github/workflows/ ディレクトリ内に.ymlまたは.yamlファイルとして配置します。
myproject/
└── .github/
└── workflows/
└── ci.yml
練習問題 3
基礎
mainブランチへのpush時にのみ実行されるトリガーを書いてください。
on:
push:
branches: [ main ]
練習問題 4
応用
GitHubシークレットをワークフロー内で参照する方法を書いてください。
${{ secrets.シークレット名 }}の形式で参照します。
– name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
練習問題 5
応用
テストが成功した後にのみビルドジョブを実行する設定を書いてください。
needs:キーワードを使用して依存関係を指定します。
jobs:
test:
runs-on: ubuntu-latest
steps:
– run: pytest
build:
needs: test # testジョブが成功した後に実行
runs-on: ubuntu-latest
steps:
– run: docker build .
練習問題 6
応用
Trivyでセキュリティスキャンを行い、CRITICAL脆弱性がある場合にワークフローを失敗させる設定を書いてください。
– name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ‘myapp:latest’
exit-code: ‘1’ # 脆弱性発見時に失敗
severity: ‘CRITICAL’ # CRITICALのみチェック
練習問題 7
発展
毎日午前3時(UTC)に実行されるスケジュールトリガーを書いてください。
on:
schedule:
– cron: ‘0 3 * * *’ # 分 時 日 月 曜日
# 毎日午前3時(UTC)に実行
cron形式:分 時 日 月 曜日
練習問題 8
発展
mainブランチの場合のみDocker Hubにプッシュする条件分岐を書いてください。
jobs:
push:
if: github.ref == ‘refs/heads/main’
runs-on: ubuntu-latest
steps:
– name: Push to Docker Hub
run: docker push myapp:latest
if:を使用して条件を指定できます。
📝 STEP 23 のまとめ
✅ このステップで学んだこと
- CI/CD:継続的インテグレーション/デプロイの概念
- GitHub Actions:YAMLでワークフロー定義
- Docker Hub連携:自動イメージプッシュ
- Trivy:コンテナの脆弱性スキャン
- タグ戦略:ブランチ、バージョン、SHAベースのタグ
📊 ワークフロー要素 早見表
| 要素 | 用途 | 例 |
|---|---|---|
on: | トリガー | push, pull_request, schedule |
jobs: | ジョブ定義 | test, build, deploy |
needs: | 依存関係 | needs: test |
if: | 条件分岐 | if: github.ref == ‘refs/heads/main’ |
secrets. | シークレット参照 | ${{ secrets.TOKEN }} |
🎯 次のステップの予告
次のSTEP 24では、「トラブルシューティングとベストプラクティス」を学びます。
- よくあるエラーと対処法
- セキュリティのベストプラクティス
- リソース制限の設定
- 次のステップ:Kubernetes入門
学習メモ
Docker・コンテナ技術入門 - Step 23
📋 過去のメモ一覧
▼