プロジェクト

全般

プロフィール

バグ #758

未完了

【開発計画】インフラヘルパー Phase 5 - 最適化・品質向上

Redmine Admin さんが5日前に追加.

ステータス:
新規
優先度:
高め
担当者:
-
開始日:
2025-06-26
期日:
進捗率:

0%

予定工数:

説明

🎯 Phase 5: 最適化・品質向上

概要

インフラヘルパーサービスの性能最適化、品質向上、ドキュメント整備を行い、プロダクションレディな状態にする。

対応期限

2025年10月15日(4週間)

実装タスク

⚡ 5-1. パフォーマンス最適化

バックエンド最適化

// 1. データベースクエリ最適化
// - インデックス追加
CREATE INDEX idx_alerts_triggered_at ON alerts(triggered_at);
CREATE INDEX idx_logs_container_timestamp ON logs(container_name, timestamp);
CREATE INDEX idx_metrics_container_metric_time ON metrics(container_name, metric_name, timestamp);

// 2. キャッシング実装
const redis = require('redis');
const client = redis.createClient({ host: 'redis' });

class CacheService {
  async get(key) {
    return new Promise((resolve, reject) => {
      client.get(key, (err, data) => {
        if (err) reject(err);
        resolve(data ? JSON.parse(data) : null);
      });
    });
  }
  
  async set(key, value, ttl = 300) {
    return new Promise((resolve, reject) => {
      client.setex(key, ttl, JSON.stringify(value), (err) => {
        if (err) reject(err);
        resolve();
      });
    });
  }
  
  async invalidate(pattern) {
    const keys = await this.scan(pattern);
    if (keys.length > 0) {
      await client.del(...keys);
    }
  }
}

// 3. 非同期処理の最適化
class QueueService {
  constructor() {
    this.queue = new Bull('infra-helper', {
      redis: { host: 'redis', port: 6379 }
    });
    
    this.queue.process('backup', async (job) => {
      return await backupService.createBackup(job.data);
    });
    
    this.queue.process('scan', async (job) => {
      return await securityScanner.scan(job.data);
    });
  }
  
  async addJob(type, data, options = {}) {
    return await this.queue.add(type, data, {
      attempts: 3,
      backoff: { type: 'exponential', delay: 2000 },
      ...options
    });
  }
}

// 4. メモリ使用量最適化
// - ストリーミング処理
async function streamLargeLogs(containerName, res) {
  const container = docker.getContainer(containerName);
  const stream = await container.logs({
    stdout: true,
    stderr: true,
    follow: false,
    tail: 10000
  });
  
  stream.pipe(res);
}

// 5. 接続プーリング
const dockerPool = new GenericPool.Pool({
  create: () => new Docker(),
  destroy: (docker) => docker.close(),
  max: 10,
  min: 2
});

フロントエンド最適化

// 1. React最適化
import { memo, useMemo, useCallback, lazy, Suspense } from 'react';

// コンポーネントの遅延読み込み
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));

// メモ化されたコンポーネント
const ContainerCard = memo(({ container }) => {
  const status = useMemo(() => 
    calculateStatus(container), [container.id, container.state]
  );
  
  const handleClick = useCallback(() => {
    openContainerDetails(container.id);
  }, [container.id]);
  
  return (
    <div onClick={handleClick}>
      {/* コンポーネント内容 */}
    </div>
  );
});

// 2. バンドルサイズ最適化
// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    },
    usedExports: true,
    sideEffects: false
  }
};

// 3. 仮想スクロール実装
import { FixedSizeList } from 'react-window';

function LogViewer({ logs }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {logs[index].timestamp} - {logs[index].message}
    </div>
  );
  
  return (
    <FixedSizeList
      height={600}
      itemCount={logs.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

🧪 5-2. テスト強化

単体テスト

// backend/__tests__/authService.test.js
describe('AuthService', () => {
  let authService;
  
  beforeEach(() => {
    authService = new AuthService();
  });
  
  describe('validateRedmineApiKey', () => {
    it('should validate correct API key', async () => {
      const mockResponse = { user: { id: 1, login: 'admin' } };
      fetch.mockResolvedValue({
        ok: true,
        json: async () => mockResponse
      });
      
      const result = await authService.validateRedmineApiKey('valid-key');
      expect(result).toEqual(mockResponse.user);
    });
    
    it('should reject invalid API key', async () => {
      fetch.mockResolvedValue({ ok: false, status: 401 });
      
      await expect(authService.validateRedmineApiKey('invalid-key'))
        .rejects.toThrow('Invalid API key');
    });
  });
});

// 統合テスト
describe('API Integration Tests', () => {
  let app;
  let token;
  
  beforeAll(async () => {
    app = await createApp();
    // テスト用トークン取得
    const res = await request(app)
      .post('/api/v1/auth/login')
      .send({ apiKey: process.env.TEST_API_KEY });
    token = res.body.token;
  });
  
  describe('Container Management', () => {
    it('should list all containers', async () => {
      const res = await request(app)
        .get('/api/v1/docker/containers')
        .set('Authorization', `Bearer ${token}`)
        .expect(200);
      
      expect(res.body.success).toBe(true);
      expect(Array.isArray(res.body.data)).toBe(true);
    });
  });
});

E2Eテスト

// e2e/dashboard.spec.js
import { test, expect } from '@playwright/test';

test.describe('Dashboard', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://infra.call2arm.com');
    await page.fill('#apiKey', process.env.TEST_API_KEY);
    await page.click('button[type="submit"]');
    await page.waitForNavigation();
  });
  
  test('should display container list', async ({ page }) => {
    await expect(page.locator('h1')).toContainText('Infrastructure Helper');
    const containers = page.locator('[data-testid="container-card"]');
    await expect(containers).toHaveCount.greaterThan(0);
  });
  
  test('should update metrics in real-time', async ({ page }) => {
    const cpuValue = page.locator('[data-testid="cpu-metric"]');
    const initialValue = await cpuValue.textContent();
    
    await page.waitForTimeout(5000);
    const updatedValue = await cpuValue.textContent();
    
    expect(initialValue).not.toBe(updatedValue);
  });
});

