Előkészületek
Ha több WildFly példányt szeretnénk használni, hogy minimalizáljuk a kieséseket, akkor célszerű domain módban indítani az alkalmazásszervert, mert a standalone mód helyett ez ad lehetőségeket a könnyű adminisztrációra, illetve a beállítások és a telepítések megfelelő terítésére. A WildFly domain módja esetén két (viszonylag) sovány igényű java folyamat indul el a gépeken (a Process Controller és a Host Controller), amelyeknek felügyelik az tényleges alkalmazásszerver funkciókat, de erre kicsit később még visszatérünk.
A szükséges topológia az alábbi ábra szerint alakul majd (egy master és két slave), mind a három kiszolgálóra kerül alkalmazásszerver példány, illetve httpd kiszolgáló is, amely a mod-cluster terheléselosztót fogja kezelni, így bármelyik kiszolgáló esik ki, a szolgáltatás nem fog szünetelni, csak lassulni -- feltéve, ha egyébként is kellően terheltek voltak:
Felmerülhet a kérdés, hogy mi történik a master kiesése esetén:
- a felhasználó szemszögéből semmi, a többi alkalmazásszerver példány dolgozik tovább, a terhelés elosztása automatikusan átszerveződik,
- a rendszergazdai eszköztár viszont jelentősen csökken, amíg helyre nem áll a master funkcionalitása (akár mentésből visszaállítva): ez egyfajta SPOF a rendszerben, de vállalható a kockázat.
Telepítés
Hozzuk létre a szervereken egy 'wildfly' nevű felhasználót, majd töltsük le és tömörítsük ki a WildFly 8 alkalmazásszervert:
# adduser -g users -m wildfly # su - wildfly $ wget http://download.jboss.org/wildfly/8.0.0.Final/wildfly-8.0.0.Final.tar.gz $ tar xzvf wildfly-8.0.0.Final.tar.gz ... $
Próbaképp elindíthatjuk domain módban, hogy minden szükséges függőség rendelkezésre áll-e:
$ wildfly-8.0.0.Final/bin/domain.sh ... [Server:server-two] 14:40:23,007 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.0.0.Final "WildFly" started in 30443ms - Started 207 of 255 services (86 services are lazy, passive or on-demand)
Állítsuk le a létrehozott példányokat, főzzünk egy jó kávét, közben olvassunk egy kis dokumentációt a https://docs.jboss.org/author/display/WFLY8/ oldalról indulva...
Hogy lesz ebből cluster?
A telepítés és a futtatási teszt során volt három – egymástól függetlenül futó – példányunk, amelyek nem igazán tudtak egymás létezéséről. Ennek két oka volt:
- 127.0.0.1 (avagy localhost) címen futottak
- a legtöbb VPS és/vagy Cloud szolgáltatónál nincs multicast a virtualizált gépek között, a WildFly példányok viszont multicast protokollon beszélnék meg a topológiát.
Mind a két "problémára" van megoldás, de ezek előtt alakítsuk ki a két slave és egy master topológiát, amelyhez kétféle módon kell indítanunk a WildFly szervereket:
$ bin/domain.sh --host-config=host-master.xml -Djboss.bind.address.management=10.129.214.116 -Djboss.bind.address=10.129.214.116
$ bin/domain.sh --host-config=host-slave.xml -Djboss.domain.master.address=10.129.214.116 -Djboss.bind.address.management=10.129.216.43 -Djboss.bind.address=10.129.216.43
Vegyük észre (a különböző IP címeken kívül) a két alapvető különbséget:
- a config-host paraméternél megadott XML állomány nevét,
- illetve a slave esetén a master IP címét definiáló jboss.domain.master.address paramétert.
Indításkor tapasztalni fogjuk, hogy a master példány elindul rendesen, a slave viszont hibát fog jelezni és el se indul:
[Host Controller] 03:52:36,517 ERROR [org.jboss.remoting.remote.connection] (Remoting "gacivs-test01.javaforum.hu:MANAGEMENT" I/O-1) JBREM000200: Remote connection failed: javax.security.sasl.SaslException: Authentication failed: the server presented no authentication mechanisms [Host Controller] 03:52:36,528 WARN [org.jboss.as.host.controller] (Controller Boot Thread) JBAS016535: Could not connect to master. No domain controller discovery options left. Error was: java.lang.IllegalStateException: JBAS010942: Unable to connect due to authentication failure. [Host Controller] 03:52:36,530 ERROR [org.jboss.as.host.controller] (Controller Boot Thread) JBAS010901: Could not connect to master. Aborting. Error was: java.lang.IllegalStateException: JBAS016519: Tried all domain controller discovery option(s) but unable to connect [Host Controller] 03:52:36,583 INFO [org.jboss.as] (MSC service thread 1-1) JBAS015950: WildFly 8.0.0.Final "WildFly" stopped in 35ms [Host Controller] 03:52:36,914 INFO [org.jboss.as.process.Host Controller.status] (reaper for Host Controller) JBAS012010: Process 'Host Controller' finished with an exit status of 99 03:52:36,917 INFO [org.jboss.as.process] (Thread-8) JBAS012016: Shutting down process controller 03:52:36,918 INFO [org.jboss.as.process] (Thread-8) JBAS012015: All processes finished; exiting
A "Could not connect to master" hiba oka teljesen logikus: a master nem engedi, hogy bármilyen jött-ment slave csak úgy csatlakozhasson, a csatlakozáshoz a master oldalán létre kell hozni egy technikai felhasználót, a slave oldalán pedig meg kell adni a hozzá tartozó jelszót. A technikai felhasználó nevét több módon megadhatjuk:
- a host-slave.xml fájlban a host nevénél
- a host-slave.xml fájlban a domain-controller szekcióban a remote paramétereként
- ezek hiányában az adott szerver teljes neve lesz.
Célszerű az utolsó opciót választani, ez jár a legkevesebb munkával, ezért adjunk hozzá kettő új (gacivs-test01.javaforum.hu és gacivs-test02.javaforum.hu) a master "adatbázisához":
$ bin/add-user.sh What type of user do you wish to add? a) Management User (mgmt-users.properties) b) Application User (application-users.properties) (a): a Enter the details of the new user to add. Using realm 'ManagementRealm' as discovered from the existing property files. Username : gacivs-test01.javaforum.hu Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file. - The password should not be one of the following restricted values {root, admin, administrator} - The password should contain at least 8 characters, 1 alphanumeric character(s), 1 digit(s), 1 non-alphanumeric symbol(s) - The password should be different from the username Password : Re-enter Password : What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: About to add user 'gacivs-test01.javaforum.hu' for realm 'ManagementRealm' Is this correct yes/no? y Added user 'gacivs-test01.javaforum.hu' to file '/home/wildfly/wildfly-8.0.0.Final/standalone/configuration/mgmt-users.properties' Added user 'gacivs-test01.javaforum.hu' to file '/home/wildfly/wildfly-8.0.0.Final/domain/configuration/mgmt-users.properties' Added user 'gacivs-test01.javaforum.hu' with groups to file '/home/wildfly/wildfly-8.0.0.Final/standalone/configuration/mgmt-groups.properties' Added user 'gacivs-test01.javaforum.hu' with groups to file '/home/wildfly/wildfly-8.0.0.Final/domain/configuration/mgmt-groups.properties' Is this new user going to be used for one AS process to connect to another AS process? e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls. yes/no? y To represent the user add the following to the server-identities definition <secret value="R2FDSVZTMzc5Iw==" />
Ha ugyanazt a jelszót használjuk a slave példányokhoz tartozó technikai felhasználóknál, akkor könnyedén tudjuk klónozni a slave példányt, így a változó terhelés függvényében könnyen tudunk új tagot hozzátenni a meglévő kiszolgálókhoz és minimális lesz a hibázási lehetőség! Érdemes úgy kialakítani a kiszolgálóinkat, hogy a terhelés növekedésére ne vertikálisan (kevés számú erősebb gép), hanem horizontálisan (sok gyengébb gép) legyen skálázható.
Ezek után a slave példányok már tudnak csatlakozni, ha a secret XML elemet kicseréljük az add-user.sh szkript által kiírt értékre a host-slave.xml állomány megfelelő helyén:
[Server:server-two] 04:11:27,240 INFO [org.hornetq.ra] (MSC service thread 1-1) HornetQ resource adaptor started [Server:server-two] 04:11:27,241 INFO [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-1) IJ020002: Deployed: file://RaActivatorhornetq-ra [Server:server-two] 04:11:27,248 INFO [org.jboss.as.connector.deployment] (MSC service thread 1-2) JBAS010401: Bound JCA ConnectionFactory [java:/JmsXA] [Server:server-two] 04:11:27,250 INFO [org.jboss.as.messaging] (MSC service thread 1-2) JBAS011601: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory [Server:server-two] 04:11:27,434 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.0.0.Final "WildFly" started in 31841ms - Started 222 of 349 services (170 services are lazy, passive or on-demand)
És a master is szépen jelzi, hogy megjött a két slave példány:
[Host Controller] 04:16:28,559 INFO [org.jboss.as.domain] (slave-request-threads - 1) JBAS010918: Registered remote slave host "gacivs-test01.javaforum.hu", WildFly 8.0.0.Final "WildFly" [Host Controller] 04:17:18,326 INFO [org.jboss.as.domain] (slave-request-threads - 1) JBAS010918: Registered remote slave host "gacivs-test02.javaforum.hu", WildFly 8.0.0.Final "WildFly"
Ha leállítjuk a master példányt, akkor a két slave mindenképpen megpróbál majd visszatalálni, s ha IP címek helyett a továbbiakban DNS neveket adunk meg, akkor könnyedén át tudunk állni akár egy mentésből visszaállított más IP című master példányra, ha valami baj történne az eredetivel:
[Host Controller] 04:18:00,475 WARN [org.jboss.as.host.controller] (Remoting "gacivs-test02.javaforum.hu:MANAGEMENT" I/O-1) JBAS010914: Connection to remote host-controller closed. Trying to reconnect. ... [Host Controller] 04:22:00,116 INFO [org.jboss.as.host.controller] (domain-connection-threads - 1) JBAS010916: Reconnected to master
Adminisztráció
A WildFly alapvetően két módon adminisztrálható:
- CLI (Command Line Interface) felületen át (ez a preferált módszer, mert a későbbiekben könnyen reprodukálható egy környezet a ledokumentált és frissen tartott CLI gyűtemény futtatásával)
- vagy Web felületről (amelynél jelentős kézimunka egy új környezet kialakítása).
Mind a két módszerhez szükséges egy admin felhasználó, amelyet a master példánynál kell létrehoznunk a más ismert add-user.sh futtatásával:
$ bin/add-user.sh What type of user do you wish to add? a) Management User (mgmt-users.properties) b) Application User (application-users.properties) (a): Enter the details of the new user to add. Using realm 'ManagementRealm' as discovered from the existing property files. Username : admin The username 'admin' is easy to guess Are you sure you want to add user 'admin' yes/no? yes Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file. - The password should not be one of the following restricted values {root, admin, administrator} - The password should contain at least 8 characters, 1 alphanumeric character(s), 1 digit(s), 1 non-alphanumeric symbol(s) - The password should be different from the username Password : Re-enter Password : What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: About to add user 'admin' for realm 'ManagementRealm' Is this correct yes/no? y Added user 'admin' to file '/home/wildfly/wildfly-8.0.0.Final/standalone/configuration/mgmt-users.properties' Added user 'admin' to file '/home/wildfly/wildfly-8.0.0.Final/domain/configuration/mgmt-users.properties' Added user 'admin' with groups to file '/home/wildfly/wildfly-8.0.0.Final/standalone/configuration/mgmt-groups.properties' Added user 'admin' with groups to file '/home/wildfly/wildfly-8.0.0.Final/domain/configuration/mgmt-groups.properties' Is this new user going to be used for one AS process to connect to another AS process? e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls. yes/no? y To represent the user add the following to the server-identities definition <secret value="QWRtaW4zNzkj" />
CLI csatlakozás
A WildFly tartalmaz egy jboss-cli.sh állományt, amellyel csatlakozni tudunk bármelyik példányhoz, de leginkább a master az érdekes:
$ bin/jboss-cli.sh You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands. [disconnected /] connect gacivs-test-master [domain@gacivs-test-master:9990 /] /host=gacivs-test01.javaforum.hu/server=server-one/interface=public:read-attribute(name=resolved-address) { "outcome" => "success", "result" => "10.129.216.43" }
Vegyük észre, hogy ha arról a gépről csatlakozunk, ahol a WildFly master is fut, akkor nem kér jelszót! Egyéb helyről kapcsolódva viszont kérni fogja a felhasználónevet és a jelszót:
Web felület
A WildFly az általunk megadott IP címen fog a 9990 porton indítani egy management-console alkalmazást, amely lehetővé teszi a webes felületen való adminisztrációt, itt láthatjuk a kialakított topológiát és a két szervert, amelyeket az alapértelmezett konfiguráció szerint elindít a WildFly:
Multicast helyett TCP!
Mint említettem volt, a legtöbb VPS és Cloud szolgáltató nem igazán engedi a multicast kommunikációt, még akkor se, amikor adnak egy privát IP tartományt egy második hálózati interfészen. A WildFly viszont alapból multicast csatornán áll össze egy csoportba, ezért rá kell beszélnünk, hogy ezt TCP csatornán művelje, illetve a betérő vagy kieső tagokat TCP csatornán tartsa nyilván. Ehhez az alábbi parancsot kell kiadnunk, hogy a "mindenügyi" JGroups TCP-n kapcsolódjon össze:
cd /profile=full-ha/subsystem=jgroups ./stack=tcpping:add cd stack=tcpping ./transport=TRANSPORT:add(type=TCP,socket-binding=jgroups-tcp) :add-protocol(type=TCPPING) :add-protocol(type=MERGE2) :add-protocol(type=FD_SOCK,socket-binding=jgroups-tcp-fd) :add-protocol(type=FD) :add-protocol(type=VERIFY_SUSPECT) :add-protocol(type=BARRIER) :add-protocol(type=pbcast.NAKACK) :add-protocol(type=UNICAST2) :add-protocol(type=pbcast.STABLE) :add-protocol(type=pbcast.GMS) :add-protocol(type=UFC) :add-protocol(type=MFC) :add-protocol(type=FRAG2) :add-protocol(type=RSVP) cd protocol=TCPPING ./property=initial_hosts/:add(value="10.129.216.43[7750],10.129.215.37[7750]") ./property=port_range/:add(value=0) ./property=timeout/:add(value=3000) ./property=num_initial_members/:add(value=3) cd ../.. :write-attribute(name=default-stack,value=tcpping)
Figyeljünk arra, hogy a 7750-es port úgy jön ki, hogy 7600 + 150, mert ennyi a porteltolása a full-ha profilban a server-two csoportnak!
Ezek után a két példány szépen egymásra fog találni:
[Server:server-two] 07:18:21,671 INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-1,shared=tcpping) ISPN000094: Received new cluster view: [gacivs-test01.javaforum.hu:server-two/web|1] (2) [gacivs-test01.javaforum.hu:server-two/web, gacivs-test02.javaforum.hu:server-two/web]
HTTP cluster
A felhasználó oldaláról a HTTP kiszolgálás folyamatossága és folytonossága a szempont, így konfiguráljunk be egy Apache Httpd szervert, ehhez a WildFly (régebben ugye JBoss) mellé kiadott mod_cluster modult tudjuk használni.
Elsőképp szerezzük meg a cluster-test alkalmazást a WildFly dokumentumok között említett helyről (git clone git://github.com/liweinan/cluster-demo.git), fordítsuk le, majd telepítsük a CLI használatával:
[domain@gacivs-test-master:9990 /] deploy cluster-demo.war --server-groups=other-server-group
Multicast helyett TCP!
Mivel TCP alapokon fogunk kommunikálni az Apache alá telepített mod_cluster modullal, ezért CLI használatával kapcsoljuk ki a multicast hirdetést és konfiguráljuk be a proxy példányok IP címét és port számát, majd kapcsoljuk ki a Sticky Session szolgáltatást is:
[domain@gacivs-test-master:9990 /] /profile=full-ha/subsystem=modcluster/mod-cluster-config=configuration/:write-attribute(name=advertise,value=false) { "outcome" => "success", "result" => undefined, "server-groups" => {"other-server-group" => {"host" => { "gacivs-test01.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}}, "gacivs-test02.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}} }}} }
Láthatjuk, hogy a master végrehajtotta a slave példányokon a kért módosítást (ellenőrizni is tudjuk ezt a webes felületen), de kiírta, hogy szükséges lesz az alkalmazásszerver példányok újraindítása. Még mielőtt ezt megtennénk, állítsuk be a proxy címeket is:
[domain@gacivs-test-master:9990 /] /profile=full-ha/subsystem=modcluster/mod-cluster-config=configuration/:write-attribute(name=proxy-list,value="10.129.214.116:10001,10.129.216.43:10001,10.129.215.37:10001") { "outcome" => "success", "result" => undefined, "server-groups" => {"other-server-group" => {"host" => { "gacivs-test01.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}}, "gacivs-test02.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}} }}} }
...mert itt se ússzuk meg az újraindítást, de még mindig ne indítsuk újra a kiszolgálókat, mert a Sticky Session kikapcsolásához úgyis újra kellene indítani:
[domain@gacivs-test-master:9990 /] /profile=full-ha/subsystem=modcluster/mod-cluster-config=configuration/:write-attribute(name=sticky-session,value=false) { "outcome" => "success", "result" => undefined, "server-groups" => {"other-server-group" => {"host" => { "gacivs-test01.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}}, "gacivs-test02.javaforum.hu" => {"server-two" => {"response" => { "outcome" => "success", "result" => undefined, "response-headers" => { "operation-requires-restart" => true, "process-state" => "restart-required" } }}} }}} }
...ezért most indítsuk újra, majd ellenőrizzük, hogy rendben elindultak-e a kiszolgálók:
[domain@gacivs-test-master:9990 /] /server-group=other-server-group:stop-servers { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@gacivs-test-master:9990 /] /server-group=other-server-group:start-servers { "outcome" => "success", "result" => undefined, "server-groups" => undefined } [domain@gacivs-test-master:9990 /] ls /host=gacivs-test01.javaforum.hu/server-config=server-two interface path auto-start=true group=other-server-group priority=undefined socket-binding-port-offset=150 jvm system-property cpu-affinity=undefined name=server-two socket-binding-group=undefined status=STARTED [domain@gacivs-test-master:9990 /] ls /host=gacivs-test02.javaforum.hu/server-config=server-two interface path auto-start=true group=other-server-group priority=undefined socket-binding-port-offset=150 jvm system-property cpu-affinity=undefined name=server-two socket-binding-group=undefined status=STARTED
...ami után persze panaszkodni fognak az egyes példányok, hogy nem tudnak csatlakozni a megadott címre, mert ott még nincs Apache Httpd szerver:
[Server:server-two] 05:01:58,840 ERROR [org.jboss.modcluster] (UndertowEventHandlerAdapter - 1) MODCLUSTER000043: Failed to send INFO to gacivs-test02/10.129.215.37:10001: java.net.ConnectException: Connection refused
Az Apache Httpd és a mod_cluster beállítása
Telepítsünk egy httpd csomagot, töltsük le a legfrissebb mod_cluster kiadást a megfelelő (jelen esetben CentOS) rendszerhez:
# yum install httpd ... # wget http://downloads.jboss.org/mod_cluster//1.2.6.Final/linux-x86_64/mod_cluster-1.2.6.Final-linux2-x64-so.tar.gz ... # cd /etc/httpd/modules/ # tar xzvf /root/mod_cluster-1.2.6.Final-linux2-x64-so.tar.gz ... #
Ezek után kicsit konfigurálnunk kell a /etc/httpd/conf/httpd.conf fájl szerkesztésével (figyelem: diff kimenet!):
192c192 < LoadModule proxy_balancer_module modules/mod_proxy_balancer.so --- > #LoadModule proxy_balancer_module modules/mod_proxy_balancer.so 202a203,207 > LoadModule slotmem_module modules/mod_slotmem.so > LoadModule manager_module modules/mod_manager.so > LoadModule proxy_cluster_module modules/mod_proxy_cluster.so > LoadModule advertise_module modules/mod_advertise.so > 1009a1015,1040 > > Listen 0.0.0.0:10001 > MemManagerFile /var/cache/httpd > > <VirtualHost 0.0.0.0:10001> > <Directory /> > Order deny,allow > Deny from all > Allow from all > </Directory> > > <Location /mod_cluster-manager> > SetHandler mod_cluster-manager > Order deny,allow > Deny from all > Allow from all > </Location> > > KeepAliveTimeout 60 > MaxKeepAliveRequests 0 > > ManagerBalancerName other-server-group > AdvertiseFrequency 5 > ServerAdvertise off > EnableMCPMReceive > </VirtualHost>
A httpd szolgáltatás elindítása után már láthatjuk bármelyik publikus IP címen a három közül a mod_cluster státusz és menedzsment felületét (figyelem! nincs korlátozva a mod_cluster-manager elérése, éles üzembe így nem való!):
A mod_cluster ténykedése okán nem kell azzal törődnünk, hogy felsoroljuk a Context Path konfigurációt, az élő alkalmazásaink automatikusan megjelennek az Apache Httpd kiszolgáló publikus IP címein és mindig az az alkalmazásszerver kapja a kérést, amelyik ki tudja szolgálni és pillanatnyilag a legkevesebb kérést kapta: