Spot OceanでGKEの利用料を削減してみる(その２)

## [前回](https://qiita.com/buriki/items/17bd5414315edd5d6f7a)までのあらすじ
最近様々な業界で、コンテナ上のワークロードが増えてきたように感じています。弊社のFinOpsソリューション：Spotには、Oceanというコンテナワークロードのコスト最適化サービスがあります。GKEとの連携は可能ではあるものの、日本語書かれた親切なガイドみかけないため、やり方をご紹介します。

前回の記事では、Spot OceanのSaaSプレーンとGoogle Cloud Platformでコスト最適化をするクラスタが配置されているプロジェクトとの連携が完了しました。この記事では、実際GKEクラスタとOceanを連携させて、利用料の最適化を行っていきます。

## GKEクラスターの作成
まずは、Spot Oceanでコスト最適化をかけるGKEクラスタを作成していきます。GKEは初心者なので、とりあえずRecommendedと書いてあるAutopilotモードで、リージョンはasia-east1で作成してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/61645fd4-a621-3912-11f0-167ca87d8823.png)
↓
autopilot-cluster1というクラスタが作成できました。こちらをSpot Oceanに登録していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/d5a3e3e8-facb-f9c0-52ae-8812d9246994.png)

Spotコンソールを見ると、この時点でざっくりとGoogle Cloud Platform全体での削減効果を出してくれます。「Current estmated cost」= 今月分の予想支払額も、クラスタ作成時に表示された金額と同じです。しかしこれは「Potential optimized cost」、あくまでもOceanを使って最適化したらこれぐらい安くなりますよ、という指標です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/15e68f64-e825-bf09-039d-b301551e62ef.png)


## Spot Ocean画面からGKEを登録する(失敗)
SpotコンソールのOcean画面に入ると、当然ですがまだクラスタは登録されていません。先ほど作ったautopilot-cluster1を登録してみます。[**Create Cluster**]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/b3d25b85-50a3-a4dd-d0af-6b826899031b.png)
Oceanに登録する名前、リージョンを選択すると、登録できるクラスタの名前が表示される。[**autopilot-cluster1**]を選択して次へ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/711aeda7-4103-1b40-3256-d1278747eb1d.png)
ムム？！インスタンスグループがないとエラーが出てしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/969daf24-d7ed-492c-be0f-1afbcdb03ce9.png)
[Google Cloud: インスタンスグループ](https://cloud.google.com/compute/docs/instance-groups)を見ると、「インスタンス グループとは、単一のエンティティとして管理できる仮想マシン（VM）インスタンスの集まりです。」と書いてあります。→ じゃあインスタンスが中にあれば行けるかも？確かにautopilotのクラスタは「Number of Nodes」が0だったので、中身があれば登録できるのかも？

## GKEを再作成
今度はStandardデプロイで、ノードの数を指定してみます。料金節約も兼ねて、リージョンではなくゾーン指定(asia-east1-a)で3 node構成で作ってみます。Spotに追加してみると、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/8b851dfa-ec63-dff6-4f23-63952e2fc09c.png)

先ほどのエラーはなくなりました。しかし本当にインスタンスがないからだけなのだろうか？？
とりあえずそのままCPUの数なども設定して進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/16e59c1b-8194-9988-c1ae-9fb7de679ca5.png)

次にOceanを使用するために環境に配置する、Ocean ControllerというPodをインストールする作業です。
Ocean Controllerについてはコチラ→(https://docs.spot.io/ocean/tutorials/spot-kubernetes-controller/)[https://docs.spot.io/ocean/tutorials/spot-kubernetes-controller/]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/71e47231-c2a4-5076-8d95-c00dec955cab.png)

**Step 1**
入力欄の下にある[**Generate Token**]で、新しくSpotと連携するためのトークンを発行します。赤いフォントで出力されるので保存しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/2fbeff17-6261-f307-15c7-8d50717162f9.png)

**Step 2**
Step 1でトークンを生成すると、自動的にスクリプト内の```SPOTINST_TOKEN=<ENTER YOUR TOKEN HERE> \```も更新されます。画面に表示されているスクリプトで```ControllerInit.sh```ファイルを作成して、Google Cloud PlatformのCloud Shellで実行していきます。しかしこれだけでは```./ControllerInit.sh```で実行すると、Permission Deniedが出てしまいます。

```ruby:ControllerInit.sh
#!/usr/bin/env bash
curl -fsSL http://spotinst-public.s3.amazonaws.com/integrations/kubernetes/cluster-controller/scripts/init.sh | \
SPOTINST_TOKEN=<Step 1で生成したトークン> \
SPOTINST_ACCOUNT=act-<ご自身のSpotアカウントID> \
SPOTINST_CLUSTER_IDENTIFIER=my-first-ocean-cluster-<クラスタID：自動入力されます> \

