Amazon S3 に Git LFS サーバを超簡単に立てる

元ネタはこちら。
Serverless Git LFS for Game Development – Alan Edwardes
Amazon API Gateway + Amazon Lambda + Amazon S3 を使うんだそうです。

準備: AWS のアカウントをとる

AWSのアカウントがなければとります。
作成の流れは 公式のドキュメント でだいじょうぶです。

アカウントをとったら初期設定しておきます。以下のエントリが参考になりました。
AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ – Qiita

サーバ作成手順

Screen_Shot_2018-01-21_at_22_29_22-fullpage.png

  • 「次へ」をクリックします → オプションの設定ページはスルーで「次へ」をクリックします。
  • 「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックして「作成」をクリックします。

Screen_Shot_2018-01-21_at_23_05_08-fullpage.png

  • スタックのリストに新しいスタックができて(スタックがなければリロードして下さい)、「CREATE_COMPLETE」と表示されたら成功です。

Screen_Shot_2018-01-21_at_23_10_18-fullpage.png

  • スタックの詳細画面に入って、出力を確認すると、Git LFSのエンドポイントのURLが表示されているので、リンクをコピーします。

Screen_Shot_2018-01-21_at_22_34_53-fullpage.png

Git LFS に lfs.url を設定

Git LFS は導入してあることとします。

以下のコマンドで、 .lfsconfig を作成します。

git config -f .lfsconfig lfs.url <表示されたエンドポイントのURL>

git lfs env で確認して、Endpoint= の行が設定したURLになってたらOKです。

git push すると、S3の方にLFSオブジェクトがpushされるようになります。

問題点

URL を見ればわかると思いますが、この方法で作成するとリージョンが 欧州 (アイルランド) に作成されます。
東京リージョンにS3を立てたいと思って、 URL の region=eu-west-1 の部分を region=ap-northeast-1 に変えてみましたが、リージョンを変えると成功しないみたいです。

alanedwardes/Estranged.Lfs – Github

どなたか検証お願いします。 m(_ _)m

追記

わかった気がします。

同じリージョン内のでしかオブジェクトが参照できないぽいので、
自分のS3に東京リージョンでバケット作って、 git-lfs.yaml と git-lfs-lambda.zip をコピーしました。
パブリックアクセス許可にしたので、下のURLで誰か試してください。

https://console.aws.amazon.com/cloudformation/home#/stacks/new?region=ap-northeast-1&templateURL=https://s3-ap-northeast-1.amazonaws.com/kanemu-infrastructure-ap-northeast-1/git-lfs/git-lfs.yaml

続きを読む

「Microservices Meetup vol.6」に行ってきた

2017/1/19のデイリーストックランキングにランクインしました。

【毎日自動更新】Qiitaのデイリーストックランキング!ウィークリーもあるよ_-_Qiita.png


いままでの。
「Microservices Meetup vol.2」行ってきたメモ。
「Microservices Meetup vol.4」に行ってきた

図らずして1回おきに参加してますね。

Connpassのイベントページ

少し間が空いてしまったがまた定期的に開催していきたいです。
登壇したいよーって人は直接リプ投げてください
by @qsona さん

Microservices on AWS by @Keisuke69 さん

Keisuke Nishitani @ AWS

Specialist Solutions Architect

サーバーレスアプリケーション開発ガイド 2月発売

マイクロサービスのポイント

  • 管理運用まで含めて分散型
  • 各コンポーネントが独立している
    • 単独で実行できないのは適切に設計されていない

独立して分散していると負荷の高い機能の単位でスケールさせられる=コスト効率が良い

  • 一つのことをうまくやる

    • 複雑化したら分割する
  • 多言語

    • チーム(機能)にはそれぞれの問題に対して適切なツールを選択する自由がある
    • OS、言語、各種ツールに至るまで最適なアプローチを目指せる
  • 検索:Elasticsearch / Solr

  • ソーシャル:グラフDB

  • ログデータ:Cassandra

  • セッション:Redis

†AWSでは:AWSには100ちょいのサービスがあって、その中で更にチームが分かれている。
各チームに社内標準の開発プロセスは存在しない。

  • ブラックボックス

    • 詳細は外部のコンポーネントに公開されない
  • DevOps
    • マイクロサービスにおける組織原理

†AWSでは:運用のみのチームはない。オンコールも開発チームが請け負う

  • 俊敏性

    • 狭い範囲のコンテキストで活動=サイクルタイムが短い
    • システムもシンプル
    • パラレルな開発とデプロイが可能
  • イノベーション

    • 選択に対する権限と責任を持つのでイノベーションを起こしやすい
    • DevとOpsの対立がないため、効率化やイノベーションが起こしやすい
  • 拡張性

    • 適切に非干渉化されてていることで水平方向に単独にスケールできる
  • 可用性

    • ヘルスチェック、キャッシング、隔壁、サーキットブレーカーと言った仕組みは全体の可用性を向上させる

課題

  • 分散型であることは難しい

    • システム数が増える
    • 協調動作の難しさ
    • コンポーネント間のコミュニケーションメッセージ増によるレイテンシ低下
    • ネットワークの信頼性は無限ではない、帯域も無限ではない
  • 移行が大変
    • モノリシックなシステムの分割は難しい
  • 組織
    • 組織体制の変更が必要になるが、それは大変なこと
  • アーキテクチャの難易度
    • 非同期通信
    • データ整合性
    • やっぱりココが一番の課題
    • サービスディスカバリ
    • 認証
  • 運用の複雑さ

アーキテクチャ

一番シンプルなパターン

CloudFront – ALB – ECS – datastore(ElastiCache, Dynamo, )
|
S3

  • バックエンドをRESTfulなAPIにしたSPA
  • 静的なコンテンツはS3とCloudFront
    • CDN通すことでレイテンシが上がることもある
    • キャッシュとの併用を検討
  • ECSとAutoScalingをALBとともに使うことが多い
    • ALB(L7LB)でアプリレベルの情報をルーティング
    • コンテナインスタンスにリクエストを分散
    • ECSのコンテナを負荷に応じてスケールアウト/インする

