この記事は ハンズラボ Advent Calendar 2019 22日目の記事です。
昨日は、@jnuankさんの「来月退職します」から始めるモブプログラミング でした。
はじめまして、今年4月に中途入社し、毎日AWSをがっつり触れて楽しい日々を送る@t-nmrです。
Advent Calendarに参加するの初めてなので何書こうか…?と悩みましたが、
業務でよく触れる機会のあるServerlessで、最近遭遇した内容について書こうと思います。
何があったのか
タイトルにもある通り、ServerlessでAPI Gatewayとそのカスタムドメインを作ろうとしたところ、
ベースパスマッピングが上手く作成出来ず、ちょっとハマってしまう事がありました。
何故かというと、ベースパスマッピングはパスと一緒に対象のステージを指定しなければいけないから。
つまり、初回sls deployの時点ではベースパスマッピング作成のタイミングで
まだメソッドがステージに一度もデプロイされておらず、ステージ自体が無い。
API Gatewayと一緒にfunctionsでAPIのメソッドとなるLambdaを作成していたため、
「自動的にServerlessがデプロイやってくれるんだから、そいつをDependsOnで参照してやればいい」
そんな風に考えて何回か試してみたところ、対象リソースの論理IDの名前がコロコロ変わって予測出来ない。1
(論理IDの名前は「ApiGatewayDeploymentXXXXXXXX」といった感じで毎回名前が異なる)
これじゃDependsOnで参照出来ない。凄く困る。
色々考えた結果
先にAPI GatewayとLambda作ってから2回に分けてリソース構築するとか、
別途スクリプトあたりで作るとかも考えましたが、やっぱりsls deployコマンド一発で作りたいですよね。
という事で、シンプルにserverless.yml中でステージへのデプロイを別途明示的に行い、
その完了をDependsOnで待つ事にしました。
ソースコード
こんな感じになりました (一部記載は簡略化しています)
また余談ですが、Deploymentの際にDependsOnで見ているApiGatewayMethodApiV1TestGetも、
Serverlessが自動的に作ってくれるリソースになります。
しかし、このリソースの論理IDは名前の予測が可能 かつ 普通にDependsOnで指定可能です。
(「ApiGatewayMethod + {APIパスのupper camel case} + {HTTPメソッド} 」 という形式で作成されます。
慣れてる人には常識かと思いますが、初心者だとこういう部分も知らないと地味にハマりかけたりする…)
functions:
test:
handler: lambda.handler
timeout: 15
tracing: true
events:
- http:
path: /api/v1/test
method: get
cors: true
integration: lambda-proxy
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: test-api
ApiKeySourceType: HEADER
EndpointConfiguration:
Types:
- REGIONAL
ApiGatewayDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
DomainName: ${self:custom.domain_name}
EndpointConfiguration:
Types:
- REGIONAL
RegionalCertificateArn: ${self:custom.acm_arn_regional}
# BasePathMappingの依存解決(stage作成)のために、デプロイを個別に実行する
ApiGatewayDeploy:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiGatewayMethodApiV1TestGet
Properties:
RestApiId:
Ref: ApiGatewayRestApi
StageName: ${self:provider.stage}
ApiGatewayBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
DependsOn: ApiGatewayDeploy # ここで個別に実行したステージへのデプロイ完了を待つ
Properties:
BasePath: ""
DomainName: ${self:custom.domain_name}
RestApiId:
Ref: ApiGatewayRestApi
Stage: ${self:provider.stage}
めでたしめでたし。
とでも思っていたのか?
はい、ここまで書いておいて全てをひっくり返します。
「デプロイの論理IDの名前がコロコロ変わって予測出来ない」と上で書きましたが違いました。
普通に記述出来る方法がありました。
この記事を書いている最中(Advent Calendar投稿当日)に、念のためServerless Framworkの公式ドキュメントを再調査していたらこんな記載がありました。
あのランダムだと思っていた数字列はInstanceIdとやらで、${sls:instanceId}って感じで参照出来ますと。
つまり、結論としてはAWS::ApiGateway::Deploymentを書く必要は無く、
「ApiGatewayDeployment${sls:instanceId}」というリソースを
ベースパスマッピング作成時にDependsOnで見てやればオッケー。
……… よし、週明けにserverless.ymlを書き直そう!
おわり
公式ドキュメントはちゃんと読もう。おじさんとの約束だ。
明日は@o2346さんです。
-
そう思っていた時期が私にもありました。 ↩