Article

ITベンダーに依存しないための社内IT力強化策

高田晃太郎
ITベンダーに依存しないための社内IT力強化策

96%^1^. CNCFの年次調査では、組織の大多数がKubernetes(コンテナのオーケストレーション基盤)を利用または評価していると報告されている。なお、最新の2023年調査では約84%とされ、設問や集計方法の変更に留意しつつも依然高水準だ^2^。クラウドネイティブが標準化するほど、サービスの組み合わせは複雑になり、外部ベンダーに任せる比重が自然と高まる。だが同時に、変更のリードタイムや障害復旧の遅延、契約更新時の交渉力低下といった影響も顕在化する。DevOps研究で注目される4指標(デプロイ頻度、変更のリードタイム、変更失敗率、MTTR)は、ソフトウェア提供能力の高さがビジネス成果と相関することを繰り返し示してきた^3^が、ベンダー依存はその改善を鈍らせがちだ。ここでは、依存をゼロにするのではなく、戦略的に使いながらも出口と代替可能性(ベンダーロックイン回避)を常に確保するための「社内IT力」の高め方を、測定、設計、基盤、運用の順に、KubernetesやGitOpsといった具体例も交えつつ整理する。

依存の実像を可視化する:測定と台帳化から始める

まずは現状把握だ。会計・調達データからベンダー集中を測ると、交渉力や単一障害点のリスクを言語化できる。簡便な指標として、上位三社の支出合計を総支出で割るベンダー集中度(VCR: Vendor Concentration Ratio)を用いると、体感の不安を具体的な数値に落とし込める。重要なのは数値そのものよりも、四半期単位でのトレンドと、個別サービスへの影響の紐づけだ。集中度が一定閾値を超えた領域は、設計や契約の見直し、あるいは代替技術の検討候補になる。KubernetesやマネージドDB、外部SaaSのように基盤性が高い領域ほど、閾値設定は保守的にしておくとよい。

ベンダー集中度を集計するスクリプト

調達台帳のCSVから集中度を算出し、要注意となるサービス領域を出力するPythonの例を示す。会計システムの形式差異に備えてエラーハンドリングを丁寧に入れておくと運用が安定する。

import csv
from collections import defaultdict
from typing import Dict, Tuple

class DataError(Exception):
    pass

