Note: I'm migrating from gonzalo123.com to here. When I finish I'll swap the DNS to here. The "official" blog will be always gonzalo123.com

      Playing with lambda, serverless and Python

      Couple of weeks ago I attended to serverless course. I’ve played with lambdas from time to time (basically when AWS forced me to use them) but without knowing exactly what I was doing. After this course I know how to work with the serverless framework and I understand better lambda world. Today I want to hack a little bit and create a simple Python service to obtain random numbers. Let’s start

      We don’t need Flask to create lambdas but as I’m very comfortable with it so we’ll use it here. Basically I follow the steps that I’ve read here.

      from flask import Flask
       
      app = Flask(__name__)
       
       
      @app.route("/", methods=["GET"])
      def hello():
          return "Hello from lambda"
       
       
      if __name__ == '__main__':
          app.run()
      

      And serverless yaml to configure the service

      service: random
       
      plugins:
        - serverless-wsgi
        - serverless-python-requirements
        - serverless-pseudo-parameters
       
      custom:
        defaultRegion: eu-central-1
        defaultStage: dev
        wsgi:
          app: app.app
          packRequirements: false
        pythonRequirements:
          dockerizePip: non-linux
       
      provider:
        name: aws
        runtime: python3.7
        region: ${opt:region, self:custom.defaultRegion}
        stage: ${opt:stage, self:custom.defaultStage}
       
      functions:
        home:
          handler: wsgi_handler.handler
          events:
            - http: GET /
      

      We’re going to use serverless plugins. We need to install them:

      npx serverless plugin install -n serverless-wsgi
      npx serverless plugin install -n serverless-python-requirements
      npx serverless plugin install -n serverless-pseudo-parameters
      

      And that’s all. Our “Hello world” lambda service with Python and Flask is up and running. Now We’re going to create a “more complex” service. We’re going to return a random number with random.randint function. randint requires two parameters: start, end. We’re going to pass the end parameter to our service. The start value will be parameterized. I’ll parameterize it only because I want to play with AWS’s Parameter Store (SSM). It’s just an excuse.

      Let’s start with the service:

      from random import randint
      from flask import Flask, jsonify
      import boto3
      from ssm_parameter_store import SSMParameterStore
       
      import os
      from dotenv import load_dotenv
       
      current_dir = os.path.dirname(os.path.abspath(__file__))
      load_dotenv(dotenv_path="{}/.env".format(current_dir))
       
      app = Flask(__name__)
       
      app.config.update(
          STORE=SSMParameterStore(
              prefix="{}/{}".format(os.environ.get('ssm_prefix'), os.environ.get('stage')),
              ssm_client=boto3.client('ssm', region_name=os.environ.get('region')),
              ttl=int(os.environ.get('ssm_ttl'))
          )
      )
       
       
      @app.route("/", methods=["GET"])
      def hello():
          return "Hello from lambda"
       
       
      @app.route("/random/<int:to_int>", methods=["GET"])
      def get_random_quote(to_int):
          from_int = app.config['STORE']['from_int']
          return jsonify(randint(from_int, to_int))
       
       
      if __name__ == '__main__':
          app.run()
      

      Now the serverless configuration. I can use only one function, handling all routes and letting Flask do the job.

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

      But in this example I want to create two different functions. Only for fun (and to use different role statements and different logs in cloudwatch).

      service: random
       
      plugins:
        - serverless-wsgi
        - serverless-python-requirements
        - serverless-pseudo-parameters
        - serverless-iam-roles-per-function
       
      custom:
        defaultRegion: eu-central-1
        defaultStage: dev
        wsgi:
          app: app.app
          packRequirements: false
        pythonRequirements:
          dockerizePip: non-linux
       
      provider:
        name: aws
        runtime: python3.7
        region: ${opt:region, self:custom.defaultRegion}
        stage: ${opt:stage, self:custom.defaultStage}
        memorySize: 128
        environment:
          region: ${self:provider.region}
          stage: ${self:provider.stage}
       
      functions:
        app:
          handler: wsgi_handler.handler
          events:
            - http: ANY /
            - http: 'ANY {proxy+}'
          iamRoleStatements:
            - Effect: Allow
              Action: ssm:DescribeParameters
              Resource: arn:aws:ssm:${self:provider.region}:#{AWS::AccountId}:*
            - Effect: Allow
              Action: ssm:GetParameter
              Resource: arn:aws:ssm:${self:provider.region}:#{AWS::AccountId}:parameter/random/*
        home:
          handler: wsgi_handler.handler
          events:
            - http: GET /
      

      And that’s all. “npx serverless deploy” and my random generator is running.

      comments powered by Disqus