雑食日誌

PythonとJS。ときどきチーム開発

AppSyncのデプロイについてまとめてみた

はじめに

ある案件でAppSyncを使うことになったが、商用利用の事例が少なくデプロイ方法がわからなかった。
自動でデプロイできる仕組みを探したところ、Serverless FrameworkのAppSyncプラグインがあったので使ってみた。

AppSyncとは

AppSyncはAWSで利用できるGraphQLベースのマネージドサービスである。

特徴として

  • バックエンドサービスを自由に切り替えることができる
  • GraphQLベースのため、フロントエンドに必要なデータを定義できる

などがある。
特にバックエンドにはLambdaやDynamoDB、HTTPなど多様なリソースを選択できる。

前提

今回の案件では以下の条件があった。

  • AppSyncをLambdaバックエンドとHTTPバックエンドの二種類を実装
  • フロントエンドはアプリ(iOS, Android) とWebアプリ(JavaScript)
  • 環境は開発環境・検証環境・本番環境の3つ
  • CloudFormation経験は薄く、Sceptreを使ってECS(Fargate)を作成した経験のみ

デプロイ方法の選定

現状AppSyncを商用利用して、デプロイするためには以下の手段が考えられた。

  • CloudFormationを書く
  • Amplify CLIを使う

CloudFormationを使ってデプロイするためにはYaml地獄と戦い、トライアンドエラーを繰り返す必要がある。 時間的猶予がなかったため却下した。

Amplify CLIはコマンド一つでAWSリソースを作ることができる強者である。 しかし、以下の懸念点があった。

  • 開発当初は env 機能がなかったため、環境を複数設定できない
  • 複数のフロントエンドで共有することが難しい

そのため、リソースの作成にAmplify CLIを使用しなかった。

他に良いデプロイ方法はないか探していたら、以下の記事に遭遇し、ServerlessFrameworkのAppSyncプラグインを知った。
read.acloud.guru

Serverless Frameworkを使う予定があったので、試してみたら思いのほか手軽だった。
ここからは実際にデプロイした手順を記録していく。

Serverless FrameworkでAppSyncデプロイ

今回はAppSyncのバックエンドにLambdaを使用する。 GraphQL SchemaにはブログWebアプリケーションを想定に実装する。

サンプルはGitHubにもあげているので併せてみていただきたい。

github.com

CLIインストール

NPMでインストールする。

$ npm install -g serverless

プロジェクト初期化

プロジェクトを初期化する。
このタイミングでは package.json は作成されていない。

$ serverless create --template aws-nodejs --path appsync-deploy
$ cd appsync-deploy
$ ls -a
.gitignore     handler.js     serverless.yml

create コマンドを実行するには template オプションが必要である。
template の種類は以下に詳細が記載されている。

serverless.com

成果物のうち serverless.yml はCloudFormationのもとになるYaml定義。
handler.jsAWS Lambdaを使用する場合の実行ファイルのため、AppSyncのデプロイだけでは不要である。

プラグインのインストール

ServerlessFrameworkのプラグインはNPMパッケージでまとめられているため、 package.jsonプラグインを管理していく。
Node環境がまだないので、改めてNPM初期化を実施する

