observability・8分で読める
SLA・SLO・SLI完全ガイド:サービス品質を定義・測定・管理する
SLA、SLO、SLIの違いと実践的な設定方法を解説。エラーバジェット、計算式、具体例を含む、Google SREの考え方に基づいた信頼性管理の完全ガイド。
#SRE#SLA#SLO#SLI#信頼性#モニタリング#運用
SLA・SLO・SLI完全ガイド
SLA(Service Level Agreement)、SLO(Service Level Objective)、SLI(Service Level Indicator)は、サービスの信頼性を定義・測定・管理するための重要な概念です。
基本概念
SLI(Service Level Indicator)- サービスレベル指標
定義: サービスのパフォーマンスを測定する具体的な指標。
- 何を測定するか: サービスの品質を数値化したもの
- 例: リクエスト成功率、レイテンシ、稼働率
- 形式: 通常はパーセンテージや時間で表現
例: - 成功したリクエストの割合: 99.9% - 500msec以内に完了したリクエストの割合: 95% - サービスが利用可能だった時間の割合: 99.95%
SLO(Service Level Objective)- サービスレベル目標
定義: SLIに対して設定する目標値。
- 何を定義するか: 「どれくらいのSLIを達成すべきか」という内部目標
- 例: 「可用性99.9%を維持する」
- 特徴: 社内チーム向けの目標、SLAよりも厳しく設定
例: - 可用性 SLO: 99.9%(月間ダウンタイム43.8分以内) - レイテンシ SLO: P95で500ms以内 - エラー率 SLO: 0.1%未満
SLA(Service Level Agreement)- サービスレベル合意
定義: 顧客と交わす、サービス品質に関する契約・約束。
- 何を約束するか: 「このレベルのサービスを提供します」という外部への契約
- 例: 「可用性99.5%を保証、未達成時は返金」
- 特徴: 法的拘束力、ペナルティ規定を含む
例: - 月間稼働率99.5%を保証 - 未達成時は利用料金の10%を返金 - サポート応答時間: 1時間以内
SLI・SLO・SLAの関係
厳しい 緩い ↓ ↓ [内部目標] SLO ≧ [外部約束] SLA ≧ [実測値] SLI 例: - SLO: 99.9%(内部目標) - SLA: 99.5%(顧客への約束) - SLI: 99.95%(実際の測定値) この場合: ✓ SLI (99.95%) > SLO (99.9%) → 目標達成 ✓ SLI (99.95%) > SLA (99.5%) → 契約遵守
なぜSLOがSLAより厳しいのか
- バッファの確保: SLAを違反する前に対策を打つため
- 早期警告: SLO違反をアラートのトリガーにする
- 継続的改善: より高い基準で品質を維持
ゴールデンシグナルとSLI
Google SREが提唱する「4つのゴールデンシグナル」に基づいたSLIの設計:
1. レイテンシ(Latency)
リクエストの応答時間。
推奨SLI:
成功したリクエストの95%が500ms以内に完了 成功したリクエストの99%が1000ms以内に完了
計算式:
P95レイテンシ = 全リクエストを速い順に並べた時の95%点の値
2. トラフィック(Traffic)
システムに対する需要の量。
推奨SLI:
1秒あたりのリクエスト数(RPS) 1秒あたりのトランザクション数(TPS)
3. エラー(Errors)
失敗したリクエストの割合。
推奨SLI:
成功したリクエストの割合: 99.9% = エラー率: 0.1%未満
計算式:
成功率 = (成功したリクエスト数 / 総リクエスト数) × 100 エラー率 = (失敗したリクエスト数 / 総リクエスト数) × 100
4. サチュレーション(Saturation)
システムリソースの使用状況。
推奨SLI:
CPU使用率: 80%未満 メモリ使用率: 80%未満 ディスク使用率: 85%未満
SLIの選び方
良いSLIの条件
- ユーザー体験に直結: ユーザーが実際に感じる品質を反映
- 測定可能: 明確に数値化できる
- 制御可能: チームの努力で改善できる
- シンプル: 複雑すぎない、理解しやすい
SLIの例:Webサービス
| 種類 | SLI | 測定方法 |
|---|---|---|
| 可用性 | 成功したリクエストの割合 | (2xx,3xx ステータス数 / 全リクエスト数) × 100 |
| レイテンシ | P95レイテンシ | CloudWatch、Datadogで測定 |
| 品質 | エラー率 | (5xx ステータス数 / 全リクエスト数) × 100 |
| スループット | 秒間処理リクエスト数 | CloudWatch Metricsで測定 |
SLIの例:バッチ処理
| 種類 | SLI | 測定方法 |
|---|---|---|
| 完了率 | 成功したバッチの割合 | (成功したバッチ数 / 実行したバッチ数) × 100 |
| 処理時間 | バッチの実行時間 | 処理完了時刻 - 処理開始時刻 |
| 鮮度 | データの最新性 | 現在時刻 - データ更新時刻 |
SLIの例:データベース
| 種類 | SLI | 測定方法 |
|---|---|---|
| 可用性 | 接続成功率 | (成功した接続数 / 接続試行数) × 100 |
| レイテンシ | クエリ応答時間 | RDS Performance Insightsで測定 |
| 正確性 | データ整合性チェック | 定期的な整合性検証 |
SLOの設定方法
ステップ1: ユーザーの期待を理解する
質問:
- ユーザーは何を期待しているか?
- どのくらいのダウンタイムなら許容されるか?
- どの程度のレスポンス速度が必要か?
ステップ2: 現在のパフォーマンスを測定する
実施:
- 過去3〜6ヶ月のメトリクスを分析
- 現実的な目標値を設定
- ピーク時とオフピーク時を考慮
ステップ3: SLOを定義する
フォーマット:
[SLI] は [期間] において [目標値] を達成する 例: - リクエストの99%は30日間において500ms以内に完了する - APIの可用性は1ヶ月において99.9%を維持する - エラー率は1週間において0.1%未満を維持する
ステップ4: エラーバジェットを計算する
エラーバジェット: SLOで許容される失敗の量。
例:SLO 99.9%の場合 月間(30日 = 43,200分): - 許容ダウンタイム: 43.2分(0.1%) - これが「エラーバジェット」 月間100万リクエストの場合: - 許容エラー数: 1,000リクエスト(0.1%)
SLO設定のベストプラクティス
1. 100%を目指さない
理由:
- 100%可用性は現実的ではない
- 過度な投資が必要
- イノベーションの阻害
推奨:
サービスタイプ別の推奨SLO: - ミッションクリティカル: 99.95%〜99.99% - ビジネスクリティカル: 99.9%〜99.95% - 一般的なサービス: 99%〜99.9% - 内部ツール: 95%〜99%
2. 複数のSLOを設定する
例:ECサイト:
- 可用性 SLO: 99.9% - レイテンシ SLO: P95 < 500ms、P99 < 1000ms - エラー率 SLO: < 0.1% - 決済成功率 SLO: 99.95%
3. 期間を明確にする
推奨:
- 短期: 7日、30日(アラート用)
- 長期: 四半期、年次(ビジネス報告用)
4. 段階的な目標設定
現状 → 第1段階 → 第2段階 → 理想 例: - 現状: 可用性 99.5% - 第1段階(3ヶ月後): 99.7% - 第2段階(6ヶ月後): 99.9% - 理想(1年後): 99.95%
エラーバジェット
エラーバジェットとは
SLOで許容される「失敗の余裕」。
エラーバジェット = 100% - SLO 例:SLO 99.9%の場合 エラーバジェット = 100% - 99.9% = 0.1%
エラーバジェットの使い方
1. イノベーションとリスクのバランス
エラーバジェットが残っている → 新機能をリリースできる エラーバジェットが枯渇 → 安定化作業に集中
2. 意思決定の基準
| エラーバジェット残量 | アクション |
|---|---|
| 80%以上残 | 積極的に新機能リリース、実験的な機能追加 |
| 50-80%残 | 通常のリリースサイクル |
| 20-50%残 | リリース頻度を下げる、安定性重視 |
| 20%未満 | 新機能リリース凍結、安定化作業に集中 |
| 枯渇 | リリース完全停止、障害対応のみ |
3. チーム間の調整
開発チーム:
- 新機能の追加
- アーキテクチャの変更
- パフォーマンス改善
SREチーム:
- インフラの安定性
- 監視とアラート
- 障害対応
合意事項:
エラーバジェットポリシー: 1. エラーバジェットが50%以上残っている場合 - 週2回のデプロイ可能 - 新機能の追加OK 2. エラーバジェットが50%未満の場合 - 週1回のデプロイに制限 - バグ修正のみ 3. エラーバジェットが枯渇した場合 - デプロイ凍結 - 安定化作業に専念 - 次の測定期間まで新機能なし
エラーバジェットの計算例
例1: 可用性ベース
SLO: 99.9%(月間) 月間: 30日 = 43,200分 エラーバジェット = 43,200分 × 0.1% = 43.2分 月の途中で20分のダウンタイムが発生: - 消費: 20分 - 残量: 23.2分(53.7%残) - 判断: 通常のリリースサイクルを継続
例2: リクエストベース
SLO: エラー率 0.1%未満(週間) 週間リクエスト数: 1,000,000 エラーバジェット = 1,000,000 × 0.1% = 1,000リクエスト 月曜日に500エラーが発生: - 消費: 500リクエスト - 残量: 500リクエスト(50%残) - 判断: リリース頻度を下げる、慎重にテスト
SLAの設計
SLAに含めるべき要素
- サービスレベル: 保証する品質指標
- 測定方法: どのように測定するか
- 対象範囲: 何が対象で何が除外されるか
- ペナルティ: 未達成時の補償
- 例外事項: 不可抗力などの除外規定
SLAの例:SaaSサービス
## サービスレベル契約(SLA) ### 1. 稼働率保証 月間稼働率: 99.5%以上を保証 ### 2. 測定方法 稼働率 = (月間総時間 - ダウンタイム) / 月間総時間 × 100 ### 3. ダウンタイムの定義 - HTTPステータス 5xxエラーが1分間継続 - APIエンドポイントが応答しない状態が1分間継続 ### 4. 除外事項 以下はダウンタイムに含まれません: - 事前通知した定期メンテナンス(月次、最大4時間) - お客様側のネットワーク障害 - お客様の利用規約違反による停止 - 不可抗力(天災、戦争、テロ等) ### 5. 補償 | 月間稼働率 | サービスクレジット | |-----------|------------------| | 99.5%未満99.0%以上 | 月額利用料の10% | | 99.0%未満95.0%以上 | 月額利用料の25% | | 95.0%未満 | 月額利用料の50% | ### 6. クレジット申請 - 申請期限: 該当月の翌月末まで - 申請方法: サポートチケット経由 - 適用: 翌月の請求から減額
SLA設計のベストプラクティス
1. 現実的な目標
悪い例: - 99.999%(ファイブナイン)を約束 → 年間5分しか許されない 良い例: - 99.5%を約束 → 月間3.6時間の余裕 - SLO 99.9%で内部管理 → バッファあり
2. 明確な測定方法
悪い例: - 「高い可用性を提供します」→ 曖昧 良い例: - 「AWS CloudWatchで測定した月間稼働率99.5%以上」→ 明確
3. 段階的なペナルティ
推奨: - 99.5%未満: 10%返金 - 99.0%未満: 25%返金 - 95.0%未満: 50%返金 非推奨: - 99.5%未満: 100%返金 → 過度なリスク
CloudWatchでのSLI測定
可用性SLIの実装
import boto3 from datetime import datetime, timedelta cloudwatch = boto3.client('cloudwatch') # ALBのメトリクスから可用性を計算 def calculate_availability_sli(load_balancer_name, start_time, end_time): # 成功リクエスト数(2xx, 3xx) success_metrics = cloudwatch.get_metric_statistics( Namespace='AWS/ApplicationELB', MetricName='HTTPCode_Target_2XX_Count', Dimensions=[{'Name': 'LoadBalancer', 'Value': load_balancer_name}], StartTime=start_time, EndTime=end_time, Period=3600, Statistics=['Sum'] ) # 総リクエスト数 total_metrics = cloudwatch.get_metric_statistics( Namespace='AWS/ApplicationELB', MetricName='RequestCount', Dimensions=[{'Name': 'LoadBalancer', 'Value': load_balancer_name}], StartTime=start_time, EndTime=end_time, Period=3600, Statistics=['Sum'] ) success_count = sum([point['Sum'] for point in success_metrics['Datapoints']]) total_count = sum([point['Sum'] for point in total_metrics['Datapoints']]) availability = (success_count / total_count) * 100 if total_count > 0 else 0 return { 'availability': availability, 'success_count': success_count, 'total_count': total_count, 'error_count': total_count - success_count } # 月間SLIの計算 end_time = datetime.now() start_time = end_time - timedelta(days=30) result = calculate_availability_sli('my-load-balancer', start_time, end_time) print(f"可用性SLI: {result['availability']:.3f}%") print(f"成功リクエスト: {result['success_count']:,}") print(f"総リクエスト: {result['total_count']:,}") print(f"エラー: {result['error_count']:,}") # SLO判定 SLO = 99.9 if result['availability'] >= SLO: print(f"✓ SLO {SLO}%達成") else: print(f"✗ SLO {SLO}%未達成") deficit = SLO - result['availability'] print(f"不足: {deficit:.3f}%")
レイテンシSLIの実装
def calculate_latency_sli(load_balancer_name, start_time, end_time): # P95レイテンシの取得 latency_metrics = cloudwatch.get_metric_statistics( Namespace='AWS/ApplicationELB', MetricName='TargetResponseTime', Dimensions=[{'Name': 'LoadBalancer', 'Value': load_balancer_name}], StartTime=start_time, EndTime=end_time, Period=3600, Statistics=['Average'], ExtendedStatistics=['p95', 'p99'] ) # P95レイテンシの平均 p95_values = [point['ExtendedStatistics']['p95'] for point in latency_metrics['Datapoints']] avg_p95 = sum(p95_values) / len(p95_values) if p95_values else 0 return { 'p95_latency_ms': avg_p95 * 1000, # 秒からミリ秒に変換 'datapoints': len(p95_values) } result = calculate_latency_sli('my-load-balancer', start_time, end_time) print(f"P95レイテンシ: {result['p95_latency_ms']:.2f}ms") # SLO判定(P95 < 500ms) SLO_LATENCY = 500 if result['p95_latency_ms'] <= SLO_LATENCY: print(f"✓ レイテンシSLO {SLO_LATENCY}ms達成") else: print(f"✗ レイテンシSLO {SLO_LATENCY}ms未達成")
エラーバジェット追跡
def calculate_error_budget(sli, slo, period_minutes): """ エラーバジェットの計算と残量追跡 Args: sli: 実際のSLI値(%) slo: 目標SLO値(%) period_minutes: 測定期間(分) """ # エラーバジェット(分) error_budget_minutes = period_minutes * (100 - slo) / 100 # 消費したバジェット(分) consumed_minutes = period_minutes * (100 - sli) / 100 # 残量(分) remaining_minutes = error_budget_minutes - consumed_minutes # 残量率(%) remaining_percentage = (remaining_minutes / error_budget_minutes) * 100 return { 'error_budget_minutes': error_budget_minutes, 'consumed_minutes': consumed_minutes, 'remaining_minutes': remaining_minutes, 'remaining_percentage': remaining_percentage, 'status': get_budget_status(remaining_percentage) } def get_budget_status(remaining_percentage): if remaining_percentage >= 80: return 'HEALTHY - 積極的なリリース可能' elif remaining_percentage >= 50: return 'GOOD - 通常のリリースサイクル' elif remaining_percentage >= 20: return 'CAUTION - リリース頻度を下げる' elif remaining_percentage > 0: return 'WARNING - 安定化作業に注力' else: return 'CRITICAL - リリース凍結' # 月間エラーバジェットの計算 SLI = 99.95 # 実測値 SLO = 99.9 # 目標値 PERIOD = 30 * 24 * 60 # 30日(分) budget = calculate_error_budget(SLI, SLO, PERIOD) print(f"エラーバジェット総量: {budget['error_budget_minutes']:.2f}分") print(f"消費: {budget['consumed_minutes']:.2f}分") print(f"残量: {budget['remaining_minutes']:.2f}分") print(f"残量率: {budget['remaining_percentage']:.1f}%") print(f"ステータス: {budget['status']}")
ダッシュボードとアラート
CloudWatchダッシュボード構成例
{ "widgets": [ { "type": "metric", "properties": { "title": "可用性SLI(月間)", "metrics": [ ["AWS/ApplicationELB", "RequestCount", {"stat": "Sum", "label": "総リクエスト"}], [".", "HTTPCode_Target_2XX_Count", {"stat": "Sum", "label": "成功"}] ], "period": 86400, "stat": "Average", "region": "ap-northeast-1", "yAxis": { "left": { "min": 0 } }, "annotations": { "horizontal": [ { "label": "SLO: 99.9%", "value": 99.9, "fill": "above" }, { "label": "SLA: 99.5%", "value": 99.5, "fill": "below" } ] } } }, { "type": "metric", "properties": { "title": "エラーバジェット残量", "metrics": [ ["CustomMetrics", "ErrorBudgetRemaining", {"stat": "Average"}] ], "period": 3600, "stat": "Average", "region": "ap-northeast-1", "yAxis": { "left": { "min": 0, "max": 100 } }, "annotations": { "horizontal": [ { "label": "CRITICAL", "value": 20, "fill": "below" }, { "label": "WARNING", "value": 50, "fill": "below" } ] } } } ] }
SLO違反のアラート設定
# CloudFormation例 SLOViolationAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: "SLO-Violation-Availability" AlarmDescription: "可用性SLOが99.9%を下回った" MetricName: AvailabilitySLI Namespace: CustomMetrics Statistic: Average Period: 3600 EvaluationPeriods: 3 Threshold: 99.9 ComparisonOperator: LessThanThreshold TreatMissingData: breaching AlarmActions: - !Ref SNSTopicForSLO ErrorBudgetDepletionAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: "Error-Budget-Critical" AlarmDescription: "エラーバジェット残量が20%未満" MetricName: ErrorBudgetRemaining Namespace: CustomMetrics Statistic: Average Period: 3600 EvaluationPeriods: 1 Threshold: 20 ComparisonOperator: LessThanThreshold TreatMissingData: breaching AlarmActions: - !Ref SNSTopicForCritical
まとめ
重要ポイント
- SLI: 何を測定するか(指標)
- SLO: どのレベルを目指すか(内部目標)
- SLA: 何を約束するか(外部契約)
- エラーバジェット: どれだけ失敗できるか(イノベーションの余地)
実践ステップ
ステップ1: SLIの選定 - ユーザー体験に直結する指標を選ぶ - 可用性、レイテンシ、エラー率が基本 ステップ2: SLOの設定 - 現状を測定し、現実的な目標を設定 - 100%を目指さない(99.9%〜99.95%が妥当) ステップ3: 測定の自動化 - CloudWatch、Datadogで自動計測 - ダッシュボードで可視化 ステップ4: エラーバジェット管理 - 残量に応じてリリース判断 - チーム間で合意形成 ステップ5: 継続的改善 - 四半期ごとにSLOを見直し - ユーザーフィードバックを反映
参考資料
- Google SRE Book: https://sre.google/sre-book/table-of-contents/
- Service Level Objectives: https://sre.google/sre-book/service-level-objectives/
- Monitoring Distributed Systems: https://sre.google/sre-book/monitoring-distributed-systems/
- Google SRE Workbook: https://sre.google/workbook/table-of-contents/
- Implementing SLOs: https://sre.google/workbook/implementing-slos/
SLI・SLO・SLAを正しく理解し、適切に設定・管理することで、サービスの信頼性と開発速度のバランスを取りながら、ユーザーに価値を提供し続けることができます。