Deploy automático Websphere

Tempo de leitura: menos de 1 minuto

Hoje irei falar do primeiro grande desafio que tive quando ainda era estagiária na Synchro, onde trabalho. Resolvi falar dele pelo fato de ter sido bastante difícil encontrar as informações necessárias, mesmo no Google, e acredito que assim talvez este post possa a vir a ajudar alguém.

O Requisito

Precisávamos que fosse feita a atualização automática de nossa aplicação web em um servidor com o Websphere. O que diminuiria o tempo gasto (podia demorar mais de 1 hora quando era feito manualmente) com o deploy, como também para que fossem executados os teste de regressão (usando Selenium) diariamente.

O Desafio

Não sabíamos se era possível fazer todas as configurações do Websphere que precisávamos por meio de tasks (tarefas) ANT ou Gradle.

A Solução

Achei em um site estrangeiro (nem irei mais lembrar qual era) que eu poderia criar as tasks para configurar a “Detecção de Carregamento e Atualização de Classe” do .EAR <configClassloaderApp> e do .WAR <configClassloaderViewer>. Mas não foi muito simples, pois em tese era somente escrever as tasks no Gradle, o que teria sido lindo, mas o Websphere não cooperou muito, pois era necessário logar com o “admin” do Websphere para executar os comandos.

O pulo do gato foi colocar as tasks do Websphere em um arquivo .XML, o qual é transferido (1) para o servidor do Websphere todas as vezes que solicitado o deploy (até porque é possível que as configurações necessárias mudem, devendo, portanto, ser substituido em cada execução), então a partir das tasks no Gradle envio os comandos SSH (2) que executarão as que estão no .XML.

(1) Transferência do arquivo .XML para o servidor remoto

[code language=”groovy”]ant.scp(file:${localPath}/build.xml,
remotetofile:${user}:${password}@${host}:${wsInstallPath}/build.xml,
trust:true)[/code]

(2) Task que executa um comando SSH, o qual solicita a execução de uma task contida no .XML

[code language=”groovy”]ant.sshexec(username: ${user},
password:${password},
host:${host},
command:${wsInstallPath}/ws_ant.sh -f ${wsInstallPath}/build.xml stopServer,
trust:true,
failonerror:true,
outputproperty:saidaComando)[/code]

Como puderam reparar, na linha 4 mandei o ws_ant.sh executar o stopServer, este comando foi implementado no .XML e logo mostrarei como montar esse arquivo.

Algumas observações:

  • O ws_ant.sh, ele vem com o Websphere e estará no diretório de instalação.
  • A própria IBM fala que executar tasks do Websphere fora do ws_ant.sh não é suportado e nem recomendado, ou seja, está aqui um dos motivos desta automação ser um pouco mais complexa.

Agora sobre as tasks que serão feitas no .XML, primeiro temos que defini-las:

[code language=”xml”]<taskdef name="wsStopApp" classname="com.ibm.websphere.ant.tasks.StopApplication"/>
<taskdef name="wsStartApp" classname="com.ibm.websphere.ant.tasks.StartApplication"/>
<taskdef name="wsUninstallApp" classname="com.ibm.websphere.ant.tasks.UninstallApplication"/>
<taskdef name="wsInstallApp" classname="com.ibm.websphere.ant.tasks.InstallApplication"/>
<taskdef name="wsStartServer" classname="com.ibm.websphere.ant.tasks.StartServer"/>
<taskdef name="wsStopServer" classname="com.ibm.websphere.ant.tasks.StopServer"/>
<taskdef name="wsadmin" classname="com.ibm.websphere.ant.tasks.WsAdmin"/>[/code]

Em seguida, definir as propriedades que iremos utilizar, que no caso serão o .EAR, o .WAR, a Raiz de Contexto e a home do Websphere, como consta abaixo, mas lembrando que esses caminhos podem ser diferentes:

[code language=”xml”]<property name="EAR" value="/d/IBM/WebSphere/AppServer/bin/app.ear"/>
<property name="WAR" value="/d/IBM/WebSphere/AppServer/bin/viewer.war"/>
<property name="contextroot" value="/viewer"/>
<property name="wasHome" value="/d/IBM/WebSphere/AppServer/"/>[/code]

Agora faremos as tasks, começando pelas mais críticas e complexas, que são justamente as de Detecção de Carregamento e Atualização de Classe:

EAR

