AWS SAM CLIを使わずにLambda+Ruby2.7でNokogiriなどのGemを使う方法

## 概要
Lambda+Rubyでgem(Nokogiri)を使おうと思ったところ
```
cannot load such file -- nokogiri/nokogiri
```
というエラーになってしまい、使えなかったので使えるようにした話

## 前段
調べると、割と`AWS SAM CLI`を使った方法が出てきて手順自体は簡単なのですが何してるかわからん...というふうになってしまったので、今回はそちらを使わない方法にしたいと思います。

## 準備するもの
- Lambdaのソースファイル
- Docker環境

## 手順
### Lambdaで動かすソースコードの準備
自分の使っているコードが有る場合は

https://qiita.com/itaya/items/dbe628b7b8d896eb6c8f#%E6%97%A2%E5%AD%98%E3%81%AE%E9%96%A2%E6%95%B0%E3%81%AE%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88

この記事を参考にエクスポートしてもらうと良いかと。

自分は今回このようなコードを準備しました。
Gemfile
```ruby
source 'https://rubygems.org'

gem 'nokogiri'
```
lambda_funcrion.rb
```rb
require 'json'
require 'nokogiri'
require 'kconv'
require 'net/http'
require 'uri'

def lambda_handler(event:, context:)

    area = URI.encode_www_form_component('横浜')
    keyword = URI.encode_www_form_component('居酒屋')
    
    uri = URI.parse("https://r.gnavi.co.jp/area/jp/rs/?fwp=#{area}&date=&fw=#{keyword}")
    request = Net::HTTP::Get.new(uri)
    request["Authority"] = "r.gnavi.co.jp"
    request["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    request["Accept-Language"] = "ja"
    request["Cache-Control"] = "max-age=0"
    request["Cookie"] = "_td_global=2c7201bb-65d9-447f-8871-5c8aef70e6f0; _ra=1664900409701|e581f62a-e16f-4c02-97dc-b7ab203924e6; GU=e9f8c8528b624ea8cb1a55aadf705bd0; gt=GT139faa476002ac1e4ae520fpfeoIzpEqTDWQ1Rwc2xzV; ds=622474de466075942cc80e90a606c14bc6c474c7b09327535e48d587017aa4c0; gUser=03139faa476003ac1e4ae520L_I2dpFCb1q8eMMN-5-LmU; gcom=%7B%22login_type%22%3A1%2C%22guser_type%22%3A0%7D; _gid=GA1.3.151665219.1669743686; __gads=ID=61224817bcdcc5e2:T=1669743685:S=ALNI_MYQItLoO9f62eC3t0aHssBPge-pXQ; __gpi=UID=00000b86e8eecc21:T=1669743685:RT=1669743685:S=ALNI_MZ324J7fmISVdaU-A1IuoqdhZBTIQ; _ts_yjad=1669743686386; _fbp=fb.2.1669743686439.1159228644; _gcl_au=1.1.1560200380.1669743686; __pp_uid=INDH6tuKhtkFKURk66AXkbVTWOoJTybu; __lt__cid=c5331b8a-2798-4df1-9a8c-afb6b1af0c3c; __lt__sid=ab800c27-56441560; _dctagfq=1318:1670338799.0.0|1319:1670338799.0.0|1320:1670338799.0.0|1321:1670338799.0.0|1536:1670338799.0.0|1841:1670338799.0.0|1856:1670338799.0.0|1871:1670338799.0.0|1886:1670338799.0.0|1901:1670338799.0.0|1916:1670338799.0.0|1931:1670338799.0.0; ___o2u_o2id=0793b44a-526d-4f6f-bbfb-c923eb5be40f; rtosrch=gg7t002%3Dhttps%253A%252F%252Fr.gnavi.co.jp%252Farea%252Fjp%252Frs%252F%253Ffwp%253D%2525E6%2525A8%2525AA%2525E6%2525B5%25259C%2526date%253D%2526fw%253D%2525E3%252581%25259F%2525E3%252581%252593%2525E7%252584%2525BC%2525E3%252581%25258D%26a567203%3Dhttps%253A%252F%252Fr.gnavi.co.jp%252Farea%252Fjp%252Frs%252F%253Ffw%253D%2525E5%2525B1%252585%2525E9%252585%252592%2525E5%2525B1%25258B%2526fwp%253D%2525E6%2525A8%2525AA%2525E6%2525B5%25259C; GHistory=a567203%3A110%3A1669744542%2Cgg7t002%3A110%3A1669744522; ghistory_reserve=a567203%3A1669744542%2Cgg7t002%3A1669744522; __cribnotes_prm=__t_1669744547013_%7B%22uuid%22%3A%227a38921c-4d2e-4d30-bcb9-80d0686da7f0%22%7D; cto_bundle=sGp-Ll9kUDVaaHJYMHN3SkFtYyUyQkFmUTh2QWdka0pTQ0xqWlhHVFpETzB3ckh4dG1ya0J4NTB1Y0Z1V09mOUtMRDJWRlBKbDNSU2FTMWFDRXgyOFU4Q0l6UzAzYWFoWE9TMVd0WmVqOFZVcjBmQ3JHVHJyNkFodzhVcDlKTGdRYTBIWXNEdHdET3RzeENSc0Q0eDRGdUlwJTJGS3pWJTJGaXk1QW9sajk1ZUhJRHNaelpaYSUyRllHR0klMkZlNnhWWXIlMkJYTXB3eU00eUw; s_sess=%20sc_oncook%3D%3B%20sc_prop1%3Dr%3B%20cpnt_referer%3D%3B; _dc_gtm_UA-43329175-1=1; _td=639fb236-c732-4fb7-b246-0a6ed66c2a87; _ga_L9BHK8C28C=GS1.1.1669743685.1.1.1669744820.51.0.0; _ga=GA1.1.111137152.1669743686"
    request["Sec-Ch-Ua"] = "\"Google Chrome\";v=\"105\", \"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"105\""
    request["Sec-Ch-Ua-Mobile"] = "?0"
    request["Sec-Ch-Ua-Platform"] = "\"macOS\""
    request["Sec-Fetch-Dest"] = "document"
    request["Sec-Fetch-Mode"] = "navigate"
    request["Sec-Fetch-Site"] = "same-origin"
    request["Sec-Fetch-User"] = "?1"
    request["Upgrade-Insecure-Requests"] = "1"
    request["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
    
    req_options = {
      use_ssl: uri.scheme == "https",
    }
    
    response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
      http.request(request)
    end
    
    doc = Nokogiri::HTML.parse(response.body.toutf8, nil, 'utf-8')
    result = doc.css('.style_restaurantNameWrap__wvXSR').map do |target|
      target.text
    end
  
  { statusCode: 200, body: JSON.generate(result) }
end

```
中身の細かい話が知りたければ

