Carpe Diem

備忘録

Kubernetesの初学者がlabelについて気になること

概要

ラベルという名前ゆえか若干軽視されがちですが、Kubernetesにおいてlabelはリソースを識別する上で非常に重要な要素です。
そのlabelについてKubernetesの初学者が気になる(自身も気になった)ことを挙げて行きます。

気になること

Deploymentにあるlabelが多い

公式ドキュメントに記載されているDeploymentの例を見てみます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:  # ①
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:  # ②
      app: nginx
  template:
    metadata:
      labels:  # ③
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

この様にlabelに関連するところがなんと3つもあります。

それぞれ役割があり、まとめると以下です。

番号 説明
Deployment自体のラベル
どのPodをDeploymentの対象とするかのラベルセレクタ
Pod自体のラベル

DeploymentのmatchLabelsとtemplateは全て一致させるべき?

先程の②と③についてですが

.spec.selector.spec.template.metadata.labelsと一致している必要があり、一致しない場合はAPIによって拒否されます。

ref: https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/#selector

とあるように一致しない場合は次のようなエラーになります。

The Deployment "xxxx" is invalid: spec.template.metadata.labels: Invalid value: map[string]string{"app":"nginx", "track":"stable"}: selector does not match template labels

ただし次のように一部を一致させる、であればAPIはエラーになりません。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx  # ここだけ一致
        track: stable
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

しかし

セレクターが重複する複数のコントローラーを持つとき、そのコントローラーは互いに競合状態となり、正しくふるまいません。

ref: https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/#selector

とあるように、意図せず他のtemplateと一致したりしないよう基本的にはtemplateのラベルとセレクタは全てのラベルを一致させるのが良いでしょう。

labelの一部を一致させたいケースって?

カナリアリリースのユースケースの場合、本番のDeploymentでは以下のようにし

     name: frontend
     replicas: 3
     ...
     labels:
        app: guestbook
        tier: frontend
        track: stable
     ...
     image: gb-frontend:v3

カナリアのDeploymentでは

     name: frontend-canary
     replicas: 1
     ...
     labels:
        app: guestbook
        tier: frontend
        track: canary
     ...
     image: gb-frontend:v4

のように一部のlabelで区別します。

そしてServiceは共通部分でラベルを一致させます。

  selector:
     app: guestbook
     tier: frontend

これによって、Deploymentは分けつつも、ServiceがルーティングするPodは両方になりトラフィックがそれぞれ流れるようにできます。

ref: https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments

なんでServiceはmatchLabelsがないの?

stackoverflow.com

を読む感じでは歴史的な背景があり、元々は等価ベースのセレクタ(DeploymentでいうmatchLabels)しかなかったものの、後から集合ベースの条件設定(matchExpressions)をできるようにしたためなようです。

なので新しいリソースであるDeploymentなどはmatchLabelsmatchExpressionsを選べるようにフィールドが増えており、古いリソースであるServiceは等価ベースのみで区別する必要がないのでmatchLabelsが無いようです。

ラベルセレクタがimmutable

ラベルセレクタはimmutableであり、削除・再作成せずに変更をrolloutしようとすると

v1.LabelSelector{MatchLabels:map[string]string{“app”:“nginx”}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

のようなエラーが出ます。

これは↓のIssueからラベルセレクタがimmutableになったためです。

github.com

ラベルセレクタを変更することで孤児Podが生まれる可能性があるなど、意図しない挙動が発生するためこの判断に至ったようです。

まとめ

Kubernetesの初学者がLabelについて気になることについてまとめてみました。

参考