Helmで行き詰まったとき
ルーチンの更新を本番環境にプッシュし、CI/CDパイプラインからグリーンのチェックマーク(成功)を期待しているとします。しかし、デプロイが突然停止します。Helmが特定の、イライラさせるようなエラーを返します:rendered manifests contain a resource that already exists。リリースがスタックし、クラスターは中途半端な状態になります。
Error: UPGRADE FAILED: rendered manifests contain a resource that already exists. Unable to continue with update: existing resource conflict
この競合は、HelmがConfigMap、Secret、Serviceなどのリソースを作成しようとした際に、そのリソースが既に名前空間に存在するものの、現在のHelmリリースの管理下にない場合に発生します。通常、これは誰かが kubectl を使用して手動でクラスターを調整したか、以前のインストール試行が失敗して「孤立した」リソースが残されたことが原因です。
なぜHelmは処理を拒否するのか
Helmは過保護な設計になっています。helm upgrade を実行すると、YAMLマニフェストが生成され、稼働中のクラスターと比較されます。同じ名前と種類(Kind)のリソースが見つかり、それに現在のリリースの特定のメタデータ(ラベルとアノテーション)が含まれていない場合、Helmは処理を中止します。Helmはそのリソースが他の誰かの管理下にあると判断し、予期せぬデータ損失を防ぐために上書きを拒否します。
通常、以下の3つのシナリオで見られます:
- チームメンバーがクイックフィックスのために手動で
kubectl applyを実行した。 - 以前の
helm installが5分でタイムアウトし、クラッシュした。 - レガシーなモノリシックチャートから新しいサブチャートにリソースを移動している。
ステップ1:原因を特定する
エラーメッセージは通常、問題を引き起こしている特定のリソースを特定します。以下のように表示されるはずです:
Existing resource conflict: kind: Secret, namespace: production, name: app-credentials
そのリソースの現在の状態を確認し、実際に何かに管理されているか見てみましょう:
kubectl get secret app-credentials -n production -o yaml
metadata セクションを確認してください。app.kubernetes.io/managed-by: Helm と meta.helm.sh/release-name アノテーションがない場合、Helmはそのリソースを「所有」していません。それは孤立したリソースです。
オプションA:クイックリセット(削除して再作成)
もしそのリソースがステートレス(重要なデータを保持しないServiceやConfigMapなど)であれば、最も早い解決策はそれを削除することです。次回のアップグレード試行時に、Helmがそれを再作成します。
# 競合しているリソースを削除
kubectl delete secret app-credentials -n production
# アップグレードを再試行
helm upgrade --install my-app ./charts/my-app -n production
警告: 検証済みのバックアップがない限り、PersistentVolumeClaims や、本番環境の固有のキー(RDSのパスワードなど)を含む Secrets に対してこれを行わないでください。
オプションB:リソースをHelmの管理下に取り込む
ダウンタイムが1秒も許されない場合があります。この場合、既存のリソースをHelmが所有していることを「教える」必要があります。これは、Helmが期待するラベルとアノテーションを手動で適用することで行います。
既存のリソースにタグを付けるために、以下のコマンドを実行します:
# 変数を定義
RELEASE_NAME="my-app"
NAMESPACE="production"
RESOURCE_KIND="secret"
RESOURCE_NAME="app-credentials"
# 必要なアノテーションを追加
kubectl annotate $RESOURCE_KIND $RESOURCE_NAME -n $NAMESPACE meta.helm.sh/release-name=$RELEASE_NAME
kubectl annotate $RESOURCE_KIND $RESOURCE_NAME -n $NAMESPACE meta.helm.sh/release-namespace=$NAMESPACE
# 必要なラベルを追加
kubectl label $RESOURCE_KIND $RESOURCE_NAME -n $NAMESPACE app.kubernetes.io/managed-by=Helm
タグを付けたら、再度 helm upgrade を実行してください。Helmはそのリソースを自身のものとして認識し、シームレスに更新します。
「Another Operation in Progress」エラーの修正
以前の試行が中断された場合(CIランナーが300秒後にタイムアウトしたなど)、二次的なエラー another operation is in progress が表示されることがあります。Helmはデプロイがまだ実行中であると考え、リリースの状態を pending-upgrade としてロックします。
このロックを解除するには、その特定の失敗した試行を追跡するためにHelmが使用しているSecretを見つけて削除する必要があります。これらは通常、sh.helm.release.v1.[RELEASE_NAME].v[VERSION] という名前です。
# 全てのリリースのバージョンをリストして最大の番号を確認
kubectl get secrets -n production | grep sh.helm.release.v1.my-app
# 「pending」バージョンのSecret(例:v15)を削除
kubectl delete secret sh.helm.release.v1.my-app.v15 -n production
そのSecretが削除されると、Helmは最後に成功したバージョン(例:v14)に戻り、アップグレードを再試行できるようになります。
検証と予防
修正後、リリースが正常であることを確認します:
helm status my-app -n productionを実行し、STATUS: deployedであることを確認します。- メタデータを確認します:
kubectl get secret app-credentials -n production -o jsonpath='{.metadata.labels}'。Helmの管理ラベルが表示されているはずです。
今後これを避けるために、パイプラインでは常に --atomic フラグを使用してください。これにより、デプロイが失敗した場合にHelmが自動的に前の安定した状態にロールバックし、リリースが pending ループでスタックするのを防ぎます。

