DEV Community

Chris Wendt
Chris Wendt

Posted on • Edited on

Faster builds with VMs than with Google Cloud Build

Google Cloud Build has to pull your Docker image on every build, which can be slow if the image is 1GB+ (e.g. when using cutting edge Wasm tooling that doesn't work on all OSes/architectures yet).

Here's how to speed up your builds using plain VMs instead:

initiate-build.sh creates/starts/resumes the build VM if it's nonexistent/stopped/suspended, copies a Dockerfile and context, then runs build.py on the VM and copies the artifact back to your local machine:

set -xeuo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")"

: "${INSTANCE:=some-build-vm}"

status() {
  gcloud compute instances list --format="value(status)" --filter="name=$INSTANCE" | tr -d '\n'
}

state="$(status)"
case "$state" in
"")
  gcloud compute instances create "$INSTANCE" --machine-type=n2-standard-2 --create-disk=boot=yes,image=projects/ubuntu-os-cloud/global/images/ubuntu-2210-kinetic-amd64-v20221101,size=100
  ;;
"SUSPENDED")
  gcloud compute instances resume "$INSTANCE"
  ;;
"STOPPED")
  gcloud compute instances start "$INSTANCE"
  ;;
"RUNNING") ;;
*)
  echo "Unrecognized instance state $state"
  exit 1
  ;;
esac

rm -rf scratch
mkdir scratch
cp setup.py auto-shutdown.py auto-shutdown.service Dockerfile otherfiles scratch
gcloud compute scp --recurse scratch "$INSTANCE":"~"
gcloud compute ssh "$INSTANCE" -- 'bash -c "( cd scratch && python3 setup.py ); rm -rf scratch"'
gcloud compute scp "$INSTANCE":"~/artifact" artifact
Enter fullscreen mode Exit fullscreen mode

build.py installs Docker if needed, sets up an auto-shutdown after 5mins of inactivity (to mimic the serverless nature of Google Cloud Build), builds the Docker image, then copies the artifact out:

#!/usr/bin/env python3

import subprocess
from subprocess import check_call


def main():
    ensure("jc", "jc")
    ensure("docker", "docker.io")

    check_call("sudo chmod 755 auto-shutdown.py", shell=True)
    check_call("sudo mv auto-shutdown.py /usr/bin", shell=True)
    check_call("sudo mv auto-shutdown.service /etc/systemd/system", shell=True)
    check_call("sudo systemctl daemon-reload", shell=True)
    check_call("sudo systemctl restart auto-shutdown", shell=True)

    check_call("sudo docker build -t img .", shell=True)

    check_call(
        f"sudo docker run --rm --entrypoint cat img /artifact > ~/artifact", shell=True
    )


def ensure(command, pkg):
    try:
        check_call(["which", command])
    except subprocess.CalledProcessError:
        check_call("sudo apt-get update", shell=True)
        check_call(["sudo", "apt-get", "install", "-y", pkg])


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

See auto-shutdown.

Top comments (0)