kakakakakku blog

Weekly Tech Blog: Keep on Learning!

便利な Kubernetes マニフェスト用エディタ「Monokle」

最近「Monokle」を使っている.Monokle(モノクレ)は「Kubernetes マニフェスト用エディタ」とも言えるアプリで,2週間ほど使って操作にも慣れてきたので紹介する!macOS でも Windows でもアプリをダウンロードすればすぐに使えるぞ!

github.com

機能 ☸️

Monokle の機能(できること)はドキュメントに載っている.機能は本当に多くて,継続的にリリースもされている.今回は代表的な機能を4点紹介する!Monokle v1.6.0 を前提にする.UI は日々変わるかも!

  1. マニフェスト操作
  2. Kustomize 対応
  3. Helm 対応
  4. Kubernetes クラスタ統合

kubeshop.github.io

検証環境 ☸️

今回はブログ記事用にサンプルマニフェストを管理するディレクトリを作った(非公開).すぐに試すなら普段仕事などで使っている GitHub リポジトリのディレクトリなどを読み込んでしまって OK!個人的に Monokle を使い始めたときは複雑な UI に慣れなかったけど,使い続ければ慣れてくる!

$ tree -L 1 .
.
├── helm
├── kustomize
└── manifests

1. マニフェスト操作 ☸️

まず,Monokle 画面は大きく「3種類」のウィジェットで構成されている.まず,左側にある「File Explorer」でディレクトリのマニフェストなどを一覧できる.適当にマニフェストを選択すると,右側にある「Editor」で YAML を直接編集できる(ちゃんと補完も効くぞー👏).そして,真ん中にある「Navigator」では Kubernetes リソースごとにオブジェクトを一覧できる.さらにオブジェクトの関係性を確認できる Outgoing Links 機能も便利!

f:id:kakku22:20220401175849p:plain

「Editor」で特に便利なのは Form 機能で,マニフェストの各フィールドを GUI で設定できる.マニフェストを書くときにフィールド名を調べることも多いけど,Form 機能を使えば簡単に YAML に反映できる.以下のキャプチャには Deployment の spec.selector.matchLabelsstrategy:type などを載せている.下にスクロールをするとまだまだ多くのフィールドが出てくる!

f:id:kakku22:20220401175934p:plain

新しくマニフェストを作るときは File ExplorerAdd Resource を選択すると GUI で作れる.

f:id:kakku22:20220401175943p:plain

またマニフェストの記法エラーにも気付ける.意図的に以下のエラーを混入させてみた.Editor でエラーに気付けるのは便利ー!

  • spec.replicas に文字列を指定する🔥
  • spec.template.spec.volumes に存在しない ConfigMap を指定する🔥

f:id:kakku22:20220401175954p:plain

2. Kustomize 対応 ☸️

Monokle は Kustomize に対応している.左側のメニューで Kustomize アイコンを選択する.今回は検証用にサンプルプロジェクトを作った.

$ tree kustomize
kustomize
├── base
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── namespace.yaml
└── overlays
    ├── dev
    │   └── kustomization.yaml
    └── prd
        └── kustomization.yaml

kustomize/overlays/prd/kustomization.yaml を以下のように設定して,Monokle で Preview を押すと自動的に kustomize build overlays/prd が実行されて Editor に表示される.試行錯誤をしながら kustomization.yaml を実装するときに便利!また Kustomize の Editor にも Form 機能があり configMapGeneratorpatchesStrategicMerge なども GUI で設定できる!イイ!

bases:
  - ../../base

namespace: my-kustomize-prd

replicas:
  - name: my-kustomize-prd-nginx
    count: 10

namePrefix: my-kustomize-prd-

f:id:kakku22:20220401180005p:plain

3. Helm 対応 ☸️

Monokle は Helm にも対応している.左側のメニューで Helm アイコンを選択する.今回は検証用にサンプルプロジェクトを作った.

$ tree helm
helm
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

