I recently deployed GNR Comparo. I initially built it with Laravel on Google App Engine because that’s works well for prototypes when you’re unsure if you need a frontend, backend, API or fullstack app. Here’s some notes about my experience deploying this stack.
(Eventually GNRComparo ended up using Lumen instead, but even that proved to be overkill. The app ended up entirely client-side so shoudl be redeployed as a static app without any server frameworks).
I followed these instructions and worked. But to get things up and running there were then some conceptual things I needed to understand, which did not seem to be explained anywhere. Here’s what I worked out.
It was not immediately clear what files I should be ignoring (ie, not deploying). After some experimentation, I settled on:
Google Cloud Build will run Composer for you, so you do not need to send your vendor
folder up to Google App Engine.
However, it will not build your NPM jobs (in Laravel, typically npm run prod
). I decided to run this on my localhost and deploy the built code in the /public
folder. So you do not need to send the node_modules
folder or your JS sources.
And then you can ignore the usual meta files that are also in your .gitignore - eg, on a Mac things like .DS_Store
.
My resulting .gcloudignore
file is here:
In the standard environment, app.yaml
does a lot of the work of Laravel’s .env file, notably holding potentially private or secret environment variables such as APP_KEY
. So it is both private and environment-specific, just like .env
. You should not add it to a git repo. I’ve added a app.yaml.example
file with a blank key, just like Laravel’s .env.example
. The project README can then explain how to edit the data in this file (eg adding the app key). The app.yaml.example
file is copied below.
I am deploying the results of npm run prod
so I had to add some static paths for assets in the public
folder.
The resulting app.yaml.example
holding the above config and these static folder definitions looks like this:
These paths could change depending on your webpack.mix.js
script. Basically, any directory in public
into which you place built assets will need a corresponding static path entry in app.yaml
.
I found the standard Laravel Mix versioning to work just fine with Google App Engine Standard Environment, without any extra config. This is added to webpack.mix.js
:
and this in the relevant Blade file with my layout:
and then Laravel found the mix-manifest.json
and served up the correct links.
Deployment is then as simple as building the production assets and calling deploy:
That said, I found to get a reliable deployment, I needed to replace gcloud app deploy
with gcloud beta app deploy --no-cache
. This ensured a fresh set of caches and recompiled views, etc.
To set up Laravel with Cloud SQL, first create the Cloud SQL instance. Then add the following to the env_variables
of your app.yaml
. Replace myinstance
with your instance name.
This works really well, in that .env
holds your localhost config, and app.yaml
holds your deployed config. You do not need to manage config files for different environments. However, the live database password is stored in plaintext in your project root - more reason not to add app.yaml
to a Git repo.
Then you will need to connect to the remote database to run jobs like php artisan migrate
. I could not find a way to do this as part of an automated deployment in Google Cloud itself, so settled on this workaround to script it.
First, install Google Cloud Proxy and as described on that page create a service account and download a credentials key in JSON. Store this is your project root, but again do not add it to a git repo.
A it stands, if you run php artisan migrate
it will pick up the config vars from .env
and run the migration job on your local DB. To make it work with Cloud SQL, you neet to override these .env
vars to point at the Cloud SQL instance and not the localhost. The following shell script, running in its own shell, will do this as a one-off without you needing to swap between different .env
files.
In the following, replace myinstance
with your instance name, and mypassword
with your password.
The flaw is that your production DB password is stored in plaintext. The solution I thought of would be to store this password in an Ansible vault and build an Ansible deployment system. This would create the app.yaml and the above DB migration script from templates, run gcloud app deploy
and the DB migration script, then tidy up.