0

I have many VMs in Google Cloud that are required seldomly for debugging. Running them all the time would cost quite a lot, so they're shutdown most of the time. The problem is that Ephemeral IP addresses change if machine is rebooted and DNS records will mismatch. I know one can use static IP addresses, but IP Static IP address (assigned but unused) are quite expensive, source https://cloud.google.com/vpc/network-pricing

What would be a good way to point external DNS record to a VM which is restarted seldomly without using static external IP addresses?

I've got a domain in Google Cloud DNS and also external DNS domains in other providers.

2 Answers 2

1

hope you are well. To add a little bit to the previous answer from @Sergiusz, here is what I did.

  • Configured a zone in Cloud DNS (outside of the scope of the question)

  • Created a service account with DNS Administrator role/permissions assigned (can be tightened a bit), then downloaded the json key for the service account to /root/sa-file.json on the VM

  • Updated the VM's metadata with custom values:
    dns_zone_name (to store the name of the zone, fe gcp-jd-zone)
    dns_domain (to store the fqdn, fe gcp.jabbsondude.com)
    dns_record_ttl (to store ttl for created A record, fe 600)
    key_path (to store the path to the file with Service Account Key, fe /root/sa-file.json)

  • Updated the VM's metadata with a startup-script:

#!/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

Here is how it looks in the VM instance details: img

  • Rebooted the VM

The output from the logs (/var/log/daemon.log in case of Debian):

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

The script works for creating and updating A records for each VM started with above script. It can probably be improved with couple more checks, this is just something I came up with out of curiosity in the last hour.

Let me know if you need any help with that or if something isn't working.

1
  • Thanks! I tested with a new Debian VM and at least initial DNS record creation worked. If somebody wonders how to add startup-script to an existing VM, use Custom metadata key startup-script, source: cloud.google.com/compute/docs/…
    – Eastman
    Commented Mar 15, 2021 at 14:26
3

If you rarely use those virtual machines it might be better to simply check current external IP with command: gcloud compute addresses list --filter="VM-NAME"

Anyway, here's my (ugly) solution:

  1. Create public DNS zone and add A record pointing to VM's current external IP.
  2. On registrar's page add NS record redirecting your domain to Google DNS, it will be one of following (they will be listed in your zone):
ns-cloud-a1.googledomains.com.
ns-cloud-b1.googledomains.com.
ns-cloud-c1.googledomains.com.
  1. Add startup script that will check the VM's ephemeral public IP and update public DNS zone:
#!/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

You can find more info on DNS records here.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .