Cloud9にLambdaで動くFlaskアプリを構築する

はじめに

運用レスのインフラ基盤でアプリ動かしたいと考え、
Flask(pythonのフレームワーク)を使った簡単なアプリをLambdaで動かしてみた。
※ すでにpython3.6がインストールされている前提(Cloud9は初期から入っている)
※ すでにNode.jsがインストールされており、npmコマンドが打てる前提(Cloud9は初期から入っている)

所要時間

本手順は30分ほどで完了する想定です。(60分あれば十分なハズ)

モジュールのインストール

ここではCloud9でserverless frameworkを用いてLambdaにFlaskをデプロイする方法を載せます。

npmがインストールされていることを確認

$ npm --version

npmのバージョンアップ

$ npm update -g

serverless frameworkのインストール

$ npm install -g serverless

labmdaでflaskを使用するためのモジュールをインストール

$ npm install --save-dev serverless-wsgi serverless-python-requirements

Flaskのインストール

$ sudo python -m pip install flask

Serverless Frameworkの初期設定

IAMユーザ作成

Serverless Framework用のIAM作成 Serverless Frameworkに管理者権限を渡すと権限がでかすぎるので、 下記のポリシーを付与したIAMユーザを作成した。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "cloudformation0",
            "Effect": "Allow",
            "Action": [
                "cloudformation:DescribeStackEvents",
                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:UpdateStack",
                "cloudformation:DescribeStackResources",
                "cloudformation:DescribeStackResource",
                "cloudformation:DescribeStacks",
                "cloudformation:ListStackResources"
            ],
            "Resource": "arn:aws:cloudformation:ap-northeast-1:<アカウントID>:stack/<サービス名>*"
        },
        {
            "Sid": "cloudformation1",
            "Effect": "Allow",
            "Action": [
                "cloudformation:ValidateTemplate"
            ],
            "Resource": "*"
        },
        {
            "Sid": "iam",
            "Effect": "Allow",
            "Action": [
                "iam:*"
            ],
            "Resource": [
                "arn:aws:iam::<アカウントID>:policy/<サービス名>*",
                "arn:aws:iam::<アカウントID>:role/<サービス名>*"
                ]
        },
        {
            "Sid": "s3",
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*",
                "s3:Put*",
                "s3:CreateBucket",
                "s3:DeleteBucket",
                "s3:DeleteBucketPolicy",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::<サービス名>*"
        },
        {
            "Sid": "cloudwatch",
            "Effect": "Allow",
            "Action": [
                "logs:DescribeLogGroups",
                "logs:CreateLogGroup",
                "logs:DeleteLogGroup",
                "logs:PutRetentionPolicy"
            ],
            "Resource": "arn:aws:logs:ap-northeast-1:<アカウントID>:*"
        },
        {
            "Sid": "cloudwatchevents",
            "Effect": "Allow",
            "Action": [
                "events:PutRule",
                "events:DescribeRule",
                "events:DeleteRule",
                "events:PutTargets",
                "events:RemoveTargets"
            ],
            "Resource": "arn:aws:events:ap-northeast-1:<アカウントID>:rule/<サービス名>*"
        },
        {
            "Sid": "apigateway",
            "Effect": "Allow",
            "Action": [
                "apigateway:*"
            ],
            "Resource": "arn:aws:apigateway:ap-northeast-1::*"
        },
        {
            "Sid": "lambda",
            "Effect": "Allow",
            "Action": [
                "lambda:GetFunction",
                "lambda:DeleteFunction",
                "lambda:CreateFunction",
                "lambda:GetFunctionConfiguration",
                "lambda:ListVersionsByFunction",
                "lambda:AddPermission",
                "lambda:RemovePermission",
                "lambda:PublishVersion",
                "lambda:UpdateFunctionCode",
                "lambda:ListAliases",
                "lambda:UpdateFunctionConfiguration"
            ],
            "Resource": "arn:aws:lambda:ap-northeast-1:<アカウントID>:function:<サービス名>*"
        }
    ]
}

<サービス名>:この後serverless.ymlで設定するサービス名
<アカウントID>:各自のAWSのアカウントID

