chaingng / shoeisha_serverless_python_tutorial

翔泳社「動かして学ぶ!Pythonサーバレスアプリ開発入門 」サンプルコード

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

「動かして学ぶ!Pythonサーバレスアプリ開発入門」 サンプルコード

動かして学ぶ!Pythonサーバレスアプリ開発入門」のサンプルコードです。

動かして学ぶ!Pythonサーバレスアプリ開発入門

Table of Contents

概要

  • application: 最終版(本書読了時)のサーバレスBlogアプリケーション
  • serverless-bot: 最終版(本書読了時)のサーバレスBot
  • snippets: 本文中のリストのスニペット(例:リスト6.2 -> list.6.2.txt)
  • chapXX: 各章終了時のサンプルアプリケーション

zappa_settings.jsonzappa_settings.json.sampleとしてサンプルを置いております。

誤字

環境変数 (誤)AWS_ACEESS_KEY_ID -> (正)AWS_ACCESS_KEY_ID

変更後の箇所は以下になります。(詳細はこちらをご覧ください)

  1. ~/.bashrc
AWS_ACCESS_KEY_ID = 'AWS_ACCESS_KEY_ID'
  1. application/flask_blog/models/entries.py
aws_access_key_id = app.config.get('AWS_ACCESS_KEY_ID')
  1. application/flask_blog/models/sessions.py
aws_access_key_id = app.config.get('AWS_ACCESS_KEY_ID')
  1. application/flask_blog/config.py
class DevelopmentConfig(Config):
    ....
    AWS_ACCESS_KEY_ID = 'AWS_ACCESS_KEY_ID'
    ....
    SESSION_DYNAMODB_KEY_ID = AWS_ACCESS_KEY_ID
    ....

class ProductionConfig(Config):
    ....
    AWS_ACCESS_KEY_ID = os.environ.get('SERVERLESS_AWS_ACCESS_KEY_ID')
    ....
    SESSION_DYNAMODB_KEY_ID = AWS_ACCESS_KEY_ID

本書で利用しているライブラリの全バージョン

本書で利用しているライブラリの全バージョンです。

ライブラリのインストール時にコンフリクトが発生する際は、本バージョンを参考に以下のコマンドでバージョンを指定してインストールしてください。

# 例
pipenv install "flask-login==0.5.0"

Chapter1~9

Flask-Login==0.5.0
  - Flask [required: Any, installed: 1.1.2]
    - click [required: >=5.1, installed: 7.1.2]
    - itsdangerous [required: >=0.24, installed: 1.1.0]
    - Jinja2 [required: >=2.10.1, installed: 2.11.3]
      - MarkupSafe [required: >=0.23, installed: 1.1.1]
    - Werkzeug [required: >=0.15, installed: 0.16.1]
Flask-Script==2.0.6
  - Flask [required: Any, installed: 1.1.2]
    - click [required: >=5.1, installed: 7.1.2]
    - itsdangerous [required: >=0.24, installed: 1.1.0]
    - Jinja2 [required: >=2.10.1, installed: 2.11.3]
      - MarkupSafe [required: >=0.23, installed: 1.1.1]
    - Werkzeug [required: >=0.15, installed: 0.16.1]
Flask-Sessionstore==0.4.5
  - Flask [required: >=0.8, installed: 1.1.2]
    - click [required: >=5.1, installed: 7.1.2]
    - itsdangerous [required: >=0.24, installed: 1.1.0]
    - Jinja2 [required: >=2.10.1, installed: 2.11.3]
      - MarkupSafe [required: >=0.23, installed: 1.1.1]
    - Werkzeug [required: >=0.15, installed: 0.16.1]
pynamodb==5.0.3
  - botocore [required: >=1.12.54, installed: 1.20.62]
    - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
    - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
      - six [required: >=1.5, installed: 1.15.0]
    - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
