AWS Lambda(Python Runtime)でMicrosoft SQL Serverとの接続に必要なカスタムレイヤーについて

### はじめに

AWS Lambda（Python Runtime）を使用して、Amazon RDS Microsoft SQLに接続するには、ODBCドライバーとpyodbcモジュールを含むカスタムレイヤーを用意する必要があります。Lambda環境でODBCドライバーを使用するのは少し複雑ですが、適切に設定すれば非常に強力な機能を提供します。

**なぜカスタムレイヤーが必要なのか？**

AWS LambdaのRuntimeには基本的なライブラリやモジュールしか含まれていないため、特定のライブラリやモジュールを使用する場合はカスタムレイヤーが必要です。例えば、Pyodbcモジュールや特定のバージョンのPythonライブラリはLambdaの標準環境には含まれておらず、これらを利用するためにカスタムレイヤーが必要となります。

**カスタムレイヤーのメリット**：

- **再利用性**: 一度作成したレイヤーは、複数のLambda関数で再利用できます。
- **依存関係の管理**: ドライバーやライブラリの依存関係を簡単に管理でき、バージョンアップや修正も容易です。
- **デプロイの簡素化**: Lambda関数をデプロイする際、必要なライブラリやドライバーを毎回含める必要がなくなります。

この記事では、PythonのLambda関数からMicrosoft SQL Serverに接続するためのカスタムレイヤーを作成する手順を解説します。

**注**: この方法はPython 3.8、3.9、3.10、及び3.12で動作しますが、Python 3.8のサポートはまもなく終了します。また、Python 3.11では動作しません。

### カスタムレイヤーの作成

ODBCドライバーとpyodbcモジュールを含むLambdaレイヤーを作成するには、いくつかの方法があります。それぞれの方法について説明します。自分の状況やニーズに合う方法を選んでください。

#### Docker環境でカスタムレイヤーを作成
Docker環境でカスタムレイヤーを作成する際には、以下の2つのベースイメージを選択できます。

##### ベースイメージの比較

| 比較項目        | public.ecr.aws/lambda/python:$version      | amazonlinux:2023                 |
|------------------------|--------------------------------------------------|--------------------------------------------------|
| **Python 環境**    | 既に Python がインストールされている       | 自ら構築する    |
| **開発効率**      | 高い (パッケージ管理やインストール不要)      | 低い (パッケージやツールのインストールが必要)   |
| **柔軟性**       | 低い (Lambda 環境に特化)             | 高い (任意のバージョンやパッケージを自由に選べる) |
| **互換性**       | 高い (Lambda 環境と一致)             | 低い (環境構築によって異なる可能性がある)     |
| **迅速な開発**     | 高い (すぐに開発を始められる)           | 低い (環境構築に時間がかかる)           |
| **カスタマイズの自由度**| 低い (Lambda 環境に合わせた制限あり)       | 高い (自由にカスタマイズ可能)           |
| **ユースケース** | - 迅速な開発と Lambda 環境との互換性を重視する場合 <br> - Lambda 環境の Python バージョンに対応するコンテナイメージが公開されている場合　<br> - EC2インスタンスを使わずに、ローカルマシン上でLambdaレイヤーを作成したい場合 | - 柔軟なカスタマイズや特定の環境が必要な場合 <br> - 特定のPythonバージョンに対応する`public.ecr.aws/lambda/python:$version`コンテナイメージが公開されていない場合 <br> - EC2インスタンスを使わずに、ローカルマシン上でLambdaレイヤーを作成したい場合 |

- **`public.ecr.aws/lambda/python:$version`**:
Lambdaの公式環境と同じ基盤であるため、このベースイメージを利用することで互換性の問題が軽減されます。
バージョンに応じて、以下のDockerfileを作成します。
以下は、Python 3.10、unixODBC 2.3.12、msodbcsql18 を使用する例です。バージョンによって依存パッケージが異なる場合があるため、注意してください。

