[OCI]Always FreeのAutonomous Databaseのみを使用してComputeインスタンスの自動起動/停止をスケジューリングしてみた

# はじめに
PL/SQL SDKとDBMS_SCHEDULERによるジョブの自動実行を組み合わせて、Always FreeのAutonomous Databaseのみを使用してComputeインスタンスの自動起動/停止をスケジューリングしてみました。

こちらの手順では、リソース・プリンシパルを使用していますので、[こちら](https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/adbsa/resource-principal-use.html#GUID-EE5C24AD-34A5-4474-9B6A-9F606AABBA98)のドキュメントを参考にして、あらかじめadminユーザーに対して、Autonomous Databaseインスタンスのリソース・プリンシパルを有効にしておく必要があります。

また、こちらの内容はあくまで検証のため、エラーハンドリング等は行っておりません。
本番環境で行う際はきちんとエラーハンドリング、リトライ処理を実装されることをお勧めします。

# 1.自動起動/停止の対象となるインスタンスの情報を格納するテーブルの作成
データベース・アクションまたはSQL*Plusから、adminユーザとしてAutonomous Databaseにアクセスします。

自動起動/停止の対象となるComputeインスタンスの情報を格納するテーブル"instance_list"を作成します。

```sql:
SQL> CREATE TABLE instance_list
  2  (
  3  	instance_ocid VARCHAR2(128),
  4  	enable_flg NUMBER NOT NULL,
  5  	PRIMARY KEY (instance_ocid)
  6   );

表が作成されました。
```

# 2.自動起動/停止の対象となるインスタンスの情報をテーブルに追加
対象となるComputeインスタンスのOCIDをinstance_listテーブルに追加します。
instance_ocid列に自動起動/停止の対象となるComputeインスタンスのOCIDを、enable_flgに1をセットします。
※enable_flgに0を入れると、レコードを削除せずに自動起動/停止の対象から外せるようにしています。

```sql:
SQL> INSERT INTO instance_list VALUES ('ocid1.instance.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',1);

1行が作成されました。

SQL> INSERT INTO instance_list VALUES ('ocid1.instance.oc1.ap-tokyo-1.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',1);

1行が作成されました。

SQL> INSERT INTO instance_list VALUES ('ocid1.instance.oc1.ap-tokyo-1.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz',1);

1行が作成されました。

SQL> commit;

コミットが完了しました。

SQL> 
```
以上で準備が整いました。

# 3.対象インスタンスを起動/停止するPL/SQLブロックの作成

instance_listテーブルから起動対象となるComputeインスタンスのOCIDのリストを取得し、行数分だけループしてComputeインスタンスを起動するPL/SQLブロックを作成しました。

```sql:
DECLARE
	response_body	dbms_cloud_oci_core_instance_t;
	response		dbms_cloud_oci_cr_compute_instance_action_response_t;

-- instance_listテーブルからenable_flgが1になっているレコードセットを取得するカーソル
	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
-- 処理対象のレコードを保持する変数
	target_ocid target_ocid_cur%ROWTYPE;

BEGIN
-- カーソルのオープン
	OPEN target_ocid_cur;
--ループ処理開始
		LOOP
-- 1行フェッチ
			FETCH target_ocid_cur into target_ocid;
			EXIT WHEN target_ocid_cur%NOTFOUND;
-- 確認のためにテーブルから取得したOCIDを表示
    		DBMS_OUTPUT.PUT_LINE('OCID:'||target_ocid.instance_ocid);
-- 取得したOCIDのインスタンスを起動
			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
				instance_id => target_ocid.instance_ocid,
				action => 'START',
				region => 'ap-tokyo-1',
				credential_name => 'OCI$RESOURCE_PRINCIPAL'
			);
-- ループ処理終了
		END LOOP;
-- カーソルのクローズ
	CLOSE target_ocid_cur;
END;
/
```
コンソールで対象のComputeインスタンスの状態を確認します。
![スクリーンショット 2022-02-13 17.24.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/3a63fee3-027d-445a-f77d-7a5c9a765a8d.png)
対象のComputeインスタンスは停止済の状態です。

Computeインスタンスを起動するPL/SQLブロックを実行してみます。

```sql:
SQL> set serveroutput on
SQL> DECLARE
  2  	response_body	dbms_cloud_oci_core_instance_t;
  3  	response		dbms_cloud_oci_cr_compute_instance_action_response_t;
  4  -- instance_listテーブルからenable_flgが1になっているレコードを取得するカーソル
  5  	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
  6  -- 処理対象のレコードを保持する変数
  7  	target_ocid target_ocid_cur%ROWTYPE;
  8  
  9  BEGIN
 10  -- カーソルのオープン
 11  	OPEN target_ocid_cur;
 12  --ループ処理開始
 13  		LOOP
 14  -- 1行フェッチ
 15  			FETCH target_ocid_cur into target_ocid;
 16  			EXIT WHEN target_ocid_cur%NOTFOUND;
 17  -- 確認のためにテーブルから取得したOCIDを表示
 18      		DBMS_OUTPUT.PUT_LINE('OCID:'||target_ocid.instance_ocid);
 19  -- 取得したOCIDのインスタンスを起動
 20  			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
 21  				instance_id => target_ocid.instance_ocid,
 22  				action => 'START',
 23  				region => 'ap-tokyo-1',
 24  				credential_name => 'OCI$RESOURCE_PRINCIPAL'
 25  			);
 26  -- ループ処理終了
 27  		END LOOP;
 28  -- カーソルのクローズ
 29  	CLOSE target_ocid_cur;
 30  END;
 31  /
OCID:ocid1.instance.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OCID:ocid1.instance.oc1.ap-tokyo-1.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
OCID:ocid1.instance.oc1.ap-tokyo-1.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
PL/SQLプロシージャが正常に完了しました。

SQL> 
```
問題なく実行されました。
コンソールでComputeインスタンスが起動されたかを確認してみます。
![スクリーンショット 2022-02-13 17.31.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/82ac5bf8-3b8d-de67-b023-4e597e85a7a4.png)
対象のインスタンスの状態が実行中となっており、インスタンスが起動されたことが確認できました。

同様に、instance_listテーブルから停止対象となるComputeインスタンスのOCIDのリストを取得し、行数分だけループしてComputeインスタンスを停止するPL/SQLブロックを作成しました。

```sql:
DECLARE
	response_body	dbms_cloud_oci_core_instance_t;
	response		dbms_cloud_oci_cr_compute_instance_action_response_t;

-- instance_listテーブルからenable_flgが1になっているレコードセットを取得するカーソル
	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
-- 処理対象のレコードを保持する変数
	target_ocid target_ocid_cur%ROWTYPE;

BEGIN
-- カーソルのオープン
	OPEN target_ocid_cur;
--ループ処理開始
		LOOP
-- 1行フェッチ
			FETCH target_ocid_cur into target_ocid;
			EXIT WHEN target_ocid_cur%NOTFOUND;
-- 確認のためにテーブルから取得したOCIDを表示
    		DBMS_OUTPUT.PUT_LINE('OCID:'||target_ocid.instance_ocid);
-- 取得したOCIDのインスタンスを停止
			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
				instance_id => target_ocid.instance_ocid,
				action => 'SOFTSTOP',
				region => 'ap-tokyo-1',
				credential_name => 'OCI$RESOURCE_PRINCIPAL'
			);
-- ループ処理終了
		END LOOP;
-- カーソルのクローズ
	CLOSE target_ocid_cur;
END;
/
```
Computeインスタンスを停止するPL/SQLブロックを実行してみます。

```sql:
SQL> DECLARE
  2  	response_body	dbms_cloud_oci_core_instance_t;
  3  	response		dbms_cloud_oci_cr_compute_instance_action_response_t;
  4  
  5  -- instance_listテーブルからenable_flgが1になっているレコードセットを取得するカーソル
  6  	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
  7  -- 処理対象のレコードを保持する変数
  8  	target_ocid target_ocid_cur%ROWTYPE;
  9  
 10  BEGIN
 11  -- カーソルのオープン
 12  	OPEN target_ocid_cur;
 13  --ループ処理開始
 14  		LOOP
 15  -- 1行フェッチ
 16  			FETCH target_ocid_cur into target_ocid;
 17  			EXIT WHEN target_ocid_cur%NOTFOUND;
 18  -- 確認のためにテーブルから取得したOCIDを表示
 19      		DBMS_OUTPUT.PUT_LINE('OCID:'||target_ocid.instance_ocid);
 20  -- 取得したOCIDのインスタンスを停止
 21  			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
 22  				instance_id => target_ocid.instance_ocid,
 23  				action => 'SOFTSTOP',
 24  				region => 'ap-tokyo-1',
 25  				credential_name => 'OCI$RESOURCE_PRINCIPAL'
 26  			);
 27  -- ループ処理終了
 28  		END LOOP;
 29  -- カーソルのクローズ
 30  	CLOSE target_ocid_cur;
 31  END;
 32  /
OCID:ocid1.instance.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OCID:ocid1.instance.oc1.ap-tokyo-1.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
OCID:ocid1.instance.oc1.ap-tokyo-1.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

PL/SQLプロシージャが正常に完了しました。

SQL>
```
問題なく実行されました。
コンソールでComputeインスタンスが停止されたかを確認してみます。
![スクリーンショット 2022-02-13 17.24.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/30dc7394-7bb5-93e2-f7b4-179c81a66a4d.png)
対象のインスタンスが停止されていることが確認できました。

# 4.対象インスタンスを起動/停止するストアド・プロシージャの作成
対象のComputeインスタンスを起動するストアド・プロシージャ "start_compute_instance" を作成します。

```sql:start_compute_instance
SQL> CREATE OR REPLACE PROCEDURE start_compute_instance
  2  IS
  3  	response_body	dbms_cloud_oci_core_instance_t;
  4  	response		dbms_cloud_oci_cr_compute_instance_action_response_t;
  5  	                                                                           
  6  	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
  7  	target_ocid target_ocid_cur%ROWTYPE;
  8  
  9  BEGIN
 10  	OPEN target_ocid_cur;
 11  		LOOP
 12  			FETCH target_ocid_cur into target_ocid;
 13  			EXIT WHEN target_ocid_cur%NOTFOUND;
 14
 15  			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
 16  				instance_id => target_ocid.instance_ocid,
 17  				action => 'START',
 18  				region => 'ap-tokyo-1',
 19  				credential_name => 'OCI$RESOURCE_PRINCIPAL'
 20  			);
 21  		END LOOP;
 22  	CLOSE target_ocid_cur;
 23  END;
 24  /

プロシージャが作成されました。

SQL>
```
ストアド・プロシージャstart_compute_instanceを実行してみます。

```sql
SQL> EXECUTE start_compute_instance;

PL/SQLプロシージャが正常に完了しました。

SQL>
```
問題なく実行されました。
コンソールでComputeインスタンスが起動されたかを確認してみます。
![スクリーンショット 2022-02-13 17.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/df14b413-3ff5-960f-ee72-0cd0dc6fcdb4.png)
対象のインスタンスが起動されていることが確認できました。

同様に、対象のComputeインスタンスを停止するストアド・プロシージャ "stop_compute_instance" を作成します。

```sql:stop_compute_instance
SQL> CREATE OR REPLACE PROCEDURE stop_compute_instance
  2  IS
  3  	response_body	dbms_cloud_oci_core_instance_t;
  4  	response		dbms_cloud_oci_cr_compute_instance_action_response_t;
  5  
  6  	CURSOR target_ocid_cur IS SELECT * FROM instance_list WHERE enable_flg = 1;
  7  	target_ocid target_ocid_cur%ROWTYPE;
  8  
  9  BEGIN
 10  	OPEN target_ocid_cur;
 11  		LOOP
 12  			FETCH target_ocid_cur into target_ocid;
 13  			EXIT WHEN target_ocid_cur%NOTFOUND;
 14  
 15  			response := DBMS_CLOUD_OCI_CR_COMPUTE.instance_action (
 16  				instance_id => target_ocid.instance_ocid,
 17  				action => 'SOFTSTOP',
 18  				region => 'ap-tokyo-1',
 19  				credential_name => 'OCI$RESOURCE_PRINCIPAL'
 20  			);
 21  		END LOOP;
 22  	CLOSE target_ocid_cur;
 23  END;
 24  /

プロシージャが作成されました。

SQL>
```
ストアド・プロシージャstop_compute_instanceを実行してみます。

```sql:
SQL> EXECUTE stop_compute_instance;

PL/SQLプロシージャが正常に完了しました。

SQL>
```
問題なく実行されました。

コンソールでComputeインスタンスが停止されたかを確認してみます。
![スクリーンショット 2022-02-13 17.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784824/ec7a9ced-15d5-aadf-74b1-dd80e8940ef0.png)
対象のインスタンスが停止されていることが確認できました。

# 5.DBMS_SCHEDULERジョブの作成

作成したストアド・プロシージャをDBMS_SCHEDULERのジョブとして登録します。

Computeインスタンスを起動するストアド・プロシージャstart_compute_instanceを毎日日本時間8:00に実行するジョブ"start_compute_instance_job"を作成します。

```sql
SQL> BEGIN
  2    DBMS_SCHEDULER.CREATE_JOB (
  3     job_name           =>  'START_COMPUTE_INSTANCE_JOB',
  4     job_type           =>  'STORED_PROCEDURE',
  5     job_action         =>  'ADMIN.START_COMPUTE_INSTANCE', -- ストアド・プロシージャ名
  6     start_date         =>  TO_TIMESTAMP_TZ('2022-02-13 08:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'), -- 初回実行日時（日本時間2022/2/13 8:00を指定）
  7     repeat_interval    =>  'FREQ=DAILY;INTERVAL=1', -- 実行頻度（毎日実行）
  8     auto_drop          =>  FALSE,
  9     enabled            =>  TRUE
 10     );
 11  END;
 12  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```
同様に、Computeインスタンスを停止するストアド・プロシージャstop_compute_instanceを毎日日本時間18:00に実行するジョブ"stop_compute_instance_job"を作成します。

```sql
SQL> BEGIN
  2    DBMS_SCHEDULER.CREATE_JOB (
  3     job_name           =>  'STOP_COMPUTE_INSTANCE_JOB',
  4     job_type           =>  'STORED_PROCEDURE',
  5     job_action         =>  'ADMIN.STOP_COMPUTE_INSTANCE', -- ストアド・プロシージャ名
  6     start_date         =>  TO_TIMESTAMP_TZ('2022-02-13 18:00:00 +9:00','YYYY-MM-DD HH24:MI:SS TZH:TZM'), -- 初回実行日時（日本時間2022/2/13 18:00を指定）
  7     repeat_interval    =>  'FREQ=DAILY;INTERVAL=1', -- 実行頻度（毎日実行）
  8     auto_drop          =>  FALSE,
  9     enabled            =>  TRUE
 10     );
 11  END;
 12  /

PL/SQLプロシージャが正常に完了しました。

SQL>
```

以上で、Autonomous Databaseを使用してComputeインスタンスの自動起動/停止をスケジューリングすることができました。

# 参考情報
[DBMS_CLOUDでのリソース・プリンシパルの使用](https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/adbsa/resource-principal-use.html#GUID-EE5C24AD-34A5-4474-9B6A-9F606AABBA98)
[DBMS_CLOUD_OCI_CR_COMPUTE.INSTANCE_ACTIONファンクション](https://docs.oracle.com/en-us/iaas/pl-sql-sdk/doc/dbms_cloud_oci_cr_compute.html#ADSDK-GUID-1CFBECB1-9434-4A33-864E-02F54A9302EB)
[DBMS_SCHEDULER.CREATE_JOBプロシージャ](https://docs.oracle.com/cd/F19136_01/arpls/DBMS_SCHEDULER.html#GUID-7E744D62-13F6-40E9-91F0-1569E6C38BBC)