def load_invoices(path: str) -> Dict[str, float]:
    totals: Dict[str, float] = defaultdict(float)
    try:
        with open(path, newline='', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            if 'vendor' not in reader.fieldnames or 'amount' not in reader.fieldnames or 'service' not in reader.fieldnames:
                raise DataError('CSVにvendor, amount, service列が必要です')
            for row in reader:
                try:
                    amt = float(row['amount'])
                except ValueError as e:
                    raise DataError(f"金額の変換に失敗: {row['amount']}") from e
                key = f"{row['service']}:::{row['vendor']}"
                totals[key] += amt
    except FileNotFoundError as e:
        raise DataError(f"ファイルが見つかりません: {path}") from e
    return totals

def compute_vcr(totals: Dict[str, float]) -> Dict[str, Tuple[float, float]]:
    per_service: Dict[str, Dict[str, float]] = defaultdict(lambda: defaultdict(float))
    for key, amt in totals.items():
        service, vendor = key.split(':::')
        per_service[service][vendor] += amt
    result = {}
    for service, vmap in per_service.items():
        total = sum(vmap.values())
        top3 = sum(sorted(vmap.values(), reverse=True)[:3])
        vcr = top3 / total if total > 0 else 0.0
        result[service] = (vcr, total)
    return result

if __name__ == '__main__':
    try:
        totals = load_invoices('invoices.csv')
        vcr_map = compute_vcr(totals)
        for service, (vcr, total) in sorted(vcr_map.items(), key=lambda x: x[1][0], reverse=True):
            flag = ' (要注意)' if vcr > 0.7 and total > 1_000_000 else ''
            print(f"{service}: VCR={vcr:.2f}, 年間支出={total:,.0f}{flag}")
    except DataError as e:
        print(f"入力エラー: {e}")
        exit(1)

システム台帳と所有の明確化

支出の見える化と並行して、社内で責任の所在と依存関係の台帳を整える。Backstageのような開発者ポータルで、サービス、チーム、外部SaaSをカタログ化し、契約情報やSLO(Service Level Objective: 目標とする品質指標)へのリンクを一元管理すると、緊急時の意思決定が速くなる^4^。

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: billing-api
  description: 顧客課金の中核API
  tags: [payments, critical]
spec:
  type: service
  owner: team-fintech
  system: revenue-platform
  dependsOn:
    - resource:saas:stripe
    - resource:db:billing-postgres
  lifecycle: production
  links:
    - url: https://status.example.com/slo/billing-api
      title: SLOダッシュボード

依存と所有が明確になると、外部停止の影響推定や代替経路の検討が容易になる。運用面では、インシデント後の学習が台帳に反映され、次の投資判断につながる。台帳化は単なる資産管理ではなく、ベンダー依存の健全性を保つための意思決定インフラだ。Kubernetesクラスター内の命名空間や外部マネージドサービスとの接続(例: VPC/PrivateLink)も台帳に紐づけておくと、停止時の切替計画が具体化する。

内製と外注の境界を設計する:原則と契約の両輪

社内IT力を高める第一歩は、何を内製の中核能力と定義し、どこを外注で加速するかの原則を明文化することだ。顧客体験に直結し、変更頻度が高く、差別化の源泉になる領域は社内に残す。共通インフラや非差別化領域は、標準化して外部の生産性を引き出す。この境界は一度決めて終わりではなく、プロダクトの成長段階と市場の変化に合わせて定期的に見直す。意思決定を再現可能にするため、アーキテクチャ・ディシジョン・レコード(ADR:設計判断の記録)を運用しておくと、過去の判断と前提を透明化できる。

アーキテクチャ・ディシジョン・レコードの雛形

# ADR-0001: 決済ゲートウェイ統合方式
日付: 2025-01-10
状況: 承認済み
背景: 海外展開に伴い複数ゲートウェイが必要。可用性と交渉力を高める狙い。
決定: 抽象化レイヤーを社内実装し、ゲートウェイはプラガブルに切替可能とする。
根拠: ベンダー集中度(VCR)が高く、SLOの厳格化が必要。切替コストを6週間以内に収める目標。
結果: SDK/契約差異により初期コスト増加。ただし障害時の切替手順が簡略化。
追跡: 半期ごとに費用対効果と切替演習の成績をレビューする。

原則を裏打ちするのが契約設計だ。データの持ち帰り方法、エクスポートSLA、監査権、ランタイムや構成のポータビリティ(KubernetesのマニフェストやHelmチャートの持ち運びやすさも含む)、ソースコードやスキーマのライセンス、価格改定時の退出条項といった要点は、法務と技術のクロスファンクショナルで詰め切る。交渉時には、実際に切替手順をドキュメント化し、演習を行ってからサインする。紙の条項より、再現可能な移行手順の存在が実効性を持つ。

ポリシーをコード化して境界を守る

運用段階では、ポリシーの遵守をレビューの善意に頼らない。インフラ構成やアプリ設定に対して、Open Policy Agent(OPA:ポリシー評価エンジン)とConftestで自動チェックを挟み込む^5^。プルリクエストでの逸脱は即時に検知し、デプロイ前に修正する。

name: policy-check
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
      - name: Terraform init/plan
        run: |
          terraform -chdir=infrastructure init
          terraform -chdir=infrastructure plan -out=tf.plan
      - name: Install Conftest
        run: |
          wget https://github.com/open-policy-agent/conftest/releases/download/v0.52.0/conftest_0.52.0_Linux_x86_64.tar.gz
          tar -xzf conftest_*.tar.gz
          sudo mv conftest /usr/local/bin/
      - name: Policy Test
        run: conftest test infrastructure --policy policy/

コード化されたポリシーは、レビューアのスキル差による抜け漏れを減らし、外注チームを含む全員に一貫したガードレールを提供する。境界を守る仕組みがなければ、内製と外注の棲み分けは時間とともに崩れていく。KubernetesのAdmission ControllerやPolicy Engine(例: Gatekeeper)と組み合わせれば、クラスター内の設定逸脱も同様に抑止できる。

共通基盤を製品化する:プラットフォームエンジニアリング

ベンダー依存を弱めるには、都度の個別対応をやめ、セルフサービスな共通基盤を整備するのが近道だ。チームは基盤が提供するゴールデンパスに乗るだけで、セキュアに、監視・ロギングが揃い、再現可能な形でサービスを立ち上げられる。重要なのは、基盤が単なるツールの寄せ集めではなく、SLOとフィードバックループを備えた「製品」として提供されることだ^4^。Kubernetesを中核に据える場合、Namespace、NetworkPolicy、Ingress、Helm/GitOps(宣言的なデプロイ運用)の標準を揃えておくと、チーム間での差分が減り、可搬性も高まる。

プロバイダ非依存のモジュール境界の例

完全なマルチクラウドを目指す必要はないが、サービスチームが触れるインターフェースはできる限りクラウド非依存に保つ。下層での実装差はモジュールが吸収する。

// modules/service/main.tf
variable "name" { type = string }
variable "ingress_cidr" { type = list(string) }
variable "runtime" { type = string } // e.g., container, functions

module "network" {
  source = "../network"
  name   = var.name
  cidr   = var.ingress_cidr
}

module "runtime" {
  source = "../runtime-${var.runtime}" // provider-specific under the hood
  name   = var.name
  subnet_ids = module.network.subnet_ids
}

output "endpoint" {
  value = module.runtime.endpoint
}

サービス側は変化が速い領域のパラメータだけを認識し、クラウドやベンダーの差分はモジュールが内側で隠蔽する。こうした境界設計は、移行時の影響範囲を小さくする。Kubernetesを採用しているなら、上層は「コンテナを動かす」ことに集中し、下層でクラウド固有のロードバランサやストレージ差分を吸収する構成が現実的だ。加えて、SBOM(Software Bill of Materials:ソフトウェア部品表)を自動生成し、依存の追跡性を保つと、退出時の棚卸しが大幅に楽になる^7^。

SBOMの自動生成と保存

#!/usr/bin/env bash
set -euo pipefail
APP=${1:-app}
COMMIT=$(git rev-parse --short HEAD)

syft packages . -o cyclonedx-json > sbom.json
cosign attest --predicate sbom.json --type cyclonedx \
  --key cosign.key ghcr.io/example/${APP}@${COMMIT}

aws s3 cp sbom.json s3://internal-artifacts/sbom/${APP}/${COMMIT}.json

依存の証跡が残っていれば、監査対応だけでなく、ベンダー障害時に代替ライブラリや代替SaaSの検討が現実的になる。プラットフォームは生成物の保管先や可視化ダッシュボードまで含めて提供し、チームが迷わない導線を用意する。

外部API依存を安全に扱うリトライ制御

外部APIを呼ぶ箇所は、ネットワークやレート制限の揺らぎを考慮した堅牢なクライアントに包む。タイムアウト、指数バックオフ、ジャitter(ランダムゆらぎ)は最低限の装備だ^6^。

import time
import random
import requests
from requests.exceptions import RequestException

class VendorClient:
    def __init__(self, base_url: str, timeout: float = 5.0):
        self.base_url = base_url
        self.timeout = timeout

    def get(self, path: str, retries: int = 5) -> dict:
        backoff = 0.5
        for attempt in range(1, retries + 1):
            try:
                resp = requests.get(f"{self.base_url}{path}", timeout=self.timeout)
                if resp.status_code == 429:
                    raise RequestException("rate limited")
                resp.raise_for_status()
                return resp.json()
            except RequestException as e:
                if attempt == retries:
                    raise
                sleep = backoff * (2 ** (attempt - 1)) + random.uniform(0, 0.2)
                time.sleep(sleep)
        return {}

こうした基本を徹底しておくと、外部の揺らぎがそのままユーザー体験に波及する事態を避けられる。依存をなくせないなら、揺らぎを吸収する設計に投資するのが合理的だ。Kubernetesのコントローラやジョブでも同様に、再試行やタイムアウトの標準値をプラットフォーム側で規定しておくと、ばらつきが減る。

計測で回す:SLO・DORA・ROIの継続改善

改善は計測から始まり、計測で終わる。変更リードタイム、デプロイ頻度、変更失敗率、MTTR(平均復旧時間)の4つに加え、エラーバジェットの消費率(SLOに対する余力の使い方)と、ベンダー集中度の推移をレギュラーな経営レビューに載せる。社内IT力の強化はコストセンターの話ではなく、機会損失と交渉力の話だと全員に理解してもらう必要がある。ここではダッシュボードの基礎となるクエリを示す。

SLOとバーンレートのPromQL

# 成功率ベースのSLO 99.9% の30日ローリング(billing-apiのHTTP 2xx割合)
sum(rate(http_requests_total{job="billing-api",code=~"2.."}[5m]))
/
sum(rate(http_requests_total{job="billing-api"}[5m]))

# 2時間ウィンドウのバーンレート(閾値はSLOと期間で調整)
(1 - (sum(rate(http_requests_total{job="billing-api",code=~"2.."}[2h]))
/ sum(rate(http_requests_total{job="billing-api"}[2h])))) / (1 - 0.999)

バーンレートは、短期と長期の二つの窓で見ると誤検知を減らせる。短期窓での急激な悪化と、長期窓でのじわじわした消耗の両方を監視し、アラートに段階を設ける。これにより、エンジニアリングの注力を事業優先度と整合させやすくなる。

リードタイムを算出するSQLの一例

デプロイイベントとプルリクエストのメタデータをデータウェアハウスに集約できていれば、サービス単位のリードタイムを集計できる。日次で集計してトレンドを見るのが実践的だ。

WITH prs AS (
  SELECT repo, pr_id, created_at, merged_at, author
  FROM github_pull_requests
  WHERE merged_at >= CURRENT_DATE - INTERVAL '30 days'
), deploys AS (
  SELECT repo, sha, deployed_at
  FROM cd_deployments
  WHERE deployed_at >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT p.repo,
       DATE(d.deployed_at) AS day,
       PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (d.deployed_at - p.created_at))) AS p50_lead_time_sec,
       PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (d.deployed_at - p.created_at))) AS p90_lead_time_sec
