【GCP】Cloud Functions (python)をCloud Build でdeploy する


# はじめに

Cloud Functions を python で使ってみるための調査をしました。いまどき、サーバレスでなくてはね、ということで、老体に鞭を打ち、休日に作業しています。戦いの証をこちらに掲げています。

https://github.com/xtkd77/XPT60-memorandum/tree/main/2022_01_16_python_cloudfunctions

# Quick Start から CDまで

## まずはQuick Start 

Quick start は、コンソールだけでdeploy するところまでできます。テストも楽にできるんだなーというのが、印象的です。

https://cloud.google.com/functions/docs/quickstart-python

その後、gcloud による操作をここを読んでざっと理解しました。

https://cloud.google.com/functions/docs/quickstart#functions_quickstart_helloworld-python


これらをもとに、Cloudbuild でCI/CDできるように頑張りました。

## 作業内容

### 準備

準備します。Python3.9 にしているのは、使ってみたいからです。

```
$ virtualenv -p python3.9 venv
$ source ./venv/bin/activate
(venv) $ $ python -V

Python 3.9.5
```

ローカルで動かすために必要なものを入れます。

```
$ python -m pip install functions-framework
...
# python -m pip list
$ python -m pip list
Package                        Version
------------------------------ ---------
...
Flask                          2.0.2
functions-framework            3.0.0
google-api-core                2.4.0
google-auth                    2.3.3
grpc-google-iam-v1             0.12.3
grpcio                         1.43.0
...
```

という感じでした。


### ローカルで動かす


関数 my_hello_http が実装してあるフォルダで、function-framework を実行します。
実装はサンプルにあるもののコピペです。

```python:main.py
from flask import escape
import functions_framework

@functions_framework.http
def my_hello_http(request):
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'name' in request_json:
        name = request_json['name']
    elif request_args and 'name' in request_args:
        name = request_args['name']
    else:
        name = 'World'
    return 'Hello {}!'.format(escape(name))
```

これを functions-framework で実行します。

```
$ functions-framework --target my_hello_http --signature-type=http --port=8080 --debug
 * Serving Flask app 'hello_http' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on all addresses.
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://172.25.125.68:8080/ (Press CTRL+C to quit)
 * Restarting with watchdog (inotify)
 * Debugger is active!
 * Debugger PIN: 111-222-666
```

別のターミナルで接続してみる。

```
$ curl -H "accept: application/json" -H "Content-Type: application/json" -d '{"name": "baby."}' http://172.25.125.68:8080/my_hello_sample
Hello baby.!
```

動いた。よしよし。


### 手動で Deploy

deploy は gcloud functions deploy で行ける。覚えられるかな。

```shell
$ gcloud functions deploy my_hello_http --runtime python39 --trigger-http --allow-unauthenticated

Deploying function (may take a while - up to 2 minutes)...⠛
For Cloud Build Logs, visit: https://console.cloud.google.com/cloud-build/builds;region=us-central1/xxxxxxxxxxxxxxxxxxxxx?project=1234567890123
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
buildId: xxxxxxxxxxxxxxxxxxxxxxxxxxx
buildName: projects/1234567890123/locations/us-central1/builds/xxxxxxxxxxxxxxxxxxxxxxxxxxx
entryPoint: my_hello_http
httpsTrigger:
  securityLevel: SECURE_ALWAYS
  url: https://us-central1-myproject.cloudfunctions.net/my_hello_http
ingressSettings: ALLOW_ALL
labels:
  deployment-tool: cli-gcloud
name: projects/myproject/locations/us-central1/functions/my_hello_http
runtime: python39
serviceAccountEmail: ambmonitordev@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/gcf-upload-us-central1-yyyyyyyyyyyyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzzzzzz.zip
status: ACTIVE
timeout: 60s
updateTime: '2022-01-16T11:16:18.258Z'
versionId: '1'
```

内部ではどのように動いているのかな。build したり、storage の設定が出てきたり、entryPoint の文字が見えたり。
この情報は、以下でも表示できる。

```
$ gcloud functions describe my_hello_http
```

deploy しているserverless application を忘れたら、list する。

```
$ gcloud functions list
NAME           STATUS  TRIGGER       REGION
my_hello_http  ACTIVE  HTTP Trigger  us-central1
```

### CloudBuild でDeploy

今のコマンドをcloudbuild.yaml に書いてみる。

https://cloud.google.com/build/docs/deploying-builds/deploy-functions

```
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  args:
  - gcloud
  - functions
  - deploy
  - my_hello_http
  - --region=asia-northeast1
  - --source=.
  - --trigger-http
  - --runtime=python39
```

とりあえずローカルで実行してみる。今、main.py と同じディレクトリで下記を実行することを想定していますが、そうでなければ"source=..."の値を書き換えてください。

```
$ gcloud builds submit --config cloudbuild.yml .
```

がエラーになってしまった。

```
- '@type': type.googleapis.com/google.rpc.ErrorInfo
  domain: googleapis.com
  metadata:
    consumer: projects/12345678901234
    service: cloudresourcemanager.googleapis.com
  reason: SERVICE_DISABLED
ERROR
```

と言われたので、Cloud Resouce Manager API を有効にしました。そうしたら成功はしたのですが、

```
Deploying function (may take a while - up to 2 minutes)...
WARNING: Setting IAM policy failed, try `gcloud alpha functions add-iam-policy-binding my_hello_http --region=asia-northeast1 --member=allUsers --role=roles/cloudfunctions.invoker`
```

と言われたので、素直にコマンドを叩いておきました。

```
$ gcloud alpha functions add-iam-policy-binding my_hello_http --region=asia-northeast1 --member=allUsers --role=roles/cloudfunctions.invoker
bindings:
- members:
  - allUsers
  role: roles/cloudfunctions.invoker
etag: BwXVssdV2mE=
version: 1
```

いよいよCloud Build にtrigger を作ります。いろいろエラーが出たのですが、

- CloudBuild のサービスアカウントに CloudFunctions 開発者のロールが無効だった。これはコンソールのCloudBuild の設定から変更できます。
- build trigger を実行するサービスアカウントの権限がLogging になかった。これはせってしない方が良かったのかもしれない。

で最後は無事にdeploy できました!! (^^)/


# まとめ

GCPのサーバレスをpython で利用するために、Cloud Functions を python 使い方を調査し、動作確認をとった。

- ローカルで動かすことも可能。(functions-)
- CloudBuild から deploy 可能

いろいろ作っていくのか。location(region?) が違うと別のものとして扱われているようだった。

来週も生き延びれるかな。。。


### メモ

- 参考にさせていただきました。PubSubもさくっと動作確認されている。

https://qiita.com/morita-toyscreation/items/e5d62673c3c5fafbdbbc

- ローカルに作ったパッケージを呼び出す。

https://cloud.google.com/functions/docs/writing/specifying-dependencies-python

- google 様のサンプル: python-doc-samples/functions/ 以下に全部ある。

https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/functions

- gcloud の使い方入門

https://cloud.google.com/functions/docs/quickstart#functions-deploy-command-python