Oracle Cloudの無料枠をもぎ取る方法


結論：https://github.com/hitrov/oci-arm-host-capacity をやってみた記事です．

---

今更ながらOracle CloudのVPSが非常に強力だということを知って，今からでも試すことができるのか検証してみる．

Oracle Cloudの使い方の想定としては次のようなことを考えている．

- 背景
    - 自宅で動かしているサーバが最近調子悪い...
    - そろそろRasPi4も手に入るし新しくサーバ環境を整えるか
    - 友人からVPSの勧誘を受ける
    - どうやらGMOの株主優待などを使うととてもお安く使えるらしい
    - ほかのVPSはどうなのかしら？
- Oracle Cloud
    - GCP, AWSなど普及しているものに比べて微妙にマイナーだが，政府のクラウド調達リストにも入っている
    - 無料枠の特に計算資源が潤沢
    - AMD CPUなら1コア/1GB * 2インスタンスが使える
    - ARM CPUなら4コア/24GBの範囲で1~4インスタンスを柔軟に使える
        - ARMといいつつRasPi4の数倍の性能があるみたい[参照](https://zenn.dev/for/articles/eea2e7ec39cebd)
- セルフホスト勢として
    - メモリたくさん動くならいろいろ楽しめそう！
    - インスタンスもいくつか動かせるので，クラスタ構成で勉強もできるじゃん！

といった感じで建前は自宅サーバも交えてマルチクラウドなクラスタリング構成の勉強のため，本音はセルフホスト環境をより楽しくするためにOracle Cloudを使うことにした．

---

# Oracle Cloud Free Tierアカウントの作成

GUIで完結し調べるといくらでも方法が出てくるので詳細は割愛．

困ったところとしては以下の通り．

1. クレジットカードの引き落としがうまくいったのに(クレカ側で引き落としとキャンセルの通知が来たのに)UI上だと失敗判定となり最初からやり直しとなる
1. 最初からやり直そうと思ったらクレカの登録後のアカウント作成ボタンで無限に待たされる．(DevTool見たら404が出てた?)
1. 試行錯誤をしているとリクエストの上限になったと怒られる

結局は時間を空けてもう一回最初からやり直したらうまくいった．
ネットでもアカウント作成でつまずいている人がたくさんいるみたいなので，ここは根気よく何度かチャレンジしないといけないのかもしれない．
 
# AMDインスタンスの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652864/f5186021-5c9c-f3fe-3c03-fe864738e86a.png)

こちらはとても簡単にできた．
EC2など別のIaaS系のVPSを触ったことがあれば特段難しくもなく利用できる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652864/63a536c9-3806-19d4-a0a0-9186fa482499.png)

`Always Free`と分かりやすくタグが付いている点，後いくつかわかりやすく表示される点が非常に好感触．

# ARMインスタンスの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652864/7e311bd1-d548-a53b-390a-4317019f5946.png)

問題はこちら．

リージョンごとに利用できる枠が決まっているらしく，基本的にその枠の取り合いになっている．
特に東京は枠の取り合いが激しいらしく，なかなか取れないらしい．

今回も試しに作ろうとしてみたら案の定バーナーで怒られてしまった...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652864/629d54f8-d083-cc91-4881-e9f8b1ca9321.png)

## 自動化スクリプトの設定

この問題を解決するにはリージョンを変えて争いの少ない平和な世界へ行くか，自動化スクリプトで常に空きがないか監視する殺伐とした世界へ行くかを決める必要がある．

まずは東京リージョンがどんなもんなのか知りたいってのと，ほかのリージョンに移るためには，申請をしてリージョンの枠を広げてもらわないといけないので，後者の世界線へ行くことにした．

---

今回利用したのはこちら：https://github.com/hitrov/oci-arm-host-capacity
PHPが動く環境であれば問題なく動作するらしい．

今回取ったアプローチの流れは下記の通り．

1. APIキーの発行
1. Docker環境の作成
1. 環境変数の取得・設定
1. 自動化

### APIキーの発行

Consoleの`My Profile > Resources > API Keys`から発行できる．
発行する際に証明書を用意する必要があるのと，コンフィグファイルがコピーできるので，後の作業のために保全しておく．

ここの作業で出てくるデータで，後々必要となるのは下記の3点．

1. `private key`
1. `configファイルの中身`
1. `API key Finger print`(API keysで一覧で表示されているモノ)

### Docker環境の作成

せっかくなので先ほど作成したAMDインスタンスを使って自動実行をしてみる．
PHPは環境を大きく汚すので，何らかの隔離をしたいというのと，インストールなどがだるいのでDockerを利用して実行環境を作成することにした．

こちらではAMDインスタンスにインストールしたところからコマンドをベタ書きしておく．

