現在地

Oracle Cloud 最新機能紹介:Oracle Functions でサーバーレスをはじめよう ~ Python で ADWC に接続 ~



サンプル

基盤ソリューション事業本部 オラクル事業部
サービス推進担当 サービス・イノベーション・グループ

Tricy Liu

背景

Oracle Functions は、Oracle Cloud のサーバーレスコンピューティングを実現する FaaS ( Function as a Service ) です。
イベントドリブンで呼び出せるイベントサービスの「 Oracle Cloud Infrastructure Events 」と一緒に利用することで、Oracle Cloud 上の特定のイベントが発生した際に、指定されたファンクションを作動することができます。
これを利用して、オブジェクト・ストレージ上にユーザーが分析用の Excel ファイルをアップロードしたら、Oracle Functions でその Excel ファイルを自動的に ADWC へ取り込み、すぐにデータ分析を行うといったことが可能になります。

本ブログでは、Oracle Functions ( Fn project ) から ADWC へ接続する手順を書いてみました。
作業に使用する言語として Java 、Node.js 、Go などがあげられますが、今回はまだネット上で記事数が比較的に少ない Python で接続したいと思います。

事前準備 ( Oracle Functions が使用できるまでのステップ )

Oracle Functions が使用できるまで、Oracle Cloud Infrastructure 上での必要項目の作成や、Fn project と Docker のインストールが必要です。
マニュアルに詳しく記載されていますので、以下を参照してください。(ステップ数が多くてやや煩雑ですが、気を抜かずに最後まで頑張ってください)

https://docs.oracle.com/cd/E97706_01/Content/Functions/Concepts/functionsprerequisites.htm

赤枠で囲まれているステップを完了すれば問題ありません。
これで正確に作動できるか不安な方はその次のステップで、簡単な Helloworld ファンクションを作成して遊んでみてもいいと思います。
また、Oracle Functions の仕組みについて上のステップで詳しく書かれていますので、理解を深めたい方は読んでみてください。

ADWC に接続するまでのステップ

ADWC に接続するまでの流れとして、まずはファンクションを実行するためのアプリケーションを用意します。
次に、ファンクションを正確に作動させるために下記の必須ファイルを揃えていきます。

DockerfileDocker コンテナの構成内容をまとめて記述するファイルです。
※本来、Fn project では Docker コンテナは変更できないように、Dockerfile はデフォルトでは用意されていませんが、今回、ADWC へ接続するために Oracle Instant Client のインストールや Wallet 情報の引渡しで構成変更を必要とするため、手動で作成します。
func.pyファンクションのソースコードが記載されたファイルです。
※Fn project で Python を書く際に、必ず handler 関数を使用する必要があります。Oracle Functions は Cloud 上のイベントサービスによって作動するため、handler 関数はイベント情報が書かれた JSON ファイルを読み取るために使用されます。
func.yamlファンクションのコンフィグファイルです。
設定変更をした場合、必ずアプリケーションの再デプロイが必要になります。
requirements.txtファンクションのソースコード内でインポートするパッケージを記述するファイルです。
アプリケーションをデプロイする際に記述されたパッケージが自動で Docker コンテナにインストールされます。
Oracle Instant Client インストール zip ファイルADWC への接続に Oracle Instant Client が必要です。
zip ファイルのまま、ファンクション名のディレクトリ下に格納してください。
Wallet.zip ファイルADWC に接続するために必要な構成情報ファイルです。
解凍した状態でファンクション名のディレクトリ下に格納してください。

1. アプリケーションの作成

まずは、現在作成されているアプリケーションを確認します。下記の場合ではアプリケーションはまだ作成されていません。

$ fn list apps
No apps found
NAME    ID

アプリケーションを作成するために事前準備で作成した Virtual Cloud Networks サブネットの OCID が必要です。
まず、OCI にログインし、以下の画面で1つのサブネットを選択します。

下記の画面でサブネットの OCID をコピーします。

コピーした OCID を以下のコマンドに貼り付けて、実行します。

fn create app --annotation oracle.com/oci/subnetIds='["サブネット OCID "]' <アプリケーション名>

下記はアプリケーション名を「 fn-new-app 」とした場合のコマンドです。

$ fn create app --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.phx...."]' fn-new-app

作成後、アプリケーションが存在することを確認します。

$ fn list apps
NAME              ID
fn-new-app      ocid1.fnapp.oc1.iad.aaaaaaaaaft67iqssgjrp7wwf3ck42j362mxv5234c4via…

Fn project のファンクション専用の作業用ディレクトリを作成します。

$ mkdir ~/fn_work/
$ cd ~/fn_work/

2. ファンクションの作成

Python の初期ファンクションを生成します。

fn init --runtime python <ファンクション名>

下記はファンクション名を「 adwc-conn 」とした場合のコマンドです。

$ fn init --runtime python adwc-conn
Creating function at: ./adwc-conn
Function boilerplate generated.
func.yaml created.

作業ディレクトリの下にファンクション名のディレクトリが作成されていることが確認できます。

$ ls -la ~/fn_work/
合計 0
drwxrwxr-x. 4 opc opc   36  9月 26 07:05 .
drwxrwxr-x. 6 opc opc   85  8月 21 01:42 ..
drwxr-xr-x. 2 opc opc   62  9月 26 07:05 adwc-conn

ファンクション名のディレクトリの下には、Python のソースコード func.py、コンフィグファイル func.yaml、またパッケージをインストールする用の requirements.txt がデフォルトで生成されていることが確認できます。

$ ls -la ~/fn_work/adwc-conn/
合計 0
drwxr-xr-x. 2 opc opc  62  9月 26 07:05 .
drwxrwxr-x. 4 opc opc  36  9月 26 07:05 ..
-rw-r--r--. 1 opc opc 440  9月 26 07:05 func.py
-rw-r--r--. 1 opc opc 138  9月 26 07:05 func.yaml
-rw-r--r--. 1 opc opc   3  9月 26 07:05 requirements.txt

3. 実行用ファイルの準備

ADWC へ接続するために Oracle Instant Client のインストールと ADWC の Wallet の情報が必要です。
Oracle Instant Client は下記 URL からダウンロードできます。
https://www.oracle.com/database/technologies/instant-client/downloads.html

今回はクライアント端末が Oracle Linux 7 64-bit なので、Instant Client for Linux x86-64 から

  • instantclient-basic-linux.x64-18.5.0.0.0dbru.zip  (OCI を実行するためのパッケージ)
  • instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip  ( Oracle アプリケーションを実行するためのパッケージ)
  • instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip ( SQL と PL/SQL 文を実行するためのパッケージ)
をダウンロードして、ファンクション名のディレクトリに入れます。下記の例では「ファンクション名/oracle/」の下に入れています。

$ ls -la ~/fn_work/adwc-conn/oracle/
合計 73016
drwxr-xr-x. 2 opc opc     4096  9月 26 08:55 .
drwxr-xr-x. 6 opc opc     4096  9月 26 08:27 ..
-rw-r--r--. 1 opc opc 72902980  9月 26 08:55 instantclient-basic-linux.x64-18.5.0.0.0dbru.zip
-rw-r--r--. 1 opc opc   929988  9月 26 08:55 instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip
-rw-r--r--. 1 opc opc   918469  9月 26 08:54 instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip

それから ADWC の Web コンソールから wallet.zip をダウンロードします。

Wallet をダウンロードする際にパスワードを設定する必要があります。
※緑枠内の TNS 名を次のステップ5で使用しますので、メモをしてください。

ダウンロードした zip ファイルを解凍し、ファンクション名のディレクトリに入れます。
下記の例では「ファンクション名/wallet 」というディレクトリの下に解凍しています。

