ORA-21561の部分に関して2016/11/7に追記しています。
AWS Lambda(in VPC)からOracleに接続する時に数時間ハマったので記録として残しておきます
気付いてしまえばたいしたことないんですが、途中にある「ORA-21561: OID generation failed」のへの対応で全作業時間の9割程度なやんでいました
AWS Lambda (Node.js-v4.3.2)の実行環境に近いEC2環境の構築
AWS Lambda (Node.js-v4.3.2)の実行環境に近いEC2環境の構築を参考にしてください
Oracle InstantClientのインストール
Instant Client Downloads for Linux x86-64(要ログイン)からrpmをダウンロードします
必要なものは以下の2つです
oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm
oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm
ダウンロードしたらインストールします
$ sudo rpm -i oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm
nodejsからOracleへの接続
接続するためのコードを書く
プロジェクトの作成とoracledbのインストール
$ mkdir oracletest
$ cd oracletest
$ npm init
$ npm install oracledb --save
テスト用コードの作成
process.env.NODE_ORACLEDB_* に関しては適宜書き換え・環境変数として渡すなどしてください
※接続用の環境変数は公式のテストコードを参考にしてます
'use strict';
process.env.NODE_ORACLEDB_USER='user'
process.env.NODE_ORACLEDB_PASSWORD='password'
process.env.NODE_ORACLEDB_CONNECTIONSTRING='hostname:port/servicename'
const oracledb = require('oracledb');
exports.handler = function(event, context, callback) {
console.log('start');
oracledb.getConnection({
user : process.env.NODE_ORACLEDB_USER,
password : process.env.NODE_ORACLEDB_PASSWORD,
connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING,
},
function(err, connection) {
if (err) { callback(err); return; }
console.log('connected');
connection.execute("SELECT * FROM [table] WHERE ROWNUM < 10", [], function(err, result) {
if (err) { callback(err); return; }
callback(null, result.rows);
});
});
}
'use strict';
const t = require('./index');
t.handler({}, {}, function(error, result){
console.log(error);
console.log(result);
});
実行
$ node test.js
start
[Error: ORA-21561: OID generation failed
]
undefined
ORA-21561: OID generation failedへの対応
2016/11/07追記 ここから
その後動作を確認したりいろいろしていたところ、「DHCP Options Sets」の設定の問題だと気付きました。
VPC内で独自でDNSサーバーを構築している関係で、デフォルトで設定されているはずの「domain-name = ap-northeast-1.compute.internal」を解除していたようです。
domain-nameを追加した設定を作成し、DHCP Options setに設定したところ、LOCALDOMAINの設定なしで動作することを確認できました。
2016/11/07追記 ここまで
ぐぐるとわかりますが、/etc/hostsを書き換えるのが標準的な対応のようです
今回は、AWS Lambdaのため設定ファイルに触れないのでどうしたらいいのか悩みました
問題は自ホストのホスト名が引けないことが原因なので以下の対応を行いました
- VPCの
DNS Hostnames
を有効してVPCローカルなドメインを自ホストに紐付ける - 環境変数(LOCALDOMAIN)を利用して検索対象のドメインを設定、上記のドメインを引けるようにする
LOCALDOMAIN='ap-northeast-1.compute.internal'
※ ap-northeast-1の部分はVPCのリージョンによって切り分ける必要があります
※ AWS Lambdaの環境変数を使えばリージョンごとの切り替えは自動にできそうです
コードはこのようになりました(3行目が追加されただけ)
'use strict';
process.env.LOCALDOMAIN='ap-northeast-1.compute.internal';
process.env.NODE_ORACLEDB_USER='user'
process.env.NODE_ORACLEDB_PASSWORD='password'
process.env.NODE_ORACLEDB_CONNECTIONSTRING='hostname:port/servicename'
const oracledb = require('oracledb');
exports.handler = function(event, context, callback) {
console.log('start');
oracledb.getConnection({
user : process.env.NODE_ORACLEDB_USER,
password : process.env.NODE_ORACLEDB_PASSWORD,
connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING,
},
function(err, connection) {
if (err) { callback(err); return; }
console.log('connected');
connection.execute("SELECT * FROM [table] WHERE ROWNUM < 10", [], function(err, result) {
if (err) { callback(err); return; }
callback(null, result.rows);
});
});
}
再実行
$ node test.js
start
connected
null
[ [ 1, 2, 3], [ 3, 4, 5] ]
Lambdaへのアップロード
まずはアップロード用にzipアーカイブを作成します
zip -r oracletest.zip package.json index.js node_modules/
実行結果
アップロードして実行すると以下のようなエラーが発生しました
START RequestId: c4365deb-89fc-11e6-a186-f96f2fe34754 Version: $LATEST
2016-10-04T06:35:52.219Z c4365deb-89fc-11e6-a186-f96f2fe34754 start
2016-10-04T06:35:52.357Z c4365deb-89fc-11e6-a186-f96f2fe34754 Error: libclntsh.so.12.1: cannot open shared object file: No such file or directory
at Error (native)
at Object.Module._extensions..node (module.js:434:18)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (/var/task/node_modules/oracledb/lib/oracledb.js:35:19)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
END RequestId: c4365deb-89fc-11e6-a186-f96f2fe34754
REPORT RequestId: c4365deb-89fc-11e6-a186-f96f2fe34754 Duration: 187.52 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 15 MB
Process exited before completing request
OracleのSharedObjectのアップロード
アーカイブにsoファイルを含んでいないためだと思われるので、必要そうなものをピックアップして一緒にzipします
mkdir lib
cp /usr/lib/oracle/12.1/client64/lib/libclntsh.so.12.1 ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libclntshcore.so.12.1 ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libipc1.so ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libmql1.so ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libnnz12.so ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libocci.so ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libociei.so ./lib/
cp /usr/lib/oracle/12.1/client64/lib/libons.so ./lib/
cp /lib64/libaio.so.1 ./lib/
AWS Lambdaの環境変数を見るとわかりますが、LD_LIBRARY_PATHにプロジェクトのlibディレクトリが含まれているようなのでsoファイルをlibディレクトリ以下にコピーしています
Lambdaへの再アップロード
再アップロード用にzipファイルを作成します。
zip -r oracletest.zip package.json index.js node_modules/ lib/
最初のアップロードでは直接AWS Lambdaにアップロードできましたが、今回はsoファイルのを含むため、直接アップロード可能な50MBを超えています
そのため、今回はzipファイルをs3にアップロードし、AWS Lambda側ではそのリンクを指定してアップロードします
再実行結果
以上の対応で問題なくAWS Lambda(nodejs)からOracleへの接続ができるようになりました。
※ 再実行で出ていた取得したレコードはログではなく、AWS Lambdaの実行結果として返却されています
START RequestId: d6e7bac8-8a01-11e6-8fbc-1b17ce22866f Version: $LATEST
2016-10-04T07:12:01.578Z d6e7bac8-8a01-11e6-8fbc-1b17ce22866f start
2016-10-04T07:12:01.938Z d6e7bac8-8a01-11e6-8fbc-1b17ce22866f connected
END RequestId: d6e7bac8-8a01-11e6-8fbc-1b17ce22866f
REPORT RequestId: d6e7bac8-8a01-11e6-8fbc-1b17ce22866f Duration: 389.55 ms Billed Duration: 400 ms