GAミント至上主義

Web Monomaniacal Developer.

Google Cloud DatalabでPython3が使えるようになってた

自社では、BigQueryのデータの分析とかには主にGoogle Cloud Datalabを使っている。

cloud.google.com

現在のドキュメントでは、GCEを使う方法しかのっていないが、公開当初はローカルのdockerで動かせる方法が書いてあって、現在も下記のようなコマンドで利用可能です。

sudo docker run -d -p "8080:8080" \
    -v "/path/to/content:/content" \
    gcr.io/cloud-datalab/datalab:local-20170928

他のイメージも下記から確認できる。要ログイン?

https://console.cloud.google.com/gcr/images/cloud-datalab/GLOBAL

ローカル(実際には社内サーバー)とGCE版の使い分けは、軽いやつはローカル、大量のメモリ等のリソースを使う場合はGCEとしています。

メモリ50GB以上のマシンもちょっと使うだけなら月数千円で使えるので、買うより便利。


Python2,3の切り替えは下記で可能です
f:id:uyamazak:20170929133240p:plain

GoogleのCloud Pub/SubをやめてRedisのPub/Subに戻した話

要点を言えば、公式Pythonライブラリでメモリの問題が2回発生した上に、バージョンアップでさらにコントロールできないものになったので使うのをやめた。


自社用データ収集のプロジェクトoceanusでは、データをBigQueryに保存するだけでなく、データのリアルタイム処理、ストリーミング処理用にPub/Subにも送信している。

github.com

例えば、コンバージョンとか特定のイベントが来たらメールするとか、Googleスプレッドシートに書き込むとか。


開発してからしばらくは同一ネットワーク内(GKE)に自分で立てたRedisのPub/Subで行っていたが、Google Cloud Pub/Subの方が、自分でスケーリングとか考えなくていいし、どこからでもアクセスできたり、他のGCPサービス(Cloud Functions)と連携もしやすいので移した。

しかし、Cloud Pub/Subの公式Pythonライブラリは曲者(当時はバージョン0.25、2017/9/27現在0.28.3)で、メモリリークはしまくるし、特殊なライブラリが必要なようで、Dockerを使っているがイメージサイズの小さいalpine系のPythonでは動かくことができず、Debianベースのイメージを使う必要もあった。

メモリの使用量は同じマシンで稼働できるコンテナの数と直結するので、コスト面での影響もかなり大きい。


google-cloud-python/pubsub at master · GoogleCloudPlatform/google-cloud-python · GitHub


メモリリークは、別プロセスを立ち上げてしばらくしたら消して再起動する方法でなんとかなったけど、精神的に大分消耗した。


uyamazak.hatenablog.com



しばらくして、公式のライブラリのアップデート(0.28)があり、イメージ新しくした際に動かなくなったので、書き直すことにした。

これが大幅な変更で、理解するのにも時間がかかった。コミットメッセージは「Pub/Sub API Redesign」となっている。


Pub/Sub API Redesign (#3859) · GoogleCloudPlatform/google-cloud-python@4a8e155 · GitHub


特に大きいのが、メッセージを受け取る処理のSubscribeで、これまでは1件ずつメッセージ取り出してこちらで処理ができたが、コールバック関数を渡して処理させる形となった。

# Define the callback.
# Note that the callback is defined *before* the subscription is opened.
def callback(message):
    do_something_with(message)  # Replace this with your actual logic.
    message.ack()

# Open the subscription, passing the callback.
subscription.open(callback)

その処理は非同期(non-blocking)のため、そのあとにスリープを行うことで、続けてメッセージを処理することができるとなっている。
サンプルだと下記のように、最後でsleepの無限ループを行っている。

最初スリーブしているのに処理は半永久的に進むのが不思議だった。

def receive_messages(project, subscription_name):
    """Receives messages from a pull subscription."""
    subscriber = pubsub_v1.SubscriberClient()
    subscription_path = subscriber.subscription_path(
        project, subscription_name)

    def callback(message):
        print('Received message: {}'.format(message))
        message.ack()

    subscriber.subscribe(subscription_path, callback=callback)

    # The subscriber is non-blocking, so we must keep the main thread from
    # exiting to allow it to process messages in the background.
    print('Listening for messages on {}'.format(subscription_path))
    while True:
        time.sleep(60)

python-docs-samples/subscriber.py at master · GoogleCloudPlatform/python-docs-samples · GitHub

私の書いているoceanus/revelationでほぼ同じように処理を行ってみると、社内サーバーでは問題なく動いたが、本番環境のGKEに乗せるとメモリを1G以上も使い果たし、他のプロセスも停止させてしまった。

FlowControlという処理制御用のオブジェクトがあったので、max_messagesは1にしてみたが、変わらない。

FlowControl.__new__.__defaults__ = (
    psutil.virtual_memory().total * 0.2,  # max_bytes: 20% of total RAM
    float('inf'),                         # max_messages: no limit
    0.8,                                  # resume_threshold: 80%
)

デフォルトでメモリ制限っぽい項目もあるようだが、3GB以下のマシンで最大2GBにも達していたから機能していなさそうだ。

google-cloud-python/types.py at master · GoogleCloudPlatform/google-cloud-python · GitHub


多い時で20/秒程度のメッセージが来ているので、おそらく大量のスレッドが立ち上がったのが原因だと思われる。が、そこの制御方法は見つからず、サービスを止めとくのも問題なので、調査はやめて、過去のRedis版に切り戻した。

大量のメッセージを受け取って、フィルターしてほとんど捨てる的な使い方は想定されていないのかもしれない。

Goで書いたPublisher側は特に問題がないので、Pythonクライアント特有なのかもしれないが、1年以内でメモリの問題が複数発生し、またクライアントライブラリも破壊的な変更が入るなど安定していないので、しばらくこの用途で使うのはやめようと思った。

Google Pub/Sub自体あまりユーザーがいないのかもしれない。

Googleも普通にRedisをホスティングしてくれればいいのに。

早速App Engineのmanaged SSLを有効化してみる

Let's Encryptから大きくなったSSL自動化の流れから、いつか来るだろうと思ってたのがついに来た。さっそく有効にした。

cloudplatform.googleblog.com

既存のプロジェクトはCloud Consoleで設定が必要。簡単だった。

f:id:uyamazak:20170919101409p:plain

証明書は思っていた通りLet's Encrypt発行のものだった。


今度は、HTTPロードバランサーの方も対応してくれるのを待つ。