https://qiita.com/itaya/items/0cd39400e274b926d5be

これとか読んでください。
1文で要約すると「ぐるなびのサイトを検索して情報を店舗名を取得するコード」です。
これをLambdaで動かします。

### Dockerfileの準備
次にDockerfileを準備します。
Lambdaのソースファイルが有るディレクトリにDockerfileを作成します。
そして下記のような記述をします。
```Dockerfile
FROM lambci/lambda:build-ruby2.7

CMD "/bin/bash"
```
そもそもなんでローカルのbundle installをしただけでは動かないのかという話なのですが、nokogiriなどのgemは環境ごとのネイティブなライブラリを参照しており、手元の環境とLambda環境ではそれが異なるために起きてしまっているんですね。
そのため、**Lambda用のDocker環境を準備してそこでビルドしてあげたものをアップすればいいじゃんという話なのです！**

### Dockerビルド
下記のコマンドでDockerビルドを行います。
```sh
docker build -t lambda-ruby2.7 .
```

### bundle install
次に下記のコマンドでDocker環境の中に入り
```sh
docker run --rm -it -v $PWD:/var/task -w /var/task lambda-ruby2.7
```
bundle installを行います
```
bundle install --path vendor/bundle --clean
```
終わったら
```
exit
```
で抜けます。

### ソースファイルをZIP化
そしたらbundle installしたライブラリごとzip化します。
```
zip -q -r deploy.zip .
```

### Lambdaにアップロード
![スクリーンショット 2022-12-17 15.20.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83409/083de6bd-10b0-89f2-3728-749fa245df35.png)
![スクリーンショット 2022-12-17 15.20.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83409/ac058a33-b4f1-412b-6a1a-a5c8bf802df8.png)

最後にLambdaの環境にアップロードして、完了！！