[code language=”xml”]<target name="configClassloaderApp">
<wsadmin wasHome="${wasHome}" command="set dep [$AdminConfig getid /Deployment:app/]; set depObject [$AdminConfig showAttribute $dep deployedObject]; $AdminConfig create Classloader $depObject {{mode PARENT_LAST}}; $AdminConfig save" lang="jacl" failonerror="true">
</wsadmin>
</target>[/code]

WAR

[code language=”xml”]<target name="configClassloaderViewer">
<wsadmin wasHome="${wasHome}" command="set dep [$AdminConfig getid /Deployment:viewer.war/]; set depObject [$AdminConfig showAttribute $dep deployedObject]; $AdminConfig create Classloader $depObject {{mode PARENT_LAST}}; $AdminConfig save; set deploy [$AdminConfig getid /Deployment:viewer.war/]; set deployedObj [$AdminConfig showAttribute $deploy deployedObject]; $AdminConfig modify $deployedObj {{warClassLoaderPolicy SINGLE}}; $AdminConfig save" lang="jacl" failonerror="true">
</wsadmin>
</target>[/code]

Repare no comando! Verá que é usada uma linguagem não muito comum, a Jacl, que é uma implementação do TCL (Tool Command Language). Sendo este mais um fator que complica a automação, afinal estamos agora lidando com 3 linguagens distintas: Groovy, XML e Jacl. Mas vale lembrar que para implementar o comando também é suportado o Jython, o qual é mais recomendado, porém na época eu não havia encontrado muitos exemplos em Jython e não conhecia bem nenhuma das duas alternativas (me deem um desconto, isso foi há 3 anos, eu ainda era estagiária rsrsrs).

Para contextualizar, essas configurações são referentes as que aparecem na imagem abaixo:

websphere

Note que no .EAR configurei apenas a Ordem do Carregador (mode, linha 6 no EAR), pois nele a Política do Carregador usarei a configração padrão (PARENT_FIRST). Enquanto que para o WAR configurei ambos. E só para constar, a configuração padrão da Política do Carregador é MULTIPLE. Para constar, essas são configurações feitas no Websphere quando se está fazendo um deploy manual, por meio delas configura-se como o servidor de aplicação irá carregar as classes e os .JARs.

Outras duas tasks que você precisará e que possuem um pouco mais de detalhes, são: installApp e installViewer, elas cumprem o papel de carregar/instalar o .EAR (installApp) e o .WAR (installViewer) no Websphere. Veja a seguir como configurá-las:

[code language=”xml”]<target name="installApp">
<wsInstallApp wasHome="${wasHome}" ear="${EAR}" options="-node nodeHost -server server1 -usedefaultbindings -distributeApp yes -useMetaDataFromBinary no -preCompileJSPs true -MapWebModToVH {{.* .* default_host }} -MapModulesToServers {{.* .* server1}} -createMBeansForResources" failonerror="true"/>;
</target>[/code]

[code language=”xml”]<target name="installViewer">
<wsInstallApp wasHome="${wasHome}" ear="${WAR}" options="-node nodeHost -appname viewer.war -server server1 -usedefaultbindings -distributeApp yes -useMetaDataFromBinary no -preCompileJSPs true -MapWebModToVH {{.* .* default_host }} -MapModulesToServers {{.* .* server1}} -createMBeansForResources -contextroot ${contextroot}" failonerror="true"/>
</target>[/code]

Com estas duas não poderei ajudar muito, cabe a você pesquisar qual a configuração necessária para o seu deploy, acredito que o Google irá ajudar mais, até porque muitos atributos são opcionais.

As próximas tasks também são necessárias, mas são mais simples. São nesta ordem: stopServer (parar o servidor Websphere), startServer (iniciar o servidor Websphere), stopApp (parar a instância do .EAR), stopViewer (parar a instância do .WAR), startApp (iniciar a instância do .EAR), startViewer (iniciar a instância do .WAR), uninstallApp (desinstala/remove o .EAR) e uninstallViewer (desinstala/remove o .WAR).

[code language=”xml”]<target name="stopServer">
<wsStopServer server="server1" noWait="false" quiet="false" trace="true" wasHome="${wasHome}" failonerror="false" fileEncoding="UTF8"/>
</target>[/code]

[code language=”xml”]<target name="startServer">
<wsStartServer server="server1" noWait="false" quiet="false" trace="true" wasHome="${wasHome}" failonerror="false" fileEncoding="UTF8"/>
</target>[/code]

[code language=”xml”]<target name="stopApp">
<wsStopApp wasHome="${wasHome}" server="server1" application="app" node="nodeHost" failonerror="false"/>
</target>[/code]

