Kubernetes カスタムリソースをGoで操作する
本記事は、さくらインターネット Advent Calendar2024の11日目の記事です。
Kubernetesリソースはclient-goを使うことで、Kubernetes APIにリクエストしプログラム内で操作することができますが、CRDにより独自に定義されているKubernetes カスタムリソースは少し違う方法で扱います。
Kubernetes カスタムリソースをGoで操作する方法を記載します。
ライブラリ側で公開されているClientSetを用いる
Kubernetesカスタムリソースはライブラリ側でClientSetが提供されている場合があります。
例としては、Traefikのカスタムリソースは以下で定義されているClientSetを用いることで操作できます。(こちらはcode-generatorで生成されています。これを用いることでCRDに沿ったClientSetを作成することができます。)
以下はClientSetを用いてTraefikのIngressRouteを作成する例です。
package main
import (
"context"
"log"
traefikClient "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned"
traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
kubeconfig := "~/.kube/config"
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
log.Fatal(err)
}
traefikClient, err := traefikClient.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
ingressRoute := &traefikv1alpha1.IngressRoute{
ObjectMeta: meta.ObjectMeta{
Name: "test-ingressroute",
Namespace: "test-ns",
},
Spec: traefikv1alpha1.IngressRouteSpec{
EntryPoints: []string{"web"},
Routes: []traefikv1alpha1.Route{
{
Match: "Host(`example.com`)",
Kind: "Rule",
Priority: 10,
Services: []traefikv1alpha1.Service{
{
LoadBalancerSpec: traefikv1alpha1.LoadBalancerSpec{
Name: "loadbalancer",
Port: intstr.FromInt(80),
Namespace: "test-ns",
},
},
},
},
},
},
}
_, err = traefikClient.TraefikV1alpha1().IngressRoutes("test-ns").Create(context.Background(), ingressRoute, meta.CreateOptions{})
if err != nil {
log.Fatal(err)
}
}
実行すると、指定したnamespaceにingressrouteが作成されていることが確認できます。
> go run main.go
> kubectl get ingressroute -n test-ns
NAME AGE
test-ingressroute 25s
このようにライブラリ側で提供されている型定義を用いて、Kubernetes カスタムリソースを操作することができます。
Dynamic Clientを用いる
client-goではDynamic ClientというClientも提供されています。
https://github.com/kubernetes/client-go/blob/master/dynamic/simple.go
こちらは通常のClientとは異なり、Unstructuredという型を用いることで、Kubernetes標準リソースの型に縛られずにKubernetes APIへリクエストすることができます。
以下はDynamic Clientを用いて先ほどのIngressRouteを作成する例です。
package main
import (
"context"
"log"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
kubeconfig := "~/.kube/config"
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
log.Fatal(err)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
log.Fatal(err)
}
ingressRouteGVR := schema.GroupVersionResource{
Group: "traefik.io",
Version: "v1alpha1",
Resource: "ingressroutes",
}
ingressRoute := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "traefik.io/v1alpha1",
"kind": "IngressRoute",
"metadata": map[string]interface{}{
"name": "test-ingressroute",
"namespace": "test-ns",
},
"spec": map[string]interface{}{
"entryPoints": []interface{}{"web"},
"routes": []interface{}{
map[string]interface{}{
"match": "Host(`example.com`)",
"kind": "Rule",
"priority": 10,
"services": []interface{}{
map[string]interface{}{
"name": "loadbalancer",
"port": 80,
"namespace": "masumura-test",
},
},
},
},
},
},
}
_, err = dynamicClient.Resource(ingressRouteGVR).Namespace("test-ns").Create(context.Background(), ingressRoute, meta.CreateOptions{})
if err != nil {
log.Fatal(err)
}
}
Dynamic Clientを用いる場合は、標準リソースをclient-goで操作する場合とは異なり、
GroupVersionResourceを定義し、Unstructured typeで定義したオブジェクトを用いてAPIへリクエストします。Unstructured typeでは、map[string]interface{}
により、あらゆる型のオブジェクトを定義できるようになっています。
まとめ
GoでKubernetes カスタムリソースを操作する方法を紹介しました。
基本的には、存在する場合は型定義がされている専用のClientSetを用いるのがよいかと思いますが、Dynamic Clientを扱うことでも操作することができます。
Discussion