I have created a PHP web-application.
I have 3 environments: DEV, TEST, PROD.
What's a good tool / business practice for me to move my PHP web-application code from DEV to TEST to the PROD environment?
Realizing that my TEST environment still only connects to my TEST database; whereas, I need to PROD environment to connect to my PROD database. So the code is mostly the same, except that I need to change my TEST code once moved into PROD to connect to the PROD database and not TEST database.
I've heard of people taking down Apache in such away that it doesn't allow new connections and once all the existing connections are idle it simply brings down the web server.
Then people manually copy the code and then manually update the config files of the PHP application to also point to the PROD instance.
That seems terribly dangerous.
Does a best practice exists?
1) Separate configuration from the rest of the code. The rest of the code should then be able to run on all 3 locations without modifications. A typical config file could be:
<?
$db = "main_db"; $db_user="web1"; $db_pass = "xyz123";
$site ="example.com";
$htroot = "/var/www/prod/htdocs"
?>
And for the test environment:
<?
$db = "test_db"; $db_user="web1"; $db_pass = "xyz123";
$site ="test.example.com";
$htroot = "/var/www/test/htdocs"
?>
Do not include the configuration files with the passwords in the code base. The code base may be copied through insecure connections or be stored on third party code hosting servers later (see below). And you maybe don't want your passwords on all your backup disks of the code.
You could also create one single config file and use a switch depending on the environment the code runs:
<?
$site = $_SERVER["HTTP_HOST"];
if ($site == "example.com"; ) {
$db = "main_db"; $db_user="web1"; $db_pass = "xyz123";
$htroot = "/var/www/prod/htdocs";
}
if ($site == "test.example.com") {
$db = "test_db"; $db_user="web1"; $db_pass = "xyz123";
$htroot = "/var/www/test/htdocs";
}
?>
But now you could be tempted to put it back into the code base, which is less secure as explained above. And if you do not put it there you have to update 3 files, or use one fixed location per server, and you have to make sure the code finds the file on each server. I personally prefer the one-file-per-site solution from above.
2) You have already "versions". There is one version running on prod now. Give it a unique name and number, which will never change again. You can use that version name when you backup the code and when you refer to the version or when you move it somewhere you name the subdiectory that will contian it after the version.
The version that you will put on prod in the near future is a different version, and if you make changes again this is also a different version.
As a rule of thumb: increase the version number when you move or export the code, when you swap or exchange or upgrade between locations, when you make a demo, and after each feature or milestone and each time when you do a full backup.
Please note that the config files (3, one for prod, test and dev) are NOT part of the versions. So you can "move the versions around" but the not the config files. If you can, put the config files OUTSIDE the tree with the rest of the code, so you do not need to separate them and take care when you move around the versions later. You could move the config one directory "up" and access them from the files like this:
"include ../config.php";
3) If you want to use version control systems, they do a great job but it needs some time to get used to it and if you are in a hurry with your update it is probaby not the right time to start live with it now. But I would for the future recommend to use a latest generation distributed version control system. Distributed means you do not need to setup a server and many more advantages. I will name bazaar, if it is necessary to update over ftp it can do. Please note that a version control system makes exchanging a version very fast, because only the differences between the versions are written. Bazaar has a community and documentation which makes it easy to start. There is also Git, which has the most up to date commercial hosting site: http://github.com. You can view the code online and compare between the versions and there are many more helpful features, even if you are the only coder, but in a group it is even better. The choice between the systems is not easy. I can not recommend CVS, which is outdated. Also SVN is not the latest generation of distributed version control system, I would not recommend to use it if there is not a specific reason, and it will pollute all your subdirs with special subdirs, which can be annoying. For peolple who are used to it and have already code in it it is fine, but for a starter I would say don't.
There is also Mercurial and Darcs among the distributed and open source version control systems. Mercurial also has a great commercial site for collaboration and online code view (http://bitbucket.org).
4) As long as you do not use a version control system, how about using symlinks?
You could have a directory on the server src/versions/ somewhere and put the named versions in there, each one in their own subdirectory. You will only add versions (because a version that exists will not be changed, if you change it it becomes a new version)
You could have src/versions/v001/ src/versions/v002/ src/versions/v003/ or whatever naming scheme you use.
Now here comes the trick: /var/www/prod/htdocs is a symlink to src/versions/v001/
When you upgrade to v002 you just do the following:
shutdown apache
remove the old symlink /var/www/prod/htdocs (at this point the apache webroot is gone!)
create the new symlink /var/www/prod/htdocs being a link to src/versions/v002
start apache
You can also write a script for this with parameters and call it like this:
upgrade-web prod 002
This makes the gap even shorter.
Sometimes you have to do a "fallback", when you find out that the new version has errors in production and you have to go back. This would be easy (because you do not remove the old directories, you just stop apache, delete the symlink and re-create it to the former location, in this case src/versions/v001 )
And if test and dev is on the same server, you can of course also symlink the same directories, so there would be no any for move or copy.
5) If you do it manually without symlinks, why not move instead of copy?
(When the files are not yet on the same server, you can copy them somewhere near, and then start with the migration, so you do not have to stop the server for such a ling time)
If there are several directories on the root level of the project you could move them one at time. Be sure to NOT MOVE the config files. Or find some strategy to bring them bakc. Workflow would be:
Stop apache
Move away all current prod directories and files on the root level except config file(s)
Move all new prod directories and files to the root level except config file(s)
Start apache
6) try to find your perfect individual file and directory layout and perfect workflow. This takes maybe some time and some thinking, but it pays. Do it on a piece of paper until you find the best solution. This could mean that you have to refactor your code and change server config files, but for the future your life is easier when you do administration and upgrades. From my experience: do not wait so long with this step. Your layout shoudl nmake it easy and safe to upgrade. Upgrading is not somehting extraordinary, it is routine and it should be safe and simple to do.
7) If you name your server and workstation environments (operating system of server is linux I guess, but is it a hosted or a root server, do you have ftp acces or also shell (ssh) access, or sftp? where do you develop, on a windows machine, or a mac?) then people can name tools to do the copying and moving. Also interesting: Is the test and dev server the same machine, if not, how they are connected, or they aren't? If not, you would make a 3-way transfer (Copy it to your local workstation and then to the server).
8) Think of file permissions. If you move files around or copy them, maybe the file permissions change, and if the application depends on some of them there should be a way to check and maybe chang. Example: Some applications need writable directories where they put uploaded files or session files or template caching. Other applications do not allow some files for security to be writable.
Use configuration files to determine what database you're connecting to. That is, have a DEV configuration file, a TEST configuration file, and a PROD configuration file. It's generally the best way to avoid costly and frustrating mistakes.
Actually, I don't see any reason why TEST environment should miraculously migrate to PROD without any server shutdowns. TEST production is supposed to be for testing purposes. And even if you are actually TESTING on production server, bring it down (shutdown apache), change one line in your main config file, that is determining what set of minor config files to use) and bring it up again (start apache). This will take not more than 1-3 mins to complete and since you surely not going to do that twelve times a day, you will be fine.
Have your code in a revision control system (I prefer Subversion (svn)). This makes it easy to keep your DEV, TEST and PROD environments in sync, you don't have to keep track of files you modified. Once you are happy with your modifications on DEV, you commit the changes to svn and then run "svn update" on the TEST and eventually after testing on PROD server. Most linux hosting providers have svn client installed or you can install it yourself.
I don't like having a different version of a config file for each site because it requires manually renaming one file and removing the other two. I prefer having DEV, TEST and PROD configurations in the same config file. In the config file I determine which server the code is running on by checking either the hostname or the request url. Then I can have "if" or "switch" statement that would load configuration settings based on which server is currently running the code.
You might also need to sync database structure between your servers. I use sqlyog for this purpose, it has a built-in database structure synchronization tool that compares 2 database structures and prepares SQL to synchronize them.
When we push updates live, it's not often we have to reboot apache. This maybe a side effect of not having high traffic sites (< 1M pagedraws a month).
We have 3 branches, for various stages of development/QA: alpha (bleeding edge but available for viewing by non-developers and testers), beta (somewhat frozen for a particular release, final QA phase) and live (production).
To migrate from one branch to another, we perform a merge between say alpha and beta, commit that merge. Then run a deployment script which updates the branch from SVN on our development machine, then rsync's the code web servers to the beta document root.
As already mentioned by others, each branch can contain it's own config file with appropriate settings to cater for environment differences.
We are in the process of migrating to git to smooth out the branch merging process, which can be a little traumatic in SVN for large projects/releases.
Related
Currently using LAMP stack for my web app. My dev and prod are in the same cloud instance. Now I am getting a new instance and would like to move the dev/test environment to the new instance, separating it from the prod environment.
It used to be a simple Phing script that would do a SVN export into the prod directory (pointed to by my vhost.conf). How do I make a good build process now with the environments separated?
Thinking of transferring the SVN repository to the dev server and then doing a ssh+svn push (is this possible with Phing?)
What's the best/common practice for this type of setup?
More Info:
I'm currently using CodeIgniter for MVC framework, Phing for automated builds for localhost deployment. The web app is also supported by a few CRON scripts written in Java.
Update:
Ended up using Phing + Jenkins. Working well so far!
We use Phing for doing deployments similar to what you have described. We also use Symfony framework for our projects (which is not so much important for this but Symfony supports the concept of different environments so it's a plus).
However we still need to produce different configuration files for database, front controllers etc.
So we ended up having a folder with build.properties that define configuration for different environments (and in our case also for different clients we ship our product to). This folder is linked to the file structure using svn externals (again not necessary).
The Phing build.xml file then accept a property file as a parameter on the command line, takes the values from it and produces all necessary configuration files, controllers and other environment specific files.
We store the configuration in template files and then use copy/filter feature in Phing to replace the placeholders in the templates with the specific values.
The whole task of configuring the given environment can then be as simple as something like this:
phing configure-environment -DpropertyFile=./build_properties/build.properties.prod
In your build file you check if the propertyFile property that specifies the properties file is defined and load the file using <property file="./build_properties/build.properties.prod" override="true" />. Then you just do any magic with the values as you need.
You can still use your svn checkout/update and put all the resulting configuration files into svn ignore (you will have them generated by phing). We actually use additional steps in Phing. Those steps in the end produce a Linux shell installation self-deploy package. This is produced automatically in Jenkins. We then send the package to our clients or the support team can grab the package from Jenkins and they can do the whole deployment just by executing it (we still prefer manual deployments to production servers) or Jenkins can deploy it automatically (for example to test servers).
I'll be happy to write more info if needed.
I recommend using Capistrano (looks like they haven't updated the docs since they moved the site) and railsless-deploy for doing deployment. Eventually, you are probably going to need to add more app boxes and run other tasks as part of your deployment so choosing a framework that will support this can save you a lot of time in the future. I have used capistrano for two PHP deployments (one small and one large) and although its not perfect, it works well. It also handles all of the code checkout / update, moving symlinks into place, and rolling back if something goes wrong.
Once you have capistrano configured, all you have to do is something like:
cap dev deploy
cap prod deploy
Another option that I have explored for doing this is fabric. Although I haven't used it, if I had to deploy a complex app again, I would consider it. The interface is simple and straightforward.
A third option you might take a look at thought its still in the early stages of development is gantry (pardon the self promoting). This is something I have been working on out of frustration with using capistrano to deploy a PHP application in an environment with a lot of moving pieces. Capistrano is great and works well for non PHP application deployments, but you still have to some poking around in the code to understand what is happening and tweak it to suit your needs. This is also why I suggest giving fabric a good look.
I use a similar config now. Lamp + SVN + codeigniter + prd and dev servers.
I run the svn repos on dev. I checkout the repos into the root folder of the dev domain. Then use a post-commit hook to update the root folder everytime any developer commits.
When we are happy and have fully tested the code I ssh into the prd server and rsync the dev root to the prd root.
Heres my solution for the different configs. Outside the root folder I have a config.ini file. I parse the file in my codeigniter constants.php script. This means that the prd and dev server can have separate settings without them ever being in the repos.
If you want help with post-commit, rsync and ini code let me know.
I'm trying to use Phing to automate :
running tests
running DB migrations on each Developer machine [using dbdeply]
deployment to production when needed
I think it does make sense to add a build folder in my project and put all my build configuration files and db deltas in that folder. and commit all that into the SVN repository. so every developer will get the updated build files when he check-out from svn. and be able to run the build to update his DB with the new changes.
on the production server:
I was planning to add another build file there to get the latest Tagged version in svn and perform CSS & JS compression.
I was planning to implement continues integration using PHPUnderControl too, so I can keep track of the result of each build and get notified whenever the build fails.
so, do you think this all does make sense, or you do have other better suggestions?
What you say makes sense : it's pretty close to what I often use (sometimes with ant, sometimes with phing, and sometimes with some shell-scripts).
In the build directory, I would have something like this :
build/
testing/
development/
staging/
production/
common/
With one build.xml file in each sub-directory -- all including another build.xml file, located in the common directory, the idea being to put as much "common" code as possible in the "common" build.xml file, and to have per-environment specific files, that contain as few xml code as possible.
This can be done with the import task that exists in the last version of phing (not sure it's in the stable version -- I'm using an SVN checkout of phing, to have this one, for the project I'm currently working on).
One thing, though : you say you want to deploy to production from the production server ; I would rather, instead :
on a "development" server :
export from SVN
compress JS/CSS and all that
create a tar.gz archive
upload that archive to the production server
on the "production" server :
un-compress that uploaded archive
change a couple of symlink to use the new version of the sources (see the answer I gave here, for more informations)
update what has to be done in the DB
The idea being to :
do as few things as possible on the production server
just in case something goes wrong
and in case, one day, your production server doesn't have access to the SVN server
to have a physical archive that can be deployed on several production servers
Oh, and, as a sidenote : you have to write some kind of documentation of "how to deploy to production", step by step !
This will be really useful the day you are in holiday and someone else has to deploy to production because of an urgent bug-fix ;-)
So we are pushing to create good processes in our office. I work in a web shop that has been doing web sites for over a decade. And we don't use version control. I know! It's bad, not my fault. I'm the guy with a SoftE background pushing for this at a minimum.
The tech lead has been looking into it. We all use Mac workstations and mostly use Coda for editing since it is a great IDE. It has SVN support built in but expects it to work on local files. We're trying to explore mounting the web directory as a local network drive with an SFTP tool.
We are a LAMP shop, BTW.
I am wondering what the model is here. I think we have typically would checkout the whole site to our local machine where we have apache running and then test it there? This isn't how we work yet, we do everything on the server. We've looked at checking things in and out, but some files are owned by apache and the ownerships change when I check them in, because I'm not apache.
I just want to know a way to do this that works given my circumstances. Would be nice to not have to run apache locally.
You might want to checkout the Coda mailing list and ask there. Lots of Coda enthusiasts there with specific experience.
If you don't want to have to run locally could make Apache on your server run a copy of the site for every developer, on a different port per person, and then mount those web-roots to the local macs and make that the working directory. If you're a small shop that's not hard to manage. I find that pretty easy to set up and saves a lot of resources on the local machines. The one-site-per-person helps to avoid conflicts with multiple people working on files at the same time.
What I'd additionally recommend is to have a script that gets the latest changes from SVN and deploys the entire site to the production server when you're ready. You could have that script change permissions on appropriate files/folders as needed to be owned by Apache. The idea once you're using source control is to never manually edit the production files -- you should have something that deploys it from SVN for you.
A few notes:
Take a look at MacFuse / MacFusion (the latter is the application, the former is the library behind it) to mount remote directories via SSH / FTP as local ones.
Allow your developers to check out into their local environment (with their own LAMP stack if they're savvy), or look into a shared dev environment with individual jails. This way your developers can run their own LAMP stack (which you could deploy for them on the machine) without interfering with others.
The idea being, let them use a workflow that works best for them, to minimize the pain in adapting to this change (if change management might be an issue!)
Just as an example, we have a shared dev server where jails are created with a single command for new developers. They have a full LAMP stack ready to go, and we can upgrade and re-deploy jails easily to keep software up to date. Developers have individual control to add custom settings / extensions if they need it for work, while the sys admins have the ability to reset everything when someone accidently breaks their environment :)
Those who prefer not to use jails, and are able to, manage their own local environments (typically through Macports or MAMP).
I've read a number of topics in the same sort of ballpark as this one, but in all honesty I'm still not exactly sure on the best approach (as a starting point). I am a solo developer in a small office and I have around 30 websites which are hosted on a linux VPS. I want to start using using version control (probably SVN) and also set up a staging server. At the moment, I do development either locally on my machine before using FTP to upload to the live server, or ocassionally for small changes I edit the remote files directly, which is not an ideal approach.
I'm looking for some guidance on how to improve my development environment. I imagine I should be installing SVN on the web server, which would then allow me to check out versions to my local machine (which would also require SVN i think). Also, if I want to set up a staging server, should I just set up subdomains for each of the live websites, then use these subdomains for showing clients changes to the site before making them live?
Hope this makes sense!
This is what we do at work:
We have a staging server running Apache and a Subversion server. We have a post commit hook that updates a working copy in the htdocs directory, that way, when a developer commits something it automatically gets updated on the staging server, so everyone can see the latest code.
On the client's production servers (the ones we can control) we have the Subversion client installed and the website is a working copy. When we need to update the live site we login to a shell and run svn up. If you do something like this, make sure to limit access to the .svn directories, either with .htaccess files or from the main Apache config.
We have a custom app that manages the projects, but that is only because we're lazy and don't want to setup each project by hand, the app creates the necessary directories and working copies. You could write a quick script to do this.
We never, ever, edit files via FTP on the live site. All in all we have been using this setup for almost 2 years and aside from the occasional conflict on the staging server, we never have had any problems.
You can actually install the SVN server on your local machine, which I would recommend in lieu of installing it on the web server (assuming you make backups). The easiest thing to do, since it’s only you using it, would be to use the file:// protocol, but using svnserve is a little more robust, and the preferred method if you want to take the time to do it.
#Michael, I disagree - I would say it's better to install on the linux vps, especially if you are already paying for the hosting service. I find it very helpful to be able to browse and download stuff from my svn repo wherever I am, from whatever computer I'm on.
#nicky, I started with svn (and version control) several years ago and I took baby steps which made it easier to tackle.
If I had to do it over again, I'd read the svn book to start with. The book is very well laid out and didn't take more than 1-2 days to plow thru.
While you're reading, install svn on your linux vps with an apache front end.
Once you have that up, pick one of your websites and import it into svn. This is how I structure my svn repo. For example, say my repo is hosted at http://mysvn.mydomain.com/svn/:
mywebsite1
- trunk
- tags
- branches
mywebsite2
- trunk
- tags
- branches
Don't worry about creating the perfect structure. It's pretty easy to re-organize especially when you're starting out. After you import a few projects into svn, you'll start to get a feel for which projects should have their own "trunk/tags/branches" dir structure and which can be combined.
For creating test environments, I do exactly what you describe. I use build scripts to checkout from svn and download files into dirs that are mapped to subdomains like "test.clientsite.com" (I work primarily in java and use ant and maven, but I think you can use whatever scripting language you're familiar with).
Once you get used to version control, you'll never go back, good luck!
I've always just FTPed files down from sites, edited them and put them back up when creating sites, but feel it's worth learning to do things properly.
I've just commited everything to a SVN repo, and have tried sshing into the server and checking out a tagged build, as well as updating that build using switch.
All good, but it's a lot lot slower than my current process.
What's the best way to set something like this up? Most of my time is just bug fixes or small changes rather than large rewrites, so I'm frequently updating things.
You don't necessarily need to use SVN to deploy the files to the server. Keep using FTP for that and just use SVN for revision history.
You should look at installing rsync to upload changes to your server.
Rsync is great because it compares your local copy of the repo to the copy that's currently on the server and then only sends files that have changed.
This saves you having to remember every file that you changed and selecting them manually to FTP, or having to upload your whole local copy to the server again (and leaving FTP to do the comparisons).
Rsync also lets you exclude files/folder (i.e. .svn/ folders) when syncing between your servers.
I'd recommend you keep using Subversion to track all changes, even bug fixes. When you wish to deploy to your production server, you should use SSH and call svn update. This process can be automated using Capistrano, meaning that you can sit at your local box and call cap deploy -- Capistrano will SSH into your server and perform the Subversion update. Saves a lot of tedious manual labor.
For quick updates I just run svn update from the server.
Sometimes for really really quick updates I edit the files using vim and commit them from the server.
It's not very proper, but quick and quite reliable.
If you want to do this properly, you should definitely look into setting up a local SVN repository. I would also highly recommend setting up a continuous integration (CI) server such as cruise control, which would automatically run any tests against your PHP code when ever you check in to svn. Your CI server could also be used to publish your files via FTP to your host at the click of a button, once it has passed the tests.
Although this sounds like a lot of work, it really isn't and the benefits of a smooth deployment process will more than pay for itself in the long run.
For my projects, I usually have a repo. On my laptop is a working copy, and the live website is a working copy. I make my changes on the local copy, using my local webserver. When everything is tested and ready to go, I commit the changes, then I ssh into the remote server and svn update.
I also keep a folder in this repository which contains sql files of any changes I've made to the database structure, labelled according to their revision number. For instance, when I commit Revision 74 and it has a couple extra columns in one of the tables, included in the commit will be dbupdates/rev74.sql. That way, after I do my svn update, all I just have to run my sql file (mysql db_name -p -u username < dbupdates/rev74.sql) and I'm good to go.
If you want to get real funky with it, you could use a build script to get the current version from SVN, then compile your PHP code, then on a successful build, automatically push the changes to your server.
This will help in debugging and may make your code run faster. Also, getting into the build habit has really improved my coding over just pushing the PHP straight to the server and debugging via Firefox.
The benefits of source control reveal themselves as the complexity of the project and number of developers increase. If you are working directly on a remote server, and are only making quick patches most of the time, source control might not be worth the effort to you.
Preferably, you should be working from a local working copy of the repository (meaning you should also set up a local server). Working against a remote server using SVN as the only means to update it would slow you down quite considerably.
Having said that, working with SVN (or any other source control) will yield many benefits in the long run - you have a complete history of changes, you can always be sure the server is up-to-date (if you ran update) and if you add more developers to the project you can avoid costly source overwrites from each other.
What I do at work, is use FTP to upload changes to a test server. Then when I am finished with the section of the site that I was working on, I commit the changes and update both. Sometimes, if I am working on something and I change a lot of files in different directories, I commit it and update the test server. But I don't update the production server. But I am the only programmer here, I wouldn't recommend committing possibally buggy code if there is more than one programmer.
I use ZendStudio for Eclipse (currently version 6.1). And I use SVN to keep my source codes available. Initially I thought the process was somewhat slow due to commit process (and entering commit comment) and wait until it stops.
However after learning that Ctrl+Alt+C to Commit and check 'Always run in Background', the process doesn't slow at all.
Plus, I do run everything locally, then only SSH after a while.
I did a post-commit hook to automatically update my web. It´s fast but you can make mistakes.
IF on a *nix server AND you have the appropriate SSH access AND you have space to keep multiple copies of the website, THEN the single most useful versioning technique I have found is to use a symbolic link to point to the "current" version of the website. (You can still use SVN to version source code -- this is a way to easily/instantly switch between versions of the website on the server.)
Set up the webserver to point to /whatever.com as the root of the website.
Have a folder like /website/r1v00 to which you FTP the website files, then create a symlink called "whatever.com" that points to /website/r1v00
When you have an updated version of the website, create another folder called /website/r1v001, FTP all the files for the updated site, then change the symlink for "whatever.com" to now point to /website/r1v01. If there are any problems with the new site, you can back it out instantly by simply pointing the "whatever.com" symlink back to /website/r1v00
Of course, you can/should set up scripts to automate the creation and switching of the symlink. In my case, I have an "admin" page written in PHP that lists all the available versions, and allows me to switch to any of them. This technique has saved my bacon several times...!
Obviously this does not address any issues with versioning database schemas or database content.