目的
Kubernetes Operatorは初心者にとってはとっつきにくいと思うので、自分の備忘録を残しておく。今回は、operator-sdkのTutorialを利用してOperatorへの理解を深める
内容
Go Operator Tutorial (operator-sdk)のmemcached-operatorを実装する. (memcached_controller.go)
memcached-operatorの内容:
-
Memcached
というCustom Resource-
spec.size
でMemcachedのNodeの数を指定できる -
status.nodes
でNode情報をもつ
-
- ControllerでReconcileLoopを実装
- Memcachedインスタンスを取得
- Deploymentがなければ作成
- MemcachedのsizeでとDeploymentのreplicas数を同じにする
- status.nodesにPod名を格納する
Prerequisite
- Git
- Go version 1.15
- Docker version 17.03+.
- kubectl (互換性のあるVersionのKubernetes clusterへ接続が必要)
- (Docker registry (ローカルのみで開発し、ImageをPushしないのであれば
docker-push
を適宜外し、ローカルのtag名にする))
実行環境
- operator-sdk:
v.1.5.0
- go:
1.16.3
- docker:
20.10.5
- kubernetes (Docker for Mac):
v1.19.7
ステップ
1. Projectの作成 (operator-sdk )
ディレクトリの作成
mkdir -p $HOME/projects/memcached-operator
cd $HOME/projects/memcached-operator
initializeする
operator-sdk init --domain example.com --repo github.com/example/memcached-operator
2. APIの作成 (operator-sdk)
KubernetesのAPI ResourceとControllerを作成
operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource --controller
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-api-created"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodがRunningしていることを確認
kubectl get pod -n memcached-operator-system NAME READY STATUS RESTARTS AGE memcached-operator-controller-manager-865ffbbbcc-pdkxg 2/2 Running 0 76s
-
Custom ResourceのMemcachedをデプロイ
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
Controllerの実装をしていないので、Deploymentは作成されていない
kubectl get deploy No resources found in default namespace.
-
Operator側のログを確認 (特に新しいMemcachedを作ったEventに対応するログはない)
kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f 2021-04-12T23:04:01.886Z INFO controller-runtime.metrics metrics server is starting to listen {"addr": "127.0.0.1:8080"} 2021-04-12T23:04:01.887Z INFO setup starting manager
I0412 23:04:01.982492 1 leaderelection.go:243] attempting to acquire leader lease memcached-operator-system/86f835c3.example.com...
2021-04-12T23:04:02.078Z INFO controller-runtime.manager starting metrics server {"path": "/metrics"}
2021-04-12T23:04:02.282Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"ConfigMap","namespace":"memcached-operator-system","name":"86f835c3.example.com","uid":"dd829320-b9fb-4abc-9c18-cde6658daa96","apiVersion":"v1","resourceVersion":"2057764"}, "reason": "LeaderElection", "message": "memcached-operator-controller-manager-865ffbbbcc-pdkxg_4ecfda57-7d6a-42cc-bd5c-0cfa018a35f8 became leader"}
2021-04-12T23:04:02.282Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"Lease","namespace":"memcached-operator-system","name":"86f835c3.example.com","uid":"2324fd70-20db-49a8-a975-98d71a158bd9","apiVersion":"coordination.k8s.io/v1","resourceVersion":"2057766"}, "reason": "LeaderElection", "message": "memcached-operator-controller-manager-865ffbbbcc-pdkxg_4ecfda57-7d6a-42cc-bd5c-0cfa018a35f8 became leader"}
I0412 23:04:02.281814 1 leaderelection.go:253] successfully acquired lease memcached-operator-system/86f835c3.example.com
2021-04-12T23:04:02.379Z INFO controller-runtime.manager.controller.memcached Starting EventSource {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "source": "kind source: /, Kind="}
2021-04-12T23:04:02.479Z INFO controller-runtime.manager.controller.memcached Starting Controller {"reconciler group": "cache.example.com", "reconciler kind": "Memcached"}
2021-04-12T23:04:02.479Z INFO controller-runtime.manager.controller.memcached Starting workers {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "worker count": 1}
```
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | No |
Controller | Memcachedを取得 | No |
Controller | MemcachedのDeploymentが存在していない場合に作成 | No |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
No |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
No |
3. APIを定義 (api/v1alpha1/memcached_types.go)
自動生成された api/v1alpha1/memcached_types.go
を以下の様に変更する。
定義内容:
- MemcachedSpec: MemcachedがデプロイされたときにいくつのPodsで動かすかを指定するための
size
- MemcachedStatus: Podの名前をMemcachedのNodesに文字列のリストとしてStatusにいれる
- Memcached: 上記のSpecとStatusを使って構造体を定義
// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
//+kubebuilder:validation:Minimum=0
// Size is the size of the memcached deployment
Size int32 `json:"size"`
}
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
// Nodes are the names of the memcached pods
Nodes []string `json:"nodes"`
}
// Memcached is the Schema for the memcacheds API
//+kubebuilder:subresource:status
type Memcached struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MemcachedSpec `json:"spec,omitempty"`
Status MemcachedStatus `json:"status,omitempty"`
}
変更したあとには、これらを元に以下のコマンドで自動生成されたコードを更新する
make generate
API定義後にCRDのManifestファイルを以下のコマンドで生成
make manifests
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-api-defined"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodが新しいimageでRunningしていることを確認
kubectl get pod -n memcached-operator-system NAME READY STATUS RESTARTS AGE memcached-operator-controller-manager-bcdb99b56-n4l5c 2/2 Running 0 24s
イメージの確認:
v0.0.1-api-defined
となってることkubectl get pod -n memcached-operator-system -o yaml | grep ' image:' image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 image: nakamasato/memcached-operator:v0.0.1-api-defined image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 image: nakamasato/memcached-operator:v0.0.1-api-defined
-
Custom ResourceのMemcachedをデプロイ (一つ前の確認でApplyした場合は同じなので必要ない)
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
Controllerの実装をしていないので、Deploymentは作成されていない
kubectl get deploy No resources found in default namespace.
-
Operator側のログを確認 (特に新しいMemcachedを作ったEventに対応するログはない)
kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f 2021-04-12T23:21:10.637Z INFO controller-runtime.metrics metrics server is starting to listen {"addr": "127.0.0.1:8080"} 2021-04-12T23:21:10.639Z INFO setup starting manager I0412 23:21:10.640597 1 leaderelection.go:243] attempting to acquire leader lease memcached-operator-system/86f835c3.example.com... 2021-04-12T23:21:10.640Z INFO controller-runtime.manager starting metrics server {"path": "/metrics"} I0412 23:21:38.610611 1 leaderelection.go:253] successfully acquired lease memcached-operator-system/86f835c3.example.com 2021-04-12T23:21:38.610Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"ConfigMap","namespace":"memcached-operator-system","name":"86f835c3.example.com","uid":"dd829320-b9fb-4abc-9c18-cde6658daa96","apiVersion":"v1","resourceVersion":"2060588"}, "reason": "LeaderElection", "message": "memcached-operator-controller-manager-bcdb99b56-n4l5c_3302a23e-cab6-4527-a187-f19eeb9fedbb became leader"} 2021-04-12T23:21:38.610Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"Lease","namespace":"memcached-operator-system","name":"86f835c3.example.com","uid":"2324fd70-20db-49a8-a975-98d71a158bd9","apiVersion":"coordination.k8s.io/v1","resourceVersion":"2060589"}, "reason": "LeaderElection", "message": "memcached-operator-controller-manager-bcdb99b56-n4l5c_3302a23e-cab6-4527-a187-f19eeb9fedbb became leader"} 2021-04-12T23:21:38.611Z INFO controller-runtime.manager.controller.memcached Starting EventSource {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "source": "kind source: /, Kind="} 2021-04-12T23:21:39.001Z INFO controller-runtime.manager.controller.memcached Starting Controller {"reconciler group": "cache.example.com", "reconciler kind": "Memcached"} 2021-04-12T23:21:39.001Z INFO controller-runtime.manager.controller.memcached Starting workers {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "worker count": 1}
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | Yes |
Controller | Memcachedを取得 | No |
Controller | MemcachedのDeploymentが存在していない場合に作成 | No |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
No |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
No |
4.1. コントローラーの実装 (MemcachedのInstanceを取得)
Reconcile
に Memcached を取得するロジックを追加 (ログで自分で書いたコードを判別しやすくするため 1. Fetch the Memcached instance.
をすべてのログメッセージに書いている)
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("memcached", req.NamespacedName)
// 1. Fetch the Memcached instance
memcached := &cachev1alpha1.Memcached{}
err := r.Get(ctx, req.NamespacedName, memcached)
if err != nil {
if errors.IsNotFound(err) {
log.Info("1. Fetch the Memcached instance. Memcached resource not found. Ignoring since object must be deleted")
return ctrl.Result{}, nil
}
// Error reading the object - requeue the request.
log.Error(err, "1. Fetch the Memcached instance. Failed to get Mmecached")
return ctrl.Result{}, err
}
log.Info("1. Fetch the Memcached instance. Memchached resource found", "memcached.Name", memcached.Name, "memcached.Namespace", memcached.Namespace)
return ctrl.Result{}, nil
}
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-controller-fetch-memcached-instance"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodが新しいimageでRunningしていることを確認
kubectl get pod -n memcached-operator-system
イメージの確認: v0.0.1-controller-fetch-memcached-instance
となってること
```
kubectl get pod -n memcached-operator-system -o yaml | grep ' image:'
```
-
Custom ResourceのMemcachedをデプロイ (一つ前の確認でApplyした場合は同じなので必要ない)
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
Controllerの実装をしていないので、Deploymentは作成されていない
kubectl get deploy No resources found in default namespace.
-
Operator側のログを確認 (
1. Fetch the Memcached instance.
がログにかかれている!)kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f ... 2021-04-12T23:35:02.751Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"}
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | Yes |
Controller | Memcachedを取得 | Yes |
Controller | MemcachedのDeploymentが存在していない場合に作成 | No |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
No |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
No |
4.2. コントローラーの実装 (Deploymentをなければ作成)
- 必要なlibraryをImport
- Controllerに必要なRBACをMarkerにより追加
- NamespacedNameからDeploymentが存在しない場合 (
IsNotFound
)はdeployemntForMemcached
関数でDeploymentを作成 -
deployemntForMemcached
関数の定義 (DeploymentをMemcached用に作成する) -
labelsForMemcached
関数の定義 (新規Deployment作成時につけるlabelを返す)
...
import (
appsv1 "k8s.io/api/apps/v1" // 追加
corev1 "k8s.io/api/core/v1" // 追加
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // 追加
"k8s.io/apimachinery/pkg/types" // 追加
"context"
...
)
...
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // 追加
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; // 追加
...
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("memcached", req.NamespacedName)
// 1. Fetch the Memcached instance
// ...前節で実装済み
// 2. Check if the deployment already exists, if not create a new one
found := &appsv1.Deployment{}
err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
// Define a new deployment
dep := r.deploymentForMemcached(memcached)
log.Info("2. Check if the deployment already exists, if not create a new one. Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
err = r.Create(ctx, dep)
if err != nil {
log.Error(err, "2. Check if the deployment already exists, if not create a new one. Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
return ctrl.Result{}, err
}
// Deployment created successfully - return and requeue
return ctrl.Result{Requeue: true}, nil
} else if err != nil {
log.Error(err, "2. Check if the deployment already exists, if not create a new one. Failed to get Deployment")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// deploymentForMemcached returns a memcached Deployment object
func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment {
ls := labelsForMemcached(m.Name)
replicas := m.Spec.Size
dep := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: m.Name,
Namespace: m.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Image: "memcached:1.4.36-alpine",
Name: "memcached",
Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
Ports: []corev1.ContainerPort{{
ContainerPort: 11211,
Name: "memcached",
}},
}},
},
},
},
}
// Set Memcached instance as the owner and controller
ctrl.SetControllerReference(m, dep, r.Scheme)
return dep
}
// labelsForMemcached returns the labels for selecting the resources
// belonging to the given memcached CR name.
func labelsForMemcached(name string) map[string]string {
return map[string]string{"app": "memcached", "memcached_cr": name}
}
...
SetupWithManager
で、どのリソースをWatchするかを設定している。今回作成した Memcached
以外にも管理したいDeploymentをOwns
で管理できるようにする。
// SetupWithManager sets up the controller with the Manager.
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cachev1alpha1.Memcached{}).
Owns(&appsv1.Deployment{}).
Complete(r)
}
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-controller-create-memcached-deployment"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
以下のエラーがでたら 指示通りに
go get k8s.io/api/core/v1@v0.19.2
を実行するgo: github.com/example/memcached-operator/controllers: package k8s.io/api/core/v1 imported from implicitly required module; to add missing requirements, run: go get k8s.io/api/core/v1@v0.19.2
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodが新しいimageでRunningしていることを確認
kubectl get pod -n memcached-operator-system
イメージの確認: v0.0.1-controller-create-memcached-deployment
となってること
```
kubectl get pod -n memcached-operator-system -o yaml | grep ' image:'
```
-
Custom ResourceのMemcachedをデプロイ
Fieldが変わっているので一度削除してからApplyし直す
kubectl delete -f config/samples/cache_v1alpha1_memcached.yaml
以下のようにSpecを
size: 3
に変更config/samples/cache_v1alpha1_memcached.yamlapiVersion: cache.example.com/v1alpha1 kind: Memcached metadata: name: memcached-sample spec: size: 3
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
ControllerでDeploymentを作成する部分を実装したのでDeploymentができている
kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE memcached-sample 3/3 3 3 4s
しかしspecのサイズを替えても、Deploymentには反映しない。 (次の節で実装) (試したい場合は、
kubectl patch memcached memcached-sample -p '{"spec":{"size": 5}}' --type=merge
をアプライしてから、kubectl get deploy
で数が変わらないことを確認) -
Operator側のログを確認 (
2. Check if the deployment already exists, if not create a new one. Creating a new Deployment
がログにかかれている!)kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f ... 2021-04-13T00:16:21.687Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-04-13T00:16:21.982Z INFO controllers.Memcached 2. Check if the deployment already exists, if not create a new one. Creating a new Deployment {"memcached": "default/memcached-sample", "Deployment.Namespace": "default", "Deployment.Name": "memcached-sample"} 2021-04-13T00:16:22.006Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"}
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | Yes |
Controller | Memcachedを取得 | Yes |
Controller | MemcachedのDeploymentが存在していない場合に作成 | Yes |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
No |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
No |
4.3. コントローラーの実装 (Deployment sizeとMemcached sizeを同じ値に保つ)
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 1. Fetch the Memcached instance
// 実装済み
// 2. Check if the deployment already exists, if not create a new one
// 実装済み
// 3. Ensure the deployment size is the same as the spec
size := memcached.Spec.Size
if *found.Spec.Replicas != size {
found.Spec.Replicas = &size
err = r.Update(ctx, found)
if err != nil {
log.Error(err, "3. Ensure the deployment size is the same as the spec. Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
return ctrl.Result{}, err
}
// Spec updated - return and requeue
log.Info("3. Ensure the deployment size is the same as the spec. Update deployment size", "Deployment.Spec.Replicas", size)
return ctrl.Result{Requeue: true}, nil
}
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-controller-sync-deployment-size"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodが新しいimageでRunningしていることを確認
kubectl get pod -n memcached-operator-system
イメージの確認: v0.0.1-controller-sync-deployment-size
となってること
```
kubectl get pod -n memcached-operator-system -o yaml | grep ' image:'
```
-
Custom ResourceのMemcachedをデプロイ
以下のようにSpecを
size: 3
->size: 2
に変更config/samples/cache_v1alpha1_memcached.yamlapiVersion: cache.example.com/v1alpha1 kind: Memcached metadata: name: memcached-sample spec: size: 2
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
ControllerでDeploymentを作成する部分を実装したReplicaが2に変更されている!
kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE memcached-sample 2/2 2 2 4s
-
Operator側のログを確認 (
3. Ensure the deployment size is the same as the spec.
がログにかかれている!)kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f ... 2021-04-13T00:42:26.366Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-04-13T00:42:26.377Z INFO controllers.Memcached 3. Ensure the deployment size is the same as the spec. Update deployment size {"memcached": "default/memcached-sample", "Deployment.Spec.Replicas": 2} 2021-04-13T00:42:26.383Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"}
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | Yes |
Controller | Memcachedを取得 | Yes |
Controller | MemcachedのDeploymentが存在していない場合に作成 | Yes |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
Yes |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
No |
4.4. コントローラーの実装 (Memcached statusを Pod名で更新)
-
reflect
をimportに追加 - ロジック追加: podListを取得→
getPodNames
からPod名のarrayを取得→memcached.Status.Nodes
に反映させる -
getPodNames
関数を定義: arrayにPod名を入れて返す
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"reflect" //追加
"context"
cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
...
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("memcached", req.NamespacedName)
// 1. Fetch the Memcached instance
// 実装済み
// 2. Check if the deployment already exists, if not create a new one
// 実装済み
// 3. Ensure the deployment size is the same as the spec
// 実装済み
// 4. Update the Memcached status with the pod names
// List the pods for this memcached's deployment
podList := &corev1.PodList{}
listOpts := []client.ListOption{
client.InNamespace(memcached.Namespace),
client.MatchingLabels(labelsForMemcached(memcached.Name)),
}
if err = r.List(ctx, podList, listOpts...); err != nil {
log.Error(err, "4. Update the Memcached status with the pod names. Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name)
return ctrl.Result{}, err
}
podNames := getPodNames(podList.Items)
log.Info("4. Update the Memcached status with the pod names. Pod list", "podNames", podNames)
// Update status.Nodes if needed
if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
memcached.Status.Nodes = podNames
err := r.Status().Update(ctx, memcached)
if err != nil {
log.Error(err, "4. Update the Memcached status with the pod names. Failed to update Memcached status")
return ctrl.Result{}, err
}
}
log.Info("4. Update the Memcached status with the pod names. Update memcached.Status", "memcached.Status.Nodes", memcached.Status.Nodes)
return ctrl.Result{}, nil
}
...
// getPodNames returns the pod names of the array of pods passed in
func getPodNames(pods []corev1.Pod) []string {
var podNames []string
for _, pod := range pods {
podNames = append(podNames, pod.Name)
}
return podNames
}
確認 (Optional)
この時点でのOperatorを確認したい場合は以下のようにDeployができる (ステップごとにOperatorがどの機能ができてどの機能ができていないのかを確認するためなので、スキップも可)
-
OPERATOR_IMG
をセットdocker_hub_name=nakamasato # 自分のDocker Hubのユーザ名を指定 (memcached-operatorというRepositoryは事前に作成しておく) export OPERATOR_IMG="$docker_hub_name/memcached-operator:v0.0.1-controller-update-memcached-status"
-
Dockerイメージを焼いてPushする
make docker-build docker-push IMG=$OPERATOR_IMG
-
OperatorをDeploy
make deploy IMG=$OPERATOR_IMG
-
OperatorのPodが新しいimageでRunningしていることを確認
kubectl get pod -n memcached-operator-system
イメージの確認: v0.0.1-controller-update-memcached-status
となってること
```
kubectl get pod -n memcached-operator-system -o yaml | grep ' image:'
```
-
Custom ResourceのMemcachedをデプロイ (前節でDeployしてあれば変更なし)
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
-
Memcachedのstatus.nodesがpod名になっていることを確認
kubectl get memcached memcached-sample -o jsonpath='{.status}' {"nodes":["memcached-sample-6c765df685-5b9dm","memcached-sample-6c765df685-z5zgn"]}
kubectl get pod NAME READY STATUS RESTARTS AGE memcached-sample-6c765df685-5b9dm 1/1 Running 0 37m memcached-sample-6c765df685-z5zgn 1/1 Running 0 37m
-
Operator側のログを確認 (``がログにかかれている!)
kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f ... 2021-04-13T00:57:17.594Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-04-13T00:57:17.660Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Pod list {"memcached": "default/memcached-sample", "podNames": ["memcached-sample-6c765df685-5b9dm", "memcached-sample-6c765df685-z5zgn"]} 2021-04-13T00:57:17.660Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Update memcached.Status {"memcached": "default/memcached-sample", "memcached.Status.Nodes": ["memcached-sample-6c765df685-5b9dm", "memcached-sample-6c765df685-z5zgn"]}
ここまででできているもの:
タイプ | 機能 | 実装 |
---|---|---|
APIリソース | MemcachedのAPI定義 (SpecのsizeとstatusのNodes) | Yes |
Controller | Memcachedを取得 | Yes |
Controller | MemcachedのDeploymentが存在していない場合に作成 | Yes |
Controller | DeploymentのsizeをCustome Resource Memcached のspecで指定された値と同じにする |
Yes |
Controller | Custom Resource Memcached の statusを memcached のpod名で更新する |
Yes |
5. 確認
-
もしもMemcachedを前節でDeployしていれば削除。
kubectl delete -f config/samples/cache_v1alpha1_memcached.yaml
-
memcached-operatorのデプロイ -> operatorは
memcached-operator-system
というNamespaceにデプロイされるexport OPERATOR_IMG="nakamasato/memcached-operator:v0.0.1" make deploy IMG=$OPERATOR_IMG
-
Memcached (CR)を作成
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
ControllerのLogから以下が見て取れる:
1. Memcachedの取得
2. Deploymentの作成
3. MemcachedStatusの更新kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f
2021-11-12T00:47:04.417Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-11-12T00:47:04.417Z INFO controllers.Memcached 2. Check if the deployment already exists, if not create a new one. Creating a new Deployment {"memcached": "default/memcached-sample", "Deployment.Namespace": "default", "Deployment.Name": "memcached-sample"} 2021-11-12T00:47:04.422Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-11-12T00:47:04.523Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Pod list {"memcached": "default/memcached-sample", "podNames": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72"]} 2021-11-12T00:47:04.529Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Update memcached.Status {"memcached": "default/memcached-sample", "memcached.Status.Nodes": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72"]}
-
Memcached のサイズ変更
kubectl patch memcached memcached-sample -p '{"spec":{"size": 5}}' --type=merge
ログを確認:
1. Memcachedの取得
2. DeploymentのReplica更新
3. MemcachedStatusの更新kubectl logs $(kubectl get po -n memcached-operator-system | grep memcached-operator-controller-manager | awk '{print $1}') -c manager -n memcached-operator-system -f
2021-11-12T00:48:44.764Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-11-12T00:48:44.772Z INFO controllers.Memcached 3. Ensure the deployment size is the same as the spec. Update deployment size {"memcached": "default/memcached-sample", "Deployment.Spec.Replicas": 5} 2021-11-12T00:48:44.772Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memchached resource found {"memcached": "default/memcached-sample", "memcached.Name": "memcached-sample", "memcached.Namespace": "default"} 2021-11-12T00:48:44.772Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Pod list {"memcached": "default/memcached-sample", "podNames": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72"]} ... 2021-11-12T00:48:45.987Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Pod list {"memcached": "default/memcached-sample", "podNames": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72", "memcached-sample-6c765df685-g9pj5", "memcached-sample-6c765df685-9wjrv", "memcached-sample-6c765df685-nbbmk"]}
2021-11-12T00:48:45.987Z INFO controllers.Memcached 4. Update the Memcached status with the pod names. Update memcached.Status {"memcached": "default/memcached-sample", "memcached.Status.Nodes": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72", "memcached-sample-6c765df685-g9pj5", "memcached-sample-6c765df685-9wjrv", "memcached-sample-6c765df685-nbbmk"]}
```
`3. Ensure the deployment size is the same as the spec. Update deployment size`で、Deploymentの数が更新されていることがわかる
さらに、`4. Update the Memcached status with the pod names. Update memcached.Status {"memcached": "default/memcached-sample", "memcached.Status.Nodes": ["memcached-sample-6c765df685-n7ml4", "memcached-sample-6c765df685-74l72", "memcached-sample-6c765df685-g9pj5", "memcached-sample-6c765df685-9wjrv", "memcached-sample-6c765df685-nbbmk"]}` で Status.Nodesにも反映されていることがわかる
```bash
kubectl get po
NAME READY STATUS RESTARTS AGE
memcached-sample-6c765df685-74l72 1/1 Running 0 4m36s
memcached-sample-6c765df685-9wjrv 1/1 Running 0 2m56s
memcached-sample-6c765df685-g9pj5 1/1 Running 0 2m56s
memcached-sample-6c765df685-n7ml4 1/1 Running 0 4m36s
memcached-sample-6c765df685-nbbmk 1/1 Running 0 2m56s
```
```bash
kubectl get memcached memcached-sample -o jsonpath='{.status}'
{"nodes":["memcached-sample-6c765df685-n7ml4","memcached-sample-6c765df685-74l72","memcached-sample-6c765df685-g9pj5","memcached-sample-6c765df685-9wjrv","memcached-sample-6c765df685-nbbmk"]}
```
-
Memcachedの削除
kubectl delete -f config/samples/cache_v1alpha1_memcached.yaml
ログを確認すると削除された
2021-11-12T00:53:22.424Z INFO controllers.Memcached 1. Fetch the Memcached instance. Memcached resource not found. Ignoring since object must be deleted {"memcached": "default/memcached-sample"}
6. operatorの削除
make undeploy
まとめ
- Operator-sdkのTutorialで、初心者でも一歩ずつ開発からDeployして確認までのプロセスを学べた
- Code: https://github.com/nakamasato/memcached-operator