2025/09/03

AWSのSCTとDMSを試す

SCTとDMSを使用してオンプレミスにあるOracleをAWSのRDSへ移行してみる。
- [AWS Schema Conversion Toolとは](https://docs.aws.amazon.com/ja_jp/SchemaConversionTool/latest/userguide/CHAP_Welcome.html)
- [AWS Database Migration Serviceとは](https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/Welcome.html)

## 典型的な構成イメージ
```
[オンプレ Oracle DB(:1521)]
      │
   (VPN / Direct Connect)
      │
[AWS VPC内 EC2(SCTインストール)]
      │ │
      │(VPN / Direct Connect経由)
      │ │
      │ └── JDBC接続 → オンプレミス側ルータ → Oracle(:1521)
      │
  (VPC経由)
      │
      └── JDBC接続 → AWS VPC内 MySQL(:3306)

[AWS VPC内 DMS Replication Instance]
      │ │
      │(VPN / Direct Connect経由)
      │ │
      │ └── 接続 → オンプレミス側ルータ → Oracle(:1521)
      │
  (VPC経由)
      │
      └── 接続 → AWS VPC内 MySQL(:3306)
```
> 必要な穴あけ
> - SCT用EC2のセキュリティグループ
>   - アウトバウンド:ソースDB(オンプレ Oracle)とターゲットDB(RDS for MySQL)への JDBC ポート(通常 1521 や 3306)を許可
>   - インバウンド:必要に応じて SSH(22)などを制限付きで許可
> - DMSレプリケーションインスタンスのセキュリティグループ
>   - アウトバウンド:ソースDB(オンプレ Oracle)とターゲットDB(RDS for MySQL)へのTCPポート(1521 や 3306)を許可
>   - インバウンド:不要(DMSはアウトバウンド接続)
> - ターゲットDB(RDS for MySQL)のセキュリティグループ
>   - インバウンド:SCT用EC2 と DMSインスタンスのセキュリティグループからの接続を許可。ポートは DBエンジンに応じて(例:MySQLなら 3306)
> - オンプレ側のファイアウォール設定
>   - SCTとDMSからの接続元IP(またはVPN経由のCIDR)を許可
>   - Oracleリスナーのポート(1521)を開放
> 

## 代替の構成イメージ(仮想オンプレミス)
ここでは仮想オンプレミスとしてデータセンターの代わりに自宅からAWSへ接続します。
また、SCTは自宅LAN側のPC内に配置します。
```
[自宅 LAN内 Oracle DB(:1521)]
      │
[自宅 LAN内 PC(SCTインストール)]
      │ │
      │(LAN経由)
      │ │
      │ └── JDBC接続 → 自宅 LAN内 Oracle(:1521)
      │
   (インターネット経由)
      │
      └── JDBC接続 → 自宅ルータ(GIP:65533) → AWS VPC内 MySQL(:3306)

[AWS VPC内 AWS DMS Replication Instance]
      │ │
      │(インターネット経由)
      │ │
      │ └── 接続 → 自宅ルータ(GIP:65533) → 自宅LAN内 Oracle(LocalIP:1521)
      │
  (VPC経由)
      │
      └──── 接続 → AWS VPC内 MySQL(:3306)
```

## 構築の主な流れ
1. ソースDB(Oracle)を作成
2. ターゲットDB(MySQL)を作成
3. スキーマの移行
4. データの移行

## 1. ソースDB(Oracle)を作成
まず、自宅LAN内のPCにVirtualBoxで仮想マシン(Windows)を作成し、ソースDBにあたるOracleデータベース環境を構築する。

### 1.1 Virtualboxで仮想マシン(Windows)を作成
以下、主な設定履歴
```
- システム
  - CPU: 1→2
  - メモリ: 2048MB→4096MB
- ストレージを設定
  - OS用: 20GB
  - Oracle用: HDD(15GB)を追加
- ネットワークを設定
  - ブリッジアダプター(ルーターから直接ポート転送するため)
- OSユーザ
  - testuser/P@ssw0rd
```

### 1.2 仮想マシンのWindowsにOracle XEをインストール
以下、主な設定履歴
```
- tnsname.oraを設定
  - ブリッジアダプターのインターフェース(192.168.xx.xx)で起動するように設定
  - 以下を追記 

XEPDB1 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.xx.xx)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XEPDB1)
    )
  )
```

```
- listener.ora
  - sqlnet.oraがデフォルトで以下のためlistener.oraは未使用(EZCONNECTは簡易接続ネーミング)

NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT)
```

### 1.3 ユーザ作成、権限付与
Oracleはスキーマ名が基本的にユーザー名と同じになる。
ユーザー名 testuser を作成すると、スキーマ名は TESTUSER (大文字) となる。

以下、主な設定履歴
```
# ユーザ作成と権限付与
sqlplus / as sysdba
show con_name
show pdbs
alter session set container=XEPDB1
create user testuser identified by testuser;
grant connect, resource, dba to testuser;

# PDBにはリスナー経由でログインする
sqlplus testuser/testuser@XEPDB1
```

### 1.4 移行データの作成
#### テーブル
```
CREATE TABLE departments (
    department_id NUMBER PRIMARY KEY,
    department_name VARCHAR2(100) NOT NULL
);

CREATE TABLE employees (
    employee_id NUMBER PRIMARY KEY,
    first_name VARCHAR2(50),
    last_name VARCHAR2(50) NOT NULL,
    hire_date DATE DEFAULT SYSDATE,
    salary NUMBER(8,2),
    department_id NUMBER,
    CONSTRAINT fk_department FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
```
```
INSERT INTO departments (department_id, department_name) VALUES (10, 'Sales');
INSERT INTO departments (department_id, department_name) VALUES (20, 'Engineering');
INSERT INTO departments (department_id, department_name) VALUES (30, 'Human Resources');
INSERT INTO departments (department_id, department_name) VALUES (40, 'Finance');

INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Taro', 'Yamada', TO_DATE('2022-04-01', 'YYYY-MM-DD'), 5500.00, 10);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Hanako', 'Suzuki', TO_DATE('2023-01-15', 'YYYY-MM-DD'), 6200.00, 20);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('John', 'Smith', TO_DATE('2021-07-20', 'YYYY-MM-DD'), 4800.00, NULL);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Alice', 'Tanaka', TO_DATE('2021-07-20', 'YYYY-MM-DD'), 7000.00, 30);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Bob', 'Lee', TO_DATE('2020-12-31', 'YYYY-MM-DD'), 5100.00, 40);
```

#### シーケンス
```
CREATE SEQUENCE emp_seq
START WITH 1000
INCREMENT BY 1
NOCACHE
NOCYCLE;
```

#### ユーザ定義関数
```
CREATE OR REPLACE FUNCTION get_employee_salary_fn (
    p_emp_id IN NUMBER
) RETURN NUMBER IS
    v_salary NUMBER;
BEGIN
    SELECT salary INTO v_salary FROM employees WHERE employee_id = p_emp_id;
    RETURN v_salary;
END;
/
```

#### ストアドプロシージャ 
```
CREATE OR REPLACE PROCEDURE get_employee_salary (
    p_emp_id IN NUMBER,
    p_salary OUT NUMBER
) AS
BEGIN
    SELECT salary INTO p_salary FROM employees WHERE employee_id = p_emp_id;
END;
/
```

#### トリガー
```
CREATE OR REPLACE TRIGGER trg_before_insert_emp
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    :NEW.employee_id := emp_seq.NEXTVAL;
END;
/
```

#### ビュー
```
CREATE VIEW emp_summary AS
SELECT employee_id, first_name || ' ' || last_name AS full_name, salary
FROM employees
WHERE salary > 5000;
```

### 1.5 LAN上のPCからDB接続確認
1. 必要な穴あけ
  - 仮想マシンのWindowsファイアーウォール(wf.msc)
    - インバウンド:任意のIPアドレスからのTCP1521を許可

2. DBクライアントツールで接続を確認
  URL: jdbc:oracle:thin:@//192.168.xx.xx:1521/xepdb1

### 1.6 DMSで変更データキャプチャ(CDC)を利用する場合に必要な設定
1. サプリメンタルログの有効化
2. アーカイブログモードの有効化

#### 1.6.1 サプリメンタルログの有効化
サプリメンタル・ロギング(Supplemental Logging)は、OracleデータベースでCDCを行う際に必要な設定。どの種類のサプリメンタル・ログを有効にするかは選択可能。
```
# データベースレベルでの補足ログ設定
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

# すべての列の補足ログを有効化
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

# テーブル単位で補足ログの有効化
ALTER TABLE TESTUSER.DEPARTMENTS ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
ALTER TABLE TESTUSER.EMPLOYEES ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
```

#### 1.6.2 アーカイブログモードの有効化
アーカイブログが未設定の場合、以下のエラーが出た。
```
# アーカイブログが無効な場合
E:  To use CDC, the Oracle database must be configured to archived Redo logs [1022313]

# アーカイブログが取得できない場合(パスエラーや権限不足など)
E:  Cannot retrieve Oracle archived Redo log destination ids [1020401]
```
```
sqlplus / as sysdba

# 確認
ARCHIVE LOG LIST;

# データベースをマウント状態に切り替え(オープン状態では変更不可)
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;

# ARCHIVE LOGモードを有効にする
ALTER DATABASE ARCHIVELOG;

# データベースをオープンに戻す
ALTER DATABASE OPEN;

# ログモードを確認
ARCHIVE LOG LIST;

# ここではアーカイブログの保存先を「E:/oracle_db/archivelogs」とする。
# Oracleサービスの実行ユーザーを確認し、アーカイブログ保存先へ書き込み権限を与えること。

# アーカイブログの保存先を定義
ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='LOCATION=E:/oracle_db/archivelogs' SCOPE=SPFILE;

# 変更後の再起動
SHUTDOWN IMMEDIATE;
STARTUP;
```

## 2. ターゲットDB(MySQL)を作成
AWSにRDS for MySQLをセットアップする。
自宅からDBクライアントツールで接続したいため、今回はパブリックサブネットに直置き。

以下、主な設定履歴
```
# MySQL接続
mysql -h xxx.yyy.zzz.rds.amazonaws.com -u admin -ppassword

# DB確認
SHOW DATABASES;

# データベースを作成
CREATE DATABASE testuser;

# ユーザ作成
CREATE USER 'testuser'@'%' IDENTIFIED BY 'testuser';

# ユーザ権限付与
GRANT SELECT, SHOW VIEW, CREATE, ALTER, DROP, INDEX, REFERENCES, CREATE VIEW, TRIGGER, CREATE ROUTINE, ALTER ROUTINE, EXECUTE ON *.* TO 'testuser'@'%';

# LinuxのMySQLは大文字小文字が区別される。「testuser.*」と「TESTUSER.*」は別物
GRANT ALL PRIVILEGES ON testuser.* TO 'testuser'@'%'; 

# Oracle特有の構造をターゲット側で再現するために SCT が生成したりする。※MySqlはSEQUENCEに非対応など
GRANT ALL PRIVILEGES ON aws_oracle_ext_data.* TO 'testuser'@'%';
GRANT ALL PRIVILEGES ON aws_oracle_ext.* TO 'testuser'@'%';

# 権限を適用
FLUSH PRIVILEGES;

# 権限確認
SHOW GRANTS FOR 'testuser'@'%';
```

## 3. スキーマの移行
### 3.1 SCTを設定
ユーザーガイドを参照してSCTを設定する。
1. SCTのインストール
[AWS Schema Conversion Toolのインストール](https://docs.aws.amazon.com/ja_jp/SchemaConversionTool/latest/userguide/CHAP_Installing.Procedure.html)

2. JDBCドライバーのダウンロード
[AWS Schema Conversion Tool用の JDBC ドライバーのインストール](https://docs.aws.amazon.com/ja_jp/SchemaConversionTool/latest/userguide/CHAP_Installing.JDBCDrivers.html)
> ソースデータベースエンジンとターゲットデータベースエンジンの JDBC ドライバーをダウンロードします。
- [Oracle](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html)
- [MySQL](https://downloads.mysql.com/archives/c-j/)

#### 3.2 database migration project
[AWS SCTのユーザーインターフェイスの操作 ](https://docs.aws.amazon.com/ja_jp/SchemaConversionTool/latest/userguide/CHAP_UserInterface.html)

新規プロジェクトウィザードを使用して以下の流れで設定していく。
1. Choose a source
1. Connect to the source database
1. Choose a schema
1. Run the database migration assessment
1. Choose a target

#### 3.3 スキーマの変換
**1. Migration rules(変換ルールがあれば作成。Number型をDecimalでなく、BIGING UNSIGNEDとするなど)**

設定例
```
> - For: Column
> - Action: Change Data Type
> - From: NUMBER
> - To: BIGINT UNSIGNED
- NUMBER の定義が曖昧(精度・スケール未指定)の場合、実データの確認が必須。小数が含まれていると BIGINT ではデータ損失となる。
- SCTの評価レポートで「精度損失の可能性」などの警告が出る場合は、NUMERIC や DECIMAL の方が安全。
```
**2. Convert schema**
 > この操作により、すべてのソーススキーマのメタデータが AWS SCT プロジェクトにロードされます。

以下のエラーが出たが、ログを確認して問題なければ続行。
```
 Metadata loading was interrupted because of data fetching issues. For details, see the log.
```
SCTのログ出力先
```
C:\Users\<ユーザー名>\AWS Schema Conversion Tool\Log
```

**3. Apply to database**
> 変換されたスキーマがターゲット DB インスタンスに適用されます。
## 4. データの移行
### 4.1 レプリケーションインスタンスを作成
1. 必要なロール
```
- dms-vpc-role
- dms-cloudwatch-logs-role
```

2. レプリケーションインスタンスを作成
```
- 事前にサブネットグループを作成しておく

今回インターネット経由でソースデータベースへアクセスするために以下を指定。
- ✔ パブリックアクセス可能

- インスタンス作成に10分程度がかかる
```
### 4.2 ソースエンドポイントを作成
ソースDBの情報を指定する。
以下、設定履歴
```
- エンドポイントタイプ: ソースエンドポイント
- ソースエンジン: Oracle
- サーバー名: 自宅のGIP
- ポート: 65533
- ユーザー名: xxxx
- パスワード: xxxx
- SID/サービス名: xepdb1

# 以下を追加
- 追加の接続属性: useLogMinerReader=N;useBfile=Y;
```

エンドポイント接続属性を指定する前の接続テストでは、以下のエラーが出た。
```
Test Endpoint failed: Application-Status: 1020912, Application-Message: Log Miner is not supported in Oracle PDB environment Endpoint initialization failed.
```
> AWS DMS が Oracle の PDB(Pluggable Database)環境では LogMiner を使用できないことを示しています。つまり、現在の構成では LogMinerベースのCDC(変更データキャプチャ)方式が使えないということです。
> AWS DMS では、PDB 環境で Oracle をソースにする場合、LogMiner の代わりに Binary Reader を使用する必要があります。

対処)DMS ソースエンドポイントの「追加接続属性」に以下を指定
```
useLogMinerReader=N;useBfile=Y;
```
補足)
Binary Reader を使うには以下の権限が必要(testuserが「V\$LOG」などのV_\$ビューを参照できていればOK)
```
GRANT SELECT ON V_$LOG TO testuser;
GRANT SELECT ON V_$LOGFILE TO testuser;
GRANT SELECT ON V_$ARCHIVED_LOG TO testuser;
GRANT SELECT ON V_$DATABASE TO testuser;
GRANT SELECT ON V_$THREAD TO testuser;
GRANT SELECT ON V_$PARAMETER TO testuser;
GRANT SELECT ON V_$NLS_PARAMETERS TO testuser;
GRANT SELECT ON V_$TIMEZONE_NAMES TO testuser;
```

#### 4.3 ターゲットエンドポイントを作成
ターゲットDBの情報を指定する。
以下、設定履歴
```
- エンドポイントタイプ: ターゲットエンドポイント
- RDSインスタンス: test-migrate-rds
- ターゲットエンジン: MySQL
- サーバー名: xxxxxxxxxx.us-east-1.rds.amazonaws.com
- ポート: 3306
- ユーザー名: xxxx
- パスワード: xxxx
- 追加の接続属性: なし
```
#### 4.4 データベース移行タスクを作成
移行タスクの情報を指定する。
以下、設定履歴
```
- レプリケーションインスタンス: 上で作成したインスタンス
- ソースデータベースエンドポイント: 上で作成したソースエンドポイント
- ターゲットデータベースエンドポイント: 上で作成したターゲットエンドポイント
- 移行タイプ:
  - フルロードのみ
  - ✔ フルロード+CDC
  - CDCのみ
- レプリケーション期間: 一定期間
- タスク設定
  - カスタムCDC停止モード: 無効
  - ターゲットテーブル準備モード:
    - ✔ 何もしない
    - ターゲット上のテーブルを削除
    - 切り捨て
  - フルロード完了後にタスクを停止: 停止しない
  - LOB列設定: 
    - LOB列を含めない
    - ✔ 完全LOBモード
    - 制限付きLOBモード
  - データ検証: オフ
  - タスクログ: CloudWatchログを有効化
- テーブルマッピング: 以下を指定
```

- テーブルマッピング
移行対象のテーブルを指定する。(対象スキーマの全テーブル(%)を指定)
```
{
  "rules": [
    {
      "rule-type": "selection",
      "rule-id": "1",
      "rule-name": "include-all",
      "object-locator": {
        "schema-name": "TESTUSER",
        "table-name": "%"
      },
      "rule-action": "include"
    },
    {
      "rule-type": "selection",
      "rule-id": "2",
      "rule-name": "exclude-aws-oracle-ext",
      "object-locator": {
        "schema-name": "aws_oracle_ext_data",
        "table-name": "%"
      },
      "rule-action": "include"
    }
  ]
}
```
aws_oracle_extスキーマはOracle特有の構造をターゲット側で再現するためにSCTが生成したもの。
今回はSCTがOracleのシーケンスをMySQLで模倣したもの。(移行対象とせずにautoincrementへテーブル定義を手直しするかのどちらか。)

LinuxのMySQLはデフォルトで大文字小文字を区別するので、lower_case_table_namesを指定してない場合は、大文字と小文字のマッピングが必要な場合もある。

以下、指定例
```
{
  "rules": [
    {
      "rule-type": "selection",
      "rule-id": "1",
      "rule-name": "include-all",
      "object-locator": {
        "schema-name": "TESTUSER",
        "table-name": "%"
      },
      "rule-action": "include"
    },
    {
      "rule-type": "transformation",
      "rule-id": "2",
      "rule-name": "map-schema",
      "rule-action": "rename",
      "rule-target": "schema",
      "object-locator": {
        "schema-name": "TESTUSER"
      },
      "value": "testuser"
    },
    {
      "rule-type": "transformation",
      "rule-id": "3",
      "rule-name": "rename-table",
      "rule-action": "rename",
      "rule-target": "table",
      "object-locator": {
        "schema-name": "TESTUSER",
        "table-name": "DEPARTMENTS"
      },
      "value": "departments"
    },
    {
      "rule-type": "transformation",
      "rule-id": "4",
      "rule-name": "rename-table",
      "rule-action": "rename",
      "rule-target": "table",
      "object-locator": {
        "schema-name": "TESTUSER",
        "table-name": "EMPLOYEES"
      },
      "value": "employees"
    }
  ]
}
```
移行タスクの実行後は、CloudWatch Logsでログをモニタリングする。
## 5. 変更データキャプチャ(CDC)の確認
レプリケーション中にソースDBへデータ追加してCDCの動作を確認する。
```
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Add1', 'xxx', TO_DATE('2020-12-31', 'YYYY-MM-DD'), 7000.00, 40);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Add2', 'xxx', TO_DATE('2020-12-31', 'YYYY-MM-DD'), 4000.00, 40);
INSERT INTO employees (first_name, last_name, hire_date, salary, department_id)
VALUES ('Add3', 'xxx', TO_DATE('2020-12-31', 'YYYY-MM-DD'), 5500.00, 40);
```

## 関連記事
[AWS DMS と AWS Snowball を使用して大規模なデータベース移行を可能にする](https://aws.amazon.com/jp/blogs/news/enable-large-scale-database-migrations-with-aws-dms-and-aws-snowball)

0 件のコメント:

コメントを投稿

人気の投稿