bash
```

親切に「恐らく権限のエラーが出るので、権限を上げる用のコマンド」が載っています。しかし、こちらもこれだけだとうまく動きません。Spotチーム惜しい！

```
$ kubectl create clusterrolebinding <CLUSTER NAME> --clusterrole=cluster-admin --user=<userEmail>

F0119 02:33:37.694476     547 cred.go:123] print credential failed with error: Failed to retrieve access token:: failure while executing gcloud, with args [config config-helper --format=json]: exit status 1
error: failed to create clusterrolebinding: Post "https://35.194.166.0/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?fieldManager=kubectl-create&fieldValidation=Strict": getting credentials: exec: executable gke-gcloud-auth-plugin failed with exit code 1
```
このエラーはアクセストークンの取得に失敗していると言っていて、kubectlが有効なconfigを持っていない場合に発生します。試しに中身を見てみるとこんな感じです。まっさらですね。

```
$ kubectl config view
apiVersion: v1
clusters: []
contexts: []
current-context: ""
kind: Config
preferences: {}
users: []
```

このコマンドで、コンテナのエンドポイント情報などをkubectlのconfigに入力します。

```
$ gcloud container clusters get-credentials <CLUSTER NAME> --zone <CLUSTER=LOCATED=ZONE>
Fetching cluster endpoint and auth data.
kubeconfig entry generated for <CLUSTER NAME>.
```

<details><summary>kubectl configをもう一度見るとこんな中身になっています。</summary>

```
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://XX.XX.XX.XX
  name: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://XX.XX.XX.XX
  name: gke_rational-logic-374504_asia-east1_hello-cluster
contexts:
- context:
    cluster: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
    user: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
  name: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
- context:
    cluster: gke_rational-logic-XXXXXXX_asia-east1_hello-cluster
    user: gke_rational-logic-XXXXXXX_asia-east1_hello-cluster
  name: gke_rational-logic-XXXXXXX_asia-east1_hello-cluster
current-context: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
kind: Config
preferences: {}
users:
- name: gke_rational-logic-XXXXXXX_asia-east1-a_<CLUSTER NAME>
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args: null
      command: gke-gcloud-auth-plugin
      env: null
      installHint: Install gke-gcloud-auth-plugin for use with kubectl by following
        https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
      interactiveMode: IfAvailable
      provideClusterInfo: true
- name: gke_rational-logic-XXXXXXX_asia-east1_hello-cluster
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args: null
      command: gke-gcloud-auth-plugin
      env: null
      installHint: Install gke-gcloud-auth-plugin for use with kubectl by following
        https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
      interactiveMode: IfAvailable
      provideClusterInfo: true
```

</details>

今回は、権限の付与が成功しました。そのまま```ControllerInit.sh```ファイルも実行していきます。

```
$ kubectl create clusterrolebinding cluster-1 --clusterrole=cluster-admin --user=<userEmail>
clusterrolebinding.rbac.authorization.k8s.io/cluster-1 created

$ ./ControllerInit.sh
2023-01-19T03:08:30.192Z downloading
2023-01-19T03:08:30.383Z rendering
2023-01-19T03:08:30.407Z applying
secret/spotinst-kubernetes-cluster-controller created
configmap/spotinst-kubernetes-cluster-controller-config created
serviceaccount/spotinst-kubernetes-cluster-controller created
clusterrole.rbac.authorization.k8s.io/spotinst-kubernetes-cluster-controller created
clusterrolebinding.rbac.authorization.k8s.io/spotinst-kubernetes-cluster-controller created
deployment.apps/spotinst-kubernetes-cluster-controller created
```

**Step 3**
Ocean Controllerのデプロイは完了したので、Spotコンソールに戻りControllerとOceanを連携させていきます。Test Connectivityから、Ocean Controllerと連携を開始します。緑色のチェックマークがついたら成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/0b7c3496-0a33-7124-05c5-40fdef0cd70a.png)

最後にReviewページで内容を確認して[**Create**]を押すと、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504005/90d1e5aa-d1a4-5ada-a909-651f8a89a23d.png)

こんなところにも落とし穴が、、、
次号に続きます。
↓
[Spot OceanでGKEの利用料を削減してみる(その３)](https://qiita.com/buriki/items/2eb94bd7f2fedec0a7b8)

## (おまけ)なんでAutopilot Clusterだとインポートできなかったのか？
Google Cloud Platformのautopilot clusterはAWSのFargate同様、サーバーレスのコンテナサービスです。Spotの公式にドキュメントには書いてありませんが、Spot側からコンテナの操作ができないため弾かれているのだと予想します。ただ、Fargateに関しては、対応してインポートが可能になりましたので、Google Cloud Platformのautopilot clusterの対応もいずれ訪れるのではないかなと思います。