この記事は CodeChrysalis Advent Calendar 2019 の記事です。
はじめに
Serverless Framework はYAMLでサーバサイドの環境構築ができる、Infrastructure As Code の真骨頂です。確かにAWS等のコンソールでもポチポチクリックしながら環境構築はできますが、
- 変更の管理ができない
- 画面でポチポチクリックするのが本当に面倒(Lazy...)
なのでServerless Framework の CLI でコマンド一発で全てが構築されるのはとても気持ちが良い。今回はその中でもAWSのDynamoDBをどのように構築するかを紹介します。
まずはYAML
早速YAMLを紹介します。
service: cc-database
custom:
stage: ${opt:stage, self:provider.stage}
ccGSIForStudentId: ${self:custom.stage}-dynamodbCcGSIForStudentId
ccTableName: ${self:custom.stage}-dynamodbCc
tableThroughputs:
prod: 5
default: 1
tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}
provider:
name: aws
stage: dev
profile: cc-admin
region: ap-northeast-1
resources:
Resources:
CcDynamoDbTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
Properties:
TableName: ${self:custom.catalogsTableName}
AttributeDefinitions:
- AttributeName: cohortId
AttributeType: S
- AttributeName: studentId
AttributeType: S
KeySchema:
- AttributeName: cohortId
KeyType: HASH
- AttributeName: studentId
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
GlobalSecondaryIndexes:
- IndexName: ${self:custom.ccGSIForStudentId}
KeySchema:
- AttributeName: studentId
KeyType: HASH
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
Outputs:
CcDynamoDbTableArn:
Value:
Fn::GetAtt:
- CcDynamoDbTable
- Arn
Export:
Name: ${self:custom.stage}-CcDynamoDbTableArn
要素を解説
必ず必要な要素
service
, provider
, resources
は定義が必ず必要です。
service
そのサービスの名前を定義します。
ここでサービスはモノリスサービスかマイクロサービスにするのかを決める必要があります。一つのYAMLにサーバサイドのすべての要素を入れてモノリスサービスにしても良いし、それぞれをサービスに分けてマイクロサービスにしても良いし、それは設計次第です。どちらが良いかはこのドキュメントのテーマではないので深追いしません。
この説明ではこのサービスはデータベースだけ(つまりマイクロサービス)として、cc-database
としました。
provider
ここではどのクラウドサービスを利用するかを定義します。今回はAWSなので以下のとおり。
name: aws
stage: dev
profile: cc-admin
region: ap-northeast-1
stageはデフォルトの環境を定義しておき、逆に言えば明確に指定しないとproduction環境にデプロイしないようにしています。
profileはserverless-frameworkでデプロイするときのユーザーをあらかじめIAMで作成しておき、ここで指定します。regionは比較的親しみのあるワードだと思いますが、お使いになるリージョンを指定します。
resources
ここで実際にデプロイしたいサービスの内容を記述します。もしAWSを利用する場合はSyntaxはCloudFormationに準じます。
Type: AWS::DynamoDB::Table
これでどのプロダクトをデプロイするかを指定します。今回はDynamoDBを指定しました。
DeletionPolicy: Retain
Serverless Framework からデータベースを変更したり削除したりしてもデータを残す指定をしています。開発時点では Delete
でも良いかもしれませんが、本番サービスに入ってからデータを消してしまうと大惨事です。保持する設定をしています。
Properties:
ここで実際に作成するテーブル、ローカルインデックス、グローバルセカンダリインデックスを指定します。
TableName: ${self:custom.catalogsTableName}
テーブル名を定義しています。custom
については後ほど解説します。
AttributeDefinitions:
- AttributeName: cohortId
AttributeType: S
- AttributeName: studentId
AttributeType: S
キーとして使う要素をAttributeDefinitions
ですべて洗い出す必要があります。要素の名前とそのデータ型を指定します。またテーブルだけではなくインデックスで使う要素についてもここで洗い出す必要があります。
KeySchema:
- AttributeName: cohortId
KeyType: HASH
- AttributeName: studentId
KeyType: RANGE
ここではテーブルのキーを指定します。パーティーションキーをcohortId
、ソートキーをstudentId
にしています。
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
ここでスループットを指定します。読み込みと書き込みでそれぞれ指定する必要があります。オートスケールにしたくないのでこの状態にしています。
GlobalSecondaryIndexes:
- IndexName: ${self:custom.ccGSIForStudentId}
KeySchema:
- AttributeName: studentId
KeyType: HASH
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
こちらはグローバルセカンダリインデックスの設定です。ほぼテーブルと同じですが、Projection
だけインデックス特有で、どのカラムをインデックスに持たせるかの設定です。今はALL
にしているのですべてを射影しますが、一部のカラムだけで良ければ変更します。
Outputs:
CcDynamoDbTableArn:
Value:
Fn::GetAtt:
- CcDynamoDbTable
- Arn
Export:
Name: ${self:custom.stage}-CcDynamoDbTableArn
もし他のマイクロサービスからDynamoDBのArnを読み取りたい場合はここでOutputsを指定しておくと、"Fn::ImportValue"
で他のマイクロサービスを指定するYAMLに書くことで読み込めます。例えばLambdaがDynamoDBにアクセスするための設定で必要です。
custom
ここは変数のようなもので、定義は必須ではないが、定義しておくと便利なものを記述します。
custom:
stage: ${opt:stage, self:provider.stage}
ccGSIForStudentId: ${self:custom.stage}-dynamodbCcGSIForStudentId
ccTableName: ${self:custom.stage}-dynamodbCc
tableThroughputs:
prod: 5
default: 1
tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}
たとえばここで
stage: ${opt:stage, self:provider.stage}
はCLIを実行するときの引数にSTAGEが引き渡されたらそれを使用するし、そうでなければprovider
に指定されているSTAGE、すなわちdev
を使用する設定としています。
テーブル名、グローバルセカンダリインデックスの名前、スループットの値を変数化しておき、この内部のYAMLや外部のYAMLから読み込むようにしています。
補足ですが、外部のYAMLから当該YAMLを読み込むためには
${file(./database/serverless.yml):custom.ccTableName}
を使うことで読み込めます。例えばこれはテーブル名を読み込んでいます。
さいごに
Code Chrysalisは東京を拠点にした12週間の短期集中型ソフトウェアエンジニア養成学校です。ソフトウェアエンジニアになるための12週間特別集中コース:イマーシブやプログラミング初心者向けの講座もありますので、ぜひ見てみてください。