• HOME
  • /
  • ARTICLES
  • /
  • 速くて軽量なAPI、Starletteを使ってGCPにデプロイする Part4
python / Starlette
Created at:3/2/2020 Updated at:3/4/2020

速くて軽量な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.

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

Related Articles

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

Policy

Reproduction or reproduction of all or part of this article is prohibited without the consent of the copyright holder. However, citations are only accepted if the author and article URLs are displayed.