FROM prs p
JOIN deploys d ON d.repo = p.repo AND d.sha = (SELECT merge_commit_sha FROM github_pull_requests WHERE pr_id = p.pr_id)
GROUP BY p.repo, DATE(d.deployed_at)
ORDER BY day DESC;

目標値の設定は組織の文脈に依存するが、プロダクト市場適合が進んだ段階でも、中規模サービスで日単位のリードタイム、時間単位のMTTR、週単位の失敗率改善が現実的な水準になる。これらが改善するほど、外注範囲の見直しや再交渉における選択肢も広がる。内製力の向上は、同じベンダーを使うにしても、より良い条件で使うための交渉材料になる。

変更のガードレールを崩さないリリース運用

社内IT力の要は、誰もが自信を持って出せるリリースの習慣だ。フィーチャーフラグで段階的に露出を増やし、カナリアとロールバックを自動化し、失敗の学習をプロダクトと基盤に還元する。Kubernetes環境なら、マニフェストをGitで管理し(GitOps)、段階的リリースやヘルスチェック(Readiness/Liveness)を標準化することで、変更の安全性とスピードを両立できる。契約やSLAが現場のSLOに接続されていれば、関係者は同じメトリクスを見て議論できる。最後に、基盤のパフォーマンスを自ら計測する仕組みを入れておく。CI/CDの所要時間、IaCのapply時間、アラートから検知までのレイテンシなど、改善余地は常に見つかる。計測結果はデータとして残し、四半期レビューでROIを評価するために使う。

Terraform計画のドリフト検出を継続計測する

構成ドリフトは外部変更の温床だ。計画差分のサイズやapply時間を継続的に収集し、アラートのしきい値をSLOと連動させると、負債の蓄積を早期に検知できる。

name: tf-drift-metrics
on:
  schedule:
    - cron: '0 * * * *'
jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: |
          start=$(date +%s)
          terraform -chdir=infrastructure plan -no-color -detailed-exitcode | tee plan.out || true
          end=$(date +%s)
          changes=$(grep -E "(\+|\-|~)" plan.out | wc -l)
          curl -X POST https://metrics.example.com/ingest \
            -H 'Content-Type: application/json' \
            -d "{\"metric\":\"tf_plan_changes\",\"value\":$changes}"
          curl -X POST https://metrics.example.com/ingest \
            -H 'Content-Type: application/json' \
            -d "{\"metric\":\"tf_plan_duration_sec\",\"value\":$((end-start))}"

計測の仕組みを入れると、具体的な投資判断がしやすくなる。例えば、セルフサービスのテンプレート整備に2人月を投じて、リードタイムが中央値で40%短縮できるなら、四半期の開発コストと機会損失の削減額を踏まえてROIが正の領域に入るかを定量的に判断できる。これは感覚ではなく、データに基づく経営判断だ。詳しいメトリクス設計や可視化の考え方は、社内向けベストプラクティスとして整理し、開発者ポータルから誰でも参照できるようにしておくと良い。

