lambda rubyでクロールしてs3にhtmlを出力するをAWS SAMで

# 概要
* ulrをクロールしたhtmlをS3に保存するlambda functionをSAMでdeployした
* ふつーにクロールすると1 url辺り5秒かかるとして1000url 5000秒。lambdaを使うと並列しょりできるので、所要時間はinvokeの時間+5秒なのでだいたい60秒ぐらいで終わる
* lambdaはruby

# 手順
## AWS SAM インストール
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install-mac.html
https://aws.amazon.com/jp/blogs/developer/announcing-ruby-build-support-for-aws-sam-cli/

```console
brew tap aws/tap

brew install aws-sam-cli
```

## ひな形作成

```
sam init --runtime ruby2.5 --name hello-ruby-sam
cd hello-ruby-sam
```


## バケット作る
* SAM保存する & クロールしたhtmlを保存するバケットを作ります。（別々がいいですが、今回は手間を省略）

```console
aws s3 mb s3://YOUR_NEW_BUCKET_NAME --region ap-northeast-1
```

## コード書く

* urlのhtmlを取得してs3に保存するメソッドです。これがlambdaで実行されます。
* urlをハッシュにしてフォルダ名にして、その中にクロール日付（yyyymmdd.html）がついたファイルが作成されます。
  * なのでページを取得するときはハッシュをキー `Digest::SHA256.hexdigest(url)` にして取得して下さい（urlをフォルダ名にしたいけどそれだとうまくいかないので）
  * そこら辺は説明省略
* awsのキーとかはご自分のキーなどに適宜書き換えて下さい。

```hello-ruby-sam/crawl_url/app.rb
require 'json'
require 'open-uri'
require 'aws-sdk-s3'

BUCKET_NAME = 'bucket-name'
BUCKET_REGION = 'bucket_region'
AWS_ACCESS_KEY = 'aws_access_key'
AWS_SECRET_KEY = 'aws_secret_key'

def lambda_handler(event:, context:)
  url = event['url']
  html = open(url) do |f|
    f.read
  end

  s3 = Aws::S3::Resource.new(
      region: BUCKET_REGION,
      credentials: Aws::Credentials.new(AWS_ACCESS_KEY, AWS_SECRET_KEY)
  )
  obj = s3.bucket(BUCKET_NAME).object("#{Digest::SHA256.hexdigest(url)}/#{Time.now.strftime('%Y%m%d')}.html")

  obj.put(body: html)
end
```

## テンプレート
最後にテンプレートを編集。いらないところをバッサリと削ります。

```hello-ruby-sam/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  CrawlUrl

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 30

Resources:
  CrawlUrlFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      FunctionName: CrawlUrl
      CodeUri: crawl_url/
      Handler: app.lambda_handler
      Runtime: ruby2.5

Outputs:
  CrawlUrlFunction:
    Description: "CrawlUrl Lambda Function ARN"
    Value: !GetAtt CrawlUrlFunction.Arn
  CrawlUrlFunctionIamRole:
    Description: "Implicit IAM Role created for CrawlUrl function"
    Value: !GetAtt CrawlUrlFunctionRole.Arn
```

## デプロイ
デプロイします。
ついでにAWSのcloudformationを開いて見ているとごりごり動くのがわかるので楽しいです。

```console
sam build
sam package \
    --template-file template.yaml \
    --output-template-file packaged.yaml \
    --s3-bucket バケット名

sam deploy \
    --template-file packaged.yaml \
    --stack-name sam-app \
    --capabilities CAPABILITY_IAM
```

* lambadaのファンクションが作られていれば成功です。

# 使う

* irbで試します。
* `aws-sdk` のgemが入った状態、 `invoke(url)` のurlの箇所に任意のurlを入れるて、S3にurlのhtmlが保存されます。


```ruby
require 'aws-sdk'

BUCKET_NAME = 'bucket-name'
BUCKET_REGION = 'bucket_region'
AWS_ACCESS_KEY = 'aws_access_key'
AWS_SECRET_KEY = 'aws_secret_key'


def invoke(url)
  client = Aws::Lambda::Client.new(region: 'ap-northeast-1', credentials: Aws::Credentials.new(AWS_ACCESS_KEY, AWS_SECRET_KEY))

  req_payload = { url: url }
  payload = JSON.generate(req_payload)
  client.invoke({
                          function_name: 'CrawlUrl',
                          invocation_type: 'Event',
                          log_type: 'None',
                          payload: payload
                      })
end

invoke('https://ほしいurl')
```


以上

# わからなかったこと
deploy時awsのキーを環境変数などで渡す方法がわからない。