権限を絞るのがすごく苦労しました。(IAMとAPI Gatewayは結局絞り切れなかった。)
まだ絞れる場合は教えてもらえると助かります。

参考:https://docs.aws.amazon.com/index.html
参考:https://blog.kozakana.net/2019/12/serverless-framework-deploy-policy-for-aws/

Serverless Framework用のAWSアカウント設定

$ serverless config credentials --profile sls --provider aws --key <アクセスキーID> --secret <シークレットアクセスキー>

※ profileを指定しないと[Defalt]のアカウントが使用されます。

Flaskアプリの構築

プロジェクト作成

$ sls create -t aws-python3 -p <プロジェクト名>
$ cd <プロジェクト名>

requirements.txtの作成

pythonで使用するライブラリはrequirements.txtで指定しないと
Lambdaにデプロイする際にデプロイモジュールの中に含まれない。

$ pip freeze > requirements.txt

requirements.txtの中身はデプロイしたいライブラリだけ残して後は消します。
下記は今回残しておいたものです。

boto3==1.12.14
Flask==1.0.4
requests==2.22.0
serverless-wsgi==1.7.5
serverlessrepo==0.1.9
Werkzeug==1.0.0

Flaskアプリの配置

今回は動作することまで確認するので、プロジェクトディレクトリ配下に以下の内容のmain.pyを配置します。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, Flask!"

if __name__ == "__main__":
    app.run()

serverless.ymlの編集

プロジェクトディレクトリ配下にあるserverless.ymlを編集します。

service: 
  name: <サービス名>

plugins:
  - serverless-python-requirements
  - serverless-wsgi

provider:
  name: aws
  runtime: python3.6
  stage: ${opt:stage, 'dev'}
  region: ap-northeast-1
  timeout: 300
  memorySize: 2048 # Overwrite the default memory size. Default is 1024
  deploymentBucket: ${self:service}-deployment

custom:
  name: ${self:service.name}
  wsgi:
    app: main.app
    packRequirements: false
  stage: ${opt:stage, self:provider.stage}
  logRetentionInDays:
    dev: "30"
    stg: "60"
    prod: "90"
  pythonRequirements:
    dockerizePip: non-linux

functions:
  app:
    handler: wsgi.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

※ logRetentionInDaysはログを何日分保存するかの設定です。デプロイしたLambdaのログはCloudWatch Logsに吐き出されますが、この保持期間を設定しています。

プロジェクトディレクトリ内のファイル ここまでで、プロジェクトディレクトリ内には以下が配置されているかと思います。

  • requirements.txt
  • main.py
  • serverless.yml

Lambdaモジュールデプロイ先のS3構築

上記のyamlの中身に「deploymentBucket: ${self:service}-deployment」という部分があったが、
ここでLambdaのデプロイ先S3を指定している。
特に指定しなければServerless FrameworkがS3を作ってくれるが、
名前がいまいちだと感じたため自分で作ったS3を指定することにした。

S3バケット名は${self:service}-deploymentとなりますが、
これは「<サービス名>-deployment」を指しているので、
「<サービス名>-deployment」という名前のS3バケットをあらかじめ作っておく。

デプロイ

Serverless Frameworkを用いてLambdaにデプロイしていきます。

$ sls deploy -v --aws-profile <プロファイル名>

デプロイが完了すると、以下の出力があると思います。
ブラウザからURLにアクセスしてうまく動作していることを確認できます。
※ 何か問題がある場合はCloudWatch Logsへ行き、ログを確認します。

endpoints:
  ANY - https://xxxxxxxxxxxx.ap-northeast-1.amazonaws.com/dev

削除

検証が終わって不要な場合は以下のコマンドで削除します。

$ serverless remove -v --aws-profile <プロファイル名>

おわりに

簡単なアプリながら、Lambdaを用いてFlaskアプリを動かすことができました。
サーバレスによる運用フリーなアプリが作成できることが確認できました。

最後に、調査のきっかけになったLambdaについての記事リンクを張っておきます。
すごくまとまっていて、サーバレスアプリをこれから勉強していきたいと思う内容でした。

Spread the love