返回列表

GCP代理帳號開戶 GCP 域名動態 DNS 解析配置腳本教學

谷歌雲GCP / 2026-07-22 13:56:15

第一章:先把問題想清楚

動態 DNS 的本質很簡單:你的服務端(例如家庭網路、雲外機房的某台主機)外網 IP 會變,但你希望用固定的域名連到它。於是我們寫一個腳本,定期抓取目前的外網 IP,然後透過 DNS 提供商的 API,把域名的解析記錄更新成新 IP。

在 GCP(Google Cloud Platform)上做這件事,常見前提有三個:

  1. 你的網域 DNS 是由 Cloud DNS 管理(不是只有註冊商頁面那種簡易管理)。
  2. 你能建立服務帳戶並拿到足夠權限,讓腳本可以改動 DNS 記錄。
  3. 你要明確知道要更新哪一條記錄(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 更新腳本,通常包含以下流程:

  1. 讀取目前外網 IP(例如呼叫一個回傳你公網 IP 的服務)。
  2. 取得 DNS 現有解析結果(查 Cloud DNS 該 name 的 A 記錄目前有哪些 IP)。
  3. 判斷是否需要更新:如果新 IP 與目前一致,就不用呼叫更新。
  4. 發送更新請求:把 record set 更新成新 IP。
  5. 記錄日志:至少要能看出更新成功或失敗原因。

為什麼要先查現有 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 更新後如何確認

你至少要做三步驗證:

  1. 腳本輸出的日志:應該能看到 public IP、current A record、以及是否觸發更新。
  2. 在 Cloud 控制台查看 DNS:確認 A 記錄 rrdata 已改成新 IP,TTL 是否正確。
  3. 用解析查詢工具驗證:例如在你的電腦上使用 dignslookup 查看最新解析結果。注意可能需要等 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)把腳本改成完全貼合你專案的版本。

Telegram售前客服
客服ID
@cloudcup
联系
Telegram售后客服
客服ID
@yanhuacloud
联系