zappa==0.52.0
  - argcomplete [required: Any, installed: 1.12.3]
  - boto3 [required: Any, installed: 1.17.62]
    - botocore [required: >=1.20.62,<1.21.0, installed: 1.20.62]
      - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
      - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
        - six [required: >=1.5, installed: 1.15.0]
      - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
    - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
    - s3transfer [required: >=0.4.0,<0.5.0, installed: 0.4.2]
      - botocore [required: >=1.12.36,<2.0a.0, installed: 1.20.62]
        - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
        - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
          - six [required: >=1.5, installed: 1.15.0]
        - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
  - durationpy [required: Any, installed: 0.5]
  - future [required: Any, installed: 0.18.2]
  - hjson [required: Any, installed: 3.0.2]
  - jmespath [required: Any, installed: 0.10.0]
  - kappa [required: ==0.6.0, installed: 0.6.0]
    - boto3 [required: >=1.2.3, installed: 1.17.62]
      - botocore [required: >=1.20.62,<1.21.0, installed: 1.20.62]
        - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
        - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
          - six [required: >=1.5, installed: 1.15.0]
        - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
      - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
      - s3transfer [required: >=0.4.0,<0.5.0, installed: 0.4.2]
        - botocore [required: >=1.12.36,<2.0a.0, installed: 1.20.62]
          - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
          - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
            - six [required: >=1.5, installed: 1.15.0]
          - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
    - click [required: >=5.1, installed: 7.1.2]
    - placebo [required: >=0.8.1, installed: 0.9.0]
    - PyYAML [required: >=3.11, installed: 5.4.1]
  - pip [required: >=9.0.1, installed: 21.1.2]
  - pip-tools [required: Any, installed: 6.1.0]
    - click [required: >=7, installed: 7.1.2]
    - pep517 [required: Any, installed: 0.10.0]
      - toml [required: Any, installed: 0.10.2]
    - pip [required: >=20.3, installed: 21.1.2]
  - python-dateutil [required: Any, installed: 2.8.1]
    - six [required: >=1.5, installed: 1.15.0]
  - python-slugify [required: Any, installed: 5.0.0]
    - text-unidecode [required: >=1.3, installed: 1.3]
  - PyYAML [required: Any, installed: 5.4.1]
  - requests [required: >=2.20.0, installed: 2.25.1]
    - certifi [required: >=2017.4.17, installed: 2020.12.5]
    - chardet [required: >=3.0.2,<5, installed: 4.0.0]
    - idna [required: >=2.5,<3, installed: 2.10]
    - urllib3 [required: >=1.21.1,<1.27, installed: 1.26.4]
  - six [required: Any, installed: 1.15.0]
  - toml [required: Any, installed: 0.10.2]
  - tqdm [required: Any, installed: 4.60.0]
  - troposphere [required: Any, installed: 2.7.1]
    - cfn-flip [required: >=1.0.2, installed: 1.2.3]
      - Click [required: Any, installed: 7.1.2]
      - PyYAML [required: >=4.1, installed: 5.4.1]
      - six [required: Any, installed: 1.15.0]
  - Werkzeug [required: <1.0, installed: 0.16.1]
  - wheel [required: Any, installed: 0.36.2]
  - wsgi-request-logger [required: Any, installed: 0.4.6]

Chapter10~11

gspread==3.7.0
  - google-auth [required: >=1.12.0, installed: 1.30.0]
    - cachetools [required: >=2.0.0,<5.0, installed: 4.2.2]
    - pyasn1-modules [required: >=0.2.1, installed: 0.2.8]
      - pyasn1 [required: >=0.4.6,<0.5.0, installed: 0.4.8]
    - rsa [required: >=3.1.4,<5, installed: 4.7.2]
      - pyasn1 [required: >=0.1.3, installed: 0.4.8]
    - setuptools [required: >=40.3.0, installed: 57.4.0]
    - six [required: >=1.9.0, installed: 1.15.0]
  - google-auth-oauthlib [required: >=0.4.1, installed: 0.4.4]
    - google-auth [required: >=1.0.0, installed: 1.30.0]
      - cachetools [required: >=2.0.0,<5.0, installed: 4.2.2]
      - pyasn1-modules [required: >=0.2.1, installed: 0.2.8]
        - pyasn1 [required: >=0.4.6,<0.5.0, installed: 0.4.8]
      - rsa [required: >=3.1.4,<5, installed: 4.7.2]
        - pyasn1 [required: >=0.1.3, installed: 0.4.8]
      - setuptools [required: >=40.3.0, installed: 57.4.0]
      - six [required: >=1.9.0, installed: 1.15.0]
    - requests-oauthlib [required: >=0.7.0, installed: 1.3.0]
      - oauthlib [required: >=3.0.0, installed: 3.1.0]
      - requests [required: >=2.0.0, installed: 2.25.1]
        - certifi [required: >=2017.4.17, installed: 2020.12.5]
        - chardet [required: >=3.0.2,<5, installed: 4.0.0]
        - idna [required: >=2.5,<3, installed: 2.10]
        - urllib3 [required: >=1.21.1,<1.27, installed: 1.26.4]