```Dockerfile
# Lambda Python 3.10 ベースイメージを使用する
FROM public.ecr.aws/lambda/python:3.10

# 必要なパッケージをインストールする
RUN yum update -y && \
    yum install -y \
    zip \                      # zip ツール
    tar \                      # tar ツール
    gzip \                     # gzip ツール
    make \                     # make ツール
    gcc                        # GCC コンパイラ

# unixODBC をインストールする
RUN curl ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.12.tar.gz -O && \
    tar xzvf unixODBC-2.3.12.tar.gz && \
    cd unixODBC-2.3.12 && \
    ./configure --sysconfdir=/opt --disable-gui --disable-drivers --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --prefix=/opt && \  # 設定を行う
    make && \                  # ビルド
    make install && \          # インストール
    cd .. && \
    rm -rf unixODBC-2.3.12 unixODBC-2.3.12.tar.gz  # 不要なファイルを削除

# Microsoft リポジトリを追加し、ODBC ドライバをインストールする
RUN curl https://packages.microsoft.com/config/rhel/9/prod.repo | tee /etc/yum.repos.d/mssql-release.repo && \
    yum install e2fsprogs.x86_64 fuse-libs.x86_64 libss.x86_64 -y && \  # 追加のライブラリをインストール
    ACCEPT_EULA=Y yum install -y msodbcsql18  # Microsoft ODBC ドライバをインストール

# 環境変数を設定し、pyodbc をインストールする
RUN export CFLAGS="-I/opt/include" && \
    export LDFLAGS="-L/opt/lib" && \
    cp -r /opt/microsoft/msodbcsql18/ /opt/msodbcsql18/ && \  # ODBC ドライバのディレクトリをコピー
    rm -rf /opt/microsoft/ && \                   # 不要な Microsoft ディレクトリを削除
    mkdir /opt/python/ && \                       # Python 用のディレクトリを作成
    python3.10 -m pip install pyodbc -t /opt/python/  # pyodbc を指定ディレクトリにインストール

# ODBC の設定を行う
RUN DRIVER_FILE=$(find /opt/msodbcsql18/lib64/ -name "libmsodbcsql*.so.*" | head -n 1) && \
    DRIVER_BASENAME=$(basename "$DRIVER_FILE") && \
    cat <<EOF > /opt/odbcinst.ini
[ODBC Driver 18 for SQL Server]



EOF

# odbc.ini ファイルを作成する
RUN cat <<EOF > /opt/odbc.ini
[ODBC Driver 18 for SQL Server]
Driver = ODBC Driver 18 for SQL Server
Description = My ODBC Driver 18 for SQL Server
Trace = No
EOF

# カスタムレイヤーに必要なファイルを圧縮する
RUN cd /opt && zip -r9 /root/pyodbc-layer.zip .
```


- **`amazonlinux:2023`**:
特定のPythonバージョンに対応する `public.ecr.aws/lambda/python:$version` コンテナイメージが公開されていない場合や、EC2インスタンスを利用せずにローカルマシンでカスタムレイヤーを作成したい場合にご利用ください。
必要に応じてバージョンを変更し、以下のDockerfileを作成します。
以下は、Python 3.12、unixODBC 2.3.12、msodbcsql18 を使用する例です。バージョンによって依存パッケージが異なる場合があるため、注意してください。

