AWS CLIに複数のAWSアカウントを登録する方法

複数のAWSを利用するプロジェクトに携わっている場合、複数のAWSアカウントをCLIで操作する可能性が出てきます。
Secret Keyは作成時しか見えませんので、毎回AWSにログインしてSecret Keyを作り直すこともナンセンスです。
本記事では、AWS CLIに複数のAWSアカウントを登録し、再度configする手間を省く方法をまとめます。

AWS CLIのインストール

AWSの公式ドキュメントを参考にinstallを行います。
https://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

初期設定

defaultで利用するアカウントを登録します。

$ aws configure
AWS Access Key ID [None]: ${Your Default ID}
AWS Secret Access Key [None]: ${Your Default Secret Key}
Default region name [None]: ${Region}
Default output format [None]: json

AWS CLIに複数のアカウントを登録する

AWS CLIは–profileオプションを利用することで、複数のアカウントを登録することが可能です

$ aws configure --profile xxxx
AWS Access Key ID [None]: ${Your ID}
AWS Secret Access Key [None]: ${Your Secret Key}
Default region name [None]: ${Region}
Default output format [None]: json

では、どのようにcredentialなどは登録されているのでしょうか。
AWS CLIのconfig、およびcredentialを保存しているfileを確認してみましょう。

~/.aws
$ tree ~/.aws
.aws
├── config
└── credentials

まずはconfigから確認してみましょう。

~/.aws/config
[default]
output = json
region = ap-northeast-1
[profile xxxx]
output = json
region = ${Region}

[profile xxxx]といった項目が追加され、先ほど入力した情報と同様の情報が格納されていることが確認できます。
credentialsの方も確認してみましょう。

~/.aws/credentials
[default]
aws_access_key_id = ${Your Default ID}
aws_secret_access_key = ${Your Default Secret Key}
[xxxx]
aws_access_key_id = ${Your ID}
aws_secret_access_key = ${Your Secret Key}

こちらも先ほど入力した情報と同様の情報が格納されていることが確認できました。

AWS CLIの利用方法

–profileオプションを利用することによって、~/.aws以下に保存されている情報から、–profileの引数と同様の項目からkeyなどの情報を参照して利用します。
以下の例は、s3バケットの一覧をアカウントを変えて表示させています。
アカウントが変わることで、s3のバケットが変更されるのがわかると思います。

s3バケットの一覧が変更されていることを確認
$ aws s3 ls
$ aws s3 ls --profile xxxx

まとめ

今回はAWS CLIに複数のAWSアカウントを登録する方法をまとめました。
最近はAWS CLIだけではなく、Serverless frameworkやterraformを利用した構築方法もありますので、そちらの方も随時まとめていけたらと思います。

続きを読む

カップル向けのサービスをメンテフリーで有名なserverless構成で出した話

「Glance(グランス)」 ~カップルのためのYes/No枕アプリ~

2018/01/03より、「Yes/No枕をアプリで」というコンセプトの元に作られたアプリをリリースしましたー!

結構、謎仕様が多くて使い所も不明ですが、
9月ぐらいの「八耐|八時間耐久製作会」というイベントでアイデアから本日のリリースに至りました。

itunes.apple.com_jp_app_glance-yes-no%E3%81%BE%E3%81%8F%E3%82%89_id1294641754_l=ja&ls=1&mt=8.png

https://itunes.apple.com/jp/app/glance-yes-no%E3%81%BE%E3%81%8F%E3%82%89/id1294641754?l=ja&ls=1&mt=8

機能

  • アプリアイコンが動的に変わる、Yes/No枕アプリ

特徴

  • iOS10.3からの機能で、アプリアイコンがYes/Noに動的に変わり、お互いにステータスが見れるというコンセプトです。
  • サーバーサイドはserverless frameworkで
    LambdaとDynamoDBを使っており、メンテコストはほぼ0(ゼロ)

構成

  • アプリ Swift4.0
  • サーバー serverless framework
    • DynamoDB
    • Lambda
    • API Gateway

serverless frameworkとは

  • 「AWS Lambda」と「AWS API Gateway」を利用したサーバレスなアプリケーションを構築するためのツールです。
  • AWSコンソールからだとボタン操作の履歴が残らないですが、serverless frameworkだと全てコードベースで管理できます。

サーバーレスのメリット

  • 管理するサーバーが無いのでサーバーダウンの概念がありません。
  • とにかく安い!
  • 保守メンテが楽でセキュリテイパッチやサーバー管理などのメンテが不要
  • 詳しくはserverlessの魅力を御覧ください

Lambda上でNode.js + Expressがおすすめ

  • API Gatewayでpath管理できますが、serverless + expressのプラグインがあるのでそれに任せたほうが圧倒的に楽になります。