```bash
# dockerのインストール
# https://docs.docker.com/engine/install/ubuntu/

## リポジトリを設定
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

## docker関係のインストール

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# phpのコンテナの用意
sudo docker pull php:alpine

# API keyなどの用意
# APIkeysはファイルの位置を指定，configは`~/.oci/config`に配置する
mkdir .oci
cd .oci
## コピーした内容をファイルへ書き出し
cat > apikeys.pem
cat > config

# コンテナ内で作業を開始する
# 証明書類はボリュームを渡しておく
sudo docker run -v ~/.oci:/oci -it php:alpine /bin/sh

# === 以下dockerコンテナ内でのコマンド実行 ===

# 必要なパッケージのインストール
apk add git composer php-xml php-tokenizer php-dom php-xmlwriter nano

# スクリプトのダウンロード
git clone https://github.com/hitrov/oci-arm-host-capacity

# 環境変数の設定
cd oci-arm-host-capacity
cp .env.example .env
nano .env
```

### 環境変数の設定

.envファイルに含まれる環境変数は以下の通り．

| 環境変数名 | サンプル値 | コンソール上での入手方法など|
|-----------|----|---|
| OCI_REGION | eu-frankfurt-1 |右上のリージョン選択画面|
| OCI_USER_ID | ocid1.user.oc1..aaaaaaaax72***kd3q |My Profile|
| OCI_TENANCY_ID | ocid1.tenancy.oc1..aaaaaaaakpx***qmpa |Tenancyで検索すると出てくる表|
| OCI_KEY_FINGERPRINT | b3:a5:90:***:b0:8d:1c |My ProfileのAPI Keys|
| OCI_PRIVATE_KEY_FILENAME | "/path/to/oracleidentitycloudservice_***-07-14-10-35.pem" |apikeys.pemのファイルパス(not console)|
| OCI_SUBNET_ID | ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaaahbb***faq |networking>virtual cloud networks> details > subnet　※後述|
| OCI_IMAGE_ID | ocid1.image.oc1.eu-frankfurt-1.aaaaaaaa23z***v6wa |※後述|
| OCI_OCPUS | 4 |割り当てるCPU数|
| OCI_MEMORY_IN_GBS | 24 | 割り当てるメモリ(GB)|
| OCI_SHAPE | VM.Standard.A1.Flex |インスタンスタイプ(AMDも可)|
| OCI_MAX_INSTANCES | 1 |スクリプトの動作上限|
| OCI_SSH_PUBLIC_KEY | "ssh-ed25519 AAAAC3N***o2U user@example.com" |インスタンスにコピーするSSH公開鍵|
| OCI_AVAILABILITY_DOMAIN |  |optional|
| OCI_BOOT_VOLUME_SIZE_IN_GBS |  |optional| 
| OCI_BOOT_VOLUME_ID |  |optional|

#### `OCI_IMAGE_ID`の入手方法

> `OCI_SUBNET_ID`も新しく作成した場合は同様

これ以外の値はコンソールから取得できるが，これだけ特殊な方法で取得しないといけない．
具体的にはコンソール上からインスタンスの作成を試みて，`Out of Capacity`と怒られる瞬間の引数を抜き出す．

DevToolを開きながら作成したいインスタンスの設定を入力して，送信するとほしい情報が含まれたペイロードが見つかる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652864/9f5ac8c8-4469-d802-5c2b-85d957ba427f.png)

OCI_IMAGE_ID:`sourceDetails>imageId`に存在．

# 自動化

ここまででスクリプトの動作ができるようになったはずなので，`php ./index.php`を実行してみる．

```
{
    "code": "InternalError",
    "message": "Out of host capacity."
}
```

これで`Out of host capcacity`と出てくれば動作がうまくいっているので，あとはエンドレスに問い合わせをすればよい．

dockerの操作が完了したので次の要領で自動化を行う．

```bash
# dockerコンテナをimageへ書き起こしておく　
sudo docker commit <container ID> oci-knock:0.0.1
sudo docker images
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# oci-knock    0.0.1     134120124795   4 seconds ago   128MB
# php          alpine    b674211ab0cc   5 days ago      95.1MB

# shellで無限ループで実行させる
sudo docker run -v ~/.oci:/oci -d --name knock oci-knock:0.0.1 sh -c 'export TZ=JST-9; while true; do date;php /root/oci-arm-host-capacity/index.php;sleep 600;done'

# 動作間隔は10分間，あまり短くしすぎると怒られるっぽいので適当に調整しておく．
# export TZ=JST-9
# while true; do
#    date
#    php /root/oci-arm-host-capacity/index.php
#    sleep 600
# done
```

後は`sudo docker logs knock`でログが見えるようになったので確認しておけばよい．

本家はGithub Actionsを使って通知まで自動化している様子?なので，それに比べたら拙いが設定は簡単なはず．

# 確保までの実行回数

記事作成時:`Thu Mar 23 00:00:29 JST 2023`
4コアだとそれだけ一気に確保しないといけないので，難しいと考え1コアの確保を目指して動かしている．

VM1 x 4取得成功@`Fri Mar 24 03:15:43 JST 2023`

10分おきに上記インスタンス生成スクリプトを動かしているので，上の時間から40分間かけて4つのインスタンスが取得できた．
インフラとして一気にインスタンスが増えたのか，それとも実は誰も戦争をしていない世界線で，平和に取得することができたのか...

多少のコストはかかるものの，大きなインスタンス単体があった方がLXCとかで分割もできるし都合がいいので，4つのインスタンスを一つにまとめることができないか検討中．

> 追記予定, 現在放置中