oauth2client==4.1.3
  - httplib2 [required: >=0.9.1, installed: 0.19.1]
    - pyparsing [required: >=2.4.2,<3, installed: 2.4.7]
  - pyasn1 [required: >=0.1.7, installed: 0.4.8]
  - pyasn1-modules [required: >=0.0.5, installed: 0.2.8]
    - pyasn1 [required: >=0.4.6,<0.5.0, installed: 0.4.8]
  - rsa [required: >=3.1.4, installed: 4.7.2]
    - pyasn1 [required: >=0.1.3, installed: 0.4.8]
  - six [required: >=1.6.1, installed: 1.15.0]
pytz==2021.1
slack-sdk==3.5.1
zappa==0.52.0
  - argcomplete [required: Any, installed: 1.12.3]
  - boto3 [required: Any, installed: 1.17.62]
    - botocore [required: >=1.20.62,<1.21.0, installed: 1.20.62]
      - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
      - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
        - six [required: >=1.5, installed: 1.15.0]
      - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
    - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
    - s3transfer [required: >=0.4.0,<0.5.0, installed: 0.4.2]
      - botocore [required: >=1.12.36,<2.0a.0, installed: 1.20.62]
        - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
        - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
          - six [required: >=1.5, installed: 1.15.0]
        - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
  - durationpy [required: Any, installed: 0.5]
  - future [required: Any, installed: 0.18.2]
  - hjson [required: Any, installed: 3.0.2]
  - jmespath [required: Any, installed: 0.10.0]
  - kappa [required: ==0.6.0, installed: 0.6.0]
    - boto3 [required: >=1.2.3, installed: 1.17.62]
      - botocore [required: >=1.20.62,<1.21.0, installed: 1.20.62]
        - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
        - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
          - six [required: >=1.5, installed: 1.15.0]
        - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
      - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
      - s3transfer [required: >=0.4.0,<0.5.0, installed: 0.4.2]
        - botocore [required: >=1.12.36,<2.0a.0, installed: 1.20.62]
          - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
          - python-dateutil [required: >=2.1,<3.0.0, installed: 2.8.1]
            - six [required: >=1.5, installed: 1.15.0]
          - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.4]
    - click [required: >=5.1, installed: 7.1.2]
    - placebo [required: >=0.8.1, installed: 0.9.0]
    - PyYAML [required: >=3.11, installed: 5.4.1]
  - pip [required: >=9.0.1, installed: 21.2.4]
  - pip-tools [required: Any, installed: 6.1.0]
    - click [required: >=7, installed: 7.1.2]
    - pep517 [required: Any, installed: 0.10.0]
      - toml [required: Any, installed: 0.10.2]
    - pip [required: >=20.3, installed: 21.2.4]
  - python-dateutil [required: Any, installed: 2.8.1]
    - six [required: >=1.5, installed: 1.15.0]
  - python-slugify [required: Any, installed: 5.0.0]
    - text-unidecode [required: >=1.3, installed: 1.3]
  - PyYAML [required: Any, installed: 5.4.1]
  - requests [required: >=2.20.0, installed: 2.25.1]
    - certifi [required: >=2017.4.17, installed: 2020.12.5]
    - chardet [required: >=3.0.2,<5, installed: 4.0.0]
    - idna [required: >=2.5,<3, installed: 2.10]
    - urllib3 [required: >=1.21.1,<1.27, installed: 1.26.4]
  - six [required: Any, installed: 1.15.0]
  - toml [required: Any, installed: 0.10.2]
  - tqdm [required: Any, installed: 4.60.0]
  - troposphere [required: Any, installed: 2.7.1]
    - cfn-flip [required: >=1.0.2, installed: 1.2.3]
      - Click [required: Any, installed: 7.1.2]
      - PyYAML [required: >=4.1, installed: 5.4.1]
      - six [required: Any, installed: 1.15.0]
  - Werkzeug [required: <1.0, installed: 0.16.1]
  - wheel [required: Any, installed: 0.36.2]
  - wsgi-request-logger [required: Any, installed: 0.4.6]

