Kubernetes の Watch API とタイムアウト
Kubernetes の Watch API を下記クライアントライブラリを使って試してみました。
ソースは http://github.com/fits/try_samples/tree/master/blog/20180409/
はじめに
下記のコマンドを実行して Javascript Kubernetes Client をインストールしておきます。
Javascript Kubernetes Client のインストール
> npm install @kubernetes/client-node
Watch API による Pod の監視
Watch API で default
Namespace の Pod に関するイベントを監視して、イベントのタイプと Pod 名を標準出力する処理を実装してみます。
watch
の第一引数に Watch API のエンドポイント URL、第三引数でイベントハンドラを指定します。(第二引数はクエリパラメータ)
今回は Pod を監視していますが、default
Namespace の Deployment を監視する場合は endpoint
を /apis/apps/v1/namespaces/default/deployments
とします。
なお、$HOME/.kube/config
もしくは %USERPROFILE%\.kube\config
ファイルから Kubernetes への接続情報を取得するようにしています。
sample_watch_pod.js
const k8s = require('@kubernetes/client-node') // default Namespace の Pod const endpoint = '/api/v1/namespaces/default/pods' // Windows 環境用の設定 if (!process.env.HOME) { process.env.HOME = process.env.USERPROFILE } const conf = new k8s.KubeConfig() conf.loadFromFile(`${process.env.HOME}/.kube/config`) const w = new k8s.Watch(conf) w.watch( endpoint, {}, (type, obj) => { console.log(`${type} : ${obj.metadata.name}`) }, err => { if (err) { console.error(err) } else { console.log('done') } } )
動作確認
今回、Kubernetes の環境を minikube で用意します。
minikube コマンドを使って start を実行するとローカル用の Kubernetes 環境が立ち上がります。
その際に、%USERPROFILE%\.kube\config
ファイル等が作られます。
minikube 開始
> minikube start ・・・ Starting local Kubernetes v1.9.0 cluster... Starting VM... Getting VM IP address... Moving files into cluster... Setting up certs... Connecting to cluster... Setting up kubeconfig... Starting cluster components... Kubectl is now configured to use the cluster. Loading cached images from config file.
sample_watch_pod.js の実行
> node sample_watch_pod.js
下記 YAML ファイルを使って、Kubernetes 環境へ nginx 実行用の Deployment と Service を作成してみます。
nginx.yaml (nginx 用の Deployment と Service 定義)
apiVersion: v1 kind: Service metadata: name: nginx-service labels: app: nginx spec: ports: - name: http port: 80 nodePort: 30001 selector: app: nginx type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80
kubectl を使って Deployment と Service を作成します。
Deployment と Service 作成
> kubectl create -f nginx.yaml service "nginx-service" created deployment "nginx-deploy" created
Watch の結果は以下のようになりました。
sample_watch_pod.js の結果1
> node sample_watch_pod.js ADDED : nginx-deploy-679dc9c764-r9ds5 MODIFIED : nginx-deploy-679dc9c764-r9ds5 ADDED : nginx-deploy-679dc9c764-54d5d MODIFIED : nginx-deploy-679dc9c764-r9ds5 MODIFIED : nginx-deploy-679dc9c764-54d5d MODIFIED : nginx-deploy-679dc9c764-54d5d MODIFIED : nginx-deploy-679dc9c764-r9ds5 MODIFIED : nginx-deploy-679dc9c764-54d5d
ここで、いつまでも接続が続くわけでは無く、minikube の環境では 40分程度(ただし、毎回異なる)で接続が切れ以下のようになりました。
sample_watch_pod.js の結果2 (一定時間経過後)
> node sample_watch_pod.js ・・・ done
タイムアウト時間の確認
Kubernetes と minikube のソースから、タイムアウトに関係していると思われる箇所 timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0))
を見つけました。※
※ minikube では localkube 内で Kubernetes の API Server を実行しているようです
これだと、タイムアウトは 30 ~ 60分でランダムに決まる事になりそうなので、接続の切れる時間が毎回異なるという現象に合致します。
ソース kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/get.go
func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { ・・・ if opts.Watch || forceWatch { ・・・ timeout := time.Duration(0) if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } if timeout == 0 && minRequestTimeout > 0 { timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0)) } glog.V(2).Infof("Starting watch for %s, rv=%s labels=%s fields=%s timeout=%s", req.URL.Path, opts.ResourceVersion, opts.LabelSelector, opts.FieldSelector, timeout) ・・・ return } ・・・ } }
ソース minikube/pkg/localkube/apiserver.go
// defaults from apiserver command config.GenericServerRunOptions.MinRequestTimeout = 1800
get.go の処理ではログレベル 2 でタイムアウトの値をログ出力しているので(glog.V(2).Infof(・・・)
の箇所)ログから確認できそうです。
ただし、普通に minikube start
で実行してもログレベル 2 のログは見れないようなので、minikube を -v <ログレベル>
オプションを使って起動しなおします。
ログレベル 2 で miinkube 開始
> minikube start -v 2
sample_watch_pod.js の実行
> node sample_watch_pod.js ・・・
minikube logs
でログ内容を確認してみると、get.go が出力しているタイムアウトの値を確認できました。
ログ確認
> minikube logs ・・・ Apr 08 01:00:30 minikube localkube[2995]: I0408 01:00:30.533448 2995 get.go:238] Starting watch for /api/v1/namespaces/default/pods, rv= labels= fields= timeout=58m38.2420124s ・・・