Glassfish 4.1, JAX-RS, Jersey, ja Jackson JSON-serializer

Jep, päivitin vähän servereitä Glassfish 4.1 versioon, ja vastaan tuli mielenkiintoinen bugi: JAX-RS palvelut lakkasivat toimimasta ja antoivat sensijaan kaikenlaisia herjoja, niistä ehkä mielenkiintoisin:

Severe: Error occurred when processing a response created from an already mapped exception.
Warning: StandardWrapperValve[com.qpr.entice.common.ApplicationConfig]: Servlet.service() for servlet com.mycompany.jaadajaada.ApplicationConfig threw exception
java.lang.ClassNotFoundException: com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found by com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider [129]

Glassfish käyttää oletuksena Moxya JSON serialisointiin, mutta itse käytän Jacksonia suorituskyvyn ja ominaisuuksien johdosta. Mielenkiintoista kyllä Glassfish toimitetaan osittaisin Jackson kirjastoin joten sen käyttöönotto on niinkin helppoa kuin aktivoida se config tiedostossa, tähän tapaan:

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {

  public ApplicationConfig() {
    packages(true,
       "com.mycompany.jaadajaada",
       "com.wordnik.swagger.jersey.listing");
    register(JacksonFeature.class);
  }
}

Eli rekisteröidään JacksonFeature – ja samantien käytössä on Jackson kirjastot. Aiemmassa Glassfish 4.0:ssa tämä pelasi hienosti – mutta 4.1 antaa ylläolevaa virheilmoitusta. Kokeilin myös jättää JacksonFeaturen pois ja testata pelkkää Moxya mutta sieltä tuli ihan omat herjansa. (Tuossa muuten näkyy myös Swagger joka automaattisesti dokumentoi REST APIt – ja käyttää Jackson kirjastoja)

Mielenkiintoista virheilmoituksessa on, että se viittaa puuttuvaan tiedostoon joka itselläkin oli kirjastopoluissa – mutta käy ilmi kaksi asiaa: se pitää olla Glassfishin alla, ja sen pitää olla oikea versio. Tiedosto joka uupuu on jackson-module-jaxb-annotations-2.3.2.jar, sen pitäisi olla glassfishin alla kansiossa modules, ja tosiaan versionumero 2.3.2, ei uusin. Tämän päälle kun putsaa osgi-cachen ja buuttaa serverin, saa taas REST JSON palvelut takaisin. 😉

Eli aika spesifi pulma. Aika noloa että Glassfishistä on hiljalleen turvonnut kohtuullisen monoliittinen mötikkä jossa on paljon kirjastoriippuvuuksia joita oma sovellus ei pysty helposti ylikirjoittamaan. JBOSS muistuu mieleen äärimmäisen modularisuutensa ansiosta, mutta muistelen kyllä sielläkin olleen aina uuden julkaisun kohdalla kirjastohaasteita. Mutta siellä on esim. web services implementaatio mukavan helposti vaihdettavissa.

Mutta, minä taidan digressoida 😉 Tässä joka tapauksessa virheilmoitus, ja korjaus, ja tuossa linkkiä keskusteluun samasta pulmasta:

https://java.net/jira/browse/GLASSFISH-21141

Teoriassa on mahdollista tehdä korjaus siistimminkin, autodeploy/bundles kansioon asennettavana paikkana joka ei muuta serverin toimintaa, mutta itse en saanut sitä vaihtoehtoa toimimaan.

Mainokset

Glassfish remote deployment

Olen viritellyt Glassfish serveriklusterin deploymentin parissa. Ideana on saada etäkoneelta buildi, esim. Jenkins tai kehittäjäkone, tipauttamaan nätisti buildin tuotokset serverille jossa niitä voidaan testata ja demoilla.

Tähän on moniakin vaihtoehtoja:

– Glassfish serverissä on autodeploy-kansio, eli jos sinne siirtää tiedoston esim. scp:llä tai ftp:llä, se nousee itsestään pystyyn

– Deployn voi tietysti tehdä käsin, glassfish hallintanäyttöjen kautta

– Cargo plugin on maineikas sen suhteen, että se osaa asennella monellekin eri serverille kun vain kerrotaan miten

– Asadmin työkalu toimii komentoriviltä, se kykenee myös asentamaan tuotoksia

– Joku muu jonka olen unohtanut :p

Tuosta listasta kokeilen ensiksi Cargo pluginia. Se vaikutti muuten lupaavalta paitsi ei toiminut ollenkaan. Ilmeisesti pulmia teeman deploy/redeploy välillä, ja monessa lähteessä mainittu force=true ei tuntunut auttavan. Ongelma siis että jos serverillä ei ole sovellusta, pitäisi tehdä deploy, myöhemmillä kerroilla taas redeploy. En saanut tätä kuitenkaan toimimaan. Sain kivoja nullpointer exceptioneitä Glassfish nelosen kanssa.

Noista tiedostopohjaisista kujeista en ole suunnattoman innostunut, eli käytän niitä vasta jos on pakko. Tiedostojen kanssa on ongelmia mm. lukituksen/säikeiden ja transaktionaalisen eheyden kanssa siirrossa, sekä jälkien putsauksen kanssa.

Asadmin työkalua tuli käyteltyä ja virittelin sen päälle maven exec tehtävän joka osaa sitä ajaa. En ole vielä aivan tyytyväinen vakauteen, mutta toistaiseksi riittävä ratkaisu.

