• HOME
  • /
  • 開発ノート
  • /
  • 速くて軽量なAPI、Starletteを使ってGCPにデプロイする Part4
python / Starlette
作成日:2020/3/2 更新日:2020/3/4

速くて軽量なAPI、Starletteを使ってGCPにデプロイする Part4

今回はPythonの便利なテストライブラリpytestを使ったテストを作成します。Starletteにテスト用のクライアントは実装されているのでそれを使って、前回追加した機能のテストを行い、テストが無事に通ったらGCPのCloudBuildを使ってCI/CDを行い、CloudRunへデプロイするまでを解説しています。

今回すすめるトピック

Gitでリポジトリをクローンする
docker-composeで依存しているミドルウェアを起動する
python main.pyでStarletteを起動する
APIのエンドポイントを追加する
バックグラウンドタスクを追加する
pytestで非同期処理をテストする
CI/CDはCloudBuildで利用して、継続的にテスト、CloudRunへデプロイできるようにする
CloudRunが起動しているのを確認したら削除する
PostgreSQL

追加機能のテスト作成とテストデータベースを準備

GUIアプリで新たにDBを作成、コピしてもよいですし、初期データとして用意していたfixtureをデータベース名の部分だけ変えて、コマンドで流しても作成するのでも良いです。まずはテスト用のデータベースを用意しましょう。





#!/bin/bash
psql -U docker -d docker << EOL
create database docker;
create table robots (id int PRIMARY KEY, name varchar, created_at timestamp);
insert into robots (id, name, created_at) values (1, 'GUIDO', '2020-01-01');
EOL


次に、新たに追加したGETメソッドのAPI、参照カウント数を1増やす機能のテストを追加しましょう。新たに追加したエンドポイントにGETでリクエストすると200を返すこと、モデルをインスタンスして、参照数+1カウントさせて、新しいインスタンスのカウント数が等しいことを確認します。
ここではstore内でセットしているDBから向き先を先程作ったDBへ変更してから接続しています。この内容であればわざわざ作らなくても良いと思いますが、誤って本番DBに接続していたなんてことが無いよう、書いておいたほうが心配しなくていいので安心です。


--- a/tests.py
+++ b/tests.py
@@ -1,8 +1,9 @@
 import pytest
 from starlette.testclient import TestClient
+from databases import Database, DatabaseURL
 
 from main import app
-from src import store
+from src import store, config
 
 
 class TestView:
@@ -14,12 +15,30 @@ class TestView:
             )
             assert response.status_code == 200
 
+    def test_retrieve(self):
+        with TestClient(app) as client:
+            response = client.get("/v1/robots/1")
+            assert response.status_code == 200
+
 
 class TestModel:
 
+    def setup_method(self, method):
+        store.database.url = DatabaseURL(config.TEST_DATABASE_URL)
+        self.test_db: Database = store.database
+
     @pytest.mark.asyncio
     async def test_retrieve(self):
-        await store.database.connect()
+        await self.test_db.connect()
         res = await store.Robot.retrieve({'robot_id': 1})
         assert res.name == 'GUIDO'
-        await store.database.disconnect()
+        await self.test_db.disconnect()
+
+    @pytest.mark.asyncio
+    async def test_count(self):
+        await self.test_db.connect()
+        old_robot = await store.Robot.retrieve({'robot_id': 1})
+        await old_robot.count_ref()
+        new_robot = await store.Robot.retrieve({'robot_id': 1})
+        assert old_robot.count + 1 == new_robot.count
+        await self.test_db.disconnect()

コードを実装したらテストを実行してみましょう。


(starlette-cloudrun) __ksh__@macbook-pro starlette-cloudrun % pytest -v -s
======================================================================================================================= test session starts =======================================================================================================================
platform darwin -- Python 3.7.6, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /Users/__ksh__/.local/share/virtualenvs/starlette-cloudrun-Dn0VzN5Q/bin/python
cachedir: .pytest_cache
rootdir: /Users/__ksh__/workspace/starlette-cloudrun, inifile: pytest.ini
plugins: asyncio-0.10.0
collected 4 items                                                                                                                                                                                                                                                 

tests.py::TestView::test_repair PASSED
tests.py::TestView::test_retrieve PASSED
tests.py::TestModel::test_retrieve PASSED
tests.py::TestModel::test_count PASSED

======================================================================================================================= 4 passed in 12.42s ========================================================================================================================

