Save 37% off PRO during our Black Friday Sale! »

Operationalizing Serverless: New Developer

Operationalizing Serverless: New Developer

Slides for the ServerlessOps Operationalizing Serverless: New Developer workshop.

24794783b45c456b21ff78d6d8259456?s=128

Tom McLaughlin

April 03, 2018
Tweet

Transcript

  1. ServerlessOps NEW DEVELOPER ONBOARDING OPERATIONALIZING SERVERLESS

  2. TEXT OBJECTIVES ▸ Learn what serverless on AWS looks like

    ▸ Go through an example developer workflow ▸ Debug a system failure
  3. TEXT HOW WE’LL ACCOMPLISH THIS ▸ Deploy an example website

    and service ▸ Develop and deploy a new feature ▸ Debug an alert that has triggered
  4. WHAT IS SERVERLESS?

  5. WHAT IS SERVERLESS? ▸ No servers to manage or provision

    ▸ Consumption (not capacity) priced ▸ Never pay for idle ▸ Feature of the event based nature of the architecture ▸ Components scales with usage ▸ You are responsible for maintaining performance and reliability ▸ Availability and fault tolerance built in ▸ Everything is a system
  6. SERVERLESS USE CASES

  7. WELCOME TO WILD RYDES!

  8. TEXT WHY WE CHOSE SERVERLESS ▸Focus ▸Velocity ▸Cost

  9. MODULE 1: DEVELOPER SETUP

  10. TEXT OUR AWS ENVIRONMENT ▸ 3 AWS Accounts ▸ Prime

    (user management), Prod, and Dev ▸ Dev includes personal developer environments. ▸ Developer rights. ▸ Dev: wide rights ▸ Prod: limited rights
  11. TEXT SERVERLESS FRAMEWORK ▸ DSL for describing serverless systems ▸

    Supports multiple cloud providers ▸ We we chose it because ▸ Provides deploy, testing, and debugging capabilities ▸ Plugins make it extensible
  12. TEXT WILD RYDES SITE HTTP://WILD-RYDES-WEBSITE-PROD.PROD.TRAINING.SERVERLESSOPS.IO/

  13. TEXT WILD RYDES SERVICES

  14. TEXT WILD RYDES SERVICES: WEBSITE - WILD-RIDES-WEBSITE ▸ S3 performs

    web hosting ▸ No nginx, apache, etc. ▸ Cost is based on use ▸ Hosts all site static assets ▸ HTML ▸ JavaScript ▸ CSS ▸ Images
  15. TEXT WILD RYDES SERVICES: AUTH - WILD-RYDES-AUTH ▸ Cognito handles

    user: ▸ Management ▸ Authentication / Authorization
  16. TEXT WILD RYDES SERVICES: REQUEST RIDE - WILD-RYDES-RIDE-REQUESTS ▸ API

    Gateway + Lambda + DynamoDB ▸ Backend to request and record rides ▸ No webserver involved ▸ APIG has an endoint and knows to invoke our Lambda when that is called. ▸ DynamoDB stores all ride request info
  17. TEXT DEPLOY AND SIGNUP FOR WILD RYDES ▸ Deploy services

    ▸ Signup, confirm, login, & hail ride ▸ Questions ▸ How is the website updated with Cognito setup information? ▸ Give alternative ways to accomplish this. ▸ How might you refactor the Unicorn stable?
  18. MODULE 2: DEVELOPMENT WORKFLOW

  19. TEXT WHAT IS PROMO DISCOUNT SERVICE ▸ Given a username

    and location ▸ Randomly returns a random discount value. ▸ We’re adding some gamification to test ▸ Retention ▸ Revenue
  20. TEXT DEPLOY PROMO DISCOUNT SERVICE AND REQUEST RIDE DISCOUNT FEATURE

    ▸ Promo Discount ▸ REST API for returning a discount ▸ Ride Request ▸ Calls Promo Discount and records the discount value with rest of ride info
  21. TEXT LOCAL TESTING ▸ Run local unit tests. ▸ We

    use Moto to mock AWS services ▸ Let’s us test against (fake) AWS resources without deploying @mock_sts # Let's us handle assumed roles. @mock_dynamodb2 def test__record_ride(ride, ride_id): '''Test recording a ride''' ddb = boto3.client('dynamodb') ddb.create_table( TableName=DYNAMODB_TABLE, KeySchema=[ { 'AttributeName': DYNAMODB_HASH_KEY, 'KeyType': 'HASH' } ], AttributeDefinitions=[{'AttributeName': DYNAMODB_HASH_KEY, 'AttributeType': 'S'}], ProvisionedThroughput={'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1} ) h._record_ride(ride) this_item = ddb.get_item(TableName=DYNAMODB_TABLE, Key={DYNAMODB_HASH_KEY: {'S': ride_id}}) assert this_item.get('Item').get(DYNAMODB_HASH_KEY).get('S') == ride_id
  22. TEXT REMOTE TESTING ▸ Tail logs ▸ Invoke remote function

    ▸ Run integration tests.
  23. TEXT DEPLOY AND TEST PROMO DISCOUNT ▸ Test locally ▸

    Test remotely ▸ Questions: ▸ Where is the security risk we've potentially just introduced? ▸ How did we have service discovery between Ride Requests and Promo Discount?
  24. MODULE 3: TESTING CHANGES

  25. TEXT TRAVIS CI/CD PIPELINE ▸ What is Travis ▸ Why

    I chose it ▸ How it’s setup ▸ Consistent setup across projects ▸ Modular configuration ▸ Single artifact from testing to deploy
  26. TEXT TRAVIS CI/CD PIPELINE: DIAGRAM

  27. TEXT TRAVIS CI/CD PIPELINE: EXAMPLE BUILD

  28. TEXT CI/CD QUESTIONS ▸ Questions ▸ How might we speed

    up the build? ▸ How might we simplify tighten deploy user setup?
  29. MODULE 4: FAILURE!

  30. TEXT IOPIPE ALERT: LONG DURATION FUNCTIONS Alert Email Alerts Page

  31. TEXT IOPIPE ALERT: LONG DURATION FUNCTIONS IOpipe Invocation

  32. TEXT IOPIPE: FUNCTION INSTRUMENTATION ▸ Instrument function ▸ Deploy ▸

    Run artillery ▸ View alert ▸ See Tracing in IOpipe diff --git a/handlers/request_ride.py b/handlers/request_ride.py index b04f33e..996bc54 100644 --- a/handlers/request_ride.py +++ b/handlers/request_ride.py @@ -122,6 +122,9 @@ def _record_ride(ride_item): @iopipe def handler(event, context): '''Function entry''' + + context.iopipe.mark.start('request_ride') + _logger.debug('Request: {}'.format(json.dumps(event))) # IOpipe state. @@ -140,17 +143,27 @@ def handler(event, context): } else: - user = _get_user_from_authorizer(authorizer) + with context.iopipe.mark('get_user_from_authorizer'): + user = _get_user_from_authorizer(authorizer) + body = json.loads(event.get('body')) - pickup_location = _get_pickup_location(body) - ride_resp = _get_ride(user, pickup_location) + + with context.iopipe.mark('get_pickup_location'): + pickup_location = _get_pickup_location(body) + + with context.iopipe.mark('get_ride'): + ride_resp = _get_ride(user, pickup_location) + + with context.iopipe.mark('get_ride_discount'): + discount_multiplier = _get_ride_discount(user, pickup_location) + + with context.iopipe.mark('record_ride'): + _record_ride(ride_resp) # Note: testing discount feature: discount_multiplier = _get_ride_discount(user, pickup_location) ride_resp['DiscountMultiplier'] = discount_multiplier - _record_ride(ride_resp) - resp = { 'statusCode': 201, 'body': json.dumps(ride_resp), @@ -158,5 +171,6 @@ def handler(event, context): "Access-Control-Allow-Origin": "*", } } + context.iopipe.mark.end('request_ride') return resp
  33. TEXT IOPIPE: FINDING SLOWNESS