目次
AWSアンチパターン集
周辺資料: 学習インデックス · AWSサービス一覧(2026) · サービス選定ガイド · アーキテクチャパターン集
AWS設計・運用で頻出するアンチパターンを領域別に整理。なぜ起きるか・何が問題か・正しい設計を具体的に示す。
1. セキュリティのアンチパターン
IAM ワイルドカード権限
[アンチパターン]
"Action": "*", "Resource": "*"
理由: 「とりあえず動かしたい」「IAMを調べるのが面倒」
[問題]
- 攻撃者に侵害された場合の被害が全アカウントに及ぶ
- コンプライアンス審査で必ず指摘される
- 誰が何をできるか把握できなくなる
[正しい設計]
必要なサービス・アクション・リソースを最小化して明示:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/uploads/*"
}
Permission Boundary を使い開発者が作成できるロールの範囲を制限
IAM Access Analyzer で意図しない外部公開リソースを定期検出
ハードコードされた認証情報
[アンチパターン]
# ソースコードや環境変数に直書き
AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
DB_PASSWORD = "my-prod-password"
[問題]
- Git に混入すると即座に悪用される(GitHub には常時スキャナーが動いている)
- ローテーションが困難になる
- 複数の環境で同じ認証情報を使うリスク
[正しい設計]
AWS サービスには IAM ロール(EC2/ECS/Lambda に直接アタッチ)
DB パスワード → Secrets Manager(自動ローテーション付き)
設定値 → SSM Parameter Store(SecureString)
# Lambda での正しい実装
import boto3, json
def get_secret(secret_name: str) -> dict:
client = boto3.client('secretsmanager')
return json.loads(client.get_secret_value(SecretId=secret_name)['SecretString'])
# 関数起動時にキャッシュして API コールを最小化
_secret_cache = {}
def get_db_password():
if 'db' not in _secret_cache:
_secret_cache['db'] = get_secret('prod/myapp/db')
return _secret_cache['db']['password']
ルートアカウントの日常利用
[アンチパターン]
ルートアカウントで日常の AWS 操作を行う
ルートアカウントへの MFA 未設定
[問題]
- ルートアカウントは全権限を持ち、SCP でも制限できない
- 漏洩時の影響範囲が最大
[正しい設計]
ルートアカウント:
MFA を必ず設定(ハードウェアMFAを推奨)
アクセスキーを作成しない
IAM Identity Center の初期設定時のみ使用
使用後はロック(MFAデバイスを安全な場所に保管)
日常操作:
IAM Identity Center → SSO でアクセス
各環境に対応した IAM ロールに AssumeRole
S3 パブリックバケット
[アンチパターン]
bucket.set_acl('public-read')
または ACL: public-read でバケット全体を公開
[問題]
- 機密データの意図しない公開
- データ漏洩インシデントの主要原因の1つ
- コンプライアンス違反
[正しい設計]
1. アカウントレベルで S3 パブリックアクセスブロックを有効化(全バケット)
2. 静的サイトなど公開必要なものは CloudFront + OAC で限定公開
3. Config ルール s3-bucket-public-read-prohibited で継続監視
4. SCP で s3:PutBucketPublicAccessBlock の変更を Deny
2. 可用性のアンチパターン
単一 AZ 構成
[アンチパターン]
EC2 を1つのAZにのみ配置
RDS Single-AZ
ALB のターゲットが1 AZ のみ
[問題]
AWS は年に数回 AZ 単位の障害を起こすことがある
AZ 障害時にサービスが完全停止する
[正しい設計]
最低限:
EC2: ASG で 2AZ 以上
RDS: Multi-AZ 有効化
ALB: 2AZ 以上のターゲット登録
推奨:
3AZ 構成(ap-northeast-1a/1c/1d)
DynamoDB: リージョン内で自動 3AZ 複製(設定不要)
Lambda: リージョン内で自動 Multi-AZ(設定不要)
ヘルスチェックの不備
[アンチパターン]
ALB ヘルスチェックが / (ルート) のみで、実際のアプリロジックを確認しない
ヘルスチェックのタイムアウトが長すぎる(デフォルト30秒のまま)
ECS タスクの unhealthy 時に再起動しない設定
[問題]
- アプリが実質停止していても「正常」と判定してトラフィックを送り続ける
- 障害の検知が遅れる
[正しい設計]
/health エンドポイントを実装:
DB 接続確認(ping クエリ)
外部依存サービスの疎通確認
レスポンス: {"status": "ok", "db": "ok", "cache": "ok"}
ALB ヘルスチェック設定:
Path: /health
Interval: 15秒
Timeout: 5秒
HealthyThreshold: 2
UnhealthyThreshold: 3
ECS タスク定義:
healthCheck:
command: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
interval: 15
timeout: 5
retries: 3
デプロイ時の全停止
[アンチパターン]
ECS サービスの minimumHealthyPercent = 0
直接 ALB のターゲットグループを変更してデプロイ
デプロイ中にリクエストが失敗する
[問題]
- デプロイのたびにサービス停止(ダウンタイム発生)
- ロールバックに時間がかかる
[正しい設計]
ECS Rolling Update:
minimumHealthyPercent: 100
maximumPercent: 200
DeregistrationDelay: 30秒(既存接続のドレイン)
Blue/Green デプロイ(CodeDeploy + ECS):
新タスクセットを起動 → テスト → トラフィック切替 → 旧タスク削除
失敗時は15秒でロールバック可能
Lambda のエイリアス + 重み付き設定:
prod エイリアス: v1=90%, v2=10% でカナリアリリース
3. パフォーマンスのアンチパターン
N+1 クエリ問題(RDS)
[アンチパターン]
# ユーザー一覧を取得後、1件ずつ注文を取得
users = db.query("SELECT * FROM users LIMIT 100")
for user in users:
orders = db.query(f"SELECT * FROM orders WHERE user_id = {user.id}")
# → 101回のクエリが発生
[問題]
- RDS 接続数が増大(接続プールを圧迫)
- レイテンシが線形増加
- RDS CPU 使用率が高騰
[正しい設計]
JOIN または IN クエリで一括取得:
users_with_orders = db.query("""
SELECT u.*, o.order_id, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id IN (SELECT id FROM users LIMIT 100)
""")
または RDS Proxy で接続プール最適化
+ Read Replica にクエリを分散(参照系)
DynamoDB のスキャン多用
[アンチパターン]
# テーブル全体をスキャンして条件でフィルタ
response = dynamodb.scan(
TableName='Orders',
FilterExpression=Attr('status').eq('pending')
)
[問題]
- テーブル全体をスキャン → コスト爆発(読み取りキャパシティ全消費)
- テーブルが大きくなるほど遅くなる
- FilterExpression はスキャン後に適用(コスト削減にならない)
[正しい設計]
アクセスパターンを先に定義し、GSI/LSI で対応:
GSI: status-createdAt-index
HASH KEY: status
RANGE KEY: createdAt
# GSI を使ったクエリ
response = dynamodb.query(
TableName='Orders',
IndexName='status-createdAt-index',
KeyConditionExpression=Key('status').eq('pending')
)
→ スキャンではなくクエリ → 必要なデータのみ読み取り
Lambda コールドスタート放置
[アンチパターン]
API Gateway + Lambda(Provisioned Concurrency なし)を P99 SLO < 500ms 要件で使用
[問題]
- Node.js: コールドスタート 100〜500ms
- Java: コールドスタート 1〜5秒
- VPC 内 Lambda: さらに追加で 1〜2秒
[正しい設計]
選択肢A: Provisioned Concurrency(コストかかるが確実)
常時暖機状態を維持
最小: 1〜2 ユニット(スパイク前に手動調整 or Auto Scaling)
選択肢B: Lambda SnapStart(Java のみ、起動時間を大幅短縮)
.zip デプロイ時にスナップショット作成
コールドスタート: 5秒 → 200ms に短縮
選択肢C: ECS Fargate へ移行
常時稼働が必要で SLO が厳しい場合
選択肢D: アーキテクチャで回避
コールドスタートが許容できる非同期処理にオフロード
ElastiCache の接続数爆発
[アンチパターン]
Lambda から直接 Redis(ElastiCache)に接続
高トラフィック時に接続数が急増
[問題]
Redis のデフォルト最大接続数は 65,000
Lambda が 1000 同時実行 → 1000 接続が同時に発生
接続確立のオーバーヘッドでレイテンシが増大
[正しい設計]
Lambda の場合: ElastiCache Serverless(接続管理をマネージドに)
ECS/EC2 の場合: 接続プール(valkey-py の ConnectionPool)を適切に設定
# Lambda 外での初期化(コンテナ再利用で接続を使い回す)
import redis
# Lambdaのグローバルスコープに配置(ハンドラの外)
_redis_client = None
def get_redis():
global _redis_client
if _redis_client is None:
_redis_client = redis.Redis(
host=os.environ['REDIS_HOST'],
port=6379,
max_connections=10, # コンテナあたりの接続数を制限
decode_responses=True
)
return _redis_client
4. コストのアンチパターン
「とりあえず EC2」症候群
[アンチパターン]
すべてのワークロードを EC2 + Auto Scaling で実装
理由: 「慣れているから」「何でもできるから」
[問題]
- OS パッチ適用・セキュリティ対応が必要
- スケールが遅い(AMI 起動に数分かかる)
- 利用率が低い時間帯も固定コストが発生
[正しい設計]
ワークロード別の最適なコンピュート選択:
イベント駆動・散発的処理 → Lambda
常時起動 HTTP サービス → ECS Fargate
大規模バッチ → AWS Batch (Fargate Spot)
GPU 機械学習 → EC2 G インスタンス or SageMaker
Kubernetes 必須 → EKS
→ EC2 は「上記で対応できない場合」の選択肢
RI/SP 未購入でオンデマンド常用
[アンチパターン]
本番環境で安定稼働している EC2/RDS/Lambda が
すべてオンデマンド料金のまま運用
[問題]
Compute Savings Plans で最大66%の割引機会を逃す
例: EC2 m5.xlarge を東京で1年24/7稼働
オンデマンド: $1,314/年
1年 No Upfront SP: $855/年(35%削減)
3年 All Upfront SP: $527/年(60%削減)
[正しい設計]
安定稼働から3ヶ月後に購入判断を行う(パターンが把握できてから)
まず Compute Savings Plans(最も柔軟)を70%カバレッジから
RDS は Reserved Instances(1年)で固定
Cost Explorer の RI/SP Purchase Recommendations を活用
データ転送コストの見落とし
[アンチパターン]
- 同一リージョン内で複数 AZ にまたがるトラフィックが多い
- EC2 から S3 へのアクセスにインターネット経由(NAT Gateway 経由)
- マルチリージョンでデータを頻繁に複製
[問題]
AZ 間転送: $0.01/GB × 双方向 = $0.02/GB
NAT Gateway: $0.045/GB(S3 アクセスに不要)
リージョン間: $0.02/GB(方向による)
[正しい設計]
S3/DynamoDB へのアクセスは VPC Gateway エンドポイント(無料)
EC2 → S3 は Interface ではなく Gateway エンドポイント使用
AZ 間転送はアプリ側の AZ Affinity で最小化
CloudFront を使用してアウトバウンド転送コストを削減
5. 運用のアンチパターン
監視後付け設計
[アンチパターン]
本番リリース後に「障害が起きてから」監視を追加する
アラートがメール垂れ流しで誰も確認しない
[問題]
- 障害が起きても気づくのが遅い
- 原因調査に必要なログが取れていない
- アラートが多すぎて重要なものを見逃す
[正しい設計]
設計フェーズから監視項目を定義(Well-Architected レビューに組み込む):
必須メトリクス(MVP でも必ず設定):
可用性: ALB 5XX エラー率 > 1%
レイテンシ: P99 > 2秒
エラー: CloudWatch Logs エラーカウント急増
アラート送信先:
P1/P2 → PagerDuty/Opsgenie(即時電話)
P3 → Slack(テキスト通知)
1週間以内に誰も対応しないアラートは削除または P3 に格下げ
IaC なしの手動管理
[アンチパターン]
マネジメントコンソールで手動でリソースを作成・変更
「ドキュメントに書いてあるから大丈夫」という管理
[問題]
- ドキュメントと実態がズレる(ドリフト)
- 環境の再現が困難
- 誰がいつ何を変えたか追跡できない
- 開発・検証・本番の差異が増大する
[正しい設計]
IaC ファースト(コードなしでリソースを作らない):
AWS CDK(TypeScript/Python)または Terraform を使用
すべてのリソース変更は PR レビュー必須
本番直接変更は SCP で Deny し CI/CD 経由のみに制限
既存リソースの IaC 化:
AWS CloudFormation IaC Generator でドリフトをインポート
Terraformer(Terraform 用)でリバースエンジニアリング
シークレットローテーション未実装
[アンチパターン]
DB パスワードを2年以上変更していない
アクセスキーを削除した従業員の分が残ったまま
[問題]
- 漏洩した場合の被害が長期間続く
- 退職者のアクセスが残る
- コンプライアンス(PCI DSS/SOC2)で指摘
[正しい設計]
Secrets Manager の自動ローテーション:
RDS: マネージドローテーション(Lambda 不要)
その他: カスタム Lambda ローテーター
周期: 90日以内(PCI DSS 要件)
IAM アクセスキーの定期棚卸し:
IAM Credential Report を月次確認
90日以上未使用のキーを無効化 → 削除
AWS Config ルール: access-keys-rotated でアラーム
6. アーキテクチャのアンチパターン
モノリスをそのままリフト&シフト
[アンチパターン]
オンプレのモノリシックアプリを EC2 に "そのまま" 移行
クラウドの恩恵(Auto Scaling / マネージドサービス)を得られない
[問題]
- クラウドの弾力性・耐障害性を活かせない
- 運用負荷はオンプレと変わらずコストだけ増加
- 一部のコンポーネントが全体のボトルネックになる
[正しい設計]
段階的移行の 3R アプローチ:
Phase 1 Rehost(リホスト):
まず EC2 に移行してリスクを下げる
RDS への DB 移行(DMS 活用)
Phase 2 Replatform(リプラットフォーム):
DB → RDS/Aurora(マネージド化)
ファイルストレージ → S3
セッション → ElastiCache
Phase 3 Refactor(リファクタ):
スケール/コストのボトルネックになっている部分を
マイクロサービス / サーバーレスに分離
同期呼び出しの連鎖
[アンチパターン]
API → Service A → Service B → Service C → DB
(全サービスが同期的に直列接続)
[問題]
- Service B が遅いと全体が遅くなる
- Service C が落ちると API が 5XX を返す
- タイムアウトの設定が難しい(最長チェーンに合わせると長すぎる)
[正しい設計]
読み取り: 同期 OK(即時レスポンスが必要)
Circuit Breaker パターン(フォールバックを用意)
書き込み: 非同期化
API → SQS → Lambda(Consumer)→ DB
失敗時: DLQ → リトライ → アラーム
EventBridge で疎結合化:
Order Service → EventBridge(OrderCreated)
├── Inventory Service(在庫引当)
├── Notification Service(メール送信)
└── Analytics Service(ログ集計)
→ Order Service はイベントを発火するだけ
バックアップなし / 未テスト
[アンチパターン]
RDS の自動バックアップ保持期間 = 1日(デフォルト)
「バックアップ取ってます」と言うが復元テストをしたことがない
[問題]
- RDS バックアップはリストアに1〜4時間かかる(規模による)
- 保持期間1日 → 翌日に気づいた障害に対応できない
- 復元テストなしのバックアップは「あると思っていたが実は機能していなかった」リスクがある
[正しい設計]
AWS Backup でバックアップを一元管理:
保持期間: RDS 最低7日(本番は35日推奨)
クロスリージョンバックアップ: 重要データは別リージョンにコピー
S3: Object Lock で削除・改ざんを防止
定期的な復元テスト(四半期1回):
RDS: スナップショットからステージング環境を復元
目標: RTO/RPO の達成を確認して記録に残す
7. アンチパターン早見表
| カテゴリ | アンチパターン | 影響 | 優先度 |
|---|---|---|---|
| Security | IAM ワイルドカード権限 | 高(全権限漏洩) | ★★★ |
| Security | 認証情報ハードコード | 高(資格情報漏洩) | ★★★ |
| Security | S3 パブリックバケット | 高(データ漏洩) | ★★★ |
| Reliability | 単一AZ構成 | 高(AZ障害で停止) | ★★★ |
| Reliability | バックアップ未テスト | 高(復旧不能) | ★★★ |
| Performance | DynamoDB スキャン多用 | 中(コスト爆発) | ★★☆ |
| Performance | Lambda コールドスタート放置 | 中(SLO 違反) | ★★☆ |
| Performance | N+1 クエリ | 中(RDS 負荷増) | ★★☆ |
| Cost | オンデマンド常用 | 中(コスト増) | ★★☆ |
| Cost | NAT 経由 S3 アクセス | 低〜中(転送費) | ★☆☆ |
| Operations | 監視後付け | 高(障害気づかず) | ★★★ |
| Operations | IaC なし手動管理 | 中(ドリフト) | ★★☆ |
| Architecture | 同期呼び出し連鎖 | 中(連鎖障害) | ★★☆ |
| Architecture | リフト&シフトのみ | 低(クラウド恩恵なし) | ★☆☆ |