無事にテストをパスできましたか?
物足りない方は、住所、為替、国、都市などのAPIを作ってみるのもいいかもしれませんね。このあとのGCPサービスCloudRunで、過剰なアクセスがなければ毎月無料か、10円くらいで運用できると思うので、作成して公開してみるのも楽しいと思います😍

GitへプッシュしたタイミングでCI/CDを行う

それではCI/CDを行うために、まずはさきほどのテストDBの向き先を変えているので、CIでデータ参照できるようにTEST_DATABASE_URLをcloudbuild.yamlに追加しましょう。
BuildステップではfixtureでDB名がdockerと指定しているので、向き先をテスト実行したときにdockerになるようにします。そして、テストに使う初期データも仕様変更があったので編集しましょう。


--- a/cloudbuild.yaml
+++ b/cloudbuild.yaml
@@ -13,6 +13,7 @@ steps:
     env:
       - DEBUG=1
       - DATABASE_URL=postgresql://docker:docker@postgres/docker
+      - TEST_DATABASE_URL=postgresql://docker:docker@postgres/docker
   - id: "DEPLOY"
     name: "gcr.io/cloud-builders/gcloud"
     args:



--- a/src/fixtures/000-sample-data.sh
+++ b/src/fixtures/000-sample-data.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 psql -U docker -d docker << EOL
 create database docker;
-create table robots (id int PRIMARY KEY, name varchar, created_at timestamp);
-insert into robots (id, name, created_at) values (1, 'GUIDO', '2020-01-01');
+create table robots (id int PRIMARY KEY, name varchar, count bigint, created_at timestamp);
+insert into robots (id, name, count, created_at) values (1, 'GUIDO', 0, '2020-01-01');
 EOL

CloudBuildにリポジトリを接続してトリガーを作る

リポジトリを接続をクリックして次へ遷移する。

リポジトリ一覧から自分で作ったリポを選んでください。

保存したら一覧に戻って、該当のトリガーを実行するか、再度Gitになにか変更を加えてプッシュしてください。そうするとビルドが始まってステップが実行されます。

正常にビルドが進めば、テストをクリアしてデプロイは成功するものの、データベース接続エラーでアプリが正常でないため失敗になりますが、CloudRunの画面を見るとサービスが作成されているのが確認できます。

少し寄り道になりますが、GCPでRDBを使う場合はCloudSQLがオススメです。
CloudRunから簡単に接続できます。一旦デプロイ自体を成功してアプリを動かしたい場合はDBを使わない構成にして試してみるといいでしょう。もしくはCloudSQLでPostgreSQL11.6の最安プランで運用か、既存のDBからあればDATABASE_URLを接続情報を書き換えて接続してください。

Cloud SQL  |  Google Cloud
Cloud SQL は、リレーショナル データベース(PostgreSQL、MySQL、SQL Server)の設定、運用、管理を容易にするフルマネージド リレーショナル データベース サービスです。

最後にCloudRun画面から「新しいバージョンをデプロイ」を押して、接続設定を行い、稼働させましょう。

イメージの下らへんにあるCloudSQLへの接続と、アプリの環境変数を追加して、デプロイを行えば完了です。


CloudRunはエラー、ログ、メトリックス、課金もリクエスト毎で安いため、最も利用頻度の高いGCPサービスです。また、AnthosをGKEに追加することで、Kubernetes内のコンテナとして利用することもできますし、Appengine2nd,フレキシブルと違って、Knativeなのでデプロイがとても速く、Dockerさえ理解していれば扱えるので非常に便利です。
また、スケールアウトも自動ですし、バージョンへのトラフィックの移行、配分もできるAppengineの上位互換といったサービスかなと思います。
複数回、ご拝読ありがとうございました。次は最も得意なDjangoのノウハウやKubernetes,Istio関連の記事を単発で書いてみたいと思っています、興味があったら是非読んでみてください🤔

ksh3/starlette-cloudrun
Starlette framework on GCP CloudRun. Contribute to ksh3/starlette-cloudrun development by creating an account on GitHub.

今回の内容はこちらにコミットしています。

関連記事

python / Starlette
作成日:2020/3/2 更新日:2020/3/3
速くて軽量なAPI、Starletteを使ってGCPにデプロイする Part3
今回は実装したAPIにバックグラウンドで実行する非同期処理を実装します。 APIで受けとったリクエストにレスポンスを返した後、async/awaitを使って、非同期で処理を実行します。

ポリシー

この記事のすべてまたは一部の複製は、著作権者の同意なしでは禁止されています。 引用については著者名と記事のURLが表示されている場合に限り認められます。