27

I'm experimenting with more cost effective ways to deploy my Rails apps, and went through the Ruby Starter Projects to get a feel for Google Cloud Platform.

It's almost perfect, and certainly competitive on price, but I can't figure out how to automatically delete old deployed version instances after redeploying.

ie: let's say I have one version of each instance running:

Compute Engine -> VM Instances

And then I make a change to my app and redeploy with:

$ gcloud preview app deploy app.yaml worker.yaml --promote

So now I have two versions of each instance deployed (as Google switches between them intelligently, I'd assume):

Compute Engine -> VM Instances have doubled

But now what? Will these instances ever turn off by themselves? The best way I've found of getting rid of them so far is from the tutorial's Deleting the project page:

Deleting app versions

You can find the list of your app versions in the Versions page. To delete the non-default versions, select the check boxes and then click Delete.

appengine/versions

Is everyone on Google App Engine just manually deleting the old deployments of their apps?

Community
  • 1
  • 1
cgenco
  • 2,834
  • 2
  • 28
  • 32
  • 3
    Automatic stopping of non-default VMs has been mentioned on the mailing-lists (or release notes?) as a future-thing. For now, you have to do it manually. – Greg Dec 28 '15 at 21:09
  • 1
    @greg as it seems to evolve quickly, any updates on this one ? – Ben Mar 29 '16 at 20:54

9 Answers9

12

To stop all instances of all non-default versions of all modules (independently of what languages those modules are in), you could add a small control module, written in Python, using the modules API:

from google.appengine.api.modules import modules

# core logic (inside a cron or other handler)
for m in modules.get_modules():
    dv = modules.get_default_version(m)
    for v in modules.get_versions(m):
        if v != dv: modules.stop_version(m, v)

This doesn't delete the non-default versions (the modules API does not appear to currently support deletion), but does ensure that none of their instances are running (so no charges would be incurred for them).

This core logic is meant for you to wrap it inside a handler, so you can trigger it as required, for example in a cron job, or in a "regular" handler that you trigger from the outside (with appropriate auth) e.g. via wget or curl within your bash scripts.

I don't believe there's a Ruby version of Python's google.appengine.api.modules.modules , but, I could be wrong... I just couldn't find one. However, a simple Python-coded module should let you control modules coded in whatever other App Engine language (since App Engine lets you mix and match modules coded in different languages).

Aert
  • 1,869
  • 2
  • 14
  • 17
Alex Martelli
  • 811,175
  • 162
  • 1,198
  • 1,373
  • Oh neat - I can make that work. I should be able to build that into my deploy script. Thank you for the tip! – cgenco Dec 31 '15 at 02:51
  • 3
    This is mind blowing that this is even an issue--it didn't used to be! Is there a valid reason to have instances running for versions that aren't serving traffic? Why google, why? – Robert Sep 25 '19 at 00:04
  • does anyone now what is the pip install command for using modules API? – mmarquezvacas Mar 25 '20 at 17:36
10

We deploy an App Engine instance from Cloud Build, which is a common deployment pattern. At the end of the cloudbuild.yaml, we specify the following commands as the last build step, which deletes every version except the last 5 versions:

versions=$(gcloud app versions list \
  --service default \
  --sort-by '~VERSION.ID' \
  --format 'value(VERSION.ID)' | sed 1,5d)
for version in $versions; do
  gcloud app versions delete "$version" \
    --service default \
    --quiet
done
Nebulastic
  • 7,845
  • 2
  • 31
  • 56
9

The following removes all version except 5 most recent: Uses ghead (on macOS, brew install coreutils), replace with head for linux.

gcloud app versions delete `gcloud app versions list | sed 's/  */:/g' | cut -f 2 -d : | tail -n +2 | ghead -n -5 | tr "\n" " "`
Gabriel
  • 934
  • 9
  • 6
  • 4
    The `gcloud` command supports formatting and filtering of results. To remove all versions older than 2 weeks ``gcloud app versions delete --quiet `gcloud app versions list --format="value(id)[terminator=' ']" --filter="version.createTime – Richard Johnson Jul 14 '20 at 21:12
  • @Richard Johnson is there a way to filter gloud results to select all versions other than the latest 5 as Gabriel does in his answer ? otherwise, this does not really answer the question does it ? – Cyril Duchon-Doris Oct 11 '20 at 13:32
  • Also using this script (or Richar's one) I'm getting the error `ERROR: (gcloud.app.versions.delete) The default service (module) may not be deleted, and must comprise at least one version.` Since I have microservices, I need to select the N oldest versions for each app service, can this be done easily ? – Cyril Duchon-Doris Oct 11 '20 at 13:33
  • Can this be tweaked to not throw error `the default service may not be deleted`? – Evans M. Oct 16 '20 at 14:14
5

Just to throw my solution here after reading all the others provided:

gcloud app versions list --format="value(version.id)" --sort-by="~version.createTime" | tail -n +6 | xargs -r gcloud app versions delete --quiet

Main benefit is that it can still run successfully even if theres 5 or less existing versions.

bkanuka
  • 767
  • 6
  • 16
3

The command below may work:

gcloud app deploy app.yaml worker.yaml --promote --stop-previous-version

see: https://cloud.google.com/sdk/gcloud/reference/app/deploy

I cannot find any documents about the default values of promote and stop-previous-version. As far as I observed, promote is True and stop-previous-version is False though. I wrote both options for safety.

Neal Mummau
  • 373
  • 3
  • 10
Naoyoshi Aikawa
  • 1,075
  • 10
  • 16
  • This does not work. I have tried it and still instances are running for the past versions. –  Jun 09 '20 at 17:39
  • @user10314103 Maybe you use auto-scaling? Then it is ignored. "Note that if the version is running on an instance of an auto-scaled service in the App Engine Standard environment, using --stop-previous-version will not work and the previous version will continue to run because auto-scaled service instances are always running." – Spock Feb 20 '21 at 17:58
2

For those not familiar with xargs syntax, I took the very nice answer from @bkanuka and split it in two lines. It keeps last 5 versions and works even if there is less than 5 versions deployed:

    VERSIONS=$(gcloud app versions list --format="value(version.id)" --sort-by="~version.createTime" | tail -n +6 )
    gcloud app versions delete --quiet $VERSIONS
Mapad
  • 8,064
  • 5
  • 39
  • 40
  • Nice work ... although I had to change the second line to this to get it to work `echo $VERSIONS | xargs gcloud app versions delete --quiet` – Matt Byrne May 04 '22 at 00:55
0

To turn off the instances associated with older versions you could try to use the teardown scripts (eventually customised) mentioned in the delete tutorial resources doc:

If you tried the tutorial about how to run Ruby on Compute Engine, see the Deployment and teardown scripts section of that tutorial

Careful with deleting the app, though, you can't re-use the app id!

Since the older versions are no longer the default ones at least no new instances will be automatically starting for them. You may still need to delete them manually, unless Google automatically deletes the older automatically assigned versions exceeding the max number of versions allowed for an app.

Dan Cornilescu
  • 38,757
  • 12
  • 59
  • 95
0

We created a little Python script to delete old versions automagically. We run it before every deploy so that our deploys always succeed (and never fail due to having too many versions deployed already).

https://pypi.org/project/appengine-clean/

dgrant
  • 1,397
  • 2
  • 15
  • 23
0

Using zx, you can write it with relatively clean code.

#!/usr/bin/env zx

// firstly, deploy
await $`gcloud app deploy --quiet --project=XXX`

// secondly, delete versions except the one deployed just now
let versions = await $`gcloud app versions list --project=XXX | sed 's/  */:/g' | cut -f 2 -d : | tail -n +2 | ghead -n -1`
console.log("---")

versions = versions.toString().split("\n") // makes array
versions.pop() // removes last blank element created by new line
console.log(versions)

await Promise.all(versions.map(version =>
    $`gcloud app versions delete ${version} --project=XXX`
))
console.log("done!")