mcpField
A
aws-cloud-services

aws-cloud-services

github.com/manutejクラウド・DevOps62450 回インストール39 週間前に更新
リポジトリ

aws-cloud-services について

aws-cloud-servicesは、Amazon Web Servicesのインフラを構築・運用する開発者やアーキテクト向けの学習スキルです。主要サービスに加え、セキュリティ、デプロイ、設計の実践を扱います。

主な機能

  1. 01S3オブジェクトストレージ操作
  2. 02Lambdaイベント駆動関数
  3. 03DynamoDBとRDSのデータベースガイド
  4. 04IAMアクセス制御と認証情報管理
  5. 05CloudFormationによるインフラ自動化
  6. 06アーキテクチャとコスト最適化の指針

活用シーン

  1. 01Lambdaを使ったサーバーレスアプリの設計
  2. 02S3のファイルアップロードとダウンロード管理
  3. 03最小権限のIAMポリシー設定
  4. 04CloudFormationによるインフラの自動デプロイ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/manutej/luxor-claude-marketplace --skill aws-cloud-services

インストール後、Claude Code で/aws-cloud-servicesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.summary.txt
EXAMPLES.md
README.md

詳細

AWS クラウドサービス

Amazon Web Services(AWS)上でクラウドインフラストラクチャを構築、デプロイ、管理するための包括的なスキルです。S3 オブジェクトストレージ、Lambda サーバーレス関数、DynamoDB NoSQL データベース、EC2 コンピューティングインスタンス、RDS リレーショナルデータベース、IAM セキュリティ、CloudFormation による Infrastructure as Code、エンタープライズグレードのクラウドアーキテクチャパターンを習得します。

このスキルを使用する場面

次のような場合にこのスキルを使用します。

  • AWS インフラストラクチャ上でスケーラブルなクラウドアプリケーションを構築する
  • Lambda と API Gateway を使用してサーバーレスアーキテクチャを実装する
  • S3 を使用してオブジェクトストレージとファイルアップロードを管理する
  • DynamoDB を使用して NoSQL データベースソリューションを設計する
  • EC2 インスタンスをデプロイし、コンピューティングリソースを管理する
  • リレーショナルデータの保存に RDS データベースを設定する
  • IAM セキュリティポリシーとアクセス制御を実装する
  • CloudFormation を使用してインフラストラクチャのデプロイを自動化する
  • マルチリージョンの高可用性システムを設計する
  • クラウドのコストとパフォーマンスを最適化する
  • オンプレミスのアプリケーションを AWS に移行する
  • イベント駆動型アーキテクチャを実装する
  • データパイプラインと分析ソリューションを構築する
  • シークレットと認証情報を安全に管理する
  • AWS サービスを使用して CI/CD パイプラインを設定する

主な概念

AWS の基礎

AWS は、コンピューティング、ストレージ、データベース、ネットワーク、セキュリティなどにわたる 200 以上のサービスを提供する、Amazon の包括的なクラウドコンピューティングプラットフォームです。

主な概念

リージョンとアベイラビリティーゾーン

  • リージョン:複数のデータセンターを擁する地理的なエリア(例:us-east-1、eu-west-1)
  • アベイラビリティーゾーン(AZ):リージョン内にある独立したデータセンター
  • エッジロケーション:CloudFront のコンテンツ配信に使用する CDN エンドポイント
  • ローカルゾーン:超低レイテンシーを実現するためにリージョンを拡張したもの

AWS アカウント構成

  • ルートアカウント:すべてのアクセス権を持つプライマリアカウント(使用は最小限にしてください)
  • IAM ユーザー:特定の権限を持つ個別ユーザーアカウント
  • IAM ロール:サービスやアプリケーション向けの一時的な認証情報
  • Organizations:企業向けのマルチアカウント管理

サービスカテゴリ

  • コンピューティング:EC2、Lambda、ECS、EKS、Fargate
  • ストレージ:S3、EBS、EFS、Glacier
  • データベース:RDS、DynamoDB、Aurora、ElastiCache、Redshift
  • ネットワーク:VPC、Route 53、CloudFront、API Gateway、ELB
  • セキュリティ:IAM、Cognito、Secrets Manager、KMS、WAF
  • インフラストラクチャ:CloudFormation、CDK、Systems Manager
  • モニタリング:CloudWatch、X-Ray、CloudTrail
JavaScript 用 AWS SDK v3

AWS SDK v3 はモジュール型で、ツリーシェイキングに対応し、最新の JavaScript/TypeScript アプリケーション向けに最適化されています。

主な改善点

モジュール型アーキテクチャ

// v2 (monolithic)
const AWS = require('aws-sdk');
const s3 = new AWS.S3();

// v3 (modular)
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const client = new S3Client({ region: 'us-east-1' });

コマンドパターン

  • 各操作はコマンドクラスです
  • クライアントとコマンドが明確に分離されています
  • TypeScript のサポートと型推論が向上しています

ミドルウェアスタック

  • カスタマイズ可能なリクエスト/レスポンスのパイプライン
  • リトライと指数バックオフを標準でサポート
  • リクエストの署名と認証
アイデンティティとアクセス管理(IAM)

IAM は、すべての AWS サービスにおける認証と認可を制御します。

IAM の主要コンポーネント

ユーザー

  • 長期的な認証情報を持つ個別の ID
  • プログラムによるアクセス用のアクセスキー
  • コンソールアクセス用のパスワード
  • MFA(多要素認証)のサポート

