I have a staging and a production project on App Engine, with 6 services on each.
For the moment, we deploy from the developers computer, using
gcloud app deploy app.staging.yaml --project staging-project
or gcloud app deploy app.production.yaml --project production-project
It works but it causes issues with environment variables, and especially with secrets.
Our apps gets their Api keys, database credentials and other things from the environment variables - that allows us to have the exact same app running locally, in a Docker Container or in App Engine without knowing where it is deployed.
If I follow the documentation way to deploy, our app.yaml files would look like this:
app.production.yaml
runtime: nodejs
env: flex
manual_scaling:
instances: 1
env_variables:
DATABASE_PASSWORD: "topsecret"
MY_API_KEY: "ultrasecret"
I think everybody easily understands why it's a bad idea to store that on a Git repository.
For the moment, we have this shadow file that every developer have to fill before deployment
app.production.yaml.shadow
runtime: nodejs
env: flex
manual_scaling:
instances: 1
env_variables:
DATABASE_PASSWORD: "set me"
MY_API_KEY: "set me"
But as the team grows and we want everybody to be able to deploy on staging, it becomes more and more difficult to have the right settings for each developpers and each service.
I found out 3 workarounds, and their reason to not be used:
- Use Google KMS - allows us to put encrypted secrets directly into the project, but it requires us to put custom code in our apps to decrypt them. It creates a different environment management between local, staging and production. It increases the risk of bugs due to the complexity.
- Store secrets in Google Datastore - I tried it, I created a helper that searches env vars in proccess.ENV, then in cache and ultimately in Datastore. But like KMS, it increases complexity a lot.
- Store secrets in a JSON file and put in on Google Cloud Storage : again, it requires to load env variables through an helper that checks env vars, then loads the file etc...
Ultimately, we are exploring the feasibility to use a deployment server that would be triggered by developers or Continuous Integration and handle all the secrets injection when deploying to App Engine. But these tools like Ansible, Salt, Puppet, Chef only have plugins for Compute Engine and do not support App Engine.
+-------------------------+ +-------------------+ +---------------------+
| | | +---> |
| Developer workspace | | Ansible | | App Engine STAGING |
| +----> (or other) | | |
+-------------------------+ | | +---------------------+
| |
+-------------------------+ | | +---------------------+
| +----> Injects secrets | | |
| Continous Integration | | | App Engine PROD. |
| | | +---> |
+-------------------------+ +-------------------+ +---------------------+
This leads me to 3 questions:
- Do you think using a deployment server with App Engine is a good idea ?
- How can I make sure that production and staging secrets keep synchronisation so that deployments from developers are always right ?
- Is there a way to use classical environment variables for secrets on App Engine ?