2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kubernetes Operatorから管理対象のPodにexecする

Last updated at Posted at 2020-03-01

前回に引き続きKubernetes Operator。
今日はOperatorからそれが生成したPod/コンテナ内でexecを実行するところにチャレンジしてみたい。

とりあえずGoogleでリサーチしてみた感じでは、Kubernetesのクライアントライブラリにremotecommandと言うのがあるのでそれを使うのが簡単で良さそうだ。

・・・と思うだろうが、これは無理筋である。remotecommand.NewSPDYExecutor()という関数でチャチャっとOperator内からexec実行と言うのは世界中のOperator仲間が夢見ている事ではあるらしいが、かなり一筋縄では行かない。とりあえずschema.GroupVersionKindなんだろうねというところで今回は挫折した。

なんか、ドキュメントの整備具合とか、Operator開発をちょっと試みただけでもGo言語というかそのエコシステムに限界を感じるな。。

そういう時はとりあえずos/execである。OSでコマンド実行すれば大抵の事は何とかなる。

Operatorのひな型を作る

カスタムリソースの作成

# mkdir -p ~/projects/examples
# cd ~/projects/examples
# operator-sdk new exec-test --repo github.com/examples/exec-test
# cd exec-test
# operator-sdk add api --api-version=example.com/v1alpha1 --kind=ExecTest

ファイル「pkg/apis/example/v1alpha1/exectest_types.go」編集して、どうすっか、Specに文字列のプロパティ「Message」を追加。

type ExecTestSpec struct {
        Message string `json:"message"`
}

CRD等を再生成。

# operator-sdk generate k8s
# operator-sdk generate crds

CRのサンプルにプロパティ「message」を追加。
ファイル「deploy/crds/example.com_v1alpha1_exectest_cr.yaml」を編集する。

(修正前)
spec:
  # Add fields here
  size: 3
(修正後)
spec:
  message: "Hello My Child!"

また、Operator内でkubectl execを実行するため、それが可能な様にSAというかroleに権限を追加する。「deploy/role.yaml」の最後に以下を追加する。

- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create"]

コントローラーのDockerイメージ

今回Operator内でkubectlコマンドやシェルスクリプトを実行するため、Dockerイメージに仕込んでおく。
「build/Dockerfile」を修正しても良いだろうが、その中で「build/bin」ディレクトリの中を丸ごと/usr/local/binにコピーしている処理があるので、build/binに実行したいファイルを全部配置する。
kubectlのダウンロードについては以下。
https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-linux

# pushd build/bin
# curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
# chmod +x kubectl
# cat>custom_probe<<'EOF'
#!/bin/sh
kubectl exec $1 -- sh -c "echo $(date) $2 >>~/messages"
kubectl exec $1 -- ls -l ~/messages
EOF
# chmod +x custom_probe
# popd

カスタムコントローラーの作成

カスタムコントローラーのひな型を作る。

# operator-sdk add controller --api-version=example/v1alpha1 --kind=ExecTest

続いて、ファイル「pkg/controller/exectest/exectest_controller.go」を編集する。

importの追加

ファイル先頭のimportを編集。パッケージ "os/exec" と "time" を追加する。


import (
...
        "os/exec"
        "time"
)

custom_probe実行の処理を追加

130行目あたり、Operatorの子オブジェクトが生成済みの場合の処理を弄れば良いか。

(修正前)
        // Pod already exists - don't requeue
(修正後)
        // exec-test
        reqLogger.Info("exec-test: ", "Pod.Namespace", found.Namespace,         "Pod.Name", found.Name, "Message", instance.Spec.Message)
        out, err := exec.Command("custom_probe", found.Name, instance.Spec.Message).Output()
        reqLogger.Info("exec-test(output): ", "out", out)

Reconcile loopをRequeueAfterに変更

コンテナ生成時、exec.Command実行時のそれぞれのreturn処理をRequeueAfterに変え、Reconcileが5秒に一回実行されるようにする。

(123行目あたり)
                // // Pod created successfully - don't requeue
                // return reconcile.Result{}, nil
                // Pod created successfully - and requeue
                return reconcile.Result{RequeueAfter: time.Second*5}, nil
(135行目あたり)
        // return reconcile.Result{}, nil
        return reconcile.Result{RequeueAfter: time.Second*5}, nil
}

Operatorをビルドしてデプロイ