```Dockerfile
# Amazon Linux 2023 をベースイメージとして使用する
FROM amazonlinux:2023

# 必要なパッケージをインストールする
RUN yum update -y && \
    yum install -y \
    openssl-devel \            # OpenSSL 開発ライブラリ
    bzip2-devel \              # bzip2 開発ライブラリ
    libffi-devel \             # libffi 開発ライブラリ
    zlib-devel \               # zlib 開発ライブラリ
    zip \                      # zip ツール
    tar \                      # tar ツール
    gzip \                     # gzip ツール
    make \                     # make ツール
    gcc &&                     # GCC コンパイラ
    yum groupinstall -y "Development Tools"  # 開発ツール群をインストール

# Python 3.12 をインストールする
RUN cd /usr/src && \
    curl https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tgz -o Python-3.12.4.tgz && \
    tar xzf Python-3.12.4.tgz && \
    cd Python-3.12.4 && \
    ./configure --enable-optimizations && \  # 最適化を有効にしてコンパイル
    make altinstall && \                   # Python をインストール
    python3.12 -m pip install --upgrade pip  # pip を最新バージョンに更新

# unixODBC をインストールする
RUN curl ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.12.tar.gz -O && \
    tar xzvf unixODBC-2.3.12.tar.gz && \
    cd unixODBC-2.3.12 && \
    ./configure --sysconfdir=/opt --disable-gui --disable-drivers --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --prefix=/opt && \  # 設定を行う
    make && \                            # ビルド
    make install && \                    # インストール
    cd .. && \
    rm -rf unixODBC-2.3.12 unixODBC-2.3.12.tar.gz  # 不要なファイルを削除

# Microsoft リポジトリを追加し、ODBC ドライバをインストールする
RUN curl https://packages.microsoft.com/config/rhel/9/prod.repo | tee /etc/yum.repos.d/mssql-release.repo && \
    yum install e2fsprogs.x86_64 fuse-libs.x86_64 libss.x86_64 -y && \  # 追加のライブラリをインストール
    ACCEPT_EULA=Y yum install -y

 msodbcsql18  # Microsoft ODBC ドライバをインストール

# 環境変数を設定し、pyodbc をインストールする
RUN export CFLAGS="-I/opt/include" && \
    export LDFLAGS="-L/opt/lib" && \
    cp -r /opt/microsoft/msodbcsql18/ /opt/msodbcsql18/ && \  # ODBC ドライバのディレクトリをコピー
    rm -rf /opt/microsoft/ && \                   # 不要な Microsoft ディレクトリを削除
    mkdir /opt/python/ && \                       # Python 用のディレクトリを作成
    python3.12 -m pip install pyodbc -t /opt/python/  # pyodbc を指定ディレクトリにインストール

# ODBC の設定を行う
#ドライバーバージョンに応じて、`Driver 18` のバージョンを変更してください。
RUN DRIVER_FILE=$(find /opt/msodbcsql18/lib64/ -name "libmsodbcsql*.so.*" | head -n 1) && \
    DRIVER_BASENAME=$(basename "$DRIVER_FILE") && \
    cat <<EOF > /opt/odbcinst.ini
[ODBC Driver 18 for SQL Server]
Description=Microsoft ODBC Driver 18 for SQL Server
Driver=/opt/msodbcsql18/lib64/$DRIVER_BASENAME
UsageCount=1
EOF

# odbc.ini ファイルを作成する
#ドライバーバージョンに応じて、`Driver 18` のバージョンを変更してください。
RUN cat <<EOF > /opt/odbc.ini
[ODBC Driver 18 for SQL Server]
Driver = ODBC Driver 18 for SQL Server
Description = My ODBC Driver 18 for SQL Server
Trace = No
EOF

# カスタムレイヤーに必要なファイルを圧縮する
RUN cd /opt && zip -r9 /root/pyodbc-layer.zip .
```

各ステップを確認し、必要に応じて調整してください。
以下の手順に従って、コンテナをビルドし、カスタムレイヤーのZipファイルを取得します。

**コンテナをビルドする**:
   ```bash
   docker build -t my-amazonlinux-python-odbc .
   ```

**コンテナを起動し、コンテナにアクセスする**:
   ```bash
   docker run -it my-amazonlinux-python-odbc /bin/bash
   ```
   または
   ```bash
   docker run -it --entrypoint /bin/bash my-amazonlinux-python-odbc
   ```

**pyodbc-layer.zip ファイルが問題なく作成され、コンテナ内の /root に保存されていることを確認する**:
   ```bash
   cd # rootディレクトリに移動するコマンド
   ls -al # 存在する全てのファイルとディレクトリの情報を表示するコマンド
   ```

**コンテナから退出する**:
   ```bash
   exit
   ```

**コンテナ ID を確認する**:
   ```bash
   docker ps -a
   ```

**コンテナを再起動する**:
   ```bash
   docker start <container_id>
   ```

**コンテナからpyodbc-layer.zip ファイルをローカルにコピーする**:
   ```bash
   docker cp <container_id>:/root/pyodbc-layer.zip ./pyodbc-layer.zip
   ```