まとめ:依存しないために、賢く使い、常に動ける準備をする

ベンダーに依存しないとは、孤立して自力で全てを賄うことではない。むしろ、外部の強みを取り込みながらも、切替や停止に耐えられる回復力を日々の仕組みに埋め込むことだ。支出と依存の可視化、原則と契約の明文化、セルフサービスな共通基盤、SLOとDORAを核にした継続計測という四点を回し続ければ、交渉力と選択肢は着実に増える。次に取るべき一歩は、集中度の高い領域を一つ選び、切替手順のドキュメント化と演習日程を決めることかもしれない。あるいは、プラットフォームのゴールデンパスにSBOM生成やポリシーチェック、Kubernetes向けのGitOps運用を組み込み、来週のリリースから恩恵を得ることかもしれない。あなたの組織にとって最も痛みの強い箇所はどこか。そして、そこに明日から打てる小さな手は何か。問いをチームで共有し、測り、学び、また次の一手を打っていこう。

参考文献

  1. Cloud Native Computing Foundation. CNCF Annual Survey 2021. https://www.cncf.io/reports/cncf-annual-survey-2021/
  2. Cloud Native Computing Foundation. CNCF Annual Survey 2023. https://www.cncf.io/reports/cncf-annual-survey-2023/
  3. DORA. Accelerate: State of DevOps 2018. https://dora.dev/research/2018/dora-report/
  4. Spotify Engineering. Supercharged developer portals (Backstage), 2024. https://engineering.atspotify.com/2024/04/supercharged-developer-portals/
  5. Open Policy Agent. OPA: Policy-based control for cloud-native environments. https://www.openpolicyagent.org/
  6. AWS Builders’ Library. Timeouts, retries, and backoff with jitter. https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
  7. NIST. Software Supply Chain Security—Executive Order 14028 resources (SBOM等). https://www.nist.gov/itl/executive-order-14028-improving-nations-cybersecurity/software-security-supply-chains-software-1