Kubernetes クラスタ内に一時的な Pod をデプロイして kubectl コマンドを実行したい

Kubernetes クラスタ内に一時的な Pod をデプロイして、その Pod から kubectl コマンドを実行し、Kubernetes クラスタを操作したいと思った。

一時的な Pod を作るだけなら、$ kubectl run--rm オプションと --restart=Never を使えば良い。その場で Docker イメージをダウンロードし、コンテナ・Pod として展開、シェルに接続できる。

# 一時的な Pod を作る
$ kubectl run temp-node --image=node:15 --namespace=default --rm -it --restart=Never --command -- bash

# 生成された Pod 内で kubectl コマンドをインストールする
$$ curl -sL "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl

色々調べると、kubectl コマンドを使わずとも、次のようにトークンを利用すれば、Pod 一覧を取得したりできるらしいのだが、権限が足りずできなかった。コレはデフォルトの挙動として真っ当なモノらしい。

# 権限付与が要る
$$ curl -ik -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc.cluster.local/api/v1/namespaces/default/pods

当然、ホストマシンから KubeConfig ファイルをコピーしたりすれば、動かすことはできるが、なんかダサい。

# Pod 内でディレクトリだけ作っておく
$$ mkdir -p /root/.kube/

# ホストマシン側から Pod に対して KubeConfig ファイルを転送する
$ kubectl cp ~/.kube/my-config temp-node:/root/.kube/config

というワケで、こんなことをしなくてもいいように、ServiceAccountClusterRoleBinding という、権限管理用のリソースを作ってやる。

# Service Account を作る
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kubectl-in-cluster
  namespace: default
# 作成した Service Account に cluster-admin 権限をバインディングする
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubectl-in-cluster-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: kubectl-in-cluster
    namespace: default

このような YAML ファイルを作って、Kubernetes クラスタに投入する。

$ kubectl apply -f cluster-role-binding-kubectl-in-cluster.yaml
$ kubectl apply -f service-account-kubectl-in-cluster.yaml

# デプロイできたか確認する
$ kubectl get serviceaccount --namespace default
$ kubectl clusterrolebinding

一時的な Pod を作る際、投入した Service Account を使うよう、--serviceaccount オプションを指定する。

$ kubectl run temp-node --image=node:15 --namespace=default --serviceaccount=kubectl-in-cluster --rm -it --restart=Never --command -- bash

# kubectl コマンドはとりあえず入れるとして…
$$ curl -sL "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl

# 次のようなコマンドが動くようになった
$$ kubectl get pod

できたできた。

ちなみに、コマンドで実現していた一時 Pod の生成を YAML ファイルに起こすと、こんな感じ。

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: kubectl
  name: temp-node
  namespace: default
spec:
  serviceAccountName: kubectl-in-cluster
  restartPolicy: Never
  containers:
    - name: temp-node
      image: node:15
      lifecycle:
        postStart:
          exec:
            # ココで kubectl を用意しておく
            command: ['sh', '-c', 'curl -sL "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl']
      # ['bash'] でもとりあえず死なない。tty: true や stdin: true を付けても kubectl apply -f 直後にシェルにアタッチはされないみたい
      command: ['tail', '-f', '/dev/null']

コレを $ kubectl apply -f で投入して、$ kubectl attach 【Pod 名】 でアタッチすれば良さそう。