Composer & Cache Permissions
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeLook back at the Symfony Deployment article: we now have a parameters file! Woo! Next, we need to run composer install
- which was the original reason the site didn't work - and then warm up the Symfony cache. We're really close to a functional site. We won't need to dump the Assetic assets - we're not using Assetic. But we will need to do some asset processing later.
Running composer install
Let's add a new task to run composer
install. In the hook file, add "Install Composer deps". Use the composer
module and tell it to run the install
command. We also need to set the working_dir
: use {{ ansistrano_release_path.stdout }}
:
- name: Set up infrastructure-related parameters | |
// ... lines 3 - 6 | |
- name: Install Composer dependencies | |
composer: | |
command: install | |
working_dir: '{{ ansistrano_release_path.stdout }}' |
Perfect! One gotcha with the composer
module is that, by default, it runs composer install --no-dev
. That means that your require-dev
dependencies in composer.json
will not be downloaded:
{ | |
// ... lines 2 - 30 | |
"require-dev": { | |
"sensio/generator-bundle": "^3.0", | |
"symfony/phpunit-bridge": "^3.0", | |
"doctrine/data-fixtures": "^1.1", | |
"hautelook/alice-bundle": "^1.3" | |
}, | |
// ... lines 38 - 69 | |
} |
For production, that's a good thing: it will give you a small performance boost. Just make sure that you're not relying on anything in those packages!
Also, in Symfony 3, if you use --no-dev
, then some of the post-install
Composer tasks will fail, because they need those dependencies. To fix that, we need to set an environment variable: SYMFONY_ENV=prod
.
No problem! In deploy.yml
, add a new key called environment
. And below, SYMFONY_ENV
set to prod
:
- hosts: aws | |
// ... lines 3 - 27 | |
environment: | |
SYMFONY_ENV: prod | |
// ... lines 30 - 33 |
Thanks to this, the Composer post-install tasks will not explode. And that's good... it's not great when your deployment explodes.
Oh, and important note: for this all to work, Composer must be already installed on your server. We did that in our provision playbook.
Clearing & Warming Cache
Before we try this, let's tackle one last thing: clearing the Symfony cache... which basically means running two console commands.
To make this easier, in deploy.yml
, add a new variable: release_console_path
. Copy the ansistrano_release_path.stdout
variable and paste it: {{ ansistrano_release_path.stdout }}/bin/console
:
- hosts: aws | |
// ... lines 3 - 14 | |
vars: | |
release_console_path: "{{ ansistrano_release_path.stdout }}/bin/console" | |
# Ansistrano vars | |
// ... lines 19 - 35 |
Cool! Back in the hook file, add a new task to clear the cache. Use the command
module to simply say {{ release_console_path }} cache:clear --no-warmup --env=prod
:
// ... lines 1 - 6 | |
- name: Install Composer dependencies | |
// ... lines 8 - 11 | |
- name: Clear the cache | |
command: '{{ release_console_path }} cache:clear --no-warmup --env=prod' | |
// ... lines 14 - 17 |
That's basically the command that you see in the docs.
If you're not familiar with the --no-warmup
flag, it's important. In Symfony 4, instead of running cache:clear
and expecting it to clear your cache and warm up your cache, cache:clear
will only clear your cache. Then, you should use cache:warmup
separately to warm it up. By passing --no-warmup
, we're imitating the Symfony 4 behavior so that we're ready.
Add the second task: "Warm up the Cache". Copy the command, but change it to just cache:warmup --env=prod
:
// ... lines 1 - 11 | |
- name: Clear the cache | |
command: '{{ release_console_path }} cache:clear --no-warmup --env=prod' | |
- name: Warm up the cache | |
command: '{{ release_console_path }} cache:warmup --env=prod' |
Now, technically, since the cache/
directory is not shared between deploys, we don't really need to run cache:clear
: it will always be empty at this point! But, I'll keep it.
Ok! Phew! I think we've done everything. Let's deploy! Find your local terminal and run the playbook:
ansible-playbook -i ansible/hosts.ini ansible/deploy.yml --ask-vault-pass
Use beefpass
as the vault password and deploy to master
. Then... wait impatiently! Someone fast forward, please!
Yes! No errors! On the server, move out of current/
and then back in. Check it out! Our vendor/
directory is filled with goodies!
Fixing the File Permissions
Moment of truth: try the site again: mootube.example.com. Bah! It still doesn't work. Let's find out why. On the server, tail the log file:
sudo tail /var/log/nginx/mootube.example.com_error.log
Ooooh:
PHP Fatal error: The stream or file "var/logs/prod.log" could not be opened
Of course! We have permissions problems on the var/
directory! Fixing this is actually a very interesting topic. There is an easy way to fix this... and a more complex, but more secure way.
For now, let's use the simple way: I really want our app to work! Add a new task: "Setup directory permissions for var". Use the file
module. But, quickly, go back to deploy.yml
and make another variable: release_var_path
set to the same path {{ ansistrano_release_path.stdout }}/var
:
- hosts: aws | |
// ... lines 3 - 14 | |
vars: | |
release_console_path: "{{ ansistrano_release_path.stdout }}/bin/console" | |
release_var_path: "{{ ansistrano_release_path.stdout }}/var" | |
// ... lines 18 - 36 |
Now, back in after-symlink-shared.yml
, set the path to {{ release_var_path }}
, state
to directory
, mode
to 0777
and recurse: true
:
// ... lines 1 - 17 | |
- name: Setup directory permissions for var/ | |
file: | |
path: "{{ release_var_path }}" | |
state: directory | |
mode: 0777 | |
recurse: true |
On deploy, this will make sure that the directory exists and is set to 777. That's not the best option for security... but it should get things working!
Deploy one more time:
ansible-playbook -i ansible/hosts.ini ansible/deploy.yml --ask-vault-pass
Type beefpass
, deploy to master
... and watch the magic. I can see the new directory permissions task... and it finishes.
Refresh the site! Eureka! Yea, it's still a 500 error, but this comes from Symfony! Symfony is running! Change the URL to http://mootube.example.com/about
. It works! Yea, it's super ugly - we need to do some work with our assets - but it does work. The homepage is broken because our database isn't setup. But this static page proves our deploy is functional! Victory!
Now, let's smooth out the missing details... like the insecure permissions, the database and our assets... because this site is horrible to look at!
Hello guys:
I just upgrade my Symfony app to 4.3 version from 3.4. Everything is working in local but I have problems deploying by Ansistrano.
The task Install Composer dependencies is exploting and appears the message "Executing script cache:clear [KO]", Environment variable not found: \"DATABASE_URL\". But, I put that variable in a file .env.prod.local that I put in the ansistrano_release_path.stdout using the synchronize module of Ansible. Please, can you tell me any suggestion about how to fix this?
Thanks.
Cesar