Cloud SQLからクロスクラウド経由でOCIのOracle Databaseへ直接接続してみた

# はじめに

Google CloudとOCIをクロスクラウド接続し、その接続経由で Cloud SQL for PostgreSQL から Oracle Database(BaseDB)へ oracle_fdw を利用して直接接続してみた話です。

Google CloudとOCIはクロスクラウド接続で直接接続することが可能です。
- [Google Cloud から クロスクラウド接続で OCI のデータベースに接続してみた](https://qiita.com/asahide/items/fe0b33928b676dc1a760)

一方、Google CloudではCloud SQLやAlloyDBは内部VPCに配置されるため、直接他のVPCと接続することはできません。ただ、次の記事によると、BGPにカスタムルートを設定することで他のVPCとも接続することができる、というような記載がありましたので、クロスクラウド接続でも同様に行うことができるのではと思い試してみました。

- [Connect AlloyDB to Oracle using Oracle FDW](https://medium.com/google-cloud/connect-alloydb-to-oracle-using-oracle-fdw-e811780a2db8)

# テスト構成

Google Cloud上のCloud SQLからクロスクラウド接続経由でOCIのOracle Database(BaseDB)へリンク(oracle_fdw)で接続してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/105fc821-397b-8949-abd4-6e136503cbab.png)



# ネットワーク接続 追加設定ポイント

- Cloud Routerへカスタムルートの追加

利用している Cloud Router の「カスタムIP範囲」にCloud SQLのInternal VPCを追加します。今回はCloud SQLのInternal VPCのCIDRである **10.102.96.0/24** ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/d6012325-4b8b-9824-4e03-47afb1792ae9.png)

- VPCピアリングへカスタムルート交換の追加

次図のように、ピアリング設定でカスタムルートの交換に「カスタムルートのインポートとエクスポート」を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/7ec40584-afa7-41de-ba20-b8b8ba25aeee.png)

設定すると、OCI側のDynamic Routing Gateway (DRG)のルートに **10.102.96.0/24 (Cloud SQLのネット和ワーク)** が追加されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/a9fb83ab-df68-5d23-a030-48ceaed22d46.png)

# Cloud SQLからOracle Databaseへの接続

※それぞれのVPC/VCNのネットワーク設定でファイやウォールなどの設定は事前に行っておく必要があります

#### エクステンションインストール

まずはCloud SQLに接続し、oracle_fdw extensionをインストールします。

```SQL:oracle_fdwインストール

$ psql -h 10.102.96.5 -p 5432 -U postgres -d postgres
Password for user postgres: 
psql (10.23, server 16.3)
WARNING: psql major version 10, server major version 16.
         Some psql features might not work.
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=> CREATE EXTENSION oracle_fdw;
CREATE EXTENSION
postgres=> \dew+
                                                   List of foreign-data wrappers
    Name    |  Owner   |      Handler       |      Validator       | Access privileges | FDW options |         Description         
------------+----------+--------------------+----------------------+-------------------+-------------+-----------------------------
 oracle_fdw | postgres | oracle_fdw_handler | oracle_fdw_validator |                   |             | Oracle foreign data wrapper

postgres=> SELECT name, default_version FROM pg_available_extensions WHERE name LIKE 'oracle%';
    name    | default_version 
------------+-----------------
 oracle_fdw | 1.2
(1 row)
postgres=> SELECT oracle_diag();
                                  oracle_diag                                   
--------------------------------------------------------------------------------
 oracle_fdw 2.6.0, PostgreSQL 16.3, Oracle client 21.10.0.0.0, ORACLE_HOME=/lib
(1 row)
```

#### 外部サーバー設定

BaseDBを ora_server という名前で、PostgreSQL上で設定します。

```SQL:外部サーバー設定
postgres=> CREATE SERVER ora_server FOREIGN DATA WRAPPER oracle_fdw OPTIONS (dbserver '192.168.1.25:1521/PDBTEST.testsub.testvpc.oraclevcn.com');
CREATE SERVER
postgres=> \des+
                                                                         List of foreign servers
    Name    |  Owner   | Foreign-data wrapper |  Access privileges  | Type | Version |                             FDW options                              | Description 
------------+----------+----------------------+---------------------+------+---------+----------------------------------------------------------------------+-------------
 ora_server | postgres | oracle_fdw           | postgres=U/postgres |      |         | (dbserver '192.168.1.25:1521/PDBTEST.testsub.testvpc.oraclevcn.com') | 
(1 row)

```

