如何在没有静态外部 IP 的情况下将外部 DNS 记录指向 GCP VM 实例?

如何在没有静态外部 IP 的情况下将外部 DNS 记录指向 GCP VM 实例?

我在 Google Cloud 中有很多虚拟机,很少需要用于调试。一直运行它们会花费很多钱,所以它们大部分时间都处于关闭状态。问题是,如果机器重新启动,临时 IP 地址会发生变化,DNS 记录会不匹配。我知道可以使用静态 IP 地址,但是IP 静态 IP 地址(已分配但未使用)相当昂贵,来源https://cloud.google.com/vpc/network-pricing

有什么好方法可以将外部 DNS 记录指向很少重启的虚拟机,而无需使用静态外部 IP 地址?

我在 Google Cloud DNS 中有一个域名,在其他提供商中也有外部 DNS 域名。

答案1

希望你一切都好。为了补充@Sergiusz 之前的回答,我做了以下事情。

  • 在 Cloud DNS 中配置了一个区域(超出了问题范围)

  • 创建一个分配有 DNS 管理员角色/权限的服务帐户(可以稍微收紧一点),然后将服务帐户的 json 密钥下载到虚拟机上的 /root/sa-file.json

  • 使用自定义值更新了虚拟机的元数据:
    dns_zone_name(存储区域的名称,fe gcp-jd-zone)
    dns_域名(存储 fqdn,fe gcp.jabbsondude.com)
    dns_record_ttl(存储创建的 A 记录的 ttl,fe 600)
    密钥路径(存储服务帐户密钥文件的路径,fe /root/sa-file.json)

  • 使用启动脚本更新了虚拟机的元数据:

#!/bin/bash

host=$HOSTNAME

url_project_id="http://metadata.google.internal/computeMetadata/v1/project/project-id"
url_ip_address="http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip"
url_attributes="http://metadata.google.internal/computeMetadata/v1/instance/attributes"

get_metadata() {
  local data=`curl -s -f $url_attributes/$1 -H "Metadata-Flavor: Google"`
  if [ -z "$data" ]; then
    echo "$1 does not exist in metadata. Exiting."
    exit 1
  fi
  echo "$data"
}

auth_gcloud() {
  project=`curl -s $url_project_id -H "Metadata-Flavor: Google"`

  echo "Authenticating gcloud with service account ($key_path), project ($project)."
  gcloud auth activate-service-account --project=$project --key-file=$key_path >/dev/null 2>&1
  if [ $? -eq 0 ]; then
    echo "Authentication succeeded."
  else
    echo "Authentication failed. Exiting."
    exit 2
  fi
}

dns_check_record_exists() {
  echo "Checking if record ($host.$dns_domain) already exists in Cloud DNS"

  dns_data=`gcloud beta dns record-sets list --zone=$dns_zone_name --name=$host.$dns_domain --format=text`

  if [ $? -eq 0 ]; then
    echo "Received answer from Cloud DNS."
  else
    echo "Didn't receive any data from Cloud DNS. Exiting."
    exit 3
  fi

  if [[ -n "$dns_data" ]]; then
    echo "Found existing record."
    return 1 # data exists
  else
    echo "Didn't find existing record."
    return 0 # no data
  fi
}

dns_record_create() {
  echo "Creating new record in Cloud DNS ($host.$dns_domain) in zone ($dns_zone_name) with ip ($ip) and ttl ($dns_record_ttl)"
  gcloud beta dns record-sets create $host.$dns_domain --rrdatas=$ip --type=A --zone=$dns_zone_name --ttl=$dns_record_ttl >/dev/null 2>&1
}

dns_record_update() {
  echo "Updating existing record in Cloud DNS ($host.$dns_domain) in zone ($dns_zone_name) with ip ($ip) and ttl ($dns_record_ttl)"
  gcloud beta dns record-sets update $host.$dns_domain --rrdatas=$ip --type=A --zone=$dns_zone_name --ttl=$dns_record_ttl >/dev/null 2>&1
}

# getting metadata
dns_zone_name="$(get_metadata dns_zone_name)"
dns_domain="$(get_metadata dns_domain)"
dns_record_ttl="$(get_metadata dns_record_ttl)"
key_path="$(get_metadata key_path)"

# getting exterinal ip
ip=`curl -s $url_ip_address -H "Metadata-Flavor: Google"`

# validating external ip
if [[ -n "$ip" && $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
  echo "External ip detected: $ip"
  auth_gcloud
  dns_check_record_exists

  if [ $? -eq 0 ]; then
    dns_record_create
  else
    dns_record_update
  fi
else
  echo "No external ip detected. Exiting."
  exit 5
fi

虚拟机实例详细信息中的内容如下: 图片

  • 重新启动虚拟机

日志的输出(对于 Debian 来说,是 /var/log/daemon.log):

Found startup-script in metadata.
startup-script: External ip detected: 123.213.132.2
startup-script: Authenticating gcloud with service account (/root/sa-file.json), project (jabbsondude-proj).
startup-script: Authentication succeeded.
startup-script: Checking if record (instance-1.gcp.jabbsondude.com) already exists in Cloud DNS
startup-script: Received answer from Cloud DNS.
startup-script: Didn't find existing record.
startup-script: Creating new record in Cloud DNS (instance-1.gcp.jabbsondude.com) in zone (gcp-jd-zone) with ip (123.213.132.2) and ttl (600)
startup-script exit status 0

该脚本用于为使用上述脚本启动的每个虚拟机创建和更新 A 记录。通过更多检查,它可能会得到改进,这只是我在过去一小时出于好奇而想到的。

如果您需要任何帮助或者出现任何问题,请告诉我。

答案2

如果您很少使用这些虚拟机,最好使用命令检查当前的外部 IP:gcloud compute addresses list --filter="VM-NAME"

无论如何,这是我的(丑陋的)解决方案:

  1. 创造公共 DNS 区域并添加指向VM当前外部IP的A记录。
  2. 在注册商页面上添加 NS 记录,将您的域名重定向到 Google DNS,它将是以下之一(它们将在您的区域中列出):
ns-cloud-a1.googledomains.com.
ns-cloud-b1.googledomains.com.
ns-cloud-c1.googledomains.com.
  1. 添加启动脚本这将检查虚拟机的临时公共 IP 并更新公共 DNS 区域:
#!/bin/bash
IP=$(curl -s api.ipify.org) 
gcloud beta dns record-sets delete mydomain.com \
    --type=A \
    --zone=ZONE-NAME
gcloud beta dns record-sets create mydomain.com \
    --rrdatas=$IP \
    --ttl=TTL \
    --type=A \
    --zone=ZONE-NAME

您可以找到有关 DNS 记录的更多信息这里

相关内容