QA

1.Flask-Scriptがインストールできない

原因

現時点(2021/07/20)のFlask-Scriptの最新バージョンにて、Flask2.0との組み合わせでバグ報告が上がっております。

$ python manage.py init_db
Traceback (most recent call last):
  File "C:\Users\xxx\workspace\python-serverless\application\manage.py", line 1, in <module>
    from flask_script import Manager
  File "C:\Users\xxx\.virtualenvs\application-rWZzxA8f\lib\site-packages\flask_script\__init__.py", line 15, in <module>
    from flask._compat import text_type
ModuleNotFoundError: No module named 'flask._compat'

対応方法

以下のコマンドで、下記のライブラリをバージョン指定でインストールしてください。

pipenv install "Flask==1.1.2"
pipenv install "jinja2==2.11.3"
pipenv install "itsdangerous==1.1.0"
pipenv install "werkzeug==0.16.1"
pipenv install "markupsafe==1.1.1" 

その後、Flask-Scriptのインストールが可能になります。

2.flask-loginのインストール時にコンフリクトが発生する

以下のコマンドで、バージョン0.5.0を指定してインストールしてください。

pipenv install "flask-login==0.5.0"

3.zappaのインストール時にコンフリクトが発生する

原因

現時点(2021/07/20)のzappaの最新バージョン(0.53.0)では、Flaskバージョン1.0系にのみ対応しております。

そのため、Flaskバージョン2.0以上をインストールしている場合にはコンフリクトが発生します。

対応方法

Flaskバージョンを本書記載の1.1.2でインストールしてください。

pipenv install "Flask==1.1.2"

その後、zappaのインストールが可能になります。

4.zappa deploy実行時にエラーが発生する

原因

zappaの関連ライブラリのアップデートにより、zappaにて以下のエラーが発生しております。