📚 5-3. ドキュメント整備

APIドキュメント(OpenAPI)

openapi: 3.0.0
info:
  title: Infrastructure Helper API
  version: 1.2.0
  description: VPS-ROOT Infrastructure management service

servers:
  - url: https://infra.call2arm.com/api/v1

security:
  - bearerAuth: []

paths:
  /auth/login:
    post:
      summary: Authenticate with Redmine API key
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - apiKey
              properties:
                apiKey:
                  type: string
                  description: Redmine API key
      responses:
        200:
          description: Successful authentication
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  token:
                    type: string
                  user:
                    $ref: '#/components/schemas/User'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        login:
          type: string
        admin:
          type: boolean
        email:
          type: string

ユーザーマニュアル

# Infrastructure Helper ユーザーマニュアル

## 目次
1. はじめに
2. 初期設定
3. 基本機能
4. 高度な機能
5. トラブルシューティング
6. FAQ

## 1. はじめに

Infrastructure Helperは、VPS-ROOT環境を効率的に管理するためのWebベースのツールです。

### 主な機能
- Dockerコンテナ管理
- リアルタイム監視
- ログ管理
- バックアップ・復旧
- 自動スケーリング
- セキュリティ監査

## 2. 初期設定

### 2.1 アクセス方法
1. https://infra.call2arm.com にアクセス
2. Redmine APIキーでログイン

### 2.2 APIキーの取得
1. Redmineにログイン
2. 「個人設定」→「APIアクセスキー」
3. 「表示」をクリックしてキーをコピー

## 3. 基本機能

### 3.1 ダッシュボード
メインダッシュボードでは以下の情報が確認できます:
- 稼働中のコンテナ数
- システムリソース使用状況
- 最近のアラート
- クイックアクション

### 3.2 コンテナ管理
#### コンテナ一覧表示
左メニューから「Containers」を選択...

🔒 5-4. セキュリティ強化

セキュリティ実装

// 1. セキュリティヘッダー
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", "cdn.tailwindcss.com"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "wss://infra.call2arm.com"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

// 2. 入力検証
const { body, validationResult } = require('express-validator');

app.post('/api/v1/scripts/execute',
  authenticateToken,
  [
    body('scriptType').isIn(['vps_status', 'nginx_reload', 'docker_health']),
    body('args').optional().isObject()
  ],
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // 処理続行
  }
);

// 3. SQLインジェクション対策
const db = new Database();
const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
const user = stmt.get(userId);

// 4. レート制限
const rateLimiter = new RateLimiter({
  windowMs: 15 * 60 * 1000,
  max: 100,
  skipSuccessfulRequests: false,
  keyGenerator: (req) => req.user?.id || req.ip
});

// 5. 監査ログ
class AuditLogger {
  async log(action, user, details) {
    await db.insert('audit_logs', {
      action,
      user_id: user.id,
      user_login: user.login,
      ip_address: user.ipAddress,
      user_agent: user.userAgent,
      details: JSON.stringify(details),
      timestamp: new Date()
    });
  }
}

📈 5-5. 監視・運用

監視設定

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'infra-helper'
    static_configs:
      - targets: ['infra-helper-api:3000']
    metrics_path: '/api/v1/metrics/prometheus'

# alerts.yml
groups:
  - name: infra-helper
    rules:
      - alert: HighMemoryUsage
        expr: process_resident_memory_bytes > 1e9
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage detected"
          
      - alert: APIHighLatency
        expr: http_request_duration_seconds{quantile="0.99"} > 1
        for: 5m
        labels:
          severity: critical

成果物

  • パフォーマンス最適化済みコード
  • 90%以上のテストカバレッジ
  • 完全なAPIドキュメント
  • 日本語/英語ユーザーマニュアル
  • 運用手順書
  • 監視ダッシュボード設定
  • セキュリティ監査レポート
  • パフォーマンスベンチマーク結果

表示するデータがありません

他の形式にエクスポート: Atom PDF