serverless.ymlを晒しとく

  • eventsはanyですべてnodeのExpressに投げます。
serverless.yml
service: yesnoapp

custom:
  defaultStage: dev

provider:
  name: aws
  runtime: nodejs6.10
  profile: yesnoapp
  stage: ${opt:stage, self:custom.defaultStage}
  region: ap-northeast-1
  memorySize: 100
  timeout: 2
  iamRoleStatements:
      - Effect: Allow
        Action:
          - dynamodb:DescribeTable
          - dynamodb:Query
          - dynamodb:Scan
          - dynamodb:GetItem
          - dynamodb:PutItem
          - dynamodb:UpdateItem
          - dynamodb:DeleteItem
        Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/*"

package:
  exclude:
    - .serverless
    - .webpack
    - coverage
    - .babelrc
    - .eslintignore
    - .eslintrc
    - .gitignore
    - LICENSE
    - package.json
    - README.md
    - serverless.yml
    - webpack.config.js


functions:
  app:
    handler: handler.app
    events:
      - http: any {proxy+}

resources:
  Resources:
    YesNoAPPDynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: device_token
            AttributeType: S
        KeySchema:
          -
            AttributeName: device_token
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: "Users"

とまあ、こんな感じでExpressが動く!

  • メンテフリー!コストフリー!みんなハッピー!

良かったこと/悪かったこと

ハッカソンのような短期開発には向いている

  • 開発のスタートダッシュが早く、ハッカソンのような短期即本番には向いているといえます。

運用後も安心な価格とコスト

  • ハッカソンで作った作品なので、アクセスはほぼ見込まれません。t2インスタンスなどは、固定でどんどんお金がかかりますが、料金はアクセスが増えた分だけだし、無料枠も十分あるので運用後もずっと残して置けるでしょう。

バズってもなお安心

  • バズることはほぼないですが、もし万が一バズったとしても、サーバーレス設計なら理論上無限スケールで対応することができます。

中盤がきつい

  • スタートダッシュは早いものの、だんだんこだわって作り出すと、Railsだったらこうできるのに…や、DynamoDBだからテーブルわけなくちゃ…NoSQLの苦悩など、中盤のブラッシュアップ期間は忍耐が必要そうです。

まとめ

  • サーバーレス設計は、開発速度が早く安い。とくにハッカソンイベントでは効果を発揮するのでおすすめです!という話でした!

参考

今回参加した八耐の紹介

『八耐』とは

  • 『八耐:八時間耐久作品制作会(仮)』は、8時間でゲームやCG、映像を制作し、参加者全員で発表するイベントです。
  • イベントの最後に発表と試遊会を通じて、新しい繋がりや見知らぬクリエーター同士の交流を深めるイベントにもなっています。
  • 東京と福岡会場があります。私は毎月、東京会場で参加しておりますのでぜひ!
  • http://www.daihachitai.com/index.php/about

続きを読む

AWS SAMとServerless Frameworkを比較してみた – Qiita

AWS SAMとServerless Frameworkを比較してみた – Qiita. 1 users テクノロジー 記事元: Qiita [キータ] – プログラマの技術情報共有サービス …. AWS SAMとServerless Frameworkを比較してみた – Qiita. この記事はServerless Advent Calendar 2017 23日目の記事です。 最近Serverless Frameworkを使ってい… 続きを … 続きを読む

AWS SAMとServerless Frameworkを比較してみた

この記事はServerless Advent Calendar 2017 23日目の記事です。

最近Serverless Frameworkを使っていたのですが、先輩に「今の時代はAWS SAMだぞ」と言われ、気になったので比べてみました。

Serveless Frameworkの特徴

デプロイしやすい・速い

まずこれが一番の特徴ですね。
下記の例みたいにたったコマンド2つですぐ動かせるサーバーレスアプリケーションが出来上がります。

$ serverless create --template hello-world
$ serverless deploy

もし↑と同じことSAMでやろうとした場合:

  • リリースステージ用のバケットの作成
  • Lambda関数のコーディング
  • 設定ファイルを書く
  • Lambda関数のzip化
  • Cloudformationによるパッケージング

これらの作業を全て自分でやらなければなりません。
Serverless Frameworkを使うとこういったデプロイ前の準備作業をよしなにやってくれますね。

まぁスクリプトやらCIでそこは解決できるとは思いますが、そもそもそこに時間や手間をかけずに自分のアプリケーションだけに集中したい人であれば、Serverless Frameworkの方が圧倒的に使い勝手が良いのではないでしょうか。

連携可能なサービスが豊富

Serverless FrameworkはAWSだけでなく、Azure FunctionsやGoogle Cloud Functionsなど他社のクラウドサービスでも利用できます。
もちろんそれぞれのサービスの仕様や特性をある程度把握した上で利用するべきですが、Serverless Frameworkから利用する部分に関してはほぼ変わりません。
特にデプロイはどのサービスも同じコマンドでできます。

$ serverless deploy

様々なプラグインが利用できる

Serverless Frameworkは使い勝手がとても良いのですが、もちろんまだ足りない部分や痒い所に手が届かないときもあったりします。そういう時はプラグインで補ったりすることができます。
例えば、デプロイしたファンクション毎にDead letter queueをserverless-plugin-lambda-dead-letterで設定できたり、Serverless Frameworkの定義ファイル内でStep Functionsの設定を可能にするserverless-step-functionsなどがあります。
Serverless Frameworkのコミュニティ自体が大きいので、様々なプラグインが多数みつかるのも良い点だと思います。

AWS Serverless Application Modelの特徴

AWS関連サービスとの親和性が高い

Cloud Formationを拡張したサービスなので当たり前かもしれませんが、AWSリソースとの連携がしやすい印象でした。
もしすでにCloud Formationを使ってきた方であればあまり不自由なくSAM利用できるでしょう。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  PutFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs6.10

このように通常のCloudFormationのテンプレートに Transform: AWS::Serverless-2016-10-31 と1行追加すれば、SAMのテンプレートフォーマットを利用することができます。

ローカル環境でのテストが可能

事前にDockerのインストールが必要になりますが、AWS SAM Localをインストールすればローカル環境でのファンクションのテストが可能になります。

$ npm install -g aws-sam-local

以下のように実行したいファンクション名と渡したいリクエストのボディをjsonで指定するとテストが可能になります。

$ sam local invoke "FunctionName" -e event.json

また、Lambdaを起動するイベントのペイロードもエミュレートできます、以下が対象サービスです:

  • S3
  • Kinesis
  • DynamoDB
  • Cloudwatch Scheduled Event
  • Cloudtrail
  • API Gateway

生成したイベントのペイロードをローカルで起動するファンクションに以下のようにパイプラインで渡すことができます。

$ sam local generate-event s3 --bucket bucket-name --key xxxxx | sam local invoke "FunctionName" 

※ Serveless Frameworkもserverless-dynamodb-localserverless-offlineというプラグインをインストールすればローカルでのテストも可能ですが、独自の設定が必要な上Node.js限定という欠点があります。

まとめ

すこしサクッとさわってみて比べた印象ですが、今後他のクラウドサービスに切り替えたいかもしれない時やとにかくスピード重視でアプリケーションを動かしたいのであればServerless Frameworkがいいと思います。
もしアプリケーションをAWSのサービス中心で構成し、それなりに長く運用していくのであればAWS SAMが適しているかもしれません。
ただ、「アプリケーションを素早く本番運用できる状態にし、またサーバーやそれに関連するサービスの運用にかける時間をアプリケーションのコーディングに割り当てることを可能とし、ユーザーにより価値のあるサービスを提供する」といったサーバレスの思想に一番近いのはServerless Frameworkではないでしょうか。

続きを読む

Serverless Frameworkを使ってAWSにSPAを構築する

Merry Xmas!!! エンジニアの小林です。 この記事は Riot.js Advent Calendar 2017 25日目の記事です。 今年はRiot.jsによるフロントエンド開発と、Serverless Framework+AWSによるバックエンド開発が中心の1年でした。今秋にリリースした ダイエットサポート… 続きを読む

CloudFormationのススメ

概要

CloudFormationとは

公式曰く、

AWS CloudFormation は Amazon Web Services リソースのモデル化およびセットアップに役立つサービスです。
AWS CloudFormation とは – AWS CloudFormation

らしいです。難しいですね。
ざっくりいうと AWSリソースをコード管理 するためのツールです。
所謂「 Infrastructure as Code 」ってやつです。

公式で発表されるアーキテクチャはCloudFormationで書かれる事が多いので、
AWSを触れるエンジニアは書けなくても読めることで選択の幅が広がるのではないでしょうか
(例えば これ とか これ など)

CloudFormationの読み方

フォーマット

CloudFormationは json と yaml で記述することが可能です。
特に理由がない限りyamlの方が可読性に優れるため、yamlを選択するのがベターです。

テンプレートの構成

テンプレートは 9個の主要セクション で成り立っていますが、多くのユースケースで必要なのは以下の 5つ で一般的なユースケースは満たされるはずです。

AWSTemplateFormatVersion: '2010-09-09'
Description:
Parameters:
Resources:
Outputs:

一つずつ見ていきましょう。

AWSTemplateFormatVersion

CloudFormationのバージョンを指定。
2017年12月現在 '2010-09-09' が唯一のバージョンなので決め打ち。

Description

テンプレート実行時に表示される説明。
このセクションはコードに影響は及ぼしませんが、後から引き継ぐ人のためにも書いてあげた方が親切でしょう。
設定した値がテンプレートの実行時に 説明 カラムに表示されます。
Screen Shot 2017-12-22 at 22.50.40.png

Parameters

ここでパラメータを宣言するとテンプレート実行時に任意の値を宣言することができます。
この値はResourcesでAWSリソースを作成する際に用いることができます

Webコンソールからパラメータ宣言を行っているテンプレートを実行すると、以下のように 「パラメータ」 というフォームが表示されます
Screen Shot 2017-12-23 at 21.25.56.png

Resources

このセクションにAWSリソースを記述していきます。

雰囲気をつかむために実際にS3の作成を例に見てみましょう。

MyBucket: 
  Type: AWS::S3::Bucket 
  Properties:
    BucketName: 'MyBucket'
    LifecycleConfiguration:
      Rules:
        - Status: Enabled
          ExpirationInDays: 1

MyBucket:
1行目は論理IDの設定です。
ここは任意に命名することができ、設定したIDを元に他のリソースから参照します。

Type: AWS::S3::Bucket
2行目は作成するAWSリソースの指定です。今回はS3を作成したいので、 AWS::S3::Bucket を指定します。
他に指定できるTypeは以下を参考にしてください。
AWS リソースプロパティタイプのリファレンス – AWS CloudFormation

Properties:
この配下に含まれるのがリソースの設定値です。
このS3の設定であれば「 MyBucket というバケット名で、 ライフサイクルを1日 」となります

Outputs

主に 他のテンプレート から呼び出される、リソースが出力する値を記述するのに使います。
規模が大きくなってくると1枚テンプレートに記述しきれなくなってくるので、Outputsに他のテンプレートに渡す値を記述します。
ここについては後述します。

CloudFormationの書き方

ここからは個人的な見解が多めです。参考程度に。

ディレクトリ構成

特に制約がなければCLIから実行したいため、以下のようなディレクトリ構成にします。

.
├── apply.sh # cloudformationコマンドのキック用スクリプト
├── parameters
│   ├── develop
│   │   ├── alb.json
│   │   └── network.json
│   ├── staging
│   │   ├── alb.json
│   │   └── network.json
│   └── production
│       ├── alb.json
│       └── network.json
└── templates
    ├── alb.yaml
    └── network.yaml

apply.sh

CloudFormationはWebコンソール、CLI、SDKから実行できますが、シェルスクからCLIを実行させます。これには3つ理由があります。

  • 誰の環境でも実行できること

    • 当たり前ですがシェルスクであればSDKのようにランタイムの構築をせず動かすことが可能です。
  • パラメータを扱いやすいこと
    • CLIからはパラメータをjsonファイルで渡すことが可能なので、jsonとしてパラメータを扱うことでバージョン管理に含めることが可能になります。
    • (Webコンソールからポチポチ カタカタしたくないので…)
  • テンプレートを格納するS3の制御
    • CloudFormationはS3バケット内のファイルを指定して実行するか、ファイルを直接アップロードするかが選択できます。直接アップロードした場合ランダムな命名のバケットが作成され、そこに自動で格納されてしまいます。
    • また、複数人で作業をする際に勝手に書き換わらないようにS3バケットを用意したり、各環境毎にS3バケットを用意したりすることもできます。

parameters

これは前項でも述べましたが、パラメータをjsonファイルとして扱うことでバージョン管理に含めたいためです。
jsonファイルは以下のフォーマットで呼び出す事が可能です。

$ aws cloudformation create-stack 
    --stack-name alb-stack 
    --template-url https://s3-ap-northeast-1.amazonaws.com/<YOUR BUCKET>/templates/alb.yaml 
    --parameters file://parameters/develop/alb.json # ここでjsonファイルを呼び出している

templates

ここに各種テンプレートを格納します。

ファイル分け

テンプレートはすぐに肥大化するので、ライフサイクル毎/用途毎にテンプレートを分けます。
私であればLAMP構成の場合、templatesディレクトリ配下は以下のような形で分けます。

└── templates
    ├── alb.yaml
    ├── autoscale.yaml
    ├── network.yaml # ネットワーク系(vpc/subnet/routetable/etc)だけ1テンプレートに
    ├── rds.yaml
    └── securitygroup.yaml

そもそも管理すべきリソースかを見定める

S3やRDSやDXのような1度作成したら削除は行わないようなリソース。
LambdaやAPI GatewayのようにCloudFormationだけでは複雑過ぎるリソース。
そういったものは手作業で作成しても良いのではないのか、管理するのにもっと良いソリューション(SAMやServerless Framework)が無いかを一度考えてからコードに落とし込むことをおすすめします。

ネステッドスタックとインポートバリュー

他のテンプレートのリソースを参照したい場合にどうするか。
その場合「ネステッドスタック」というテンプレートからテンプレートを呼び出すことでファイルを擬似的に結合させる機能と、「インポートバリュー」というリソースが出力する値をグローバルに展開し受け渡す機能の2つがあります。
基本的にネステッドスタックは使わず、インポートバリューを選択します。
理由としてはネステッドスタックの制約が多いためです。
ネステッドスタックの場合、変更セットが作成できなかったり、可読性が悪かったり、理由は他にもありますが、、、最大の理由としてスタック上で管理されているリソースを手動で操作するとスタックは簡単に壊れることにあります。
ネステッドスタックの場合一箇所壊れると結合されているテンプレート全てに影響してしまい、スタック全体が壊れてしまいます。
その影響範囲を最低限に減らすためにもテンプレートはなるべく小さくし、テンプレート間の値の受け渡しにはインポートバリューを使います。

ネステッドスタック: ネストされたスタックの操作 – AWS CloudFormation
インポートバリュー: Fn::ImportValue – AWS CloudFormation

秘匿したいパラメータを扱う

テンプレート実行時にパラメータとして渡したいが、秘匿したい値を含ませたい場合が往々にしてあります
例えば、RDSを作成する際のユーザー名とパスワードや、ECSタスクの環境変数など。
こういった値をパラメータで渡すと、Webコンソールから平文で見えてしまうのが問題になってきます。
そういった平文になってしまうことを避ける方法を紹介します。

NoEcho

パラメータの宣言時に用いることで、実行後パラメータの秘匿が行えます。
これは観るのが早いでしょう。

例えばデータベースのパスワードを秘匿したいとするなら以下のようなyamlを書くことで平文で表示される回避することができます

MasterPassword: 
  NoEcho: true
  Type: String

Screen Shot 2017-12-23 at 15.33.26.png

Systems Manager パラメータ

パラメータの値に Systems Manager パラメータ(EC2パラメータストア)を用いることが可能です。
NoEchoの場合実行後に値を確認するすべがなくなるため、個人的にはこちらを併用することを推奨です。

拙作ですが、呼び出し方はこちらの記事を参照
Systems Manager パラメータをCloudFormationで使う – Qiita

再現性と汎用性

某CloudFormation勉強会で話題になった再現性と汎用性についてとりあげます。
再現性の担保はコード化をする以上もちろんですが、個人的に汎用性も担保するべきだと思っています。
どのプロジェクトでも対応可能 なテンプレートではなく、プロジェクト専用だが どの環境・どのリージョンでも対応可能 なテンプレートという意味での汎用性を担保したいです。

環境

CloudFormationを書くコストを払う以上、開発環境・ステージング・本番に対応できるだけの汎用性の対応は行いたいです
これは多少複雑度が増しますが、 Conditions セクションを用いることで実現できます。
上記の3環境だけではなくその他に、個人環境や負荷試験用環境などにも対応ができるとさらに良いですね

リージョン対応

なぜリージョン対応が必要かというと、AWSの新サービス/新機能は日本リージョンに来ていないことが往々にしてあります。
また、「同一アカウントで開発から本番環境まで扱いたい。本番環境は日本リージョンに、開発・ステージング環境は別々のリージョンに置く」といった方法で各環境を運用しているケースもあります。
そういったケースに対応するためにもリージョンの対応を行います。
(個人的に、各環境はアカウントを分けたほうが良いとは思いますが…)
CloudFormationにはリージョン情報を取得する関数が存在するので、それを用いることでハードコーディングを防ぐことができ、異なるリージョンでも素直に実行できるテンプレートを作成することができます。

例えば、サブネットの作成であれば以下のように Fn::GetAZs を用います。

MySubnet:
  Type: AWS::EC2::Subnet
  Properties:
    VpcId: 'vpc-xxxxxx'
    CidrBlock: 10.0.0.0/24
    AvailabilityZone: !Select [ 0, !GetAZs ] #=> 'ap-northeast-1a'

まとめ

CloudFormationは素晴らしいサービスですが、実際にテンプレートの記述/運用を行う場合には安くないコストを支払うことになります。
そのコストを無駄にしないためにもCloudFormationの書き方には気をつけたいですね。

続きを読む

コストレスアーキテクチャでLINE Botを作ろうとして苦戦した話

この記事は「ボット (Bot) Advent Calendar 2017」 の22日目の記事です。

今月、とあるイベントのサブ企画として、
「イベント用LINEアカウントに写真を投稿すると記念フレームが付いて返ってくる!」
というLINE Botを作って公開しました。

主に設計面で困ったことを中心に書こうと思います。

コストレスアーキテクチャとは!!!!

  • さっき考えた造語です
  • サーバレスアーキテクチャとほぼ同義です
  • 料金がかかりそうなものは極力使わないという一種の縛りプレイみたいなものです

経緯とか方針とか考えとか技術選定とか

  • 何か面白い企画できない?とイベント主催者に相談されたのがきっかけ
  • 2~300人の集客が想定された
  • 若者ばかりだったので、LINEならみんな使ってくれそうだと思った
  • ほぼ身内だし、最悪コケてもしゃあないくらいの軽い気持ち
  • なんか写真とか使って記念になりそうなことがしたい
  • 実装よりも企画やフレームのデザイン作りに時間をかけたい
  • 単発だしとりあえず動かせればおk
  • 単発だしデプロイとかも泥臭くていい
  • ケチだからお金は極力かけたくない(重要)

→AWS Lambda + node.jsで画像加工のLINEBotを作ろう。
動くものを安くサクッと仕上げよう。

構成について

当初、以下のような単純なものを想定していました。
やることは投稿画像をトリミングしてからフレームを合成するだけなので、
APIGateWayから実行したLambda内で処理を完結させ、
Messaging APIのReplyAPIで返信するつもりでした。

※Messaging APIはLINEが提供するAPIで、無料のDeveloperプランがあります
https://developers.line.me/ja/services/messaging-api/

Untitled Diagram (2).png

さぁ作るかと思ったのも束の間、いくつか課題が浮き彫りになりました。

  • Replyに必要なReplyTokenは数秒(?)で切れてしまう
  • Pushで画像を返す場合はURL指定が必須
  • Messaging APIのDeveloper Trialプランは友達50人まで

Replyに必要なReplyTokenは数秒(?)で切れてしまう

MessagingAPIには、
ユーザの投稿に返信するReplyAPIと
Bot側から投稿できるPushAPIがあります。

1つ目の問題は、Replyに必要なReplyTokenの切れる時間です。
何秒で切れるのかいまいち情報が無かったのですが、
LINEへの投稿で発行されたReplyTokenを確認して急いでコピってcurlを投げてもエラーになってしまうレベルでした。
文字の返信くらいしかできないのかなーという印象だったので、画像加工をする時間があるか不安でした。

Pushで返す画像はURL指定が必須

ならば上記の構成のままPushAPIで返せばいいじゃん!と思ったのですが、
PushAPIで画像を返す場合は、画像のパスをURLで指定しなければいけませんでした。

ユーザがLINEに投稿した画像は、画像取得APIを通じて取得できますが、
そのままフレーム合成してそのままReplyAPIで返すことができなかったため、
一度どこかのストレージに保存する必要がありました。
このため、functionをいくつかのstepに分けることになり、

①画像を取得してS3に保存
②S3の画像をトリミング、フレーム合成をしてもう一度保存
③S3の画像をPushAPIで返信
と、3つに分けました。

※①②は分けなくても良いのですが、1機能で1つとしました。

Untitled Diagram (1).png

PushAPIにはUserIDが必要ですが、投稿と非同期になるため返信先をどう特定するかが問題です。
しっかりやるなら、画像URLとUserIdをDBにでも持たせて、キューなどで順番にPushするのが綺麗だと思うのですが、
極力金をかけないという謎の縛りプレイを始めた私は、
画像の保存パスを

バケット名/UserId/YYYYMMDDhhmmss.png

とすることでDBは使わない方向でいきました。
後続のLambdaはS3へのPUTをトリガーに動くので、
画像のパスからUserIdを抜き取ってReplyします。

Messaging APIのDeveloperプランは友達50人まで

これが一番問題でした。
Developerプランの範疇を超えて使おうとしている自分がいけないのですが、
今回想定されるお客さんは2~300人なので、全然足りません(泣)

有料プランもありますが、目指すはコストレスです。
しかもPushAPIはDeveloperプランかプロプラン(¥32,400/月)でしか使えないのです。世の中世知辛い。

というわけで、苦肉の策でアカウントを6つ用意しました。

Untitled Diagram.png

展開用のURL(QRコード)はAPIGateWayのエンドポイントになっており、
ランダムに1つのLINEアカウントへのリンクを返します。

素晴らしいです。ロードバランサー的な役割によって、
友達リクエストを負荷分散をさせることができました(謎)

かなりごり押しですが、ここも妥協点です。

同じ人が複数回URLにアクセスしてくることも想定できましたが、
身内イベントだしまぁいっか、という感じです。

金がかからないのはいいが、デプロイがつらい

Botアカウント6つをポチポチ作りながら、ふと思いました。

    これはデプロイで死ねる 、と。

「単発なのでデプロイは泥臭くてもいい」とは言ったものの6つはあまりに面倒です。

MessagingAPIを使うためには、
LINEアカウント毎のCHANNEL_ACCESS_TOKENが必要になります。
これをLambdaの環境変数に設定する必要があるので、
裏側は処理は全て同じLambda、というわけにはいきませんでした。

もちろん、UserIdと共にCredential情報もDBに、と行きたいところですが、
目指すはコストレスです。

少しでもデプロイが楽になればとCloudFormation(AWS SAM)を使うことにしました。

Untitled Diagram (3).png

yaml形式のテンプレートによって、AWSの構成を立ち上げることができます。
多少面倒さは残りますが、立ち上げ時に環境変数にCredential情報を設定すればいいような形でつくりました。

あまり詳しくないのですが、Serverless Frameworkとかだと環境変数をファイルで外出しして、
コマンドの引数で与えるだけで各環境が作れたりするっぽいですね。すごい。

とりあえず、これで幾分かはスマートにいったと思います。

Lambdaの実装

ここは設計ではないので、割愛します。
Node.jsとjimpという画像加工ライブラリを使いました。
Node.jsを書くのは初めてだったので非同期処理やPromiseに翻弄されしんどかったです。
jimpはシンプルで分かりやすかったのでオススメです。
https://github.com/oliver-moran/jimp

イベント当日に公開。しかし…

無事、当日を迎えたのですが、
android端末の方から画像を保存できないという報告が。
OSのバージョンや、エラーメッセージのスクショをもらいそびれたのですが、
「LINEのサーバーから送られた画像じゃない!怪しいからダウンロードしないよ!」というようなエラーでした。

タイムライン上にはフレーム付きのサムネイルが返ってきているのですが、
保存しようとするとエラーが出るようでした。。くやしい。

動作確認は数人にお願いしていたのですが、全員iPhoneだったみたいです。。
LINEBotと言えども、アプリ上で使うものだということを全く考えてなかったです。。

 まとめ

  • なんとか無理やりコストレスで作れました。Lambdaは無料枠範囲内です。
  • 厳密にはS3とAPIGateWayが若干かかってますが、11,12月の請求見ても分からないレベルです。
  • コストレスアーキテクチャって言葉は流行らなくていいです。
  • お金はケチらないほうがいいな、と思いました。

続きを読む

Chailceを使ってみた話

Chailceについて(基礎)

Chailceを使ったきっかけ

仕事でAWS Lambdaを使うことになりましたが、AWS Lambdaを使う方法は色々ありました。
AWS Lambdaで実行するコードを作成して、AWSコンソールにアップロードしたら、
API Gatewayで設定する方法もありますが
最近話題になっているServerless Frameworkを使って、簡単にServerless Architectureができました。
また、Serverless Frameworkも色々ありますが、
先輩の提案でChailceというFrameworkを使うことにしました。

Chailceとは

PythonベースであるServerless Frameworkとして、
AWS LambdaとAPI GatewayをまとめてServerless ArchitectureができるFrameworkです。
さすがに動かしてみないと分からないので、簡単にやってみました。

Chailce使い方

1.Vitualenv Install
Chailceを使う時、Vitualenvをお勧めします。

$ pip install virtualenv
$ virtualenv ~/.virtualenvs/{virtualenv名} (virtualenv作成)
$ source ~/.virtualenvs/{virtualenv名}/bin/activate (仮想環境を有効化)

2.Chailce Install

$ pip install chalice

3.Project作成

$ chalice new-project {project名}

4.File構成

 /.chalice
 /.gitignore
 /app.py
 /requirements.txt

確認できたら、実際にchaliceを動かすapp.pyを見ると下記のようです。

app.py
from chalice import Chalice

app = Chalice(app_name='helloworld')


@app.route('/')
def index():
    return {'hello': 'world'}


# The view function above will return {"hello": "world"}
# whenever you make an HTTP GET request to '/'.
#
# Here are a few more examples:
#
# @app.route('/hello/{name}')
# def hello_name(name):
#    # '/hello/james' -> {"hello": "james"}
#    return {'hello': name}
#
# @app.route('/users', methods=['POST'])
# def create_user():
#     # This is the JSON body the user sent in their POST request.
#     user_as_json = app.current_request.json_body
#     # We'll echo the json body back to the user in a 'user' key.
#     return {'user': user_as_json}
#
# See the README documentation for more examples.
#

5.Deploy

ひとまず、Deployをやってみます。

$ chalice deploy

Initial creation of lambda function.
Creating role
Creating deployment package.
Lambda deploy done.
Initiating first time deployment...
Deploying to: dev

https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/

コマンドの下に表示されるURLにアクセスすると、
{‘hello’: ‘world’}がresponseされていることが確認できます。

Deployの流れをDiagramでみると下記のようです。
スクリーンショット 2017-12-20 23.13.01.png

感想

何よりもAWSを触ってみて非常におもしろかったです。
Serverless Frameworkを通じて、エンジニアは他のところを気にせずに、
ビジネスロジックだけ夢中になれることに気づきました。
また、API Gateway + AWS Lambda + 他の技術が触れることを考えると、
もっとやってみたいと思いました。

参考資料

https://aws.amazon.com/jp/blogs/developer/preview-the-python-serverless-microframework-for-aws/
https://github.com/aws/chalice

続きを読む

はじめてのServerless ✕ Webpack ✕ TypeScript


このエントリーはaratana Advent Calendar 201712日目のエントリーです。

こんばんは!最近Google Home MiniAmazon echo dotを購入したはいいが置き場所に困っている蔭山です。
みなさんはどのような場所に置かれているのでしょうか。。。

前日は新卒エンジニアには決して見えない安定感をお持ちの猿渡くんの「NoSQLについて何か。」という記事でした!
NoSQL?あぁ、聞いたことはある。
みたいな僕でもわかりやすい記事でした!
最近AWSに興味が出始めたところでしたので、ぜひDynamoDBを使って軽い画像投稿サービスでも作ってみます!

さて今回はServerless ✕ Webpack ✕ TypeScriptの組み合わせで使えるように手順をまとめてみたいと思います!

動作環境

今回の動作環境は以下になります!

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G1114

$ node -v
v8.2.1

$ serverless -v
1.24.0

$ webpack -v
3.5.6

環境準備

nodeやserverlessのインストールについては下記記事を参考に。。。

とりあえずやってみる

まずはServerlessのプロジェクトを作ってみましょう

$ serverless create -t aws-nodejs -p hogehoge
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/fugafuga/hogehoge"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.24.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"
$ cd ./hogehoge

JSer御用達npmを使って必要なパッケージをダウンロードしましょう。

$ npm init
$ npm install --save-dev serverless-webpack serverless-offline ts-loader typescript webpack

インストールが終わり次第、各種設定を行います。
今回はTypescript -> ES2015へのコンパイルを目的に設定させていただきます。
細かい設定内容に関しては割愛します。

./serverless.yml
service: hogehoge

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: ap-northeast-1

plugins:
- serverless-webpack
- serverless-offline

functions:
  hello:
    handler: handler.hello
    events:
     - http:
         path: hello
         method: get
./webpack.config.js
module.exports = {
  entry: './handler.ts',
  target: 'node',
  module: {
    loaders: [{
      test: /\.ts$/,
      loader: 'ts-loader'
    }]
  },
  resolve: {
    extensions: ['.ts']
  },
  output: {
    libraryTarget: 'commonjs',
    path: __dirname + '/.webpack',
    filename: 'handler.js'
  }
};
./tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs"
  },
  "exclude": [
    "node_modules"
  ]
}

準備はできたので、次はTypeScriptでコーディングしてみましょう!

./handler.ts
export * from './src/ts/functions/hello';
./src/ts/functions/hello.ts
export function hello(event, context, callback): void {
  const response = {
    statusCode: 200,

    headers: {
    },

    body: JSON.stringify({
      "message": "Hello Serverless x Webpack x TypeScript!!"
    })
  };

  callback(null, response);
};

コードが書けたらローカル環境で動作確認

$ sls offline
・・・・・・・・・・・・・・・・
途中は割愛。m(__)m
・・・・・・・・・・・・・・・・
Serverless: Routes for hello:
Serverless: GET /hello

Serverless: Offline listening on http://localhost:3000

きちんと動作するか確認。

$ curl -X GET http://localhost:3000/hello
{"message":"Hello Serverless x Webpack x TypeScript!!"}

動作が問題なければ、早速デプロイしてみましょう!

$ sls deploy
・・・・・・・・・・・・・・・・
途中は割愛。m(__)m
・・・・・・・・・・・・・・・・
api keys:
  None
endpoints:
  GET - https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/dev/hello
functions:
  hello: hogehoge-dev-hello

デプロイが完了したようです。
では早速動作確認。

$ curl -X GET https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/dev/hello
{"message":"Hello Serverless x Webpack x TypeScript!!"}

ちゃんと動きましたね!

最後に

無理やりTypeScriptを使った感が凄まじいですね。。。申し訳ありません><
僕個人がTypeScriptを使ったことがなかったため使ってみたかったんです

明日は新卒田村くんの「Ctagsで自由な翼を得たVimについて」です!
お楽しみに!

参考

主にこちらの記事を参考にさせて頂きました!ありがとうございますm(__)m

続きを読む