---
layout: blog
title: "よくあるKubernetesの7つの落とし穴(そして私がそれらを回避する方法をいかに学んだか)"
date: 2025-10-20T08:30:00-07:00
slug: seven-kubernetes-pitfalls-and-how-to-avoid
author: >
  Abdelkoddous Lhajouji
translator: >
  [Takuya Hashimoto](https://github.com/hassaku63)
---

Kubernetesが強力でありながら時にイライラさせられることは周知の事実です。
私がコンテナオーケストレーションに初めて手を出したとき、落とし穴のリスト全体をまとめるのに十分なほど多くの失敗をしました。
この投稿では、私が遭遇した(または他の人が遭遇するのを見た)7つの大きな落とし穴を順に説明し、それらを回避する方法についてのヒントを共有したいと思います。
Kubernetesを試し始めたばかりの方でも、すでに本番クラスターを管理している方でも、これらの洞察が余計なストレスを回避するのに役立つことを願っています。

## 1. リソースrequestsとlimitsの設定を怠る {#1-skipping-resource-requests-and-limits}

**落とし穴**: Pod仕様でCPUとメモリの要件を指定しないこと。
これは通常、Kubernetesがこれらのフィールドを必須としておらず、ワークロードはこれらなしでも起動して実行できる場合が多いために発生します。
そのため、設定の初期段階や迅速なデプロイサイクルの最中に見過ごされがちです。

**背景**:
Kubernetesでは、リソースのrequestsとlimitsは効率的なクラスター管理に不可欠です。
リソースのrequestsは、スケジューラーが各Podに適切な量のCPUとメモリを確保し、動作に必要なリソースを保証します。
リソースlimitsは、Podが使用できるCPUとメモリの量に上限を設け、単一のPodが過剰なリソースを消費して他のPodを枯渇状態にすることを防ぎます。
リソースrequestsとlimitsが設定されていない場合:

 1. リソース不足: Podが不十分なリソースしか得られず、パフォーマンスの低下や障害につながる可能性があります。
  これは、Kubernetesがrequestsの値に基づきPodをスケジュールするためです。
  requestsがないと、スケジューラーは単一のノードに過剰数のPodを配置する可能性があり、リソースの競合やパフォーマンスのボトルネックにつながります。
 2. リソースの占有: 逆に、limitsがないと、あるPodが公平な配分以上のリソースを消費し、同じノード上の他のPodのパフォーマンスと安定性に影響を与える可能性があります。
  これにより、利用可能なメモリ不足のために他のPodが退避されたり、Out-Of-Memory(OOM)キラーによって強制終了されたりする問題が発生する可能性があります。

### 回避方法: {#how-to-avoid-it}
- 控えめな`requests`(例えば`100m` CPU、`128Mi`メモリ)から始めて、アプリの動作を確認します。
- 実際の使用状況を監視して値を調整します。[HorizontalPodAutoscaler](/docs/tasks/run-application/horizontal-pod-autoscale/)は、メトリクスに基づいてスケーリングを自動化するのに役立ちます。
- `kubectl top pods`やログ/監視ツールを注視して、過剰または過小なプロビジョニングになっていないことを確認します。

**私の実体験**: 初期の頃、私はメモリ制限について考えたことがありませんでした。
ローカルクラスターでは問題なく見えました。
しかし、より大規模な環境では、Podが次々と*OOMKilled*されました。
教訓を得ました。
コンテナのリソースrequestsとlimitsを設定する詳細な手順については、[コンテナおよびPodへのメモリリソースの割り当て](/docs/tasks/configure-pod-container/assign-memory-resource/)(公式Kubernetesドキュメントの一部)を参照してください。

## 2. liveness probeとreadiness probeを軽視する {#2-underestimating-liveness-and-readiness-probes}

**落とし穴**: Kubernetesがコンテナの健全性や準備状態をチェックする方法を明示的に定義せずにコンテナをデプロイすること。
これは、Kubernetesが内部のプロセスが終了していない限りコンテナを「実行中」と見なすために起こりがちです。
追加のシグナルがないと、Kubernetesは、たとえ内部のアプリケーションが応答しない、初期化中、またはスタックしていても、ワークロードが機能していると想定してしまいます。

**背景**:
liveness、readiness、startup probeは、Kubernetesがコンテナの健全性と可用性を監視するために使用するメカニズムです。

- **Liveness probe**は、アプリケーションがまだ生きているかどうかを判断します。
  livenessチェックが失敗すると、コンテナは再起動されます。
- **Readiness probe**は、コンテナがトラフィックを処理する準備ができているかどうかを制御します。
  readiness probeが合格するまで、コンテナはServiceエンドポイントから除外されます。
- **Startup probe**は、長い起動時間と実際の障害を区別するのに役立ちます。

### 回避方法: {#how-to-avoid-it-1}
- ヘルスエンドポイント(例えば`/healthz`)をチェックするためのシンプルなHTTP `livenessProbe`を追加して、Kubernetesがハングしたコンテナを再起動できるようにします。
- `readinessProbe`を使用して、アプリがウォームアップされるまでトラフィックがアプリに到達しないようにします。
- probeはシンプルに保ちます。
  過度に複雑なチェックは、誤検知や不要な再起動を引き起こす可能性があります。

**私の実体験**: かつて、ロードに時間がかかるWebサービスのreadiness probeを忘れたことがあります。
ユーザーが早すぎるタイミングでアクセスして、奇妙なタイムアウトが発生し、何時間も頭を抱えました。
たった3行のreadiness probeがあれば、防げたはずでした。

コンテナのliveness、readiness、startup Probeを設定する包括的な手順については、公式Kubernetesドキュメントの[Liveness Probe、Readiness ProbeおよびStartup Probeを使用する](/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)を参照してください。

## 3. 「コンテナログを見ればいいだけ」(これが悲劇の始まり) {#3-we-ll-just-look-at-container-logs-famous-last-words}

**落とし穴**: `kubectl logs`で取得したコンテナログのみに依存すること。
このコマンドは迅速かつ便利で、多くのセットアップにおいて、開発中や初期のトラブルシューティング中にログにアクセスできるように見えるため、これが起こりがちです。
しかし、`kubectl logs`は現在実行中または最近終了したコンテナからのログのみを取得し、それらのログはノードのローカルディスクに保存されます。
コンテナの削除、退避、またはノードの再起動が発生すると、すぐにログファイルはローテーションされるか、永久に失われる可能性があります。

### 回避方法: {#how-to-avoid-it-2}
- **ログを集中管理**するために、[Fluentd](https://kubernetes.io/docs/concepts/cluster-administration/logging/#sidecar-container-with-a-logging-agent)や[Fluent Bit](https://fluentbit.io/)のようなCNCFツールを使用して、すべてのPodからの出力を集約します。
- **OpenTelemetryを採用**して、ログ、メトリクス、(必要に応じて)トレースの統合ビューを取得します。
  これにより、インフラストラクチャイベントとアプリレベルの振る舞いとの相関関係を見つけることができます。
- **ログとPrometheusメトリクスを連携**し、アプリケーションログと並行してクラスターレベルのデータを追跡します。
  分散トレーシングが必要な場合は、[Jaeger](https://www.jaegertracing.io/)のようなCNCFプロジェクトを検討してください。

**私の実体験**: 突然の再起動によって初めてPodログを失ったとき、「kubectl logs」だけではいかに頼りないかを実感しました。
それ以来、重要な手がかりを見逃さないように、すべてのクラスターに適切なパイプラインをセットアップしています。

## 4. 開発環境と本番環境を完全に同じに扱う {#4-treating-dev-and-prod-exactly-the-same}

**落とし穴**: 開発、ステージング、本番環境全体で同一の設定を持つ同じKubernetesマニフェストをデプロイすること。
これは、チームが一貫性と再利用性を目指すものの、環境固有の要因—トラフィックパターン、リソースの可用性、スケーリングのニーズ、またはアクセス制御など—が大きく異なりうることを見落としている場合によく起こります。
カスタマイズなしでは、ある環境向けに最適化された設定が別の環境では不安定性、パフォーマンス低下、またはセキュリティ欠陥を引き起こす可能性があります。

### 回避方法: {#how-to-avoid-it-3}
- 環境オーバーレイまたは[kustomize](https://kustomize.io/)を使用して、共通ベースを維持しながら、各環境のリソースrequests、レプリカ、または設定をカスタマイズします。
- 環境固有の設定をConfigMapやSecretに切り出します。
  機密データを管理するには、[Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)のような特化したツールを使用できます。
- 本番環境ではスケールを考慮した計画を。
  開発クラスターは最小限のCPU/メモリで済むかもしれませんが、本番環境では大幅により多くのリソースが必要になる可能性があります。

**私の実体験**: ある時、小さな開発環境で「テスト」のために`replicaCount`を2から10にスケールアップしました。
すぐにリソース不足になり、後始末に半日を費やしました。
しまった。

## 5. 古いリソースを放置する {#5-leaving-old-stuff-floating-around}

**落とし穴**: 未使用または古いリソース—Deployment、Service、ConfigMap、PersistentVolumeClaimなど—をクラスター内で実行したままにすること。
これは、Kubernetesが明示的に指示されない限りリソースを自動的に削除せず、所有権や有効期限を追跡する組み込みメカニズムがないためによく起こります。
時間の経過とともに、これらの忘れられたオブジェクトが蓄積し、クラスターリソースを消費し、クラウドコストを増加させます。
特に、古いServiceやLoadBalancerがトラフィックをルーティングし続けていると、運用上の混乱を引き起こす可能性があります。

### 回避方法: {#how-to-avoid-it-4}
- **すべてにラベルを付ける**: 目的や所有者のラベルを付けます。
  そうすれば、不要になったリソースを簡単にクエリできます。
- **定期的にクラスターを監査する**: `kubectl get all -n <namespace>`を実行して、実際に実行されているものを確認し、すべてが正当であることを確認します。
- **Kubernetesのガベージコレクションを採用する**: [K8sドキュメント](/docs/concepts/architecture/garbage-collection/)は、依存オブジェクトを自動的に削除する方法を示しています。
- **ポリシーの自動化を活用する**: [Kyverno](https://kyverno.io/)のようなツールは、一定期間後に古いリソースを自動的に削除またはブロックしたり、ライフサイクルポリシーを強制したりできます。
  そのため、すべてのクリーンアップ手順をひとつひとつ覚えておく必要がありません。

**私の実体験**: ハッカソンの後、外部ロードバランサーに固定された「test-svc」を削除するのを忘れました。
3週間後、そのロードバランサーの料金をずっと支払っていたことに気づきました。
やってしまった。

## 6. ネットワークを早々に深掘りしすぎる {#6-diving-too-deep-into-networking-too-soon}

**落とし穴**: Kubernetesのネイティブなネットワークプリミティブを完全に理解する前に、高度なネットワークソリューション—サービスメッシュ、カスタムCNIプラグインやマルチクラスター通信—を導入すること。
これは、チームがコアのKubernetesネットワークの仕組み(Pod間通信、ClusterIP Service、DNS解決、基本的なIngressトラフィック処理を含む)を最初に習得せずに、外部ツールを使用してトラフィックルーティング、可観測性、mTLSなどの機能を実装する際によく発生します。
結果として、ネットワーク関連の問題のトラブルシューティングが難しくなります(特に、オーバーレイが追加の抽象化や障害点をもたらす場合)。

### 回避方法: {#how-to-avoid-it-5}

- 小さく始める: Deployment、Service、そしてNGINXをベースとするような基本的なIngressコントローラー(例: Ingress-NGINX)を使用します。
- クラスター内でのトラフィックの流れ、サービスディスカバリの仕組み、DNSの設定方法を理解していることを確認します。
- 本格的なメッシュや高度なCNI機能は、実際に必要な場合にのみ移行します。
  複雑なネットワークはオーバーヘッドを増加させます。

**私の実体験**: かつて、小さな内部アプリでIstioを試したところ、実際のアプリよりもIstio自体のデバッグに多くの時間を費やしました。
最終的に一歩引いてIstioを削除したところ、すべてが正常に機能しました。

## 7. セキュリティとRBACを軽視する {#7-going-too-light-on-security-and-rbac}

**落とし穴**: 安全でない設定でワークロードをデプロイすること。
例えば、rootユーザーとしてコンテナを実行する、`latest`イメージタグを使用する、セキュリティコンテキストを無効にする、`cluster-admin`のような過度に広範なRBACロールを割り当てるなど。
これらの慣習が根強く残っているのは、Kubernetesが初期状態では厳格なセキュリティデフォルトを強制せず、プラットフォームが意見を押し付けるのではなく柔軟に設計されているためです。
明示的なセキュリティポリシーが設定されていない場合、クラスターはコンテナエスケープ、不正な権限昇格、あるいはバージョン固定されていないイメージによる意図しない本番環境の変更といったリスクにさらされ続ける可能性があります。

### 回避方法: {#how-to-avoid-it-6}

- [RBAC](/docs/reference/access-authn-authz/rbac/)を使用して、Kubernetes内でロールと権限を定義します。
  RBACはデフォルトであり最も広くサポートされている認可メカニズムですが、Kubernetesは代替の認可メカニズムの使用も許可しています。
  より高度または外部ポリシーのニーズについては、[OPA Gatekeeper](https://open-policy-agent.github.io/gatekeeper/)(Regoベース)、[Kyverno](https://kyverno.io/)、またはCELや[Cedar](https://cedarpolicy.com/)のようなポリシー言語を使用したカスタムWebhookなどのソリューションを検討してください。
- イメージを特定のバージョンに固定しましょう(`:latest`はもう使わない！)。
  これにより、実際に何がデプロイされているかを把握しやすくなります。
- [Podのセキュリティアドミッション](/docs/concepts/security/pod-security-admission/)(またはKyvernoのような他のソリューション)を活用して、非rootコンテナ、読み取り専用ファイルシステムなどを強制します。

**私の実体験**: 私は大きなセキュリティ侵害を経験したことはありませんが、教訓となる話は数多く聞いています。
対策を講じなければ、何か問題が起こるのは時間の問題です。

## 最後に {#final-thoughts}

Kubernetesは素晴らしいですが、超能力者ではありません。
何が必要かを伝えなければ、魔法のように正しいことをしてくれるわけではありません。
これらの落とし穴を心に留めておくことで、多くの悩みの種と無駄な時間を避けられます。
失敗は起こります(信じてください、私も十分失敗しました)が、それぞれがKubernetesの仕組みをより深く学ぶチャンスです。
より深く掘り下げたい場合は、[公式ドキュメント](/docs/home/)と[コミュニティSlack](http://slack.kubernetes.io/)が次のステップとして最適です。
そしてもちろん、あなた自身の失敗談や成功のヒントを自由に共有してください。
結局のところ、私たちは皆、このクラウドネイティブの冒険を一緒に歩んでいるのですから。

**Happy Shipping!**
