LoginSignup
1
1

More than 1 year has passed since last update.

ServelessFrameworkのflaskとdynamodbをDockerでローカル開発する

Last updated at Posted at 2022-07-16

経緯

副業でちょいとお手伝いをしているところがありまして、フロントだけの開発〜って感じで聞いていたのですが、フロントエンドだけじゃどうにもならなくなってきてしまいまして。
本職ぺちぱーなので普通にLaravelとかで組んでもいいんですけど、先方のやってみたいことやっていいよ〜という温かい言葉に甘えて、こりゃいっちょ新しいことやってみっかな、ってなったのです。
で、やっぱ時代はサーバーレスじゃないですか。
サーバーレスでサーバーサイドは案件でAWS SAM使ったNode.jsのはやったのですけど、せっかくなのでServerless Frameworkpython使ってやってみようかなーって思い至ったわけです。

やりたい構成

難しい事やる気はサラサラなくて、API GatewayからLambda通ってDynamoDBの読み書きするってやつですね。
んで、ここのLambdaDynamoDBのところをDockerコンテナとしてそれぞれ置こう!ってしたいわけですね。
docker-compose.ymlはこんな感じになりました。

docker-compose.yml
version: '3'
services:
  # フロントエンド開発用
  web:
    build: .
    ports:
      - 9080:9080
    volumes:
      - .:/app
    stdin_open: true
    tty: true

  # serverless開発用
  serverless:
    build: ./serverless
    environment:
      - AWS_PROFILE=default
    volumes:
      - ~/.aws/:/root/.aws:ro
      - ./serverless:/app
    ports:
      - 50000:5000
    stdin_open: true
    tty: true
  
  dynamodb-local:
    image: 'amazon/dynamodb-local'
    container_name: dynamodb-local
    user: root
    ports:
      - 8000:8000
    volumes:
      - ./dynamodb_data:/data
    command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/data"]
    
  dynamodb-admin:
    image: aaronshaf/dynamodb-admin:latest
    container_name: dynamodb-admin
    environment:
      - DYNAMO_ENDPOINT=dynamodb-local:8000
    ports:
      - 8001:8001
    depends_on:
      - dynamodb-local

で、serverlessコンテナのDockerfileはこんな感じです。

Dockerfile
FROM python:3.9.13-alpine
ARG AWS_PROFILE

ENV NODE_PATH /usr/lib/node_modules/
# install nodejs
RUN apk update \
    && apk add --no-cache nodejs npm curl

# install aws-cli
RUN pip install --upgrade pip
RUN pip install awscli werkzeug boto3

# install serverless framework
RUN npm install -g serverless serverless-plugin-existing-s3

# change work directory
WORKDIR /app/flask-dynamodb-api

それから、serverless.ymlはこんな感じになりました。
これはserverlessコマンドのflask-dynamodb-apiのテンプレートから自動生成されるやつにちょこっと書き加えたやつです。

serverless.yml
service: ando-san-flask-dynamodb-api

frameworkVersion: '3'

custom:
  tableName: 'users-table-${self:provider.stage}'
  wsgi:
    app: app.app
  dynamodb:
    start:
      host: dynamodb-local
      port: 8000
      noStart: true
      migrate: true
    stages:
      - dev

provider:
  name: aws
  region: ap-northeast-1
  runtime: python3.9
  stage: dev
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
          Resource:
            - Fn::GetAtt: [ UsersTable, Arn ]
  environment:
    USERS_TABLE: ${self:custom.tableName}

functions:
  api:
    handler: wsgi_handler.handler
    events:
      - httpApi: '*'

plugins:
  - serverless-wsgi
  - serverless-python-requirements
  - serverless-dynamodb-local
resources:
  Resources:
    UsersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: userId
            AttributeType: S
        KeySchema:
          - AttributeName: userId
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:custom.tableName}

ポイントを解説していきましょう。

開発の起点どうすんべ