コンテナのメリット

  • Portable
  • Flexible
  • Fast
    • 軽量で早い
    • ポータビリティと関連して開発サイクルも早く
  • Efficient

  • 一貫性のある環境

  • バージョン管理出来る

Dockerの特徴

  • Package
  • Ship
  • Run

ECS

  • 複数のコンテナをEC2のクラスタ上で一元管理

    • まだTokyoにきてないけど、FargateでEC2の管理もいらなくなるよ
    • EKS(Kubernetes)も発表されています

データストア

  • インメモリ

    • Memcached, Redis
    • ElastiCache
    • DB負荷の軽減
  • RDBMS
    • 無限のスケーリングには向いていない
    • Amazon RDS
  • NoSQL
    • 水平スケーリングをサポートするものが多い
    • テーブル結合ができないのでロジックの実装が必要になる場合も
    • Amazon DynamoDb, KynamoDB Accelarator(DAX)

APIの実装

  • APIの設計、改善、デプロイ、モニタリング、保守派手間がかかる
  • 異なるバージョンが混在すると大変

Amazon API Gateway

  • 複数バージョンとステージ
  • Cognite User Poolsと連携して認証を追加
  • スロットリング、モニタリング
  • バックエンドとしてLamdbaが使える

サーバーレス

  • サーバーはないに越したことはない
  • スケーリングと高可用性の担保が大変
CloudFront - API Gateway - Lamdba - datastore
   |                            (ElastiCache, Dynamo)
   |
  S3

課題をAWSでどうするか

サービスディスカバリ

  • お互いの死活確認や発見
  • ハードコードするとスケールできない

    • メタデータをどこに置くか
  • ALBを利用したサービスディスカバリ

  • DNS(Route53)のサービスディスカバリ

    • VPC単位の振り分け
  • ECSイベントストリームを使用したサービスディスカバリ

    • CloudWatchイベントで拾ってキック
    • LamdbaでRoute53に登録
    • これが一番多いかも
  • DynamoDBを使用したサービスディスカバリ

    • DNSキャッシュの問題がない
    • 自由度が高い
    • 実装が大変
    • DynamoDBストリームを活用して他のサービスへステータス変更を反映
  • イベントベースのアーキテクチャ

    • イベントで処理する
    • いわゆる結果整合性とも関連
    • Dual Write Problem
    • Event Sourcing
      • 状態の記録ではなく状態の変更「イベント」を記録
      • Amazon Kinesis
      • kinesisにpublishして他サービスはsubscribe
      • S3にログを残す
      • トランザクションログの仕組み

モニタリング

  • CloudWatch

    • ログファイルの一元化
    • CloudWatch LogsかS3に保存可能
    • ECSはCloudWatch Logsに一元化できる

分散トレース

  • AWS X-Ray
  • 複数サービスに分散したリクエスト処理を透過的に追える

LogWatchの先に Kinesis FireHorse – ほにゃほにゃ

スロットリング

  • 大事だよ

リトライ

  • 多くのエラーはリトライでカバーできるものが多い
  • リトライ多発で過負荷になることがある
    • Exponential back offもしくはフィボナッチ数列を利用したリトライ感覚の調整
    • 更に乱数でゆらぎを付けて重ならないように

LT:クラウド型医療系業務システムと Microservices への歩み by @hashedhyphen さん

https://speakerdeck.com/hashedhyphen/kuraudoxing-dian-zi-karutesisutemuto-microservices-hefalsebu-mi

クラウド型電子カルテシステムの話

  • メインロジックをRails
  • 常時接続をNode.js
  • バックエンドをScala
  • 基盤はAWS

ここに至る道のり

ファーストリリース前

  • レコメンドは大量のデータが必要

  • メインロジックで実現させようとすると厳しかった

    • Threadとか試したけど……
    • 別アプリケーションとして分離
    • JVM上でScala + Skinnyでスレッドアプリケーションンを実装
    • Microservicesちっくな構成へ
  • 障害検知時

    • メインロジック内のプロトタイプ版が動く!

会計機能リリース前

  • お金を扱う機能は安定性が欲しい
  • Scala + Cats による実装を別エンドポイントとして実装
  • マイクロサービスっぽい作りになっていたから自由度のある技術選択が出来た

まとめ

  • ちょっとマイクロサービス化したことで自由度が上がった
  • 原理主義的にならなくても恩恵がある
  • エンジニアの伸びしろ!

FrontEndからみるmicroserviceとBackendからみるmicroservice by @taka_ft さん

Takahiro Fujii @ Rakuten Travel

楽天内でも採用アーキテクチャはサービスによって異なる。

サービスとしては

  • コンシューマ向け
  • Extranet
  • In-house
  • other
    • ここまではWEBとモバイルがあって、100以上のAPIを利用する
  • API(内部APIを直接利用)

Phase 1

  • 大きなモノリシックなアプリだった
  • 機能がどんどん増えていく
    • 機能別で複数のモノリシックなアプリに分割
    • その後フロントエンドとバックエンドに分割

Phase 2

  • ドメインモデルを整理
  • なるべくRESTfulで作るようになっていった
  • 大きなAPIから小さなAPIに分離
  • I/Fの決定に時間がかかるようになった
  • API呼び出しが大変になった
  • Microservicesっぽくなってきた 2014年くらい

  • 国内予約、海外予約で多言語化だけではない商習慣に根ざしたドメインの差異による重複ロジックの増殖が起きた

  • Microservicesっぽくなってきたが、どんどん品質が下がっていった

Phase 3

(ちょうど前日にプレスリリース)サイト前面刷新に向けての取り組み

  • FrontendsはJavaScriptに刷新
  • API GatewayにKong

組織

  • フロントエンドチームが2人から始まって半年でReactエンジニアを集めて20人以上に

    • 日本人は2, 3人