$ ls -la ~/fn_work/adwc-conn/wallet/
合計 44
drwxrwxr-x. 2 opc opc 4096  9月 26 06:28 .
drwxr-xr-x. 6 opc opc 4096  9月 26 08:27 ..
-rw-rw-r--. 1 opc opc 6661  9月 26 06:28 cwallet.sso
-rw-rw-r--. 1 opc opc 6616  9月 26 06:28 ewallet.p12
-rw-rw-r--. 1 opc opc 3242  9月 26 06:28 keystore.jks
-rw-rw-r--. 1 opc opc   87  9月 26 06:28 ojdbc.properties
-rw-rw-r--. 1 opc opc  114  9月 26 06:28 sqlnet.ora
-rw-rw-r--. 1 opc opc 1040  9月 26 06:28 tnsnames.ora
-rw-rw-r--. 1 opc opc 3336  9月 26 06:28 truststore.jks

4. Dockerfile の作成

ファンクション名のディレクトリの下に新しい Dockerfile を作成します。

$ vi Dockerfile

以下の内容を入力して、保存します。
※ディレクトリの場所は実際の環境に応じて修正してください。

  • Oracle Instant Client のインストール用 zip ファイルのディレクトリ(今回の例では oracle/)
  • 解凍された wallet のディレクトリ(今回の例では wallet/)
FROM fnproject/python:3.6-dev as build-stage
WORKDIR /function
ADD requirements.txt /function/
RUN pip3 install --target /python/  --no-cache --no-cache-dir -r requirements.txt && rm -fr ~/.cache/pip /tmp* requirements.txt func.yaml Dockerfile .venv
ADD . /function/
RUN rm -fr /function/.pip_cache


FROM fnproject/python:3.6
RUN apt-get update  -y
RUN apt-get install -y unzip
RUN apt-get install -y libaio-dev 
RUN apt-get clean -y

# -- Start setup Oracle
# Install Oracle Instantclient 18.5

WORKDIR /function
ADD oracle/instantclient-basic-linux.x64-18.5.0.0.0dbru.zip /function/oracle/instantclient-basic-linux.x64-18.5.0.0.0dbru.zip
ADD oracle/instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip /function/oracle/instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip
ADD oracle/instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip /function/oracle/instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip

RUN unzip /function/oracle/instantclient-basic-linux.x64-18.5.0.0.0dbru.zip -d /function && \
    unzip /function/oracle/instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip -d /function && \
    unzip /function/oracle/instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip -d /function && \
    addgroup --gid 1000 fn && \
    adduser --uid 1000 --gid 1000 fn