グループ

  • ユーザーの集合
  • ポリシーを適用し、権限を一括管理
  • ユーザーは複数のグループに所属できます

ロール

  • ユーザー、サービス、アプリケーションが引き受ける一時的な認証情報
  • クロスアカウントアクセス
  • サービス間通信
  • 外部 ID プロバイダーとのフェデレーション

ポリシー

  • 権限を定義する JSON ドキュメント
  • ID ベースのポリシー(ユーザー/グループ/ロールに適用)
  • リソースベースのポリシー(S3 バケットなどのリソースに適用)
  • Organizations 向けのサービスコントロールポリシー(SCP)
ポリシーの構造
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        }
      }
    }
  ]
}

ポリシーの要素

  • Effect:許可または拒否
  • Action:サービスと操作(例:s3:GetObject)
  • Resource:影響を受けるリソースの ARN
  • Condition:任意の制約(IP、時間、MFA など)
  • Principal:ポリシーの適用対象(リソースベースのポリシーの場合)
最小権限の原則

必要最小限の権限のみを常に付与してください。

  • 権限のない状態から始めます
  • 必要に応じて権限を段階的に追加します
  • 一般的なパターンには管理ポリシーを使用します
  • 特定のユースケースにはカスタムポリシーを作成します
  • 定期的に監査し、未使用の権限を削除します
認証情報の管理
認証情報チェーン

SDK は次の順序で認証情報を検索します。

  1. 環境変数:AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY
  2. 共有認証情報ファイル:~/.aws/credentials
  3. 共有設定ファイル:~/.aws/config
  4. IAM ロール(EC2/ECS/Lambda):インスタンスメタデータサービス
  5. プロセス認証情報:カスタム実行可能ファイルから取得
ベストプラクティス
  • ソースコードに認証情報をハードコードしないでください
  • EC2、Lambda、ECS ではIAM ロールを使用してください
  • 可能な限り一時的な認証情報を使用してください
  • アクセスキーを定期的にローテーションしてください(推奨期間は 90 日です)
  • アプリケーションのシークレットにはAWS Secrets Manager を使用してください
  • 特権アカウントではMFA を有効にしてください
  • 一元的なアクセス管理にはAWS SSO を使用してください
リージョンとエンドポイントの設定
import { S3Client } from '@aws-sdk/client-s3';

// Specify region explicitly
const client = new S3Client({
  region: 'us-west-2',
  endpoint: 'https://s3.us-west-2.amazonaws.com' // Optional custom endpoint
});

// Use default region from environment/config
const defaultClient = new S3Client({}); // Uses AWS_REGION or default region

S3(Simple Storage Service)

S3 は、あらゆる場所から任意の量のデータを保存、取得できる AWS のオブジェクトストレージサービスです。

S3 の基本概念
バケット
  • グローバルに一意な名前:すべての AWS アカウント間で一意である必要があります
  • リージョナルリソース:特定のリージョンに作成されます
  • オブジェクト数無制限:オブジェクト数に制限はありません
  • バケットポリシー:リソースベースのアクセス制御
  • バージョニング:オブジェクトの複数のバージョンを保持します
  • 暗号化:サーバー側とクライアント側の暗号化
オブジェクト
  • キーと値のストア:キーはオブジェクト名、値はデータです
  • メタデータ:システム定義およびユーザー定義のメタデータ
  • サイズ制限:オブジェクトあたり 5TB
  • マルチパートアップロード:100MB を超えるオブジェクトに使用します(5GB を超える場合は必須)
  • ストレージクラス:Standard、IA、Glacier など
S3 ストレージクラス

S3 Standard

  • 頻繁にアクセスされるデータ
  • 可用性 99.99%
  • ミリ秒単位のレイテンシー

S3 Intelligent-Tiering

  • コストを自動的に最適化します
  • データをアクセス階層間で移動します

S3 Standard-IA(低頻度アクセス)

  • アクセス頻度の低いデータを低コストで保存します
  • データ取得料金が適用されます

S3 One Zone-IA

  • 重要度の低いデータ向けの単一 AZ ストレージ
  • Standard-IA より 20% 安価です

S3 Glacier

  • 長期アーカイブ
  • 取得に数分から数時間かかります
  • 非常に低コストです

S3 Glacier Deep Archive

  • 最も低コストのストレージ
  • 取得に 12 時間かかります
  • コンプライアンス用アーカイブに最適です
S3 の操作
オブジェクトのアップロード
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { readFileSync } from 'fs';

const client = new S3Client({ region: 'us-east-1' });

// Simple upload
const uploadFile = async (bucketName, key, filePath) => {
  const fileContent = readFileSync(filePath);

  const command = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    Body: fileContent,
    ContentType: 'image/jpeg', // Optional
    Metadata: { // Optional custom metadata
      'uploaded-by': 'user-123',
      'upload-date': new Date().toISOString()
    },
    ServerSideEncryption: 'AES256', // Enable encryption
    ACL: 'private' // Access control
  });

  const response = await client.send(command);
  return response;
};
オブジェクトのダウンロード
import { GetObjectCommand } from '@aws-sdk/client-s3';
import { writeFileSync } from 'fs';

const downloadFile = async (bucketName, key, destinationPath) => {
  const command = new GetObjectCommand({
    Bucket: bucketName,
    Key: key
  });

  const response = await client.send(command);

  // Convert stream to buffer
  const chunks = [];
  for await (const chunk of response.Body) {
    chunks.push(chunk);
  }
  const buffer = Buffer.concat(chunks);

  writeFileSync(destinationPath, buffer);
  return response.Metadata;
};
オブジェクトの一覧表示
import { ListObjectsV2Command } from '@aws-sdk/client-s3';

const listObjects = async (bucketName, prefix = '') => {
  const command = new ListObjectsV2Command({
    Bucket: bucketName,
    Prefix: prefix, // Filter by prefix
    MaxKeys: 1000, // Max 1000 per request
    Delimiter: '/' // Treat / as folder separator
  });

  const response = await client.send(command);
  return response.Contents; // Array of objects
};

// Pagination for large buckets
const listAllObjects = async (bucketName) => {
  let allObjects = [];
  let continuationToken;

  do {
    const command = new ListObjectsV2Command({
      Bucket: bucketName,
      ContinuationToken: continuationToken
    });

    const response = await client.send(command);
    allObjects = allObjects.concat(response.Contents || []);
    continuationToken = response.NextContinuationToken;
  } while (continuationToken);

  return allObjects;
};
オブジェクトの削除
import { DeleteObjectCommand, DeleteObjectsCommand } from '@aws-sdk/client-s3';

// Delete single object
const deleteObject = async (bucketName, key) => {
  const command = new DeleteObjectCommand({
    Bucket: bucketName,
    Key: key
  });

  await client.send(command);
};

// Delete multiple objects (up to 1000 at once)
const deleteMultipleObjects = async (bucketName, keys) => {
  const command = new DeleteObjectsCommand({
    Bucket: bucketName,
    Delete: {
      Objects: keys.map(key => ({ Key: key })),
      Quiet: false // Return list of deleted objects
    }
  });

  const response = await client.send(command);
  return response.Deleted;
};
署名付き URL

AWS 認証情報を使わずにファイルを安全にアップロード/ダウンロードするための、一時的な URL を生成します。

import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';

// Presigned URL for upload
const createUploadUrl = async (bucketName, key, expiresIn = 3600) => {
  const command = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    ContentType: 'image/jpeg'
  });

  const url = await getSignedUrl(client, command, { expiresIn });
  return url; // Client can PUT to this URL
};

// Presigned URL for download
const createDownloadUrl = async (bucketName, key, expiresIn = 3600) => {
  const command = new GetObjectCommand({
    Bucket: bucketName,
    Key: key
  });

  const url = await getSignedUrl(client, command, { expiresIn });
  return url; // Client can GET from this URL
};
マルチパートアップロード

大きなファイル(100MB 超)には、パフォーマンスと信頼性を高めるためにマルチパートアップロードを使用します。

import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand,
  AbortMultipartUploadCommand
} from '@aws-sdk/client-s3';

const multipartUpload = async (bucketName, key, fileBuffer, partSize = 5 * 1024 * 1024) => {
  // 1. Initiate multipart upload
  const createCommand = new CreateMultipartUploadCommand({
    Bucket: bucketName,
    Key: key
  });

  const { UploadId } = await client.send(createCommand);

  try {
    // 2. Upload parts
    const parts = [];
    const numParts = Math.ceil(fileBuffer.length / partSize);

    for (let i = 0; i < numParts; i++) {
      const start = i * partSize;
      const end = Math.min(start + partSize, fileBuffer.length);
      const partBody = fileBuffer.slice(start, end);

      const uploadCommand = new UploadPartCommand({
        Bucket: bucketName,
        Key: key,
        UploadId,
        PartNumber: i + 1,
        Body: partBody
      });

      const { ETag } = await client.send(uploadCommand);
      parts.push({ PartNumber: i + 1, ETag });
    }

    // 3. Complete multipart upload
    const completeCommand = new CompleteMultipartUploadCommand({
      Bucket: bucketName,
      Key: key,
      UploadId,
      MultipartUpload: { Parts: parts }
    });

    const result = await client.send(completeCommand);
    return result;

  } catch (error) {
    // Abort on error to avoid storage charges for incomplete uploads
    const abortCommand = new AbortMultipartUploadCommand({
      Bucket: bucketName,
      Key: key,
      UploadId
    });
    await client.send(abortCommand);
    throw error;
  }
};

Lambda

AWS Lambda は、サーバーをプロビジョニングせずに、イベントに応じてコードを実行するサーバーレスコンピューティングサービスです。

Lambda の基本概念
実行モデル
  • イベント駆動型:AWS サービスからのイベントまたは HTTP リクエストによって起動されます
  • ステートレス:呼び出しはそれぞれ独立しています
  • 同時実行:需要に応じて自動的にスケールします
  • タイムアウト:最大実行時間は 15 分です
  • メモリ:128MB から 10GB(CPU はメモリに応じてスケールします)
ハンドラー関数
// Lambda handler signature
export const handler = async (event, context) => {
  // event: Input data (API request, S3 event, etc.)
  // context: Runtime information (request ID, remaining time, etc.)

  console.log('Event:', JSON.stringify(event, null, 2));
  console.log('Request ID:', context.requestId);
  console.log('Remaining time:', context.getRemainingTimeInMillis());

  // Process event
  const result = await processEvent(event);

  // Return response
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(result)
  };
};
呼び出しタイプ