Asadminilla onnistuu .war paketin asennus verkon yli serverille esim. näin:

c:\glassfish4\bin\asadmin deploy --host devserver.mycompany.com --force=true --availabilityenabled=true --target cluster1 --virtualservers server c:\projects\MyProject\target\MyDeliverable.war

Tämä komento hoitaa myös redeployn. Tämä kuitenkin edellyttää että komennon ajavalla käyttäjällä on salasanatiedot jo talletettuna profiiliinsa. Saamme tästä hieman geneerisemmän näin:

c:\glassfish4\bin\asadmin --user admin --passwordfile c:\glassfish4\password --host devserver.mycompany.com deploy  --force=true --availabilityenabled=true --target cluster1 --virtualservers server  c:\projects\MyProject\target\MyDeliverable.war

Tässä koukataan salasanat salasanatiedostosta, joka on aluksi Glassfish serverin kansiossa (paikallinen asennus, jotta saamme asadmin työkalun käyttöön). Sen voi suojata haluamallaan tavalla.

Tältä näyttäisi mavenin exec plugin:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.3.1</version>
  <configuration>
    <executable>${glassfish.asadmin.executable}</executable>
    <arguments>
      <argument>--user</argument>
      <argument>${glassfish.user}</argument>
      <argument>--passwordfile</argument>
      <argument>${glassfish.password.file}</argument>
      <argument>--host</argument>
      <argument>${glassfish.remote.host}</argument> 
      <argument>deploy</argument> 
      <argument>--force=true</argument>
      <argument>--availabilityenabled=true</argument>
      <argument>--target</argument>
      <argument>cluster1</argument>
      <argument>--virtualservers</argument>
      <argument>server</argument>
      <argument>${project.build.directory}/${project.build.finalName}.war</argument>
    </arguments>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>exec</goal>
      </goals>
    </execution>
  </executions>
</plugin>

 

Ja tältä näyttävät tuossa käytetyt propertyt:

<glassfish.asadmin.executable>c:/glassfish4/bin/asadmin.bat</glassfish.asadmin.executable>
 <glassfish.user>admin</glassfish.user>
 <glassfish.password.file>${basedir}/password</glassfish.password.file>
 <glassfish.remote.host>devserver.mycompany.com</glassfish.remote.host>

Ja propertythän voi taas pistää profiilien alle, samoin kuin muutenkin exec konfiguraatiot, eli näillä voi tarvittaessa joka devaaja tai joka ympäristö asennella eri serverille eri tiedoilla.

JAX-RS ja Swagger: Helposti dokumentoity REST API

Hetken aikaa jo on tykyttänyt mietintämyssyn alla miten REST rajapintoja voisi helposti dokumentoida – WSDL kun sieltä uupuu (Ja WADL:ia ei kukaan halua). Kuitenkin pitäisi pystyä kertomaan ja rekisteröimään missä osoitteissa on palvelua, mitä ne tekevät, miten niitä kutsutaan, jne, muutenkin kuin testeillä ja esimerkeillä.

Törmäsin mukavaan jersey-yhteensopivaan laajennukseen nimeltä jersey-swagger. Sovitin tätä Glassfishin versioon, jossa pyörii Jersey 2, ja sainkin jotain aikaan. Tässä muistiinpanoja:

Käyttöönotto vaatii muutaman askelen: Ensiksi täytyy Mavenissä tai Gradlessa ottaa käyttöön swagger jersey versiolle kaksi. Samalla on tuikitärkeää määrittää pari exclusionia, tai serverille päätyy duplikaatteja hieman eri versioista ja tulee mukavia poikkeuksia logista. Tässä toimivaksi testattu dependency Glassfish neloselle:

 <dependency>
   <groupId>com.wordnik</groupId>
   <artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
   <version>1.3.7</version>
   <exclusions>
     <exclusion>
       <groupId>org.glassfish.jersey.media</groupId>
       <artifactId>jersey-media-multipart</artifactId>
     </exclusion>
     <exclusion>
       <groupId>org.glassfish.jersey.containers</groupId>
       <artifactId>jersey-container-servlet-core</artifactId> 
     </exclusion>
   </exclusions>
 </dependency>

Hyvä tarkistaa tässä välissä että vanhat palvelut edelleen toimivat, jos niitä on. Seuraavaksi rekisteröidään Swagger servicet. Tämän voi tehdä xml:ssä – mutta itse käytän ResourceConfig-luokkaa ja annotaatioita:

@ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("my.own.resources","com.wordnik.swagger.jersey.listing");
 }
}

Noin, sieltä aktivoituu tosiaan pari resurssiluokkaa, automaattisesti /rest/api-docs osoitteella. Seuraavaksi rekisteröidään Swagger servlet. Tämänkin voi tehdä annotaatioilla mutta kun sattui olemaan vielä web.xml (se on ainoa paikka jossa voi tehdä distributable-setin ha klusteriin) – tein sen siellä näin:

 <servlet>
 <servlet-name>JerseyJaxrsConfig</servlet-name>
 <servlet-class>com.wordnik.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
 <init-param>
 <param-name>api.version</param-name>
 <param-value>1.0.0</param-value>
 </init-param>
 <init-param>
 <param-name>swagger.api.basepath</param-name>
 <param-value>http://localhost:8080/rest/api-docs</param-value>
 </init-param>
 <load-on-startup>2</load-on-startup>
 </servlet>

