Slide 1

Slide 1 text

Web application deployments with Gradle From the developer's machine to Continuous Deployment Benjamin Muschko Principal Engineer, Gradleware @bmuschko

Slide 2

Slide 2 text

Java Build Tools – Part 2: A Decision Maker’s Comparison of Maven, Gradle and Ant + Ivy "However, as Gradle is still quite new, it does not have such a wide variety of application server integrations as Maven or Ant." January 2014

Slide 3

Slide 3 text

Is

Slide 4

Slide 4 text

deployment is a multifaceted problem

Slide 5

Slide 5 text

Embedded container Dev Machine bundle JVM Process target Embedded Impl.

Slide 6

Slide 6 text

Standalone container Dev Machine deploy JVM Process JVM Process

Slide 7

Slide 7 text

Remote container Dev Machine JVM Process JVM Process Remote Machine deploy

Slide 8

Slide 8 text

Embedded container Gradle plugins

Slide 9

Slide 9 text

Tomcat plugin composition tomcat tomcat-base ‣ adds custom task types ‣ pre-configures task types ‣ adds standard tasks to project ‣ configures default conventions ‣ exposes DSL applies

Slide 10

Slide 10 text

apply plugin: 'tomcat' repositories { mavenCentral() } dependencies { def tomcatVersion = '6.0.29' tomcat "org.apache.tomcat:catalina:${tomcatVersion}", "org.apache.tomcat:coyote:${tomcatVersion}", "org.apache.tomcat:jasper:${tomcatVersion}" } tomcat { httpPort = 8090 httpsPort = 8091 enableSSL = true } Declare Tomcat dependencies Using & configuring standard tasks gradlew tomcatRun Configure tasks via extension tomcat

Slide 11

Slide 11 text

apply plugin: 'tomcat-base' ext { tomcatStopPort = 8081 } task functionalTomcatRun(type: org.gradle.api.plugins.tomcat.tasks.TomcatRun) { stopPort = tomcatStopPort daemon = true } task functionalTomcatStop(type: org.gradle.api.plugins.tomcat.tasks.TomcatStop) { stopPort = tomcatStopPort } task functionalTest(type: Test) { dependsOn functionalTomcatRun finalizedBy functionalTomcatStop } Create enhanced task Using & configuring custom tasks gradlew functionalTest Use embedded container for testing tomcat-base

Slide 12

Slide 12 text

Typical development workflow gradle tomcatRun Hello Wolrd! Spotting a typo gradle compileJava change code restart start reload

Slide 13

Slide 13 text

Rapid development workflow gradle tomcatRun Hello World! Hurray! gradle compileJava change code reload bytecode start reload ‣ built-in capabilities ‣

Slide 14

Slide 14 text

Arquillian plugin features arquillian reads configures container dependencies

Slide 15

Slide 15 text

arquillian { debug = true deployable = file('my/path/arbitraryWebApp.war') containers { jetty { version = '8' type = 'embedded' config = ['bindHttpPort': 8085, 'bindAddress': '127.0.0.1', 'jettyPlus': false] } glassfish { version = '3' type = 'embedded' } } } Path to WAR file Arquillian plugin DSL gradlew arquillianRun Declarative configuration of multiple containers arquillian

Slide 16

Slide 16 text

"Cargo is a thin wrapper that allows you to manipulate various type of application containers (Java EE and others) in a standard way." CARGO

Slide 17

Slide 17 text

Supported containers ...and many more

Slide 18

Slide 18 text

Cargo plugin composition cargo cargo-base ‣ adds custom task types ‣ pre-configures Ant dependencies ‣ adds standard tasks to project ‣ configures default conventions ‣ exposes DSL applies

Slide 19

Slide 19 text

Deploying to a local container JVM Process spawn process deploy 1 2 JVM Process

Slide 20

Slide 20 text

apply plugin: 'cargo' cargo { containerId = 'tomcat7x' port = 9090 local { homeDir = file('/home/dev/tools/apache-tomcat-7.0.42') timeout = 60000 containerProperties { property 'cargo.tomcat.ajp.port', 9099 } } } Configure local container Declare container Using Cargo plugin for local deployments gradlew cargoRunLocal