ENV ORACLE_HOME=/function/instantclient_18_5
ENV LD_LIBRARY_PATH=/function/instantclient_18_5:$LD_LIBRARY_PATH
ENV PATH=/function/instantclient_18_5:$PATH
COPY wallet/* /function/instantclient_18_5/network/admin/
# -- End setup Oracle

COPY --from=build-stage /function /function
COPY --from=build-stage /python /python
ENV PYTHONPATH=/python

ENTRYPOINT ["/python/bin/fdk", "/function/func.py", "handler"]

5. func.py の編集

ファンクションのソースコードを以下のように修正して保存します。
下記のスクリプトでは「 CREATE TABLE TEST (ID NUMBER) 」という SQL 文を発行します。
※ユーザー名とパスワードは ADWC へ接続する際に使用されるもので、そのスキーマでテーブルが作成されます。
※TNS 名はステップ3でメモした3つの名前の中の1つを入力してください。また ADWC の tnsnames.ora でも確認できます。

import io
import os
import json

from fdk import response
import cx_Oracle

def handler(ctx, data: io.BytesIO=None):
    sql="CREATE TABLE TEST (ID NUMBER)"
    try:
        body = json.loads(data.getvalue())
    except Exception as ex:
         print(str(ex))

    # execute sql
    exe_sql(sql)

    return response.Response(
        ctx, response_data=json.dumps(sql),
        headers={"Content-Type": "application/json"}
    )

def exe_sql(sql):
    conn = cx_Oracle.connect('<ユーザー名>','<パスワード>','<TNS 名>')
    cur = conn.cursor()
    try:
        cur.execute(sql)
        conn.commit()
    except Exception as e:
        print(e)
    finally:
        cur.close()
        conn.close()

6. func.yaml の編集

下記のように「 timeout 」 と「 idle_timeout 」の欄を追加してください。(タイムアウトは最大120秒までしか設定できません)

schema_version: 20180708
name: adwc-conn
version: 0.0.1
runtime: python
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256
timeout: 120
idle_timeout: 1800

7. requirements.txt の編集

Python から ADWC へ接続する際に、cx_Oracle パッケージを使用しますので、下記のように「 cx_Oracle 」を追加します。

fdk
cx_Oracle

8. 全てのファイルが準備完了になっていることの確認

ファンクション名のディレクトリの下を確認します。

$ ls -lR ~/fn_work/adwc-conn/
.:
合計 36
-rw-rw-r--. 1 opc opc  1698  9月 26 09:29 Dockerfile         追加済み
-rw-rw-r--. 1 opc opc   709  9月 26 08:27 func.py            修正済み
-rw-r--r--. 1 opc opc   167  9月 26 08:27 func.yaml          修正済み
drwxr-xr-x. 2 opc opc  4096  9月 26 08:55 oracle             追加済み
-rw-r--r--. 1 opc opc    43  9月 26 05:17 requirements.txt   修正済み
drwxrwxr-x. 2 opc opc  4096  9月 26 06:28 wallet             追加済み

./oracle:
合計 73008
-rw-r--r--. 1 opc opc 72902980  9月 26 08:55 instantclient-basic-linux.x64-18.5.0.0.0dbru.zip
-rw-r--r--. 1 opc opc   929988  9月 26 08:55 instantclient-sdk-linux.x64-18.5.0.0.0dbru.zip
-rw-r--r--. 1 opc opc   918469  9月 26 08:54 instantclient-sqlplus-linux.x64-18.5.0.0.0dbru.zip

./wallet:
合計 36
-rw-rw-r--. 1 opc opc 6661  9月 26 06:28 cwallet.sso
-rw-rw-r--. 1 opc opc 6616  9月 26 06:28 ewallet.p12
-rw-rw-r--. 1 opc opc 3242  9月 26 06:28 keystore.jks
-rw-rw-r--. 1 opc opc   87  9月 26 06:28 ojdbc.properties
-rw-rw-r--. 1 opc opc  114  9月 26 06:28 sqlnet.ora
-rw-rw-r--. 1 opc opc 1040  9月 26 06:28 tnsnames.ora
-rw-rw-r--. 1 opc opc 3336  9月 26 06:28 truststore.jks

9. ファンクションの実行

実行する前にまず ADWC 上で指定するスキーマでは「 TEST 」という名のテーブルが存在しないことを確認します。

アプリケーションをデプロイします。

fn --verbose deploy --app <アプリケーション名>

下記はアプリケーション名を「 fn-new-app 」で実行した場合になります。
※実行後の出力はやや多いですが、成功するまで待ちましょう。

$ fn --verbose deploy --app fn-new-app

デプロイが成功したあと、ファンクションを実行します。

fn invoke <アプリケーション名> <ファンクション名>

下記はアプレケーション名を「 fn-new-app 」、ファンクション名を「 adwc-conn 」で実行した場合になります。

$ fn invoke fn-new-app adwc-conn
"CREATE TABLE TEST (ID NUMBER)"

ADWC 上でも確認してみましょう。

テーブルが正確に作成されたことが確認できました。
これと同様にソースファイル「 func.py 」の中の SQL 文のところに修正を加えれば、お好みの SQL 文を ADWC 上で発行することが可能になります。
ぜひお試しください。

エラー対応

ファンクション実行してエラーになった場合、syslog で確認することができます。
下記のページを参考し、ログを確認してデバッグをしてみてください。
https://fnproject.io/tutorials/Troubleshooting/