Kubernetes ラベルセレクターを client-go から使う

この記事は 第二のドワンゴ Advent Calendar 2019 の20日目の記事です。

qiita.com

投稿が遅くなりました。

「Kubernetesのカスタムコントローラーを作ってみた」的な記事を書きたかったのですが、作ってる際にハマったclient-goでのラベルセレクターの扱いについて書いていこうかと思います。

ラベルセレクターとは

kubernetes.io

ラベルはユニーク性を提供しません。通常、多くのオブジェクトが同じラベルを保持することを想定します。 ラベルセレクター を介して、クライアントとユーザーはオブジェクトのセットを指定できます。ラベルセレクターはKubernetesにおいてコアなグルーピング機能となります。

Podをスケジュールする際にNodeを決定する nodeSlector もラベルセレクターの一種で、ラベルを持つノードからこの nodeSelector を利用してPodをスケジュールするノードをグルーピングします。

kubectl get コマンドにおいては --selector オプションにて入力可能です。

たとえば全てのネームスペースから、app = myapp というラベルのついて Pod をグルーピングする場合は、以下のようなコマンドを実行します。

kubectl get pods --all-namespaces --selector='app=myapp'

client-go からラベルセレクターを利用する

Podについてラベルセレクターを利用してフィルタリングを行います。

ラベルセレクターなどを利用せずに全てのネームスペースからPodを取得する場合、以下のようなコードになるかと思います。

package main
import (
"fmt"
flag "github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
var (
apiServerHost, kubeconfigFilePath string
)
flag.StringVar(&apiServerHost, "master", "", "Address of the Kubernetes API server.")
flag.StringVar(&kubeconfigFilePath, "kubeconfig", "", "Path to a kubeconfig file containing authorization and API server information.")
flag.Parse()
kubeconfig, err := clientcmd.BuildConfigFromFlags(apiServerHost, kubeconfigFilePath)
if err != nil {
panic(err.Error())
}
clientset, err := kubernetes.NewForConfig(kubeconfig)
if err != nil {
panic(err.Error())
}
versionInfo, err := clientset.ServerVersion()
if err != nil {
panic(err.Error())
}
fmt.Printf("Server version: %s\n", versionInfo.String())
fmt.Println("All pods")
podListAll, err := clientset.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
if len(podListAll.Items) == 0 {
fmt.Println("Not Found")
}
for _, pod := range podListAll.Items {
fmt.Printf("%s/%s\n", pod.GetNamespace(), pod.GetName())
}
}
view raw list-all-pods.go hosted with ❤ by GitHub

Podのリストを取得する際の ListOptions には、それらしい LabelSelector という文字列を与えることができます。

一方で、ラベルセレクターを表すのLabelSelector構造体には、ListOptionsLabelSelector が期待する文字列を生成する関数は定義されていません*1

では、どうするかというと、同パッケージに定義されている FormatLabelSelector(labelSelector *LabelSelector) string を利用します。

labelSelector := &metav1.LabelSelector{
    MatchLabels: map[string]string{
            "app": "myapp",
    },
}
clientset.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
    LabelSelector: metav1.FormatLabelSelector(labelSelector),
})

こうすることで、app = myapp というラベルを持つPodを取得することができます。

また、LabelSelector 構造体を利用せずに、ListOptions.LabelSelector"app=myapp" という文字列を与えることでも同じ結果を得られます。

clientset.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
    LabelSelector: "app=myapp",
})

まとめ

client-go からKubernetesラベルセレクターを利用する方法について説明しました。

以下のリポジトリに本記事で使用したコードがあります。

github.com

ラベルセレクターは強力な仕組みで kubectl や client-go からでも簡単に利用できます。 カスタムコントローラーを開発する際にも役立てていきたいですね!

*1:ここでめちゃくちゃハマった