Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HIGOBASHI.AWS 第5回 「初めてサーバーレス開発をやってみた振り返り」

HIGOBASHI.AWS 第5回 「初めてサーバーレス開発をやってみた振り返り」

2018/7/27に開催されたHIGOBASHI.AWS 第5回の発表資料です。

サーバーレス開発未経験者が入社〜2ヶ月間のサーバーレス開発を振り返り、
・ハマった点
・もっとこうすれば良かったという反省点
・うまくいった点
についてお話ししました。

TomoyaIwata

July 27, 2018
Tweet

More Decks by TomoyaIwata

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ   ؠాɹஐ࠸ w αʔόʔϨε։ൃ෦ w ೥݄ೖࣾ w Ϋϥϝιೖࣾ·Ͱ͸4JFSͰ೥ۈ຿

    w ӡ༻೥ डୗγεςϜܥӡ༻  w ։ൃ೥ /&5 1)1  w "84ྺɺαʔόʔϨεྺ໿ϲ݄
  2. ϓϩδΣΫτͷମ੍   w ૬ํɹ"͞Μ w ೥݄ೖࣾɾɾɾೖࣾ̍ϲ݄  w 1ZUIPOܦݧແ͠

    w ࢲ w ೥݄ೖࣾɾɾɾೖࣾϲ݄ w 1ZUIPOܦݧແ͠ Ҏ্ʂʂ
  3. 4XBHHFSΛ࢖͏৔߹ͱ࢖Θͳ͍৔߹Ͱॻ͖ํ͕ҧ͏ʂ   ɾɾɾུ x-amazon-apigateway-gateway-responses: DEFAULT_4XX: responseTemplates: application/json: > {"message":"$context.authorizer.message"}

    GatewayResponse: Type: "AWS::ApiGateway::GatewayResponse" Properties: ResponseTemplates: application/json: > {"message":"$context.authorizer.message"} ResponseType: DEFAULT_4XX RestApiId: !Ref HelloAPI "84"QJ(BUFXBZ(BUFXBZ3FTQPOTF ͷϓϩύςΟ3FTQPOTF5FNQMBUFTͰఆٛ YBNB[POBQJHBUFXBZHBUFXBZSFTQPOTFT Ͱఆٛ "84"QJ(BUFXBZ(BUFXBZ3FTQPOTFͰఆٛ͠ ͯ΋ແࢹ͞ΕΔ 4XBHHFSΛ࢖Θͳ͍৔߹ 4XBHHFSΛ࢖͏৔߹
  4. CPUPʹର͢ΔΠϝʔδ   ͍͍ͪͪςʔϒϧ໊Λࢦఆͨ͠Γ ΩʔͷଐੑΛࢦఆ͢Δͷ͕൥ࡶ ઈରλΠϙ͢Δࣗ৴͕͋ͬͨ import boto3 dynamodb =

    boto3.resource('dynamodb') table = dynamodb.Table('test') res = table.get_item(Key={ "hash_key": "1", "range_key": "ABC" }) ͍͍ײ͡ʹΫϥεઃܭͯ͠%ZOBNP%#΁ͷΞΫηεΛ؆ૉԽ͍ͨ͠ αʔόʔϨεΞϓϦͷσʔλετΞͱ͍͑͹%ZOBNP%#Ͱ͕͢ɾɾɾ
  5. ΍ͬͨ͜ͱ   جఈΫϥε ۩৅Ϋϥε from abc import ABC, ABCMeta,

    abstractmethod from boto3.dynamodb.conditions import Key class TableBase(ABC): """ DynaoDBͷςʔϒϧૢ࡞ͷͨΊͷந৅Ϋϥε """ def __init__(self, dynamo): """ :param dynamo: """ self.__dynamo = dynamo @property def dynamo(self): return self.__dynamo @property @abstractmethod def table(self): pass @property @abstractmethod def table_name(self): pass @property @abstractmethod def hash_key(self): pass class MDeviceResourceState(TableBase): def __init__(self, dynamo): """ :param dynamo: """ super().__init__(dynamo) self.__table_name = os.getenv('M_DEVICE_RESOURCE_STATE') self.__hash_key = 'device_mng_id' self.__range_key = None self.__table = self.dynamo.Table(self.__table_name) @property def table_name(self)->str: return self.__table_name @property def table(self)->str: return self.__table @property def hash_key(self)->str: return self.__hash_key @property def range_key(self)->str: return self.__range_key def get_item_by_device_id(self, device_id): return self.__table.query( IndexName='GSI_device_id',
  6. ΍ͬͨ͜ͱ   kinesis_data = base64.b64decode(record['kinesis']['data']) kinesis_data = json.loads(kinesis_data, parse_float=Decimal)

    raw_data = kinesis_data['payloads'] # DynamoDB͔Βߋ৽લͷϨίʔυऔಘ device_id = kinesis_data['deviceId'] table = MDeviceResourceState(dynamo) before_item = table.get_item_by_device_id(device_id) ! ݺͼग़͠ଆͷ࣮૷͸ൺֱతγϯϓϧʹ ! λΠϙ͠ͳ͘ͳͬͨ
  7. ॲཧͷڞ௨Խʹؔ͢Δ೰Έɾɾɾ   "1*ΛԿຊ΋࡞͍ͬͯΔͱɺେମॲཧ͕ࣅ௨ͬͯ͘Δ def lambda_handler(event, context): try: # ϦΫΤετύϥϝʔλΛ୯߲໨νΣοΫͯ͠NGͳΒ400Τϥʔฦ͢

    # ୯߲໨νΣοΫ͕OKͳΒؔ࿈νΣοΫͯ͠NGͳΒ400Τϥʔฦ͢ # ϝΠϯͷϏδωεϩδοΫ # OKͳΒϨεϙϯεΛฦ͢ɹྫ֎͕ൃੜͨ͠Β500ΤϥʔΛฦͨ͠Γ except Exception as e: logger.error(e) raise e ͏·͘ڞ௨Խ͍ͨ͠ɾɾɾ
  8. ΍ͬͨ͜ͱ   def valid_json_check(func): """ ϦΫΤετBODYʹଥ౰ͳJSON͕ૹΒΕ͖͍ͯͯΔ͔ΛνΣοΫ͢ΔσίϨʔλ JSON͕ૹΒΕ͖͍ͯͳ͍৔߹͸BadRequestΛฦ͢ :param func:

    :return: """ def json_check_wrapper(*args, **kwargs): body = args[0]["body"] if not is_valid_json(body): res_dict = add_system_time(({ "message": "request body not contain valid json" })) return HttpResponseUtil.bad_request(json.dumps(res_dict)) return func(*args, **kwargs) return json_check_wrapper @valid_json_check def lambda_handler(event, context): try: data = json.loads(event["body"]) v = Validator(__schema()) if v.validate(data) is not True: return HttpResponseUtil.bad_request(json.dumps(v.errors, cls=DecimalEncoder)) ϦΫΤετ#0%:ʹଥ౰ͳ +40/ؚ͕·Ε͍ͯͳ͚Ε͹ ΤϥʔΛฦ͢ ͱ͍ͬͨڞ௨ॲཧΛσίϨʔλ ͱ࣮ͯ͠૷ σίϨʔλͰڞ௨ॲཧΛ෇͚଍ ͍ͯ͘͜͠ͱͰɺ֤-BNEBͷ த਎͸ۃྗγϯϓϧʹ
  9. ίϨ͸ݏɾɾɾ   @mock_dynamodb2 def test_get_api(self): dynamodb = boto3.resource('dynamodb') dynamodb.create_table(

    TableName='Movies', KeySchema=[ { 'AttributeName': 'year', 'KeyType': 'HASH' }, { 'AttributeName': 'title', 'KeyType': 'RANGE' } ], AttributeDefinitions=[ { 'AttributeName': 'year', 'AttributeType': 'N' }, { 'AttributeName': 'title', 'AttributeType': 'S' }, ], ProvisionedThroughput={ 'ReadCapacityUnits': 10, 'WriteCapacityUnits': 10 } ) ςʔϒϧఆٛ͸4".ςϯϓϨʔ τͰ؅ཧ͍ͯ͠Δͷʹɺςετίʔ υʹ͍͍ͪͪςʔϒϧఆٛΛॻ͖ ͨ͘ͳ͍
  10. %ZOBNP%#ͷఆٛΛύʔεͨ͠ࡍͷΤϥʔᶄ   5SBDFCBDL NPTUSFDFOUDBMMMBTU  'JMFUFTUQZ MJOF JONPEVMF NBJO

     'JMFUFTUQZ MJOF JONBJO EBUBZBNMMPBE pMF  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNM@@JOJU@@QZ MJOF JOMPBE SFUVSOMPBEFSHFU@TJOHMF@EBUB  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JOHFU@TJOHMF@EBUB SFUVSOTFMGDPOTUSVDU@EPDVNFOU OPEF  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@EPDVNFOU GPSEVNNZJOHFOFSBUPS 'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@ZBNM@NBQ WBMVFTFMGDPOTUSVDU@NBQQJOH OPEF  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@NBQQJOH SFUVSOTVQFS DPOTUSVDU@NBQQJOH OPEF EFFQEFFQ  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@NBQQJOH WBMVFTFMGDPOTUSVDU@PCKFDU WBMVF@OPEF EFFQEFFQ  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@PCKFDU EBUBDPOTUSVDUPS TFMG OPEF  'JMF6TFSTJXBUBUPNPZB%PDVNFOUTYYYYYMJCQZUIPOTJUFQBDLBHFTZBNMDPOTUSVDUPSQZ MJOF JODPOTUSVDU@VOEFpOFE OPEFTUBSU@NBSL  ZBNMDPOTUSVDUPS$POTUSVDUPS&SSPSDPVMEOPUEFUFSNJOFBDPOTUSVDUPSGPSUIFUBHb4VC`JOEZOBNPZNM MJOF DPMVNO
  11. "84$-*ͷؔ਺Λ࢖༻ͯ͠%ZOBNP%#ͷఆٛΛύʔε   w 4VC΍3FGͷΑ͏ͳ୹ॖه๏͸:".-ͱͯ͠͸ύʔεग़དྷͳ͍ w "84$-*಺ͷؔ਺Λ࢖͏͜ͱͰύʔεग़དྷΔ w ˞಺෦తʹ͸'O4VC΍'O3FGʹల։͞Ε͍ͯΔ IUUQTEFWDMBTTNFUIPEKQTFSWFSTJEFTFSWFSMFTTEZOBNPMPDBMGSPNDGO

    from awscli.customizations.cloudformation.yamlhelper import yaml_parse def main(): with open("dynamo.yml") as file: data = yaml_parse(file.read()) # ςετ༻ͷςʔϒϧΛ࡞੒͢Δॲཧ # .... if __name__ == '__main__': main()
  12. ࠷ऴܥ   w 'JYUVSFͷςʔϒϧ࡞੒ָ͕νϯʹ w ςʔϒϧఆٛͷ؅ཧΛҰݩԽ͢Δ͜ͱʹ੒ޭ region = os.getenv('AWS_DEFAULT_REGION',

    'ap-northeast-1') self.dynamodb = boto3.resource('dynamodb', region_name=region) table_fixture = CreateTableFixture(self.dynamodb) table_fixture.create_m_device_resources_state()
  13. Τϥʔϝοηʔδ   botocore.exceptions.ClientError: An error occurred (ValidationException) when callin

    g the PutItem operation: One or more parameter values were invalid: An Attribut eValue may not contain an empty string
  14. ·ͱΊ   w "1*(BUFXBZͱ4XBHHFSͷඇޓ׵͸$*$%Ͱٵऩ͢Δ w "1*Λઃܭ͢Δࡍ͸ɺ4XBHHFSͷར༻༗ແͰॻ͖ํ͕มΘΔ͜ͱΛ೦಄ʹ w %ZOBNP%#΁ͷΞΫηε͸CPUP୯ମͰ͸ͳ͘ɺؒʹΫϥεΛט·͢ͱ ָʹͳΓͦ͏ʁ

    w "1*ͷϩδοΫڞ௨ԽʹσίϨʔλ͕ޮՌత͔΋ʁ w %ZOBNP%#ͷςʔϒϧఆٛ͸ຊ൪༻΋ςετ༻΋Ұݩ؅ཧ͢Δ w %ZOBNP%#ʹ͸ۭจࣈྻొ࿥Ͱ͖ͳ͍ͷͰؾΛ͚ͭΔ w ετϦʔϜϕʔεͷΠϕϯτ͔Βىಈ͢Δ-BNCEB͸ೖ೦ͳςετΛ