**コンテナの停止・削除** (オプション):
カスタムレイヤーがLambda関数で問題なく利用できることを確認したら、必要に応じてコンテナの停止や削除を行ってください。停止や削除のコマンドは、[こちら](https://docs.docker.com/reference/cli/docker/container/)をご確認ください。

#### Amazon Linux 2023上でのカスタムレイヤーを作成

Docker コンテナ環境が整っていない場合や、コンテナ作成時にエラーが発生した場合は、EC2のAmazon Linux 2023インスタンスを使用して、bashコマンドでカスタムレイヤーを作成できます。
必要に応じてバージョンを変更することができます。以下は、Python 3.12、unixODBC 2.3.12、msodbcsql18 を使用したbashコマンドの例を示します。バージョンにより依存パッケージが異なる可能性があるため、注意してください。

**システムパッケージの更新**:
```bash
sudo yum update -y
```

**必要なパッケージのインストール**:
```bash
sudo yum install -y openssl-devel bzip2-devel libffi-devel zlib-devel zip tar gzip make gcc
```

**開発ツールのインストール**:
```bash
sudo yum groupinstall -y "Development Tools"
```

**作業ディレクトリの移動**:
```bash
cd /usr/src
```

**Python 3.12.4 のダウンロードと解凍**:
```bash
sudo curl https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tgz -o Python-3.12.4.tgz
sudo tar xzf Python-3.12.4.tgz
```

**Python ソースディレクトリでのビルドとインストール**:
```bash
cd Python-3.12.4
sudo ./configure --enable-optimizations
sudo make altinstall
```

**pip のアップグレード**:
```bash
sudo python3.12 -m pip install --upgrade pip
```

**unixODBC のダウンロードと解凍**:
```bash
sudo curl ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.12.tar.gz -O
sudo tar xzvf unixODBC-2.3.12.tar.gz
```

**unixODBC ソースディレクトリでのビルドとインストール**:
```bash
cd unixODBC-2.3.12
sudo ./configure --sysconfdir=/opt --disable-gui --disable-drivers --enable-iconv --with-iconv-char-enc=UTF8 --with-iconv-ucode-enc=UTF16LE --prefix=/opt
sudo make
sudo make install
```

**ソースディレクトリの削除**:
```bash
cd ..
rm -rf unixODBC-2.3.12 unixODBC-2.3.12.tar.gz
```

**Microsoft リポジトリの追加**:
```bash
sudo curl https://packages.microsoft.com/config/rhel/9/prod.repo | sudo tee /etc/yum.repos.d/mssql-release.repo
```

**必要なパッケージのインストール**:
```bash
sudo yum install e2fsprogs.x86_64 fuse-libs.x86_64 libss.x86_64 -y
```

**Microsoft ODBC ドライバのインストール**:
```bash
sudo ACCEPT_EULA=Y yum install -y msodbcsql18
```

**コンパイルおよびリンクに必要な環境変数の設定**:
```bash
export CFLAGS="-I/opt/include"
export LDFLAGS="-L/opt/lib"
```

**ODBC ドライバを /opt ディレクトリにコピー**:
```bash
cd /opt
sudo cp -r /opt/microsoft/msodbcsql18/ .
```

**不要なディレクトリの削除**:
```bash
sudo rm -rf /opt/microsoft/
```

**Python モジュールをインストールするディレクトリの作成**:
```bash
sudo mkdir /opt/python/
```

**pyodbc のインストール**:
```bash
cd /opt/python/
pip3.12 install pyodbc -t .
```

**ODBC ドライバ設定ファイルの作成**:
ドライバーバージョンに応じて、`Driver 18` のバージョンやパス`/opt/msodbcsql18/lib64/libmsodbcsql-18.4.so.1.1`を変更してください。
```bash
cd /opt
sudo bash -c 'cat <<EOF > odbcinst.ini
[ODBC Driver 18 for SQL Server]
Description=Microsoft ODBC Driver 18 for SQL Server
Driver=/opt/msodbcsql18/lib64/libmsodbcsql-18.4.so.1.1
UsageCount=1
EOF'
```

**odbc.ini ファイルの作成**:
```bash
sudo bash -c 'cat <<EOF > odbc.ini
[ODBC Driver 18 for SQL Server]
Driver = ODBC Driver 18 for SQL Server
Description = My ODBC Driver 18 for SQL Server
Trace = No
EOF'
```

**カスタムレイヤーに必要なファイルの圧縮**:
```bash
cd /opt
zip -r9 ~/pyodbc-layer.zip .
```

以上の手順で作成したアーカイブファイル（pyodbc-layer.zip）を、Lambdaのカスタムレイヤーとして使います。作成したファイルをAWSコンソールのLambda管理画面からアップロードするために、アーカイブファイルをローカルマシン上にコピーします。
EC2インスタンスからローカルマシンにファイルをコピーする手順は、使用中のターミナルによって異なります。ターミナルに応じた手順を確認してください。
Tera Term の場合は、[こちら](https://qiita.com/go-to/items/409e32116213bdf4b1ce)の記事を参考にしてください。



### Lambdaレイヤーの作成とzipファイルのアップロード
AWSコンソールでレイヤーを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/4f7dbfc1-1024-71c6-4622-4b6621a2a49e.png)

「レイヤーの作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/5a3e05e4-8eef-d36f-919e-9a3f3a645de5.png)

