All, Thanks for your replies, very good information.
Christopher, Let me share additional information about my environment: 1) All my servers, production and non-production, are hosted at two data centers (different locations) with a third-party company (A) -Server specs: Mac mini, 16 GB memory and 1 TB HD 2)There is one Tomcat instance per server and all Tomcats are in a cluster, having HTTPD in front -on HTTPD, mod_jk is used -one cluster per each location, multicast is used - I do recall a presentation where not using multicast was mentioned - I need to check my notes 3) The application is developed by a third-party company (B) and uploaded to one of my non-production servers 4) When an application needs to be deployed, I connect (ssh) to the target server(s) and from there, I use scp to get the war file from the non-production server and place the war file into a particular directory; then, I have a script that moves the war file to $CATALINA_BASE and do some additional validations -Autodeploy and Parallel deployment are used -I explored the FarmWebDeployer option years ago but had the war file removed from the cluster when the master node was down… maybe I had a wrong configuration then.. so I will need to check this option again and experiment 5) As per the JAVA_OPTS… -I use it for “-server -Dfile.encoding=UTF-8” -It seems that I can add “-server -Dfile.encoding=UTF-8” to CATALINA_OPTS; I will change and test it. I have an additional question: 1) Would there be any recommendations for not having one Tomcat instance per physical server? ____________ Israel Timoteo > On May 19, 2017, at 2:06 PM, Mark Eggers <its_toas...@yahoo.com.INVALID> > wrote: > > Folks, > > Beware, the following is extremely ugly Groovy. I've changed the names > to protect the guilty (I think). > > Also, the *_SVR and *_URL values are environment values stored centrally > in Jenkins. That way they can be changed easily. They also show up in > the job logs, which means with a little scripting effort I can get a > history of what versions were deployed to what server when. > > Please review and laugh . . . or better yet, proffer improvements :-). > > On 5/19/2017 10:10 AM, Decker, Richard M wrote: >> Mark, >> >>> -----Original Message----- >>> From: Mark Eggers [mailto:its_toas...@yahoo.com.INVALID] >>> Sent: Friday, May 19, 2017 10:44 AM >>> To: Tomcat Users List <users@tomcat.apache.org> >>> Subject: Re: Tomcat on macOS >>> >>> Chris, >>> >>> On 5/19/2017 7:33 AM, Christopher Schultz wrote: >>>> Israel, >>>> >>>> On 5/18/17 10:52 AM, Israel Timoteo wrote: >>>>> Any comments from the community for ... >>>> >>>>> 1) What tools is the community using for simultaneous applications >>>>> deployment on several servers, let’s say more than 20? >>>> >>>> I am using neither of these strategies, but... >>>> >>>> a. FarmWebDeployer [1] >>> >>> Doesn't this require a cluster (and therefore multicast)? That becomes >>> challenging in a cloud environment where there's no multicast easily >>> available. >>> >>>> b. Auto-deploy + scp >>> >>> This would be nice with a little scripting. >>> >>>> >>>> Why in the world are you deploying a web application to 20+ >>>> macos-based servers? Or do you have a Macos client and 20+ >>>> non-macos-based servers? >>>> >>>>> 4) Is JAVA_OPTS required? >>>> >>>> JAVA_OPTS is only required if you require any java opts. Do you >>>> require such options? Usually, when people set JAVA_OPTS they really >>>> want to set CATALINA_OPTS instead. >>>> >>>> Hope that helps, >>>> -chris >>>> >>>> [1] >>>> http://tomcat.apache.org/tomcat-8.0-doc/config/cluster-deployer.html >>> >>> What I do is use Jenkins, Maven, Nexus, and a little Groovy scripting. >>> >>> 1. Maven with the Tomcat Maven Plugin [1] >>> >>> The WAR file is customized (context.xml) based on the target environment. >>> >>> 2. Jenkins >>> >>> The build is run by Jenkins, and the build number (with a little 0 padding >>> via a >>> Groovy script) is tacked onto the WAR name as app##0000nn.war. >> >> I don't mean to hijack the thread, but could you expand on this? Could you >> please provide examples of your Groovy scripts? >> >>> >>> This allows the parallel deploy feature to be used [2]. >>> >>> 3. Nexus >>> >>> This is where all of the base artifacts are stored. Nexus 2 is used >>> currently >>> since Nexus 3 doesn't have the REST API needed to cleanly interact with the >>> Jenkins job via a Groovy script. Maybe I should learn how to write a Nexus >>> plugin to get lists of artifact versions via REST . . . >>> >>> 4. Groovy scripting >>> >>> Groovy is used in Jenkins to do the following: >>> >>> a. Query Nexus to get a list of artifact versions b. Prevent non-production >>> artifacts from landing on production platforms c. Create the final number >>> for >>> parallel deployment >>> >>> To expand this to multiple machines, a set of pipeline jobs could be >>> created. >>> >>> a. Build the customized WAR for the target environment b. Multiple jobs >>> deploy to the servers in the target environment c. Multiple jobs validate >>> the >>> deployment d. Final job sends mail to interested parties with success / >>> failure >>> >>> I know that's a lot of infrastructure. There are certainly things that >>> could be >>> done differently. Ant (with Ivy), or gradle could be used for the builds. A >>> different repository manager could be used (other than Nexus). A different >>> CI / CD system could be used (other than Jenkins). >>> >>> Anything that meets at least the following requirements could be strung >>> together. >>> >>> a. Reliable place to get the WAR file you need to deploy b. Reliable build >>> system that can be automated c. Build system that can deploy to Tomcat d. >>> Testing that the deployment actually worked e. Notification >>> >>> The end result is that some authorized person can log into Jenkins, select a >>> version of an application to deploy, deploy it to the target environment, >>> know that it's been successful (or not), and have notifications >>> automatically >>> sent out. >>> >>> [1] http://tomcat.apache.org/maven-plugin.html >>> [2] >>> https://tomcat.apache.org/tomcat-8.0- >>> doc/config/context.html#Parallel_deployment >>> >>> . . . just my (rather lengthy) 2 cents >>> /mde/ > > // talks to Nexus 2 servers - NOT Nexus 3 > // Gets list of versions for a particular groupId and artifactId > // review and use at your own risk > def prod = > "http://my.nexus.server.com/nexus/service/local/repositories/releases/content" > def snap = > "http://my.nexus.server.com/nexus/service/local/repositories/snapshots/content" > > def groupId = ARTIFACT_GROUP > def artifactId = ARTIFACT_ID > def target = ARTIFACT_TARGET > > def versions = [] > > def locationp = prod + "/" + groupId.replaceAll("\\.","/") + "/" + > artifactId > def locations = snap + "/" + groupId.replaceAll("\\.","/") + "/" + > artifactId > > // get all available SNAPSHOT versions > try { > def scontent = new XmlParser().parseText(new > URL(locations).getText()); > for (contentNode in scontent.data.'content-item') { > if (contentNode.leaf.text() != "true") { > versions.add(contentNode.text.text()) > } > } > } catch (FileNotFoundException e) { > } > > // get all available released versions > try { > def pcontent = new XmlParser().parseText(new > URL(locationp).getText()); > for (contentNode in pcontent.data.'content-item') { > if (contentNode.leaf.text() != "true") { > versions.add(contentNode.text.text()) > } > } > } catch (FileNotFoundException e) { > } > > // no artifacts in Nexus repository > if ( versions.isEmpty() ) { > versions.add("NO-VERSION") > return versions > } > > switch (target) { > // production servers cannot host SNAPSHOT releases > case ["PROD","PROD02","PROD01","PROD03","PROD04","PROD05"]: > def pversions = [] > for (version in versions) { > if (!version.endsWith("SNAPSHOT")) { > pversions.add(version) > } > } > versions = pversions > // no releases in Nexus repository > if (versions.isEmpty()) { > versions.add("NO-VERSION") > } > break > default: > break > } > > sversions = versions.sort().reverse() > return sversions > > // takes artifact versions and creates appropriate Maven parameters > // popcorn needed - scotch recommended - review and use at your own risk > def tname = '' > def url = '' > def svr = '' > def bn = '' > def host = '' > def vurl = '' > def ddate = new Date() > > // target server > if (binding.variables.containsKey('ARTIFACT_TARGET')) { > tname = binding.variables.get('ARTIFACT_TARGET') > switch (tname) { > // production - prod01.server.com > case "PROD01": > if (binding.variables.containsKey('PROD01_URL')) { > url = binding.variables.get('PROD01_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl ="https://prod01.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('PROD01_SVR')) { > svr = binding.variables.get('PROD01_SVR') > } > break > // production - prod02.server.com > case "PROD02": > if (binding.variables.containsKey('PROD02_URL')) { > url = binding.variables.get('PROD02_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = "http://prod02.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('PROD02_SVR')) { > svr = binding.variables.get('PROD02_SVR') > } > break > // production - PROD03.server.com > case "PROD03": > if (binding.variables.containsKey('PROD03_URL')) { > url = binding.variables.get('PROD03_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = "http://prod03.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('PROD03_SVR')) { > svr = binding.variables.get('PROD03_SVR') > } > break > // production - prod04.server.com > case "PROD04": > if (binding.variables.containsKey('PROD04_URL')) { > url = binding.variables.get('PROD04_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = "prod04.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('PROD04_SVR')) { > svr = binding.variables.get('PROD04_SVR') > } > break > // production - prod05.server.com > case "PROD05": > if (binding.variables.containsKey('PROD05_URL')) { > url = binding.variables.get('PROD05_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = "http://prod05.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('PROD05_SVR')) { > svr = binding.variables.get('PROD05_SVR') > } > break > // user acceptance test - uattest.server.com > case "UATTEST": > if (binding.variables.containsKey('UATTEST_URL')) { > url = binding.variables.get('UATTEST_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = "http://uattest.server.com" + '/' + > binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('UATTEST_SVR')) { > svr = binding.variables.get('UATTEST_SVR') > } > break > // internal test - itest.server.com > case "ITEST": > if (binding.variables.containsKey('ITEST_URL')) { > url = binding.variables.get('ITEST_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = host + '/' + binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('ITEST_SVR')) { > svr = binding.variables.get('ITEST_SVR') > } > break > // default is to deploy to internal test > default: > if (binding.variables.containsKey('ITEST_URL')) { > url = binding.variables.get('ITEST_URL') > host = url - ~/:\d{4}\/manager\/text/ > vurl = host + '/' + binding.variables.get('ARTIFACT_ID') > } > if (binding.variables.containsKey('ITEST_SVR')) { > svr = binding.variables.get('ITEST_SVR') > } > break > } > } > > // build number with proper zero padding > if (binding.variables.containsKey('BUILD_NUMBER')) { > bns = binding.variables.get('BUILD_NUMBER') > bni = bns.toInteger() > if (bni < 10) { > bn = '0000' + bni.toString() > } else if (bni < 100) { > bn = '000' + bni.toString() > } else if (bni < 1000) { > bn = '00' + bni.toString() > } else if (bni < 10000) { > bn = '0' + bni.toString() > } else { > bn = bn.toString() > } > } else { > bn = '00001' > } > > return [ TARGET_URL:url, TARGET_SERVER:svr, BN:bn, ARTIFACT_URL:vurl, > DEPLOY_DATE:ddate ] > > The second script creates appropriate environment variables for the > following Maven command: > > mvn -U clean package tomcat7:deploy -Dbuild.number=${BN} > -Dversion.number=${ARTIFACT_VERSION} -Ddeploy.url=${TARGET_URL} > -Ddeploy.server=${TARGET_SERVER} > > The DEPLOY_DATE is used in the email notification, as is the BUILD_USER > Jenkins variable. That way, everyone knows who did bad things to a > server when. > > Sorry for the line wrap. > > . . . just my (ugly scripting) two cents > /mde/ >