• [Cloud DA] Best Practices for Serverless


    Overview

    • How to architect our application for testability
    • How to run our application locally
    • Have multiple envs for our application
    • Implement observability
      • monitoring, logging, distributed tracing
    • Improve performance of our application
      • Cold start issue
    • Security of our application

    Vendor lock-in

    • Vendor lock-in is Inability to switch from a selected technology provider to another
    • Discuessed a lot when related to serverless

    What can cause a vendor lock-in?

    • Code in not root cause of vendor lock-in
    • Other third party services are the cause
      • Database
      • File storage
      • Messaging systems

    How can we defend from a vendor lock-in?

    • Using "Ports and Adapters Architecture" or "hexagonal architecture"

    Ports and Adapters Architecture

    Main idea is to separate application into two components.

    • Business logic which are independent from external services
    • Ports and adaptor which interact with outside services such as DB, messaging queue.

    Ports: You can think of ports as interface which defines the common actions to handle busniess logic

    Adapter: You can think or Adapter as the real implementation detail behind the interface, for example database port might have 'read', 'write' interface

    adapters are targeting different database, such as mysql, DyanmoDB .... 

    Benefits of the Ports and Adapters Architecture

    • Business logic can be easily ported
      • Can move application to another provider
    • Allwe need to do is re-implement ports if switch third party vendor

    See Example: Refactoring the code with Ports and Adapters pattern

    Multi Stages Deployment

    For testing, we can have multistaging env.

    For example:

    • CI: For intergion testing
    • Dev: For developers
    • Staging: For testers
    • Prod: for customers

    Here is how we can deploy a serverless application to different stages:

    # Deploy to production ("prod" stage)
    sls deploy -v --stage prod
    
    # Can have additional stages
    sls deploy -v --stage staging

    Run function locally

    Invoke a function in AWS

    serverless-offline-plugin

    • Emulates API Gateway and Lambda
    • Starts local web server that calls Lambda functions locally
    • Intergets Authorizers, Lambda integrations, CORS, Velocity templates... etc.
    • Can attach a debugger to a local process
    • Works with plugins that emulate other services...DynamoDB, Kinesis, SNS, etc...
    ### Run locally
    
    1. install the plugins.
    2. Define in yaml:
    
    ```yaml
    plugins:
      - serverless-webpack
      - serverless-reqvalidator-plugin
      - serverless-aws-documentation
      - serverless-dynamodb-local
      - serverless-offline
    
    custom:
      topicName: ImagesTopic-${self:provider.stage}
    
      serverless-offline:
        httpPort: 3003
    
      dynamodb:
        stages:
          - dev
        start:
          port: 8000
          inMemory: true
          migrate: true
    ```
    
    3. run sls install/start:
    
    ```bash
    sls dynamodb install
    sls dynamodb start
    
    sls offline
    ```

    Seeding a database

    We can also automatically seed a local DynamoDB database. To do this, we need to set dynamodb.start.seed to true and provide seed configuration, like this:

    custom:
      dynamodb:
        start:
          seed: true
    
        seed:
          # Categories of data. We can optionally load just some of them
          users:
            sources:
              - table: users
                sources: [./users.json]
              - table: user-roles
                sources: [./userRoles.json]
          posts:
            sources:
              - table: blog-posts
                sources: [./blogPosts.json]
    
    

    You can then seed a database with the following command:

    sls dynamodb seed --seed=users,posts
    

    Or you can start a local DynamoDB database and seed it:

    sls dynamodb start --seed=users,posts

    Canary Deployment

    • Allows to release a new version slowly
    • Exposes only a small percentage of users to potential issues
    • Quickly rollback if any problems encountered

    Lambda Weighted Alias

    https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html

    ### Canary Deployment
    
    ```yaml
    plugins:
      ...
      - serverless-plugin-canary-deployments
    
    iamRoleStatements:
      ...
      - Effect: Allow
        Action:
          - codedeploy:*
        Resource:
          - "*"
    
      CreateGroup:
        handler: src/lambda/http/createGroup.handler
        events:
          - http:
              method: post
              path: groups
              authorizer: RS256Auth
              cors: true
              reqValidatorName: RequestBodyValidator
              request:
                schema:
                  application/json: ${file(models/create-group-request.json)}
        deploymentSettings: ## add deploymentSetting for canary deployment
          type: Linear10PrecentEveryMinute
          alias: Live ### new alias called Live
    ```

    X-Ray

    provider:
      tracing:
        apiGateway: true
        lambda: true
    import * as AWS from "aws-sdk";
    import {
      APIGatewayProxyEvent,
      APIGatewayProxyHandler,
      APIGatewayProxyResult,
    } from "aws-lambda";
    import * as AWSXRay from "aws-xray-sdk";
    const XAWS = AWSXRay.captureAWS(AWS);
    const docClient = new XAWS.DynamoDB.DocumentClient();
    const imagesTable = process.env.IMAGES_TABLE;
    const imageIdIndex = process.env.IMAGE_ID_INDEX;
    
    export const handler: APIGatewayProxyHandler = async (
      event: APIGatewayProxyEvent
    ): Promise<APIGatewayProxyResult> => {
      const imageId = event.pathParameters.imageId;
      const result = await docClient
        .query({
          TableName: imagesTable,
          IndexName: imageIdIndex,
          KeyConditionExpression: "imageId= :imageId",
          ExpressionAttributeValues: {
            ":imageId": imageId,
          },
        })
        .promise();
    
      if (result.Count === 0) {
        return {
          statusCode: 404,
          headers: {
            "Access-Control-Allow-Origin": "*",
          },
          body: "",
        };
      }
    
      return {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Origin": "*",
        },
        body: JSON.stringify({
          item: result.Items[0],
        }),
      };
    };

    Lambda Cold start

    • Common problem for all FaaS Solutions
    • Functions are provisioned only when they are used
    • Instancees removed when not used
    • Take time to provision

    When does cold start happen?

    • Only experienced when a new function is started
      • When no instances are running
      • When an additional instance is required
    • Mostly a problem for
      • Processing customers requests
      • Infrequent requests
        • A new function instance for every request
      • A spike of requests
        • Not enough function instnaces running
    •  Not a problem if you have a steady stream of requests

    Optimize - Code Size

    • Smaller functions have a faster cold start
      • AWS won't ship devDependencies
      • We don't need to ship aws-sdk, lambda has it built-in
      • Use Serverless plugins to optimize functions
    • Split one function into multiple function 
      • One function pre task

    Avoid Cold start altoghther

    • Call your function periodically
      • Send a special payload (warm-up request)
    • If it is a warm-up request, your function should do nothing
      • Enough to keep it ready to process incoming requests
    • Serverless has a plugin for this

    Optimize - Function Code

    • Re-use state between executions
    • Avoid invocation (for example, function should only being called when new object added to S3, then use S3 events filters to only invocation Lambda for create object event)
    • Avoid sleep() calls in Lambda function
      • You are paying for waiting
      • Use AWS Step Function for orchestration
    • Send multiple request in parallel

    Optimize - Avoid Running in VPC

    • Only run a function in a VPC if it needs an access a protected endpoint
    • Running in VPC in creases cold start dtastically

    Optimize - Bootstrapping New Runtime

    • Select a different runtime
      • Go, Node.js Python have lowest cold start
      • Java, .NET have the highest cold start
    • Provision more memory
      • Provides a faster CPU
      • Allows to bootstrap a runtime faster

    Optimize - More Memory

    • Can allow to save money
      • May seem conterintuitive
      • We pay more if we provision more memory
    • More memory => faster CPU
      • Spend less time
      • Pay less for execution time

    Lambda Power Tuning

    • Selecting an optimal memory size can be tricky
    • aws-lambda-power-tuning
      • Run a function with different memory sizes
      • Fidns the most cost efficient memory sizes

     

    Optimize - functions size

    • Package functions individually
      • Supported by Webpack and Serverless
    • Re-build our application

    When use Serverless framework, it packages all the deps used by at least one Lambda function, and create a single zip package. So the single zip package is used by all the Lambda funtion.

    The only difference is that the hander for each function is different.

    // serverless.yaml
    
    package:
      individually: true

    [Notice]: A small size reduces the cold start time but we might pay for longer compilation time.

    Security

    Lambda

    • Application security (OWASP Top 10 still applies)
    • Configuring IAM permissions
      • Define what function / role can access what resources
      • Use the principle of least privilege
    • Data encryption
      • Enable encryption at rest for services like DynamoDB and Kinesis
    • Secrets handling
      • Use KMS, secrets manager, SSM

    API

    • Use throttling
      • Can be configured per stage or per method
      • Throttle individual customers if applicable
    • Validate incoming data
      • Do it in API Gateway or in your code
    • Use a firewall to limit access to your API
      • AWS Web Application Firewall (WAF)

    https://docs.aws.amazon.com/lambda/latest/dg/lambda-security.html

    Lambda Minimal Privileges for Serverless

    • All our functions have the same set of permissions
    • We need to set minimal required privileges per function
    • Will use serverless-iam-roles-per-function for this
      GetImages:
        handler: src/lambda/http/getImages.handler
        events:
          - http:
              method: get
              path: group/{groupId}/images
              cors: true
        iamRoleStatements:
          - Effect: Allow
            Action:
              - dynamodb:Query
            Resource: arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.IMAGES_TABLE}
          - Effect: Allow
            Action:
              - dynamodb:GetItem
            Resource: arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.GROUPS_TABLE}
  • 相关阅读:
    迭代器模式
    工厂方法模式
    ajax发送多个跨域请求回调不混乱
    Java中&和&&的区别
    使用jstl方式替换服务器请求地址
    用jinja2替换Django的模板
    Django在apache中的配置
    从word中提取图片的三种方法
    IE中出现 "Stack overflow at line" 错误的解决方法
    c# 空接合(??)运算符的运用
  • 原文地址:https://www.cnblogs.com/Answer1215/p/14811646.html
Copyright © 2020-2023  润新知