UI Component

  • SpringからJavaScriptでSPAに変更
  • zeplinのデザインモックからUI Componentを実装するようになった
  • Storyboardを使ってUI Coponentを管理
  • ドメインを含むUI Componentはドメインと結び付きが強いことが多い=専用のオーケストレーションAPIを用意してUI Componentないで処理を閉じることが出来る
  • レスポンシビリティを明示的に定義

    • どこまでフロントエンドでやるか、どこからAPIからもらうか
  • フロントエンドの「使いやすいレスポンス」の要求

  • バックエンドの「汎用的でシンプルなレスポンス」の希望

    • これらのバランスを取る
  • API Gatewayはフロントエンド(UI)チームの管轄

    • ただし状況によってはバックエンド側に持っていくことも検討するつもり

LT: “マイクロサービスはもう十分”か? by @qsona さん

https://speakerdeck.com/qsona/enough-with-the-microservices

POSTDに投稿していた翻訳記事。

スタートアップ企業のほとんどはマイクロサービスをさいようすべきではない

銀の弾丸はない。

「チーム間の依存性」の解決にマイクロサービスというアプローチは違う。疎結合と分散は別。

組織が大きくなると、複数チームが1つのコードベースを触るようになる。そしてマイクロサービス化したくなる。

しかし、モノリスの分割で十分。

チームとは何か

  • 自律的に動けるべき

    • チームが増えるとコミュニケーションコストや、しがらみ
  • チームの分割は目的にそった分割を行う

  • 悪い分割の例: Dev / Ops

  • 依存度が低いほど自律的に動ける

  • High Output という本

  • 使命型組織/技術型組織

    • Micorservicesは使命型組織
    • 組織間が密ならサービス間も密になる

話戻して

  • スタートアップは組織をキレイに分割することが難しい
  • 分割しても密になってしまう

  • 大きなモノリスになるとやはり分割は難しい

    • ドメインの意識は必要
  • マイクロサービス設計しなくても「マイクロサービス精神」で開発すると効果的なのではないか

FiNCが初期からマイクロサービスでやってた理由

  • 単独の事業にできるような一つ一つのサービスを組み合わせて提供してきたから

あとで読み返して修正します。
資料パスの追加とかも。

続きを読む

AWSとAzureとGCPを比較してみる – FaaS編

FaaSについてAWSとAzureとGCPを比較してみました。

注)

1. FaaS比較表

AWS Azure GCP
Lambda Functions Cloud Functions***
言語 Python,
node.js,
java,
C#,
go
ランタイムバージョン1.X
C#,
JavaScript,
F#,
Python*,
PHP*,
TypeScript*,
バッチ (.cmd、.bat)*,
Bash*,
PowerShell*

ランタイムバージョン2.X
C#**,
JavaScript**,
Java**
node.js***
最大実行時間 5分 10分 (従量課金プラン)
無制限 (App Serviceプラン)
9分
直接HTTPアクセスを受け付けるか 受け付けない(API Gatewayと連携必要) 受け付ける 受け付ける
トリガー Amazon S3,
Amazon DynamoDB,
Amazon Kinesis Data Streams,
Amazon Simple Notification Service,
Amazon Simple Email Service,
Amazon Cognito,
AWS CloudFormation,
Amazon CloudWatch Logs,
Amazon CloudWatch Events,
AWS CodeCommit,
スケジュールされたイベント (Amazon CloudWatch Events を使用),
AWS Config,
Amazon Alexa,
Amazon Lex,
Amazon API Gateway,
AWS IoT ボタン,
Amazon CloudFront,
Amazon Kinesis Data
Blob Storage,
Cosmos DB,
Event Hubs,
HTTP,
Microsoft Graph Events(2.Xのみ),
Queue storage,
Service Bus,
Timer,
Webhooks(1.Xのみ)
HTTP,
Cloud Storage,
Cloud Pub/Sub

*試験段階
**プレビュー
***ベータ

2. 対応言語の比較

言語の種類は試験段階とプレビューを含めればAzureが一番多いのですが、正式リリースされたものに限定すればAWSの方が種類が多いです。
一方GCPは機能自体がベータリリースなので、まだこれからといった感じでしょうか。

AzureはBashにも対応しているのが特徴です。運用系のシェルスクリプトをFaaS化すれば、スクリプト用のサーバが不要になりますね。

3. 最大実行時間

最大実行時間はAzureの10分(要host.jsonのfunctionTimeoutプロパティ変更)、GCPの9分に対しAWS Lamdbaは5分と約半分です。実際にAWS Lambdaを利用していると5分の壁を結構感じます。この点は他クラウドが羨ましいですね。
2017年のRe:InventでAWSはFargateというコンテナのサービスをリリースしましたが、このサービスがlambdaが5分以上実行できないことに対するAWSからの回答のように感じます。

4. 直接HTTPアクセスを受け付けるか

AWS lambdaだけ直接HTTPアクセスを受け付けることができません。HTTPアクセスを受け付けるには、API Gatewayと連携する必要がありますが、多機能な分やや設定が面倒な印象です。(但しAPI経由でLambdaを起動することは可能)

まとめ

AWS Lambdaのリリース後、Azure・GCP・Bluemix(現IBM Cloud)は超特急で追従しました。AWS LambdaがIT業界に与えたインパクトはとても大きかったと思います。
現在は「FaaS無ければばクラウドにあらず」といったところでしょうか。

また、AWS GreengrassやAzure IoT Edge**というエッジにデプロイするサービスも出てきています。
将来AWS LambdaがiPhoneやApple Watchにデプロイできるようにならないかなーと妄想中です。

**プレビュー

続きを読む

AWS初心者がLambdaでLINE BOTを作る

LINE BOTでオウム返しのBOTを作ります。
正直AWSを触ったことが無いので、よく分からず困ったので同じように困る人が出ないように
記事にすることにしました。

言語・ツール選定

私はRaspberry pi & PHPでLine botを作ったことが有る。
内容としては位置情報を渡すと近くのコンビニを教えてくれるものだ。
しかし、Raspberry piでLine botを作ると、常にRaspberry piを起動しておく必要があり電力的に優しくなく、
サーバの構築や認証鍵の設定が面倒だった。
そこでサーバレスのLine Botを作ろうと思いました。
私が選択したのはLambda+Node.js。
選択肢としてGAS(Google App Script)もあったけれど今後の拡張性を考えた時、
ES6が簡単に使えない(変換すれば使えるが面倒な)のが嫌だと思ったのでやめました。

