deployment tactics
DESCRIPTION
The slides from my Deployment Tactics talk at the ThinkVitamin Code Management online conference (http://thinkvitamin.com/online-conferences/code-manage-deploy/).TRANSCRIPT
![Page 1: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/1.jpg)
DEPLOYMENT
TACTICS
Managing code from development to production
Ian Barber - [email protected] twitter.com/ianbarber | phpir.com
![Page 2: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/2.jpg)
Image: http://flickr.com/photos/denisdervisevic/4527695803
![Page 3: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/3.jpg)
- Table of Contents -
1.... Change Control2.... Environments3.... Version Control 4.... The Deploy Process5.... Scripts6.... Continuous Integration7.... Remote Releases8.... Packaged Releases9.... Package Management10.. Managing Hotfixes11.. Managing Database Changes12.. Rollbacks13.. Tactical Deployment
![Page 4: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/4.jpg)
Change control
identify need
plan change
verify
execute change
deliverclose
![Page 5: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/5.jpg)
require release
export code
compare md5
copy to server
restart apache
report back
![Page 6: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/6.jpg)
Change Request Form
Requested By: J. Teamlead Authorised By: S. Manager Submit Date: 2011-01-27 Change Date: 2011-02-04
Reason For Change: Resolve JIRA-1602 - Listen for new .com variants on vhostChange Request:Release tag 1.1.3 via normal processmv /etc/httpd/conf.d/fooweb.conf /etc/httpd/conf.d/fooweb.oldmv ~releases/1.1.3/conf/fooweb.conf /etc/httpd/conf.d/fooweb.conf Verification:http://foweb.com shows the same page as http://fooweb.comRollback: Re-release 1.1.2mv /etc/httpd/conf.d/fooweb.old /etc/httpd/conf.d/fooweb.conf
![Page 7: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/7.jpg)
Environments
Production Development
staticrobustreliableoptimised
verbosedynamicunstable
experimental
![Page 8: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/8.jpg)
Image: http://flickr.com/photos/lejoe/3763218501
The Production Environment
![Page 9: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/9.jpg)
Image: http://flickr.com/photos/simononly/4454401446
The Staging Environment
![Page 10: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/10.jpg)
Image: http://flickr.com/photos/unfoldedorigami/2374016430
The Integration Environment
![Page 11: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/11.jpg)
Image: http://flickr.com/photos/drewnew/511936681
The Development Environment
![Page 12: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/12.jpg)
VERSION CONTROL
Image: http://flickr.com/photos/robbie73/4346732208
![Page 13: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/13.jpg)
/trunk
/branches/search
/branches/newpage /branches/...
/tags/1.1.2/branches/1.1.2
![Page 14: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/14.jpg)
/branches/newpage
/trunk
/branches/search
/branches/...
/tags/1.1.2/branches/1.1.2
Development
Staging Production
Integration
![Page 15: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/15.jpg)
master
devel
search feature
release1.1.1
long feature
![Page 16: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/16.jpg)
master
devel
search feature
release1.1.1
long feature
Development
Integration
Staging
Production
![Page 17: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/17.jpg)
The DEPLoy PROCESS
![Page 18: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/18.jpg)
The DEPLoy PROCESS
transparent easy
scalable reliable
flexible
graceful
![Page 19: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/19.jpg)
code config
commandspackages
SMTP config apache
vhost
support process
app update
libpng update
cache service
restart service
file perms
![Page 20: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/20.jpg)
server
code repository
package repository
deployment controllerserver
config repository
server commands
data
![Page 21: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/21.jpg)
server
code repository
package repository
deployment controllerserver
config repository
server commands+ data
data
![Page 22: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/22.jpg)
BUILD SCRIPTS
#!/bin/bash # Deployment script for FooWeb Projectgit archive --format=tar \ --remote=git://repo.com/myrepo/myrepo.git \ HEAD -o fooweb.tartar -xf fooweb.tar /var/wwwservice httpd restart
![Page 23: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/23.jpg)
#!/bin/bash # Deployment script for FooWeb Projectsvn export svn://localhost/fooweb-service/trunk releasecd release && mkdir buildcp -r web/* build/javac -cp /usr/share/java/servlet-api-2.5.jar -d build/WEB-INF/classes src/com/fooweb/service/*.javacd build && jar cvf ../fooweb.war * && cd ../# assumes autoDeploy is truecp fooweb.war /var/lib/tomcat6/webapps
![Page 24: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/24.jpg)
BUILDS TOOLS
code
assets
tests
build
testresults
docs
release
![Page 25: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/25.jpg)
buildtools
![Page 26: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/26.jpg)
<?xml version="1.0" encoding="UTF-8"?><project name="FooWeb"><property name="install" location="/var/lib/tomcat6/webapps" /><property name="svn.repo" value="svn://localhost/fooweb-service/trunk" />
<!--A "clean" target to delete compiled files--><target name="clean"> <delete dir="build" /> <delete dir="release" /> <delete file="fooweb.war" /></target>
![Page 27: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/27.jpg)
<!-- Checkout, mkdir and compile--><target name="build"> <exec executable="svn"> <arg line="export ${svn.repo} release" /> </exec> <mkdir dir="build"/> <copy todir="build"> <fileset dir="release/web" /> </copy> <javac srcdir="release/src" destdir="build/WEB-INF/classes/"> <classpath> <pathelement path="/usr/share/java/servlet-api-2.5.jar"/> </classpath> </javac></target>
<!-- Build our WAR file -->
![Page 28: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/28.jpg)
<!-- Build our WAR file --><target name="war" depends="build"> <war destfile="fooweb.war" webxml="build/WEB-INF/web.xml"> <fileset dir="build"/> <classes dir="build/WEB-INF/classes"/> </war></target>
<!-- Copy our file --><target name="deploy" depends="war"> <copy file="fooweb.war" todir="${install}" /></target>
</project>
![Page 29: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/29.jpg)
$ sudo ant deployBuildfile: build.xmlbuild: [exec] Exported revision 8. [mkdir] Created dir: /tmp/build [copy] Copying 2 files to /tmp/build [copy] Copied 3 empty directories to 1 empty directory under /tmp/build [javac] Compiling 1 source file to /tmp/build/WEB-INF/classeswar: [war] Building war: /tmp/fooweb.wardeploy: [copy] Copying 1 file to /var/lib/tomcat6/webapps
BUILD SUCCESSFUL Total time: 2 seconds
![Page 30: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/30.jpg)
CONTINUOUS INTEGRATION
Look at the Hudson Wiki at
http://wiki.hudson-ci.org
![Page 31: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/31.jpg)
![Page 32: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/32.jpg)
![Page 33: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/33.jpg)
![Page 34: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/34.jpg)
![Page 35: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/35.jpg)
![Page 36: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/36.jpg)
<project name="Fooweb" default="build"> <target name="build" depends="phpunit" />
<target name="init"> <mkdir dir="${basedir}/build/logs" /> </target>
<target name="phpunit" depends="init"> <exec executable="phpunit" dir="${basedir}/tests" failonerror="on"> <arg line=" --log-junit '${basedir}/build/logs/phpunit.xml' --coverage-clover '${basedir}/build/logs/clover.xml' --coverage-html '${basedir}/build/logs/coverage'" /> </exec> </target>
![Page 37: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/37.jpg)
<target name="phpcpd" depends="init"> <exec executable="phpcpd" dir="${basedir}/application" failonerror="on"> <arg line=" --log-pmd '${basedir}/build/logs/php-cpd.xml' ." /> </exec> </target></project>
![Page 38: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/38.jpg)
![Page 39: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/39.jpg)
![Page 40: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/40.jpg)
REMOTE RELEASES
Image: http://flickr.com/photos/scragz/309353618
![Page 41: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/41.jpg)
server
deployment controller
server
ssh / scpid_rsa.pub
authorized_keys authorized_keys
ssh-keygen -t rsauser “deploy”
ssh
/ scp
![Page 42: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/42.jpg)
from fabric.api import * # Development environmentdef dev(): env.user = 'deployer' env.roledefs = { "web" : ['localhost'], "db" : ['localhost'], }
# Production environmentdef production(): env.user = 'deployer' env.roledefs = { "web" : ['primary.fooweb.com', 'secondary.fooweb.com'], "db" : ['backend.fooweb.com'], }
Fabrichttp://fabfile.org
![Page 43: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/43.jpg)
# Package up release - run localdef prepare_deploy(): local('svn export svn://localhost/fooweb/trunk release') with cd('release'): local('tar cvzf ../fooweb.tar.gz .') local('rm -rf release') # Restart web server @roles('web')def restart_webserver(): sudo('/etc/init.d/apache2 restart')
![Page 44: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/44.jpg)
# Deploy to remote servers@roles('web')def deploy(): prepare_deploy() # in case of already existing with settings(warn_only=True): run('mkdir /tmp/release') run('rm -rf /tmp/release/*') put("fooweb.tar.gz", '/tmp/release') with cd('/tmp/release'): run("tar xvzf fooweb.tar.gz") run("rm -rf fooweb.tar.gz") run("mv * /tmp/test") restart_webserver(); local("rm -rf fooweb.tar.gz");
![Page 45: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/45.jpg)
$ fab dev deploy
[localhost] run: svn export svn://localhost/fooweb/trunk release[localhost] run: tar cvzf ../fooweb.tar.gz .[localhost] run: rm -rf release[localhost] run: mkdir /tmp/release[localhost] err: mkdir: cannot create directory `/tmp/release': File exists
Warning: run() encountered an error (return code 1) while executing 'mkdir /tmp/release'
[localhost] run: rm -rf /tmp/release/*[localhost] put: fooweb.tar.gz -> /tmp/release/fooweb.tar.gz
![Page 46: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/46.jpg)
[localhost] run: tar xvzf fooweb.tar.gz[localhost] run: rm -rf fooweb.tar.gz[localhost] run: mv * /tmp/test[localhost] sudo: /etc/init.d/apache2 restartPassword for ianbarber@localhost: [localhost] out: * Restarting web server apache2[localhost] out: ... waiting ...done.[localhost] run: rm -rf fooweb.tar.gz
Done.Disconnecting from localhost... done.
![Page 47: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/47.jpg)
$ fab production deploy[localhost] run: tar cvzf ../fooweb.tar.gz .....[primary.fooweb.com] run: rm -rf /tmp/release/[localhost] run: tar cvzf ../fooweb.tar.gz .....[secondary.fooweb.com] run: mkdir /tmp/release....Disconnecting from secondary.fooweb.com...doneDisconnecting from primary.fooweb.com... done
![Page 48: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/48.jpg)
set :application, "set your application name "set :repository, "set your repository"set :scm, :subversionrole :web, "your web-server here" role :app, "your app-server here" role :db, "your primary db-server here", :primary => true role :db, "slave db"
$ mkdir config && cd config && capify .[add] writing './Capfile'[add] making directory './config'[add] writing './config/deploy.rb'[done] capified!
Capistrano
https://github.com/
capistrano/
![Page 49: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/49.jpg)
set :application, "fooweb"set :repository,"svn://localhost/fooweb/trunk"
set :scm, :subversionset :scm_username, "deployment" set :scm_password, "s3kkr3tp4a55" set :scm_checkout, "export"set :keep_releases, 4 set :normalize_asset_timestamps, falseset :deploy_to, "/usr/local/#{application}"
role :web, "primary.fooweb.com"role :web, "secondary.fooweb.com"role :db, "backend.fooweb.com"
![Page 50: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/50.jpg)
namespace :deploy do task :migrate do # nothing end
task :restart do sudo "/etc/init.d/apache2 restart" endend
namespace :fooweb do task :perms do sudo "chmod -R a+w #{deploy_to}" endend
after "deploy:setup", "fooweb:perms"
![Page 51: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/51.jpg)
$ cap deploy:setup * executing `deploy:setup' * executing "sudo mkdir -p /usr/local/fooweb [...]" servers: ["primary","secondary", "backend"] [backend] executing command [...] command finished triggering after callbacks for deploy:setup * executing `fooweb:perms' * executing "sudo chmod -R a+w /usr/local/fooweb" servers: ["primary","secondary","backend"] [primary] executing command [...] command finished
![Page 52: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/52.jpg)
$ cap deploy * executing `deploy' * executing `deploy:update' ** transaction: start * executing `deploy:update_code' executing locally: "svn info svn://localhost/fooweb/trunk -rHEAD"/usr/bin/svn * executing "svn checkout -q -r17 svn://localhost/fooweb/trunk /usr/local/fooweb/releases/20110116192456 && (echo 17 > /usr/local/fooweb/releases/20110116192456/REVISION)" servers: ["primary.fooweb.com"] [primary.fooweb.com] executing command[....] * executing `deploy:finalize_update' * executing "chmod -R g+w /usr/local/fooweb/releases/20110116192456" servers: ["primary.fooweb.com"] [primary.fooweb.com] executing command command finished * executing "rm -rf /usr/local/fooweb/releases/20110116192456/log /usr/local/fooweb/releases/20110116192456/public/system /usr/local/fooweb/releases/20110116192456/tmp/pids &&\\\n mkdir -p /usr/local/fooweb/releases/20110116192456/public &&\\\n mkdir -p /usr/local/fooweb/releases/20110116192456/tmp &&\\\n ln -s /usr/local/fooweb/shared/log /usr/local/fooweb/releases/20110116192456/log &&\\\n ln -s /usr/local/fooweb/shared/system /usr/local/fooweb/releases/20110116192456/public/system &&\\\n ln -s /usr/local/fooweb/shared/pids /usr/local/fooweb/releases/20110116192456/tmp/pids" servers: ["primary.fooweb.com"] [primary.fooweb.com] executing command command finished * executing `deploy:symlink' * executing "rm -f /usr/local/fooweb/current && ln -s /usr/local/fooweb/releases/20110116192456 /usr/local/fooweb/current" servers: ["primary.fooweb.com"] [primary.fooweb.com] executing command command finished ** transaction: commit * executing `deploy:restart' * executing "sudo -p 'sudo password: ' /etc/init.d/apache2 restart" servers: ["primary.fooweb.com"]
![Page 53: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/53.jpg)
/usr/local/fooweb/!"" current -> releases/20110116192316!"" releases# !"" 20110116190608# # !"" application# # !"" log -> /usr/local/fooweb/shared/log# # !"" public# # !"" REVISION# # !"" tmp# !"" 20110116192316# # !"" application# # !"" log -> /usr/local/fooweb/shared/log# # !"" public# # !"" REVISION# # !"" tmp $"" shared
![Page 54: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/54.jpg)
Webistrano
https://github.com/
peritor/webistrano
![Page 55: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/55.jpg)
![Page 56: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/56.jpg)
![Page 57: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/57.jpg)
![Page 58: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/58.jpg)
![Page 59: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/59.jpg)
![Page 60: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/60.jpg)
![Page 61: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/61.jpg)
![Page 62: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/62.jpg)
![Page 63: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/63.jpg)
![Page 64: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/64.jpg)
PACKAGEDRELEASES
Image: http://flickr.com/photos/halfbisqued/2353845688
![Page 65: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/65.jpg)
Fooweb
FoowebMail
FoowebService
Symfony 1.3
PHP 5.2.12
Tomcat 6.0Any SMTP Server
Java 1.6
![Page 66: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/66.jpg)
!"" application# !"" controllers# # $"" home.php# $"" library# $"" Foow# $"" Router.php!"" fooweb.spec!"" public# $"" index.php$"" vhosts $"" fooweb.conf
![Page 67: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/67.jpg)
Summary: Fooweb ApplicationVendor: FoowebName: foowebVersion: 1.0Release: 1Source0: fooweb-%{version}.tar.gzLicense: BSDGroup: FoowebBuildArch: noarchBuildRoot: %{_tmppath}/%{name}-%{version}-buildrootRequires: php%descriptionThis is the Fooweb web application
%prep%setup
![Page 68: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/68.jpg)
%installmkdir -p $RPM_BUILD_ROOT/var/www/foowebmkdir -p $RPM_BUILD_ROOT/etc/httpd/conf.d/cp -r application $RPM_BUILD_ROOT/var/www/foowebcp -r public $RPM_BUILD_ROOT/var/www/foowebcp vhosts/fooweb.conf $RPM_BUILD_ROOT/etc/httpd/conf.d/
%cleanrm -rf $RPM_BUILD_ROOT
%files%dir /var/www%dir /var/www/fooweb%config /etc/httpd/conf.d/fooweb.conf/var/www/fooweb/*
![Page 69: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/69.jpg)
~$ mkdir buildroot buildroot/tmp~$ cat .rpmmacro %packager Fooweb Release Manager%_topdir ~/buildroot%_tmppath ~/buildroot/tmp~$ cd ~/tags~/tags$ tar cvzf fooweb-1.0.tar.gz fooweb-1.0
![Page 70: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/70.jpg)
~$ rpmbuild -ta fooweb-1.0.tar.gz ~$ rpm -qip ~/rpmbuild/RPMS/noarch/fooweb-1.0-1.noarch.rpm Name : fooweb Relocations: (not relocatable)Version : 1.0 Vendor: FoowebRelease : 1 Build Date: Thu 13 Jan 2011 12:26:24 AM PSTInstall Date: (not installed) Build Host: ubuntu.localdomainGroup : Fooweb Source RPM: fooweb-1.0-1.src.rpmSize : 781 License : BSDSignature : (none)Summary : Fooweb ApplicationDescription : This is the Fooweb web application
![Page 71: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/71.jpg)
$ mkdir /var/www/repo$ cd /var/www/repo$ mkdir centox/5/fooweb/{SRPMS,X86_64,i386,noarch}$ cp ~rpmbuild/RPMS/noarch/* centos/5/fooweb/noarch$ cp ~rpmbuild/SRPMS/* centos/5/fooweb/SRPMS$ createrepo -v centos/5/fooweb/noarch/
centos/5/fooweb/noarch/!"" fooweb-1.0-1.noarch.rpm$"" repodata !"" filelists.xml.gz !"" other.xml.gz !"" primary.xml.gz $"" repomd.xml
![Page 72: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/72.jpg)
$ cat /etc/yum.repos.d/fooweb.repo [fooweb_noarch]name = Fooweb Private Repositorybaseurl = http://fooweb.com/repo/centos/5/fooweb/noarchenabled = 1gpgcheck = 0gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fooweb
$ yum updatefooweb_noarch 100% |========| 951 B fooweb_noarch/primary 100% |========| 701 B fooweb_noarch 1/1Setting up Update ProcessNo Packages marked for Update
![Page 73: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/73.jpg)
$ yum info foowebAvailable PackagesName : foowebArch : noarchVersion : 1.0Release : 1Size : 3.3 kRepo : fooweb_noarchSummary : Fooweb ApplicationLicense : BSDDescription: This is the Fooweb web application
![Page 74: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/74.jpg)
![Page 75: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/75.jpg)
<target name="buildrpm" depends="init"> <tar destfile="build/rpm/SOURCES/fooweb.tar.gz" compression="gzip"> <tarfileset dir="${basedir}" prefix="fooweb-1.0"> <include name="*/**" /> <exclude name="build/**" /> </tarfileset> </tar> <copy file="${basedir}/fooweb.spec" tofile="${basedir}/build/rpm/SPECS/fooweb.spec" /> <rpm command="-ba" specFile="fooweb.spec" topDir="${basedir}/build/rpm" cleanBuildDir="true" failOnError="true" /></target>
![Page 76: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/76.jpg)
PACKAGE MANAGEMENT
Image: http://flickr.com/photos/southerncalifornian/2129676744
![Page 77: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/77.jpg)
$ sudo aptitude install puppetmaster 0 packages upgraded, 10 newly installed, 0 to remove and 76 not upgraded.Need to get 3,233kB of archives. After unpacking 13.7MB will be used.
$ sudo aptitude install puppet0 packages upgraded, 5 newly installed, 0 to remove and 76 not upgraded.Need to get 587kB of archives. After unpacking 1,892kB will be used.
Puppethttp://puppetlabs.com
![Page 78: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/78.jpg)
Puppet master
primary fooweb.com
intfooweb.com
seconday fooweb.com
staging fooweb.com
backend fooweb.com
![Page 79: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/79.jpg)
/etc/puppet!"" auth.conf!"" fileserver.conf!"" manifests# $"" site.pp!"" modules# !"" apache2# # $"" manifests# # $"" init.pp# $"" fooweb# !"" files# # $"" fooweb.conf# $"" manifests# $"" init.pp!"" puppet.conf$"" templates
![Page 80: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/80.jpg)
class fooweb { package { "fooweb": ensure => latest, }" file { "/etc/apache2/sites-enabled/fooweb.conf": owner => root, group => root, mode => 0444, source => "puppet:///files/fooweb/files/fooweb.conf", notify => Service["apache2"] }}
modules/fooweb
manifests/init.pp
![Page 81: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/81.jpg)
node "ubuntu.localdomain" { include fooweb include apache2}
class apache2 { service { apache2: ensure => running }
manifests/site.pp
modules/apache2 manifests/init.pp
![Page 82: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/82.jpg)
# puppet agent -o -v --no-daemonizeinfo: Caching catalog for ubuntu.localdomaininfo: Applying configuration version '1295514488'notice: /Stage[main]/Fooweb/Package[fooweb]/ensure: ensure changed 'purged' to 'latest'notice: /Stage[main]/Fooweb/File[/etc/apache2/sites-enabled/fooweb.conf]/ensure: defined content as '{md5}d41d8cd98f00b204e9800998ecf8427e'info: /Stage[main]/Fooweb/File[/etc/apache2/sites-enabled/fooweb.conf]: Scheduling refresh of Service[apache2]notice: /Stage[main]/Apache2/Service[apache2]: Triggered 'refresh' from 1 eventsnotice: Finished catalog run in 3.33 seconds# ls /etc/httpd/conf.d/fooweb.conf /etc/httpd/conf.d/fooweb.conf# ls /var/www/fooweb/application public
![Page 83: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/83.jpg)
MANAGING HOTFIXES
Image: http://flickr.com/photos/moogan/8206134
![Page 84: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/84.jpg)
/trunk
/branches/1.1.3
/tags/1.1.2 /tags/1.1.3
![Page 85: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/85.jpg)
package
run db changes
run db backup
copy code
make code active
![Page 86: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/86.jpg)
package
run db changes
run db backup
copy code
make code active
![Page 87: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/87.jpg)
MANAGING Database ChanGES
Image: http://flickr.com/photos/theplanetdotcom/4878814847
![Page 88: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/88.jpg)
![Page 89: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/89.jpg)
CREATE TABLE `blogpost` ( `id` int(11) auto_increment NOT NULL PRIMARY KEY, `title` VARCHAR(255), `timestamp` DATETIME, `content` TEXT);
--//@UNDO
DROP TABLE `blogpost`;DBDeploy
http://dbdeploy.com
![Page 90: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/90.jpg)
ALTER TABLE `blogpost` ADD `author` varchar(255) NULL;
--//@UNDO
ALTER TABLE `blogpost` DROP `author`;
![Page 91: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/91.jpg)
CREATE TABLE changelog ( change_number BIGINT NOT NULL, delta_set VARCHAR(10) NOT NULL, start_dt TIMESTAMP NOT NULL, complete_dt TIMESTAMP NULL, applied_by VARCHAR(100) NOT NULL, description VARCHAR(500) NOT NULL, PRIMARY KEY(change_number, delta_set));
$ wget http://dbdeploy.googlecode.com/files/dbdeploy-dist-3.0M2-distribution.zip
![Page 92: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/92.jpg)
$ java -cp mysql-connector-java.jar:dbdeploy-cli-3.0M2.jar com.dbdeploy.CommandLineTarget -D com.mysql.jdbc.Driver -d mysql -o delta.sql -u jdbc:mysql://localhost/foowebdb -U root -P ******dbdeploy 3.0M2Reading change scripts from directory dbdeploy.Changes currently applied to database: (none)Scripts available: 1, 2To be applied: 1, 2
![Page 93: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/93.jpg)
-- START CHANGE SCRIPT #1: 1-create-blogposts.sql
CREATE TABLE `blogpost` ( `id` int(11) auto_increment NOT NULL PRIMARY KEY, `title` VARCHAR(255), `timestamp` DATETIME, `content` TEXT);
INSERT INTO changelog (change_number, complete_dt, applied_by, description)VALUES (1, CURRENT_TIMESTAMP, USER(), '1-create-blogposts.sql');
COMMIT;
![Page 94: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/94.jpg)
-- END CHANGE SCRIPT #1: 1-create-blogposts.sql
-- START CHANGE SCRIPT #2: 2-add-author.sql
ALTER TABLE `blogpost` ADD `author` varchar(255) NULL;
INSERT INTO changelog (change_number, complete_dt, applied_by, description) VALUES (2, CURRENT_TIMESTAMP, USER(), '2-add-author.sql');
COMMIT;
-- END CHANGE SCRIPT #2: 2-add-author.sql
![Page 95: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/95.jpg)
<?xml version="1.0" encoding="UTF-8" standalone="no"?><databaseChangeLog [....]> <changeSet author="ianbarber" id="1"> <createTable tableName="blogposts"> <column autoIncrement="true" name="id" type="int(11)"> <constraints nullable="false" primaryKey="true" /> </column> <column name="title" type="varchar(255)" /> <column name="body" type="text" /> <column name="author" type="varchar(255)"/> <column name="date" type="timestamp" /> </createTable> </changeSet> Liquibase
http://liquibase.org
![Page 96: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/96.jpg)
<?xml version="1.0" encoding="UTF-8" standalone="no"?><databaseChangeLog [....] > <include file="v000/master.xml" /></databaseChangeLog>
<?xml version="1.0" encoding="UTF-8" standalone="no"?><databaseChangeLog [....]> <include file="v000/create-blog-posts-1.xml" /></databaseChangeLog>
update.xml
v000/master.xml
![Page 97: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/97.jpg)
# Liquibase propertiesdriver: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost/foowebclasspath: /usr/share/java/mysql-connector-java.jarusername: rootpassword: *******
$ wget http://downloads.sourceforge.net/project/liquibase/Liquibase%20Core/2.0.0/liquibase-2.0.0-bin.zip
liquibase.properties
![Page 98: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/98.jpg)
$ liquibase --changeLogFile=update.xml updateLiquibase Home: /opt/liquibaseINFO 1/18/11 1:32 PM:liquibase: Successfully acquired change log lockINFO 1/18/11 1:32 PM:liquibase: Reading from `DATABASECHANGELOG`INFO 1/18/11 1:32 PM:liquibase: Reading from `DATABASECHANGELOG`INFO 1/18/11 1:32 PM:liquibase: ChangeSet v000/create-blog-posts-1.xml::1::ianbarber ran successfully in 101msINFO 1/18/11 1:32 PM:liquibase: Successfully released change log lockLiquibase Update Successful
![Page 99: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/99.jpg)
class CreateProjects < ActiveRecord::Migration def self.up create_table :projects do |t| t.column :name, :string t.column :description, :text t.column :template, :string t.column :created_at, :datetime t.column :updated_at, :datetime end end
def self.down drop_table :projects endend
![Page 100: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/100.jpg)
ROLLING BACK
Image: http://flickr.com/photos/roolrool/4758613588
![Page 101: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/101.jpg)
<?xml version="1.0" encoding="UTF-8" standalone="no"?><databaseChangeLog [....]> <changeSet author="ianbarber" id="2"> <addColumn tableName="blogposts"> <column name="commenter" type="varchar(255)" /> </addColumn> </changeSet></databaseChangeLog>
$ liquibase --changeLogFile=update.xml updateLiquibase Home: /opt/liquibaseINFO 1/18/11 2:38 PM:liquibase: ChangeSet v000/add_commenter-2.xml::2::ianbarber ran successfully in 136msLiquibase Update Successful
![Page 102: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/102.jpg)
$ liquibase --changeLogFile=update.xml rollbackCount 1Liquibase Home: /opt/liquibaseINFO 1/18/11 2:39 PM:liquibase: Successfully acquired change log lockINFO 1/18/11 2:39 PM:liquibase: Reading from `DATABASECHANGELOG`INFO 1/18/11 2:39 PM:liquibase: Rolling Back Changeset:v000/add_commenter-2.xml::2::ianbarber::(Checksum: 3:cc45ae1014b26f8b35cb70a5fc39a1ae)INFO 1/18/11 2:39 PM:liquibase: Successfully released change log lockLiquibase Rollback Successful
![Page 103: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/103.jpg)
![Page 104: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/104.jpg)
TACTICAL DEPLOYMENTS
Image: http://flickr.com/photos/romainguy/230416692
![Page 105: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/105.jpg)
readslave read slave
Primary DB
Backup 30 MinuteDelay
Replication
E.G. mk_slave_delayhttp://bit.ly/hDWDFi
![Page 106: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/106.jpg)
namespace :deploy do namespace :web do task :disable, :roles => :web do on_rollback { rm "#{shared_path}/system/maintenance.html" }
require 'erb' deadline, reason = ENV['DATE'], ENV['WHY'] maintenance = ERB.new( File.read("./templates/maintenance.erb" )).result(binding)
put maintenance, "#{shared_path}/system/maintenance.html", :mode => 0644 end end
![Page 107: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/107.jpg)
# DATE="16:00 MST" WHY="a database upgrade" cap deploy:web:disable
if (-f $document_root/system/maintenance.html) { rewrite ^(.*)$ /system/maintenance.html last; break;}
![Page 108: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/108.jpg)
caches & proxies sessions links
migrate
warm redirect
![Page 109: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/109.jpg)
sys admins QA
developers
devops
![Page 110: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/110.jpg)
See: Continuous
Deployment In 5
Easy Stepshttp://oreil.ly/13EPgd
Image: http://flickr.com/photos/jurvetson/3961794276
![Page 111: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/111.jpg)
Image: http://flickr.com/photos/rossharmes/4153769740
Feature Flags
![Page 112: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/112.jpg)
0%
25%
50%
75%
100%
Day 1 Day 2 Day 3 Day 4
With Feature Without Feature
Gradual Ramp
![Page 113: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/113.jpg)
Dark Launches
![Page 114: Deployment Tactics](https://reader030.vdocuments.us/reader030/viewer/2022013111/5558230cd8b42a5e468b50a7/html5/thumbnails/114.jpg)
THanks!
Deployment Tactics: Managing code from development to
production
Ian Barber - [email protected] twitter.com/ianbarber | phpir.com