# operator-sdk build exec-test
# sed -i "s|REPLACE_IMAGE|exec-test|g" deploy/operator.yaml
# sed -i "s|imagePullPolicy:.*|imagePullPolicy: Never|" deploy/operator.yaml
# kubectl create -f deploy/service_account.yaml
# kubectl create -f deploy/role.yaml
# kubectl create -f deploy/role_binding.yaml
# kubectl create -f deploy/crds/example.com_exectests_crd.yaml
# kubectl create -f deploy/operator.yaml
# kubectl create -f deploy/crds/example.com_v1alpha1_exectest_cr.yaml

動作確認

CRまでデプロイした所で、クラスターにはoperatorとCRに紐づいたPodが1つずつ生成されているはずである。

[root@ip-172-26-4-124 exec-test]# kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
example-exectest-pod         1/1     Running   0          8s
exec-test-5df88dfb47-s9lts   1/1     Running   0          14s

「exec-test-*」がOperatorであり、「example-exectest-pod」がCRを反映して生成されたOperatorの子Podである。

カスタムコントローラーで、コマンド実行後にその出力をログに出すようにしていたためまずはそれを見てみよう。

[root@ip-172-26-4-124 exec-test]# kubectl logs --tail 3 exec-test-5df88dfb47-s9lts
{"level":"info","ts":1583056428.8085327,"logger":"controller_exectest","msg":"Reconciling ExecTest","Request.Namespace":"default","Request.Name":"example-exectest"}
{"level":"info","ts":1583056428.8086174,"logger":"controller_exectest","msg":"exec-test: ","Request.Namespace":"default","Request.Name":"example-exectest","Pod.Namespace":"default","Pod.Name":"example-exectest-pod","Message":"Hello My Child!"}
{"level":"info","ts":1583056429.2487488,"logger":"controller_exectest","msg":"exec-test(output): ","Request.Namespace":"default","Request.Name":"example-exectest","out":"LXJ3LXItLXItLSAgICAxIHJvb3QgICAgIHJvb3QgICAgICAgICAgIDQ4NCBNYXIgIDEgMDk6NTMgL3Jvb3QvbWVzc2FnZXMK"}

「"out":"LXJ3LXItLXItLSAgICAxIHJvb3QgICAgIHJvb3QgICAgICAgICAgIDQ4NCBNYXIgIDEgMDk6NTMgL3Jvb3QvbWVzc2FnZXMK"」は、シェルスクリプトがコンテナでlsを実行した結果の出力なのだが、base64エンコードされているのでデコードする。

[root@ip-172-26-4-124 exec-test]# echo "LXJ3LXItLXItLSAgICAxIHJvb3QgICAgIHJvb3QgICAgICAgICAgIDQ4NCBNYXIgIDEgMDk6NTMgL3Jvb3QvbWVzc2FnZXMK" | base64 -d
-rw-r--r--    1 root     root           484 Mar  1 09:53 /root/messages

うんまあ、良さそうじゃないか。
子Pod「example-exectest-pod」を調べると「/root/messages」というファイルが作られており、これは親であるOperatorがkubectl execを実行した結果としてできたものである。

[root@ip-172-26-4-124 exec-test]# kubectl exec example-exectest-pod cat /root/messages
Sun Mar 1 09:52:59 UTC 2020 Hello My Child!
Sun Mar 1 09:52:59 UTC 2020 Hello My Child!
Sun Mar 1 09:53:05 UTC 2020 Hello My Child!
Sun Mar 1 09:53:10 UTC 2020 Hello My Child!
Sun Mar 1 09:53:16 UTC 2020 Hello My Child!
...

まあ、大体5秒おきにexec(custom_probe)が実行されている様子も見て取れる。

Operatorを消す

クラスターにデプロイしたOperatorを削除する。

# kubectl delete -f deploy/crds/example.com_v1alpha1_exectest_cr.yaml
# kubectl delete -f deploy/operator.yaml
# kubectl delete -f deploy/role.yaml
# kubectl delete -f deploy/role_binding.yaml
# kubectl delete -f deploy/service_account.yaml
# kubectl delete -f deploy/crds/example.com_exectests_crd.yaml

おわりに

困ったときはOS上でコマンド実行すれば何とかなる。
ていうか、カスタムコントローラーをGO言語でガリガリ作り込むって多分あまり現実的じゃないので、実用的と言うかちょっと込み入ったことをOperatorで実装しようと思ったら、処理の大半をシェルスクリプトとかGO言語の外に移管するんじゃないか?

で、その場合ReconcileはKubernetesオブジェクトのイベントがトリガーではなく、ReconcileAfterで定期的に実行する形にせざるを得ない。

次はOLM、Operator Lifecycle Managerあたりか。
また日を改めて。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?