同期(RequestResponse)

  • API Gateway、SDK による呼び出し
  • 呼び出し元はレスポンスを待機します
  • エラーは呼び出し元に返されます

非同期(Event)

  • S3、SNS、CloudWatch Events
  • Lambda はイベントをキューに登録し、すぐに応答を返します
  • 組み込みの再試行(2 回)
  • 失敗時のデッドレターキュー

ポーリングベース(Stream)

  • DynamoDB Streams、Kinesis
  • Lambda はストリームをポーリングして関数を呼び出します
  • シャード内で順序どおりに処理します
Lambda の設定
// Using AWS SDK to create/update Lambda function
import {
  LambdaClient,
  CreateFunctionCommand,
  UpdateFunctionCodeCommand,
  UpdateFunctionConfigurationCommand
} from '@aws-sdk/client-lambda';

const lambdaClient = new LambdaClient({ region: 'us-east-1' });

const createFunction = async () => {
  const command = new CreateFunctionCommand({
    FunctionName: 'myFunction',
    Runtime: 'nodejs20.x',
    Role: 'arn:aws:iam::123456789012:role/lambda-execution-role',
    Handler: 'index.handler',
    Code: {
      ZipFile: zipBuffer // Or S3Bucket/S3Key for S3-stored code
    },
    Environment: {
      Variables: {
        'BUCKET_NAME': 'my-bucket',
        'TABLE_NAME': 'my-table'
      }
    },
    MemorySize: 512, // MB
    Timeout: 30, // seconds
    Tags: {
      'Environment': 'production',
      'Team': 'backend'
    }
  });

  const response = await lambdaClient.send(command);
  return response.FunctionArn;
};
Lambda のイベントソース
API Gateway との統合
// Lambda function for API Gateway
export const handler = async (event) => {
  // Parse request
  const { httpMethod, path, queryStringParameters, body } = event;
  const requestBody = body ? JSON.parse(body) : null;

  // Route based on HTTP method and path
  if (httpMethod === 'GET' && path === '/users') {
    const users = await getUsers();
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(users)
    };
  }

  if (httpMethod === 'POST' && path === '/users') {
    const newUser = await createUser(requestBody);
    return {
      statusCode: 201,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newUser)
    };
  }

  // Not found
  return {
    statusCode: 404,
    body: JSON.stringify({ message: 'Not found' })
  };
};
S3 イベントとの統合
// Lambda function triggered by S3 events
export const handler = async (event) => {
  // Process each S3 event record
  for (const record of event.Records) {
    const bucket = record.s3.bucket.name;
    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
    const eventName = record.eventName;

    console.log(`Event: ${eventName}, Bucket: ${bucket}, Key: ${key}`);

    if (eventName.startsWith('ObjectCreated:')) {
      await processNewFile(bucket, key);
    } else if (eventName.startsWith('ObjectRemoved:')) {
      await handleFileDeleted(bucket, key);
    }
  }

  return { statusCode: 200 };
};
DynamoDB Streams との統合
// Lambda function for DynamoDB Streams
export const handler = async (event) => {
  for (const record of event.Records) {
    const { eventName, dynamodb } = record;

    // INSERT, MODIFY, REMOVE
    console.log(`Event: ${eventName}`);

    if (eventName === 'INSERT') {
      const newItem = AWS.DynamoDB.Converter.unmarshall(dynamodb.NewImage);
      await handleNewItem(newItem);
    }

    if (eventName === 'MODIFY') {
      const oldItem = AWS.DynamoDB.Converter.unmarshall(dynamodb.OldImage);
      const newItem = AWS.DynamoDB.Converter.unmarshall(dynamodb.NewImage);
      await handleItemUpdate(oldItem, newItem);
    }

    if (eventName === 'REMOVE') {
      const oldItem = AWS.DynamoDB.Converter.unmarshall(dynamodb.OldImage);
      await handleItemDeleted(oldItem);
    }
  }
};
Lambda のベストプラクティス

コールドスタートの最適化

  • デプロイパッケージを小さく保ちます
  • 外部依存関係を最小限に抑えます
  • レイテンシーに敏感な関数にはプロビジョニング済み同時実行数を使用します
  • SDK クライアントをハンドラーの外部で初期化します

エラー処理

export const handler = async (event) => {
  try {
    // Process event
    const result = await processEvent(event);
    return { statusCode: 200, body: JSON.stringify(result) };

  } catch (error) {
    console.error('Error processing event:', error);

    // Log to CloudWatch
    console.error('Error details:', {
      message: error.message,
      stack: error.stack,
      event
    });

    // Return error response
    return {
      statusCode: 500,
      body: JSON.stringify({
        error: 'Internal server error',
        requestId: context.requestId
      })
    };
  }
};

環境変数

// Access environment variables
const BUCKET_NAME = process.env.BUCKET_NAME;
const TABLE_NAME = process.env.TABLE_NAME;
const API_KEY = process.env.API_KEY; // Use Secrets Manager for sensitive data

DynamoDB

DynamoDB は、あらゆる規模で 1 桁ミリ秒のパフォーマンスを実現する、フルマネージドの NoSQL データベースサービスです。

DynamoDB の基本概念
テーブルと項目

テーブル:項目のコレクション(SQL のテーブルに相当) 項目:個々のレコード(行に相当)、最大 400KB 属性:キーと値のペア(列に相当) プライマリキー:各項目を一意に識別します

