GCPでCloud Schedulerを使用して定期的にスケールダウンする

利用料金の節約のため、GCP上にある開発環境のサーバーを夜間だけスケールダウンしたいと考えたことはありませんか？
Cloud Schedulerというフルマネージドなcronサービスを利用して、定期的にGCP上のリソースの設定を変更する方法をご紹介します。

GKE(Google Kubernetes Engine)のスケールインを例として紹介しますが、APIが公開されているGCPのリソースであれば同様の方法で設定を変更することが可能です。


## Cloud Schedulerについて

具体的な仕組み作りの前にCloud Schedulerについて簡単に説明します。
Cloud Schedulerは安価で簡単に利用することの出来るcronサービスですが、実行可能なタスクは限られています。

### できること

- cron形式のタスクのスケジュール
- 実行出来るタスクの種類
  - Google Pub/Sub のトピックへのメッセージ送信
  - App Engine Http の呼び出し
  - 任意のhttp(s)エンドポイントの呼び出し (今回はこれを使用します)


### できないこと

- 任意のShell scriptやプログラムなどを直接実行する
- サービスアカウントを使っての認可[1]
- ~~delete, get, head, post, put 以外のMethodを使用したAPI呼び出し~~[2]

[1] Google Cloud Next '19でCloud Schedulerに認証を加える発表があったようですが、2019年4月現在はまだ利用できないようです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">この Cloud Scheduler と Cloud Tasks の HTTP Auth はまだ使えない機能に言及しているということで良いのかな(後者は HTTP Push 自体がまだパブリックじゃない)<a href="https://t.co/UJUuCiGHeM">https://t.co/UJUuCiGHeM</a></p>&mdash; _ (@apstndb) <a href="https://twitter.com/apstndb/status/1116309974064308225?ref_src=twsrc%5Etfw">2019年4月11日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[2] GAになってからはPATCH, OPTIONSも許可されたようです。
https://cloud.google.com/scheduler/docs/reference/rest/v1/projects.locations.jobs#Job


## 利用するサービス

前述の通りCloud Schedulerだけでは直接GCP上のリソースの設定を変更することは出来ないため以下のサービスを組み合わせて実現します。

__Cloud Scheduler__ Cloud Functionの関数を定期的に呼び出すためのスケジューラー
__Cloud Functions__ GCP上の各種リソースを設定変のAPIを実行する関数
__Service Account__ Cloud Functionsから設定変更を行うための権限付与


## Service Accountの作成

IAMと管理から、新たにサービスアカウントを作成して、Cloud Functionsが変更したいリソースに対する編集権限を付与します。
今回はfunction-serviceaccountというアカウントを作成し、GKEのノードプールのサイズを変更するため"Kubernetes Engine Cluster 管理者" の権限を付与します。

![スクリーンショット 2019-04-19 21.55.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/6dd07bd5-b826-7db5-0163-a278debc25e9.png)


## Cloud Functionsの作成

Cloud Functionで実際にGCP上のリソースの設定変更を行う関数を作成します。

関数の作成を行うと以下の画像のようにサンプルコードが用意されるので、これを元に関数を作成します。
今回はPythonを使用しますが、GoやNode.jsなどを使用することができます。
表示されているURLはCloud Schedulerが呼び出すエンドポイントとして使用するため控えておきます。

![スクリーンショット 2019-04-19 22.11.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/e5941672-7a1f-bd95-e645-23635206dcc9.png)

![スクリーンショット 2019-04-19 22.15.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/899a2c09-cf90-4816-be91-a1cfa6f4c3f4.png)

サンプルコードを編集しGKEのノードプールのサイズを変更する関数を作成します。
GCPのAPIのドキュメントには、HTTPでの直接APIを呼び出す方法のほか、各言語からの呼び出し方法のサンプルも記載されているため、これを参考にrequirements.txtとmain.pyの内容を書き換えます。

GKEのAPIドキュメント:
https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters.nodePools/setSize?hl=ja

foo-projectのasia-northeast1-aにあるbar-clusterのbaz-poolのノードプールのサイズを1に変更する関数のサンプルコードは以下の通りです。


```requirements.txt
# Function dependencies, for example:
# package>=version
oauth2client
google-api-python-client
```


```main.py
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
import logging

logging.getLogger().setLevel(logging.INFO)

def main(request):
    request_json = request.get_json()
    if request_json and 'password' in request_json:
        if request_json['password'] == 'dolphin':

            credentials = GoogleCredentials.get_application_default()

            service = discovery.build('container', 'v1', credentials=credentials)

            name = 'projects/foo-project/locations/asia-northeast1-a/clusters/bar-cluster/nodePools/baz-pool'

            set_node_pool_size_request_body = {
                "nodeCount": 1
            }

            request = service.projects().locations().clusters().nodePools().setSize(name=name, body=set_node_pool_size_request_body)
            response = request.execute()

            logging.info(str(response))
            return str(response)
    
    logging.info("not accept")
    return 'not accept'
```

コードの入力した後は、詳細オプションから先ほど作成したサービスアカウント設定します。
その後、作成ボタンを押すと関数が作成されます。

![スクリーンショット 2019-04-20 12.10.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/5ea8060c-0ff0-1491-9d93-606b20871318.png)



## Cloud Schedulerの作成

Cloud Schedulerで定期的にCloud Functionsを呼び出すスケジュールを作成します。

Cloud ConsoleにはCloud Schedulerを作成するページもありますが、詳細な設定を行うことができず、
API呼び出しの際のContent-Typeを指定する場合はgcloudコマンドを使用する必要があります。

ローカルPCにgcloudコマンドをインストールしてもよいのですが、今回はCloud Shellを使用します。
Cloud ShellはCloud Consoleのヘッダーから起動できます。

![スクリーンショット 2019-04-19 22.57.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/8aee9dce-60b0-0cfd-170c-e59b08f93b11.png)

Cloud Shellでコマンドを実行して、スケジュールを作成します。
以下は毎日2時に関数を実行するスケジュールの例です。uri部分にはCloud FunctionのURLを設定します。

```
$ gcloud beta scheduler jobs create http gke-scalein --schedule="0 2 * * *" --uri="https://****.cloudfunctions.net/function-1" --headers Content-Type=application/json --time-zone=Asia/Tokyo --message-body='{"password":"dolphin"}'
```

Cloud Schedulerの一覧に作成したスケジュールが表示されるので、"今すぐ実行"のボタンを押して動作を確認できます。

![スクリーンショット 2019-04-20 12.29.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82768/72a950b7-a66a-38f8-5273-93cd1e5a890b.png)