🥮

Lambda上に適当に置いてあったコードをちゃんと管理する(個人開発)

2022/12/17に公開

この記事はPython Advent Calendar 2022の17日目の記事です。

2019年に Lambda へデプロイした Python コードが今日まで動いていますが、これが何も管理されていなかったので、そろそろ重い腰を上げてちゃんとやっていこうと思います。

アプリケーションの説明

Twitterで自分の名前の後ろに、東京都の天気予報の結果を表示します。これは1時間毎に自動更新されます。

https://twitter.com/app1e_s

やること

1. コード管理

(なし) -> Git & GitHub

今動いているコードが何も管理されておらず、なお且つ、開発をしていたPCがお亡くなりになったので、ローカル環境にコードがない状況です。
とりあえず、 Lambda 上にデプロイされたコードが有るので、こちらを DL して Git と GitHub でコード管理を行うようにします。

2. パッケージ管理

(なし) -> Pipenv

おそらく pip で管理されていたはずでしたが、ソースコードのデプロイ時(デプロイ用のzip作成時)にrequirements.txt入れてなかったため、何も管理がされていない状態になっていました。

今回は pip ではなくPipenvを利用します。

3. インフラ管理

コンソール -> Serverless Framework

Lambda は CloudWatch Events からの定期実行になっています。

構成はとてもシンプルなサーバーレスアーキテクチャとなっているので、IaC化する上でサーバーレスアーキテクチャに特化した Serverless Framework を採用しました。

Git & GitHub の初期設定

まずはコードの置き場所となるディレクトリと、 GitHub 上のリポジトリを作成します。

mkdir twitter-username-weather/
cd twitter-username-weather/

GitHubでリポジトリを作って、いつものコードをコピペします。

echo "# twitter-username-weather" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:your-user-name/twitter-username-weather.git
git push -u origin main

Serverless Framework のセットアップ

Serverless Framework でアプリケーションの管理をするので、aws-python3で作成します。

serverless create --template aws-python3 --name twitter-username-weather

すると、以下のようにファイルが自動生成されます。

.
├── .git/
├── .npmignore
├── handler.py
└── serverless.yml

自動生成されたserverless.ymlからちょっと設定を変えて、ランタイムをLambdaで利用可能な最新バージョンにあげ、リージョンを東京にします。

- runtime: python3.8
- #  region: us-east-1
+ runtime: python3.9
+ region: ap-northeast-1

そして、Pythonで外部ライブラリを利用するため、 Serverless Framework でもプラグインを使います。

sls plugin install -n serverless-python-requirements

https://www.serverless.com/plugins/serverless-python-requirements

sls deployを実行して、東京リージョンにLambdaアプリケーションが追加されていればOK。

コードのコピー

これまで管理されていなかったコードを Git で管理し、 GitHub で公開するにあたり、まずはAPIキーなどがコードにハードコードされていないか確認します。

もし、ハードコードされていたら、必ず環境変数から取ってくるように書き換えます。

os.environ.get("OPEN_WEATHER_MAP_API_KEY")

環境変数は.envファイルに書き込むことで、 Serverless Framework から Lambda の環境変数として設定することが出来ます。

https://www.serverless.com/framework/docs/environment-variables/

.envファイルから環境変数を読み込む場合は、useDotenvtrueにすることで${env:OPEN_WEATHER_MAP_API_KEY}のように環境変数へアクセスできるようになります。
そして、各関数の中のenvironment:の下に環境変数を書くことで、デプロイ時に Lambda 関数へ環境変数が設定されます。

+ useDotenv: true

...

functions:
  hello:
    handler: handler.hello
+    environment:
+      OPEN_WEATHER_MAP_API_KEY: ${env:OPEN_WEATHER_MAP_API_KEY}
      ...

https://mseeeen.msen.jp/how-to-set-environment-variables-of-lambda-with-serverless-framework/

また、コピペするとハンドラーとなる関数名がhelloではなくなるかと思います。その時はserverless.yamlを書き換えます。

-    handler: handler.hello
+    handler: handler.lambda_handler

実行環境のセットアップ

Lambdaのランタイムと合わせて Python 3.9 の実行環境を用意します。ここでは Pipenv を利用します。

pipenv --python 3.9

次に外部ライブラリを入れます。

今回のコードではRequestsrequests-oauthlibのライブラリを使っているので、これらをインストールします。

pipenv install requests requests-oauthlib

これで動作環境が整備されました。とりあえずローカル環境で試してみます。if __name__ == '__main__':lambda_handlerから呼び出される関数をprintしてみます。

if __name__ == '__main__':
    w = get_weather()
    name = get_user_name()
    print(name, w)

pipenv でこの Python ファイルを実行します。この時.envファイルは自動で読み込まれます。

pipenv run python ./handler.py

これで適当な情報がprintされればOKです。そうしたらLambdaへデプロイします。

sls deploy

無事デプロイされたらコンソールからテストを実行します。入力データは不要です。
各設定に問題がなければ、以下の様なResponseが返ってきて、Twitterの自分のアカウント名が変わっているかと思います。(最近のTwitter Blueの話はわからないです。)

定期実行の設定

Lambdaの動作確認できたので、これを定期実行するようにします。1時間に1回実行されうようにするのでserverless.yamlへ cron を設定します。

functions:
  main:
    handler: handler.lambda_handler
    environment:
      OPEN_WEATHER_MAP_API_KEY: ${env:OPEN_WEATHER_MAP_API_KEY}
      TWITTER_AS: ${env:TWITTER_AS}
      TWITTER_AT: ${env:TWITTER_AT}
      TWITTER_CK: ${env:TWITTER_CK}
      TWITTER_CS: ${env:TWITTER_CS}
+    events:
+      - schedule: cron(0 * * * ? *)

これで、毎時0分のときに定期実行され、天気が変わればデプロイ完了です。

後片付け

まず Lambda 関数を定期実行している EventBrige を削除して、最後に Lambda 関数そのものを削除します。

もともと CloudFormation で管理されているわけではないですが、この2つしか使われているサービスがなかったので、これですべて完了です。お疲れさまでした!

完成したコード

https://github.com/meihei3/twitter-username-weather

注意 Pipenv のバージョンについて

これまで serverless-python-requirements で Pipenv の requirements を取得する時、内部的にはpipenv lock --requirementsを実行していたようですが、これが deprecated となり、削除されたようです。

https://blog.foresta.me/posts/serverless-python-requirements-error/

serverless-python-requirements は6.0.0のリリースでこの問題に対応しました。そのため、上の記事で紹介されているようなpip install pipenv==2022.8.5をする必要はなくなりました。

https://github.com/serverless/serverless-python-requirements/releases/tag/v6.0.0

しかし、今回のリリースは破壊的な修正となったため、逆に古いバージョンの Pipenv は使えなくなりました。

もし、古いバージョンの pipenv を利用していて、特別な理由がなければ普通にバージョンアップしたほうが良いです。

python3 -m pip install --upgrade pipenv

Discussion