[code language=”xml”]<target name="stopViewer">
<wsStopApp wasHome="${wasHome}" server="server1" application="viewer.war" node="nodeHost" failonerror="false"/>
</target>[/code]

[code language=”xml”]<target name="startApp">
<wsStartApp wasHome="${wasHome}" application="app" failonerror="true"/>
</target>[/code]

[code language=”xml”]<target name="startViewer">
<wsStartApp wasHome="${wasHome}" application="viewer.war" failonerror="true"/>
</target>[/code]

[code language=”xml”]<target name="uninstallApp">
<wsUninstallApp wasHome="${wasHome}" application="app" failonerror="false"/>
</target>[/code]

[code language=”xml”]<target name="uninstallViewer">
<wsUninstallApp wasHome="${wasHome}" application="viewer.war" failonerror="false"/>
</target>[/code]

A task a seguir é para auxiliar nosso trabalho no Gradle, mas não é necessária, pois ela apenas junta as tasks acima para serem chamadas de uma vez no Gradle. Mas lembre-se que se não quiser colocá-la no .XML, precisará fazer uma chamada para cada task no Gradle.

[code language=”xml”]<target name="installWebSphere7">
<antcall target="startServer"/>
<antcall target="stopApp"/>
<antcall target="stopViewer"/>
<antcall target="uninstallApp"/>
<antcall target="uninstallViewer"/>
<antcall target="installApp"/>
<antcall target="installViewer"/>
<antcall target="configClassloaderApp"/>
<antcall target="configClassloaderViewer"/>
<antcall target="startApp"/>
<antcall target="startViewer"/>
</target>[/code]

Por fim, deixo a dica de no Gradle criar uma task que inicialmente chame a stopServer do .XML. Em seguida as tasks que transferem os arquivos .EAR e .WAR para o servidor do Websphere, para então chamar a task installWebSphere7, como mostrado a seguir:

[code language=”groovy”]ant.sshexec(username: "${user}",
password: "${password}",
host: "${host}",
command: "${wsInstallPath}/ws_ant.sh -f ${wsInstallPath}/build.xml stopServer",
trust: "true",
failonerror: "true",
outputproperty: "saidaComando1")[/code]

[code language=”groovy”]def earPath = fileTree(dir: "${localPath}", include: ‘*.ear’).files.find().absolutePath
ant.scp(file: earPath, remoteTofile: "${user}:${password}@${host}:${wsInstallPath}/app.ear", trust: "true")[/code]

[code language=”groovy”]ant.scp(remotetodir: "${user}:${password}@${host}:${wsInstallPath}", trust: "true") {
fileset(dir: "${warDir}", includes: "*.war")[/code]

[code language=”groovy”]ant.sshexec(username: "${user}",
password: "${password}",
host: "${host}",
command: "${wsInstallPath}/ws_ant.sh -f ${wsInstallPath}/build.xml installWebSphere7",
trust: "true",
failonerror: "true",
outputproperty: "saidaComando2")[/code]

Algumas Considerações

  • Esta automação está configurada para o Websphere 7, mas quando foi feita pela primeira vez era para o Websphere 6, podendo haver pequenas diferenças nas configurações. Porém ainda não fizemos para o Websphere 8, mas acredito que se mudar alguma coisa, também não será muita.
  • Aqui usamos o Gradle, mas nada impede de ser feito em ANT, até porque se você reparar, as tasks SCP e SSHEXEC são na verdade do ANT.
  • A automação foi feita considerando que o Websphere está instalado em um ambiente remoto, mas é possível fazer para um ambiente local, na verdade será até mais simples.

É isso pessoal, espero que tenham gostado e que sirva de alguma ajuda. Qualquer elogio, reclamação ou sugestão podem deixar nos comentários. Até a próxima!

 

Referências

  1. IBM Knowledge Center – Using Ant to automate tasks (http://www.ibm.com/support/knowledgecenter/SSAW57_7.0.0/com.ibm.websphere.nd.doc/info/ae/ae/tovr_ant.html)
  2. IBM Knowledge Center – Using wsadmin scripting with Jacl (https://www.ibm.com/support/knowledgecenter/was_beta/com.ibm.websphere.base.doc/ae/cxml_jacl.html)
  3. IBM Knowledge Center -Modifying WAR class loader policies for applications using wsadmin scripting (https://www.ibm.com/support/knowledgecenter/was_beta/com.ibm.websphere.base.doc/ae/txml_warclass.html)

Nenhum comentário


  1. Debby congratulations on the initiative, this information is precious!

    Responder

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *