GCP代理帳號開戶 GCP 域名動態 DNS 解析配置腳本教學
第一章:先把問題想清楚
動態 DNS 的本質很簡單:你的服務端(例如家庭網路、雲外機房的某台主機)外網 IP 會變,但你希望用固定的域名連到它。於是我們寫一個腳本,定期抓取目前的外網 IP,然後透過 DNS 提供商的 API,把域名的解析記錄更新成新 IP。
在 GCP(Google Cloud Platform)上做這件事,常見前提有三個:
- 你的網域 DNS 是由 Cloud DNS 管理(不是只有註冊商頁面那種簡易管理)。
- 你能建立服務帳戶並拿到足夠權限,讓腳本可以改動 DNS 記錄。
- 你要明確知道要更新哪一條記錄(A 記錄?AAAA 記錄?是否還有 CNAME?)。
另外,我建議你先回答兩個實務問題,會直接影響腳本怎麼寫:
- 你的網路端是否可能同時有 IPv4 與 IPv6 變動?如果只用 IPv4,就把範圍鎖死,流程更穩。
- 更新頻率要多高?太頻繁會增加 API 風險與失敗機率;太稀疏又可能導致切換期間域名指向舊 IP。實務上常見是 5~30 分鐘視環境。
第二章:準備工作與前置條件
2.1 確認 Cloud DNS 目標
打開 Cloud 控制台,確認你要用的 Managed Zone(管理區)。你需要知道三個關鍵資訊:
- Project ID
- Managed Zone 名稱(或至少知道它對應的 zone 名)
- 要更新的 FQDN(例如
home.example.com.;注意 Cloud DNS 通常會以尾端的點號呈現 FQDN)
接著確認記錄類型。最常見的是:
- A 記錄:IPv4 對應 IPv4 位址
- AAAA 記錄:IPv6 對應 IPv6 位址
如果你同時要支援兩種,就需要兩段邏輯或一個同時更新兩種記錄的腳本;否則只做 A 記錄會更清晰。
2.2 建立服務帳戶並配置權限
腳本更新 DNS 記錄需要權限。常見做法是建立一個獨立服務帳戶,避免用高權限帳號混用。
你需要賦予以下權限(以最常見的模式來說):
- Cloud DNS 管理員或更精準的 DNS 編輯權限
在許多專案中,最簡單的方式是把權限給到該 Managed Zone 所在的範圍(依你的組織政策可能需要調整)。如果你只是學習或內部測試,選擇「能更新記錄」的最小權限會最理想。
建立完成後,你會拿到一份憑證(例如 JSON Key)。務必保護它:不要上傳到公用程式碼倉庫,不要放到可被外部讀取的公開位置。
2.3 選定執行環境與排程方式
腳本要跑在「需要更新 DNS 的那台機器」上,或至少能從那台機器取得對外 IP。常見選項:
- 家庭網路的一台小主機(例如路由器後端、NUC、樹莓派)
- 雲端 VM(但你要確保抓到的是你目標服務端的外網 IP,不是 VM 自己的 IP)
GCP代理帳號開戶 若你的目標是把「家庭寬帶」映射到域名,那腳本就應該在家庭網路那端跑,抓取外網 IP 才準確。
排程可用:
- Linux:
cron或 systemd timer - Windows:Task Scheduler
第一次先手動跑,確認更新成功,再把它交給排程。
第三章:DNS 記錄的設計策略
3.1 A 記錄能不能直接更新?
可以。Cloud DNS 的做法通常是針對特定 record set 更新其 IP 清單。你需要注意兩件事:
- TTL:更新後 TTL 仍會影響解析延遲。若你設 TTL 太長,切換到新 IP 可能慢;太短會造成頻繁解析壓力。常見在 60~300 秒之間。
- 記錄唯一性:同一個 name + type + ttl 組合通常會受到記錄集合規則影響。腳本最好以 type + name 為核心找到正確 record set。
GCP代理帳號開戶 3.2 CNAME vs A 記錄
很多人想用 CNAME 指向一個「會自動更新的中繼」。但在動態 DNS 的場景裡,你通常還是要最終把 A/AAAA 指向真實 IP,因為中繼服務未必存在或成本更高。
所以這篇文章採用最直接的方式:更新 A 記錄(IPv4)。如果你要支援 IPv6,照同樣模式加上 AAAA 即可。
第四章:腳本的核心流程(先講清楚再給你程式)
一個穩健的動態 DNS 更新腳本,通常包含以下流程:
- 讀取目前外網 IP(例如呼叫一個回傳你公網 IP 的服務)。
- 取得 DNS 現有解析結果(查 Cloud DNS 該 name 的 A 記錄目前有哪些 IP)。
- 判斷是否需要更新:如果新 IP 與目前一致,就不用呼叫更新。
- 發送更新請求:把 record set 更新成新 IP。
- 記錄日志:至少要能看出更新成功或失敗原因。
為什麼要先查現有 IP?因為 DNS 更新不是免費的。你可以把更新頻率設得較高,但只要 IP 沒變,就不必觸發更新,系統會更穩。
第五章:GCP 權限與 API 呼叫方式
5.1 用服務帳戶做授權
腳本呼叫 Cloud DNS API 時,需要 OAuth token。最常見做法是:
- GCP代理帳號開戶 用服務帳戶 JSON Key
- 用憑證建立授權
- 呼叫 Cloud DNS 的查詢與更新端點
你可以用官方提供的 client library(例如 Python 的 cloud dns 用戶端)。相比自己手寫 REST + token,穩定性會高一些。
5.2 更新記錄時要小心的點
Cloud DNS 的更新通常需要你提供:
- GCP代理帳號開戶 Managed Zone(zone name 或 id)
- record set 的 name / type
- ttl
- rrdata(也就是 IP 列表)
另外,若你曾經手動建立過多個 A 記錄到同一個 name/type,腳本必須明確採取策略:要替換為單一 IP,還是維持多值?本篇教學以最常見需求為主:維持單一 A 記錄值,也就是 rrdata 只放一個 IP。
第六章:Python 範例腳本(IPv4 動態 DNS 更新)
以下以 Python 示範,流程清楚且容易改。你只需要把你自己的資訊填進去。
6.1 安裝依賴
在你的執行環境安裝所需套件(版本依你環境調整):
pip install google-cloud-dns google-auth requests
6.2 參數設定
假設你有:
- 服務帳戶 JSON Key 路徑:
/path/to/key.json - Project ID:
my-project - Managed Zone 名稱:
my-zone - 要更新的主機名:
home.example.com. - TTL:例如 120
- 外網 IP 檢測 URL:使用回傳你公網 IP 的服務
GCP代理帳號開戶 6.3 腳本內容
import json
import requests
from google.cloud import dns
# ========== 你需要修改的設定 ==========
PROJECT_ID = 'my-project'
ZONE_NAME = 'my-zone'
FQDN = 'home.example.com.' # 注意末尾的點號
TTL = 120
SERVICE_ACCOUNT_FILE = '/path/to/key.json'
# 取得公網 IP(可換成你信任的服務)
IP_CHECK_URL = 'https://api.ipify.org?format=json'
# =======================================
def get_public_ip():
r = requests.get(IP_CHECK_URL, timeout=15)
r.raise_for_status()
data = r.json()
return data['ip'].strip()
def get_current_a_record(client, zone_name, fqdn):
# 列出指定 name/type 的 A 記錄
zone = client.zone(zone_name)
record_sets = zone.list_resource_record_sets()
# list_resource_record_sets 可能很大,這裡用簡單比對;實務可加更精準的篩選策略
for rrset in record_sets:
if rrset.name == fqdn and rrset.type == 'A':
return rrset.rrdatas
return []
def update_a_record(client, zone_name, fqdn, ttl, new_ip):
zone = client.zone(zone_name)
# 先找目前設定,確保我們以一致方式替換
existing_ips = get_current_a_record(client, zone_name, fqdn)
# 如果 DNS 已經是同一個 IP,就不更新
if existing_ips and existing_ips == [new_ip]:
return False
# 準備變更:通常做法是先刪除舊 rrset,再新增新 rrset
# 注意:如果存在多個值,需要你決定策略。這裡採「只保留單一值」
changes = []
if existing_ips:
changes.append(dns.changes.Change(
'DELETE',
dns.ResourceRecordSets(
name=fqdn,
type_='A',
ttl=ttl,
rrdatas=existing_ips
)
))
changes.append(dns.changes.Change(
'ADD',
dns.ResourceRecordSets(
name=fqdn,
type_='A',
ttl=ttl,
rrdatas=[new_ip]
)
))
# 提交變更
client.zone(zone_name).changes().create(
project=PROJECT_ID,
managedZone=zone_name,
body={
'additions': [
{
'name': fqdn,
'type': 'A',
'ttl': ttl,
'rrdatas': [new_ip]
}
],
'deletions': [
{
'name': fqdn,
'type': 'A',
'ttl': ttl,
'rrdatas': existing_ips
}
] if existing_ips else []
}
).execute()
return True
def main():
# 建立 client(使用服務帳戶授權)
import os
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = SERVICE_ACCOUNT_FILE
client = dns.Client(project=PROJECT_ID)
public_ip = get_public_ip()
print(f'Public IP: {public_ip}')
current_ips = get_current_a_record(client, ZONE_NAME, FQDN)
print(f'Current A record: {current_ips}')
updated = update_a_record(client, ZONE_NAME, FQDN, TTL, public_ip)
if updated:
print('DNS updated successfully.')
else:
print('DNS already up to date; no change needed.')
if __name__ == '__main__':
main()
上面提供的是「教學式」範例:重點是流程與易懂性。你在實際環境可能會遇到兩類差異:
- client library 的命名或方法略有不同(不同版本 cloud dns wrapper 介面會有差)。若你用的是官方方式,照版本調整即可。
- 刪除舊 rrset 時的 ttl 必須匹配。Cloud DNS 的刪除通常要求你提供和現有 rrset 完整一致的條件。若你的現有 A 記錄 TTL 和腳本設定不一致,就可能刪除失敗。解法是「查到現有 rrset 的 ttl 再刪除」,而不是用固定 TTL 刪除。
因此,進入實戰我建議你把「刪除」的 ttl 也跟著現有 rrset 取值,這樣成功率會高很多。
6.4 更穩的做法:查到 ttl 再刪除
你可以把 get_current_a_record 回傳目前 rrset 的 ttl 與 rrdata,例如回傳:
- ips
- ttl
更新時 DELETE 用現有 ttl,ADD 用你希望的 ttl。這能避免「ttl 不一致導致刪除不成功」的常見坑。
第七章:排程與故障處理(別只會更新,要會活下去)
7.1 Linux 上的 cron 範例
假設腳本檔名是 ddns_gcp.py,路徑在 /opt/ddns/。
*/10 * * * * /usr/bin/python3 /opt/ddns/ddns_gcp.py >> /var/log/ddns_gcp.log 2>&1
這代表每 10 分鐘執行一次。你可以從 10~30 分鐘開始,再看 IP 變更頻率調整。
7.2 你一定會遇到的失敗原因
我把最常見的狀況整理成檢查清單:
- 權限不足:常見錯誤是服務帳戶沒有 Cloud DNS 記錄更新權限。檢查 IAM 角色。
- 刪除失敗:ttl 或 rrdata 與現有 rrset 不一致。用「查到現有 rrset 的 ttl 再刪除」能顯著改善。
- 記錄不存在:如果你要刪除但該 rrset 沒存在,就會失敗。腳本要先判斷 existing_ips 是否為空。
- 外網 IP 檢測失敗:IP 檢測服務暫時不可用或網路限制。務必使用 timeout,並捕捉例外,避免腳本整段掛掉。
- 更新成功但解析尚未同步:TTL 與快取會影響你立刻看到的結果。這不是腳本壞,而是 DNS propagation。
7.3 建議加入簡單的例外處理與退避機制
如果 API 呼叫失敗,你可以選擇:
- 記錄錯誤後退出(讓下一次排程再試)
- 或採用簡單重試(例如最多重試 3 次,每次間隔 30 秒)
動態 DNS 更新的重點是穩定,不是一次執行就保證成功。
第八章:驗證方法與實際運作檢查
8.1 更新後如何確認
你至少要做三步驗證:
- 腳本輸出的日志:應該能看到 public IP、current A record、以及是否觸發更新。
- 在 Cloud 控制台查看 DNS:確認 A 記錄 rrdata 已改成新 IP,TTL 是否正確。
- 用解析查詢工具驗證:例如在你的電腦上使用
dig或nslookup查看最新解析結果。注意可能需要等 TTL。
8.2 如何測試「IP 不變」的分支
更新腳本最容易犯的錯誤是「每次都更新」,但 DNS 更新其實不需要。你可以先:
- 在 IP 穩定的情況下連續跑兩次腳本
- 確認第二次顯示「no change needed」
如果第二次仍然更新,代表你比對邏輯不夠嚴謹(例如現有 rrset 回傳包含多個值,或解析出的 IP 格式有差異)。
第九章:常見最佳實務(讓它更像產品而不是玩具)
9.1 限制更新次數與寫入策略
即使 IP 可能每幾天才變一次,也建議設定合理的排程頻率,避免 API 被頻繁打到。你可以加一層「上一次更新時間」的本地快取:
- GCP代理帳號開戶 把最後成功更新的 IP 寫入檔案
- 下次如果檢測到相同 IP,就直接跳過
這樣就算你 DNS 查詢偶爾有延遲或錯誤,也不會造成不必要更新。
9.2 TTL 的取捨
TTL 越低,切換時更快;TTL 越低,快取與解析壓力也更大。對於家庭寬帶或一般內網服務,我通常建議:
- 日常服務:TTL 60~120 秒
- 追求快速切換但資源足夠:TTL 30~60 秒
如果你把 TTL 設到 1 秒,等於把所有人都逼著重新查詢,未必划算。
9.3 對 IPv6 的延伸
如果你的環境有 IPv6 且會變動,你可以用同樣邏輯更新 AAAA 記錄:
- 取得 IPv6 公網地址(使用對應服務)
- 查 AAAA 記錄現值
- 需要時刪除舊 AAAA 記錄並新增新值
GCP代理帳號開戶 注意 IPv6 的變動頻率可能與 IPv4 不同,務必先觀察你的網路行為再決定排程。
第十章:把腳本做成可維護的版本
當你從「能用」走到「能長期用」,維護性變得重要。你可以做幾個小改造:
- 把設定放到檔案或環境變數:避免每次改域名都要手動改程式碼。
- 把日志寫到檔案:讓你能回看過去錯誤。
- 把更新邏輯封裝成函式:未來要加 AAAA 或支援多個主機名時很快。
- GCP代理帳號開戶 加入「dry-run」模式:偵錯時先不真正更新,只印出會更新什麼。
例如,你可以讓腳本讀取一個設定列表,包含多個 FQDN,每次執行時依序更新。這能讓你一台主機同時更新多個子網域(如 home.example.com.、vpn.example.com.),但前提是它們對應同一個外網 IP。
結語:你現在可以自己把它跑起來
GCP 的動態 DNS 並不神秘,核心就三件事:抓到目前外網 IP、用服務帳戶取得 Cloud DNS 權限、把 A(或 AAAA)記錄用 API 更新到新值。真正難的是細節:TTL 與刪除條件要一致、權限要到位、失敗時要能快速定位。
你可以從「只更新單一 A 記錄」開始,把流程跑通後,再慢慢擴展到多主機名、支援 IPv6、加入快取與重試。等你看到域名真的跟著 IP 變動而更新,那種穩定感會很直接。
如果你願意,我也可以依你的實際條件(你的 Managed Zone 結構、你要更新的記錄類型、你用的是 IPv4 還是 IPv6、你的執行環境是 Linux 還是 Windows)把腳本改成完全貼合你專案的版本。

