kubectlでやってることをcurlでやりたい


概要

kubernetesを運用する際に、アドオン的な感じで動的にPod(Kubernetes Resource)を生成するというユースケースを知った。そのため例えばPod内から新たにPodを生成するなどといった場合が考えられ、その際にkubectlをPod内から使用するケースやcurlなどの汎用的なツールを簡易的に使えるかを確認したくなった。

下記では、kubectl proxyを用いて、kubernetesクラスタ外からKubernetes APIをcurlを通して実行することで同様のことを確認した。Pod内から同様のことを行う際は、Kubernetes APIを実行するためのトークン 取得方法が異なっているので注意が必要。

Kubernetes APIとは

Kubernetes内のコンポーネント間の通信や捜査はすべてAPIを通して行われており、REST形式のAPIとなっています。

kubectlを用いることで大半の操作は行うことができますし、直接REST APIをcurlで叩くことでも同様の操作を行うことができます。

直接Kubernetes API叩いてみる

kubectl proxyなしの場合

なければクラスタ起動

kind create cluster

クラスタ一覧確認

kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

アクセスするクラスタ名を選択(自分の場合は、kindを使用しているのでこのクラスタ名を選択)

export CLUSTER_NAME="kind-kind"

クラスタ名からアクセス先取得

APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")

アクセストークン 取得

TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 --decode)

kubernetes APIを実行

curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "172.17.0.2:6443"
    }
  ]
}

kubectl proxyありの場合

kubectl proxyを起動

8001番ポートで動作していることを確認

kubectl proxy
Starting to serve on 127.0.0.1:8001

Kubernetes APIを叩いてみると、先ほどと同様の結果を取得できる

curl -X GET 127.0.0.1:8001/api 
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "172.17.0.2:6443"
    }
  ]
}

試しにnginxのPodを動作させてみてから、Kubernetes APIでPodを確認してみる

k run nginx --image=nginx --restart=Never --dry-run -oyaml > nginx.yaml

k apply -f nginx.yaml 

k get po
NAME    READY   STATUS              RESTARTS   AGE
nginx   0/1     ContainerCreating   0          1s

curlでPod一覧取得してみるとjson形式で同様の情報が取得できる