Traceback (most recent call last):
  File "/Users/hondatakatomo/.local/share/virtualenvs/application-tAyQEiLX/lib/python3.8/site-packages/zappa/cli.py", line 3422, in handle
    sys.exit(cli.handle())
  File "/Users/hondatakatomo/.local/share/virtualenvs/application-tAyQEiLX/lib/python3.8/site-packages/zappa/cli.py", line 588, in handle
    self.dispatch_command(self.command, stage)
  File "/Users/hondatakatomo/.local/share/virtualenvs/application-tAyQEiLX/lib/python3.8/site-packages/zappa/cli.py", line 630, in dispatch_command
    self.deploy(self.vargs["zip"], self.vargs["docker_image_uri"])
  File "/Users/hondatakatomo/.local/share/virtualenvs/application-tAyQEiLX/lib/python3.8/site-packages/zappa/cli.py", line 952, in deploy
    template = self.zappa.create_stack_template(
  File "/Users/hondatakatomo/.local/share/virtualenvs/application-tAyQEiLX/lib/python3.8/site-packages/zappa/core.py", line 2417, in create_stack_template
    self.cf_template.add_description("Automatically generated with Zappa")
AttributeError: 'Template' object has no attribute 'add_description'

対応方法

該当のzappa関連ライブラリであるtroposphereを、以下のバージョン指定でインストールしてください。

pipenv install "troposphere==2.7.1"

その後、すでにzappa deployを実施していた場合は一度undeployしてから再度deployしてください。

zappa undeploy dev
zappa deploy dev

2021/08/04時点でのzappa最新バージョン0.53.0では未対応ですが 現在修正対応中のため、今後はzappaの最新バージョンにてエラーが発生しないよう対応される見込みです。 随時こちらでも情報をアップデートさせていただきます。

5. P177の python manage.py init_db で 「Unable to locate credentials」エラーが発生する

原因

環境変数が正しく読み込まれていないことが原因になります。

...
botocore.exceptions.NoCredentialsError: Unable to locate credentials
...

対応方法

以下の5つの環境変数が正しく設定されているか確認してみてください。

誤字」の項での値が正しく直っているか(ACEESS -> ACCESS)も再度ご確認ください。

SERVERLESS_BLOG_CONFIG
SERVERLESS_USER_PW
SERVERLESS_SECRET_KEY
SERVERLESS_AWS_ACCESS_KEY_ID
SERVERLESS_AWS_SECRET_KEY

設定後、windowsを再起動して環境変数が読み込まれているか確認してみてください。

6. 10章にてzappa deploy時にError: Warning! Status check on the deployed lambda failed. A GET request to '/' yielded a 502 response code. が発生するが動作に問題ないか

Zappaのデフォルトでは、Webアプリケーションも動くことを想定しているため エンドポイントが存在しない場合、つまり10章のスクリプトだけの場合には上記のWarningが表示されますが、動作に問題はございません。

もしWarningを消したい場合は、Configにて以下の設定を加えることで表示されなくなります。

{
    "dev": {
        "apigateway_enabled": false,
        ....
    }
}

本書ではWarning非表示のための設定を追記することでチュートリアルが煩雑になることを防ぐため、あえて注釈での説明のみにとどめておりましたが、こちらで追加説明させていただきます。

7. P146で「これまでセッション情報はローカルに保存していましたが」とあるものの、これまでセッションのコードは出てきていないためどの箇所で利用しているのか

前章8章の「ログイン機能を導入する」のところですが、前提としてログイン機能の実現にはセッションが必要となります。 そのため、Flask-loginが内部的にFlaskのsession機構を利用しております。

ご質問の通り明示的な操作として出てきていなかったため、こちらにて補足させていただきます。

8. P157の.bashrcの環境変数はP.175でzappa_settings.jsonで設定しているので不要ではないか

.bashrcの環境変数ですが2つの理由で作成しております。

1つめは、ローカルで本番用データベースの作成コマンドを実行するためです。

2つめは、それぞれの環境で動作確認および切り分けしながらステップごとに進められるよう用意しました。

  • ローカル環境でDyanmoDB localで動作確認
  • ローカル環境で本番DynamoDBでの動作確認
  • サーバレス環境で本番DynamoDBでの動作確認

問題があったときにどの環境まで正しく動作しているか切り分けたり、 コードをアップデートしたときに順に動作確認することで急に大きな問題が発生することを防いだり、 また本書の構成としてステップごとに学んでいただけるようにというコンセプトも理由としてございます。

9. Mac M1を使っている場合にどのPythonバージョンをインストールすればよいか

Support macOS 11 and Apple Silicon Macsにあるとおり、Python3.8以降ではM1もサポートされています。

Python3.9以降を新規で使いたい場合はリリースニュースである通り Apple Siliconに対応したuniversal版のインストーラーをお使いください。

10. P177でpython manage.py init_dbを実行すると 「AccessDeniedException」エラーが発生する

本書では、2つのIAMユーザを作成しております。

  • flask-blog-dynamodb: DynamoDBを操作するためのIAMユーザ
  • zappa-exec-user: Zappaを使ってサーバレスにデプロイするためのIAMユーザ

init_dbはDynamoDBの操作のため、zappa-exec-userではなくflask-blog-dynamodbが使われているかをご確認ください。

また、権限を追加することで、1つのIAMユーザのみですべての操作を行うことも可能です。 本書では締切の関係上掲載できなかったため、興味がありましたら元となっておりますPythonで作るはじめてのサーバレスアプリケーションも合わせてご参考ください。

About

翔泳社「動かして学ぶ!Pythonサーバレスアプリ開発入門 」サンプルコード


Languages

Language:Python 58.8%Language:HTML 40.2%Language:CSS 1.0%