GKEでAPIを利用してみる

GKEのAPIを利用してリソースの操作してみるメモ.
多分オンプレも同じですがまだ検証してないので動作確認したら追記します.

追記 2019/03/06
オンプレ環境下でも問題なく動きました

環境

環境は以下です.

1$ kubectl version
2Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.7", GitCommit:"0c38c362511b20a098d7cd855f1314dad92c2780", GitTreeState:"clean", BuildDate:"2018-08-20T10:09:03Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
3Server Version: version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.7-gke.11", GitCommit:"dc4f6dda6a08aae2108d7a7fdc2a44fa23900f4c", GitTreeState:"clean", BuildDate:"2018-11-10T20:22:02Z", GoVersion:"go1.9.3b4", Compiler:"gc", Platform:"linux/amd64"}

APIの利用

とりあえず叩いてみる

公式ドキュメント記載の方法でひとまず叩いてみます

1$ APISERVER=$(kubectl config view | grep server | cut -f 2- -d ":" | tr -d " ")
2$ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t')
3$ echo ${APISERVER}
4$ echo ${TOKEN}

取得できたら試しに叩いてみます.

 1$ curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
 2{
 3  "kind": "APIVersions",
 4  "versions": [
 5    "v1"
 6  ],
 7  "serverAddressByClientCIDRs": [
 8    {
 9      "clientCIDR": "0.0.0.0/0",
10      "serverAddress": "xxx.xxx.xxx.xxx"
11    }
12  ]
13}

ひとまず叩けました.
ちなみにAPISERVERはGKEの管理コンソールからも確認できます.
Google Cloud Console -> Kubernetes Engine -> クラスタ -> 任意のクラスタ -> クラスタのエンドポイントに記載があります.

APIの認証方式はいろいろあるようですが今回はサービスアカウントを用いた接続になっています.
具体的に追っていくと

1$ kubectl get sa
2NAME               SECRETS   AGE
3default            1         7d

このように各ネームスペースにはネームスペースと同名のサービスアカウントが作成されています.
サービスアカウントにはそれに付随する認証情報が自動で生成されています.

1$ kubectl get sa default -o yaml | grep token
2- name: default-token-xxxxxx

xxxxx部分はランダムに割当られます.
次にこのsecretsの中身からtokenを取り出します.

1$ kubectl get secrets default-token-xxxxxx -o yaml | grep token:
2  token: [TOKEN]

これでtokenが取り出せたのであとはサンプルのcurlのようにリクエストを送ることで利用できます.

ただ,ネームスペースにあるデフォルトの権限では実際にリソースにアクセスすることができません.

権限はkubectl auth can-iを使うことで確認できます.
例えば普段ノード管理に使っているマシンからネームスペースを取得できるか確認すると

1$ kubectl auth can-i list ns
2yes
3$ kubectl auth can-i list ns --as=system:serviceaccount:default:default
4no

のようになります.
実際にアクセスしてみると

1$ kubectl get ns --as=system:serviceaccount:default:default
2Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:default:default" cannot list namespaces at the cluster scope
3$ kubectl get ns
4NAME          STATUS    AGE
5default       Active    7d
6kube-public   Active    7d
7kube-system   Active    7d

のようにデフォルトのサービスアカウントではアクセスできないことがわかります.
今回は専用のサービスアカウントを作成しそれに権限を与えていきます.

専用のサービスアカウントを作る

というわけで操作用のサービスアカウントを作成します.

1$ kubectl create sa sample
2$ kubectl get sa
3NAME               SECRETS   AGE
4default            1         7d
5sample           1         20h

次にこのサービスアカウントへの権限を設定します.
権限は大きく2つに分けられます.
ネームスペース内だけで有効な「Role」とネームスペースをまたがって有効な「ClusterRole」です.
今回は,ClusterRoleを利用してみます.
以下のようにしてyamlを作成して権限を設定します.
今回は大雑把に発行しますが,細かく設定できるので適宜設定します.

