ノンプロでもLet’s FaaS〜GCP cloud functionsの紹介〜

cloud functionsPython
この記事は約12分で読めます。

本記事は私の所属しているコミュニティ:ノンプログラマーのためのスキルアップ研究会 の『ノンプロ研 Advent Calendar 2020』15日目の記事です。

ノンプロ研 Advent Calendar 2020 - Adventar
プログラミング学習を行うノンプログラマーが集まる(メンバーによるカレンダーです。ハッシュタグは #ノンプロ研アドベントカレンダー です。

頑張って今年もアドヴェントやってますので、ぜひノンプロメンバーの記事をいろいろ読んでみてください。一年の振り返りから、いろいろ試行されたメンバーの発見なんかも知見になっているかと思います。

さて、私もノンプロメンバーなので、今年はアドベント記事を1つ書くことになりました。正直ネタがねぇなぁ。。。ってなっていたので、ちょっとみなさんに知ってもらってもいいかなという内容をテーマに記事を書くことにしました。結構個人的には力作ですw

FaaSって便利でっせって感じの記事と、GCP cloud functionsを利用したサンプルの紹介です。では、いってみましょうw

スポンサーリンク
もしも_楽天

そもそもFaaSって何?

IaaS・PaaS・SaaSまでは聞いたことあるけど、FaaSってなんだよ、ってかXaaSって増えすぎじゃない?って思うノンプロのみなさん、正しいですw XaaSは今もいろんなパターンが増えてますw BaaS(Backend as a service)、MaaS(mobility as a service)、CaaS(container as a servie)など、XaaSは毎年のように増えていってますw

そこの中からノンプロでも利用する価値がありそうなサービスとして、FaaS(Function as a service)を紹介していきたいと思います。

FaaSの概要

FaaSは「Function as a Service」の略で、サーバレスで利用できるクラウドサービスのことです。

https://it-trend.jp/paas/article/301-0019

とりあえず、引用から開始しましたが、まー、ざっくりとはサーバレスで利用できるクラウドって理解では大枠としては正解です。ただ、それだと他にもあるよねってなります。

FaaSの特徴としては、関数(function)だけを開発者側が管理すると言う点がざっくりざっくりの理解ではいいのではないかなぁと思います。

また、イベントが発生したら、関数を実行してくれるというのが基本的な作りです。これってどこかで聞いたことありません?

ざっくりいってしまうと、webhookみたいなものだと理解してください(かなりざっくりですが汗)。ほら、ノンプロでも使えそうw

FaaS紹介:GCP cloud functions

GCPで提供されているcloud functionsの説明などは以下のページをご参照ください。

Cloud Functions のドキュメント  |  Google Cloud Functions に関するドキュメント
単一目的の小規模な関数。

その他のメジャーなFaaS

hook.io

FaaSのはしりとも言われているhook.ioです。webhookというフレーズはよくslackとかでも聞きませんか?

Buddy Pond
Buddy Pond - Cloud OS and Instant Messenger all wrapped up in one delightfully fun to use App. We are making the Internet Fun Again.

AWS lamda

AWSが提供するFaaSです。ストレージ等をAWSご利用の方は、lamdaをご利用いただくといいかなと思います。

AWS Lambda(イベント発生時にコードを実行)| AWS
AWS Lambda を使用すれば、サーバーのプロビジョニングや管理なしでコードを実行できます。課金は実際に使用したコンピューティング時間に対してのみ発生し、コードが実行されていないときには料金も発生しません。

GCP cloud functionsを使って定期実行してみよう

対象とするのは以下の記事で紹介したtdnetのAPIにしてみましょう。

利用するのは以下の2つです。

・cloud functions(python3.8)
・cloud scheduler(イベント発生用、localのcron相当だと思ってください)

functionsを動かすためにschedulerでpub/subを経由して、event発生させて、動作させていきます。

動作イメージとしては以下のような形です。

functionsで利用するコードの紹介

基本は参照記事で紹介したコードと同様なのですが、functionsで実行するために一部実行部分のコードをif __main__からdef(関数)に変更しています。なぜ変更する必要があるかは、コードみた後に説明します。

import requests
import pprint
import json


def get_data_from_yanoshin():
    """
    yanoshin APIからデータを100件取得してcontentを返す
    :return: dict
    """
    base_url = "https://webapi.yanoshin.jp/webapi/tdnet/list/"

    condition = "today"
    format = "json"

    query = "limit=100"

    res = requests.get(base_url+condition+'.'+format+'?'+query)

    res_loads = json.loads(res.content)
    pprint.pprint(res_loads)


    return res_loads


def pickup_growth_possibility_material(data):
    """
    成長可能性に関する説明資料
    :param data: dict
    :return: list
    """
    output = []
    for x in data['items']:
        if x['Tdnet']['title'] == "成長可能性に関する説明資料":
            output.append([x['Tdnet']['company_name'], x['Tdnet']['document_url']])

    return output




def send_email(data):
    # メール送信関係
    import smtplib
    from email.mime.text import MIMEText
    from email.header import Header

    from_adress = "gmail_adress"  # 送付もとメールアドレス、gmailを前提
    from_adress_pass = "app pass"  # Google accountから発行されるアプリパスワードに変更(2要素認証)
    to_adress = "gmail_adress"  # 受信メールアドレス

    string = ""

    if len(data):
        for i in range(len(data)):
            string = string + str(data[i][0]) + "\n" + str(data[i][1])
    else:
        string = "本日は対象データがありません"



    msg = MIMEText("新規成長性説明資料。\n\n" + string, "plain")
    msg["Subject"] = Header("from_bot 成長性説明資料")

    smtp_obj = smtplib.SMTP("smtp.gmail.com", 587)
    smtp_obj.ehlo()

    smtp_obj.starttls()

    smtp_obj.login(from_adress, from_adress_pass)
    smtp_obj.sendmail(from_adress, to_adress, msg.as_string())
    smtp_obj.quit()



def event_call(event, context):
    data = get_data_from_yanoshin()
    pickup_data = pickup_growth_possibility_material(data)
    send_email(pickup_data)

なぜ__main__ではダメなのか?

さて、今回利用しているのはあくまでFaaSです。Function as a serviceということで、関数を利用するものです。そのため、実行する対象として指定するのはあくまで関数になります。

ということは、__main__だとファイル自体を呼び出した時に、どれを実行するかというだけで、明確にどの関数と指定ができません。なので、今回はdef event_callとして実行関数を定義しています。

実行関数で利用する引数は固定してください

さて、もう一個注意点として、実行関数として利用するevent_call関数には今回eventとcontextを引数として渡しています。これは、使いはしないのですが、pub/subを経由したschedulerの実行においては、必要な引数なので、そのまま設定しておいてください。

本来的にはeventやcontext(schedulerでpayloadとして渡すもの)などを活用してfunctionsを活用するとより便利ですが、今回はcronの代替としてcloud functionsを利用しますので、活用していません。興味ある方は調べてみてください。

Cloud Functions:実際の設定内容

さて、コードの部分だけ説明してきたので、あとは実際の設定についてもみていきます。

1st step: cloud functionsの立ち上げ

まずは、GCPにログインして、利用するプロジェクトを選択した上で、メニューバーからcloud functionsを選択します。

2nd step: cloud functionsの構成設定

関数名は自由に付けてもらってOKです。

トリガーだけ、気をつけてください。今回のケースではpub/subを利用しますので、cloud pub/subを選択してtopicを作成してください。

このtopicの名前が後ほどschedulerで設定するtopic名になります。別に名前自体はなんでもいいですが、schedulerでも利用するので忘れなければOKです(別に忘れても後から見返せばいいだけですけどw)

設定したら、【次へ】をクリックします。

3rd Step: cloud functions コードの設定

コードの設定に関しては、以下のような画面で登録します。今回はcloud source repositoryは使わず、インラインエディタ(GUIで直接編集)でコードを登録していきます。

さて、コードの設定で注意する点は以下です。

ランタイムの選択

Go, Java, Node.jsなどが選べます。pythonは3.7と3.8が対象となっています。今回は3.8を利用します。

エントリポイントの選択

エントリポイントというのが、実行する関数名になります。そのため、今回はevent_call関数を選択します。ここはドロップダウンとかではないので、間違えないようにコピペしとくのが安全です。

ソースコードの構成

さて、ここも一点注意事項があります。functionsではpipなどのライブラリ管理ツールが使えません。というか、root directoryにあるrequirements.txtを読み込んでライブラリ管理を行います。

そのため、ローカル環境でコードを作成した後、以下のようなコマンドをターミナルで実行して、requirmetns.txtファイルを作成しておくことをお勧めします。

pip freeze > requirements.txt

これで、作ったファイルをコピペします。

なお、別の.pyファイルを使ってコードを作ってインポートしたい場合は、以下のソースコードの構造というページをご確認ください。

Cloud Functions の関数を作成する  |  Google Cloud Functions に関するドキュメント

4th Step: cloud schedulerの設定

さて、次はcloud schedulerの設定です。メニューバーから、cloud schedulerに移動していることを前提に解説していきます。この画面をfunctionsで探してないぞーって文句言うのはやめてくださいw

ジョブを作成と言うボタンをクリックすると以下のような画面が表示されます。

この画面で、以下のように入力していきます。肝となるのは頻度・ターゲット・トピックの3点です。

それでは、早速肝となる頻度などの設定方法について紹介していきます。ここら辺はcron知ってる方だとイメージは非常にしやすいと思います。

Cloud scheduler: 頻度の設定

頻度の設定は、まさにcronの設定そのものです。慣れている方は普通のcronだと思って設定してもらえればOKです。cronに慣れていない方は以下のイメージで設定していきましょう。ちなみにこの形式をUnix-cron形式っていいます。

cron設定 各要素の意味

*5つを使って日時を設定していきますが、それぞれの意味合いは上の画像の通りです。各項目は以下の設定が可能です。

項目有効な値の形式
0-59
時間0-23
1-31
1-12
曜日0-6(日曜日から土曜日)
Cloud scheduler: ターゲットの設定

選択肢は以下の3つが与えられています。今回はその中から、Pub/Sub(publisher/subscriber)を利用します。メッセージング経由で実行する形です。

対象実行手段
HTTPジョブが接続するエンドポイントの完全修飾 URL を指定、 methodはPOST
Pub/Subジョブの公開先トピックの名前を指定
App Engine HTTPジョブが接続する App Engine エンドポイントの相対 URL を指定。デフォルト値 / を使用する場合、ジョブは PROJECT-ID.appspot.com を使用
ターゲットの選択肢

個人的にはApp Engine HTTPで使うケースが多いですが、Pub/Subも結構利用します。

Cloud scheduler: トピックの設定

今回のcloud schedulerとcloud functionsを連携させるための肝となるトピックの設定となります。

2nd step: cloud functionsの構成設定で設定した、トピック名をここで指定します。ここ間違えると動きませんのでご注意を。

実行結果を確認する

最後は実行結果の確認です。一応コードの最終結果はメールで送られてくることになるので、表示されるメールをみていきます。実際には新規上場会社が11月末から12月15日までないので、ブランクの結果が返ってきています。(この記事の公開日はアドベントの関係上12/15ですが、書いてるのは12月上旬でして。。)

一応メールが飛んできていますよ、って言うくらいの結果報告です(実行はテスト実行のものです)

まとめ

さて、アドベントカレンダー記事として、FaaSサービスの紹介と実際例として、GCP Cloud Functionsを利用したスクリプトや設定を紹介させていただきました。

ちょっと一記事に入れ込む内容ではなくて、かなーり長くなってしまいました。。まぁご容赦ください。

年明け以降で、ノンプロメンバーやノンプログラマーの皆さんの中で、一人でも多くの方がFaaSに興味を持ってもらって、実際に触ってみてもらえたら個人的には嬉しいです。GASもいいですが、今の時代多くの選択肢が提供されていますので、いろいろな選択肢を少しづつ触れていくとやれる範囲が広がっていくかなと思います。

そんなかんなで、アドベントカレンダー15日目の記事でした。最後までお読みいただいた皆様、ありがとうございました。