Monokle で Preview を押すと templates/*.yaml のテンプレートに values.yaml を展開したマニフェストを確認できる.アプリケーションを Helm Chart にまとめている場合に便利そう!

f:id:kakku22:20220401180016p:plain

4. Kubernetes クラスタ統合 ☸️

ここまでは「Kubernetes マニフェスト用エディタ」として,マニフェスト関連の操作を紹介してきたけど,Monokle を使うと Kubernetes クラスタに接続もできる(kubeconfig の設定は前提となる).クラスタにデプロイされた Kubernetes オブジェクトを一覧したり,マニフェストを編集して反映もできる.以下のキャプチャは検証用の Amazon EKS クラスタに接続している(なんと 662 オブジェクトもあった!).

f:id:kakku22:20220401180026p:plain

まとめ ☸️

最近使っている「Monokle」を紹介した.今後も使っていくぞー!

github.com

2022年のリリース ☸️

kubeshop.io

kubeshop.io

Software Design 2022年4月号の特集「本質から学ぶ Git」を読んだ

「Software Design 2022年4月号」を読んだ.本誌の第2特集「堂々と使える!人に教えられる!本質から学ぶ Git」に寄稿をされた id:syobochim に献本(ギフトコード)をもらったので第2特集を中心にまとめる.献本ありがとうございます!

第2特集 目次

本特集は以下の4章から構成されている.基礎的な内容から実践的な内容まで幅広くまとまっている.ただ内容的には 1,2,4 章と 3 章で前提となる技術レベルが違う気もするので,初学者は 1,2,4 章を読んで,Git の内部まで興味があれば 3 章も読んでも良さそう.

  • 第1章 : コミットの記録、リポジトリの状態確認のやり方
  • 第2章 : ブランチやリモートリポジトリの扱い
  • 第3章 : 本質から学ぶ git コマンド
  • 第4章 : チーム開発 / OSS 開発におけるマナー

また特集名にも含まれている「人に教えられる!」という観点もイイ!

第1章

「第1章」では「コミットとは何か?」「作業ディレクトリとステージングエリアの違いは何か?」など,Git に入門するときに知っておくべき知識がわかりやすくまとまっていた.雑誌ならではのコンパクトな分量で図解が多いのも良かった.実際に git configgit init コマンドを使ってセットアップをして,git addgit commit コマンドを使って「お買い物リストファイル」を作り上げていくので,初学者でも挫折せずに学べる.

日常的に Git を使っていて慣れてるけど,せっかくだから試したw

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   ShoppingList.md
    new file:   ToDoList.md

個人的に今まで初学者に Git を教えてきた経験からすると「なぜ "ステージングエリア" に登録する必要があるの?」とよく聞かれる.直接コミットすれば良いじゃん!と.本誌ではステージングエリアを使えば「変更したファイルの中からコミットするファイルを明示的に指定できる」と紹介されている.具体的にファイルを明示的に指定してコミットする例や git commit -a -m コマンドの紹介があっても良かったかも!

git-scm.com

第2章

「第2章」では git mergegit pull コマンドを使ってブランチ操作の基礎を学べる.実際にターミナルと GitHub 画面を使って,一人二役で練習できる流れも良かった.そして,初学者がよくつまずく「コンフリクト」「origin って何?」という内容も載っている.より詳しく学ぶには同じく今月に出版された「いちばんやさしい Git & GitHub の教本 第2版」を読むと良さそう!

また本誌では git checkout コマンドを使いつつ,関連する git switch コマンドの紹介が載っているのも良かった.

kakakakakku.hatenablog.com

第3章

「第3章」では Git リポジトリの内部構造を学べる.「第1,2章」とは違ってより深く学べて git cat-file コマンドを使って Git オブジェクトを確認したりする.このあたりは今まで「何となく」理解していたこともあって,コミットグラフの解説などもとても楽しめた.確かにここまで詳しく理解できれば「堂々と使える!」に近付けそう!

$ git cat-file -p dbe1
tree 4013e11029cac8cdc48706a85104a3ae7dbfa64f
parent 226c37e1a7068c4d30b4e4e2ebd0b75db464cef6
author kakakakakku <y.yoshida22@gmail.com> 1648731388 +0900
committer kakakakakku <y.yoshida22@gmail.com> 1648731407 +0900

memo.txt を新規作成しました

git-scm.com

第4章

「第4章」は仕事や OSS で Git を使うときに知っておくべき「お作法」を学べる.「意味のある」コミットメッセージを書くコツや Conventional Commits を取り入れる案なども紹介されていてイイ!また「コミットが大きすぎる可能性」を意識しましょう!というメッセージもとても重要で良かった.ちょうど4月から開発プロジェクトに参加する人もいるだろうし,本誌で最低限の「お作法」を学んで,取り込まれやすいプルリクエストを作れるようになろう!

www.conventionalcommits.org

まとめ

「Software Design 2022年4月号」を読んだ.今回の記事では第2特集「堂々と使える!人に教えられる!本質から学ぶ Git」を読んだ感想をまとめた.他にも「HashiCorp Vault 連載」「Log4j2 特集」も良かった.最近は正直言って技術雑誌を読まなくなってたけど,久し振りに読んでみると多岐にわたる技術トピックをコンパクトに学べるメリットを再認識できた!

関連記事

寄稿おめでとうございまーす🎉

syobochim.hatenablog.com

Git や GitHub の操作を学ぶなら「GitHub Learning Lab」もイイぞー!

kakakakakku.hatenablog.com

Descheduler for Kubernetes : 戦略に違反する Pod を他のノードに移動する

Kubernetes を使っていると,運用面で起動中の Pod を他のノードに移動(再スケジューリング)したくなる場面がある.以下に具体的な例を挙げる.理由としては,Kubernetes では kube-scheduler によって Pod を起動する前にノードが決まる仕組み(スケジューリング)になっている.よって,スケジューリング後の Pod は再スケジューリングされず,起動され続けることになる.

  • ノードを追加した後に起動中の Pod を移動したい
  • 一部ノードの使用率が高いので起動中の Pod を移動したい
  • 後からノードに taint を追加したから条件に合わない Pod を移動したい

Descheduler for Kubernetes 🧩

今回紹介する「Descheduler for Kubernetes」を使うと,設定した「戦略」によって起動中の Pod を再スケジューリングする仕組みを Kubernetes クラスタに導入できる.正確に言うと「Descheduler」によって Pod が「再スケジューリング」されるのではなく,「Descheduler」によって「Eviction(排出)」された Pod が kube-scheduler によって「再スケジューリング」される.

github.com

セットアップ 🧩

Quick Start を読むと複数のセットアップ方法が紹介されている.今回は Helm を使う.Helm を使うとデフォルトでは CronJobDescheduler が起動されるけど,設定によって Deployment も選べるようになっている.

  • Run As A Job
  • Run As A CronJob
  • Run As A Deployment
  • Install Using Helm
  • Install Using Kustomize

手順は Artifact Hub に載っている.helm install コマンドを実行すれば簡単にセットアップできる.ConfigMap を確認すると,デフォルトでは以下の5個の「戦略」が有効化されていた.戦略に関しては後述する!

  • LowNodeUtilization
  • RemoveDuplicates
  • RemovePodsViolatingInterPodAntiAffinity
  • RemovePodsViolatingNodeAffinity
  • RemovePodsViolatingNodeTaints

artifacthub.io

$ helm repo add descheduler https://kubernetes-sigs.github.io/descheduler/

$ helm install descheduler --namespace kube-system descheduler/descheduler

$ kubectl get cronjobs -n kube-system
NAME          SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
descheduler   */2 * * * *   False     1        40s             45s

