2018年11月30日金曜日
AWSLambdaとServerlessを使ってみる[第2回]
こんにちは、シタテルエンジニアの工です!
AWSLambdaとServerless第2回に入っていきます!
前回は、AWSLambdaとserverlessについてさらっと確認して
serverlessでServiceをcreateするところまでをやってきました。
AWSLambdaとServerlessを使ってみる[第1回]|sitateru tech blog
今回は実際にSansan OpenAPIを利用して名刺情報を取得してみます 🚀
SansanAPI
公式ドキュメント Sansan Open API
名刺API 名刺Set取得(期間指定)
を使ってみます。
SansanAPIを利用するにはAPI Keyが必要です。
取得方法はこちらを参考にしてください。
作る
前回createしたServiceを利用して作っていきます。
現在この2ファイルが作成されている状態です。
- serverless.yml
- 各種設定を書いていきます
- handler.js
- 処理を書いていきます
axiosインストール
今回は、httpリクエストにaxiosを使用します。
$ npm install axios
インストールされました
.
├── handler.js
├── node_modules
│ ├── axios
├── package-lock.json
└── serverless.yml
serverless.yml
環境変数の設定
lambdaには環境変数を設定することができます。
SansanAPIKeyを環境変数として設定してみます。
serverless.yml
# you can define service wide environment variables here
environment:
SANSAN_API_KEY: xxx
環境変数を利用する場合は、このように取得できます。
process.env.SANSAN_API_KEY
handler.js
それでは処理を書いていきます。
lambdaの非同期処理とcallbackについてはこちらを参考にしてください。
名刺情報はお見せできないので、取得した件数を出力してみます。
'use strict';
const axios = require('axios')
axios.defaults.headers.common['X-Sansan-Api-Key'] = process.env.SANSAN_API_KEY
axios.defaults.headers.get['Content-Type'] = 'application/json'
module.exports.hello = async (event, context, callback) => {
var updatedFrom = "2018-11-26T01:00:00Z"
var updatedTo = "2018-11-27T21:00:00Z"
await axios.get(`https://api.sansan.com/v2.0/bizCards?updatedTo=${updatedTo}&range=all&entryStatus=completed`)
.then(function (response) {
callback(null, response.data.data.length)
})
.catch(function (error) {
callback(error)
});
};
deploy
cliでdeployします。
serverlessを使わない場合、serviceを手動でzipにしてawsにdeployする必要があるのでとても面倒です...
$ serverless deploy -v
deployが完了したら情報が出力されます。
...
Serverless: Stack update finished...
Service Information
service: hello-sansan
stage: dev
region: us-east-1
stack: hello-sansan-dev
api keys:
None
endpoints:
None
functions:
hello: hello-sansan-dev-hello
lambdaの関数のところにも表示されています。
環境変数も設定されています。
実行
こちらもcliで実行します。
$ sls invoke -f hello
16
件数が取得できました。
おわりに
今回はlambdaからSansanOpenAPIを利用して名刺情報をゲットしてみました!
lambdaはサーバーのこと気にせずに処理だけ書けば良く、serverlessはcliでごにょごにょできるし、とても便利です!
sitateruではlambdaを使って、SansanとHubSpotの同期を自動化しています。
どこかのタイミング第3回としてserverless-offlineを使用したlambdaの開発についても書こうと思います。
2018年11月2日金曜日
Google Cloud EndpointsとAWS上のAPIサーバをつないでみる
こんにちは、シタテルの茨木です。
シタテルではSCS(Sitateru-Control-System)という生産管理システムをRuby on Railsで実装し、AWSでホスティングしています。
SCS自体もViewを持っているのですが、徐々にAPI化とフロントエンドの分離を進めており、一部のフロントエンドは既にSPAで分離されていたりします。
今後はシステムをAPI仕様ベースで分離&疎結合化していきたいねーという流れで、API-Gateway系のプロダクト、今回は特にGoogle Cloud Endpointsに触ってみました。
やること
既にあるAWS上のOpenAPI仕様で実装されたAPIサーバと、Google Cloud Endpointsを接続して、APIアクセスを管理できるか見てみる。
とりあえずトラフィックが見えるところまでで、認証系とかはやりません。
構成イメージ
API-Server ---- ESP ---- Client App
|
|
Cloud Endpoints
ESP(Extensible Service Proxy)
ESPはEndpoints固有の要素で、リバースプロキシとしてAPIリクエストを一次受けし、Cloud Endpointsと連携しつつ、通していいリクエストだけバックエンド(API-Server)に流してくれます
- API-Serverの手前でESPを動作させるのが必須
- ESPはdockerで稼働が必要、ESPの稼働プラットフォームは任意
- ESPを間に噛ませさえすれば、API Serverのプラットフォームも任意
手順
身も蓋もないですが公式ガイドに書いてあるのを読むのが確実です
ざっと流れを書いておきます
GCPにプロジェクト作る
プロジェクトにサービスアカウント作って秘密鍵を払い出す
- サービスアカウントはESPがendpointにアクセスする際に使われます
OpenAPIの定義ファイル(いわゆるswagger)をAPIサーバから持ってくる
- 今回はgrape-swaggerで生成したものを使いました
- ガイドだとyamlになってますが、jsonでも読んでくれます
APIサーバのドメイン所有権の証明を行う
- 今回はGCP外のAPIサーバにつなぐので必要
- API定義を読ませたタイミングで、所有権を証明されていないドメインだと怒られます
- https://cloud.google.com/endpoints/docs/openapi/verify-domain-name
gcloud endpoints services deploy [API定義ファイル]
でendpoint構成をデプロイする- デプロイが通ると、下図の様にGCPのポータル上で見えるようになります
- あくまでendpoint構成をデプロイしただけで、ESPと繋いでないので何も起きません
- デプロイが通ると、下図の様にGCPのポータル上で見えるようになります
ESP用のインスタンスをAWS上に作って、dockerでESPを稼働させる
- docker導入は一般的な手順なので割愛
- ESP稼働させるdocker runは下記の様になります(サンプル)
sudo docker run \
--detach \
--name="esp" \
--net="host" \
--volume=$HOME/esp:/esp \
--publish=8082 \
gcr.io/endpoints-release/endpoints-runtime:1 \
--service=hogehoge.sitateru.com \
--rollout_strategy=managed \
--http_port=8082 \
--backend=https://hogehoge.sitateru.com \
--service_account_key=/esp/key.json
ざっとオプション説明
--service
endpoint構成のサービス名(≒APIサーバのドメイン名≒swaggerのhostエントリ)と一致させる
--backend
ESPのプロキシ先。APIサーバを指定する
--service_account
サービスアカウントの鍵ファイルを指定する
- ここまでやるとESP経由でリクエストが通るようになります
- curl等で試してみましょう
curl --request POST \
--header "Content-Type:application/json" \
--header "APIサーバ固有で必要なヘッダ等あれば" \
--data '{"email":"hogehoge", "password":"fugafuga"}' \
http://(ESPインスタンスのIP):8082/path/to/api
- ESP経由でのアクセスは、GCPポータルからログが確認できます
ハマった点
上記手順ではすんなりswaggerを読ませていますが、実際は結構エラーと戦いました。
ちゃんと読むとガイドにも書いてありますが、endpoint構成はOpenAPI仕様を完全にはサポートしていないようです。
例1. type: fileはダメっぽい
"/definitions/putApiV2Topics/properties/files/items/type": domain: validation; keyword: enum; message: value not found in enum; enum: ["array","boolean","integer","null","number","object","string"]; value: "file"
type: fileはサポートしてない。
仕様的にはstringに置換してよさそう?
例2. request-paramの渡し方に制限がある
ERROR: unknown location: http: body field path 'ids' must be a non-repeated message.
ERROR: unknown location: http: body field path 'api_v2_files' must be a non-repeated message.
ERROR: unknown location: http: body field path 'api_v2_items_id_files' must be a non-repeated message.
ERROR: unknown location: http: body field path 'emails' must be a non-repeated message.'
request-parameterのbody直下にarrayはサポートしない模様。
API側のパラメタ受け取り仕様で、arrayはobjectでくるむように作る必要がある?
まとめ
ESPまわりの構成は実際に組むまでイメージ湧かなかったので、その辺の助けになれば幸いです。
OpenAPIはほぼデファクトだと思っていたので、制約がいくつか出てきたのは意外でした。gRPC推しなんですかね?