$ git init
$ npm init 
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (appsync-deploy)
version: (1.0.0)
description:
entry point: (handler.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to appsync-deploy/package.json:

{
  "name": "appsync-deploy",
  "version": "1.0.0",
  "description": "",
  "main": "handler.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

AppSyncプラグインをインストールする。

$ npm i serverless serverless-appsync-plugin

デプロイコマンドをNPMの scripts に定義する。

"scripts": {
  "deploy": "serverless deploy -v"
},

これでデプロイを実行する準備が整った。 serverless.yml を整えながら、AppSyncの設定を加えていく。

Serverless Framework共通設定

はじめに基本設定を記述していく。
serverless.yml を開くと例が記載されているが、今回必要な部分は少ないので消す。

service: appsync-deploy

provider:
  name: aws
  region: ap-northeast-1

plugins:
  - serverless-appsync-plugin

service はCloudFormationのStack Nameに使用される部分であり、 デプロイ環境の総称となる。今回はプロジェクトのディレクトリ名を使用する。

provider はCloud Providers(AWS)およびリージョンの設定をする。
plugins にはインストールしたAppSyncのプラグインを記述する。

AppSyncの基本設定

このサンプルではAppSyncの認証に Amazon Cognitoを選択する。
Schemaは簡易的にするために QueryMutation を一つずつにした。
Schemaの詳細を以下に記載する。

schema {
    query: Query
    mutation: Mutation
}

type Query {
    listArticle: ArticlesResponse
}

type Mutation {
    createArticle(title: String, content: String): ArticlesResponse
}

type Article {
    id: Int
    title: String
    content: String
}

type ArticlesResponse {
    errorCode: String
    articles: [Article]
}

Schemaと認証設定を serverless.yml に追記する。

custom:
  accountId: ${env:AWS_ACCOUNT_ID}
  appSync:
    name: BlogApp # AppSyncのAPI名
    authenticationType: AMAZON_COGNITO_USER_POOLS # API Keyなど選択
    userPoolConfig:
      # Cognitoの設定
      awsRegion: ap-northeast-1
      defaultAction: ALLOW
      userPoolId: ap-northeast-1_XXXXX
    schema: schema.graphql

Data Sources

バックエンドにはLambdaを使用するため、二つのARNが必要になる。

  • LambdaそのもののARN
  • AppSyncがLambdaを実行するRoleのARN

Roleはデフォルトの設定で作成されないため、事前にコンソールから作るか、 Serverless Frameworkの resources 機能で作る。

DataSourcesを serverless.yml に反映すると以下になる。

custom:
  accountId: ${env:AWS_ACCOUNT_ID}
  appSync:
    name: BlogApp # AppSyncのAPI名
    authenticationType: AMAZON_COGNITO_USER_POOLS # API Keyなど選択
    userPoolConfig:
      # Cognitoの設定
      awsRegion: ap-northeast-1
      defaultAction: ALLOW
      userPoolId: ap-northeast-1_XXXXX
    schema: schema.graphql
    dataSources:
      - type: AWS_LAMBDA
        name: BlogAppResolver
        config:
          functionName: blog-app-resolver
          lambdaFunctionArn: "arn:aws:lambda:ap-northeast-1:${env:AWS_ACCOUNT_ID}:function:blog-app-resolver"
          serviceRoleArn: "arn:aws:iam::${env:AWS_ACCOUNT_ID}:role/service-role/appsync-ds-lam-xxxxxxxxxxxxx

Mapping Templates

最後にMapping Templatesを整える。
Mapping Templatesのリクエストとレスポンスそれぞれをファイル化し、 mapping-templates ディレクトリに格納することでAppSyncに反映する仕組みになっている。

レスポンスのMapping Templatesは共通化させて設定した。

レスポンス
common-response.vtl

$util.toJson($context.result)

Queryのリクエス
query-list-blog-request.vtl

{
  "version" : "2017-02-28",
  "operation": "Invoke",
  "payload": {
    "path": "list_blog",
    "data": $util.toJson($context.args)
  }
}

Mutationのリクエス
mutation-create-blog-request.vtl

{
  "version" : "2017-02-28",
  "operation": "Invoke",
  "payload": {
    "path": "create_blog",
    "data": $util.toJson($context.args)
  }
}

これらを mapping-templates ディレクトリ以下に格納する。ディレクトリ名は変更することもできる。

Mapping Templatesの設定を serverless.yml に反映すると以下のようになる。

custom:
  accountId: ${env:AWS_ACCOUNT_ID}
  appSync:
    name: BlogApp # AppSyncのAPI名
    authenticationType: AMAZON_COGNITO_USER_POOLS # API Keyなど選択
    userPoolConfig:
      # Cognitoの設定
      awsRegion: ap-northeast-1
      defaultAction: ALLOW
      userPoolId: ap-northeast-1_H8OHxpYAa
    schema: schema.graphql
    dataSources:
      - type: AWS_LAMBDA
        name: BlogAppResolber
        config:
          functionName: blog-app-resolver
          lambdaFunctionArn: "arn:aws:lambda:ap-northeast-1:${env:AWS_ACCOUNT_ID}:function:blog-app-resolver"
          serviceRoleArn: "arn:aws:iam::${env:AWS_ACCOUNT_ID}:role/service-role/appsync-ds-lam-ln4jdz-blog-app-resolver"
    mappingTemplates:
      - dataSources: BlogAppResolber
        type: Query # Query, Mutation, Subscription
        field: listArticle # Schema内のフィールド名
        request: "query-list-blog-request.vtl"
        response: "common-response.vtl"
      - dataSources: BlogAppResolber
        type: Mutation
        field: createArticle
        request: "mutation-create-blog-request.vtl"
        response: "common-response.vtl"

デプロイ実行

package.json に登録したコマンドでデプロイを実行する。 サンプルはCognitoやRole ARNを仮で記載しているので、編集してから実行する。

$ npm run deploy

エラーが出る場合は環境変数SLS_DEBUG を追加することで、実行の詳細を確認することができる。

$ export SLS_DEBUG=* 

Serverless Frameworkは最初に、 serverless.yml ファイルをコンパイルして、CloudFormationに変換し、S3バケットに格納する。
成果物は .serverless ディレクトリにも保存される。

S3に格納されたら、CloudFormationが実行され、定義したAWSリソースが生成される。

まとめ

  • AppSyncの情報は少ない
  • デプロイはServerless Frameworkが便利

参考

github.com