114
DEPLOYMENT TACTICS Managing code from development to production Ian Barber - [email protected] twitter.com/ianbarber | phpir.com

Deployment Tactics

Embed Size (px)

DESCRIPTION

The slides from my Deployment Tactics talk at the ThinkVitamin Code Management online conference (http://thinkvitamin.com/online-conferences/code-manage-deploy/).

Citation preview

Page 1: Deployment Tactics

DEPLOYMENT

TACTICS

Managing code from development to production

Ian Barber - [email protected] twitter.com/ianbarber | phpir.com

Page 3: Deployment Tactics

- 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

Change control

identify need

plan change

verify

execute change

deliverclose

Page 5: Deployment Tactics

require release

export code

compare md5

copy to server

restart apache

report back

Page 6: Deployment Tactics

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

Environments

Production Development

staticrobustreliableoptimised

verbosedynamicunstable

experimental

Page 8: Deployment Tactics

Image: http://flickr.com/photos/lejoe/3763218501

The Production Environment

Page 9: Deployment Tactics

Image: http://flickr.com/photos/simononly/4454401446

The Staging Environment

Page 10: Deployment Tactics

Image: http://flickr.com/photos/unfoldedorigami/2374016430

The Integration Environment

Page 11: Deployment Tactics

Image: http://flickr.com/photos/drewnew/511936681

The Development Environment

Page 12: Deployment Tactics

VERSION CONTROL

Image: http://flickr.com/photos/robbie73/4346732208

Page 13: Deployment Tactics

/trunk

/branches/search

/branches/newpage /branches/...

/tags/1.1.2/branches/1.1.2

Page 14: Deployment Tactics

/branches/newpage

/trunk

/branches/search

/branches/...

/tags/1.1.2/branches/1.1.2

Development

Staging Production

Integration

Page 15: Deployment Tactics

master

devel

search feature

release1.1.1

long feature

Page 16: Deployment Tactics

master

devel

search feature

release1.1.1

long feature

Development

Integration

Staging

Production

Page 17: Deployment Tactics

The DEPLoy PROCESS

Page 18: Deployment Tactics

The DEPLoy PROCESS

transparent easy

scalable reliable

flexible

graceful

Page 19: Deployment Tactics

code config

commandspackages

SMTP config apache

vhost

support process

app update

libpng update

cache service

restart service

file perms

Page 20: Deployment Tactics

server

code repository

package repository

deployment controllerserver

config repository

server commands

data

Page 21: Deployment Tactics

server

code repository

package repository

deployment controllerserver

config repository

server commands+ data

data

Page 22: Deployment Tactics

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

#!/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

BUILDS TOOLS

code

assets

tests

build

testresults

docs

release

Page 25: Deployment Tactics

buildtools

Page 26: Deployment Tactics

<?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

<!-- 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

<!-- 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

$ 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 31: Deployment Tactics
Page 32: Deployment Tactics
Page 33: Deployment Tactics
Page 34: Deployment Tactics
Page 35: Deployment Tactics
Page 36: Deployment Tactics

<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

<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
Page 39: Deployment Tactics
Page 40: Deployment Tactics

REMOTE RELEASES

Image: http://flickr.com/photos/scragz/309353618

Page 41: Deployment Tactics

server

deployment controller

server

ssh / scpid_rsa.pub

authorized_keys authorized_keys

ssh-keygen -t rsauser “deploy”

ssh

/ scp

Page 42: Deployment Tactics

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

# 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

# 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

$ 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

[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

$ 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 49: Deployment Tactics

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

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

$ 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

$ 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

/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 55: Deployment Tactics
Page 56: Deployment Tactics
Page 57: Deployment Tactics
Page 58: Deployment Tactics
Page 59: Deployment Tactics
Page 60: Deployment Tactics
Page 61: Deployment Tactics
Page 62: Deployment Tactics
Page 63: Deployment Tactics
Page 64: Deployment Tactics

PACKAGEDRELEASES

Image: http://flickr.com/photos/halfbisqued/2353845688

Page 65: Deployment Tactics

Fooweb

FoowebMail

FoowebService

Symfony 1.3

PHP 5.2.12

Tomcat 6.0Any SMTP Server

Java 1.6

Page 66: Deployment Tactics

!"" application#   !"" controllers#   #   $"" home.php#   $"" library#   $"" Foow#   $"" Router.php!"" fooweb.spec!"" public#   $"" index.php$"" vhosts $"" fooweb.conf

Page 67: Deployment Tactics

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

%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

~$ 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

~$ 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

$ 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

$ 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

$ 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
Page 75: Deployment Tactics

<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

PACKAGE MANAGEMENT

Image: http://flickr.com/photos/southerncalifornian/2129676744

Page 77: Deployment Tactics

$ 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

Puppet master

primary fooweb.com

intfooweb.com

seconday fooweb.com

staging fooweb.com

backend fooweb.com

Page 79: Deployment Tactics

/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

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

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

# 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

MANAGING HOTFIXES

Image: http://flickr.com/photos/moogan/8206134

Page 84: Deployment Tactics

/trunk

/branches/1.1.3

/tags/1.1.2 /tags/1.1.3

Page 85: Deployment Tactics

package

run db changes

run db backup

copy code

make code active

Page 86: Deployment Tactics

package

run db changes

run db backup

copy code

make code active

Page 87: Deployment Tactics

MANAGING Database ChanGES

Image: http://flickr.com/photos/theplanetdotcom/4878814847

Page 88: Deployment Tactics
Page 89: Deployment Tactics

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

ALTER TABLE `blogpost` ADD `author` varchar(255) NULL;

--//@UNDO

ALTER TABLE `blogpost` DROP `author`;

Page 91: Deployment Tactics

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

$ 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

-- 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

-- 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

<?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

<?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 98: Deployment Tactics

$ 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

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

ROLLING BACK

Image: http://flickr.com/photos/roolrool/4758613588

Page 101: Deployment Tactics

<?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

$ 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
Page 104: Deployment Tactics

TACTICAL DEPLOYMENTS

Image: http://flickr.com/photos/romainguy/230416692

Page 106: Deployment Tactics

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

# 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

caches & proxies sessions links

migrate

warm redirect

Page 109: Deployment Tactics

sys admins QA

developers

devops

Page 111: Deployment Tactics

Image: http://flickr.com/photos/rossharmes/4153769740

Feature Flags

Page 112: Deployment Tactics

0%

25%

50%

75%

100%

Day 1 Day 2 Day 3 Day 4

With Feature Without Feature

Gradual Ramp

Page 113: Deployment Tactics

Dark Launches

Page 114: Deployment Tactics

THanks!

Deployment Tactics: Managing code from development to

production

Ian Barber - [email protected] twitter.com/ianbarber | phpir.com