$ kubectl get configmaps -n kube-system descheduler -o yaml
apiVersion: v1
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      LowNodeUtilization:
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            targetThresholds:
              cpu: 50
              memory: 50
              pods: 50
            thresholds:
              cpu: 20
              memory: 20
              pods: 20
      RemoveDuplicates:
        enabled: true
      RemovePodsViolatingInterPodAntiAffinity:
        enabled: true
      RemovePodsViolatingNodeAffinity:
        enabled: true
        params:
          nodeAffinityType:
          - requiredDuringSchedulingIgnoredDuringExecution
      RemovePodsViolatingNodeTaints:
        enabled: true
kind: ConfigMap
(中略)

Descheduler 戦略 🧩

GitHub に載っているドキュメントを読むと非常に多機能で「戦略」だけでも「計10種類」ある.今回は RemovePodsViolatingTopologySpreadConstraintPodLifeTime にフォーカスして紹介する.

  • RemoveDuplicates
  • LowNodeUtilization
  • HighNodeUtilization
  • RemovePodsViolatingInterPodAntiAffinity
  • RemovePodsViolatingNodeAffinity
  • RemovePodsViolatingNodeTaints
  • RemovePodsViolatingTopologySpreadConstraint ✅ 今回試す
  • RemovePodsHavingTooManyRestarts
  • PodLifeTime ✅ 今回試す
  • RemoveFailedPods

検証環境 🧩

今回は検証環境として eksctl を使って構築した Amazon EKS クラスタ (Kubernetes 1.21) を使う.以下のように 3 Availability Zone に 6 ノードを構築してある.IP アドレスは説明のためにわかりやすく書き換えてある(第3オクテットを .10 / .50 / .90 に合わせた).

$ kubectl get nodes \
>   -l 'eks.amazonaws.com/nodegroup=nodegroup' \
>   -o=custom-columns=NAME:.metadata.name,ZONE:metadata.labels."topology\.kubernetes\.io/zone",STATUS:status.conditions[-1].type \
>   --sort-by metadata.labels."topology\.kubernetes\.io/zone"
NAME                                                ZONE              STATUS
ip-192-168-10-100.ap-northeast-1.compute.internal   ap-northeast-1a   Ready
ip-192-168-10-101.ap-northeast-1.compute.internal   ap-northeast-1a   Ready
ip-192-168-50-100.ap-northeast-1.compute.internal   ap-northeast-1c   Ready
ip-192-168-50-101.ap-northeast-1.compute.internal   ap-northeast-1c   Ready
ip-192-168-90-100.ap-northeast-1.compute.internal   ap-northeast-1d   Ready
ip-192-168-90-101.ap-northeast-1.compute.internal   ap-northeast-1d   Ready

1. RemovePodsViolatingTopologySpreadConstraint

さっそく RemovePodsViolatingTopologySpreadConstraint を試す.これは「Pod Topology Spread Constraints」に違反している Pod を探して Eviction(排出)してくれる.「Pod Topology Spread Constraints」に関しては以下の記事にまとめてある.

kakakakakku.hatenablog.com

まず spec.topologySpreadConstraints を設定した Deployment で Pod を「6個」起動する.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-constraints
spec:
  replicas: 6
  selector:
    matchLabels:
      app: topology-spread-constraints
  template:
    metadata:
      labels:
        app: topology-spread-constraints
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
          - containerPort: 80
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: topology-spread-constraints

期待通りに各 Availability Zone に 2 Pod 起動している.

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-7bcb664dc6-b6wt4   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-jlwrx   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-774lt   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-tk7rd   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-92fx8   ip-192-168-90-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-rt46n   ip-192-168-90-101.ap-northeast-1.compute.internal

次に kubectl drain コマンドを使って Availability Zone 1d のノードを意図的にメンテナンスにする.kubectl drain コマンドに関しては以下の記事にまとめてある.

kakakakakku.hatenablog.com

$ kubectl drain ip-192-168-90-100.ap-northeast-1.compute.internal --ignore-daemonsets
$ kubectl drain ip-192-168-90-101.ap-northeast-1.compute.internal --ignore-daemonsets

結果的に 6 Pod は Availability Zone 1a と Availability Zone 1c に偏った.

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-7bcb664dc6-b6wt4   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-jlwrx   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-l77cf   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-nmwpd   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-774lt   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-tk7rd   ip-192-168-50-101.ap-northeast-1.compute.internal

