Part 2 / 3 - Deploying Symfony 4 with git - Sharing Folders
In the previous video we covered how to upload / deploy our code using Git. We went down the route of simplicity, whereby our local git push
command would copy all our local data that is tracked by git up to our remote server, right into the live /var/www/crvfakeexample.com
directory.
There are some drawbacks to doing this.
As we're serving data from this directory to live site visitors, we might break our site during a deploy if changing a file the visitor is trying to access, whilst they are trying to access it. The bigger your site in terms of active visitors, the more likely this problem is to occur.
Also, rolling back to a previous version is difficult, as it means local and remote changes are required.
A better strategy is to use the concept of builds
.
In order to do this, however, we need to re-do our directory structure, update our post-receive
hook file, and share our var
directory.
This is a little easier to achieve with Symfony 4 than it was during the Symfony 2 / Symfony 3 equivalent.
Revised Directory Structure
Rather than have all our code stored under /var/www/crvfakeexample.com
, we are going to move the code into a subdirectory called code
. Not the most imaginative name, but it works.
We're also going to add in a shared
directory, into which we will move the var
directory.
Our new directory structure will be:
/var/www/crvfakeexample.com/shared/var
/var/www/crvfakeexample.com/code
We'll take care of setting up the appropriate symlinks in our post-receive
hook file.
I'm working on the assumption you're following along in a controlled demo environment. If in live, don't be so hasty, and adapt accordingly:
# server side
rm -rf /var/www/crvfakeexample.com/*
rm -rf /var/www/crvfakeexample.com/.*
These two commands remove any files and folders, including hidden files / folders.
Next, create the directory structure:
# server side
cd /var/www/crvfakeexample.com
mkdir code
mkdir -p shared/var
mkdir -p
creates folders and subfolders in one go, creating any -p
arent directories as needed.
At this point we also need to re-do the required Symfony permissions for the var
directory:
cd shared
HTTPDUSER=$(ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1)
sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var
sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var
Lastly, as far as permissions are concerned, I'm going to set the shared
directory and any subdirectories to have the owning user and owning group of www-data
:
# server side
sudo chown -R www-data:www-data /var/www/crvfakeexample.com/shared
This should hopefully have preempted any permissions problems that we might have otherwise faced.
Updating post-receive
Next we need to update our post-receive hook file.
Again from the server:
vim /var/www/crvfakeexample.git/hooks/post-receive
Here are the changes in full:
#!/bin/bash
WEB_ROOT=/var/www/crvfakeexample.com
export GIT_WORK_TREE=$WEB_ROOT/code
export GIT_DIR=/var/www/crvfakeexample.git
git --work-tree=$GIT_WORK_TREE --git-dir=$GIT_DIR checkout -f master
rm -rf $GIT_WORK_TREE/var
ln -sfn $WEB_ROOT/shared/var $GIT_WORK_TREE
cd $GIT_WORK_TREE
composer install --no-dev --optimize-autoloader
php bin/console cache:clear --no-warmup
php bin/console cache:warmup
This is roughly what we had before, with a couple of changes.
Firstly, because we need to use the path /var/www/crvfakeexample.com
in two places, we have extracted this out to a separate variable: WEB_ROOT
.
This variable is not export
ed, as it's only required in this file, not in the wider system environment.
Any references to this path have been replaced with the variable.
Secondly, we rm -rf var
to remove the unwanted uploaded var
contents from our local. We don't want our local contents, only the existing contents from the server. This keeps around any sessions, logs, and so on from previous builds.
Doing a git push prod master
now should see us right. But as we've changed the directory structure we do need to tell Apache / nginx.
Revised Apache Vhost Configuration
<VirtualHost *:80>
ServerName crvfakeexample.com
ServerAlias www.crvfakeexample.com
DocumentRoot /var/www/crvfakeexample.com/code/public
<Directory /var/www/crvfakeexample.com/code/public>
AllowOverride None
Require all granted
Allow from All
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
</Directory>
# uncomment the following lines if you install assets as symlinks
# or run into problems when compiling LESS/Sass/CoffeeScript assets
# <Directory /var/www/crvfakeexample.com/code>
# Options FollowSymlinks
# </Directory>
# optionally disable the RewriteEngine for the asset directories
# which will allow apache to simply reply with a 404 when files are
# not found instead of passing the request into the full symfony stack
<Directory /var/www/crvfakeexample.com/code/public/bundles>
<IfModule mod_rewrite.c>
RewriteEngine Off
</IfModule>
</Directory>
ErrorLog /var/log/apache2/crvfakeexample.com_error.log
CustomLog /var/log/apache2/crvfakeexample.com_access.log combined
# optionally set the value of the environment variables used in the application
SetEnv APP_ENV ${APP_ENV}
SetEnv APP_DEBUG ${APP_DEBUG}
SetEnv APP_SECRET ${APP_SECRET}
SetEnv DATABASE_URL ${DATABASE_URL}
</VirtualHost>
Be sure to run an Apache reload after changing this config:
service apache2 reload
Revised nginx Server Configuration
server {
listen 80 default;
server_name crvfakeexample.com www.crvfakeexample.com;
root /var/www/crvfakeexample.com/code/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# optionally set the value of the environment variables used in the application
fastcgi_param APP_ENV prod;
fastcgi_param APP_SECRET some_new_secret_123;
fastcgi_param APP_DEBUG 0;
fastcgi_param DATABASE_URL "mysql://not_root:different_pass@127.0.0.1:3306/symfony_demo";
fastcgi_param MAILER_URL null://localhost;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/crvfakeexample.com_error.log;
access_log /var/log/nginx/crvfakeexample.com_access.log;
}
Be sure to run an nginx reload after changing this config:
service nginx configtest
service nginx reload
The only change in both files is to add /code
onto the existing file paths.
Testing The Revised Git Deployment
If you have pushed up code already, such as from the previous video, you will need to make a local change to your project before you can git push
successfully.
# from your local
cd {project dir}
touch fake-change
git add fake-change
git commit -m "a fake change"
git push prod master
# output here
It is strange that we have to fake a change to deploy our code. This is a big reason why I don't like using Git like this.
This approach is half way to where I'd like to be. We've got a shared var
directory, but we're still overwriting our live code when doing a deploy.
In the next video we will add in the concept of a builds
directory.