- 「名前」と「説明」をお好みで入力してください。
- ローカルマシンからファイルをアップロードするため、「.zip ファイルをアップロード」を選択します。
- 「x86_64」を有効にします。
- ランタイムは、カスタムレイヤー作成時のPythonのバージョンやLambda関数のランタイムに合わせて選択します。例: Python 3.12

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/127bf885-a3be-1ae6-e9a6-849a61141204.png)

「アップロード」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/263e288c-fcdb-1d46-92a7-cb2badec025c.png)

対象ファイルを選択し、「開く」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/b7957242-b490-5522-4ee3-c16a625dac43.png)

ファイルがアップロードされたら、「作成」ボタンをクリックします。
これでカスタムレイヤーが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/de723644-c7c5-0a1e-0c43-65c14cae4a46.png)

### Lambda関数にカスタムレイヤーをアタッチする

対象Lambda関数を開き「Layer」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/fcd21d42-b39e-d759-51e5-97ef34a8831b.png)

「レイヤーの追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/7a7317e0-a60e-4df8-6666-318712c33cbe.png)

「カスタムレイヤー」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/2c178733-d1b0-6434-2bd3-b8b9d259b2cf.png)

対象「カスタムレイヤー」と「バージョン」を選択し、「追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/53d2c6a0-4a59-bea1-237e-6c8fd622a6f9.png)


### AWS Lambdaで`pyodbc`とODBCドライバーの動作確認
対象Lambda関数を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/0bf0342d-3420-0c10-10c4-34e73564400e.png)


以下のコードで、AWS LambdaでpyodbcライブラリとODBCドライバーの動作を確認します。

```python
import pyodbc

def check_pyodbc_installed():
    try:
        import pyodbc
        drivers = pyodbc.drivers()
        if drivers:
            print("pyodbc is installed and functional.")
        else:
            print("pyodbc is installed but no ODBC drivers are available.")
    except ImportError:
        print("pyodbc is not installed.")
    except Exception as e:
        print(f"Error checking pyodbc: {e}")

def check_odbc_driver():
    try:
        drivers = pyodbc.drivers()
        if drivers:
            print("ODBC Drivers installed:")
            for driver in drivers:
                print(f" - {driver}")
        else:
            print("No ODBC drivers installed.")
    except Exception as e:
        print(f"Error checking ODBC drivers: {e}")

def lambda_handler(event, context):
    print("Starting lambda handler...")

    # Check if pyodbc is installed and functional
    print("Checking pyodbc installation...")
    check_pyodbc_installed()
    
    # Check if ODBC drivers are working
    print("Checking ODBC drivers...")
    check_odbc_driver()

# Entry point for AWS Lambda
def handler(event, context):
    print("Starting AWS Lambda handler...")
    lambda_handler(event, context)
    print("AWS Lambda handler finished.")

```
このコードをpyodbcカスタムレイヤーがアタッチされたLambda関数で実行することで、ODBCドライバーとpyodbcモジュールが正しく設定され、Lambda関数で動作しているかを確認できます。
- 「コード」ボタンをクリックし、上記のコードを貼付ます。
- 「Test」ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/f6dc6545-91e3-f6bc-1992-30a89149cf4a.png)

「呼び出す」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/fdad9d0f-6d3a-721d-5584-852e94318c28.png)

「Function Logs」に`pyodbc is installed and functional.`と`ODBC Drivers $version for SQL Server`というログが表示されることで、正常に動作していることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/22b6c757-8acf-0e52-736e-092919393a73.png)

異常な場合は、「Response」や「Function Logs」にエラーログが表示されます。Lambda RuntimeとカスタムレイヤーのPythonバージョンが一致していること、そしてカスタムレイヤーがLambda関数に正しくアタッチされていることを再度確認してください。また、原因調査やトラブルシューティングのためにエラーログを確認してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3877509/10c4e176-35a4-aa12-42ce-6d3a23d05f01.png)

### まとめ

AWS LambdaからMicrosoft SQL Serverに接続するためのカスタムレイヤーの作成方法について解説しました。カスタムレイヤーを使用することで、Lambda環境で必要なODBCドライバーや`pyodbc`モジュールを効率的に管理・再利用できます。この記事では、Dockerコンテナを用いたレイヤー作成方法とAmazon Linux 2023での手順を紹介しました。