パパエンジニアのアウトプット帳

30歳に突入した1児のパパエンジニアのブログ

Serverless Framework for Ruby - 複数関数作成編

下記の続き。今回は複数のファンクションを定義してみようと思う。

masaru-tech.hateblo.jp


serverless.ymlの書き方からなんとなく複数ファンクションの書き方は予想がつく。
handerにファイル名.アクション名だろうと。

$ vi serverless.yml
---
 functions:
   hello:
     handler: handler.hello
+    events:
+      - http:
+          path: hello
+          method: get
+  goodby:
+    handler: handler.goodby
+  fuga:
+    handler: hoge.fuga

これで1ファイルにまとめることもファイルを分けることもできる。


しかし・・・


lambdaのファンクションは複数出来ているが、コードサイズが全て同じでしかもそれなりにある。。。
S3にzipがあるのでダウンロードして中をみてみると。。。

$ ls
./            ../       handler.rb    hoge.rb       node_modules/ package.json  yarn.lock


なるほど。全てを1つのzipにまとめてそれをlambdaのhandlerの指定を変えてるんですね。。。
しかも、node_modulesとかpackage.jsonとかはrubyだと要らない。。。

lambdaでデプロイパッケージの容量制限があるのでこれは分けて欲しいなー。

docs.aws.amazon.com

Auroraのスナップショット復元時間はどれくらいかかるのだろうか?

ちょっとスナップショットからAuroraを復元する必要があったので、試しにやってみたら時間がかかった。

なんでこんなに時間かかるのだろう?と調べているとまさにドンピシャな実験をされている方がいた(圧倒的感謝)

blog.father.gedow.net


だいたいこの検証の通りっぽいが、時間によるものなのかスナップショットをとった時のインスタンスタイプも関係あるのかstaging/productionのスナップショットの復元時間がかなり違う。

db.t2.smallから取得したスナップショット48GiB(staging)とdb.r3.largeから取得したスナップショット73GiB(production)から新しくdb.r3.largeのインスタンスを復元しようとしたら、staging:約1.5h/production:約1hと本番の方が早かった。
同じインスタンスタイプだから容量しか影響しないと思っていたが、スナップショットの元となったインスタイプの違いも影響するのだろうか?
不思議だ。

TerraformでAurora2を立てる時のengine、engine_versionの指定

TerraformでAurora2のRDSを立てようと思ったらengine_versionになに指定したらいいか分からなかったのでメモ。


create-db-cluster — AWS CLI 1.16.103 Command Reference

--engine (string)

The name of the database engine to be used for this DB cluster. Valid Values: aurora (for MySQL 5.6-compatible Aurora), aurora-mysql (for MySQL 5.7-compatible Aurora), and aurora-postgresql

--engine-version (string)

The version number of the database engine to use.

Aurora MySQL

Example: 5.6.10a , 5.7.12

Aurora PostgreSQL

Example: 9.6.3

Aurora MySQL データベースエンジンの更新 (2018-02-06) - Amazon Aurora

・Aurora MySQL 2.x のエンジン名は aurora-mysql で、Aurora MySQL 1.x のエンジン名は引き続き aurora です。

・Aurora MySQL 2.x のエンジンのバージョンは 5.7.12 で、Aurora MySQL 1.x のエンジンのバージョンは引き続き 5.6.10ann です。

このドキュメントをみた感じだと、engine = "aurora-mysql"、engine_version = "7.5.12"だとなるが、これだとAurora 2.xではなくAurora MySQL 5.7.12が作成される。
engine_version = "2.03.3"とかにしてもダメで、結局一度5.7.12で作成後2.03.3にコンソールで変更してTerraformで見てみるとengine_version = "5.7.mysql_aurora.2.03.3"という。(分かるわけがない…)


なので、Terraformでのtfファイルは下記のように設定するとAurora MySQL 2.x系を作ることができます。

resource "aws_rds_cluster" "aurora57" {
  ・・・
  engine                              = "aurora-mysql"
  engine_version                      = "5.7.mysql_aurora.2.03.3"
  ・・・
}

Serverless Framework for Rubyをやってみる

AWS LambdaにRubyがきたので、Lambdaをより使う意欲が出てきた。(今まではpythonがほとんどでたまにnodejsで書いていた)
そこで、前からLambdaやるならServerless/Apexのどちらかかなーと思っていたのでまずはServerless Frameworkを試してみる。

serverlessのインストール

まずは、serverlessのインストールから。

Railsで染み付いたのかglobalにインストールするのがなんか嫌なので、プロジェクト直下にインストールします。
あと特にこだわり無いですがyarnが入っているのでnpmではなくyarnでやります。

$ mkdir sample-serverless
$ cd sample-serverless
$ yarn init
$ yarn add serverless

package.jsonにscriptsを定義

このままだとservelessコマンドが実行できないのでpackage.jsonにscriptsを追加します。

$ vi package.json
---
{
  "name": "sample-serverless",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "serverless": "serverless"
  },
  "dependencies": {
    "serverless": "^1.36.3"
  }
}

Rubyのテンプレートからサンプルプロジェクトを作成

準備が整ったので、Rubyのテンプレートからサンプルプロジェクトを作成します。

$ yarn run serverless create --template aws-ruby                 
yarn run v1.7.0
$ serverless create --template aws-ruby
Serverless: Generating boilerplate...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.36.3
 -------'

Serverless: Successfully generated boilerplate for template: "aws-ruby"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name
✨  Done in 1.87s.

デプロイ前の準備

あとは、serverless deployコマンドを打つだけ!と言いたいところですが、このままではawsへデプロイできません。
deployコマンドを実行できるようにするには、awsのcredentialsを設定する必要があります。
※公式のドキュメント参照 Serverless Framework - AWS Lambda Guide - Credentials


私の環境はすでにaws cliをインストールしているので、下記のコマンドを利用してプロファイルを新規に作成しそれを利用します。

$ aws configure --profile serverless_development
あとは聞かれる通りにaccess keyやsercret keyの情報を入力していきます。

プロファイルが作成できたら、serverless.ymlに追加します。

$ vi serverless.yml
---
※コメント省いてます

service: sample-serverless

provider:
  name: aws
  runtime: ruby2.5
  profile: serverless_development  # aws configure --profile xxxで作成したやつ
  region: ap-northeast-1
  stage: dev

functions:
  hello:
    handler: handler.hello

これで準備は完了です。

デプロイ!

それでは、serverless deployを実行です。