Nyt alkaa jo melkein tapahtumaan. Seuraavaksi pitää varustaa ainakin yksi palvelu @Api-annotaatiolla, metodeille voi pistää @ApiOperation, ja parametreille ja paluuarvoille @ApiModel annotaatioita. Jotta saat jotain näkyviin tarvitset ainakin yhden @Api-annotaatiolla varustetun palvelun, jolla on ainakin yksi @ApiOperation annotaatiolla varustettu operaatio.

Tähän tapaan:

@Path("/pet")
@Api(value = "/pet", description = "Operations about pets")
@Produces({"application/json", "application/xml"})
public class PetResource {
  ...
@GET
@Path("/{petId}")
@ApiOperation(value = "Find pet by ID", notes = "More notes about this method", response = Pet.class)
@ApiResponses(value = {
  @ApiResponse(code = 400, message = "Invalid ID supplied"),
  @ApiResponse(code = 404, message = "Pet not found") 
})
public Response getPetById(
    @ApiParam(value = "ID of pet to fetch", required = true) @PathParam("petId") String petId)
    throws WebApplicationException {
@ApiModel(value = "A pet is a person's best friend")
@XmlRootElement(name = "Pet")
public class Pet {
  @XmlElement(name = "status")
  @ApiModelProperty(value = "Order Status", required=true, allowableValues = "placed,approved,delivered")
  public void setStatus(String status) {
    this.status = status;
  }
  public String getStatus() {
    return status;
  }

Sitten voitkin käynnistellä serverin, ja kokeilla mitä löytyy kontekstin alta osoitteella /api-docs, /api-docs/myservicename, jne – tässä tietysti myservicename on joku palvelu johon olet noita Swagger-annotaatioita ripotellut.

Tuolta löytyy myös esimerkkiä live-sisällöstä, niin kauan kuin se tietysti on pystyssä:

http://petstore.swagger.wordnik.com/api/api-docs

Näyttäisi tämmöiseltä:

{"apiVersion":"1.0.0","swaggerVersion":"1.2","apis":[{"path":"/pet","description":"Operations about pets"},{"path":"/user","description":"Operations about user"},{"path":"/store","description":"Operations about store"}],"authorizations":{"oauth2":{"type":"oauth2","scopes":[{"scope":"write:pets","description":"Modify pets in your account"},{"scope":"read:pets","description":"Read your pets"}],"grantTypes":{"implicit":{"loginEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/dialog"},"tokenName":"access_token"},"authorization_code":{"tokenRequestEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/requestToken","clientIdName":"client_id","clientSecretName":"client_secret"},"tokenEndpoint":{"url":"http://petstore.swagger.wordnik.com/oauth/token","tokenName":"auth_code"}}}}},"info":{"title":"Swagger Sample App","description":"This is a sample server Petstore server.  You can find out more about Swagger \n    at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger.  For this sample,\n    you can use the api key \"special-key\" to test the authorization filters","termsOfServiceUrl":"http://helloreverb.com/terms/","contact":"apiteam@wordnik.com","license":"Apache 2.0","licenseUrl":"http://www.apache.org/licenses/LICENSE-2.0.html"}}

Törmäsin myös toiseen aika metkaan standardiin – ALPS. Sitä näyttäisi osaavan Spring Framework hyvin, mutta ainakan nyky-jerseystä ei löytynyt helppoa ratkaisua siihen. Pidän tätäkin kuitenkin silmällä. Looking good!

http://www.dzone.com/links/r/spring_data_rest_now_comes_with_alps_metadata.html

http://alps.io/

Näistä linkeistä löytyy vähän lisää ideoita. Varovaisuutta vanhemman Swagger-version kanssa!

https://github.com/wordnik/swagger-core/wiki/Java-JAXRS-Quickstart

 

 

High Availability Mysql: Galera Cluster ja haproxy

Kun sovelluspalvelin on vikasietoinen ja vakaa, on aika tuunailla tietokantakerrosta. Vikasietoisessa järjestelmässä olisi suotavaa että myös tietokannasta on useampi kopio, jotta tietoa ja tapahtumia ei katoa vaikka tapahtuisi normimaanantai.

Sain vinkin Galera klusterituotteesta jolla Mysql kannat saa replikoimaan nätisti. Sen eteen tarvitaan joku kuormantasaus, ja testailemani haproxy vaikutti enemmän kuin riittävältä siihen. Lähtökohtana tulisi olla vähintään kolmen noden rinki, jotta enemmistö voi ristiriitatilanteissa äänestää nurin vähemmistön (2 vs 1 ei ole ehkä reilua, mutta se antaa vähän enemmän takeita siitä mikä on validein tieto – jos kaikki ovat eri mieltä ollaankin pulassa). Rakentelin kuitenkin enemmän proof-of-concept ideana parista nodesta klusteroidun, vikasietoisen mysql härvelin johon kytkeydytään Glassfish connection poolin kautta. Ja hyvältä vaikuttaa!

Huom! Kahden noden mallissa voi odotaa tulevan enemmän virhetilanteita, jotka voivat vaatia mysql boottausta ja replikoinnin resetointia. Hyödylliseksi on osoittautunut komento: sudo rm -r -f /var/lib/mysql/grastate.dat

Tässä siis muistiinpanoja ja rakennusohjeita. Huom! Joku muu voi tuunatata tämän eri tavalla ja ilmiselvästi tuotantokäyttöön tarvitaan toinen kierros koventamista, mutta näillä ohjeilla pitäisi päästä perille jos haluat maistella vikasietoisuutta itsekin. MariaDB on ehkä jopa paremmin tuettu nykyisellään jos haluat hakea MySQL:lle vaihtoehtoa.

Mysql + Galera replikoiva klusteri

Asennukset ensin. Tarkista, että olet ajan tasalla, ja asenna sitten MySQL + wsrep patchi ja Galera:

sudo apt-get update

sudo apt-get dist-upgrade

sudo apt-get install libaio1 libdbi-perl libdbd-mysql-perl mysql-client rsync

sudo wget http://ftp.de.debian.org/debian/pool/main/o/openssl/libssl0.9.8_0.9.8o-4squeeze14_amd64.deb && sudo dpkg -i libssl0.9.8_0.9.8o-4squeeze14_amd64.deb

sudo wget https://launchpad.net/codership-mysql/5.5/5.5.34-25.9/+download/mysql-server-wsrep-5.5.34-25.9-amd64.deb && sudo dpkg -i mysql-server-wsrep-5.5.34-25.9-amd64.deb

sudo wget https://launchpad.net/galera/3.x/25.3.5/+download/galera-25.3.5-amd64.deb && sudo dpkg -i galera-25.3.5-amd64.deb

Seuraavaksi tehdään mysql:ään vähän tunnuksia:

sudo /etc/init.d/mysql start

mysql -u root -p

#root on ylijumala ja sillä on salasana
GRANT ALL ON *.* TO root@'%' IDENTIFIED BY 'munsalainensana';

#galera haluaa myös päästä kiinni kantoihin
GRANT ALL ON *.* to sst@'%' IDENTIFIED BY 'sstpasswd';

# ja aikanaan myös haproxy haluaa tietää ovatko kannat pystyssä
INSERT INTO mysql.user (Host,User) VALUES ('%','haproxy');

flush privileges;

Hyvä, pistetään mysql automaattikäynnistymään:

sudo  update-rc.d mysql defaults

Editoidaan mysql wsrep.cnf tiedostoa tähän tapaan:

sudo nano /etc/mysql/conf.d/wsrep.cnf

Make these changes:

# Full path to wsrep provider library or 'none'
wsrep_provider=/usr/lib/galera/libgalera_smm.so

# Group communication system handle
wsrep_cluster_address="gcomm://"

# State Snapshot Transfer method
wsrep_sst_method=rsync

# SST authentication string. This will be used to send SST to joining nodes.
# Depends on SST method. For mysqldump method it is root:
wsrep_sst_auth=sst:sstpasswd

Sitten vain mysql käynnistäen uudelleen

sudo /etc/init.d/mysql restart

Hyvä, nyt olisi yksi node pystyssä. Kakkosnodelle tehdään samat operaatiot muuten, mutta editoitaessa tämä yksi rivi muuttuu vähän: siihen tulee ykkösnoden nimi tai ip-osoite:

# Group communication system handle
wsrep_cluster_address="gcomm://debian1,debian2"

Mutta muuten käy läpi samat temput. Kun olet valmis, jos ei mysql käynnistyksessä ole ollut ongelmia, buuttaa ensin node1, sitten node2 pienellä viiveellä.

Ei ongelmia? Hyvä, kirjaudu mysql:ään haluamallasi tunnuksella ja salasanalla, ja aja seuraava komento:

show status like '%wsrep%';

Kiinnitä huomioita erityisesti kohtiin wsrep_ready (pitäisi olla ON), ja wsrep_cluster_size (pitäisi olla 2).

Jos pääsit tähän asti, onnittelut. Jos et, vedä syvään henkeä, ja aloita uudet toistot.

Voit todeta klusterin toimivuuden esim. kirjautumalla toiseen nodeista ja lisäämällä johonkin tauluun rivejä. Niiden pitäisi ilmestyä pikimmiten myös toiseen kantaan.

Haproxy asentaminen Galeran päälle

Hauskasti haproxy ei löydykään oletuksena debianin wheezystä, joten lisätään listalle uusi site. Editoi /etc/apt/sources.list tiedostoa, lisää sinne tämä:

deb http://ftp.debian.org/debian/ wheezy-backports main

Aja seuraavat komennot asentaaksesi haproxyn:

sudo apt-get update

sudo apt-get install haproxy -y

sudo service haproxy start

Pistetään se myös automaattikäynnistykseen:

sudo sed -i s/0/1/ /etc/default/haproxy

Editoi myös /etc/init.d/haproxy tiedostoa, aseta sinne ENABLED arvoon 1 (oletuksena 0).

Seuraavaksi varmuuskopioidaan haproxy.config, ja editoidaan sitä vähän:

mv /etc/haproxy/haproxy.cfg{,.original}
sudo nano /etc/haproxy/haproxy.cfg

Kopioi/editoi sinne jotain tämäntapaista:

global
  log 127.0.0.1   local0
  log 127.0.0.1   local1 notice
  #log loghost    local0 info
  maxconn 1024
  #chroot /usr/share/haproxy
  user haproxy
  group haproxy
  daemon
  #debug
  #quiet

defaults
  log     global
  mode    http
  option  tcplog
  option  dontlognull
  retries 3
  option redispatch
  maxconn 1024
  timeout connect 5000ms
  timeout client 750000ms
  timeout server 750000ms

listen galera_cluster 0.0.0.0:3307
  mode tcp
  balance roundrobin
  option tcpka
  option mysql-check user haproxy
  server debian1 192.168.0.100:3306 check weight 1 maxconn 20
  server debian2 192.168.0.101:3306 check weight 1 maxconn 20
 
listen stats 0.0.0.0:9090
  mode http
  stats enable
  stats uri /
  stats realm Strictly\ Private
  stats auth myusername:mypassword

 

Aikalailla oletusasetuksilla mennään tässä. Mutta muutama mielenkiintoinen säätö tässä tapahtui:

– timeout client ja timeout server on nostettu 5 sekunnista 75 sekuntiin – koska sovelluspalvelinten connection poolit roikuttavat yhteyksiä hyvinkin pitkään auki, 60 sekuntia on tyypillinen aika – näin yhteydet pysyvät valideina

– galera cluster pyörii tässä samassa koneessa, portissa 3307. Mysql oletusportti on 3306. Tähän voisi varata eri koneen jossa olisi haproxy, mutta ajattelin itse replikoida senkin joka nodeen, eli haproxy pyörii joka koneessa portissa 3307, sieltä saa vikasietoista ja tasattua palvelua.

– Tuossa ip osoitteet 192.168.0.100 ja 101 edustavat mysql servereitä joille kuormaa tasataan, listaan tosiaan lisää mieluusti ainakin yksi node lisää. Olen asettanut maksimiyhteysmäärän 20 kappaleeseen – tämäkin voisi olla hyvä synkata connection poolin kanssa. weight eli painotus on joka nodella tässä sama  – eli kuorma jaetaan tasaisesti.

– lopuksi on stats serveri, portissa 9090 (jonka tulisi olla vapaa) – se kertoo mukavasti selaimella tietoa haproxy toiminnasta ja nodejen tilasta. Keksi sille haluamasi osoite, portti, tunnus ja salasana, ja sitten vain testaamaan.

No niin, nyt sitten proxy käyntiin:

sudo service haproxy restart

Ja tämä tietysti joka nodessa, asennukset, säädöt ja käynnistykset.

Voit testata toimintaa esim:

telnet localhost 3307

Tai mysql clientille:

mysql -h 127.0.0.1 --port 3307 -u haproxy_root -p -e "SHOW DATABASES"

Ja voit myös ruuvata selaimesi osoitteeseen:

http://127.0.0.1:9090/stats

No niin, jos kaikki pelaa taas, voit seuraavaksi muuttaa glassfish serverisi connection poolin osoittamaan joko virtuaali-ip:hen, tai yksittäiseen haproxy nodeen, porttiin 3307 portin 3306 sijasta.

Glassfish vikasietoinen connection pool

On hyvä samalla tehdä vähän säätöjä; jos haluat connection poolin aidosti toipuvan proxyjen ja kantojen kaatumisista, hyvä tehdä seuraavaa:

Avaa mysql admin console, mene kohtaan resources-jdbc-connection pools – ja avaa connection pool jolla haluat klusteria käytellä.

Klikkaa ensin Ping ruutuun rasti, ja valitse Save.

Mene seuraavaksi Advanced välilehdelle ja tee tällaisia säätöjä:

