前陣子我的 AWS 試用期結束了,原本想要把上面的服務搬回家裡自己開,又覺得放在家裡吃電也不能保證能 24 小時運行,還是放在公共雲比較好。
雖然說上面都是一些不太重要的小東西,但還是想要找個免費的服務繼續蹭下去。
後來想起 Oracle 有提供真正的 Always free 方案,一個帳號最多可以免費開到最多 4 個 VM。經過我實測下來發現是真的可以,這邊留下一點紀錄。
Oracle Cloud 免費方案內容
30 天免費試用 (Free Trial)
和其他 cloud service 一樣,OCI 也有提供限期的免費 quota,可以在限定時間內體驗 Oracle Cloud 的各種功能,OCI 這邊是給到 300 美元,可以使用這些點數來試用各種付費服務,例如更強大的虛擬機 (VM)、數據庫、分析工具、Kubernetes 引擎 (OKE) 等。這個 quota 一樣有期限,需要在 30 天裡面把它花完。
一般來說我都把這個 quota 當作新手保護期,讓你在開免費服務的時候如果不小心開到要付費的,可以在一個月裡面把它 revert 掉。
我最怕的就是為了把額度消耗完跑去開到昂貴的服務,結果時間過以後跟你收一堆費用。所以還是穩健為上,開要用的就好。
永久免費服務 (Always Free)
這是 Oracle Cloud 最大的亮點。即使 30 天試用期結束,仍然可以無限期免費使用部分的服務,這對個人專案、學習和小程式非常有用。
因為我們的重點是開 VM,這裡以比較會用到的服務來舉例 (完整 Always Free 列表可以看這裡)
- VM (最吸引人的部分,尤其是 ARM VM)
- ARM 主機 (Ampere A1)
- 總共 4 個 OCPU (等同於 4 個 vCore) 和 24 GB 的記憶體
- 可以自由分配這些資源,例如可以開 1 台 4 OCPU, 24 GB 記憶體的 VM,或是 4 台 1 OCPU, 6 GB 記憶體
- x86 主機 (AMD):
- 數量:2 台 VM。
- 規格:每台都是「Micro」等級 (1/8 OCPU, 1 GB 記憶體)。
- ARM 主機 (Ampere A1)
- 儲存空間
- Block Storage:總共 200 GB。需要注意 Boot volume 也算在內,這裡每開一個 VM 都要用到 50 GB 空間。也就是如果你要開滿四台,你就不能在建立 VM 的時候把硬碟調大、或是建立其他 block volume。
- Object Storage (類似 S3 bucket):10 GB 的 Standard Storage + 10 GB 的 Infrequent Access。
- 網路
- 出口流量:每月 10 TB,對於個人專案應該綽綽有餘。Inbound 則沒有限制。
WARNING
要注意的是部分資源只能開在帳號建立時選擇的區域 (Home Region),如果有去另外開別的區域,例如 Home 在 Osaka,跑去開 San Jose 的 block storage,那還是要付錢的!
申請流程
建立帳號
照著自己資料填,這裡就不贅述了。
註冊期間需要提供一張有效的信用卡 (Visa 或 Master) 進行身份驗證,卡片上面的名字必須要跟註冊的名字相同,地址也要打對 (不然會過不了)。Oracle 會收取一筆預授權費用 (記得是 SGD$100,對於驗證來說算挺多的),會在幾天內退還。
Oracle 官方為了防止濫用在身分驗證這塊做的比較嚴謹,有很多原因會造成註冊失敗,在網路上看到原因有 IP 不純、信用卡無法證明身分、資料填錯等等。 我自己就是遇到註冊信用卡的名字跟本人名字不對 (當時銀行自行音譯我的中文名字,翻法跟護照上的不一樣),後來換一張姓名正確的卡就可以。
升級 PAYG 帳號
帳號註冊完成之後,可以去試著開 VM 看看,你會發現除了要付錢的高級 VM 以外,標示 Always Free-eligible 的 VM 基本上都開不起來,會顯示區域的 quota 已經滿的錯誤訊息。
網路上有人寫了個 Cloud Shell 腳本 (如附錄),會重複呼叫開 VM 的指令直到開好為止,但是我個人實測開 30 幾個小時都沒開上,後來我把帳戶 Pay As You Go 方案就能輕鬆開了,連腳本都不需要。
但這麼做的風險也很明顯,如果不小心開錯規格,或是開到不該開的服務,很容易揮收到預期以外的帳單。

並且,和其他邪惡的 cloud service 一樣,OCI 沒辦法設定一個預算上限。只能利用預算警示的功能,在預算超支的時候發個 email 提醒,讓你即時止損。
設定預算警示
這裡來示範一下怎麼添加一個 $1 的預算警示。很重要!我就被這個警示給救過一次。

- 找到 Billing & Cost Management > Budgets
- Create Budge,將 scope 設定在 root compartment (監控帳戶下的所有支出)
- 將 Budget Amount 設為 $1 (最低金額)
- 建立 Alert Rule
- **Threshold Metric :**選擇 Actual Spend
- Threshold Type:選擇 Percentage of Budget
- Threshold % :輸入
100 - Email Recipients :輸入要通知的 Email
按 Create 確認,之後只要帳戶產生了任何超過 $1 的費用,你就會立刻收到 Email 警示。
當然,最根本的預防方法就是永遠只選有 Always Free-eligible 標籤的資源。如果要建立 VM 或 storage 的時候沒看到這個標籤,雖然不見得真的表示它要收費,但還是謹慎為上。
更詳細的 always free 規則可以看 https://docs.oracle.com/en-us/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm
建立 VM
VM 建立方式跟主流的雲服務差不多,就是找到 Compute > Instance 去 Create Instance。
需要記得的是要改 Shape,不然預設都是開要付錢的 E5。

按 Change shape,Shape series 依照需求選 Ampere ARM 或 Specialty x86 主機,認明 Always Free-eligible 標籤
剩下大部分照著 default 走都沒什麼問題,記得下載 ssh key,最後按 create 就可以了。
下面有一個 View estimate cost 的按鈕,裡面雖然有說預估每月 $12,但實測下來,只要有選對 Always free 資源是不會收錢的。
創建完成就可以用 public ip 連進去 server 了。

設定防火牆
但此時如果我們開了一個對外的服務,假設我們開在 port 3000,我們會發現外面都會連不進來。
╰─ curl http://x.x.x.x:3000 ─╯curl: (7) Failed to connect to x.x.x.x port 3000 after 99 ms: No route to hostOCI 的預設入口安全規則非常的嚴格,我們首先到 instance 的 subnet security rule 會發現預設只允許 22 port (SSH) 跟 ICMP 3, 4, 5 而已,表示連 ping (ICMP 0,8) 都會被擋掉。

這裡我們把 3000 加到白名單,會發現還是連不進來。
檢查一下 iptable,我們再次看到了 VM 裡面預設也是擋 ssh 以外的入口流量。
╰─ sudo iptables -L ─╯Chain INPUT (policy ACCEPT)target prot opt source destinationACCEPT all -- anywhere anywhere state RELATED,ESTABLISHEDACCEPT icmp -- anywhere anywhereACCEPT all -- anywhere anywhereACCEPT tcp -- anywhere anywhere state NEW tcp dpt:sshREJECT all -- anywhere anywhere reject-with icmp-host-prohibited我們需要加一條規則讓 3000 能被允許,並且層級要高於 REJECT:
# Oracle Linux or CentOSsudo firewall-cmd --permanent --add-port=3000/tcpsudo firewall-cmd --reload
# generic linux: 手動在 iptable 加一條規則sudo iptables -I INPUT -p tcp --dport 3000 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT這樣就可以從外部連到裡面的服務了。
需要注意的是,因為 iptables 重開機會重置,如果不希望服務重啟後被中斷,要用工具把這些 record 存下來,例如:
# Ubuntusudo apt update ─╯sudo apt install iptables-persistent -ysudo netfilter-persistent save附錄:使用 Cloud Shell 跑腳本開 VM
需要先跑一次建立 VM 流程,並且在最後一步按 Save as stack。
之後打開裡面檔案,把底下腳本需要的欄位填一填
打開 cloud shell (上面功能列的按鈕),把底下腳本貼進去 run 就可以了。 (不過我跑了兩天都沒成功,還是升級 PAYG 比較簡單)
#!/bin/bash
# Define the availability domainsAD1="要建立的區域" # e.g. tabJ:AP-OSAKA-1-AD-1# AD2="..."# AD3="..."
# Array to hold availability domains#AVAILABILITY_DOMAINS=("$AD1" "$AD2" "$AD3") # 如果區域有多個 domain 可以加AVAILABILITY_DOMAINS=("$AD1")
# Loop indefinitely until a command succeedswhile true; do for AVAILABILITY_DOMAIN in "${AVAILABILITY_DOMAINS[@]}"; do echo "Attempting to launch instance in Availability Domain: $AVAILABILITY_DOMAIN" oci compute instance launch \ --availability-domain "$AVAILABILITY_DOMAIN" \ --compartment-id "你的compartment_id" \ --shape "VM.Standard.A1.Flex" \ --subnet-id "subnet的ID ocid1.subnet.oc1..." \ --assign-private-dns-record true \ --assign-public-ip true \ --agent-config '{"is_management_disabled": false, "is_monitoring_disabled": false, "plugins_config": [ {"desired_state": "DISABLED", "name": "Vulnerability Scanning"}, {"desired_state": "DISABLED", "name": "Management Agent"}, {"desired_state": "ENABLED", "name": "Custom Logs Monitoring"}, {"desired_state": "DISABLED", "name": "Compute RDMA GPU Monitoring"}, {"desired_state": "ENABLED", "name": "Compute Instance Monitoring"}, {"desired_state": "DISABLED", "name": "Compute HPC RDMA Auto-Configuration"}, {"desired_state": "DISABLED", "name": "Compute HPC RDMA Authentication"}, {"desired_state": "ENABLED", "name": "Cloud Guard Workload Protection"}, {"desired_state": "DISABLED", "name": "Block Volume Management"}, {"desired_state": "DISABLED", "name": "Bastion"}]}' \ --availability-config '{"recovery_action": "RESTORE_INSTANCE"}' \ --display-name "VM名稱" \ --image-id "OS的ID (source_id),例如ocid1.image.oc1..." \ --instance-options '{"are_legacy_imds_endpoints_disabled": false}' \ --shape-config '{"memory_in_gbs": 24, "ocpus": 4}' \ --ssh-authorized-keys-file ./ssh-key.key.pub
if [ $? -eq 0 ]; then echo "Instance created successfully in $AVAILABILITY_DOMAIN." exit 0 # Exit the script after successful execution else echo "Failed to launch instance in $AVAILABILITY_DOMAIN. Trying the next one..." sleep 30 # Wait 3 seconds before trying the next domain fi donedone