Slide 21

Slide 21 text

apply plugin: 'cargo' cargo { containerId = 'tomcat7x' port = 9090 deployable { file = file('/home/foo/bar/web-services.war') context = 'web-services' } deployable { file = file('/home/foo/bar/enterprise-app.ear') context = 'enterprise-app' } local { homeDir = file('/home/dev/tools/jboss-as-web-7.0.2.Final') } } Declaring a WAR Deploying multiple artifacts gradlew cargoRunLocal Declaring an EAR

Slide 22

Slide 22 text

File Server extract download Downloading & using container archive local Tomcat installation 1 2

Slide 23

Slide 23 text

apply plugin: 'cargo' cargo { containerId = 'tomcat7x' local { installer { installUrl = 'http://apache.osuosl.org/tomcat/ tomcat-7/v7.0.42/bin/apache- tomcat-7.0.42.zip' downloadDir = file("$buildDir/download") extractDir = file("$buildDir/extract") } } } Configure container installer Declare container Bootstrapping a container installation gradlew cargoRunLocal

Slide 24

Slide 24 text

tomcat1 tomcat1:8888 Deploying to a remote container username/password Remote Machine

Slide 25

Slide 25 text

apply plugin: 'cargo' cargo { containerId = 'tomcat7x' port = 9090 remote { hostname = 'cloud.internal.it' username = 'superuser' password = 'secretpwd' } } Configure remote container Using Cargo plugin for remote deployments gradlew cargoDeployRemote Declare container

Slide 26

Slide 26 text

apply plugin: 'cargo-base' import org.gradle.api.plugins.cargo.tasks.local.CargoRunLocal import org.gradle.api.plugins.cargo.tasks.remote.CargoDeployRemote task myLocalTomcatRun(type: CargoRunLocal) { containerId = 'tomcat7x' homeDir = file('/home/dev/tools/apache-tomcat-7.0.42') } task myLocalJettyRun(type: CargoRunLocal) { containerId = 'jetty9x' homeDir = file('/home/dev/tools/jetty-distribution-9.0.4.v20130625') } task deployToRemoteTomcat(type: CargoDeployRemote) { containerId = 'tomcat7x' hostname = 'cloud.internal.it' port = 9090 username = 'superuser' password = 'secretpwd' } Import plugin custom tasks Create and configure enhanced local container task Developing and configuring your own Cargo tasks gradlew deployToRemoteTomcat Create and configure enhanced remote container task

Slide 27

Slide 27 text

tomcat3 tomcat2 tomcat1 tomcat1:9090 tomcat2:8050 tomcat3:8888 Rolling deployments 1 2 3

Slide 28

Slide 28 text

class RemoteContainer { String name String hostname Integer port String username String password } def containers = [new RemoteContainer(name: 'tomcat1', hostname: 'remote-tomcat1', port: 9090, username: 'admin', password: 's3cr3t'), new RemoteContainer(name: 'tomcat2', hostname: 'remote-tomcat2', port: 8050, username: 'deployer', password: 'qwerty'), new RemoteContainer(name: 'tomcat3', hostname: 'remote-tomcat3', port: 8888, username: 'su', password: 'powerful')] Declaring remote container configuration Container class representation List of container instances Avoid defining passwords in build script

Slide 29

Slide 29 text

apply plugin: 'cargo-base' import org.gradle.api.plugins.cargo.tasks.remote.CargoDeployRemote containers.each { config -> task "deployRemote${config.name.capitalize()}"(type: CargoDeployRemote) { description = "Deploys WAR to remote Tomcat '${config.name}'." containerId = 'tomcat7x' hostname = config.hostname port = config.port username = config.username password = config.password } } task deployToAllRemoteTomcats { dependsOn containers.collect { "deployRemote${it.name.capitalize()}" } description = 'Deploys to all remote Tomcat containers.' group = 'deployment' } Dynamic deployment task creation Dynamic creation of tasks Use container configuration Rolling deployment aggregation task

Slide 30

Slide 30 text