  1. Validate At Most Once: 60 seconds
  2. Creation Retry Attempts: 3
  3. Retry Interval: 10 (taitaa oletuksena jo ollakin)
  4. Connection Validation: (Tick) Required
  5. Validation Method: custom-validation
    1. validation class name: org.glassfish.api.jdbc.validation.MySQLConnectionValidation (select from list)

Ja mitä muita säätöjä vain haluatkaan. Sitten vain Save ja mahdollisesti restart. Ja kokeilemaan.

Ja mikä hienointa – itse softan ei tarvitse mitenkään olla tietoinen vikasietoisuudesta tai kuormantasauksesta 😉

 

[edit]: Kahdella nodella tulee sitten tosiaan replikointipulmia aika ajoin, joissa korjaus on:

sudo rm /var/lib/mysql/grastate.dat
sudo service mysql restart

Joten vaivan välttämiseksi kannattaa pistää suosista kolme nodea pystyyn. Voisi olla metkaa tehdä kolmen noden Raspberry Pi-klusteri 😉

 

 

Tuossa muuten aika hyvä artikkeli hienommista tuunauksista:

https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-to-set-up-mysql-load-balancing–3

 

 

Linux + Glassfish 4.0 High availability Load Balancing Cluster – osa 3

Viime kerroille rakensimme Glassfish servereistä klusterin, joka voisi elää vaikka virtuaalikoneissa tai Amazonin palveluissa. Pistimme sen eteen Apache Http-palvelimesta rakennetun Load Balancer ratkaisun – josta kannattaa myös tehdä High Availability ratkaisu, eli pistää niitäkin useampi kopio käyttöön. Vielä pitäisi saada aikaan se, että jos yksi Load Balancer kaatuu, muut ottavat sen paikan. Sitä pohdimme tällä kertaa.

Ideana on viritellä keepalived-niminen daemon ohjelma siten, että sille annetaan yksi yhteinen jaettu virtuaalinen ip-osoite. Keepalived reitittää sen ensisijaisesti ykköskoneelle, mutta jos se kaatuu, kakkoskone ottaa virtuaalisen ip:n haltuunsa. Näin ollen elleivät kaikki load balancerit kaadu yhtäaikaa, palvelun pitäisi pyöriä lähes katkotta.

Tässä esimerkissä virtuaalinen ip-osoite on 192.168.1.100, ja sen takana olevien koneiden todelliset ip-osoitteet voivat olla vaikkapa 192.168.1.101, 102, 103, jne – niitä ei tässä esimerkissä käytetä, vaan koneiden domain nimiä, jotka ovat edelleen debian1, debian2, jne.

Ensin asenna keepalived joka nodelle:

sudo apt-get install keepalived

Seuraavaksi editoi keepalived konfiguraatiota – sitä ei alussa ole ollenkaan, joten aloitetaan tyhjästä:

sudo nano /etc/keepalived/keepalived.conf

Ensimmäiselle koneelle, joka on oletuksena master, kopioi tiedostoon jotain tämäntapaista:

vrrp_script chk_http_port {   # Requires keepalived-1.1.13

script "wget -q -T 1.0 -t 2 --delete-after -O /tmp/test.wget http://localhost:80/index.html"

interval 5            # check every 5 seconds

weight 2              # add 2 points of prio if OK

}

vrrp_instance VI_1 {

interface eth0

state MASTER          # MASTER debian1, BACKUP debian2

virtual_router_id 51  # same id in both hosts

priority 101          # 101 on master, 100 on backup

virtual_ipaddress {

192.168.1.100         # virtual IP

}

track_script {

chk_http_port

}

}

 

Ja kakkoskoneelle modifioidaan tuota vähän, tähän tapaan:

 

vrrp_script chk_http_port {   # Requires keepalived-1.1.13

script "wget -q -T 1.0 -t 2 --delete-after -O /tmp/test.wget http://localhost:80/index.html"

interval 5            # check every 5 seconds

weight 2              # add 2 points of prio if OK

}

vrrp_instance VI_1 {

interface eth0

state BACKUP          # MASTER debian1, BACKUP debian2

virtual_router_id 51  # same id in both hosts

priority 100          # 101 on master, 100 on backup

virtual_ipaddress {

192.168.1.100         # virtual IP

}

track_script {

chk_http_port

}

}

 

Eli muuten sama, mutta prioriteetti vähän pienempi, ja tilana tosiaan BACKUP. Toki sovelletaan sen mukaan onko verkkokortti eth0, ja löytyykö serverin juuresta index.html tiedosto jolla voi testata vastaako serveri, miten tiheään haluat pollailla, jne.

Mutta viiden sekunnin välein siis testataan löytyykö index.html tiedostoa – jos ei, serverin prioriteetti nousee kahdella. Eli jos molemmat serverit ovat pystyssä, debian1 prioriteetti on 101+2 eli 103, ja debian2 on 100+2 eli 102, eli debian1 saa virtuaalisen ip-osoitteen. Jos ykkösserveri kaatuu, sen prioriteetti laskee 101+0, eli 101, ja näin debian2 saa virtuaalisen ip-osoitteen.

Lopuksi potkaistaan keepalived käyntiin ja testataan:

sudo service keepalived start

Ota yhteyttä selaimella virtuaaliseen ip-osoitteeseen – voit testata suoraan aiemmin käytettyä clusterjsp-sovellusta:

http://192.168.1.100/clusterjsp

Toimiiko? Hyvä. Oletuksena kytkeydyit varmaankin debian1 koneeseen, sekä apache että glassfish instanssiin. Seuraavaksi voitaisiin nitistää debian1 koneen glassfish:

sudo service glassfish-inst stop

Toimiiko? Hyvä, en olisi uskonut. Debian1 koneen apache ohjaa kuorman debian2 koneen glassfish serverille. Nitistetään seuraavaksi debian1 koneen apache:

sudo service apache2 stop

Kokeile vielä kerran virtuaali-ip:tä. Nyt sen pitäisi ohjata debian2 koneen apachelle, joka ohjaa debian2-koneen glassfishille.

Jos ei toimi, mokasit. Savun hälvettyä tarkista kytkennät ja yritä.uudestaan. Paljon on säätöjä näissä. Mutta metkasti toimii 😉 Kannattaa vielä buutata palvelimet ja katsoa että kaikki pelaa edelleen.

 

No niin, ja tässä pari kuvaruutukaappausta high availabilitystä toiminnassa. Olen hieman retusoinut serverinimiä ja ip osoitteita suojellakseni alkuperäisten tietokonehenkien identiteettiä ja persoonaa julkisuudelta, mutta näistä käy idea selväksi:

HAClient_1

Eli sessioon on pumpattu vähän dataa, tässä välissä kävin Glassfish konsolissa nitistämässä debian1 instanssin.

HAClient_2

Ja heittämällä mentiin kakkosnodelle, sessio data replikoituneena verkon yli ja edelleen käytettävissä ilman käyttäjälle näkyvää katkosta. Samoin voisi nitistää kumman hyvänsä load balancerin, tai vaikka vetää töpselin seinästä koko koneesta.

 

Nyt kun saisi vielä tietokantakerroksen vikasietoiseksi ja kuormaa tasaavaksi…..

Linux + Glassfish 4.0 High availability Load Balancing Cluster – osa 2

Viime kerralla viritettiin kaksi Glassfish serveriä juttelemaan keskenään – ja tarvittaessa replikoimaan istunnotkin. Tällä kertaa laitetaan niiden eteen Apache Http serveri LoadBalanceriksi. LoadBalancerin tehtävänä on ohjata liikenne toimiville servereille ja ohittaa vikatilassa tai sammuneina olevat – sekä tasata kuormaa serverien välillä. Näitäkin kannattaa tehdä ainakin kaksi kappaletta – yksi joka instanssikoneeseen – koska muuten loadbalancer on uusi kriittinen pisteesi – heikoin lenkki.

Aloitin ensin MOD_JK:lla, koska se on load balancer vaihtoehdoista ’javamaisempi’, joka tarkoittaa että se osaa tehdä monenmoisia temppuja mitä muut eivät osaa, mm. ssl-salauksen, monitoroinnin ja dynaamisten säätöjen suhteen. Valitettavasti testatessa paljastui, että vaikka se toimi hienosti clusterjsp esimerkille, se antoi suurikokoisilla tiedostoilla mehukkaita Grizzly-poikkeuksia (mm. HeapBuffer has already been disposed). Tämä on ilmeisesti Glassfish 4.0 bugi – en ollut ainoa joka tästä kärsi. Kenties 4.1 korjaa tämän – 4.0:han on tarkoituskin olla high availability early access versio. Mutta kärsimätön mieli ei jaksa eikä pysty odottamaan. Joten tein ratkaisun mod_proxyllä. Listaan kuitenkin testatut mod_jk käyttöönotto-ohjeet tähän samaan artikkeliin – loppuun. Mutta ensin mod_proxy ja toimiva versio:

MOD_PROXY Load balancer asennusohjeet

Ohjeissa oletetaan että koneet joihin asennat nämä ovat nimeltään debian1 ja debian2, ja asennat kaiken joka koneelle.

Aloita asentamalla ja aktivoimalla tarvittavat moduulit:

sudo apt-get install apache2 libapache2-mod-proxy-html libxml2-dev

sudo a2enmod proxy proxy_http proxy_balancer rewrite

Testaa käynnistyykö apache2:

sudo service apache2 start

Jos kaikki hyvin, seuraavaksi editoi tiedostoa /etc/apache2/sites-available/default lempieditorillasi. Sen voisi muokata esim. tämän näköiseksi (muista muokata se klusterin kaikille koneille, ja vastaavasti muutaa host name osia, jotta load balanceristä on kopioita):

<VirtualHost *:80>
 ServerName debian1.mycompany.com
 ServerAdmin webmaster@localhost