環境構築

LINE Developers
https://developers.line.me/ja/
↑LineBotを作るためにアカウント取得する
【公式】クラウドならアマゾン ウェブ サービス (AWS)
https://aws.amazon.com/jp/
↑AWSの登録する(日本アカウントだと駄目?)

おわり

使用するAWS

初心者なのでLambdaだけで作れると思ってました。
すると、そういう訳じゃなくいくつかサービスを利用しないといけないことが分かりました。

Lambda
サーバレス、コンピューティングサービス。
サーバ構築しなくてもイベントやトリガーで実行出来るサービス。
使用できる言語はNode.js、Java、C#、Python。
無料枠は
月1,000,000リクエスト
秒400GB 月間400,000GB
有料枠は
1,000,000リクエストごとに0.20USD(約22.6円)
他のサービスは別料金

API Gateway
API の作成、配布、保守、監視、保護が出来る。
Lambdaの関数を動作させる(キックする)ために使う。
料金はデータ送出量で変わる、使用する地域ごとに料金が違う。
無料期間が終わったら有料。

IAM (これはサービスではなく仕組み)
アクセスを安全に制御するための仕組み。権限(role)の設定をする。
Lambdaでログを取ったりするときに設定が必要。

CloudWatch Logs
ログを取るために使う。
無料枠Amazon CloudWatch でのメトリックス 10 個、アラーム 10 個、API リクエスト 1,000,000 件
まぁ、サービスがうまく稼働したら止めると思うので有料枠は気にしない。

api Gatewayの設定

+APIの作成からAPI名とエンドポイントタイプを決める。

エンドポイントとは?
ゲートウェイ VPC エンドポイント??

Amazon API Gateway で、REST API およびカスタムドメインの作成時に 2 種類の API エンドポイントから選択できるようになりました。
リージョンの API エンドポイントは、REST API をデプロイするのと同じ AWS リージョンからアクセスできる新しいタイプのエンドポイントです。
これにより、API リクエストが REST API と同じリージョンから発信される場合に、リクエストのレイテンシーを減らすことができます。
さらに、独自の Amazon CloudFront ディストリビューションとリージョンの API エンドポイントの関連付けを選択できるようになりました。
API エンドポイントの 2 つ目のタイプは、エッジ最適化 API です。
エッジ最適化 API は、API ゲートウェイによって作成および管理される CloudFront ディストリビューション経由でアクセスするエンドポイントです。
以前は、エッジ最適化 API は、API ゲートウェイを使用して API を作成する際のデフォルトオプションでした。

つまり、どーいうことだ?
何か、ドメイン関係というのは分かった、
エンドポイントとは?
https://qiita.com/NagaokaKenichi/items/6298eb8960570c7ad2e9

なるほど。
URIの設定のことね。
んで、エンドポイントタイプの違いって具体的に何?

Regional:リージョン(地域が一緒なら早い)
Edge Optimized:エッジ最適化(経由して最適化する)
って感じかな?
今回はAPI GatewayもLambdaも同じハイオワにしたので、リージョンで良さそう。

API 名を決めないと何故かエラーが出る模様。訳が分からない。泣いた。
http://kubotti.hatenablog.com/entry/2017/01/13/161022

リソースで、ポストが出来るように設定しLumbdaの関数が完成したらAPIのデプロイをする。
image.png

設定で、ARNの設定をする。
image.png
Lumbdaへのアクセスできる設定とログが取れるようアクセスを許す設定した物を割り当てる。
あ、後クライアント証明書も作った。

Lamubdaの設定

image.png
実行ロールの設定でログ監視とAPIGatewayからのアクセスが可能なアカウントを設定する。

コードはこんな感じ

index.js
'use strict';

const https = require('https');
const querystring = require('querystring');

exports.myHandler = (event, context, callback) => {
    console.log('EVENT:', event);
    var event_data = JSON.parse(event.body);
    console.log('EVENT:', JSON.stringify(event_data));
    const messageData = event_data.events && event_data.events[0];
    console.log("TEST:" + JSON.stringify(messageData.message.text));
    var id = messageData.source.userId;
    if(messageData.source.groupId != null && messageData.source.groupId.length > 0){ //グループからのメッセージ
        id = messageData.source.groupId;
    }

    if(!check(messageData.message.text)){
        callback(null, 'Success!');
        return;
    }
    var postData = JSON.stringify(
    {
        "messages": [{
            "type": "text",
            "text": getResponseMessage(messageData.message.text)
        }],
        "to": id
    });
        // @TODO KMSで管理
    var ChannelAccessToken = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; //チャンネル設定

    //リクエストヘッダ
    var options = {
        hostname: 'api.line.me',
        path: '/v2/bot/message/push',
        headers: {
            'Content-Type': 'application/json; charset=utf-8',
            'Content-Length': Buffer.byteLength(postData),
            'Authorization': 'Bearer ' + ChannelAccessToken
            },
        method: 'POST',
    };
    console.log(JSON.stringify(options));
    //APIリクエスト
    var req = https.request(options,  function(res){
        res.setEncoding('utf8');
        res.on('data', function (body) {
            console.log(body);
            context.succeed('handler complete');
        });
    }).on('error', function(e) {
        context.done('error', e);
        console.log(e);
    });

    req.on('error', function(e) {
        var message = "通知に失敗しました. LINEから次のエラーが返りました: " + e.message;
        console.error(message);
        context.fail(message);
    });

    req.write(postData);
    req.on('data', function (body) {
            console.log(body);
     });
    req.end();
    console.log("TEST:" + postData);
    callback(null, 'Success!');
};

var getResponseMessage = function(message) {
    message = message.toLowerCase();
    message = message.replace('@bot ', '');
    switch (message) {
        default:
            return message;
    }
};

var check = function(message) {
    message = message.toLowerCase();
    var pattern = '@bot ';
    return message.indexOf(pattern) === 0;
};