serverlesspythonでチャレンジすると決めたので、Qiita漁って参考になりそうなのを探してきました。

こちらの記事を参考にさせていただきました。

とはいえ、4年前の記事なので、pythonのバージョン新しくしたいぜ!と思いイキってdockerhubにあるほぼ最新の3.10.5にしたら無事にserverless deployで死んだので一つ落として3.9にしました。
aws cli用のプロファイルは普通にホスト側にあるクレデンシャルを流用したかったので~/.aws/をコンテナ側にそのままマウントして、環境変数AWS_PROFILEを設定するようにdocker-compose.ymlに記述しています。

dynamodb-local って何?

dockerhubをウロウロしてたらdynamodb-localというDockerイメージが有るのを見つけました。

Serverless Flameworkではdynamodbはserverless-dynamodb-localというプラグインを使って、serverless dynamodb installとやってserverless dynamodb startするのが常套手段のようですが、以下の記事のとおり、別のプラグインによって道連れになって死ぬということがあるようです。
serverless-dynamodb-localプラグインは使わないとどうにもならないのですが、serverless dynamodb installを省きたいという意図です。

なので、この記事を参考にdynamodb-localも無事Dockerコンテナ化しました……というわけにもいかず、これだとmigrateが走らないのでテーブルが作られません。ふべん!ということで、serverless.ymlに一工夫を入れます。

serverless.yml抜粋
custom:
  tableName: 'users-table-${self:provider.stage}'
  wsgi:
    app: app.app
  dynamodb:
    start:
      host: dynamodb-local
      port: 8000
      noStart: true
      migrate: true
    stages:
      - dev

ここのhostnoStartmigrateのところですね。
hostdocker-compose.ymlにあるdynamodb-localのコンテナ名ですね。
こうすることによってserverless dynamodb startのコマンドを叩いたときにmigrateが動いてDockerコンテナのdynamodb-localの方にテーブルが作られます。

あ、もちろんapp.pyも書き換えておかないとDockerコンテナのdynamodb-localを見に行かないので注意が必要です。

app.py抜粋
dynamodb_client = boto3.client('dynamodb')

if os.environ.get('IS_OFFLINE'):
    dynamodb_client = boto3.client(
        'dynamodb', region_name='dynamodb-local', endpoint_url='http://dynamodb-local:8000'
    )

ローカルでAPIのテストをしたいだけなのに

最後、いちばん大事なポイントです。

README.mdには

serverless wsgi serve ってやるとローカルで動きまっせ

みたいなこと書いてあって、鵜呑みにしてやってみるのですが、一向にコンテナの外から叩けません。
docker-compose.ymlでちゃんとportのバインディングもしているのにねえ、と悩んでいたんですよ。

不正解のログ
Running "serverless" from node_modules
Using Python specified in "runtime": python3.9
 * Running on http://localhost:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 170-470-797
docker-compose.yml抜粋
  # serverless開発用
  serverless:
    build: ./serverless
    environment:
      - AWS_PROFILE=default
    volumes:
      - ~/.aws/:/root/.aws:ro
      - ./serverless:/app
    ports:
      - 50000:5000
    stdin_open: true
    tty: true

image.png
Connection was forcibly closed by a peerってなんやねん。

コンテナ側でhttp://localhost:5000ならホスト側でhttp://localhost:50000にしたらつながるんと違うんかい!と思うじゃないですか。違うんですよこれが。

正解のログ
Running "serverless" from node_modules
Using Python specified in "runtime": python3.9
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 170-470-797

はい! この通り! http://0.0.0.0:5000じゃないとつながらないのです!

こうするためにはこうしないといけないわけですね。

$ serverless wsgi server --host 0.0.0.0

これで無事、コンテナの外からAPIが叩けるようになって平和が訪れました。

image.png

さいごに

いかがでしたでしょうか。(言ってみたかった
これを参考にDockerでたのしいServerless開発ライフを送ってみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1