 ErrorLog ${APACHE_LOG_DIR}/error.log

 # Possible values include: debug, info, notice, warn, error, crit,
 # alert, emerg.
 LogLevel warn

 CustomLog ${APACHE_LOG_DIR}/access.log combined

 RewriteEngine On
 ProxyRequests Off
 ProxyPreserveHost On

 <Proxy *>
 Order deny,allow
 Allow from all
 </Proxy>

 <Location /balancer-manager>
 SetHandler balancer-manager
 Order Allow,Deny
 Allow from all
 # Only allow from internal network
 #Allow from 10.0.0.0/8
 </Location>

 <Proxy balancer://mycluster>
 BalancerMember http://debian1:8080 route=debian1
 BalancerMember http://debian2:8080 route=debian2
 ProxySet lbmethod=byrequests
 </Proxy>

 ProxyPass /balancer-manager !
 ProxyPass / balancer://mycluster/ stickysession=JSESSIONID
 ProxyPassReverse / http://debian1:8080/
 ProxyPassReverse / http://debian2:8080/

</VirtualHost>

Käynnistä apache uudelleen, ja kokeile ohjata selaimesi load balancer-koneen osoitteeseen. Sen pitäisi ohjata vuoronperään eri myllyille – voit testata tätä vaikkapa avaamalla pari eri selainta, tai sammuttamalla ykkösmyllyn. Proxypasseja voi tietysti lisätä ja muokata sen mukaan mitä kaikkia kansioita haluat uudelleenohjata ja miten. Load balancer algoritmejakin löytyy useampia.

No niin, seuraavaksi vaihtoehtoiset ohjeet mod_jk versiolle. Sitä en itse päätynyt käyttämään, mutta se voi olla testaamisen arvoinen Glassfish 4.1 kanssa aikanaan.

MOD_JK load balancer asennusohjeet

Aloita asentamalla softat kaikille haluamillesi koneille:

sudo apt-get install apache2 libapache2-mod-jk

Voisit tässä kohtaa editoida tiedostoa /etc/apache2/mods-available/jk.conf  – mutta siellä ei ole mitään pakollista muutettavaa, joten mennään sensijaan suoraan editoimaan toista tiedostoa:

sudo nano /etc/libapache2-mod-jk/workers.properties

Editoi tätä tiedostoa, voit kommentoida pois esim. oletus-jk13 workerin säätöjä, mutta muuta yksi riveistä tällaiseksi:

worker.list=loadbalancer,jk-status

Ja lisää tämätapaista (jos laitteita on enemmän kuin kaksi, lisää rivejä vain – tietysti sovella verkkonimet jne):

worker.debian1.port=8009
worker.debian1.host=debian1.mycompany.com
worker.debian1.type=ajp13
worker.debian1.lbfactor=1
worker.debian2.port=8009
worker.debian2.host= debian2.mycompany.com
worker.debian2.type=ajp13
worker.debian2.lbfactor=1
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=debian1,debian2
worker.loadbalancer.sticky_session=1
worker.jk-status.type=status

Huomaa miten voit load balance factoreilla esim. painottaa miten kutsuja jaetaan. Tässä on käytössä sticky_session joka ei ole välttämätöntä. Olemme jättäneet myös status-tiedot päälle. Huomaa että jk moduuli myös kirjoittelee logia oletuksena paikkaan /var/log/apache2/mod_jk.log – logit on hyvä tarkastaa aika ajoin näitä testatessa.

Hommaa alkaa olla melkein valmis. Seuraavaksi editoidaan apache serverin omaa oletuskonfiguraatiota, ja lisätään kaikki halutut hakemistot joita halutaan uudelleenohjata:

sudo nano /etc/apache2/sites-available/default

Ja konfiguraatio voi mennä esim. näin:

<Location /jk-status>
 # Inside Location we can omit the URL in JkMount
 JkMount jk-status
 #Order deny,allow
 #Deny from all
 #Allow from 127.0.0.1
 </Location>

# mount clusterjsp
 JkMount /clusterjsp loadbalancer
 JkMount /clusterjsp/* loadbalancer

Nämä tulevat tietysti kaikki juurielementin VirtualHost sisään, loppuun, ja jos haluat lisää hakemistoja loadbalanceriin clusterjsp:n lisäksi, määrittele ne kaikki tähän.

Seuraavaksi enabloidaan mod_jk (luultavasti jo päällä), buutataan apache, ja aletaan kokeilemaan:

sudo a2enmod jk
sudo /etc/init.d/apache2 restart

Näppärä testi on ensin avata selain osoitteeseen http://debian1/clusterjsp – ja katsoa aukeaako sovellus. Apache pyörii portissa 80 – glassfishit porteissa 8080 omissa koneissaan. Jos sovellus toimii, näet miltä serveriltä se on noudettu. Nyt voit mennä glassfish hallintaan (DAS) ja sammuttaa instanssin joka on käytössä. Jos load balancer toimii oikein, sen pitäisi seuraavan kerran sivua päivitettäessä saumattomasti pompauttaa sinut toiselle instanssille.

Muista tehdä tämä load balancer molempiin/kaikkiin instansseihin niin voimme seuraavalla kertaa rakentaa siitäkin high availability version. Koska mitä jos Load Balancer kaatuu?

 

 

Maven, Glassfish 4 ja Cargo plugin

Kun hioo jatkuvaa integraatiota kuntoon tulee ennen pitkää ajankohtaiseksi pohdiskella miten automatisoidaan Maven buildien asennus etäservereille – kehittäjätestiservereille, hyväksyntätestiservereille, ja tuotantoonkin. Tapojahan tähän on monia – serverien omat työkalut, ihan tiedostotasolla toiminta ja hot deploy. Itse kuitenkin halusin kytkeä tämänkin Maveniin, se helpottaa kovasti mm. Jenkinsin kanssa toimintaa ja pitää munat samassa korissa.

Kokeilin muutamaa plug-inia, ja suosikiksi niistä erääntyi Cargo plug-in. Muissa tuli vastaan bugeja, ja Cargon konfiguraatio oli simppeleimmästä päästä joten se päätyi mukaan. Mukavaa on myös se, että Cargo ei ole yhden serverin ihme vaan tukee valtaisaa määrää Java Appservereitä.

Eli lisätään pom.xml tiedostoon tällainen plug-in:

 <!-- Maven Cargo plug-in to deploy .war to test servers -->
  <plugin>
    <groupId>org.codehaus.cargo</groupId>
    <artifactId>cargo-maven2-plugin</artifactId>
    <version>1.4.8</version>
    <configuration>
      <container>
        <containerId>glassfish4x</containerId>
        <type>remote</type>
      </container>
    <configuration>
      <type>runtime</type>
      <properties>
        <cargo.hostname>munserveri</cargo.hostname>
        <cargo.remote.username>muntunnus</cargo.remote.username>
        <cargo.remote.password>munsalasana</cargo.remote.password>
      </properties>
   </configuration> 
   </configuration>
    <dependencies>
      <dependency>
        <groupId>org.glassfish.deployment</groupId>
        <artifactId>deployment-client</artifactId>
        <version>3.2-b06</version>
      </dependency>
    </dependencies>
  </plugin>

Ja siinä se. Tuossa yllä munserveri on tietysti oman Glassfish 4 serverin osoite, muntunnus ja munsalasana sen admin-oikeudet omaavat tunnukset.

Nyt voit ajaa seuraavia komentoja:

mvn clean package cargo:deploy
mvn clean package cargo:redeploy
mvn cargo:undeploy

Yritin viritellä tätä toimimaan force-parametrilla siten että deploy hoitaisi myös redeploy-toiminnon – mutta jostain syystä ei pelittänyt ainakaan tässä ympäristössä. Ratkaisuna on ajaa aina cargo:redeploy, se ei näytä pahastuvan ellei serveriltä softaa ennestään löydykään, mutta deploy pahastuu jos softa on jo serverillä samalla nimellä, ja undeploy pahastuu jos softaa ei ole.

Seuraava askel on sitten tuunata profiileja käyttöön. Niillä voit säätää helposti serveriosoitteet, salasanat ja tunnukset vaikka miten monelle eri serverille ja käytellä niitä esim. näin:

mvn -Pintegration-test package cargo:undeploy cargo:deploy

Varmaan on fiksumpiakin tapoja tehdä näitä, esim. kenties force parametrin saa toimimaan, kenties redeploy hoitaa saman kuin undeploy+deploy. Tällä pääsee kuitenkin alkuun, ja kommentoida sopii jos keksii liukkaampia tapoja.

Plug-in on CodeHousin käsialaa ja löytyy täältä: http://cargo.codehaus.org/

 

 

Ja näin opimme taas jotain kivaa ja hyödyllistä 😉