次にメンテナンスを完了したことにして Availability Zone 1d のノードを kubectl uncordon コマンドを使って戻す.しかし,起動中の Pod は移動されずそのままになるため,Availability Zone 1d のノードにスケジューリングされずに「偏ったまま」となる.

$ kubectl uncordon ip-192-168-87-26.ap-northeast-1.compute.internal
$ kubectl uncordon ip-192-168-70-254.ap-northeast-1.compute.internal

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-7bcb664dc6-b6wt4   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-jlwrx   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-l77cf   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-nmwpd   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-774lt   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-tk7rd   ip-192-168-50-101.ap-northeast-1.compute.internal

前置きが長くなった!やっとここで「Descheduler」の本領発揮となる.まずは RemovePodsViolatingTopologySpreadConstraint を有効化するために ConfigMap を以下のように更新する.

apiVersion: v1
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      RemovePodsViolatingTopologySpreadConstraint:
        enabled: true
        params:
          includeSoftConstraints: true
kind: ConfigMap
(中略)

少し待つと(Descheduler のデフォルト設定では 2 min 間隔)Pod が再スケジューリングされた.おおー👏

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-7bcb664dc6-b6wt4   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-jlwrx   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-nmwpd   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-774lt   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-l74k2   ip-192-168-90-100.ap-northeast-1.compute.internal
topology-spread-constraints-7bcb664dc6-d7tfb   ip-192-168-90-101.ap-northeast-1.compute.internal

2. PodLifeTime

今度は PodLifeTime を試す.これは設定した「Pod の起動時間上限」に違反している Pod を探して Eviction(排出)してくれる.継続的に Pod を作り直してリフレッシュしたいときに使える.PodLifeTime を有効化するために ConfigMap を以下のように更新する.

  • podLifeTime.maxPodLifeTimeSeconds
    • 「300秒(5分)」まで
  • matchLabels
    • 対象をラベルで指定する
$ kubectl get configmaps -n kube-system descheduler -o yaml
apiVersion: v1
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      PodLifeTime:
        enabled: true
        params:
          podLifeTime:
            maxPodLifeTimeSeconds: 300
          matchLabels:
            app: pod-life-time
kind: ConfigMap
(中略)

さっそく Deployment で Pod を「3個」起動する.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-life-time
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pod-life-time
  template:
    metadata:
      labels:
        app: pod-life-time
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
          - containerPort: 80

少し待つと Pod が作り直された.おおー👏

$ kubectl get pods -l app=pod-life-time
pod-life-time-5bf5bc8dcf-bqbwc                1/1     Running   0          24s
pod-life-time-5bf5bc8dcf-jc45x                1/1     Running   0          25s
pod-life-time-5bf5bc8dcf-n7xb5                1/1     Running   0          24s

$ kubectl get pods -l app=pod-life-time
pod-life-time-5bf5bc8dcf-bqbwc                1/1     Running   0          5m35s
pod-life-time-5bf5bc8dcf-jc45x                1/1     Running   0          5m36s
pod-life-time-5bf5bc8dcf-n7xb5                1/1     Running   0          5m35s

$ kubectl get pods -l app=pod-life-time
pod-life-time-5bf5bc8dcf-8pbbc                1/1     Running   0          18s
pod-life-time-5bf5bc8dcf-bbpnv                1/1     Running   0          17s
pod-life-time-5bf5bc8dcf-jzrlk                1/1     Running   0          18s

アンインストール 🧩

検証が終わったら Helm で削除しておく.

$ helm uninstall descheduler --namespace kube-system
release "descheduler" uninstalled

まとめ 🧩

今回は「Descheduler for Kubernetes」を試して,起動中の Pod を他のノードに移動(再スケジューリング)できることを確認した.記事では RemovePodsViolatingTopologySpreadConstraintPodLifeTime にフォーカスしたけど,他にも LowNodeUtilization(使用率が低いノードを有効活用する)と RemovePodsViolatingNodeTaints(taint に違反する Pod を Eviction する)も試して,理解を深められた.Kubernetes クラスタの耐障害性などを考慮すると「Descheduler for Kubernetes」を導入すると良さそう!

