はじめに
Kubernetes クラスタを IBM Instana で監視していたところ、master(control-plane)ノードも Host としてカウントされ、ライセンス使用量が想定より多くなっていることが判明しました。
本記事では、masterノードを監視対象から除外し、ライセンス使用量を最適化した実装方法とエビデンスを共有します。
TL;DR
Instana AgentのDaemonSetにnodeAffinityを追加し、control-plane/masterラベルを持つノードを除外することでライセンスコストを削減します。
- 対象: masterノードにNoSchedule taintが設定されていない環境
-
方法: nodeAffinityで
node-role.kubernetes.io/control-planeおよびnode-role.kubernetes.io/masterラベルが存在しないノードのみに配置 - 効果: masterノード分のHost Unitを削減、workerノードの監視は継続
⚠️ 重要: この記事の対象環境
この記事は 「masterノードにtaintが設定されていない環境」 を対象としています。
環境による違い
| 環境 | masterのtaint設定 | 対応要否 | 備考 |
|---|---|---|---|
| OpenShift | デフォルトでNoSchedule | 対応不要 | 自動的にmaster除外済み |
| kubeadm(標準) | デフォルトでNoSchedule | 対応不要 | 自動的にmaster除外済み |
| RKE2(標準構成) | デフォルトでNoSchedule | 対応不要 | v1.21以降は自動設定 |
| Rancher管理の検証環境 | taintなし(schedulable) | 対応必要 | 本記事の対象 |
| カスタム構成 | 環境依存 | 要確認 | taint削除している場合 |
今回の検証環境
- Kubernetes: RKE2 v1.34.3
- 管理: Rancher経由で構築
- 構成: masterノード3台 + workerノード6台
-
masterのtaint:
<none>(schedulable状態) - 問題: masterノードにもInstana AgentのDaemonSetが配置される
みなさんの環境で対応が必要か判断する
Step 1: masterノードのtaintを確認
kubectl describe node <master-node> | grep Taints
結果の見方:
# パターン1: taintあり(対応不要)
Taints: node-role.kubernetes.io/master:NoSchedule
Taints: node-role.kubernetes.io/control-plane:NoSchedule
# パターン2: taintなし(本記事の対応が必要)
Taints: <none>
Step 2: masterノードでのInstana Agent配置を確認
kubectl -n instana-agent get pod -o wide | grep master
結果の見方:
# 対応必要: DaemonSetのPodが存在
instana-agent-xxxxx 1/1 Running 0 10d master01 <none> <none>
instana-agent-yyyyy 1/1 Running 0 10d master02 <none> <none>
# 対応不要: k8sensorのみ(Deploymentなので問題なし)
instana-agent-k8sensor-abc123 1/1 Running 0 10d master01 <none> <none>
注: k8sensorはDeploymentなので、レプリカ数に応じて配置されます(DaemonSetのように全ノードには配置されません)。
Step 3: 判断
-
taintが
<none>かつ masterにinstana-agent-xxxxx(DaemonSet)が存在 → 本記事の手順が必要 - taintが設定済み または masterにDaemonSetのPodがない → 対応不要
背景と課題
Instanaのライセンス体系
Instana Observabilityは Host単位 でライセンスが課金されます:
- 1 Host Unit = 1ノードの監視
- DaemonSetで配置されたInstana Agentが稼働するノード数がカウント対象
- masterノードも監視対象になると、その分のライセンスコストが発生
課題
一般的なKubernetes環境(OpenShift、kubeadm、RKE2標準構成など)では、masterノードにデフォルトでNoSchedule taintが設定されているため、DaemonSetは自動的にmasterを除外します。
しかし、taintが設定されていない環境(Rancher管理の検証環境、シングルノードクラスタ、taint削除環境など)では、Instana AgentのDaemonSetがmasterノードにも配置され、Host Unit消費が増加します。
実際のアプリケーションはworkerノードで動作しており、masterのOS-レベル監視は優先度が低いため、このような環境ではnodeAffinityでmasterノードを明示的に除外する必要があります。
⚠️ 重要: control-plane監視は継続されます
本記事で除外するのは「masterノードのOS-レベル監視(DaemonSet)」です。
Kubernetes API経由のcontrol-plane監視(k8sensor経由)は継続されます。
control-plane監視の詳細については、以下の記事をご参照ください:
Instanaでk8sコントロールプレーンを監視してみた
Instana AgentのコンポーネントとDaemonSet/Deploymentの違い
Instana Agentは主に2つのコンポーネントで構成されます:
| コンポーネント | 種類 | 配置単位 | 役割 | ライセンス影響 |
|---|---|---|---|---|
| instana-agent | DaemonSet | 各ノードに1つ | ノード(Host)のOS・インフラ監視 | Host Unit消費 |
| k8sensor | Deployment | クラスタに1つ以上 | Kubernetes API監視 | Host Unit消費なし |
重要な違い
DaemonSet(instana-agent)の動作
# DaemonSetは各ノードに1つずつ配置される
NAME NODE
instana-agent-xxxxx master01 # ← 本記事で除外する対象
instana-agent-yyyyy master02 # ← 本記事で除外する対象
instana-agent-zzzzz master03 # ← 本記事で除外する対象
instana-agent-aaaaa worker01
instana-agent-bbbbb worker02
Deployment(k8sensor)の動作
# Deploymentはレプリカ数に応じて配置(通常1つ、HA構成では複数)
NAME NODE
instana-agent-k8sensor-abc123 master01 # レプリカ数=1の場合
# または
instana-agent-k8sensor-abc123 master01 # レプリカ数=3の場合
instana-agent-k8sensor-def456 master02
instana-agent-k8sensor-ghi789 worker01
ポイント: k8sensorはDeploymentなので、配置ノードはレプリカ数やスケジューリング条件により異なります。masterノードに配置されていても、Host Unitは消費しません。
解決方針
DaemonSetのnodeAffinityを使用して、masterノードを監視対象から除外します。
採用する手法
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
なぜ2つのラベルを指定するのか
Kubernetesのバージョン互換性のため:
| Kubernetesバージョン | masterのラベル |
|---|---|
| v1.20以降 | node-role.kubernetes.io/control-plane |
| v1.19以前 | node-role.kubernetes.io/master |
両方を指定することで、バージョン依存の問題を回避できます。
実装手順
事前準備: バックアップの取得
# 現在のDaemonSet設定をバックアップ
kubectl -n instana-agent get ds instana-agent -o yaml > backup_instana_ds.yaml
# 変更前のPod配置を記録
kubectl -n instana-agent get pod -o wide > before_pods.txt
手順1: DaemonSet設定の取得と編集
# 現在の設定を取得
kubectl -n instana-agent get ds instana-agent -o yaml > modified_ds.yaml
modified_ds.yamlを編集し、spec.template.specに以下を追加:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: instana-agent
namespace: instana-agent
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
# 以下、既存の設定が続く
containers:
- name: instana-agent
# ...
手順2: 変更の適用
# DaemonSetを更新
kubectl apply -f modified_ds.yaml
# ロールアウトの確認
kubectl rollout status daemonset/instana-agent -n instana-agent
手順3: 変更後の確認
# masterノードでのPod配置を確認
kubectl -n instana-agent get pod -o wide | grep master
# DaemonSetの配置状況を確認
kubectl -n instana-agent get ds instana-agent
# 全ノードでのPod配置を確認
kubectl -n instana-agent get pod -o wide
期待される結果:
# 変更前: masterノードにDaemonSetのPodが存在
instana-agent-xxxxx 1/1 Running 0 10d master01 <none> <none>
instana-agent-yyyyy 1/1 Running 0 10d master02 <none> <none>
instana-agent-zzzzz 1/1 Running 0 10d master03 <none> <none>
instana-agent-k8sensor-abc123 1/1 Running 0 10d master01 <none> <none>
# 変更後: masterノードからDaemonSetのPodが削除
# k8sensorはDeploymentなので影響なし(配置ノードは環境依存)
instana-agent-k8sensor-abc123 1/1 Running 0 10d master01 <none> <none>
# workerノードには引き続きDaemonSetが配置
instana-agent-aaaaa 1/1 Running 0 1m worker01 <none> <none>
instana-agent-bbbbb 1/1 Running 0 1m worker02 <none> <none>
重要: k8sensorの配置ノードは環境により異なります。masterノードに配置されていても、それはDeploymentの通常の動作であり、Host Unitを消費しません。
変更前後の比較
アーキテクチャの変更
注: k8sensorの配置ノードはレプリカ数やスケジューリング条件により異なります。上図はレプリカ数=1でmaster01に配置された例です。
監視対象の変化
| 対象 | 変更前 | 変更後 | 影響 |
|---|---|---|---|
| masterノードのOS監視 | 監視中(DaemonSet) | 除外 | Host Unit削減 |
| workerノードのOS監視 | 監視中(DaemonSet) | 監視継続 | 変更なし |
| Kubernetes API監視 | k8sensor(Deployment) | k8sensor継続 | 変更なし |
| アプリケーション監視 | 監視中 | 監視継続 | 変更なし |
変更による効果
本手法により、以下の効果が得られます:
監視対象の変化
- 対象: master(control-plane)ノードのHost監視を除外
- 削減: 環境により異なりますが、本検証環境では約27%のHost Unit削減
- 継続監視: workerノードは引き続き監視対象
具体例(参考値)
クラスタ構成が「master 3台 + worker N台」の場合:
| 項目 | 変更前 | 変更後 |
|---|---|---|
| master監視(DaemonSet) | 3 Hosts | 0 Hosts |
| worker監視(DaemonSet) | N Hosts | N Hosts |
| k8sensor(Deployment) | Host Unit消費なし | Host Unit消費なし |
| 効果 | - | master 3台分のHost Unit削減 |
監視への影響
- 継続: workerノードのOS/アプリケーション監視
- 継続: Kubernetes API監視(k8sensor経由)
- 除外: masterノードのOS-レベル監視
注: 実際の効果は、クラスタ構成・ライセンス契約により異なります。
トラブルシューティング
Q1: nodeAffinity追加後もmasterにDaemonSetのPodが残る
原因:
- DaemonSetの更新戦略が
OnDeleteの場合、手動でPodを削除する必要がある
確認方法:
kubectl -n instana-agent get ds instana-agent -o yaml | grep updateStrategy -A 3
対処方法:
# 更新戦略がOnDeleteの場合、手動でPodを削除
kubectl -n instana-agent delete pod <pod-name>
# または、masterノード上の全instana-agent DaemonSet Podを削除
kubectl -n instana-agent get pod -o wide | grep "instana-agent-" | grep master | grep -v k8sensor | awk '{print $1}' | xargs kubectl -n instana-agent delete pod
注意: k8sensorはDeploymentなので削除対象外です。上記コマンドはgrep -v k8sensorで除外しています。
Q2: workerノードでもPodが起動しない
原因:
- ノードラベルの誤付与
- nodeAffinityの条件設定ミス
確認方法:
# ノードラベルを確認
kubectl get nodes --show-labels
# 特定ノードの詳細を確認
kubectl describe node <worker-node> | grep Labels -A 10
対処方法:
# 誤ってラベルを付与している場合は削除
kubectl label node <worker-node> node-role.kubernetes.io/control-plane-
kubectl label node <worker-node> node-role.kubernetes.io/master-
Q3: k8sensorまで削除されてしまう
原因:
- 誤ってk8sensor用のDeploymentにもnodeAffinityを適用した可能性
確認方法:
kubectl -n instana-agent get deployment instana-agent-k8sensor -o yaml | grep affinity -A 20
対処方法:
k8sensorのDeploymentは別管理です。DaemonSetの変更のみを適用してください。
# DaemonSetのみを対象にしていることを確認
kubectl -n instana-agent get ds instana-agent -o yaml | grep kind
# 出力: kind: DaemonSet
# 誤ってDeploymentを変更した場合はロールバック
kubectl -n instana-agent rollout undo deployment instana-agent-k8sensor
Q4: masterノードにk8sensorが配置されているがHost Unitを消費するか心配
回答:
k8sensorはDeploymentで管理され、Kubernetes APIの監視のみを行います。Host Unitは消費しません。
根拠:
- Instana Agentの公式ドキュメントでは、DaemonSetとして配置されるagentがHost Unit消費対象
- k8sensorはクラスタ全体の情報収集を担当し、ノード単位の監視は行わない
- 実務上、masterにk8sensorが配置されても請求に影響しない
参考: IBM Instana: Monitoring Kubernetes
Q5: ロールアウトが完了しない
原因:
- リソース不足
- イメージのpullエラー
- ノードの状態異常
確認方法:
# Pod状態の詳細確認
kubectl -n instana-agent get pods -o wide
# 特定Podの詳細確認
kubectl -n instana-agent describe pod <pod-name>
# イベントログの確認
kubectl get events -n instana-agent --sort-by='.lastTimestamp'
対処方法:
# リソース不足の場合: 不要なPodを削除
kubectl -n instana-agent delete pod <pending-pod>
# イメージpullエラーの場合: ネットワーク/認証を確認
kubectl -n instana-agent get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].state}'
ロールバック手順
変更に問題がある場合、以下の手順でロールバックできます:
# バックアップから復元
kubectl apply -f backup_instana_ds.yaml
# ロールアウトの完了を待機
kubectl rollout status daemonset/instana-agent -n instana-agent
# 復元後の確認
kubectl -n instana-agent get pod -o wide
応用例: 他のノード除外シナリオ
本手法は、master除外以外にも応用できます。
応用パターン一覧
| ユースケース | 除外対象 | 適用方法 |
|---|---|---|
| GPU搭載ノード除外 | GPU専用ノード | node.kubernetes.io/instance-type NotIn [g4dn.xlarge] |
| 開発環境除外 | dev環境ノード | environment NotIn [development] |
| 特定ゾーン除外 | DR用ゾーン | topology.kubernetes.io/zone NotIn [us-west-2c] |
| メンテナンス中除外 | 一時的除外 | maintenance DoesNotExist |
応用例1: GPU搭載ノードを監視対象外にする
背景: GPUノードは高コストで監視優先度が低い
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: NotIn
values:
- g4dn.xlarge
- p3.2xlarge
適用手順:
# GPUノードのインスタンスタイプを確認
kubectl get nodes -o custom-columns=NAME:.metadata.name,INSTANCE:.metadata.labels.node\\.kubernetes\\.io/instance-type
# 上記のYAMLを適用
kubectl apply -f modified_ds.yaml
応用例2: 開発環境ノードを監視対象外にする
背景: 開発環境は頻繁に再構築されるため監視不要
事前準備: ノードにラベルを付与
# 開発環境ノードにラベルを付与
kubectl label node dev-node01 environment=development
kubectl label node dev-node02 environment=development
DaemonSet設定:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: environment
operator: NotIn
values:
- development
応用例3: 特定ゾーンを監視対象外にする
背景: DRゾーンは通常待機状態で監視優先度が低い
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: NotIn
values:
- us-west-2c # DRゾーン
応用例4: 複数条件の組み合わせ
背景: masterとGPUノードの両方を除外
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
- key: node.kubernetes.io/instance-type
operator: NotIn
values:
- g4dn.xlarge
まとめ
学んだこと
- DaemonSetはデフォルトで全ノード対象: taintがない環境ではmasterノードにも配置される
-
nodeAffinityで制御可能:
DoesNotExist演算子で特定ラベルを持つノードを除外できる -
バージョン互換性の考慮:
control-planeとmasterの両ラベルを指定することで互換性を確保 - DaemonSetとDeploymentの違い: DaemonSetは各ノードに配置、Deploymentはレプリカ数に応じて配置
- k8sensorは別管理: DeploymentなのでDaemonSetの変更の影響を受けない
- エビデンスの重要性: 変更前後の差分とPod配置を記録することでトラブル時の原因究明が容易
実務での活用
- コスト最適化: ライセンス上限に近い環境で余裕を確保
- 柔軟な監視制御: 環境・用途に応じた監視対象の選択が可能
参考資料
- Kubernetes: Assigning Pods to Nodes
- Kubernetes: Node Affinity
- Kubernetes: Taints and Tolerations
- IBM Instana Documentation: Agent Configuration
- IBM Instana Pricing
- OpenShift: Taints and Tolerations
- IBM Instana: Monitoring Kubernetes
本記事が同じような課題に直面している方の参考になれば幸いです。