Docker パブリッシングガイド
本番環境への EvoSpikeNet-Core Docker イメージ配布ワークフローを説明します。
目次
概要
パブリッシング戦略
EvoSpikeNet-Core は段階的なセキュリティレベルで配布できます:
開発環境 ステージング環境 プリプロ環境 本番環境
↓ ↓ ↓ ↓
[source] → [wheel] → [cython] → [nuitka]
完全公開 ソース除外 モジュル難読 完全保護
推奨フロー
1. ローカル開発: source モードで機能実装・テスト
↓
2. CI テスト: 全モード (source/wheel/cython/nuitka) を並列ビルド・テスト
↓
3. ステージング検証: wheel または cython でデプロイ
↓
4. 本番リリース: nuitka でビルド後、レジストリにプッシュ
↓
5. デプロイ: イメージ署名検証後、本番環境で実行
前提条件
必須ツール
# Docker (20.10+)
docker --version
# Docker Buildx(マルチプラットフォームビルド用)
docker buildx version
# crane(イメージ検査用、オプション)
which crane || echo "Not installed"
# cosign(イメージ署名用、オプション)
which cosign || echo "Not installed"
インストール例(Ubuntu 22.04):
# Docker Buildx
git clone https://github.com/docker/buildx.git
cd buildx
make build
mkdir -p ~/.docker/cli-plugins
cp ./bin/build/docker-buildx ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
# cosign
wget https://github.com/sigstore/cosign/releases/download/v2.0.0/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
レジストリアカウント
- Azure Container Registry (ACR)
- Docker Hub
- GitHub Container Registry (GHCR)
- プライベートレジストリ(自社運用)
ステージ別ワークフロー
Phase 1: ローカル開発 (source モード)
目的: 機能実装・バグ修正・デバッグ
# デフォルトで source モード
docker build -t evospikenet:dev .
# 起動して動作確認
docker run --rm -it -p 18000:8000 evospikenet:dev bash
# ホットリロード用マウント
docker run --rm -p 18000:8000 \
-v $PWD/evospikenet:/opt/venv/lib/python3.10/site-packages/evospikenet \
evospikenet:dev
テスト実行:
# コンテナ内でテスト実行
docker run --rm evospikenet:dev pytest tests/
# または docker-compose を使用
docker compose up -d api
docker compose exec api pytest tests/api/
Phase 2: CI テストパイプライン
目的: 全ビルドモードの自動検証(GitHub Actions の例)
.github/workflows/docker-publish.yml:
name: Docker Build & Publish
on:
push:
branches: [main, develop]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/evospikenet
jobs:
build-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
mode: [source, wheel, cython, nuitka]
include:
- mode: source
target: runtime
tag_suffix: latest
- mode: wheel
target: runtime-wheel
tag_suffix: wheel
- mode: cython
target: runtime-cython
tag_suffix: cython
- mode: nuitka
target: runtime-nuitka
tag_suffix: nuitka
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch,suffix=-${{ matrix.tag_suffix }}
type=semver,pattern={{version}},suffix=-${{ matrix.tag_suffix }}
type=sha,suffix=-${{ matrix.tag_suffix }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./EvoSpikeNet-Core
target: ${{ matrix.target }}
build-args: |
APP_IMAGE_MODE=${{ matrix.mode }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Test image
run: |
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-${{ matrix.tag_suffix }} \
python -c "import evospikenet.api; print('✓ Image test passed')"
ローカル CI テスト:
# すべてのモードをビルド・テスト
for mode in source wheel cython nuitka; do
echo "Building mode: $mode"
target="runtime"
[[ "$mode" != "source" ]] && target="runtime-$mode"
docker build \
--build-arg APP_IMAGE_MODE=$mode \
--target $target \
-t evospikenet:test-$mode \
. || exit 1
done
echo "All modes built successfully"
Phase 3: ステージング検証 (wheel/cython モード)
目的: 本番環境に近い環境でテスト
3.1 ステージングレジストリへプッシュ
# ステージングレジストリ設定
STAGING_REGISTRY="staging.azurecr.io"
APP_NAME="evospikenet"
VERSION="1.0.0-rc1"
# Cython モードでビルド
docker build \
--build-arg APP_IMAGE_MODE=cython \
--target runtime-cython \
-t $STAGING_REGISTRY/$APP_NAME:$VERSION-cython \
.
# ステージングレジストリにプッシュ
docker login $STAGING_REGISTRY
docker push $STAGING_REGISTRY/$APP_NAME:$VERSION-cython
3.2 ステージング環境でのテスト
docker-compose.staging.yml:
version: '3.8'
services:
api:
image: staging.azurecr.io/evospikenet:1.0.0-rc1-cython
ports:
- "18000:8000"
environment:
- LOG_LEVEL=INFO
- ENVIRONMENT=staging
volumes:
- ./logs:/opt/venv/var/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- api
テスト実行:
# ステージング環境起動
docker compose -f docker-compose.staging.yml up -d
# ヘルスチェック
curl -v http://localhost:18000/health
# パフォーマンステスト
docker run --rm --network host \
loadimpact/k6 run \
-e BASE_URL=http://localhost:18000 \
tests/k6/performance.js
# ステージング環境停止
docker compose -f docker-compose.staging.yml down
Phase 4: 本番リリース (nuitka モード)
目的: 最高のセキュリティレベルで本番環境にデプロイ
4.1 本番イメージのビルド
# 本番レジストリ設定
PROD_REGISTRY="production.azurecr.io"
APP_NAME="evospikenet"
VERSION="1.0.0"
# Nuitka モードでビルド(時間がかかる)
docker build \
--build-arg APP_IMAGE_MODE=nuitka \
--target runtime-nuitka \
-t $PROD_REGISTRY/$APP_NAME:$VERSION \
-t $PROD_REGISTRY/$APP_NAME:latest-nuitka \
.
# イメージサイズ確認
docker images | grep $PROD_REGISTRY/$APP_NAME
4.2 イメージ署名(オプション)
# cosign をインストール(未インストール時)
curl -fsSLO https://github.com/sigstore/cosign/releases/download/v2.0.0/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# キーペアを生成(初回のみ)
cosign generate-key-pair
# イメージに署名
cosign sign --key cosign.key $PROD_REGISTRY/$APP_NAME:$VERSION
# プッシュ前に署名を検証
cosign verify --key cosign.pub $PROD_REGISTRY/$APP_NAME:$VERSION
4.3 本番レジストリへプッシュ
# 本番レジストリにログイン
docker login $PROD_REGISTRY
# イメージをプッシュ
docker push $PROD_REGISTRY/$APP_NAME:$VERSION
docker push $PROD_REGISTRY/$APP_NAME:latest-nuitka
# イメージが正常にプッシュされたか確認
curl -u $REGISTRY_USER:$REGISTRY_PASSWORD \
https://$PROD_REGISTRY/v2/$APP_NAME/manifests/$VERSION
4.4 本番環境へのデプロイ
Kubernetes 例:
# k8s/evospikenet-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: evospikenet-api
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: evospikenet-api
template:
metadata:
labels:
app: evospikenet-api
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: production.azurecr.io/evospikenet:1.0.0
imagePullPolicy: Always
ports:
- containerPort: 8000
name: http
env:
- name: LOG_LEVEL
value: "INFO"
- name: ENVIRONMENT
value: "production"
- name: EVOSPIKENET_API_KEY
valueFrom:
secretKeyRef:
name: evospikenet-secrets
key: api-key
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 2Gi
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: var-tmp
mountPath: /var/tmp
volumes:
- name: tmp
emptyDir: {}
- name: var-tmp
emptyDir: {}
imagePullSecrets:
- name: registry-credentials
デプロイコマンド:
# シークレット作成
kubectl create secret docker-registry registry-credentials \
--docker-server=$PROD_REGISTRY \
--docker-username=$REGISTRY_USER \
--docker-password=$REGISTRY_PASSWORD \
-n production
# デプロイメント実行
kubectl apply -f k8s/evospikenet-deployment.yaml
# ロールアウト進捗確認
kubectl rollout status deployment/evospikenet-api -n production
# Pod ログ確認
kubectl logs -f deployment/evospikenet-api -n production
レジストリ設定
Azure Container Registry (ACR)
# レジストリ作成(初回のみ)
az acr create --resource-group myResourceGroup \
--name myregistry --sku Basic
# ログイン認証情報取得
az acr credential show --name myregistry
# Docker ログイン
az acr login --name myregistry
# イメージをプッシュ
docker tag evospikenet:latest myregistry.azurecr.io/evospikenet:1.0.0
docker push myregistry.azurecr.io/evospikenet:1.0.0
Docker Hub
# ログイン
docker login
# イメージをプッシュ
docker tag evospikenet:latest myaccount/evospikenet:1.0.0
docker push myaccount/evospikenet:1.0.0
# 公開設定確認
curl -s https://hub.docker.com/v2/users/$USER/repositories | jq '.results[] | {name, is_private}'
GitHub Container Registry (GHCR)
# Personal Access Token (PAT) を生成
# GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
# スコープ: write:packages, read:packages, delete:packages
# 環境変数設定
export CR_PAT="ghp_xxxxxxxxxxxxx"
export REGISTRY=ghcr.io
export IMAGE_NAME=github.com/${{ github.repository }}/evospikenet
# ログイン
echo "$CR_PAT" | docker login $REGISTRY -u $GITHUB_ACTOR --password-stdin
# イメージをプッシュ
docker tag evospikenet:latest $REGISTRY/$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]'):1.0.0
docker push $REGISTRY/$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]'):1.0.0
プライベートレジストリ(オンプレミス)
# レジストリイメージ起動
docker run -d \
-p 5000:5000 \
--name registry \
-v registry-data:/var/lib/registry \
registry:2
# TLS 設定(本番推奨)
mkdir -p registry-certs
openssl req -new -newkey rsa:4096 -days 365 -nodes \
-x509 -keyout registry-certs/domain.key \
-out registry-certs/domain.crt
docker run -d \
-p 5000:5000 \
--name registry \
-v registry-data:/var/lib/registry \
-v registry-certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# イメージをプッシュ
docker tag evospikenet:latest localhost:5000/evospikenet:1.0.0
docker push localhost:5000/evospikenet:1.0.0
イメージ署名と検証
イメージ署名(cosign + Sigstore)
# 初回:キーペアを生成
cosign generate-key-pair
# イメージに署名
cosign sign --key cosign.key $REGISTRY/$IMAGE_NAME:$VERSION
# 署名を検証
cosign verify --key cosign.pub $REGISTRY/$IMAGE_NAME:$VERSION
SBOM(Software Bill of Materials)の生成
# syft でSBOM を生成
syft $REGISTRY/$IMAGE_NAME:$VERSION > sbom.spdx.json
# SBOM をイメージに添付
cosign attach sbom --sbom sbom.spdx.json $REGISTRY/$IMAGE_NAME:$VERSION
# SBOM を検索して表示
cosign download sbom $REGISTRY/$IMAGE_NAME:$VERSION
スキャン(脆弱性チェック)
# Trivy でスキャン
trivy image $REGISTRY/$IMAGE_NAME:$VERSION
# 重大度別フィルタ
trivy image --severity HIGH,CRITICAL $REGISTRY/$IMAGE_NAME:$VERSION
# JSON 形式で出力
trivy image -f json -o scan-report.json $REGISTRY/$IMAGE_NAME:$VERSION
セキュリティベストプラクティス
1. イメージ署名と検証の自動化
# GitHub Actions ワークフロー
- name: Sign image with Cosign
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.0.0'
- name: Run Cosign
run: |
cosign sign --key cosign.key ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
2. イメージスキャンの自動化
# スキャン・アクション
- name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
3. イメージの不変性
# イメージハッシュでプル(タグではなく)
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' $REGISTRY/$IMAGE_NAME:$VERSION)
docker pull $DIGEST
4. ネットワークセキュリティ
# Docker ログイン用の秘密管理(GitHub Secrets)
- name: Log in to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
5. ローカルイメージ管理
# ビルド後のイメージを自動削除(タグ付け後)
docker build ... -t $REGISTRY/$IMAGE_NAME:$VERSION .
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker rmi $REGISTRY/$IMAGE_NAME:$VERSION
# ローカルストレージをクリーンアップ
docker builder prune -a
docker image prune -a
CI/CD 統合
GitHub Actions 統合ワークフロー例
.github/workflows/docker-publish-prod.yml:
name: Build & Publish Production Image
on:
push:
tags: ['v*']
env:
REGISTRY: production.azurecr.io
IMAGE_NAME: evospikenet
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Production Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.PROD_REGISTRY_USERNAME }}
password: ${{ secrets.PROD_REGISTRY_PASSWORD }}
- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Build and push Nuitka image
uses: docker/build-push-action@v5
with:
context: ./EvoSpikeNet-Core
target: runtime-nuitka
build-args: APP_IMAGE_MODE=nuitka
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-nuitka
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
- name: Create Release Notes
run: |
cat > RELEASE_NOTES.md <<EOF
# Release: ${{ steps.version.outputs.VERSION }}
Image: \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}\`
Build Mode: nuitka (maximum source protection)
Run: \`docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}\`
EOF
- name: Upload Release
uses: softprops/action-gh-release@v1
with:
files: RELEASE_NOTES.md
トラブルシューティング
イメージプッシュ失敗
# エラー: denied: authorization failed
# → レジストリ認証確認
docker login $REGISTRY
docker push $IMAGE
# エラー: invalid reference format
# → イメージ名の形式確認
docker tag local-image:latest registry.com/namespace/image:1.0.0
# エラー: blob upload unknown
# → ネットワーク接続確認、レジストリのディスク容量確認
docker push --verbose $IMAGE
イメージ署名の問題
# cosign キーが見つからない
export COSIGN_KEY_LOCATION=~/.cosign/cosign.key
cosign sign $IMAGE
# 署名検証失敗
cosign verify --key cosign.pub $IMAGE
# → cosign.pub が正しい公開鍵か確認
Kubernetes デプロイメント失敗
# イメージプルエラー
kubectl describe pod <pod-name> -n production
# → imagePullSecrets の確認
# → レジストリ認証情報の確認
# シークレット確認
kubectl get secrets -n production
kubectl describe secret registry-credentials -n production
関連ドキュメント
- DOCKER_BUILD_MODES.md - ビルドモード詳細
- DOCKER_BUILD_GUIDE.md - docker-compose ビルド方法
- E2E_RUNBOOK.md - エンドツーエンド実行ガイド