1kind: ClusterRole
2apiVersion: rbac.authorization.k8s.io/v1
3metadata:
4  name: sample-cluster-role
5rules:
6  - apiGroups: ["*"]
7    resources: ["*"]
8    verbs: ["*"]

次にこのClusterRoleとサービスアカウントの紐づけを行う「ClusterRoleBinding」を設定します.
今回はClusterRoleなのでClusterRoleBindingになりますが,Roleとのヒモ付を行う場合は「RoleBinding」になります.

 1kind: ClusterRoleBinding
 2apiVersion: rbac.authorization.k8s.io/v1
 3metadata:
 4  name: sample-cluster-role-binding
 5subjects:
 6  # サービスアカウントとのヒモ付
 7  - kind: ServiceAccount
 8    name: sample
 9    namespace: default
10# ClusterRoleとのヒモ付
11roleRef:
12  kind: ClusterRole
13  name: sample-cluster-role
14  apiGroup: rbac.authorization.k8s.io

それぞれのyamlファイルを用いてリソースを作成します.

1$ kubectl create -f clusterrole.yaml
2$ kubectl create -f culsterrolebinding.yaml

これで紐づけができたはずなのでパーミッションを確認してみます.

1$ kubectl auth can-i get ns --as=system:serviceaccount:default:sample
2yes

問題なく設定されていますね.
kubectlでも試してみます.

1$ kubectl get ns --as=system:serviceaccount:default:sample
2NAME               SECRETS   AGE
3default       Active    7d
4kube-public   Active    7d
5kube-system   Active    7d

問題ないですね.
次に,API経由でのアクセスを試します.
適当に取得します.

1$ kubectl get sa sample -o yaml | grep token
2- name: sample-token-xxxxxx
3$ kubectl get secrets sample-token-xxxxxx -o yaml | grep token:
4  token: [TOKEN]

これをbase64デコードして利用するので,適当にカットして使います.

1$ TOKEN=$(kubectl get secrets sample-token-xxxxxx -o yaml | grep token: | cut -f4 -d " " | base64 -d)

さっきと同じようにcurlでAPIを叩いてみます.

1$ curl $APISERVER/api/v1/namespaces --header "Authorization: Bearer $TOKEN" --insecure | grep \"name\"
2        "name": "default",
3        "name": "kube-public",
4        "name": "kube-system",

取得できましたね.
defaultのTOKENに差し替えて試すと権限不足のエラーが出ます.

API Clientから利用してみる

次に公式提供のClientから利用してみます.

 1import os
 2
 3from kubernetes import client, config
 4
 5configuration = client.Configuration()
 6configuration.verify_ssl = False
 7configuration.host = os.environ.get("APISERVER")
 8configuration.api_key["authorization"] = os.environ.get("TOKEN")
 9configuration.api_key_prefix["authorization"] = "Bearer"
10
11apiClient = client.ApiClient(configuration)
12v1 = client.CoreV1Api(apiClient)
13
14for ns in v1.list_namespace().items:
15    print(ns.metadata.name)

を実行すると

1default
2kube-public
3kube-system

のように取得できるはずです.

エラーとか

  • Error from server (Forbidden): error when creating hoge.yaml
    ClusterRoleを作ろうとしたらエラーが出た.
    GKEではデフォルトで自分のアカウントに作成権限がないらしいので付与する必要があるらしい.
1$ gcloud info | grep Account
2Account: [example@example.com]
3$ kubectl create clusterrolebinding [なんか適当に名前をつける] \
4  --clusterrole=cluster-admin \
5  --user=example@example.com

のようにして自分にcluster-admin権限を付与する.

  • verifyがFalseだけど良いの?
    良くない.
    secretsにあるcertをファイルに落として
    1configuration.ssl_ca_cert = "/path/to/cert/file"
    
    とかしたらいけるはず(試してない

参考記事