Many microservices applications are primarily configured through environment
variables nowadays. If you’re deploying to Cloud Run with
specifying a lot of environment variables might look rather painful:
--set-env-vars="LOG_LEVEL=verbose,EXPERIMENTS=ShowInactiveUsers,CountIncorrectLoginAttempts,"Test 1",DB_CONN=sqlserver://[email protected]/instance?param1=value¶m2=value,DB_PASS=berglas://my-bucket/path/to/my-secret?destination=tempfile,GODEBUG=schedtrace=9000"
Don’t be scared! There’s a better way to do this. I’ve explained all this in the Cloud Run documentation but this article will have some discussion.
An astute reader might immediately notice that the example above won’t work:
gcloudexpects to split
K=Vpairs using commas, though some values have commas in them
- There are unescaped
"characters inside an argument delimited by
", will cause bash to fail parsing
- If you run this in bash, it will strip off parts like
"..."before passing it to the
So in this article I’ll give you several tips to avoid thinking about this:
- Do not cram all environment variables into a single option.
- Prevent your shell (e.g. bash) from interpreting arguments as much as you can.
- If things are getting out of control, switch to declarative resource model with YAML.
1. Too many environment variables? Split them
When you run the
gcloud run deploy command the second time, you have two way of specifying
- specify all environment variables you need again, with
- specify only the environment variables to update
--update-env-varsand unset the ones you want with
I personally prefer the first option, as I write a
deploy.sh for my apps with
the fully deploy command. So how do you cope with too many environment
Cloud Run actually allows you to specify these
--update-env-vars options multiple times, so you can split the declaration
above into multiple arguments simply as:
$ gcloud run deploy \ --set-env-vars LOG_LEVEL=verbose \ --set-env-vars "EXPERIMENTS=CountIncorrectLoginAttempts,\"Test 1\"" \ --set-env-vars GODEBUG=schedtrace=9000 [...]
That’s better, but is it fool proof? No. As you might see, we started to do ugly things like escaping quotes, and there are more hidden corner cases.
2. Prevent misinterpretation of env vars
If you pass this to
gcloud, it will treat the comma (
,) character as another
environment variable declaration and will try to split the value into multiple
[...] --set-env-var "A=B,C,D"
This is by design, and is explained here.
To prevent splitting with commas, you need to specify a different custom
delimiter that you’re sure won’t occur in your value string, such as
[...] --set-env-vars "^##^A=B,C,D"
Yeah, it’s not the best solution, but it will do the trick. Here are two other examples that might give you trouble while running in the shell:
--set-env-vars A="B C D": this will not preserve the quotes, but ensures the spaces in the value are not split by your shell.
--set-env-vars 'A=B C D': single-quotes are a usually fool-proof way of preserving what’s listed in them (but what if your value actually has a single quote? —escaping those correctly can be a pain!)
--set-env-vars 'A="B C D"': this will preserve the double-quotes in the value.
3. Read environment variables from a file
gcloud run deploy does not support the
--env-vars-file unlike Google Cloud
Functions or Google App Engine deploy commands.
However, you can actually deploy your entire Cloud Run service from a manifest file written in YAML. This works because Cloud Run services are actually Knative API objects as I explained in an earlier article.
This is done with the
gcloud beta run services replace command (hoping this’ll
graduate to beta → GA soon!) In this yaml file, take a look at the
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hello spec: template: spec: containers: - image: gcr.io/google-samples/hello-app:1.0 env: - name: LOG_LEVEL value: verbose # we can break strings over multiple lines # see: https://yaml-multiline.info/ - name: EXPERIMENTS value: 'ShowInactiveUsers, CountIncorrectLoginAttempts, "Test 1"' - name: DB_CONN value: sqlserver://[email protected]/instance?param1=value¶m2=value # [...]
Then you can run this every time you want to update your Service:
gcloud beta run services replace FILE.yaml
Are there caveats with this method? Yes, now we’re bound by the rules of the
YAML syntax. If you have something like
value: 5 or
value: true, these won’t
be treated as
strings, so you need to explicitly quote them:
value: "5" value: "true"
Failing to escape these will result in errors from the API.
Hope this article has helped you specify environment variables more successfully.
(Cross posted to Google Cloud’s Medium publication).