・↑ではセキュリティガバガバなので、Lineからのアクセスかどうか検証するためにChannel SecretのIDを見るプログラムと、ChannelAccessTokenは管理するものを入れたほうが良いと思います。
・replyでも良いんですが、処理に時間がかかると処理されないらしいので今後重い処理を入れることを考えてpushにしました。
(参照:https://developers.line.me/ja/docs/messaging-api/reference/)
・グループに突っ込んだ時に関係なく反応するのがうざかったので、@botと頭につくと返すようにしました。

image.png
LINE DevelopersのChannelAccessTokenはここのやつをコピペ。

image.png
上のような形で登録する。(念のため443はつけたほうが良い)

image.png
↑のエラーが出ますが問題なく動きます。

結果

image.png
出来た。

まとめ

AWSのいい勉強になった。
私のセキュリティ意識が低いことが分かった。
よくわからないところからAPIリクエストが飛んで来ていたので気をつけたい。
LineBotはちょっと前とはAPIの書き方が変わっていた&単純にNode.jsに慣れていないというのもあって、ちょっと苦戦した。
今後は、飲み会幹事BOTなどにしていきたい。
たのしかった。

参考

https://qiita.com/akihito_nagai/items/f284ef495da380f368cc
https://qiita.com/ykyk1218/items/232437bc92f13d02a8a2
https://qiita.com/daikiojm/items/c05882bedb82d7ed664e

続きを読む

ZendeskのチケットデータをAmazon Elasticsearch Serviceへ自動的にアップロードする | Developers.IO

Amazon Elasticsearch ServiceはAmazon VPCプライベートサブネットに作成; Amazon API Gateway経由でAWS Lambdaを実行させる; Zendeskの自動化とwebhookでAmazon Elasticsearch Serviceへデータを入れるまでを自動化する; Amazon Elasticsearch Serviceへ入れるデータスキーマ … 続きを読む

AWS Mobile Hub による react-native アプリ開発(Cloud API REST呼び出し)

AWS mobile-hub が react-native に対応したとのことなので、マニュアル (Developer Guide) を元にサンプルアプリを作成・試してみる。

今回は Dynamodb にてテーブルを作成し、CRUDアプリケーションをサンプルに沿って作成してみた。

はじめに

前回の記事から Dynamodb に登録・参照する簡単なサンプルプログラムを実行してみる。

Backend (Mobile Hub) の設定

テーブルの作成

コマンドから下記を実行

awsmobile database enable --prompt

マニュアル通りの Notes テーブルを作成

Welcome to NoSQL database wizard
You will be asked a series of questions to help determine how to best construct your NoSQL database table.

? Should the data of this table be open or restricted by user? Open
? Table name Notes

You can now add columns to the table.

? What would you like to name this column NoteId
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteTitle
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteContent
? Choose the data type string
? Would you like to add another column No


Before you create the database, you must specify how items in your table are uniquely organized. This is done by specifying a Primary key. The primary key uniquely identifies each item in the table, so that no two items can have the same key.
This could be and individual column or a combination that has "primary key" and a "sort key".
To learn more about primary key:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey


? Select primary key NoteId
? Select sort key (No Sort Key)

You can optionally add global secondary indexes for this table. These are useful when running queries defined by a different column than the primary key.
To learn more about indexes:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes

? Add index No
Table Notes saved

CRUD API の作成

下記コマンドにて cloud-api を作成

awsmobile cloud-api enable --prompt

先ほど作成した Notes テーブルから生成を選択。ログイン必須を選択した。

This feature will create an API using Amazon API Gateway and AWS Lambda. You can optionally have the lambda function perform CRUD operations against your Amazon DynamoDB table.


? Select from one of the choices below. Create CRUD API for an existing Amazon DynamoDB table
? Select Amazon DynamoDB table to connect to a CRUD API Notes
? Restrict API access to signed-in users Yes
Adding lambda function code on: 
/Users/masanao/dev/awsmobile/awsmobilejs/backend/cloud-api/Notes/
...
Path to be used on API for get and remove an object should be like:
/Notes/object/:NoteId

Path to be used on API for list objects on get method should be like:
/Notes/:NoteId

JSON to be used as data on put request should be like:
{
  "NoteTitle": "INSERT VALUE HERE",
  "NoteContent": "INSERT VALUE HERE",
  "NoteId": "INSERT VALUE HERE"
}
To test the api from the command line (after awsmobile push) use this commands
awsmobile cloud-api invoke NotesCRUD <method> <path> [init]
Api NotesCRUD saved

設定の反映

下記コマンドにて AWS のモジュールを作成

awsmobile push

作成に少し時間がかかるので、完了するのを待つ。

building backend
   building cloud-api
      zipping Notes
   generating backend project content
   backend project content generation successful
done, build artifacts are saved at: 
/Users/masanao/dev/awsmobile/awsmobilejs/.awsmobile/backend-build

preparing for backend project update: awsmobile
   uploading Notes-20180108140947.zip
   upload Successful  Notes-20180108140947.zip
done

updating backend project: awsmobile
awsmobile update api call returned with no error
waiting for the formation of cloud-api to complete
cloud-api update finished with status code: CREATE_COMPLETE

Successfully updated the backend awsmobile project: awsmobile

retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud

フロントエンドの構築

App.js を編集・CRUDのコードを書く。

モジュール import

import { API } from 'aws-amplify-react-native';

CRUDコードの記述

サンプルコードをそのまま貼り付けすることとした。

state の追加

state = {
  apiResponse: null,
  noteId: ''
     };

  handleChangeNoteId = (event) => {
        this.setState({noteId: event});
}

saveNote 関数の追加

  // Create a new Note according to the columns we defined earlier
    async saveNote() {
      let newNote = {
        body: {
          "NoteTitle": "My first note!",
          "NoteContent": "This is so cool!",
          "NoteId": this.state.noteId
        }
      }
      const path = "/Notes";

      // Use the API module to save the note to the database
      try {
        const apiResponse = await API.put("NotesCRUD", path, newNote)
        console.log("response from saving note: " + apiResponse);
        this.setState({apiResponse});
      } catch (e) {
        console.log(e);
      }
    }

getNote 関数の追加

  // noteId is the primary key of the particular record you want to fetch
      async getNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.get("NotesCRUD", path);
          console.log("response from getting note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

deleteNote 関数の追加

  // noteId is the NoteId of the particular record you want to delete
      async deleteNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.del("NotesCRUD", path);
          console.log("response from deleteing note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

rennder 関数の変更(Viewの変更)

<View style={styles.container}>
        <Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
        <Button title="Save Note" onPress={this.saveNote.bind(this)} />
        <Button title="Get Note" onPress={this.getNote.bind(this)} />
        <Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
        <TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  textInput: {
      margin: 15,
      height: 30,
      width: 200,
      borderWidth: 1,
      color: 'green',
      fontSize: 20,
      backgroundColor: 'black'
   }
});

作成したApp.js ソース

react-native のサンプルソースを元にしたため、一部不要なソースコードがありますがそのままにしています。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import Amplify from 'aws-amplify-react-native';
import { API } from 'aws-amplify-react-native';
import { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  Button,
  TextInput,
  View
} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,n' +
    'Shake or press menu button for dev menu',
});

class App extends Component<{}> {
  constructor(props) {
        super(props);
    this.state = {
      apiResponse: null,
      noteId: ''
    };
  }

  handleChangeNoteId = (event) => {
        this.setState({noteId: event});
  }

  // Create a new Note according to the columns we defined earlier
    async saveNote() {
      let newNote = {
        body: {
          "NoteTitle": "My first note!",
          "NoteContent": "This is so cool!",
          "NoteId": this.state.noteId
        }
      }
      const path = "/Notes";

      // Use the API module to save the note to the database
      try {
        const apiResponse = await API.put("NotesCRUD", path, newNote)
        console.log("response from saving note: " + apiResponse);
        this.setState({apiResponse});
      } catch (e) {
        console.log(e);
      }
    }
     // noteId is the primary key of the particular record you want to fetch
      async getNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.get("NotesCRUD", path);
          console.log("response from getting note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

     // noteId is the NoteId of the particular record you want to delete
      async deleteNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.del("NotesCRUD", path);
          console.log("response from deleteing note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

  render() {
    return (
<View style={styles.container}>
        <Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
        <Button title="Save Note" onPress={this.saveNote.bind(this)} />
        <Button title="Get Note" onPress={this.getNote.bind(this)} />
        <Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
        <TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  textInput: {
      margin: 15,
      height: 30,
      width: 200,
      borderWidth: 1,
      color: 'green',
      fontSize: 20,
      backgroundColor: 'black'
   }
});
export default withAuthenticator(App);

実行

react-native run-ios

スクリーンショット 2018-01-08 18.51.18.png

テキストボックス内に NoteID を入力して “Save Note” を押すと、保存されます。

内容はソースコードに書いてあるように固定文字が登録されます。
あくまでサンプルソースということで・・・。

AWS 確認

Mobile Hub の Cloud Logic には下記のように登録されています。

スクリーンショット 2018-01-08 19.01.38.png

Lambda も確認すると、NotesCRUD-xxxx という名前で登録されているのが確認できます。

スクリーンショット 2018-01-08 19.02.59.png

DynamoDB も同様にテーブルが作成され、データも登録されているのがわかります。

スクリーンショット 2018-01-08 19.24.53.png

通常のCRUD であればほぼノンプログラミングでバックエンドロジックが生成できますね。

続きを読む

AWS Amazon API Gateway to return ASIN & Sales Rank from EAN/UPC

PHP & ソフトウェアアーキテクチャ Projects for £10 – £20. I currently use a web scraping program to give me ASIN, Current Price, Title & Sales Rank when I search using an EAN/UPC number. For example on Amazon.co.uk if I search for the EAN 5055374547342 It w… 続きを読む

Apex/UpでAWSにサーバーレスアプリケーションをデプロイする

サーバーレスアーキテクチャ上で使い慣れたWebアプリケーションフレームワークを動かしたい」。こんな風に考えたことがある方は少なからずいらっしゃるかと思います。 awslabsでも、AWS Lambda + Amazon API GatewayでExpressやSpringなどのJavaのフレームワークを使う動かすためのライブラリや実装例が公開され … 続きを読む

Serverless+SPAで低費用な個人サービス構築のススメ

Serverless+SPA構成

最近低費用でサービス運用できないかと、色々試してたのをまとめてみます。
Serverless構成だとEC2と比べて費用が安くなり、SPAであればルーティングがサーバ側制御ではないためS3のバケットに静的html、css、js、画像の配置にてサービス構築が可能となります。
SPAにはReactJSを採用してますが、VueやUnityとかのサービスでもいけると思います。
良い点ばかりではなく、デメリットも結構あるのでそれを考慮した上で導入しましょう。
個人でお金をかけたくなくてサービス運用したい場合などには良いかもしれません。
(Lambdaの無料枠+アクセス量のみで計算される。まだ1ヶ月経ってないからきちんと計測できていませんがドメイン費込みでも月$2もかからないかもしれない・・・)

メリット
・ とにかく費用が安い
・ AWSの設定周りの変更だけでスケールする
デメリット
・ 利用しているサービスが多いため、デプロイ時などの障害発生ポイントが多い、各サービスに関する知識もある程度必要
・ Lambdaに関するAPI処理実行時の起動速度制限やメモリ上限がある
・ LambdaのNodeJSのバージョンが6系(async/awaitがまだ使えない)
・ DBがDynamoDBでないとスケールしずらい
・ バックエンド側は完全AWS依存のため、別の環境に簡単には移行できない

Serverlessについてや各種AWSサービス料金に関しては下記参考にしてください
AWS料金早見表

ServerlessアーキテクチャとSPA(ReactJS)でサービス構築するには
以下のような構成になります。

スクリーンショット 2017-12-29 5.37.03.png

利用用途:
– CloudFormation: Serverlessフレームワーク経由でデプロイジョブを実行
– IAM: Lambdaの実行権限やDynamoDBアクセスをLambdaに付与
– CloudWatch: ログ収集
– Route53: ドメイン割当
– ACM: SSL証明書作成、SSL認証
– CloudFront: CDN、ドメインでの公開配信
– S3: SPAファイルを配置(ReactJSリリースビルド済みのファイル群)
– API Gateway: APIのエンドポイント作成
– Lambda: APIの処理を実行
– DynamoDB: データ保存

Serverlessフレームワーク+ReactJSによる実装

下記リンクにてサンプルサービスをデプロイしました(そのうち消すかも)
https://static.teradonburi.info
サンプルのソースコードは次gitにあります。
serverless-react

API側の実装とデプロイはserverlessフレームワークにて行います。
serverlessフレームワークはバージョン別で破壊的変更が入り、互換性がないことが多いので注意です。
今回はv1.24.1にて実装を行っています。
下記コマンドでServerlessフレームワークをインストールします。

$ npm install -g serverless

サンプルの構成です。
clientフォルダにはReactJSでのフロントエンドの実装、
serverless-apiフォルダにはServerlessフレームワークでのバックエンドの実装がそれぞれ格納してあります。
configフォルダにはnode-configの設定ファイルがあります。今回はAPIサーバの起動ドメインの設定を記述してあります。
今回はTODOサービスのサンプルを作成しました。
ReactJSからAPI経由でのDynamoDBのCRUD操作を行います。(create.js、list.js、update.js、delete.js)

├── client
│   ├── package.json
│   ├── src
│   │   ├── App.js
│   │   ├── components
│   │   │   ├── NotFound.js
│   │   │   ├── TodoPage.js
│   │   │   └── TopPage.js
│   │   ├── index.js
│   │   ├── reducer
│   │   │   ├── reducer.js
│   │   │   └── todo.js
│   │   └── static
│   │       └── index.html
│   ├── webpack.build.js
│   ├── webpack.config.js
│   └── yarn.lock
├── config
│   ├── default.js
│   └── production.js
└── serverless-api
    ├── lib
    │   └── dynamodb.js
    ├── offline
    │   └── migrations
    │       └── todo.json
    ├── package.json
    ├── serverless.yml
    ├── src
    │   ├── create.js
    │   ├── delete.js
    │   ├── list.js
    │   └── update.js
    └── yarn.lock

今回のサンプルをデプロイする上でドメインが必要です。(ローカルでの起動は問題はない)
ドメインを未購入の人は下記記事を参考にドメインの購入をしてください。(ドメインがないとデプロイ時にAWS側で自動割当されたものとなり、色々不便なので)
Amazon Route 53でドメインを購入する

ローカルでの起動

次のコマンドでローカルでのサーバのシミュレーション起動ができます。

server.sh
$ cd serverless-api
$ yarn
$ sls dynamodb install
$ SLS_DEBUG=* sls offline start

デバック方法は下記記事を参考にしてください(Node6.1なので–inspectオプションがまだ使えない・・・)
Serverlessフレームワークでデバッグする(ブレークポイントかける)方法

上記サーバを立ち上げた状態で
クライアント側は下記コマンドでwebpack-dev-serverにて起動します。

client.sh
$ cd client
$ yarn 
$ NODE_CONFIG_DIR=../config webpack-dev-server
# ブラウザを開く
$ open http://localhost:3001

スクリーンショット 2017-12-29 5.26.35.png

スクリーンショット 2017-12-29 5.26.55.png

デプロイ(初回)

クライアント(ReactJS)のリリースビルドとserverlessフレームワークでのデプロイを行います。
ローカル時は下記node-configの設定ファイルにてクライアント側から指定のAPIサーバに対してリクエストを投げます。

config/default.js
module.exports = {
  API_SERVER: 'http://localhost:3000',
  PORT: 3000,
}

リリースビルド時は下記production.jsの方が参照されます。
teradonburi.infoの部分は各自取得したドメインで書き換えてください
私はapi.(ドメイン名)にしました。

config/producation.js
module.exports = {
  API_SERVER: 'https://api.teradonburi.info',
}

クライアント側は下記コマンドでwebpackにてリリースビルドを行えるようにしました。
distフォルダにビルドされたリリース用のSPAのファイル群が作成されます。

$ cd client
$ yarn
$ rm -rf dist;NODE_ENV=production NODE_CONFIG_DIR=../config parallel-webpack -p --config webpack.build.js

ReactJSでのSPA作成方法やリリースビルドの詳細に関しては下記を参考にしてください。
ReactJSで作る今時のSPA入門(基本編)
ReactJSで作る今時のSPA入門(リリース編)

serverlessの設定ファイルのclientのbucketNameのteradonburi.infoの部分は各自取得したドメインで書き換えてください
私はstatic.(ドメイン名)にしました。

serverless.yml
custom:
  client:
    bucketName: static.teradonburi.info

下記コマンドでCloudFormation経由で各サービスにデプロイします。
(CloudFormation、API Gateway、Lambda、S3、DynamoDB、CloudWatch)

deploy.sh
# デプロイ先のAWSアカウントを明示的に指定(~/.aws/credentialsのアカウントが複数ある場合)
$ export AWS_ACCESS_KEY_ID=(アクセスキー)
$ export AWS_SECRET_ACCESS_KEY=(プライベートキー)
$ SLS_DEBUG=* sls deploy
# distフォルダを新規作成S3バケットにアップロード
$ SLS_DEBUG=* serverless client deploy

デプロイが完了するとCloudFormationのスタックが作成されます。

スクリーンショット 2017-12-29 3.41.14.png

API Gatewayのエンドポイントが作成されます。

スクリーンショット 2017-12-29 3.44.03.png

Lambdaの関数が作成されます。

スクリーンショット 2017-12-29 3.40.58.png

DynamoDBのテーブルが作成されます。

スクリーンショット 2017-12-29 3.41.37.png

S3のバケットが作成されます。

スクリーンショット 2017-12-29 3.39.37.png

serverless.ymlについて

serverless.ymlにはデプロイの設定情報を記述します。
pluginsに追加のプラグインを記述します。
ローカル実行用にserverless-offlineserverless-dynamodb-localを導入しました。
S3へdistフォルダアップロード用にserverless-finchを導入しています。

plugins:
  - serverless-offline
  - serverless-dynamodb-local
  - serverless-finch

providerにはlamdaから実行できるIAMロールの指定を記述します。
今回はLambdaからDynamoDBアクセスするため、DynamoDBのIAMロールを追加します。

provider:
  name: aws
  runtime: nodejs6.10
  environment:
    DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

customにはその他の設定を記述します。
今回はやっていませんが、開発環境stage、商用環境stage別の設定なども可能です。
dynamodbにはローカルでdynamodbをシミュレーション起動する設定を記述しています。
clientにはserverless-finchでのdistフォルダのデプロイ先S3バケットを指定しています。

custom:
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migrate: true
    migration:
      dir: offline/migrations
  client:
    bucketName: static.teradonburi.info
    distributionFolder: ../client/dist

API Gatewayレスポンスの注意点

statusCode、headers、bodyには下記のようなレスポンスが必要です。
Access-Control-Allow-Credentials、Access-Control-Allow-Origin、Content-Typeに関してheaderの指定が必要です。
bodyはJSON文字列である必要があります。(今回はapplication/json想定、binaryの場合などはまた別の設定が必要)

create.js
const response = {
  statusCode: 200,
  headers: {
    "Access-Control-Allow-Credentials": true,
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin" : "*" // Required for CORS support to work
  },
  body: JSON.stringify(result.Attributes),
}
callback(null, response)

Serverlessフレームワークでの設定は未検証ですがAPI Gateway+Lambda経由の画像のアップロードも今では可能です。
参考:API Gatewayがバイナリデータをサポートしたので試してみました

CloudFrontの設定

参考:S3 CloudFront Route 53 でReactで作ったSPAを配信する
参考:CloudFrontでS3のウェブサイトをSSL化する

CloudFrontを作成します。
Origin Domain NameにはS3のバケット名を指定します。
Origin IDは自動で入ります。
Object CachingをCustomizeにします。
TTLを全て0にしてます。(キャッシュを有効にする場合はS3デプロイ時に手動でInvalidationsにてキャッシュクリアする必要が出てきます。)
Forward CookiesをAllにします。
Compress Objects Automaticallyを有効にします。(gzip圧縮されてレスポンスが早くなるので)
Price ClassesはAll Edgeにすると日本からのアクセスは早くなりますが値段は上がります。(お財布と相談で)
Altanative Domain Names(CNAME)にはstatic.(ドメイン名)を指定します。
SSL CertificateをCustom SSL Certificateにします。
→Request or Import a Certificate with ACMにてSSL証明書を作成します(後述)
Default Root Objectをindex.htmlにします。

スクリーンショット 2017-12-29 5.28.23.png
スクリーンショット 2017-12-29 5.30.43.png
スクリーンショット 2017-12-29 5.30.57.png
スクリーンショット 2017-12-29 5.31.10.png

CloudFront作成後に払い出されたDomain Nameをコピーします。
スクリーンショット 2017-12-29 5.22.44.png

Route53にてstatic.(ドメイン名)にCloudFrontのDomain NameをAレコードのaliasとして指定します。
スクリーンショット 2017-12-29 5.21.14.png

ACMで独自ドメインのSSL証明書作成

ACMにてSSL証明書を作成します。
*.(ドメイン名)でサブドメインを作成します。
スクリーンショット 2017-12-29 5.04.44.png

DNSの検証を選択し、確定とリクエストします。
スクリーンショット 2017-12-29 5.09.58.png

Route53にて払い出されたCNAMEを登録します。(Route53でのレコードの作成)
スクリーンショット 2017-12-29 5.10.44.png

スクリーンショット 2017-12-29 5.12.51.png

SPAのルーティングを有効にする

デフォルトのままだと
S3のエラーページが表示されてしまい、SPAのルーティングが有効になりません(Reactの場合、React-Routerが効かない)
そこでCloudFront側でエラーページの遷移を修正します。

Create Custom Error Responseを作成します。
スクリーンショット 2017-12-29 5.14.00.png

403エラーコードの場合にindex.htmlを指定するように修正します。
スクリーンショット 2017-12-29 5.16.41.png

API Gatewayの設定

参考:Amazon API Gateway にカスタムドメインを設定する
参考:【新機能】Amazon API GatewayがACM (AWS Certificate Manager)に対応。簡単に独自ドメインAPIがSSL化。

API Gatewayにカスタムドメインを割り振ります。
API Gatewayのカスタムドメイン名から作成します。
私はapi.(ドメイン名)にしました。(config/production.jsと合わせてください)
作成にはかなり時間がかかります。(30分以上?)

スクリーンショット 2017-12-29 4.23.28.png

保存するとターゲットドメイン名が出力されますのでコピーします。

スクリーンショット 2017-12-29 4.26.23.png

Route53にてCreate Record SetsでAレコードを作成します。
aliasをyesにして先程のターゲットドメインを指定します。

スクリーンショット 2017-12-29 4.29.23.png

本番環境の確認

上記すべての設定が完了すれば
https://static.(ドメイン名)
にアクセスするとReactJSのSPAページが表示され、
https://api.(ドメイン名)
でAPI Gateway→Lambda→DynamoDBでのRest APIが動作するようになります。

APIの再デプロイ

API部分は次コマンドで再デプロイします。

# デプロイ先のAWSアカウントを明示的に指定(~/.aws/credentialsのアカウントが複数ある場合)
$ export AWS_ACCESS_KEY_ID=(アクセスキー)
$ export AWS_SECRET_ACCESS_KEY=(プライベートキー)
$ SLS_DEBUG=* sls deploy

S3の再デプロイ(アップロード)

シンプルにバケット内のindex.htmlとjsフォルダを削除して

スクリーンショット 2017-12-29 2.35.37.png

distフォルダ内の再ビルドしたファイルをアップロードすれば大丈夫です。

スクリーンショット 2017-12-29 2.36.40.png

続きを読む