

# Ruby Lambda 関数のレイヤーを操作する
<a name="ruby-layers"></a>

[Lambda レイヤー](chapter-layers.md)を使用して、複数の関数間で再利用するコードおよび依存関係をパッケージ化できます。レイヤーには通常、ライブラリの依存関係、[カスタムランタイム](runtimes-custom.md)、または設定ファイルが含まれています。レイヤーの作成には、次の 3 つの一般的な手順が含まれます。

1. レイヤーコンテンツのパッケージ化。これは、関数で使用する依存関係を含む .zip ファイルアーカイブを作成することを意味します。

1. Lambda でレイヤーを作成します。

1. レイヤーを関数に追加します。

**Topics**
+ [レイヤーコンテンツのパッケージ化](#ruby-layers-package)
+ [Lambda でレイヤーを作成する](#publishing-layer)
+ [関数内のレイヤーからの gem の使用](#ruby-layers-bundler-limitations)
+ [レイヤーを関数に追加する](#ruby-layer-adding)
+ [サンプルアプリ](#ruby-layer-sample-app)

## レイヤーコンテンツのパッケージ化
<a name="ruby-layers-package"></a>

レイヤーを作成するには、次の要件を満たす .zip ファイルのアーカイブにパッケージをバンドルします。
+ Lambda 関数に使用する予定のものと同じ Ruby バージョンを使用するレイヤーを作成する。例えば、Ruby 3.4 向けのレイヤーを作成する場合は、関数にも Ruby 3.4 ランタイムを使用します。
+ Layer の .zip ファイルは、次のいずれかのディレクトリ構造を使用する必要があります。
  + `ruby/gems/x.x.x` (*x.x.x* は、`3.4.0` などの Ruby バージョン)
  + `ruby/lib`

  詳細については、「[各 Lambda ランタイムのレイヤーパス](packaging-layers.md#packaging-layers-paths)」を参照してください。
+ レイヤーのパッケージは Linux と互換性がある必要があります。Lambda 関数は Amazon Linux 上で動作します。

サードパーティーの Ruby gem、または独自の Ruby モジュールとクラスが含まれるレイヤーを作成できます。一般的な Ruby gem の多くには、ネイティブ拡張機能 (C コード) が含まれています。これらは、Lambda Linux 環境用にコンパイルする必要があります。

### Pure Ruby gem
<a name="ruby-layers-pure-ruby-gems"></a>

Pure Ruby gem には Ruby コードのみが含まれており、コンパイルは必要ありません。これらの gem は、さまざまなプラットフォームでより簡単にパッケージ化して使用できます。

**Pure Ruby gem を使用してレイヤーを作成する**

1. `Gemfile` を作成して、レイヤーに含める pure Ruby gem を指定します。  
**Example Gemfile**  

   ```
   source 'https://rubygems.org'
   
   gem 'tzinfo'
   ```

1. Bundler を使用して、gem を `vendor/bundle` ディレクトリにインストールします。

   ```
   bundle config set --local path vendor/bundle
   bundle install
   ```

1. インストールされた gem を、Lambda が必要とするディレクトリ構造 (`ruby/gems/3.4.0`) にコピーします。

   ```
   mkdir -p ruby/gems/3.4.0
   cp -r vendor/bundle/ruby/3.4.0*/* ruby/gems/3.4.0/
   ```

1. レイヤーコンテンツを圧縮します。

------
#### [ Linux/macOS ]

   ```
   zip -r layer.zip ruby/
   ```

------
#### [ PowerShell ]

   ```
   Compress-Archive -Path .\ruby -DestinationPath .\layer.zip
   ```

------

   .zip ファイルのディレクトリ構造は次のようになります。

   ```
   ruby/              
   └── gems/
       └── 3.4.0/
           ├── gems/
           │   ├── concurrent-ruby-1.3.5/
           │   └── tzinfo-2.0.6/
           ├── specifications/
           ├── cache/
           ├── build_info/
           └── (other bundler directories)
   ```
**注記**  
関数コードでは、各 gem を個別に require する必要があります。`bundler/setup` や `Bundler.require` を使用することはできません。詳細については、「[関数内のレイヤーからの gem の使用](#ruby-layers-bundler-limitations)」を参照してください。

### ネイティブ拡張機能が含まれる gem
<a name="ruby-layers-native-extensions"></a>

一般的な Ruby gem の多くには、ネイティブ拡張機能 (C コード) が含まれています。これらは、ターゲットプラットフォーム用にコンパイルする必要があります。ネイティブ拡張機能が含まれる一般的な gem には、[nokogiri](https://rubygems.org/gems/nokogiri/)、[pg](https://rubygems.org/gems/pg/)、[mysql2](https://rubygems.org/gems/mysql2/)、[sqlite3](https://rubygems.org/gems/sqlite3/)、[ffi](https://rubygems.org/gems/ffi/) などがあります。これらの gem は、Lambda ランタイムとの互換性がある Linux 環境内で構築する必要があります。

**ネイティブ拡張機能が含まれる gem を使用してレイヤーを作成する**

1. `Gemfile` を作成します。  
**Example Gemfile**  

   ```
   source 'https://rubygems.org'
   
   gem 'nokogiri'
   gem 'httparty'
   ```

1. Docker を使用して、Lambda との互換性がある Linux 環境内で gem を構築します。Dockerfile で [AWS ベースイメージ](ruby-image.md#ruby-image-base)を指定します。  
**Example Ruby 3.4 用の Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/ruby:3.4
   
   # Copy Gemfile
   COPY Gemfile ./
   
   # Install system dependencies for native extensions
   RUN dnf update -y && \
       dnf install -y gcc gcc-c++ make
   
   # Configure bundler and install gems
   RUN bundle config set --local path vendor/bundle && \
       bundle install
   
   # Create the layer structure
   RUN mkdir -p ruby/gems/3.4.0 && \
       cp -r vendor/bundle/ruby/3.4.0*/* ruby/gems/3.4.0/
   
   # Create the layer zip file
   RUN zip -r layer.zip ruby/
   ```

1. イメージを構築し、レイヤーを抽出します。

   ```
   docker build -t ruby-layer-builder .
   docker run --rm -v $(pwd):/output --entrypoint cp ruby-layer-builder layer.zip /output/
   ```

   そうすることで、正しい Linux 環境に gem が構築され、ローカルディレクトリに `layer.zip` ファイルがコピーされます。.zip ファイルのディレクトリ構造は次のようになります。

   ```
   ruby/
   └── gems/
       └── 3.4.0/
           ├── gems/
           │   ├── bigdecimal-3.2.2/
           │   ├── csv-3.3.5/
           │   ├── httparty-0.23.1/
           │   ├── mini_mime-1.1.5/
           │   ├── multi_xml-0.7.2/
           │   ├── nokogiri-1.18.8-x86_64-linux-gnu/
           │   └── racc-1.8.1/
           ├── build_info/
           ├── cache/
           ├── specifications/
           └── (other bundler directories)
   ```
**注記**  
関数コードでは、各 gem を個別に require する必要があります。`bundler/setup` や `Bundler.require` を使用することはできません。詳細については、「[関数内のレイヤーからの gem の使用](#ruby-layers-bundler-limitations)」を参照してください。

### カスタム Ruby モジュール
<a name="custom-ruby-modules"></a>

**ユーザー独自のコードを使用してレイヤーを作成する方法**

1. レイヤーに必要なディレクトリ構造を作成します。

   ```
   mkdir -p ruby/lib
   ```

1. `ruby/lib` ディレクトリに Ruby モジュールを作成します。次のモジュール例は、必要な情報が含まれていることを確認して注文をバリデーションします。  
**Example ruby/lib/order\$1validator.rb**  

   ```
   require 'json'
   
   module OrderValidator
     class ValidationError < StandardError; end
   
     def self.validate_order(order_data)
       # Validates an order and returns formatted data
       required_fields = %w[product_id quantity]
       
       # Check required fields
       missing_fields = required_fields.reject { |field| order_data.key?(field) }
       unless missing_fields.empty?
         raise ValidationError, "Missing required fields: #{missing_fields.join(', ')}"
       end
       
       # Validate quantity
       quantity = order_data['quantity']
       unless quantity.is_a?(Integer) && quantity > 0
         raise ValidationError, 'Quantity must be a positive integer'
       end
       
       # Format and return the validated data
       {
         'product_id' => order_data['product_id'].to_s,
         'quantity' => quantity,
         'shipping_priority' => order_data.fetch('priority', 'standard')
       }
     end
   
     def self.format_response(status_code, body)
       # Formats the API response
       {
         statusCode: status_code,
         body: JSON.generate(body)
       }
     end
   end
   ```

1. レイヤーコンテンツを圧縮します。

------
#### [ Linux/macOS ]

   ```
   zip -r layer.zip ruby/
   ```

------
#### [ PowerShell ]

   ```
   Compress-Archive -Path .\ruby -DestinationPath .\layer.zip
   ```

------

   .zip ファイルのディレクトリ構造は次のようになります。

   ```
   ruby/              
   └── lib/
       └── order_validator.rb
   ```

1. 関数内でモジュールを require し、使用します。関数コードでは、各 gem を個別に require する必要があります。`bundler/setup` や `Bundler.require` を使用することはできません。詳細については、「[関数内のレイヤーからの gem の使用](#ruby-layers-bundler-limitations)」を参照してください。例:

   ```
   require 'json'
   require 'order_validator'
   
   def lambda_handler(event:, context:)
     begin
       # Parse the order data from the event body
       order_data = JSON.parse(event['body'] || '{}')
       
       # Validate and format the order
       validated_order = OrderValidator.validate_order(order_data)
       
       OrderValidator.format_response(200, {
         message: 'Order validated successfully',
         order: validated_order
       })
     rescue OrderValidator::ValidationError => e
       OrderValidator.format_response(400, {
         error: e.message
       })
     rescue => e
       OrderValidator.format_response(500, {
         error: 'Internal server error'
       })
     end
   end
   ```

   次の[テストイベント](testing-functions.md#invoke-with-event)を使用して関数を呼び出すことができます。

   ```
   {
       "body": "{\"product_id\": \"ABC123\", \"quantity\": 2, \"priority\": \"express\"}"
   }
   ```

   予想されるレスポンス

   ```
   {
     "statusCode": 200,
     "body": "{\"message\":\"Order validated successfully\",\"order\":{\"product_id\":\"ABC123\",\"quantity\":2,\"shipping_priority\":\"express\"}}"
   }
   ```

## Lambda でレイヤーを作成する
<a name="publishing-layer"></a>

AWS CLI または Lambda コンソールを使用して、レイヤーを発行できます。

------
#### [ AWS CLI ]

[publish-layer-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-layer-version.html) AWS CLI コマンドを実行して Lambda レイヤーを作成します。

```
aws lambda publish-layer-version --layer-name my-layer --zip-file fileb://layer.zip --compatible-runtimes ruby3.4
```

[互換性のあるランタイム](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes)パラメータの使用は任意です。指定すると、Lambda はこのパラメータを使用して Lambda コンソールのレイヤーをフィルタリングします。

------
#### [ Console ]

**レイヤーを作成するには (コンソール)**

1. Lambda コンソールの [[Layers (レイヤー)] ページ](https://console.aws.amazon.com/lambda/home#/layers)を開きます。

1. [**Create layer**] (レイヤーの作成) を選択します。

1. **[.zip ファイルをアップロードする]**を選択し、前の手順で作成しておいた .zip アーカイブをアップロードします。

1. (オプション) **[互換性のあるランタイム]** で、レイヤーを構築するために使用した Ruby バージョンに対応する Ruby ランタイムを選択します。

1. **[作成]** を選択します。

------

## 関数内のレイヤーからの gem の使用
<a name="ruby-layers-bundler-limitations"></a>

関数コードでは、使用する各 gem を明示的に require する必要があります。`bundler/setup` や `Bundler.require` などの Bundler コマンドはサポートされていません。以下は、Lambda 関数内のレイヤーから gem を適切に使用する方法です。

```
# Correct: Use explicit requires for each gem
require 'nokogiri'
require 'httparty'

def lambda_handler(event:, context:)
  # Use the gems directly
  doc = Nokogiri::HTML(event['html'])
  response = HTTParty.get(event['url'])
  # ... rest of your function
end

# Incorrect: These Bundler commands will not work
# require 'bundler/setup'
# Bundler.require
```

## レイヤーを関数に追加する
<a name="ruby-layer-adding"></a>

------
#### [ AWS CLI ]

レイヤーを関数にアタッチするには、[update-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-configuration.html) AWS CLI コマンドを実行します。`--layers` パラメータには、レイヤー ARN を使用します。ARN はバージョンを指定する必要があります (`arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1` など)。詳細については、「[レイヤーとレイヤーバージョン](chapter-layers.md#lambda-layer-versions)」を参照してください。

```
aws lambda update-function-configuration --function-name my-function --cli-binary-format raw-in-base64-out --layers "arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1"
```

AWS CLI バージョン 2 を使用している場合、**cli-binary-format** オプションは必須です。これをデフォルト設定にするには、`aws configure set cli-binary-format raw-in-base64-out` を実行します。詳細については、バージョン 2 の AWS Command Line Interface ユーザーガイドの「[AWS CLI でサポートされているグローバルコマンドラインオプション](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)」を参照してください。

------
#### [ Console ]

**関数にレイヤーを追加する方法**

1. Lambda コンソールの [[関数]](https://console.aws.amazon.com/lambda/home#/functions) ページを開きます。

1. 関数を選択します。

1. **[レイヤー]** セクションまで下にスクロールし、**[レイヤーの追加]** を選択します。

1. **[レイヤーの選択]** で **[カスタムレイヤー]** を選択し、レイヤーを指定します。
**注記**  
レイヤーの作成時に[互換性のあるランタイム](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes)を追加しなかった場合、レイヤーはここには表示されません。代わりにレイヤー ARN を指定できます。

1. **[Add]** (追加) を選択します。

------

## サンプルアプリ
<a name="ruby-layer-sample-app"></a>

Lambda レイヤーのさらなる使用方法例については、「AWS Lambda デベロッパーガイド」の GitHub リポジトリにある [layer-nodejs](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-ruby) サンプルアプリケーションを参照してください。このアプリケーションには、[lodash](https://rubygems.org/gems/tzinfo) ライブラリが含まれるレイヤーがあります。各レイヤーを作成したら、対応する関数をデプロイして呼び出し、レイヤーが期待どおりに動作するかどうかを確認できます。