Test UAT Prod –Penv=prod –Penv=uat –Penv=test Deploying to multiple environments

Slide 31

Slide 31 text

Problem con$nuous  hot  deployment   will  inevitability  lead  to  an OutOfMemoryError: PermGen space

Slide 32

Slide 32 text

tomcat1 Managing container lifecycle stop container start container 1 2 3

Slide 33

Slide 33 text

Remote daemon process Remote Machine configure send command

Slide 34

Slide 34 text

import org.gradle.api.plugins.cargo.tasks.daemon.CargoDaemonStop import org.gradle.api.plugins.cargo.tasks.daemon.CargoDaemonStart ext { tomcat7HandleId = 'tomcat7' tomcat7Hostname = 'remote-tomcat1' } task cargoDaemonStop(type: CargoDaemonStop) { handleId = tomcat7HandleId hostname = tomcat7Hostname } task cargoDaemonStart(type: CargoDaemonStart) { handleId = tomcat7HandleId hostname = tomcat7Hostname } cargoDaemonStart.mustRunAfter cargoDaemonStop cargoRedeployRemote.dependsOn cargoDaemonStop, cargoDaemonStart Restarting container before deployment Unique identifier configured in daemon web application Make container restart a task dependency of a redeployment gradlew cargoRedeployRemote

Slide 35

Slide 35 text

Deployment is often more than just copying a file

Slide 36

Slide 36 text

This is where Cargo leaves you stranded!

Slide 37

Slide 37 text

Fine-grained control with SSH ext.tomcatRemoteDir = '/opt/apache-tomcat-7.0.42' task shutdownTomcat(type: SshExec) { commands = "sudo -u tomcat $tomcatRemoteDir/bin/shutdown.sh" } task deleteTomcatWebappsDir(type: SshExec, dependsOn: shutdownTomcat) { command = "sudo -u tomcat rm -rf $tomcatRemoteDir/webapps/myapp" } task deleteTomcatWorkDir(type: SshExec, dependsOn: shutdownTomcat) { command = "sudo -u tomcat rm -rf $tomcatRemoteDir/work" } ... task startupTomcat(type: SshExec, dependsOn: copyWarToWebappsDir) { command = "sudo -u tomcat $tomcatRemoteDir/bin/startup.sh" } Custom task wrapping Ant SSH Chaining of individual commands

Slide 38

Slide 38 text

Remote command execution with plugin apply plugin: 'ssh' remotes { localhost { host = 'localhost' user = System.properties['user.name'] identity = file("${System.properties['user.home']}/.ssh/id_rsa") } } task deploy(type: SshTask) { session(remotes.localhost) { execute('sudo -u tomcat $tomcatRemoteDir/bin/shutdown.sh') execute('sudo -u tomcat rm -rf $tomcatRemoteDir/webapps/myapp') ... } } ssh Define hosts configuration Execute SSH commands against remote

Slide 39

Slide 39 text

Deployment as part of a build pipeline

Slide 40

Slide 40 text

Prod Continuous Delivery UAT Automated Acceptance  Tests manual deployment push-button release

Slide 41

Slide 41 text

Prod Continuous Deployment UAT Automated Acceptance  Tests automated deployment ...many times a day

Slide 42

Slide 42 text

Build once, deploy many Test UAT Prod publish deploy

Slide 43

Slide 43 text

Push  deployment 1.0.34 1.0.32 1.0.33 1.0.34 1.0.34 Test UAT Prod

Slide 44

Slide 44 text

Pull  deployment 1.0.34 1.0.32 1.0.33 1.0.34 Test UAT Prod trigger

Slide 45

Slide 45 text

References Gradle Tomcat Plugin https://github.com/bmuschko/gradle-tomcat-plugin Gradle Cargo Plugin https://github.com/bmuschko/gradle-cargo-plugin Gradle Arquillian Plugin https://github.com/arquillian/arquillian-gradle-plugin Gradle SSH Plugin https://github.com/int128/gradle-ssh-plugin

Slide 46

Slide 46 text

> gradle askQuestions :askQuestions BUILD SUCCESSFUL Total time: 300 secs