python-oracledb を使って Lambda(Python) から RDS for Oracle へアクセスしてみた

2022.06.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

Lambda(Python)から RDS for Oracle へアクセスする必要があり、今回実装にOracle公式の python-oracledb というモジュールを試してみたのでご紹介します。

python-oracledb とは

Oracle公式

python-oracledb とは、Pythonプログラムが Oracle データベースへアクセスできるようにするためのオープンソースモジュールです。
Python DB-API v2.0 に準拠しており、Oracle によって開発・提供されています。

cx_Oracleの後継

以前は、PythonプログラムからOracleデータベースへアクセスする際には、Python cx_Oracleというモジュールを使っていました。
python-oracledbはcx_oracleの後継です。

Oracle Client ライブラリなしでも接続可能(!!)

ドキュメントを読んでいて、これはすごいなぁと思ったのが、このモジュールのみで Oracle Client ライブラリなしで直接接続出来ます。(Thin Mode)
cx_Oracleでは別途Oracle Clientライブラリをインストールする必要があり、Lambdaの場合であればデプロイパッケージに同梱するかLambdaレイヤーを用意する必要がありました。

ただし、Oracle Client ライブラリを使って接続するモード(Thick Mode)も用意されており、高度なOracleクライアントの機能を使う場合はこちらのモードを利用する必要があります。
デフォルトはThinモードで動作しており、ThickモードでOracle Clientライブラリを使いたい場合は別途有効化が必要です。

3.1. Enabling python-oracledb Thick mode¶

ライセンスは Apache License 2.0

あと、cx_OracleはライセンスがBSDでしたが、python-oracledbではApache License 2.0に変わっています。
特に、cx_Oracleから移行される方は認識しておきましょう。

パブリックアクセス可能なRDS と SAM を使って試してみる

今回はThinモードを使って、Oracle Client ライブラリなしで RDS for Oracleへ接続してみたいと思います。
事前にパブリックアクセス可能なRDS for Oracleを作成し、適当なテーブルとレコードを作成しておきます。

認証情報や接続文字列はサンプル用に適当なものを使っています。

$ sqlplus admin/password@database-1.cpnu9ipu74g4.ap-northeast-1.rds.amazonaws.com:1521/orcl

SQL*Plus: Release 19.0.0.0.0 - Production on Thu Jun 16 18:51:19 2022
Version 19.8.0.0.0

Copyright (c) 1982, 2020, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.14.0.0.0

SQL> CREATE TABLE test
(
   hoge_id           VARCHAR2(  2    3  8) NOT NULL,
   hoge_name           VARCHAR2(50)
);

Table created.

SQL> INSERT INTO test (hoge_id, hoge_name) VALUES ('1', 'aaa');
1 row created.

SQL> INSERT INTO test (hoge_id, hoge_name) VALUES ('2', 'bbb');
1 row created.

SQL> INSERT INTO test (hoge_id, hoge_name) VALUES ('3', 'ccc');
1 row created.

SQL> select * from test;

HOGE_ID  HOGE_NAME
-------- --------------------------------------------------
1        aaa
2        bbb
3        ccc

検証用のテーブルとレコードを作成しました。

Lambda から利用してみる

次に、Lambdaから先程作成したデータを取得してみます。
今回は、SAM CLI の HelloWolrd テンプレートにデータアクセスロジックを追加してみたいと思います。

流れとしてはrequirements.txtoracledbを追加し、データアクセスロジックを追加します。
あとはsam buildしてsam deployすればうまいことレイヤー作ってうまいことデプロイしてくれます。

デプロイパッケージ作成する時どうするとか、普通にRDBMSにアクセスしちゃってるのでコネクションまわり大丈夫なのかとかあると思うのですが、今回はとりあえずモジュール使ってOracleデータベースへアクセスするってところを目的にしているので、細かいことは無視したいと思います。

$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

:

HelloWorld配下のrequirements.txtに追加し、pip installします。

requirements.txt

requests
oracledb
$ pip install -r requirements.txt  
Collecting oracledb
  Downloading oracledb-1.0.1-cp39-cp39-macosx_10_9_universal2.whl (2.4 MB)
     |████████████████████████████████| 2.4 MB 639 kB/s 
Collecting cryptography>=3.4
  Downloading cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl (2.8 MB)
     |████████████████████████████████| 2.8 MB 325 kB/s 
Collecting cffi>=1.12
  Downloading cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl (178 kB)
     |████████████████████████████████| 178 kB 529 kB/s 
Collecting pycparser
  Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB)
     |████████████████████████████████| 118 kB 692 kB/s 
Installing collected packages: pycparser, cffi, cryptography, oracledb
Successfully installed cffi-1.15.0 cryptography-37.0.2 oracledb-1.0.1 pycparser-2.21

:

データアクセスロジックを追加します。
コネクションを作成し、クエリを実行します。

以下の実装は公式サンプルを参考に実装していますが、カーソルオブジェクトからフェッチする動きはcx_Oracleと同じですね。
移行簡単に出来るかもしれないです。

app.py

import json
import oracledb
import os

un = os.environ.get('PYTHON_USERNAME')
pw = os.environ.get('PYTHON_PASSWORD')
cs = os.environ.get('PYTHON_CONNECTSTRING')

def lambda_handler(event, context):

    with oracledb.connect(user=un, password=pw, dsn=cs) as connection:
        with connection.cursor() as cursor:
            sql = """select * from test"""
            for r in cursor.execute(sql):
                print(r)

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }

あとはビルドして、クラウド上へデプロイします。

$ sam build
Your template contains a resource with logical ID "ServerlessRestApi", which is a reserved logical ID in AWS SAM. It could result in unexpected behaviors and is not recommended.
Building codeuri: /Users/iwasa.takahito/work/hoge0616oracle/hoge0616oracle/hello_world runtime: python3.9 metadata: {} architecture: x86_64 functions: ['HelloWorldFunction']
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

:

$ sam deploy --stack-name hoge0616oracle --guided

動作確認

サンプルではデータベースへ接続する際の情報を環境変数から取得しているので、環境変数を設定します。
私はsamのテンプレートでFunctionsに環境変数を追加しました。サンプルなので再デプロイで上書きされることを無視するのであれば以下のように直接Lambda上で環境変数を設定してもOKです。
実運用の際はLambdaのベストプラクティスに従って、Secret Managerなど秘匿情報の取り扱い方法を考える必要があります。

HelloWorldはAPI GatewayもデプロイされるのでHTTPアクセスしても良いですが、今回はLambdaコンソールからテスト実行までで。
実行ログを確認してみます。

START RequestId: 355656f0-29ad-40c0-af52-939d427bcd38 Version: $LATEST
('1', 'aaa')
('2', 'bbb')
('3', 'ccc')
END RequestId: 355656f0-29ad-40c0-af52-939d427bcd38
REPORT RequestId: 355656f0-29ad-40c0-af52-939d427bcd38

おー、取得出来ています。

さいごに

本日は、python-oracledb を使って Lambda(Python) から RDS for Oracle へアクセスしてみました。
Oracle公式から提供されているモジュールということもあり安心して使えますね。
Oracle Client ライブラリ不要で使えるのかなり良いですね、Lambda以外でも、ライトにOracleデータベースへアクセス出来そうです。