github.com

Pod Topology Spread Constraints : Pod を Multi AZ 配置する

Kubernetes で「Pod Topology Spread Constraints」を使うと Pod をスケジューリングするときの制約条件を柔軟に設定できる.今回は Zone Spread (Multi AZ) を試す!詳しくは以下のドキュメントに載っている!

kubernetes.io

spec.topologySpreadConstraints

Pod Topology Spread Constraints を使うために YAML に spec.topologySpreadConstraints を追加する.構文と各項目の概要を以下にまとめる.単純に Node Label を指定するだけではなく maxSkewwhenUnsatisfiable も設定できて柔軟さを感じる💡

  • topologyKey : スケジュールする制約条件に使う Node Label
  • maxSkew : どれぐらい Pod 数の差を許容できるか(maxSkew = 1 なら均等 / maxSkew >= 2 ならある程度は均等)
  • whenUnsatisfiable : 制約を満たせない場合に Pod をどうするか(デフォルトは DoNotSchedule でスケジュールしない)
  • labelSelector : スケジュールの対象とする Pod Label
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  topologySpreadConstraints:
    - maxSkew: <integer>
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>

検証環境

今回は検証環境として eksctl を使って構築した Amazon EKS クラスタ (Kubernetes 1.21) を使う.以下のように 3 Availability Zone に 6 ノードを構築してある.IP アドレスは説明のためにわかりやすく書き換えてある(第3オクテットを .10 / .50 / .90 に合わせた).

$ kubectl get nodes \
>   -l 'eks.amazonaws.com/nodegroup=managed' \
>   -o=custom-columns=NAME:.metadata.name,ZONE:metadata.labels."topology\.kubernetes\.io/zone" \
>   --sort-by metadata.labels."topology\.kubernetes\.io/zone"
NAME                                                ZONE
ip-192-168-10-100.ap-northeast-1.compute.internal   ap-northeast-1a
ip-192-168-10-101.ap-northeast-1.compute.internal   ap-northeast-1a
ip-192-168-50-100.ap-northeast-1.compute.internal   ap-northeast-1c
ip-192-168-50-101.ap-northeast-1.compute.internal   ap-northeast-1c
ip-192-168-90-100.ap-northeast-1.compute.internal   ap-northeast-1d
ip-192-168-90-101.ap-northeast-1.compute.internal   ap-northeast-1d

f:id:kakku22:20220302213001p:plain

Pod Topology Spread Constraints を試す

さっそく Pod Topology Spread Constraints を試す.Deployment で Pod を「12個」起動する以下のマニフェストを作った.ポイントは spec.topologySpreadConstraints.topologyKeytopology.kubernetes.io/zone を指定しているところ.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-constraints
spec:
  replicas: 12
  selector:
    matchLabels:
      app: topology-spread-constraints
  template:
    metadata:
      labels:
        app: topology-spread-constraints
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
          - containerPort: 80
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: topology-spread-constraints

Pod を確認するとうまく Zone Spread になった.各 Availability Zone に 4 Pod 起動している.なお kubectl get pods の結果に Zone を含めることができず,判断しにくいため図を載せておく!

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-858b874fb9-vptnt   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-rt948   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-xsbh8   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-cn992   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-qtcm7   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-xlxx4   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-75pl8   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-76t6h   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-fnz7d   ip-192-168-90-100.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-cldf2   ip-192-168-90-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-d2kgt   ip-192-168-90-101.ap-northeast-1.compute.internal
topology-spread-constraints-858b874fb9-dqk2w   ip-192-168-90-101.ap-northeast-1.compute.internal

f:id:kakku22:20220302221224p:plain

Pod Topology Spread Constraints を複数組み合わせる

今の例では 各 Availability Zone に 4 Pod 起動しているけど,ノード単位だと 1 Pod と 3 Pod など差がある.次はノードも条件に追加する.spec.topologySpreadConstraints.topologyKeykubernetes.io/hostname を追加する(クラスターによっては違う Label の場合もある).なお,ドキュメントに載ってる通り,複数の制約条件を組み合わせるとコンフリクトにより Pending になる可能性がある.今回実際に発生したため maxSkew: 2 にした.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-constraints
spec:
  replicas: 12
  selector:
    matchLabels:
      app: topology-spread-constraints
  template:
    metadata:
      labels:
        app: topology-spread-constraints
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
          - containerPort: 80
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: topology-spread-constraints
        - maxSkew: 2
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: topology-spread-constraints

Pod を確認するとうまく Zone / Node Spread になった.各 Availability Zone に 4 Pod 起動していて,各ノードに 2 Pod 起動している.

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-546fcfcb77-2zs8r   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-6zrtj   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-6nflm   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-cjrmn   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-7mxf6   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-vglz2   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-qdmmb   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-wxwn8   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-ctgth   ip-192-168-90-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-j59f6   ip-192-168-90-100.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-dzlr8   ip-192-168-90-101.ap-northeast-1.compute.internal
topology-spread-constraints-546fcfcb77-8qg5x   ip-192-168-90-101.ap-northeast-1.compute.internal

f:id:kakku22:20220302222819p:plain

Pod Topology Spread Constraints と Affinity を組み合わせる

Pod Topology Spread ConstraintsAffinity を組み合わせることもできる.例えば Zone Spread は実現しつつ,Availability Zone 1d にはスケジュールしたくない場合などが考えられる.以下のように spec.affinity.nodeAffinityNotIn を追加した.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-constraints
spec:
  replicas: 12
  selector:
    matchLabels:
      app: topology-spread-constraints
  template:
    metadata:
      labels:
        app: topology-spread-constraints
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
          - containerPort: 80
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: topology-spread-constraints
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: NotIn
                values:
                - ap-northeast-1d

Pod を確認するとうまく Availability Zone 1d 以外で Zone Spread になった.各 Availability Zone に 6 Pod 起動している.

$ kubectl get pods \
>   -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName \
>   -l app=topology-spread-constraints \
>   --sort-by .spec.nodeName
NAME                                           NODE
topology-spread-constraints-6dcfb69b4f-g24gm   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-gmrbg   ip-192-168-10-100.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-qtb8s   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-85l9t   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-z9t65   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-kxdh5   ip-192-168-10-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-tqx5m   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-hk8t9   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-zsgt9   ip-192-168-50-100.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-5ldxm   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-tqc4h   ip-192-168-50-101.ap-northeast-1.compute.internal
topology-spread-constraints-6dcfb69b4f-vkc6v   ip-192-168-50-101.ap-northeast-1.compute.internal

f:id:kakku22:20220302224603p:plain

まとめ

Kubernetes で「Pod Topology Spread Constraints」を使って Pod の Zone Spread (Multi AZ) を試した.今までも podAntiAffinity を使って実現できたけどより柔軟に設定できる.また EKS Best Practices GuidesReliability (Applications) にも Schedule replicas across nodes として紹介されているので合わせて読むと良さそう!

aws.github.io

関連記事

kubernetes.io

Pandas で NDJSON (.jsonl) を読み込む

Pandas で NDJSON (Newline Delimited JSON) を読み込む場合 read_json() 関数に lines=True パラメータを設定すれば OK!

pandas.pydata.org

NDJSON サンプル dataset.jsonl

{ "id": 1, "name": "Alice" }
{ "id": 2, "name": "Bob" }
{ "id": 3, "name": "Kakakakakku", "blog":  "https://kakakakakku.hatenablog.com/" }

サンプルコード ndjson.py

import pandas as pd

df = pd.read_json('./dataset.jsonl', lines=True)
print(df)

実行すると期待通りに DataFrame を表示できた!

$ python ndjson.py
   id         name                                 blog
0   1        Alice                                  NaN
1   2          Bob                                  NaN
2   3  Kakakakakku  https://kakakakakku.hatenablog.com/