Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deploying a Symfony project with Jenkins: Best Practices

Tags:

I manage a few Symfony 2/3 projects on my Jenkins server that I am deploying to a live server. This is my current setup:

The build

  • checkout using git plugin
  • remove database, if exists
  • executing composer install (prod mode, optimize autoloader)
  • executing bower install to fetch my assets
  • executing a gulp build, that minifies and concatenates css / javascript (We don't use assetic)
  • executing my database creation stuff
  • executing unit tests

The archiving

After the build, I archive the artifacts of the build without the vendor, node_modules and bower_components folders as a zip file using the "Compress Artifacts" Plugin.

The deployment

I use the "Promoted builds" plugin and the "Publish over SSH" plugin in combination: If I want to "go live" with a build, I publish the artifacts (my zip file) over SSH to my live system in a directory called staging_dir. After the file upload, I execute some SSH commands:

  • Set the live system to maintenance mode
  • unzip the artifacts zip in my staging_dir
  • execute composer install on the live system (same config as during the build)
  • (bower install and the gulp build is not necessary, as we use the assets we created during the build)
  • migrate database
  • move the current live system files to a backup folder
  • copy over the files from the staging_dir
  • Set the live system to "production" mode (disable maintenance mode)

Best practices?

I would now like to collect some best practices for deployment:

  • Do you prefer to transfer the vendor folder to the live system instead of executing composer install there again?
  • What about the assets? Do you bower install and gulp build on the live system again or do you use the published assets?
  • How do you handle your passwords when executing a live promotion?
  • ... other things I have forgotten.
like image 816
David Müller Avatar asked Feb 11 '16 18:02

David Müller


1 Answers

Me and a couple of colleagues have been dealing with this for quite a while now. When we started it was quite hard to find good posts regarding this topic. This is why I'd like to share what we've found to be working "best" for us.

Project details

One of our clients has a big and heavy platform used for managing his business process (something like an ERP & CRM). The platform has been initially developed using Symfony 2 and we've now upgraded to Symfony 3. To be sure everything is working properly the project has tons of test cases. We also use:

  1. Doctrine Migrations so we could safely update the database on the production servers whenever this is necessary.
  2. Phing
  3. Bower
  4. Assetic
  5. PHPUnit
  6. Git
  7. Jenkins

Testing

Once somebody commits to our git server, a hook lets Jenkins know and a build is triggered. Once the job finishes successfully we manually trigger the deployment. This is done by logging into the client's machine(s) and triggering a script we've developed.

Deployment

We used to approach this the same way as you - upload the archive after jenkins finishes the job. This, however, proved to be quite problematic because in some cases the archive might get broken (i.e. due to network connection issues between the jenkins instance and the production server). Also it took considerably longer to upload a file from our server to the client's server. This is why we decided to use git and pull the necessary version from there. Using git proved to be more reliable and ensures you have an absolute copy of the project on the client's side. Also, rolling back to previous version is just one git checkout away :)

Because most of us already had experience with ant and php we have decided to use Phing and create a build script and automate most of the regular tasks. In the build script we've added most of the common tasks we run all the time such as install, upgrade, clear cache, install assets, etc. Then we made this script available on each production server.

Once the jenkins build job is successful and we've manually released & tagged the version of the product, we'd run phing update on the client's machine via SSH (this step could have been automated but was intentionally not due to some project requirements). What this command would do is:

  1. Get the latest tagged version from our git server
  2. If the current version < latest tagged version (with hash 19a6d9) proceeds to next step
  3. phing maintenance:on (sets the platform in an offline mode)
  4. git fetch origin 19a6d9
  5. git checkout 19a6d9
  6. phing composer:install
  7. phing database:upgrade (which runs a couple of commands, including database backup and doctrine:migrations:migrate)
  8. phing assets (runs bower install, assetic commands and compiles all js/css into one.css and one.js)
  9. phing cache:clean
  10. phing maintenance:off (turns the platform on)

Our phing update task is also surrounded by trycatch and in case the update phase hits a brick it would automatically go into the rollback phase. It is done via a phing rollback PREVIOUS_VERSION command which does:

  1. git checkout PREVIOUS_VERSION - restores the fs to the previous git version
  2. phing composer:install
  3. phing database:rollback PREVIOUS_VERSION
  4. phing assets
  5. phing report (this reports there's an issue with the upgrade to our issue tracker with a log file attached)

Answers to your questions

  1. Do you prefer to transfer the bower_components and the vendor folder to the live system instead of executing bower install and composer install there again?

    • I would go with bower install and composer install, because after the first time you'll already have everything cached. Every next run would be almost instantaneous or at least much faster than re-uploading all assets again and again via ftp. That might save you somewhat bandwidth and most importantly time. If you choose to do a similar setup to mine, you might want to avoid compiling the js/css on the Jenkins instance as you'll be doing that on the prod server.
  2. How do you handle your passwords when executing a live promotion?

    • Not sure what you're asking?

Conclusion

The setup mainly depends on your project requirements, the resources you have available (i.e. vps, shared hosting, git, ssh, etc) and your release process. As you can see, our deployment is slightly different from what you're doing. This doesn't make it bad nor good - it just suits our needs. If what you already have works for you and solves all of your problems - you should stick with it and try to optimize it. In case you're just getting started this is what you should be most careful about:

  1. FULL... BACKUPS! Having a backup of the production filesystem is great, but you should also have a backup of the database. During the development course of your project it's likely you'd have new features which change the database. Some of those changes may be backwards incompatible. So be sure to backup everything or you might end up having a fs backup without a db to work with thus not being able to rollback.

  2. Rollback - create a script which does all necessary steps to rollback the project to the previous version. If you're under a lot of pressure or things are time sensitive you might unintentionally make a mistake and break the backup or something else... Therefore make a script which does this for you.

  3. Test the deployment process locally or on a testing machine to be sure everything really works before doing it on the actual production server.

I hope this helps you find the best solution to your release & deployment process. If you happen to find a better solution, please post it as an answer - it will definitely be helpful!

like image 69
tftd Avatar answered Oct 13 '22 01:10

tftd