プライマリキーのタイプ

パーティションキー(単純なプライマリキー)

User Table:
- userId (Partition Key) -> "user-123"
- name -> "John Doe"
- email -> "[email protected]"

パーティションキー + ソートキー(複合プライマリキー)

Order Table:
- userId (Partition Key) -> "user-123"
- orderId (Sort Key) -> "order-456"
- total -> 99.99
- status -> "shipped"
インデックス

グローバルセカンダリインデックス(GSI)

  • 異なるパーティションキー、または異なるソートキーを使用します
  • すべてのパーティションにまたがります
  • 結果整合性があります
  • いつでも作成・削除できます

ローカルセカンダリインデックス(LSI)

  • 同じパーティションキーを使用し、ソートキーは異なります
  • パーティションの範囲内に限定されます
  • 強い整合性または結果整合性があります
  • テーブルの作成時に作成する必要があります
DynamoDB の操作
項目の登録
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';

const client = new DynamoDBClient({ region: 'us-east-1' });
const docClient = DynamoDBDocumentClient.from(client);

const putItem = async (tableName, item) => {
  const command = new PutCommand({
    TableName: tableName,
    Item: item,
    ConditionExpression: 'attribute_not_exists(userId)', // Prevent overwrite
    ReturnValues: 'ALL_OLD' // Return previous item if existed
  });

  try {
    const response = await docClient.send(command);
    return response;
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      console.log('Item already exists');
    }
    throw error;
  }
};

// Example usage
await putItem('Users', {
  userId: 'user-123',
  name: 'John Doe',
  email: '[email protected]',
  createdAt: new Date().toISOString(),
  preferences: {
    theme: 'dark',
    notifications: true
  }
});
項目の取得
import { GetCommand } from '@aws-sdk/lib-dynamodb';

const getItem = async (tableName, key) => {
  const command = new GetCommand({
    TableName: tableName,
    Key: key,
    ConsistentRead: true, // Strong consistency (default: false)
    ProjectionExpression: 'userId, #n, email', // Return specific attributes
    ExpressionAttributeNames: {
      '#n': 'name' // name is reserved word, use placeholder
    }
  });

  const response = await docClient.send(command);
  return response.Item;
};

// Example usage
const user = await getItem('Users', { userId: 'user-123' });
項目の更新
import { UpdateCommand } from '@aws-sdk/lib-dynamodb';

const updateItem = async (tableName, key, updates) => {
  const command = new UpdateCommand({
    TableName: tableName,
    Key: key,
    UpdateExpression: 'SET #n = :name, email = :email, updatedAt = :now',
    ExpressionAttributeNames: {
      '#n': 'name'
    },
    ExpressionAttributeValues: {
      ':name': updates.name,
      ':email': updates.email,
      ':now': new Date().toISOString()
    },
    ConditionExpression: 'attribute_exists(userId)', // Only update if exists
    ReturnValues: 'ALL_NEW' // Return updated item
  });

  const response = await docClient.send(command);
  return response.Attributes;
};

// Atomic counter increment
const incrementCounter = async (tableName, key, counterAttribute) => {
  const command = new UpdateCommand({
    TableName: tableName,
    Key: key,
    UpdateExpression: 'ADD #counter :inc',
    ExpressionAttributeNames: {
      '#counter': counterAttribute
    },
    ExpressionAttributeValues: {
      ':inc': 1
    },
    ReturnValues: 'UPDATED_NEW'
  });

  const response = await docClient.send(command);
  return response.Attributes[counterAttribute];
};
クエリ

同じパーティションキーを持つ項目を効率的にクエリします。

import { QueryCommand } from '@aws-sdk/lib-dynamodb';

const queryItems = async (tableName, partitionKeyValue) => {
  const command = new QueryCommand({
    TableName: tableName,
    KeyConditionExpression: 'userId = :userId AND orderId BETWEEN :start AND :end',
    ExpressionAttributeValues: {
      ':userId': partitionKeyValue,
      ':start': 'order-100',
      ':end': 'order-200'
    },
    FilterExpression: 'orderStatus = :status', // Filter results (applied after query)
    ExpressionAttributeValues: {
      ':status': 'completed'
    },
    Limit: 100, // Max items to return
    ScanIndexForward: false // Sort descending (default: ascending)
  });

  const response = await docClient.send(command);
  return response.Items;
};

// Pagination
const queryAllItems = async (tableName, partitionKeyValue) => {
  let allItems = [];
  let lastEvaluatedKey;

  do {
    const command = new QueryCommand({
      TableName: tableName,
      KeyConditionExpression: 'userId = :userId',
      ExpressionAttributeValues: {
        ':userId': partitionKeyValue
      },
      ExclusiveStartKey: lastEvaluatedKey
    });

    const response = await docClient.send(command);
    allItems = allItems.concat(response.Items);
    lastEvaluatedKey = response.LastEvaluatedKey;
  } while (lastEvaluatedKey);

  return allItems;
};
スキャン

テーブル全体をスキャンします(非効率なため、本番環境では避けてください)。

import { ScanCommand } from '@aws-sdk/lib-dynamodb';

const scanTable = async (tableName, filterExpression) => {
  const command = new ScanCommand({
    TableName: tableName,
    FilterExpression: 'age > :minAge',
    ExpressionAttributeValues: {
      ':minAge': 18
    },
    Limit: 1000
  });

  const response = await docClient.send(command);
  return response.Items;
};

