はじめに
前回の記事 で、Oracle Cloud Infrastructure (以下OCI) 上で、Oracle DB の Data Guard の自動フェールオーバー (Fast-Start Failover) を構成しました。
データベース側の自動的なフェールオーバーが実現できましたが、これと同時にアクセス元の Oracle Client 側でフェールオーバーの動きに追従する仕組みが必要になります。
いくつかの選択肢がありますが、透過的アプリケーション・フェイルオーバー (TAF:Transparent Application Failover) 機能を使うことで、追従することが出来ます。アプリケーション側から見ると、Oracle DB の Primary に障害が発生した際に、数分 ~ 数十分のアクセス断を待機することで、自動的に接続が復活します。
注意点としては、アプリケーション側でアクセス断に対するリトライ制御が必要になります。
アクセス断を完全に無くしたい場合は、紹介する構成とは違う構成になりますが、以下の方法があります。
- Oracle DB の 構成 : RAC or Active Data Guard を利用
- アプリケーションの構成 : Application Continuity を利用
それでは、透過的アプリケーション・フェイルオーバーの構成方法を紹介します。
構成図
透過的アプリケーション・フェイルオーバー(TAF)とは
透過的アプリケーション・フェイルオーバー(TAF)はOCI、OCCI、Java Database Connectivity (JDBC) OCIのドライバおよびODP.NETのクライアント側機能であり、インスタンスまたはネットワークの障害によりデータベース接続に失敗したときに発生するエンドユーザー・アプリケーションに対する障害を最小限にするために設計されています。TAFは、Oracle Real Application Clusters (Oracle RAC)やOracle Data Guardの物理スタンバイ・データベースなどの様々なシステム構成でも、起動後の単一インスタンス・システムでも実装できます(たとえば、修復を行うとき)。
TAFにより、クライアント・アプリケーションは、事前に構成した2次インスタンスに透過的に再接続し、最初の元のインスタンスで確立したのと新たに同じ接続を自動的に作成します。つまり、接続が失われた状態にかかわらず、接続プロパティは直前の接続のプロパティと同じです。この場合、アクティブ・トランザクションはロールバックされます。また、試行の失敗後にアプリケーションが使用しようとしたすべての文もフェイルオーバーします。
DBaaS Data Guard インスタンスの設定
TAF を利用するためには、Data Guard を構成している全てのインスタンスで、同一の ServiceName を設定する必要があります。
ServiceName は、 Oracle Client がアクセスする際に必要になる識別子になります。同一の ServiceName を設定することで、フェールオーバーをしても、同一の ServiceName を使用してアクセスすることが可能になります。
Service の設定(1台目)
DBaaS 1台目にSSHして、oracle ユーザーへスイッチ
[opc@client-6032 ~]$ ssh opc@sugihostname01.priv01.vcn.oraclevcn.com
Last login: Fri Aug 30 14:11:55 2019 from 10.2.0.32
[opc@sugihostname01 ~]$ sudo su - oracle
[oracle@sugihostname01 ~]$
SQL*Plus の起動
[oracle@sugihostname01 ~]$ sqlplus / as sysdba
SQL*Plus: Release 18.0.0.0.0 - Production on Fri Aug 30 14:13:08 2019
Version 18.6.0.0.0
Copyright (c) 1982, 2018, Oracle. All rights reserved.
Connected to:
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production
Version 18.6.0.0.0
SQL>
PDB の名前を確認 SGPBD01
SQL> select name, open_mode from v$pdbs;
NAME
--------------------------------------------------------------------------------
OPEN_MODE
----------
PDB$SEED
READ ONLY
SGPBD01
READ WRITE
SQL>
srvctl add service
コマンドで、新たな Service を作成します
- -db : Database Unique Name を指定 (OCIコンソール上から確認可能)
- -pdb : PDB の名前を指定 (PDBが存在しない場合は指定しない)
- -service : 新しく作成するサービス名を指定
srvctl add service -db suginm01_nrt1mv -pdb SGPBD01 -service taf_service
実行例
何も output されない
[oracle@sugihostname01 ~]$ srvctl add service -db suginm01_nrt1mv -pdb SGPBD01 -service taf_service
[oracle@sugihostname01 ~]$
status の確認
not running
状態となっている
[oracle@sugihostname01 ~]$ srvctl status service -db suginm01_nrt1mv -service taf_service
Service taf_service is not running.
起動
srvctl start service -db suginm01_nrt1mv -service taf_service
実行例
何も output されない
[oracle@sugihostname01 ~]$ srvctl start service -db suginm01_nrt1mv -service taf_service
[oracle@sugihostname01 ~]$
status の確認
running
状態
[oracle@sugihostname01 ~]$ srvctl status service -db suginm01_nrt1mv -service taf_service
Service taf_service is running on instance(s) suginm01
lsnrctl status
コマンドを grep して、新たに作成した service がリスナーに含まれているか確認
taf_service.priv01.vcn.oraclevcn.com
を tnsnames.ora の SERVICE_NAME に指定可能となります。
[oracle@sugihostname01 ~]$ lsnrctl status | grep -i "taf" -A1
Service "taf_service.priv01.vcn.oraclevcn.com" has 1 instance(s).
Instance "suginm01", status READY, has 2 handler(s) for this service...
[oracle@sugihostname01 ~]$
Service の設定(2台目)
Data Guard 2台目のインスタンスでも、同様の設定を行う必要があります。
Data Guard の Standby ロールのままでも操作が可能です
2台目のインスタンスへ SSH ログインして、oracle ユーザーへスイッチをします
[opc@client-6032 ~]$ ssh opc@sugihostname02.priv01.vcn.oraclevcn.com
Last login: Sat Aug 31 13:49:03 2019 from 10.2.0.32
[opc@sugihostname02 ~]$ sudo su - oracle
[oracle@sugihostname02 ~]$
srvctl add service
コマンドで、新たな Service を作成します
- -db : Database Unique Name を指定 (OCIコンソール上から確認可能)
- -pdb : PDB の名前を指定 (PDBが存在しない場合は指定しない)
- -service : 新しく付与したいサービス名を指定
srvctl add service -db suginm01_nrt1fr -pdb SGPBD01 -service taf_service
status の確認
[oracle@sugihostname02 ~]$ srvctl status service -db suginm01_nrt1fr -service taf_service
Service taf_service is not running.
[oracle@sugihostname02 ~]$
Standby Role なので、起動(start) はエラーになるため、start は行いません。フェールオーバーで Primary になったときに、自動的にstartされます
PDB に ユーザーの作成
アプリケーションから接続するために、PDB にユーザを作成します。
Data Guard 1台目(Primary)の oracle ユーザーにSSHログインします
[opc@client-6032 ~]$ ssh opc@sugihostname01.pub01.vcn.oraclevcn.com
Last login: Sun Sep 1 04:03:12 2019 from 10.2.0.32
[opc@sugihostname01 ~]$
[opc@sugihostname01 ~]$ sudo su - oracle
SQL PLUS を起動して、CDB へ接続します
sqlplus / as sysdba
接続先が CDB か確認します
SQL> show con_name;
CON_NAME
------------------------------
CDB$ROOT
PDB の一覧を確認します
SQL> show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 SGPBD01 READ WRITE NO
PDB (SGPBD01) へ接続します
alter session set container=SGPBD01;
以下コマンドを実行して、ユーザーの作成・権限の付与・表領域の上限解除を行います
CREATE USER sugi IDENTIFIED BY Sug1_Passw0rd_dayo;
GRANT CONNECT, RESOURCE TO sugi;
ALTER USER sugi quota unlimited on USERS;
Client 側の設定
Client から Oracle DB への接続確認を行うために、Java アプリケーションの配置を含めた、様々な設定をします。
インスタンス作成
CentOS 7 で適当に作成します
Oracle JDK Install
Firewalld 停止
OCIの仮想ネットワークの機能で、以下のFirewall の機能があるため、インスタンス上の Firewalld を停止します
- Security List (サブネット単位の設定)
- Network Security Group (NIC単位の設定)
sudo systemctl stop firewalld
sudo systemctl disable firewalld
Oracle Call Interface の使用
Javaアプリケーションから Oracle DB に接続する際には、JDBC と呼ばれるAPIを経由してデータの入出力を行います。
JDBC には大きく分けて2個のパターンがあります。
- Oracle Call Interface (OCI) を使用して接続 : Oracle JDBC Driver と Oracle Instant Client を導入する必要がある
- Thin Driver を使用して接続 : Oracle JDBC Driver の導入だけでOK
基本的な使い分けは、Oracle Interface Driver の固有の機能(TAFなど)を使う必要がなければ、Thin Driverを使用します。
今回は TAF を使用したいため、Oracle Call Interface を使用する方法で接続を行います
Oracle JDBC Driver を Install
OTN(Oracle Technology Network) から、Oracle JDBC Driver の Jar ファイルを ダウンロードします。以下のページから、ダウンロードしたいバージョンを選択します。基本的には最新で問題ないと思います。
https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html
tar.gz ファイルを解凍します
cd /home/opc
tar xfvz ojdbc10-full.tar.gz
今回の例では、以下のディレクトリに解凍をしています。
[opc@client ~]$ ls -la /home/opc/ojdbc10-full
total 8668
drwxr-xr-x. 2 opc opc 211 Apr 29 22:15 .
drwx------. 6 opc opc 171 Aug 29 15:35 ..
-rw-r--r--. 1 opc opc 2625 Apr 29 22:15 README.txt
-r-xr-xr-x. 1 opc opc 11596 Apr 24 21:07 ojdbc.policy
-r--r--r--. 1 opc opc 4243140 Apr 24 21:07 ojdbc10.jar
-r--r--r--. 1 opc opc 144681 Apr 24 21:07 ons.jar
-r--r--r--. 1 opc opc 306004 Apr 24 21:07 oraclepki.jar
-r--r--r--. 1 opc opc 1661488 Apr 24 21:07 orai18n.jar
-r--r--r--. 1 opc opc 205154 Apr 24 21:07 osdt_cert.jar
-r--r--r--. 1 opc opc 306854 Apr 24 21:07 osdt_core.jar
-r-xr-xr-x. 1 opc opc 29205 Apr 24 21:07 simplefan.jar
-r--r--r--. 1 opc opc 1680074 Apr 24 21:07 ucp.jar
-r--r--r--. 1 opc opc 262664 Apr 24 21:07 xdb.jar
Oracle Instant Client の Install
Oracle Instant Client は、こちら のページで公開されています。今回は、CentOS 7 なので、Instant Client for Linux x86-64
からインストールを行います。
Basic Package を Install します
wget コマンドで、作成した CentOS 7 上に rpm ファイルをダウンロードします
wget https://download.oracle.com/otn_software/linux/instantclient/193000/oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm
yum localinstall で、ダウンロードしてきた rpm ファイルから install を行います
sudo yum -y localinstall oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm
依存関係は特になにもありません
Dependencies Resolved
===========================================================================================================================================================
Package Arch Version Repository Size
===========================================================================================================================================================
Installing:
oracle-instantclient19.3-basic x86_64 19.3.0.0.0-1 /oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64 225 M
Transaction Summary
===========================================================================================================================================================
環境変数の設定
export LD_LIBRARY_PATH=/usr/lib/oracle/19.3/client64/lib
echo 'export LD_LIBRARY_PATH=/usr/lib/oracle/19.3/client64/lib' >> ~/.bashrc
参考 Document [2.2 ZIPファイルから、RPMから、およびOracle Universal Installerからのインストール]
https://docs.oracle.com/cd/E96517_01/lnoci/instant-client.html#GUID-7D65474A-8790-4E81-B535-409010791C2F
tnsnames.ora の作成
Oracle Client から、アクセスする先の Oracle DB の情報を定義するために、tnsnames.ora を作成します。
ポイントは以下の通りです
- Data Guard で構成を組んでいる、2台分の Oracle DB インスタンスの情報を記載
- SERVICE_NAME に、新規作成した Service 名
taf_service.priv01.vcn.oraclevcn.com
を指定 - TIMEOUT を指定することで、1台目のサーバーに障害が発生している際でも、2台目への接続時間を短くする
cat <<'EOF' > /usr/lib/oracle/19.3/client64/lib/network/admin/tnsnames.ora
taf_service=
(DESCRIPTION=
(LOAD_BALANCE=off)
(FAILOVER=on)
(CONNECT_TIMEOUT=1000 ms)
(TRANSPORT_CONNECT_TIMEOUT=700 ms)
(RETRY_COUNT=3)
(ADDRESS=
(PROTOCOL=tcp)
(HOST=sugihostname01.priv01.vcn.oraclevcn.com)
(PORT=1521)
)
(ADDRESS=
(PROTOCOL=tcp)
(HOST=sugihostname02.priv01.vcn.oraclevcn.com)
(PORT=1521)
)
(CONNECT_DATA=
(SERVICE_NAME=taf_service.priv01.vcn.oraclevcn.com)
(FAILOVER_MODE=
(TYPE=select)
(METHOD=basic)
)
)
)
EOF
DBaaS にサンプルテーブルを作成
Visual Studio Code や SQL*PLUS から PDB に接続して、サンプルテーブルとデータを入力します
-- Create a new Relational table with 3 columns
CREATE TABLE SAMPLE_TABLE
(
id VARCHAR2(255) NOT NULL,
name VARCHAR2(1024)
);
-- Insert rows in a Table
INSERT INTO SAMPLE_TABLE
VALUES
(
'001',
'suzuki'
);
-- Insert rows in a Table
INSERT INTO SAMPLE_TABLE
VALUES
(
'002',
'satou'
);
-- Insert rows in a Table
INSERT INTO SAMPLE_TABLE
VALUES
(
'003',
'tanaka'
);
Java アプリケーション
Client 側に Java アプリケーションを配置します
vim /home/opc/java/SampleRun.java
ソースコード (サンプルなので、雑なアルゴリズムになっています・・・)
import java.sql.*;
import java.util.Calendar;
import java.util.TimeZone;
import java.lang.Thread;
public class SampleRun {
public static void main(String[] args) {
final String path = "jdbc:oracle:oci8:@taf_service";
final String id = "sugi"; //ID
final String pw = "Sug1_Passw0rd_dayo"; //password
try (
Connection conn = DriverManager.getConnection(path, id, pw);
Statement stmt = conn.createStatement();
) {
for (int i = 0; i < 100000; i++) {
ResultSet rs = stmt.executeQuery("SELECT NAME FROM SAMPLE_TABLE");
Calendar cal1 = Calendar.getInstance();
cal1.setTimeZone(TimeZone.getTimeZone("JST"));
System.out.println("JST : " + cal1.get(Calendar.YEAR) + "/"
+ (cal1.get(Calendar.MONTH) + 1) + "/"
+ cal1.get(Calendar.DAY_OF_MONTH) + " "
+ cal1.get(Calendar.HOUR_OF_DAY) + ":"
+ cal1.get(Calendar.MINUTE) + ":"
+ cal1.get(Calendar.SECOND));
while (rs.next()) {
String name = rs.getString("name");
System.out.print(name + ",");
}
System.out.print("\n----------------------------\n");
try {
Thread.sleep(1000); // wait 1sec
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch(SQLException ex) {
ex.printStackTrace(); //Error
}
}
}
コンパイル
javac /home/opc/java/SampleRun.java
実行
cd /home/opc/java
java -classpath /home/opc/ojdbc10-full/ojdbc10.jar:\
/home/opc/ojdbc10-full/ucp.jar:\
/home/opc/ojdbc10-full/oraclepki.jar:\
/home/opc/ojdbc10-full/osdt_core.jar:\
/home/opc/ojdbc10-full/osdt_cert.jar:. \
SampleRun
実行例
[opc@client-6032 java]$ java -classpath /home/opc/ojdbc10-full/ojdbc10.jar:/home/opc/ojdbc10-full/ucp.jar:/home/opc/ojdbc10-full/oraclepki.jar:/home/opc/ojdbc10-full/osdt_core.jar:/home/opc/ojdbc10-full/osdt_cert.jar:. SampleRun
JST : 2019/8/31 23:54:18
suzuki,satou,tanaka,
----------------------------
JST : 2019/8/31 23:54:19
suzuki,satou,tanaka,
----------------------------
JST : 2019/8/31 23:54:20
suzuki,satou,tanaka,
----------------------------
^C[opc@client-6032 java]$
テスト
フェールオーバー (1台目の PMON プロセスをkill)
Primary Role のインスタンスで疑似障害を発生させるために、PMON プロセスを kill します。
PMONプロセスの、PIDの確認
[opc@sugihostname01 ~]$ sudo ps -aux | grep ora_pmon
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
oracle 16233 0.0 0.2 16466872 62932 ? Ss 05:33 0:00 ora_pmon_suginm01
opc 32058 0.0 0.0 103360 1596 pts/0 S+ 05:36 0:00 grep ora_pmon
PMON の kill を行うことで、Broker により自動フェールオーバー(FSFO) が発生します
sudo kill -9 16233
実行例
- start :
JST : 2019/9/1 14:36:32
- end :
JST : 2019/9/1 14:37:28
[opc@client-6032 java]$ java -classpath /home/opc/ojdbc10-full/ojdbc10.jar:/home/opc/ojdbc10-full/ucp.jar:/home/opc/ojdbc10-full/oraclepki.jar:/home/opc/ojdbc10-full/osdt_core.jar:/home/opc/ojdbc10-full/osdt_cert.jar:. SampleRun
JST : 2019/9/1 14:36:30
suzuki,satou,tanaka,
----------------------------
JST : 2019/9/1 14:36:31
suzuki,satou,tanaka,
----------------------------
JST : 2019/9/1 14:36:32
suzuki,satou,tanaka,
----------------------------
ORA-03113: end-of-file on communication channel
Process ID: 33127
Session ID: 338 Serial number: 12940
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
=========== 略 ===========
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
JST : 2019/9/1 14:37:28
suzuki,satou,tanaka,
----------------------------
JST : 2019/9/1 14:37:29
suzuki,satou,tanaka,
----------------------------
JST : 2019/9/1 14:37:30
suzuki,satou,tanaka,
----------------------------
JST : 2019/9/1 14:37:31
suzuki,satou,tanaka,
----------------------------
疑似障害を起こした直後に、アプリケーションからアクセスが出来なくなりましたが、
今回の場合では1分ほど待つことで、フェールオーバー先のOracle DB へ接続することが確認できました。
フェールオーバー時間は、Oracle DB の更新量によって大きく左右されるため、参考レベルとなります。
わかったこと
tnsnames.ora
を以下のように構成しているときに、1台目の sugihostname01.priv01.vcn.oraclevcn.com
が死んでいるときに、初回接続が遅くなる。
初回接続完了後、Oracle Instant Client がエラーとなっている1台目の状況を理解しているのか、2回目以降の接続は早い。再起動しても早い。
手順に記載しているとおり、TIMEOUT を指定すると良さそう
↓ TIMEOUT を指定しない方式だと、接続時間が長くなる場合がある
taf_service=
(DESCRIPTION=
(LOAD_BALANCE=off)
(FAILOVER=on)
(ADDRESS=
(PROTOCOL=tcp)
(HOST=sugihostname01.pub01.vcn.oraclevcn.com)
(PORT=1521)
)
(ADDRESS=
(PROTOCOL=tcp)
(HOST=sugihostname02.pub01.vcn.oraclevcn.com)
(PORT=1521)
)
(CONNECT_DATA=
(SERVICE_NAME=taf_service.pub01.vcn.oraclevcn.com)
(FAILOVER_MODE=
(TYPE=select)
(METHOD=basic)
)
)
)
参考URL
Oracle の Document
【技術資料】透過的アプリケーション・フェイルオーバー(TAF):Oracle Database 10g
https://blogs.oracle.com/oracle4engineer/taforacle-database-10g
General
TAF(透過的アプリケーション・フェイル オーバー)の紹介
https://www.ydc.co.jp/solution/standby/article/_taf.html
Oracleへの接続
https://www.techscore.com/tech/Java/JavaEE/JDBC/oracle-1/