curl -X GET http://127.0.0.1:8001/api/v1/namespaces/default/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/namespaces/default/pods",
    "resourceVersion": "5973"
  },
  "items": [
    {
      "metadata": {
        "name": "nginx",
        "namespace": "default",
        "selfLink": "/api/v1/namespaces/default/pods/nginx",
        "uid": "119f6219-5ff3-4542-b785-e7532301a972",
        "resourceVersion": "5891",
        "creationTimestamp": "2020-03-21T06:08:56Z",
        "labels": {
          "run": "nginx"
        },
        "annotations": {
          "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":null,\"labels\":{\"run\":\"nginx\"},\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx\",\"name\":\"nginx\",\"resources\":{}}],\"dnsPolicy\":\"ClusterFirst\",\"restartPolicy\":\"Never\"},\"status\":{}}\n"
        }
      },
      "spec": {
        "volumes": [
          {
            "name": "default-token-lh646",
            "secret": {
              "secretName": "default-token-lh646",
              "defaultMode": 420
            }
          }
        ],
        "containers": [
          {
            "name": "nginx",
            "image": "nginx",
            "resources": {

            },
~中略~
      }
    }
  ]
}

jsonだとkubectlの結果より見づらいのは仕方ないが、下記のようなツールでjson結果もある程度扱いやすくできる。

Macの場合下記のコマンドで導入できる。

brew install jid

curl -X GET http://127.0.0.1:8001/api/v1/namespaces/default/pods | jid

下記のように対話的にjsonファイルを深掘りできる。

curlでPodの作成を試してみる

エンドポイントについてはKubernetes APIから確認することができる

Hwllo Worldとだけ出力するPodを作成する

curl -X POST -H 'Content-Type: application/yaml'  -d '
apiVersion: v1
kind: Pod
metadata:
  name: pod-example
spec:
  containers:
  - name: ubuntu
    image: ubuntu:trusty
    command: ["echo"]
    args: ["Hello World"]
  restartPolicy: Never
'  http://127.0.0.1:8001/api/v1/namespaces/default/pods 

実行結果

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "pod-example",
    "namespace": "default",
    "selfLink": "/api/v1/namespaces/default/pods/pod-example",
    "uid": "4ac7cf5c-69b7-40f9-806d-582cafebd590",
    "resourceVersion": "8977",
    "creationTimestamp": "2020-03-21T06:50:46Z"
  },
  "spec": {
    "volumes": [
      {
        "name": "default-token-lh646",
        "secret": {
          "secretName": "default-token-lh646",
          "defaultMode": 420
        }
      }
    ],
    "containers": [
      {
        "name": "ubuntu",
        "image": "ubuntu:trusty",
        "command": [
          "echo"
        ],
        "args": [
          "Hello World"
        ],
        "resources": {

        },
~中略~
}

Podが作成されていることを確認

先ほど作成したnginxのpodに加えて、

ログにHello Worldが出力されていることも確認できる

k logs pod-example 
Hello World

Pod内からKubernetes APIを叩く方法

必要な権限を持ったSeriveAccountを作成(ついでにnamespaceなども作成)

k apply -f api.yaml 

Namespace切り替え

kubensは別途install必要(https://github.com/ahmetb/kubectx)

kubens mynamespace

Curlを叩くためのPodのyamlを生成

k run fedora --image=fedora --restart=Never --dry-run -oyaml -- /sbin/init  > fedora.yaml

出力したyamlに一部追記(tty, stdin, serviceAccountName)

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: fedora
  name: fedora
spec:
  containers:
  - args:
    - /sbin/init
    image: fedora
    name: fedora
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  tty: true
  stdin: true
  serviceAccountName: mysa
status: {}

Podをデプロイ

k apply -f fedora.yaml

Pod内にアクセス

k exec -it fedora bash

トークン を取得して、Kubernetes APIを実行

下記は、このコマンドを実行しているPod情報を取得する

KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=mynamespace
curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" \
https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/$NAMESPACE/pods/$HOSTNAME

jsonで取得するためぱっと見わかるづらいが、k get po と同様の内容を取得できている。

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "fedora",
    "namespace": "mynamespace",
    "selfLink": "/api/v1/namespaces/mynamespace/pods/fedora",
    "uid": "7c918fc0-f194-4729-896a-f52167c4a803",
    "resourceVersion": "17841",
    "creationTimestamp": "2020-03-21T08:51:57Z",
    "labels": {
      "run": "fedora"
    },
    "annotations": {
      "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":null,\"labels\":{\"run\":\"fedora\"},\"name\":\"fedora\",\"namespace\":\"mynamespace\"},\"spec\":{\"containers\":[{\"args\":[\"/sbin/init\"],\"image\":\"fedora\",\"name\":\"fedora\",\"resources\":{},\"stdin\":true,\"tty\":true}],\"dnsPolicy\":\"ClusterFirst\",\"restartPolicy\":\"Never\",\"serviceAccountName\":\"mysa\"},\"status\":{}}\n"
    }
  },
  "spec": {
    "volumes": [
      {
        "name": "mysa-token-lt4gh",
        "secret": {
          "secretName": "mysa-token-lt4gh",
          "defaultMode": 420
        }
      }
    ],
    "containers": [
      {
        "name": "fedora",
        "image": "fedora",
        "args": [
          "/sbin/init"
        ],
        "resources": {

        },
~中略~
}

まとめ

Kubernetes APIを知ることでkubectlでやっていることをcurlでも同様に行えることを確認した。
新たにPodなどを作る処理はPod内でもかけるため、動的に増加するリソースを制御・監視する方法を検討したい。