#### ユーザマッピング設定

Oracle Database上の HR ユーザを postgres ユーザとマッピングします。

```SQL:ユーザマッピング設定
postgres=> CREATE USER MAPPING FOR postgres SERVER ora_server OPTIONS (user 'HR', password '********');
CREATE USER
postgres=> \deu+
                       List of user mappings
   Server   | User name |               FDW options                
------------+-----------+------------------------------------------
 ora_server | postgres  | ("user" 'HR', password '********')
(1 row)
```

#### 外部テーブル設定

PostgreSQL上で f_ora_tbl というテーブルを外部テーブル(Oracle Database上)として作成して、データを挿入してみます。

```SQL:外部テーブル設定
postgres=> CREATE FOREIGN TABLE f_ora_tbl(
             id integer OPTIONS (key 'true'),
             name varchar(128),
             update_time timestamp)
             SERVER ora_server OPTIONS (schema 'HR',table 'ORA_TBL');
CREATE FOREIGN TABLE
postgres=> \d
               List of relations
 Schema |   Name    |     Type      |  Owner   
--------+-----------+---------------+----------
 public | f_ora_tbl | foreign table | postgres
(1 row)
```

```SQL:データ挿入
postgres=> insert into f_ora_tbl values(1, 'asahide', current_timestamp);
INSERT 0 1
postgres=> commit;
WARNING:  there is no transaction in progress
COMMIT
postgres=> select * from f_ora_tbl;
 id |  name   |        update_time         
----+---------+----------------------------
  1 | asahide | 2024-09-27 13:48:27.917989
(1 row)
```

```SQL:Oracle Database側での確認
SQL> desc ora_tbl
 Name             Null?    Type
 ---------------- -------- ---------------------
 ID               NOT NULL NUMBER   
 NAME                      VARCHAR2(128)
 UPDATE_TIME               TIMESTAMP(6)

SQL> select * from ora_tbl;
        ID NAME       UPDATE_TIME
---------- ---------- --------------------------------------------------
         1 asahide    27-SEP-24 01.48.27.917989 PM
```


# まとめ

BGP周りの設定については具体的な方法の記載がなかったためかなり試行錯誤しましたが、結果的には繋がってよかったです。。
今回はCloud SQLで実行しましたが、もちろんAlloyDBでも行うことが可能です。
クラウド同士が接続されることで、DMSなどを利用したデータ移行だけでなく、今回紹介したような直接的なデータベース接続なども今後は行われていくのでしょうか。

#### おまけ

PostgreSQL上からの実行計画の確認結果です。

```SQL:実行結果
postgres=> EXPLAIN (analyze, verbose) SELECT * FROM f_ora_tbl;
QUERY PLAN 
------------------------------------------------------------------------
 Foreign Scan on public.f_ora_tbl  (cost=10000.00..20000.00 rows=1000 width=286) (actual time=2.293..2.302 rows=
1 loops=1)
   Output: id, name, update_time
   Oracle query: SELECT /*d9e8e872b767a314*/ r1."ID", r1."NAME", r1."UPDATE_TIME" FROM "HR"."ORA_TBL" r1
   Oracle plan: SELECT STATEMENT
   Oracle plan:   TABLE ACCESS FULL ORA_TBL
 Query Identifier: -4152246345055789969
 Planning Time: 6.254 ms
 Execution Time: 2.344 ms
(8 rows)

```

#### 参考

oracle_fdw周りは以下を参考にさせて頂きました。

- [PostgreSQL から Oracle Database への接続を試す](https://qiita.com/plusultra/items/e5023a8cf4d55e8130b2)
- [oracle_fdwを用いてPostgreSQLからOracle Databaseにアクセスする](https://jpn.nec.com/postgresql/technical_info/oracle_fdw.html)