$ yarn run serverless deploy -v
yarn run v1.7.0
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - sample-serverless-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - sample-serverless-dev
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (15.08 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - sample-serverless-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionNxhCrecCdNcXpcb9fj6uPopa8NgeMn3M05zFw92CIUQ
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionNxhCrecCdNcXpcb9fj6uPopa8NgeMn3M05zFw92CIUQ
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - HelloLambdaVersionNxhCrecCdNcXpcb9fj6uPopa8NgeMn3M05zFw92CIUQ
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - sample-serverless-dev
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - sample-serverless-dev
Serverless: Stack update finished...
Service Information
service: sample-serverless
stage: dev
region: ap-northeast-1
stack: sample-serverless-dev
api keys:
  None
endpoints:
  None
functions:
  hello: sample-serverless-dev-hello
layers:
  None

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:123456789012:function:sample-serverless-dev-hello:1
ServerlessDeploymentBucketName: sample-serverless-dev-serverlessdeploymentbucket-xxxxxxxxxxxx

✨  Done in 84.97s.

デプロイが完了しました。
serverless deployを実行すると、Lambdaのファンクションを作成するだけではなくS3のバケットやIAMロール、CloudwatchのロググループなどもCloudformationを利用して作成するようです。

デプロイしたLambdaファンクションを実行

デプロイしたLambdaファンクションの実行は、serverless invokeで行います。

$  yarn run serverless invoke --function hello 
yarn run v1.7.0
$ serverless invoke --function hello
{
    "statusCode": 200,
    "body": "\"Go Serverless v1.0! Your function executed successfully!\""
}
✨  Done in 2.34s.

テンプレートのhelloメソッドそのままですが、ちゃんと実行されているようです。 ※Cloudwatchのログを確認するとログが出力されているはずです。

httpからアクセスできるようにする

現状はlambda関数をそのまま呼び出しているだけなので、httpアクセスで呼べるようにしてみます。
下記のようにeventsのhttpを設定することでhttpアクセスが可能になります。

$ vi serverless.yml
---
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

これでデプロイするこ今度は新たにAPI Gatewayが作成され、API Gatewayを利用してhttpでアクセスできます。

$ curl deploy後に表示されているServiceEndpoint/hello
"Go Serverless v1.0! Your function executed successfully!"

ブラウザからアクセスしても同様の文字列が表示されるのでバッチリです。


とりあえずのチュートリアルレベルのことをやってみただけですが、Serverlessを利用してAWSヘデプロイできました。
ちょうど色々なツールをwebhookで連携したかったので、Serverlessを色々触ってみようと思います。

また、Apexも気になっているのでまた別の機会に触ってみようと思います。

has_oneのbuildはhas_manyと違う

Railsでhas_many :commentsみないな時にbuildするときは@blog.comments.buildだけど、has_oneの場合は.buildはnilエラーになる。

has_oneの場合はbuild_xxxというメソッドがあるので、そちらを使うと言うことを今頃になって知った。

class Blog
  has_one :star
end

@blog = Blog.new
# @blog.star.build →nilエラーになる
@blog.build_star

RubyMineでSlim+Vue.jsを利用していると開けなくなるファイルの条件が判明した

環境

  • macOS High Sierra 10.13.6
  • RubyMine 2018.2.4 Build #RM-182.4505.55, built on October 8, 2018


今の会社になってからRubyMineも会社で購入してくれるのでバリバリ使っているのですが、ジョインした時からいくつかのslimファイルがRubyMineで開けない状態だった。(VSCodeなどでは開ける)

前からいる人もなんでそのファイルたちだけが開けないのか不明で、しかも検索の対象にもならないのでそのあたりの画面を触る時にとても困っていた。


VSCodeに乗り換えるか?という話も出てきだしたが、一度RubyMineの便利さ(特にコードジャンプ)を知るとなかなか他では代替できないと自分は思っている。(VSCodeでslimファイル内でrubyのコードなどにコードジャンプできるようにする方法を知っている人がいたら是非教えて欲しい)


そんな折、ひょんな事から自分がそのRubyMineで開けないファイルを生み出してしまったので、色々試してみるとどうやらSlim内でVue.jsのv-bindの省略記法を書いているものがあると開けなることが分かった。

my-component [
  v-for = "item in list"
  track-by = "uuid"
  :item = "item"
]

このような書き方がSlim内に含まれると、RubyMineで開けなくなる(ファイルのタブは出るがずっとloading…のままになる)

なので、v-bindを省略記法ではなくちゃんと書けば大丈夫。

my-component [
  v-for = "item in list"
  track-by = "uuid"
  v-bind:item = "item"
]

今回みたいに属性が3つくらいだと[]をあまり使わないかもしれないが、属性が多くなってくるとこっちの方が見やすくなる。(ちなみにこの書き方は最近まで知らなかった。。。)


省略記法を書けないと不便に感じるかもしれないが、自分は省略記法はRails + Vue.jsでは混乱させると思っているので会社のプロジェクトでは使わないように提言しているのだが、より強く言っていこうと思った。 (:item = itemと書かれたら一瞬RubyのHash?って思いません?あと@clickとか)

webpackのProvidePluginとresolve.aliasの使い分け

webpackのProvidePluginとresolve.aliasの使い分けがなんとなく分かったような気がするのでメモ。


vue.jsについて調べていた時に両方の書き方をした記事が引っかかってどっちでやったら良いか分からずモヤモヤしていた。

ProvidePlugin

new webpack.ProvidePlugin({
    Vue: ['vue/dist/vue.esm.js', 'default'],
  })

こうすると、変数として利用可能になるのでrequireやimportせずに使える。

// import Vue from 'vue' ←不要!!

new Vue({....})

resolve.alias

resolve: {
        alias: {
            vue: 'vue/dist/vue.esm.js',
        }
    }

これは、Vue.jsでES Moduleビルドを利用する時に

import Vue from 'vue/dist/vue.esm.js'

と毎度書くのが面倒なので、上記のaliasを設定すると

import Vue from 'vue'

でvue/dist/vue.esm.jsを参照したのと同じになる。

結局どう使い分けるか?

今回のVue.jsの場合はどちらもvue/dist/vue.esm.jsを気にしなくてよくなるが、ProvidePluginを利用した方がimportも都度書かなくてもよくなるのでこっちが良さそう。


aliasは自分のコンポーネントとかで

import Hoge '../../components/hoge.js'
import Fuga '../../components/fuga.js'

とかしている時に威力を発揮しそう。


[2018/11/09 追記]

resolve.aliasは外部ライブラリでVue.jsに依存している場合に必要。

ライブラリ内に import Vue from 'vue'があるとそれはVue.js2系だとvue.runtime.esm.jsになってしまうので、それではマズイ場合にresolve.aliasを指定して強制的に上書きする時に使うので、使い分けと言うよりか両方使う感じになるのかな?


webpack完全に理解した!までの道のりはまだ遠い。。。