// Parallel scan for performance
const parallelScan = async (tableName, totalSegments = 4) => {
  const scanSegment = async (segment) => {
    const command = new ScanCommand({
      TableName: tableName,
      Segment: segment,
      TotalSegments: totalSegments
    });

    const response = await docClient.send(command);
    return response.Items;
  };

  // Scan all segments in parallel
  const promises = [];
  for (let i = 0; i < totalSegments; i++) {
    promises.push(scanSegment(i));
  }

  const results = await Promise.all(promises);
  return results.flat();
};
項目の削除
import { DeleteCommand } from '@aws-sdk/lib-dynamodb';

const deleteItem = async (tableName, key) => {
  const command = new DeleteCommand({
    TableName: tableName,
    Key: key,
    ConditionExpression: 'attribute_exists(userId)', // Only delete if exists
    ReturnValues: 'ALL_OLD' // Return deleted item
  });

  const response = await docClient.send(command);
  return response.Attributes;
};
バッチ操作
import { BatchGetCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';

// Batch get (up to 100 items)
const batchGetItems = async (tableName, keys) => {
  const command = new BatchGetCommand({
    RequestItems: {
      [tableName]: {
        Keys: keys // Array of key objects
      }
    }
  });

  const response = await docClient.send(command);
  return response.Responses[tableName];
};

// Batch write (up to 25 items)
const batchWriteItems = async (tableName, items) => {
  const command = new BatchWriteCommand({
    RequestItems: {
      [tableName]: items.map(item => ({
        PutRequest: { Item: item }
      }))
    }
  });

  await docClient.send(command);
};

// Batch delete
const batchDeleteItems = async (tableName, keys) => {
  const command = new BatchWriteCommand({
    RequestItems: {
      [tableName]: keys.map(key => ({
        DeleteRequest: { Key: key }
      }))
    }
  });

  await docClient.send(command);
};
DynamoDB のパターン
シングルテーブル設計

複雑なデータモデルには、キーを多目的に使用する 1 つのテーブルを使います。

// User entity
{
  PK: "USER#user-123",
  SK: "METADATA",
  type: "user",
  name: "John Doe",
  email: "[email protected]"
}

// User's order
{
  PK: "USER#user-123",
  SK: "ORDER#order-456",
  type: "order",
  total: 99.99,
  status: "shipped"
}

// Access patterns:
// 1. Get user: PK = "USER#user-123", SK = "METADATA"
// 2. Get all user's orders: PK = "USER#user-123", SK begins_with "ORDER#"
// 3. Get specific order: PK = "USER#user-123", SK = "ORDER#order-456"

EC2(Elastic Compute Cloud)

EC2 は、仮想マシン(インスタンス)を使用して、クラウドでサイズ変更可能なコンピューティング容量を提供します。

EC2 の基本概念
インスタンスタイプ

汎用(T3、M6i)

  • CPU、メモリ、ネットワークのバランスが取れています
  • Web サーバー、開発環境

コンピューティング最適化(C6i)

  • 高性能プロセッサ
  • バッチ処理、ゲームサーバー

メモリ最適化(R6i、X2idn)

  • 大規模なインメモリワークロード
  • データベース、キャッシュレイヤー

ストレージ最適化(I4i、D3)

  • シーケンシャル読み取り/書き込み性能が高い
  • データウェアハウス、分散ファイルシステム

高速コンピューティング(P4、G5)

  • GPU インスタンス
  • 機械学習、グラフィックスレンダリング
AMI(Amazon Machine Image)

インスタンス用の事前設定済みテンプレートで、次のものが含まれます。

  • オペレーティングシステム(Amazon Linux、Ubuntu、Windows など)
  • アプリケーションソフトウェア
  • 設定
EC2 の操作
import {
  EC2Client,
  RunInstancesCommand,
  DescribeInstancesCommand,
  StartInstancesCommand,
  StopInstancesCommand,
  TerminateInstancesCommand
} from '@aws-sdk/client-ec2';

const ec2Client = new EC2Client({ region: 'us-east-1' });

// Launch instance
const launchInstance = async () => {
  const command = new RunInstancesCommand({
    ImageId: 'ami-0c55b159cbfafe1f0', // Amazon Linux 2 AMI
    InstanceType: 't3.micro',
    MinCount: 1,
    MaxCount: 1,
    KeyName: 'my-key-pair',
    SecurityGroupIds: ['sg-0123456789abcdef0'],
    SubnetId: 'subnet-0123456789abcdef0',
    IamInstanceProfile: {
      Name: 'ec2-instance-profile'
    },
    UserData: Buffer.from(`#!/bin/bash
      yum update -y
      yum install -y httpd
      systemctl start httpd
      systemctl enable httpd
      echo "Hello from EC2" > /var/www/html/index.html
    `).toString('base64'),
    TagSpecifications: [{
      ResourceType: 'instance',
      Tags: [
        { Key: 'Name', Value: 'WebServer' },
        { Key: 'Environment', Value: 'production' }
      ]
    }]
  });

  const response = await ec2Client.send(command);
  return response.Instances[0].InstanceId;
};

// Describe instances
const describeInstances = async (instanceIds) => {
  const command = new DescribeInstancesCommand({
    InstanceIds: instanceIds,
    Filters: [
      { Name: 'instance-state-name', Values: ['running'] }
    ]
  });

  const response = await ec2Client.send(command);
  return response.Reservations.flatMap(r => r.Instances);
};

// Stop instance
const stopInstance = async (instanceId) => {
  const command = new StopInstancesCommand({
    InstanceIds: [instanceId]
  });

  await ec2Client.send(command);
};

// Terminate instance
const terminateInstance = async (instanceId) => {
  const command = new TerminateInstancesCommand({
    InstanceIds: [instanceId]
  });

  await ec2Client.send(command);
};

RDS(Relational Database Service)

RDS は、マネージド型のリレーショナルデータベース(PostgreSQL、MySQL、MariaDB、Oracle、SQL Server、Aurora)を提供します。

RDS の操作
import {
  RDSClient,
  CreateDBInstanceCommand,
  DescribeDBInstancesCommand,
  ModifyDBInstanceCommand,
  DeleteDBInstanceCommand
} from '@aws-sdk/client-rds';

const rdsClient = new RDSClient({ region: 'us-east-1' });

// Create database instance
const createDatabase = async () => {
  const command = new CreateDBInstanceCommand({
    DBInstanceIdentifier: 'mydb',
    DBInstanceClass: 'db.t3.micro',
    Engine: 'postgres',
    EngineVersion: '15.3',
    MasterUsername: 'admin',
    MasterUserPassword: 'SecurePassword123!',
    AllocatedStorage: 20, // GB
    StorageType: 'gp3',
    BackupRetentionPeriod: 7, // days
    MultiAZ: true, // High availability
    PubliclyAccessible: false,
    VpcSecurityGroupIds: ['sg-0123456789abcdef0'],
    DBSubnetGroupName: 'my-db-subnet-group',
    StorageEncrypted: true,
    Tags: [
      { Key: 'Environment', Value: 'production' },
      { Key: 'Application', Value: 'api' }
    ]
  });

  const response = await rdsClient.send(command);
  return response.DBInstance;
};

// Describe database
const describeDatabase = async (dbInstanceId) => {
  const command = new DescribeDBInstancesCommand({
    DBInstanceIdentifier: dbInstanceId
  });

  const response = await rdsClient.send(command);
  return response.DBInstances[0];
};

CloudFormation

AWS リソースを定義してプロビジョニングするための、Infrastructure as Code(IaC)サービスです。

CloudFormation テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Full-stack web application infrastructure'

Parameters:
  Environment:
    Type: String
    Default: production
    AllowedValues:
      - development
      - staging
      - production

Resources:
  # S3 Bucket for static assets
  AssetsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${AWS::StackName}-assets-${Environment}'
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256

  # DynamoDB Table
  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub '${AWS::StackName}-users-${Environment}'
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: userId
          AttributeType: S
        - AttributeName: email
          AttributeType: S
      KeySchema:
        - AttributeName: userId
          KeyType: HASH
      GlobalSecondaryIndexes:
        - IndexName: EmailIndex
          KeySchema:
            - AttributeName: email
              KeyType: HASH
          Projection:
            ProjectionType: ALL
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES

  # Lambda Execution Role
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: DynamoDBAccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - dynamodb:GetItem
                  - dynamodb:PutItem
                  - dynamodb:UpdateItem
                  - dynamodb:Query
                Resource: !GetAtt UsersTable.Arn

  # Lambda Function
  ApiFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub '${AWS::StackName}-api-${Environment}'
      Runtime: nodejs20.x
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          exports.handler = async (event) => {
            return {
              statusCode: 200,
              body: JSON.stringify({ message: 'Hello from Lambda!' })
            };
          };
      Environment:
        Variables:
          TABLE_NAME: !Ref UsersTable
          BUCKET_NAME: !Ref AssetsBucket
          ENVIRONMENT: !Ref Environment
      Timeout: 30
      MemorySize: 512

  # API Gateway
  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub '${AWS::StackName}-api-${Environment}'
      Description: REST API for application

  ApiResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref RestApi
      ParentId: !GetAtt RestApi.RootResourceId
      PathPart: users

  ApiMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestApi
      ResourceId: !Ref ApiResource
      HttpMethod: GET
      AuthorizationType: NONE
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations'

  ApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: ApiMethod
    Properties:
      RestApiId: !Ref RestApi
      StageName: !Ref Environment

  LambdaApiPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref ApiFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*'

Outputs:
  ApiUrl:
    Description: API Gateway URL
    Value: !Sub 'https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}'
    Export:
      Name: !Sub '${AWS::StackName}-api-url'

  BucketName:
    Description: S3 Bucket Name
    Value: !Ref AssetsBucket
    Export:
      Name: !Sub '${AWS::StackName}-bucket-name'

  TableName:
    Description: DynamoDB Table Name
    Value: !Ref UsersTable
    Export:
      Name: !Sub '${AWS::StackName}-table-name'
CloudFormation の操作
import {
  CloudFormationClient,
  CreateStackCommand,
  DescribeStacksCommand,
  UpdateStackCommand,
  DeleteStackCommand
} from '@aws-sdk/client-cloudformation';
import { readFileSync } from 'fs';

const cfClient = new CloudFormationClient({ region: 'us-east-1' });

// Create stack
const createStack = async (stackName, templatePath, parameters = {}) => {
  const templateBody = readFileSync(templatePath, 'utf8');

  const command = new CreateStackCommand({
    StackName: stackName,
    TemplateBody: templateBody,
    Parameters: Object.entries(parameters).map(([key, value]) => ({
      ParameterKey: key,
      ParameterValue: value
    })),
    Capabilities: ['CAPABILITY_IAM'],
    Tags: [
      { Key: 'ManagedBy', Value: 'CloudFormation' },
      { Key: 'Application', Value: 'MyApp' }
    ]
  });

  const response = await cfClient.send(command);
  return response.StackId;
};

// Get stack status
const getStackStatus = async (stackName) => {
  const command = new DescribeStacksCommand({
    StackName: stackName
  });

  const response = await cfClient.send(command);
  const stack = response.Stacks[0];

  return {
    status: stack.StackStatus,
    outputs: stack.Outputs || []
  };
};

ベストプラクティス

セキュリティ

IAM のベストプラクティス

  • root アカウントと特権アカウントで MFA を有効にします
  • アクセスキーではなく IAM ロールを使用します
  • 最小権限の原則を適用します
  • 認証情報を定期的にローテーションします
  • IAM Access Analyzer を使用して、権限が過剰なポリシーを特定します
  • 監査ログを記録するために CloudTrail を有効にします

データの暗号化

  • 保存データを暗号化します(S3、EBS、RDS、DynamoDB)
  • 転送中のデータには SSL/TLS を使用します
  • シークレットは AWS Secrets Manager または Parameter Store に保存します
  • 暗号化キーの管理には KMS を使用します

ネットワークセキュリティ

  • ネットワークを分離するために VPC を使用します
  • セキュリティグループと NACL を導入します
  • VPC Flow Logs を有効にします
  • ウェブアプリケーションを保護するために AWS WAF を使用します
  • AWS Shield で DDoS 対策を導入します
コスト最適化

コンピューティング

  • Auto Scaling を使用して、需要に合わせて容量を調整します
  • 適切なインスタンスタイプとサイズを選択します
  • 耐障害性が求められるワークロードには Spot Instances を使用します
  • イベント駆動型のワークロードには Lambda を活用します
  • 安定したワークロードには Savings Plans と Reserved Instances を使用します

ストレージ

  • S3 ライフサイクルポリシーを導入します
  • 適切なストレージクラスを使用します
  • S3 Intelligent-Tiering を有効にします
  • 使用していない EBS ボリュームとスナップショットを削除します
  • 圧縮と重複排除を使用します

データベース

  • データベースインスタンスのサイズを適正化します
  • 読み取りトラフィックを分散するためにリードレプリカを使用します
  • DynamoDB の自動スケーリングを有効にします
  • 変動するワークロードには Aurora Serverless を使用します
  • 古いデータを、より低コストのストレージにアーカイブします
パフォーマンス

アプリケーション設計

  • キャッシュを導入します(ElastiCache、CloudFront)
  • コンテンツ配信ネットワーク(CloudFront)を使用します
  • データベースのクエリとインデックスを最適化します
  • コネクションプーリングを導入します
  • 非同期処理/並列処理を使用します

モニタリングと最適化

  • CloudWatch でメトリクスとアラームを使用します
  • 分散トレーシングに X-Ray を導入します
  • パフォーマンス予算を設定します
  • 定期的にパフォーマンステストを実施します
  • AWS Compute Optimizer の推奨事項を活用します
信頼性

高可用性

  • 複数のアベイラビリティーゾーンにデプロイします
  • 自動復旧には Auto Scaling を使用します
  • ヘルスチェックと自動フェイルオーバーを導入します
  • DNS ベースのフェイルオーバーには Route 53 を使用します
  • 段階的な縮退運転が可能な設計にします

災害復旧

  • バックアップを自動化します
  • 復旧手順を定期的にテストします
  • 重要なデータにはマルチリージョンレプリケーションを使用します
  • 目標復旧時間(RTO)と目標復旧時点(RPO)を文書化します
  • カオスエンジニアリングのプラクティスを導入します

エラー処理

  • 指数バックオフを用いた再試行ロジックを導入します
  • 失敗したメッセージにはデッドレターキューを使用します
  • エラーに関する CloudWatch アラームを設定します
  • サーキットブレーカーを導入します
  • エラーを包括的に記録します
オペレーショナルエクセレンス

Infrastructure as Code

  • すべてのインフラストラクチャコードをバージョン管理します
  • リソースのプロビジョニングには CloudFormation または CDK を使用します
  • インフラストラクチャの変更に CI/CD を導入します
  • 複数アカウント/リージョンへのデプロイにはスタックセットを使用します
  • デプロイ前にテンプレートを検証します

モニタリングとログ記録

  • CloudWatch Logs でログを一元管理します
  • カスタムメトリクスとダッシュボードを設定します
  • ログの集約と分析を導入します
  • API の監査には CloudTrail を使用します
  • 重要なメトリクスに関するアラームを設定します

自動化

  • CodePipeline でデプロイを自動化します
  • パッチ管理には Systems Manager を使用します
  • 自動スケーリングポリシーを導入します
  • 運用タスクには Lambda を使用します
  • バックアップと復旧の手順を自動化します

スキルのバージョン: 1.0.0 最終更新日: October 2025 スキルカテゴリ: Cloud Infrastructure, Serverless, Database, DevOps 対応環境: AWS SDK v3, CloudFormation, AWS CLI, Terraform

コメント