CMAN(Oracle Connection Manager) と ロールベース・サービス/NLB で OCI DBCS Data Guard構成 の Primary接続 をシングル・エンドポイント化してみる。(Oracle Database, Oracle Cloud Infrastructure)
本記事は JPOUG Advent Calendar 2021 および OCI Advent Calendar 2021 の 15日目 の記事となります。
彡(^)(^)
JPOUG Advent Calendar 2021
https://adventar.org/calendars/6527Oracle Cloud Infrastructure Advent Calendar 2021
https://qiita.com/advent-calendar/2021/oci
CMAN(Oracle Connection Manager) は Oracle Database専用 のプロキシとして活用できる機能です。
10 Oracle Connection Managerの構成
https://docs.oracle.com/cd/F19136_01/netag/configuring-oracle-connection-manager.html#GUID-AF8A511E-9AE6-4F4D-8E58-F28BC53F64E4
Oracle Connection Managerは、データベースまたはプロキシ・サーバーに接続要求を転送するプロキシ・サーバーです。
本記事では CMAN と Data Guard のロールベース・サービス、NLB(Network Load Balancer)を使用して、
OCI DBCS の Data Guard構成 をシングル・エンドポイント化してみるやで彡(゚)(゚)
1. 過去記事の振り返り と やりたい事
本記事は下記記事の続編的な記事となります。
OCI DBCS VM で Data Guard を有効化して、ロールベース・サービスを構成して
PRIMARYロール に透過的に接続してみる。(Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/7f705f29fb65050bf295
上記の構成でも Primaryロール への透過的な接続はできているのですが、
Client側で下記のような複雑な接続文字列を記載する必要があるのが欠点です。
AYUDBCS_PRIMARY2=( DESCRIPTION_LIST= (LOAD_BALANCE=OFF)(FAILOVER=ON) ( DESCRIPTION= (CONNECT_TIMEOUT=5)(TRANSPORT_CONNECT_TIMEOUT=5)(RETRY_DELAY=3)(RETRY_COUNT=3)(ENABLE=BROKEN) (ADDRESS_LIST= (ADDRESS=(PROTOCOL=TCP)(HOST=ayudbcs1-scan.ayuprvsubnet2.ayuvcn1.oraclevcn.com)(PORT=1521)) ) (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ayudbcs_primary2.ayuprvsubnet2.ayuvcn1.oraclevcn.com)) ) ( DESCRIPTION= (CONNECT_TIMEOUT=5)(TRANSPORT_CONNECT_TIMEOUT=5)(RETRY_DELAY=3)(RETRY_COUNT=3)(ENABLE=BROKEN) (ADDRESS_LIST= (ADDRESS=(PROTOCOL=TCP)(HOST=ayudbcs2-scan.ayuprvsubnet2.ayuvcn1.oraclevcn.com)(PORT=1521)) ) (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ayudbcs_primary2.ayuprvsubnet2.ayuvcn1.oraclevcn.com)) ) )
前述の通り、本記事では CMAN/ロールベース・サービス/NLB で Data Guard の物理構成を隠蔽して、シングル・エンドポイント化してみます。シングル・エンドポイント化することで Client側の接続文字列がシンプルになります。
2. 接続トポロジー
以下の構成で PC から Data Guard の Primary に接続します。CMAN Compute が 2台 なのは
耐障害性を考慮して冗長化しているからですが、耐障害性が不要な場合は 1台 で問題ありません。
各OCIリソースの構成は以下の通りです。
Compute: E4 flex oCPU 1 * 2台(CMAN用) OS : Oracle Linux 7.9 Oracle Client 19c(19.3, Not Instant Client, Custom Install) DBCS : VM Standard2.2, EEHP, Version 19c(19.12), 1node * 2セット(Data Guard の Primary, Standby) Network Load Balancer * 1台
3. OCI DBCS の Data Guard構成 と ロールベース・サービス の登録
本記事では詳細は省略します。下記記事の通りに OCI DBCS を Data Guard構成にして、さらに ロールベース・サービス を登録します。
OCI DBCS VM で Data Guard を有効化して、ロールベース・サービスを構成して
PRIMARYロール に透過的に接続してみる。(Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/7f705f29fb65050bf295
#Primary で実行 srvctl add service -db ayucdb3_nrt1rt -service ayudbcs_primary -pdb ayupdb3 -role PRIMARY
#Standby で実行 srvctl add service -db ayucdb3_nrt1gh -service ayudbcs_primary -pdb ayupdb3 -role PRIMARY
#Primary でサービス開始 srvctl start service -db ayucdb3_nrt1rt -service ayudbcs_primary #Service登録を確認 lsnrctl status : Service "ayudbcs_primary.ayuprvsubnet02.ayuvcn01.oraclevcn.com" has 1 instance(s). Instance "AYUCDB3", status READY, has 2 handler(s) for this service... :
4. Oracle Client 19c のインストール(Not Instant Client、要カスタム・インストール)
CMAN Compute に Oracle Client 19c(Not Instant Cliet) をインストールします。
本記事では詳細は省略します。下記記事を参照して下さい。
カスタム・インストール で CMAN と Network Listener を導入する必要があるため、そこは注意して下さい。
Oracle Connection Managerのインストール
https://qiita.com/sa_to_ru13/items/c49366ee61afec045455How to create a database link from Oracle Autonomous Database to a private on-premise DB
https://qiita.com/RexZheng/items/92fb55686fff9811bfbe
5. CMAN Compute の DB接続ポート開放(firewall-cmd)
CMAN Compute では CMANプロセスが起動して DB接続ポート(本記事では 1521) を
リスニングするため、OS の Firewall の ポート開放 を実施しておきます。
ワイはこれを失念していて3時間ほど無駄にした……彡(-)(-)
firewall-cmd --add-port=1521/tcp --zone=public --permanent firewall-cmd --reload firewall-cmd --list-all : public (active) target: default icmp-block-inversion: no interfaces: ens3 sources: services: dhcpv6-client ssh ports: 1521/tcp ★★ココ :
両方の CMAN Compute でポート開放しておきます。
6. CMAN構成(cman.ora編集) と CMAN起動
cman.ora を編集して CMAN を構成します。前述の記事を参考にしています。
sudo su - oracle cd /u01/app/oracle/product/19.0.0/client_1/network/admin vi cman.ora
cman.ora の内容は以下の通りです。ほぼコピペで OK です。host の部分のみ Compute の ホスト名(FQDN) で修正します。
cman_listener = (configuration= (address=(protocol=tcp)(host=ayu-cman01.ayuprvsubnet02.ayuvcn01.oraclevcn.com)(port=1521)) (parameter_list = (log_level=ADMIN) (max_connections=1024) (idle_timeout=0) (registration_invited_nodes = *) (inbound_connect_timeout=0) (session_timeout=0) (outbound_connect_timeout=0) (max_gateway_processes=16) (min_gateway_processes=2) (remote_admin=on) (trace_level=off) (max_cmctl_sessions=4) (event_group=init_and_term,memory_ops) ) (rule_list= (rule= (src=*)(dst=*)(srv=*)(act=accept) ) ) )
以下のコマンドで CMAN を起動します。
export ORACLE_HOME=/u01/app/oracle/product/19.0.0/client_1 export PATH=${ORACLE_HOME}/bin:${PATH} export TNS_ADMIN=${ORACLE_HOME}/network/admin cmctl startup -c cman_listener : CMCTL for Linux: Version 19.0.0.0.0 - Production on 12-DEC-2021 12:10:20 Copyright (c) 1996, 2019, Oracle. All rights reserved. Current instance cman_listener is not yet started Connecting to (DESCRIPTION=(address=(protocol=tcp)(host=ayu-cman01.ayuprvsubnet02.ayuvcn01.oraclevcn.com)(port=1521))) Starting Oracle Connection Manager instance cman_listener. Please wait... CMAN for Linux: Version 19.0.0.0.0 - Production Status of the Instance ---------------------- Instance name cman_listener Version CMAN for Linux: Version 19.0.0.0.0 - Production Start date 12-DEC-2021 12:10:20 Uptime 0 days 0 hr. 0 min. 9 sec Num of gateways started 2 Average Load level 0 Log Level ADMIN Trace Level OFF Instance Config file /u01/app/oracle/product/19.0.0/client_1/network/admin/cman.ora Instance Log directory /u01/app/oracle/diag/netcman/ayu-cman01/cman_listener/alert Instance Trace directory /u01/app/oracle/diag/netcman/ayu-cman01/cman_listener/trace The command completed successfully.
CMAN の稼働状況は cmctl show status および cmctl show services で確認できます。
cmctl show status -c cman_listener cmctl show services -c cman_listener
停止は cmctl shutdown となります。
cmctl shutdown -c cman_listener
7. DBCS の tnsnames.ora編集 および REMOTE_LISTENER編集、サービス登録
DBCS の tnsnames.ora を編集して、CMAN のエントリーを追加します。
sudo su - oracle cd $ORACLE_HOME/network/admin vi tnsnames.ora
下記が追加するエントリーとなります。CMAN のリスニングポイント(host名 or IP + ポート番号)をセットします。
CMAN01 = (ADDRESS = (PROTOCOL = TCP)(HOST = ayu-cman01.ayuprvsubnet02.ayuvcn01.oraclevcn.com)(PORT = 1521)) CMAN02 = (ADDRESS = (PROTOCOL = TCP)(HOST = ayu-cman02.ayuprvsubnet02.ayuvcn01.oraclevcn.com)(PORT = 1521))
tnsnames.ora にエントリーを追加したら、REMOTE_LISTENERパラメータを編集してエントリを登録&有効化します。
sqlplus /nolog CONNECT /AS SYSDBA ALTER SYSTEM SET REMOTE_LISTENER="CMAN01,CMAN02" SCOPE=BOTH SID='*'; ALTER SYSTEM REGISTER; SHOW PARAMETER REMOTE_LISTENER System altered. System altered. NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ remote_listener string CMAN01,CMAN02
上記を Primary, Standby の両方で実行しておいて下さい。
上手く行けば REMOTE LISTENER相当の CMAN にDBサービスが登録されます。
DBサービスの登録状況を CMAN側 で確認します。
sudo su - oracle export ORACLE_HOME=/u01/app/oracle/product/19.0.0/client_1 export PATH=${ORACLE_HOME}/bin:${PATH} export TNS_ADMIN=${ORACLE_HOME}/network/admin cmctl show status -c cman_listener cmctl show services -c cman_listener : Service "ayudbcs_primary.ayuprvsubnet02.ayuvcn01.oraclevcn.com" has 1 instance(s). Instance "AYUCDB3", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=))(ADDRESS=(PROTOCOL=tcp)(HOST=10.0.1.82)(PORT=1521))) :
CMAN に ロールベース・サービス が登録されているのが確認できます。
8. NLB(Network Load Balancer)の構成
本記事では詳細は省略します。下記記事を参照して NLB(Network Load Balancer) の Backends に CMAN Compute をセットして下さい。
Private Subnet に作成した DBCS に Public Subnet の Network Load Balancer経由 で接続してみる。(Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/e20357387dfa6f12d2b7
今回は以下のように NLB の Backends として CMAN Compute を構成しています。
9. クライアント(PC)からの接続(SQL*Plus, 簡易接続)
クライアント(PC)から接続してみます。まず PowerShell の sqlplus(Instant Client) で接続してみます。
簡易接続の ホスト名(or IP):ポート番号/サービス名 で接続文字列を指定します。
ホスト名の部分は Network Load Balancer の Public IP を指定して下さい。
$env:ORACLE_HOME = "C:\tools\OracleInstantClient19c" cd C:\tools\OracleInstantClient19c ./sqlplus /nolog CONNECT AYSHIBAT/xxxxxxxx@1xx.1xx.1xx.1xx:1521/ayudbcs_primary.ayuprvsubnet02.ayuvcn01.oraclevcn.com SET LINESIZE 120; COLUMN HOST_NAME FORMAT A40; COLUMN INSTANCE_NAME FORMAT A40; SELECT HOST_NAME, INSTANCE_NAME FROM V$INSTANCE; SELECT DB_UNIQUE_NAME FROM V$DATABASE; Connected. HOST_NAME INSTANCE_NAME ---------------------------------------- ---------------------------------------- ayudbcs3 AYUCDB3 SQL> SELECT DB_UNIQUE_NAME FROM V$DATABASE; DB_UNIQUE_NAME ------------------------------ AYUCDB3_nrt1rt
上手く接続できました。OCI Console の Data Guard Association で Switchover してみます。
Switchover完了後に再度同じ接続を試みます。
$env:ORACLE_HOME = "C:\tools\OracleInstantClient19c" cd C:\tools\OracleInstantClient19c ./sqlplus /nolog CONNECT AYSHIBAT/xxxxxxxx@1xx.1xx.1xx.1xx:1521/ayudbcs_primary.ayuprvsubnet02.ayuvcn01.oraclevcn.com SET LINESIZE 120; COLUMN HOST_NAME FORMAT A40; COLUMN INSTANCE_NAME FORMAT A40; SELECT HOST_NAME, INSTANCE_NAME FROM V$INSTANCE; SELECT DB_UNIQUE_NAME FROM V$DATABASE; Connected. HOST_NAME INSTANCE_NAME ---------------------------------------- ---------------------------------------- ayudbcs4 AYUCDB3 SQL> SELECT DB_UNIQUE_NAME FROM V$DATABASE; DB_UNIQUE_NAME ------------------------------ AYUCDB3_nrt1gh
旧Standby(Switchover後のPrimary)に接続できてるで!彡(^)(^)
10. クライアント(PC)からの接続(SQL Developer, JDBC URL, 簡易接続)
次はクライアント(PC)の SQL Develper から接続してみます。
JDBC URL(JDBC接続文字列)に下記の簡易接続文字列を指定します。
jdbc:oracle:thin:@//NLBのPublic IP:ポート番号/ロールベース・サービス名
カスタムJDBC URL を下記のようにセットします。
接続後のSQL実行結果は下記の通りとなります。
再度Switchoverして、再接続&SQLを再実行してみます。
切り替わり後のPrimaryインスタンスに上手く接続できているで!彡(^)(^)
11. 注意事項&改善ポイント
まず CMAN を手動起動しているため、これは OS起動後 に自動起動するように
systemd に登録した方が良いです。今回は力尽きたq^
また Network Load Balancer が SPOF となります。NLB は自己回復性は有りますが
SLA は 100% ではないため、ここの耐障害性を高めたい場合は NLB も冗長化するのが推奨となります。
12. まとめ
DBCS(Oracle Database) の Data Guard構成を抽象化してシングル・エンドポイント化できました。
本記事では Primaryロール に接続していますが、ロールベース・サービス は Standby側 にも
設定可能なので、Active Data Guard の Read Only Standby側に接続する……と云った構成も可能です。
構成はまあまあ面倒なので、CMAN を Managed な Service として提供して貰いたいですやね。
とまれ、皆さんどんどん活用して下さいね。彡(^)(^)
Private Subnet に作成した DBCS に Public Subnet の Network Load Balancer経由 で接続してみる。(Oracle Cloud Infrastructure)
表題の通り Private Subnet に作成した DBCS に、Public Subnet の NLB経由 で接続してみるやで。
彡(゚)(゚)
1. 接続トポロジー/検証環境
接続トポロジーは以下の通りです。
PC -> (Internet) -> NLB(Public Subnet) ┬-> RAC #1(Private Subnet) └-> RAC #2(Private Subnet)
検証環境は下記記事の構成を使います。
検証によく使う Oracle Cloud Infrastructure(OCI) の 環境構成
(VCN, Seculity List, 各種Gateway, Route Table, Subnet, Compute)まとめ
https://qiita.com/ora_gonsuke777/items/08e4781af8c4f7e114ff
2. DBCS(EEHP, RAC) の作成
詳細は省略します。DBCS RAC(EEEP) を Private Subnet に作成します。
作成後に DBノード の 物理IP を確認します。
3. NLB(Network Load Balancer) の作成
作成する LB は NLB を選択します。
LB名は任意、Visibility Type は Public、Public IP は Ephemeral、VCN と Subnet は予め作成しておいたものを選択します。
Listener名は任意、プロトコルはTCP、ポートは1521を指定します。
Backends名は任意、この時点で Backend Servers は指定せず、Health Check のポートは TCP/1521 に変更します。
レビュー画面になるので、Submitボタンを押します。
作成が終わりましたが、この時点では Backend Server が無いので追加で設定します。
Backend Sets の画面に遷移して Add Backends ボタンを押します。
RAC DBノード #1 の 物理IP を指定して追加します。
RAC DBノード #2 の 物理IP も追加します。
上手く行くと Backends の Health が OK になります。
4. PC から NLB経由 で DBCS に接続
まず Pluggable Database の画面に遷移して、PDB のサービス名を確認します。
PC から NLB経由 で DBCS に接続します。今回は SQL Developer を使用彡(゚)(゚)
ホスト名:NLB の Public IP Address
ポート :1521
サービス名:上記で確認した PDB のサービス名
テストに成功したので接続して SQL を実行すると……。
上手くいきました。
5. 注意事項
Backend Sets の IP に RAC DBノード の 物理IP を指定しているので、Oracle MAA には沿っていません。
今回の要件で Oracle MAA準拠するには Compute に CMAN(Connection Manager) を構成して、
NLB の Backends に CMAN を2台以上配置して NLB⇒CMAN経由でDBに接続するのがエエかと思います(未検証)
彡(゚)(゚)
5.6 Oracle Connection Managerのアーキテクチャの理解
https://docs.oracle.com/cd/F19136_01/netag/understanding-oracle-net-architecture.html#GUID-AAEEC927-9A69-4012-AC2C-8423274F94C6
(※CMANの参考記事) How to create a database link from Oracle Autonomous Database to a private on-premise DB
https://qiita.com/RexZheng/items/92fb55686fff9811bfbe
6. まとめ
注意事項があるとは云え NLB構成による Public接続化 はお手軽です。
セキュリティに留意して、ご活用下さいね彡(^)(^)
Autonomous Database(ATP) に Java(JDBC Thin Driver) で ウォレット無しで接続してみる。
Autonomous Database(ATP/ADW/AJSON) は、従来は Autonomous Database自体が持つウォレットのファイル群を
クライアント側に配置する必要がありましたが、ウォレット無しのTLS接続を許可するオプションが追加されました。
Update Network Options to Allow TLS or Require Only Mutual TLS (mTLS) Authentication on Autonomous Database
https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/support-tls-mtls-authentication.html#GUID-3F3F1FA4-DD7D-4211-A1D3-A74ED35C0AF5About TLS Authentication
https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/connect-introduction.html#GUID-5ACCC8FF-7637-48F6-8FE4-049A720AC275
If the client is connecting with JDBC Thin using TLS authentication, the client can connect without providing a wallet.
:
今回はこの追加された機能を使用して、ウォレット無しで Autonomous Database に接続してみるやで。
彡(゚)(゚)
1. Autonomous Database(ATP) の作成
まず Autonomous Database を作成します。ウォレット無しのTLS接続 は ADB の
ACL(Access Control List) による接続制限を行うか、プライベートエンドポイントで作成する必要があります。
Update Network Options to Allow TLS or Require Only Mutual TLS (mTLS) Authentication on Autonomous Database
https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/support-tls-mtls-authentication.html#GUID-3F3F1FA4-DD7D-4211-A1D3-A74ED35C0AF5
If the instance is configured to operate over the public internet,
then one or more Access Control Lists (ACLs) must be defined before you allow TLS authentication.
:
If the instance is configured with a private endpoint, then you can allow TLS authentication.
:
今回は下記のトポロジーで Private Endpoint に ATP を作成します。
2. Mutual TLS(mTLS)認証必須の無効化
DB作成後、Autonomous Database のコンソールからmTLS認証必須を無効化します。
3. TLS接続の接続文字列確認
DB Connection ⇒ TLS(プルダウン) で TLS接続 の接続文字列を確認します。
従来の mTLS はポート番号が 1522 なのですが、TLS接続は 1521 となるもよう。
事前定義サービスは幾つかあるのですが、今回は TP接続 を選択します。彡(゚)(゚)
4. Instant Client(jdbc) の yum install(※オプション)
Compute に Instant Client を yum install して JDBC Thin Driver をゲットしておきます。
既に JDBC Thin Driver が有る場合は省略して構いません。下記では無駄に sqlplus をインストールしていたり…。
sudo su - cd /etc/yum.repos.d wget http://yum.oracle.com/public-yum-ol7.repo yum install -y yum-utils yum-config-manager --enable ol7_oracle_instantclient yum list | grep -i oracle-instant yum install oracle-instantclient-basic.x86_64 yum install oracle-instantclient-sqlplus.x86_64 yum install oracle-instantclient-jdbc.x86_64 yum install oracle-instantclient-tools.x86_64 export ORACLE_HOME=/usr/lib/oracle/21/client64 export PATH=${ORACLE_HOME}/bin:${PATH} export LD_LIBRARY_PATH=${ORACLE_HOME}/lib sqlplus /nolog SQL*Plus: Release 21.0.0.0.0 - Production on Sat Oct 30 06:38:42 2021 Version 21.3.0.0.0
5. JDKダウンロードと展開(※オプション)
Compute に JDK をダウンロードして展開します。既にJDKインストール済みの場合は省略して構いません。
今回は JDK17 をダウンロードして展開します。生の wget で tar.gz の JDK をダウンロードできる…!
彡(゚)(゚)
mkdir work cd work wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz tar xvzf latest/jdk-17_linux-x64_bin.tar.gz cd jdk-17.0.1 cd bin ./java -version java version "17.0.1" 2021-10-19 LTS Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39) Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode, sharing)
6. サンプルのソースコード
ソースコードは以前の記事のものを流用します。
Autonomous DB(ADW/ATP) に Java の JDBC Thin Driver で接続してみる。(OCI, Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/91ec0e15848a78ede385
接続文字列部分には 3. で確認した接続文字列をセットします。
接続文字列内のダブルクォートは \ でエスケープしておきましょう。
import java.sql.*; public class GetContainerName { public static void main(String[] args) { final String path = "jdbc:oracle:thin:@(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)(host=xxxxxxxx.adb.ap-tokyo-1.oraclecloud.com))(connect_data=(service_name=xxxxxxxxxxxx_ayuatp1_tp.adb.oraclecloud.com))(security=(ssl_server_cert_dn=\"CN=adb.ap-tokyo-1.oraclecloud.com, OU=Oracle ADB TOKYO, O=Oracle Corporation, L=Redwood City, ST=California, C=US\")))"; final String id = "ADMIN"; //ID final String pw = "xxxxxxxx"; //password try ( Connection conn = DriverManager.getConnection(path, id, pw); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT NAME FROM V$CONTAINERS"); ) { while (rs.next()) { String cn = rs.getString("name"); System.out.println("Container Name => " + cn); } } catch(SQLException ex) { ex.printStackTrace(); //Error } } }
7. コンパイルと実行
ソースをコンパイルして実行します。漢の 生javac に 生java実行……。
export JAVA_HOME=/home/opc/work/jdk-17.0.1 export PATH=${JAVA_HOME}/bin:${PATH} javac GetContainerName.java java -classpath /usr/lib/oracle/21/client64/lib/ojdbc8.jar:. GetContainerName Container Name => xxxxxxxxxxxxxx_AYUATP1
ウォレット無しでも ADB(ATP) への接続が成功して、コンテナ名が取得できました。
8. まとめ
幾つか事前準備は有りますが、ウォレット無しでも Autonomous Database に接続できるようになりました。
ウォレットの管理やローテション等の手間が無くなり、より使いやすくなっています。
Autonomous Database は Exadata のリソースを安価に使用できるので、皆さん活用して下さいね。
彡(^)(^)
OCI Bastionサービス で Private Compute に SSH接続してみる。(Oracle Cloud Infrastructure)
表題の通り、OCI Bastionサービスで Private Compute に SSH接続してみますやで彡(゚)(゚)
1. Bastionサービスとは?
いわゆる踏み台のサービスです。無料です。手元のPC等から OCI Compute への SSH接続、
Windows Compute へのリモートデスクトップ接続(DB)、DB系サービスへの SQL*Net接続などを
Bastionサービスを経由して実行可能です。サービス紹介やマニュアルは下記となります。
要塞概要
https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Concepts/bastionoverview.htm
※要塞……Bastion の直訳らしいです。踏み台と訳した方がエエですね……彡(-)(-)Oracle Bastion
https://www.oracle.com/jp/security/cloud-security/bastion/
2. 検証環境
下記記事の構成を流用します。ただし Public Subnet は今回使用しません。
Private Subnet に Compute と Bastionサービス を作成します。
検証によく使う Oracle Cloud Infrastructure(OCI) の 環境構成(VCN, Seculity List, 各種Gateway, Route Table, Subnet, Compute)まとめ
https://qiita.com/ora_gonsuke777/items/08e4781af8c4f7e114ff
3. ポリシー(権限)の付与
Bastionサービスを作成するIAMユーザーにBastionサービスに必要なポリシー(権限)を付与します。
要塞IAMポリシー
https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Reference/bastionpolicyreference.htm
今回は下記のポリシーをIAMユーザーに付与しています。
Allow group ayu-group-bastion1 to manage bastion-family in tenancy Allow group ayu-group-bastion1 to manage virtual-network-family in tenancy Allow group ayu-group-bastion1 to read instance-family in tenancy Allow group ayu-group-bastion1 to read instance-agent-plugins in tenancy Allow group ayu-group-bastion1 to inspect work-requests in tenancy
4. Compute の Bastion Plugin有効化
Private Subnet に作成した Compute をコンソールで参照して Bastion Plugin を有効化します。
Oracle Provided Image の Oracle Linux等で作成した Compute なら「OCI Cloud Agent」のサブタブから制御可能です。
5. Bastionサービスの作成
Identity&Security → Bastion で画面遷移して、Bastionサービスを作成します。
今回は Private Subnet に Bastionサービスを作成します。
CIDR Block Allowed は接続元(PC端末など)の IP を CIDR で指定します。単体のIPの場合は xxx.xxx.xxx.xxx/32 で。
6. Bastionセッションの作成
Bastionセッションを作成します。セッションを作成すると、このセッションが生存している間は
対象リソースに接続可能になります。セッション作成時に 4. の Plugin を有効化していないとエラーになります。
7. SSHコマンドのコピー/編集/接続
セッションが作成されると接続のためのSSHコマンドがコピー可能になります。
編集前のSSHコマンド
ssh -i <privateKey> -o ProxyCommand="ssh -i <privateKey> -W %h:%p -p 22 ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@host.bastion.us-phoenix-1.oci.oraclecloud.com" -p 22 opc@10.0.x.x
編集後のSSHコマンド(※5. Bastionサービス作成時の公開鍵に対応する秘密鍵を指定)
ssh -i C:\Users\user\Desktop\bastion\ayu-bastion.key -o ProxyCommand="ssh -i C:\Users\user\Desktop\bastion\ayu-bastion.key -W %h:%p -p 22 ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@host.bastion.us-phoenix-1.oci.oraclecloud.com" -p 22 opc@10.0.x.x
編集後のSSHコマンドを実行します。今回は PC端末(Windows10)の PowerShell上 で実行してみました。
Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. 新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6 PS C:\Users\user> ssh -i C:\Users\user\Desktop\bastion\ayu-bastion.key -o ProxyCommand="ssh -i C:\Users\user\Desktop\bastion\ayu-bastion.key -W %h:%p -p 22 ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@host.bastion.us-phoenix-1.oci.oraclecloud.com" -p 22 opc@10.0.x.x Last login: Mon Aug 30 11:53:24 2021 from 10.0.6.94 Last login: Mon Aug 30 11:53:24 2021 from 10.0.6.94 [opc@ayu-compute1 ~]$ id -a uid=1000(opc) gid=1000(opc) groups=1000(opc),4(adm),10(wheel),190(systemd-journal) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 [opc@ayu-compute1 ~]$ hostname ayu-compute1 [opc@ayu-compute1 ~]$
Bastionサービスのセッション経由で Compute に SSH接続できました彡(^)(^)
8. まとめ
上記 4. の Bastion Plugin有効化 がちょっとしたハマりポイントでしょうか。
Public Subnet に作成する必要が無いというのも留意すべきかもしれません。
今回は SSH接続 でしたが、ポートフォワードの要領で 22(SSH)ポート以外 でも接続可能です。
無料のサービスなので、どんどん活用して下さいね。彡(^)(^)
OCI CLI で OCI Console の「Limits, Quotas and Usage」相当の情報を取得してみる。
OCIリソースの Limits と Usage のある時点の断面を、ファイル出力して保持しておくという
必要に迫られたので、表題の件について色々検証してみました。彡(゚)(゚)
1. 前提条件/検証環境
OCI上に構築した Private Subnet の Compute(Linux) に OCI CLI をインストールして作業します。
OCI CLI は インストール と 設定(oci setup config) が完了した状態を前提とします。
検証によく使う Oracle Cloud Infrastructure(OCI) の
環境構成(VCN, Seculity List, 各種Gateway, Route Table, Subnet, Compute)まとめ
https://qiita.com/ora_gonsuke777/items/08e4781af8c4f7e114ff(マニュアル) CLIの構成
https://docs.oracle.com/ja-jp/iaas/Content/API/SDKDocs/cliconfigure.htm
2. Service Name の取得(oci limits definition list ...)
Usage を OCI CLI で取得するには、最終的には oci limits resource-availability get というコマンドを使用します。
このコマンドの引数である service-name や limit-name を取得するために、まずは oci limits definition list を実行します。
※コンパートメントの OCID が必要なため、そちらは予め OCIコンソールで確認しておくこと
oci limits definition list -c ocid1.tenancy.oc1..xxxxxxxxxx --all { "data": [ { "are-quotas-supported": true, "description": "Concurrent Appliance Count", "is-deprecated": false, "is-dynamic": false, "is-eligible-for-limit-increase": true, "is-resource-availability-supported": false, "name": "active-appliance-count", "scope-type": "REGION", "service-name": "data-transfer" ★ここが必要 }, { "are-quotas-supported": false, "description": "Concurrent Cloud Shell Users", "is-deprecated": false, "is-dynamic": false, "is-eligible-for-limit-increase": true, "is-resource-availability-supported": true, "name": "active-users", "scope-type": "GLOBAL", "service-name": "cloud-shell" ★ここが必要 }, { "are-quotas-supported": true, "description": "Always Free Autonomous Database Instance Count", "is-deprecated": false, "is-dynamic": false, "is-eligible-for-limit-increase": true, "is-resource-availability-supported": true, "name": "adb-free-count", "scope-type": "REGION", "service-name": "database" ★ここが必要 }, :
3. Limit Name と Limits値 の取得(oci limits value list ...)
上記 2. で取得した Service Name で 次は Limit Name と Limits値 を取得します。
AD(FD)リソース(※) と リージョナルリソース で必要な部分が異なるので、それぞれサンプルを載せておきます。
※AD ... Availability Domain のこと、FD ... Fault Domain のこと
- AD(FD)リソースの場合(※下記は service-name に compute を指定)
oci limits value list -c ocid1.tenancy.oc1..xxxxxxxxxx --service-name compute --all { "data": [ { "availability-domain": "xxxx:PHX-AD-1", ★ここが必要 "name": "bm-dense-io1-36-count", ★ここが必要 "scope-type": "AD", "value": 0 ★ここがLimits値(例外あり) }, { "availability-domain": "xxxx:PHX-AD-2", ★ここが必要 "name": "bm-dense-io1-36-count", ★ここが必要 "scope-type": "AD", "value": 0 ★ここがLimits値(例外あり) }, :
- リージョナルリソースの場合(※下記は service-name に vcn を指定)
oci limits value list -c ocid1.tenancy.oc1..xxxxxxxxxx --service-name vcn --all { "data": [ { "availability-domain": null, "name": "dhcp-option-count", ★ここが必要 "scope-type": "REGION", "value": 300 ★ここがLimits値(例外あり) }, { "availability-domain": null, "name": "flow-log-config-count", ★ここが必要 "scope-type": "GLOBAL", "value": 100 ★ここがLimits値(例外あり) }, { "availability-domain": null, "name": "internet-gateway-count", ★ここが必要 "scope-type": "REGION", "value": 1 ★ここがLimits値(例外あり) }, :
4. Usage の取得(oci limits resource-availability get ...)
上記 2. と 3. で取得した Service Name/Limit Name で次は Usage を取得します。
oci limits resource-availability getコマンドを実行します。
AD(FD)リソース と リージョナルリソース で必須オプションが異なるので、
それぞれサンプルを載せておきます。
- AD(FD)リソースの場合(※下記は service-name に compute、limit-name に standard-e3-core-ad-count を指定)
oci limits resource-availability get --compartment-id ocid1.tenancy.oc1..xxxxxxxxxx --limit-name standard-e3-core-ad-count --service-name compute --availability-domain xxxx:PHX-AD-1 { "data": { "available": 600, "effective-quota-value": null, "fractional-availability": 600.0, "fractional-usage": 0.0, "used": 0 ★ここがUsage値(例外あり) } }
- リージョナルリソースの場合
oci limits resource-availability get --compartment-id ocid1.tenancy.oc1..xxxxxxxxxx --limit-name vcn-count --service-name vcn { "data": { "available": 49, "effective-quota-value": null, "fractional-availability": 49.0, "fractional-usage": 1.0, "used": 1 ★ここがUsage値(例外あり) } }
5. 複数リージョン の Limits と Usage を取得する場合
上記のコマンドの場合は OCI CLI の デフォルトconfig に定義された
リージョンの Limits と Usage が取得されてきます。
複数リージョン の Limits と Usage を取得したい場合は、
各コマンドに --regionオプション を指定します。
下記は oci limits value listコマンド のサンプルとなります。
JSON の availability-domain がデフォルトの Phoenix と異なるのが判ります。
oci limits value list -c ocid1.tenancy.oc1..xxxxxxxxxx --service-name compute --all --region us-ashburn-1 { "data": [ { "availability-domain": "xxxx:US-ASHBURN-AD-1", "name": "bm-dense-io1-36-count", "scope-type": "AD", "value": 0 }, { "availability-domain": "xxxx:US-ASHBURN-AD-2", "name": "bm-dense-io1-36-count", "scope-type": "AD", "value": 0 }, :
6. スクリプト化(OCILimitsAndUsage.sh)
一連のコマンドを手動で実行するのは流石に無理なので、スクリプト化してみますた。
#!/usr/bin/bash ##################################################################################### # # Overview: This shell outputs csv of the Service Limits and Usage in the OCI tenant. # Pre-requirement1. Have the OCI CLI installed. # Pre-requirement2. Make OCI CLI ready to run through the path. # Pre-requirement3. Complete the configuration(oci setup config). # # Usage: ./OCILimitsAndUsage.sh $1 $2 $3 # $1 ... OCI CLI configration file full path # $2 ... Compartment OCID # $3 ... Tenancy Name # # Example: ./OCILimitsAndUsage.sh /home/opc/.oci/config ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxx ayutenant # ##################################################################################### # Get Region Name List function GetRegionNameList() { oci iam region-subscription list | jq -r '.data[]|[."region-name"]|@csv' | tr -d '"' > ${REGION_NAME_LIST} return 0; } # Get Service Name List function GetServiceNameList() { REGION_NAME=$1 oci limits definition list -c ${COMPARTMENT_OCID} --all --region ${REGION_NAME} | jq -r '.data[]|[."service-name"]|@csv' | sort -u > ${SERVICE_NAME_LIST} return 0; } # Get Limit Name List function GetLimitNameList() { REGION_NAME=$1 cat ${SERVICE_NAME_LIST} | while read -r SERVICE_NAME do SERVICE_NAME2=`echo ${SERVICE_NAME} | tr -d '"'` oci limits value list -c ${COMPARTMENT_OCID} --service-name ${SERVICE_NAME2} --all --region ${REGION_NAME} | jq -r '.data[]|[."availability-domain", ."name", ."value"]|@csv' > ${LIMIT_NAME_LIST_TMP} cat ${LIMIT_NAME_LIST_TMP} | sed "s/^,/\"\",/" | sed -e "s/^/${SERVICE_NAME},/" >> ${LIMIT_NAME_LIST} done; return 0; } # Get Limits and Usage function GetLimitsAndUsage() { REGION_NAME=$1 DATETIME=`date +"%Y%m%d,%H%M%S"` cat ${LIMIT_NAME_LIST} | sed "s/,/ /g" | while read -r SERVICE_NAME AD_NAME LIMIT_NAME LIMIT_VALUE do SERVICE_NAME2=`echo ${SERVICE_NAME} | tr -d '"'` AD_NAME2=`echo ${AD_NAME} | tr -d '"'` LIMIT_NAME2=`echo ${LIMIT_NAME} | tr -d '"'` LIMIT_VALUE2=`echo ${LIMIT_VALUE} | tr -d '"'` if [ "${AD_NAME}" = '""' ] ; then # echo "AD is empty." oci limits resource-availability get --compartment-id ${COMPARTMENT_OCID} --limit-name ${LIMIT_NAME2} --service-name ${SERVICE_NAME2} --region ${REGION_NAME} | jq -r '.data|[."available", ."used"]|@csv' > ${LIMIT_USAGE_TMP} cat ${LIMIT_USAGE_TMP} | sed "s/^/${DATETIME},${TENANCY_NAME},${REGION_NAME},${SERVICE_NAME2},${AD_NAME2},${LIMIT_NAME2},${LIMIT_VALUE2},/" >> ${LIMIT_USAGE_CSV} else # echo "AD is not empty." oci limits resource-availability get --compartment-id ${COMPARTMENT_OCID} --limit-name ${LIMIT_NAME2} --service-name ${SERVICE_NAME2} --availability-domain ${AD_NAME2} --region ${REGION_NAME} | jq -r '.data|[."available", ."used"]|@csv' > ${LIMIT_USAGE_TMP} cat ${LIMIT_USAGE_TMP} | sed "s/^/${DATETIME},${TENANCY_NAME},${REGION_NAME},${SERVICE_NAME2},${AD_NAME2},${LIMIT_NAME2},${LIMIT_VALUE2},/" >> ${LIMIT_USAGE_CSV} fi; done; } # Parameter Check if [ -z "$1" -o -z "$2" -o -z "$3" ] ; then echo "Usage: ./OCILimitsAndUsage.sh \$1 \$2 \$3"; exit 1; fi; # Initialize export OCI_CLI_RC_FILE=$1 export COMPARTMENT_OCID=$2 export TENANCY_NAME=$3 export REGION_NAME_LIST="${TENANCY_NAME}_region_name_list.csv" export SERVICE_NAME_LIST="${TENANCY_NAME}_servie_name_list.csv" export LIMIT_NAME_LIST="${TENANCY_NAME}_limit_name_list.csv" export LIMIT_NAME_LIST_TMP="${TENANCY_NAME}_limit_name_list_tmp.tmp" export LIMIT_USAGE_TMP="${TENANCY_NAME}_limit_usage.tmp" : > ${REGION_NAME_LIST} : > ${SERVICE_NAME_LIST} : > ${LIMIT_NAME_LIST} : > ${LIMIT_NAME_LIST_TMP} : > ${LIMIT_USAGE_TMP} # CSV file initialize export DATETIME2=`date +"%Y%m%d_%H%M%S"` export LIMIT_USAGE_CSV="${DATETIME2}_${TENANCY_NAME}_limit_usage.csv" echo "DATE,TIME,TENANCY_NAME,REGION_NAME,SERVICE_NAME,AVAILABILITY_DOMAIN,LIMIT_NAME,LIMITS,AVAILABLE,USED" > ${LIMIT_USAGE_CSV} # Get Region Name List GetRegionNameList # Get Limit and Usage in the All-Region cat ${REGION_NAME_LIST} | while read -r REGION_NAME do # Initialize : > ${SERVICE_NAME_LIST} : > ${LIMIT_NAME_LIST} : > ${LIMIT_NAME_LIST_TMP} : > ${LIMIT_USAGE_TMP} # Get Service Name List GetServiceNameList ${REGION_NAME} # Get Limit Name List GetLimitNameList ${REGION_NAME} # Get Limits and Usage GetLimitsAndUsage ${REGION_NAME} done; # Finalize rm ${REGION_NAME_LIST} rm ${SERVICE_NAME_LIST} rm ${LIMIT_NAME_LIST} rm ${LIMIT_NAME_LIST_TMP} rm ${LIMIT_USAGE_TMP} exit 0;
前提条件を整えてシェルを以下のように実行します。
./OCILimitsAndUsage.sh /home/opc/.oci/config ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxx ayutenant
以下のような CSVファイルが出力されます。
cat 20210724_055626_ayutenant_limit_usage.csv DATE,TIME,TENANCY_NAME,REGION_NAME,SERVICE_NAME,AVAILABILITY_DOMAIN,LIMIT_NAME,LIMITS,AVAILABLE,USED : 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-1,standard-e3-core-ad-count,600,600,0 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-2,standard-e3-core-ad-count,600,600,0 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-3,standard-e3-core-ad-count,600,600,0 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-1,standard-e3-core-count-reservable,0,0,0 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-2,standard-e3-core-count-reservable,0,0,0 20210724,055749,ayutenant,us-ashburn-1,compute,xxxx:US-ASHBURN-AD-3,standard-e3-core-count-reservable,0,0,0 :
GitHub にもうpしときました。彡(゚)(゚)
https://github.com/gonsuke777/OCI/tree/master/OCILimitsAndUsage
7. まとめ
事前に想像していたのの10倍位は面倒でしたやね。彡(゚)(゚)
スクリプトもダーティな作りなんですが、JSON を JSON のまま扱える言語で書いた方がスマートなんですかね。
JSON ⇒ JavaScript で扱うためのデータ構造なので、Node.js とかがエエんかしら……?
8. 参考ドキュメント
(OCI CLI Command Reference) Docs >> limits >> definition >> list
https://docs.oracle.com/en-us/iaas/tools/oci-cli/2.26.3/oci_cli_docs/cmdref/limits/definition/list.html(OCI CLI Command Reference) Docs >> limits >> value >> list
https://docs.oracle.com/en-us/iaas/tools/oci-cli/2.26.3/oci_cli_docs/cmdref/limits/value/list.html(OCI CLI Command Reference) Docs >> limits >> resource-availability >> get
https://docs.oracle.com/en-us/iaas/tools/oci-cli/2.26.3/oci_cli_docs/cmdref/limits/resource-availability/get.html
シンプルな OCI Functions を API Gateway でシンプルに呼び出して結果をブラウザに表示する。(Oracle Cloud Infrastructure)
OCI Functions は OCI のサーバーレスな実行環境や!彡(^)(^)
Java や Python など様々な言語をサーバレスで実行できます。
Oracle Cloud - Cloud Native 2021
https://speakerdeck.com/oracle4engineer/oracle-cloud-hangout-cafe-premium-oracle-cloud-cloud-native-2021?slide=34Serverless な世界をのぞいてみよう!
https://speakerdeck.com/oracle4engineer/lets-dive-serverless-world
今回はシンプルな OCI Functions を作成して、API Gateway でシンプルに
呼び出して結果をブラウザ表示してみますやで彡(゚)(゚)
1. ネットワーク構成
ネットワーク構成は下記記事のとほぼ同じです。
検証によく使う Oracle Cloud Infrastructure(OCI)環境を Terraform で作成してみる。
https://qiita.com/ora_gonsuke777/items/8651c4075ec6bf436c4f
ポートは 22番(SSH), 443(https), 80(http) あたりを開けておきます。22(SSH) は今回使いませんが。
2. OCI Functions の作成
下記記事の Getting Started で使用可能になる Java のコードをほんの少し改修しました。
Functionsそのもの は Private Subnet にデプロイしています。
Oracle Functions の Getting Started を試してみる。(Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/a9bb52faadcb9f2af38e
package com.example.fn; public class HelloFunction { public String handleRequest(String input) { System.out.println("Inside Java Hello World function"); return "{ \"key1\" : \"Hello world!\"}"; } }
package com.example.fn; import com.fnproject.fn.testing.*; import org.junit.*; import static org.junit.Assert.*; public class HelloFunctionTest { @Rule public final FnTestingRule testing = FnTestingRule.createDefault(); @Test public void shouldReturnGreeting() { testing.givenEvent().enqueue(); testing.thenRun(HelloFunction.class, "handleRequest"); FnResult result = testing.getOnlyResult(); assertEquals("{ \"key1\" : \"Hello world!\"}", result.getBodyAsString()); } }
生実行(fn invoke ~)の結果は下記のとおりです。
$ fn invoke ayu-functions1 hello-java { "key1" : "Hello world!"}
3. API Gateway の作成
API Gateway を作成します。特別な事は無く粛々と……。
API Gateway を作成するネットワークは Public の Subnet を指定します。
4. API Gateway の Deployment を作成
API Gateway の Deployment を作成します。シンプルなコール方法なので、こちらも特別なことはなく粛々と……。
METHODS は GET のみを指定
5. 実行権限の付与(リソース・プリンシパル)
API Gateway に Functions の実行権限を付与してやります。
ネットワークおよびAPIゲートウェイ関連リソースへのアクセスを制御するポリシーの作成
https://docs.oracle.com/ja-jp/iaas/Content/APIGateway/Tasks/apigatewaycreatingpolicies.htm
下記のような IAM Policy を作成して、API Gateway に Functions の実行権限を付与します。
ALLOW any-user to use functions-family in compartment <コンパートメント名> where ALL { request.principal.type= 'ApiGateway', request.resource.compartment.id = '<コンパートメントの OCID>' }
OCI の権限(Policy)は通常は Group を介して IAMユーザー に
付与されますが、権限を OCI のリソースに対しても付与することが可能で、
リソースに対して権限を付与するのがリソースプリンシパルです。(あってる?)
6. API Gateway の endpoint をブラウザでアクセス
まず API Gateway の endpoint を確認します。Deployment の下記部分です。
endpoint に 4. で作成した Deployment の PATH を追記してブラウザでアクセスします。
成功や!ブラウザに Functions の結果が表示されたやで彡(^)(^)
7. まとめ
API Gateway と Functions の組み合わせでブラウザに文字列を返すことが出来ました。
これは従来型の Web/AP + DB(データストア) の三層構造のアプリケーションを
サーバーレスなサービスで置き換えられる事を示しています。
AWS の Lambda相当の事を、OCI でも出来るんやで彡(^)(^)
Always Free も充実してきた OCI をどんどん活用して下さいね。
8. 参考ドキュメント
文中のリンクのほか、以下のドキュメントを参照しました。
サーバーレスOracle Functionsを呼び出すための完全ガイド
https://blogs.oracle.com/otnjp/the-complete-guide-to-invoking-serverless-oracle-functions-ja
APIゲートウェイの概要
https://docs.oracle.com/ja-jp/iaas/Content/APIGateway/Concepts/apigatewayoverview.htm
Functionsの概要
https://docs.oracle.com/ja-jp/iaas/Content/Functions/Concepts/functionsoverview.htm
SQL の再帰WITH句で nPr(n = r)の1桁数値順列を生成してみる。(Oracle Database)
1. やりたい事
表題の通り、再帰WITH句で下記のような数値の順列(重複無し)を生成してみます。
1 ... 1 1, 2 ... 12, 21 1, 2, 3 ... 123, 132, 213, 231, 312, 321 :
nPr の公式で n = r の順列となります。使用するのは Oracle Database で
ちょっとした検証だったので Live SQL のサイトでガチャガチャと検証彡(゚)(゚)
2. 順列を生成するSQL
以下のような SQL で対象の順列を生成できました。数値1桁まで対応します。
WITH nums AS ( SELECT LEVEL AS C0 FROM DUAL CONNECT BY LEVEL <= 3 ), cte(c1, c2) AS ( SELECT nums.c0, TO_CHAR(nums.c0) FROM nums UNION ALL SELECT c1 , c2 || nums.c0 FROM nums, cte WHERE LENGTH(c2) < 3 AND cte.c2 NOT LIKE '%' || TO_CHAR(nums.c0) || '%' ) SELECT * FROM cte WHERE LENGTH(c2) = 3 ORDER BY c2;
結果は以下の通り。n = r の順列の場合は n!(階乗) で要素数を表現できます。3 の場合は 321 で 6個
1,2,3,4,5 の順列では下記のようになります。要素数は 5!=54321 で 120個ですね。
3. SQLの解説
以下のような考え方で SQL を組んでいます。
・初めのWITH句(nums仮表)で順列の生成に必要な数値(1, 2, 3, ...)を生成しています。
・再帰WITH句の開始条件(開始レコード)はnums仮表の各要素
・再帰WITH句の継続条件は 長さが求める順列の長さより小さい場合 かつ nums仮表の要素を順列(c2列)に含まない場合
・そのままだと短い順列も取得されてしまうので、順列の文字列長で絞り込んでいます。
上記の通り再帰WITH句の継続条件の判定がショボいので、1桁の数値までしか対応しません。
4. 参考サイト
下記のサイトを参考にさせて頂きました。ありがとうございます!彡(^)(^)
SQL だけで再帰的に順列/組み合わせを列挙する
https://zenn.dev/indigo13love/articles/b04f8f2973fee3
上記サイトのように配列型を定義してゴニョゴニョすれば、1桁制限も突破できるとは思われる。
彡(゚)(゚)