Poniższy artykuł przedstawi w kilku prostych krokach co zrobić, by uruchomić klaster JBoss EAP 7.x na platformie Red Hat OpenShift. Wykorzystamy technologię S2I – Source to Image oraz obiekt ImageStream, dzięki czemu „wbudujemy” do kontenera gotową aplikację wykorzystującą klastrowanie, a następnie powołamy dowolną ilość węzłów naszego klastra. Jeśli wszystko się uda 😉 powinniśmy w logach JBoss zaobserwować, że klaster został zestawiony, a aplikacja zacznie z niego korzystać.
No to zaczynamy. W pierwszym kroku będziemy potrzebowali obrazu kontenera z serwerem aplikacyjnym JBoss EAP 6 lub EAP 7. Ja pobrałem obraz kontenera z nowym JBoss EAP 7. Jeśli dysponujecie platformą Red Hat OpenShift, gotowe obiekty ImageStream ze wskazaniem kontenerów z JBoss powinniście znaleźć w namespace openshift. Jeśli nie macie komercyjnego OpenShift, to obraz kontenera możecie pobrać ze strony Red Hat pod adresem: catalog JBoss EAP 7.2 with OpenJDK8
Znajdziemy tu: JBoss EAP 7.2 for OpenShift – gotowy kontener przygotowany przez producenta do pobrania zarówno dla Docker, Podman, jak i OpenShift. Warunek – musicie posiadać konto serwisowe w Red Hat (https://access.redhat.com/terms-based-registry/), aby dostać się do zasobów rejestru producenta.
Zawsze możecie też skorzystać z kontenera z serwerem WildFly, który powinien zachować się podobnie jak jego komercyjna wersja – JBoss.
Mając ten pierwszy krok za sobą, uznaję, że każdy czytelnik ma już obraz JBoss/WildFly w swoim rejestrze. Teraz czas przygotować aplikację, która będzie wykorzystywała nasze HA. Ja mam małą aplikację zliczającą sesje użytkowników. Jeśli na którymś węźle klastra ktoś odświeży stronę z aplikacją – ona powiększy licznik wejść. Jeśli któryś z węzłów zostanie zamknięty lub nowy węzeł zostanie dodany, aplikacja nieprzerwanie powinna pokazywać właściwy stan licznika podczas odświeżania strony. Wszystko dzięki HA. Podobne proste aplikacje znajdziecie na github’ie.
Przygotujmy zatem nasz zestaw do „zabawy”. Musimy połączyć obraz serwera JBoss EAP z aplikacją wykorzystującą klastrowanie. W tym celu wykorzystamy technologię S2I, która zamiast tworzenia pliku Dockerfile pozwoli nam „wrzucić” gotową aplikację (*.war) do obrazu kontenera i stworzyć nowy kontener, który będzie węzłem naszego klastra zawierającym aplikację i serwer EAP w jednym.
1) tworzymy katalog, do którego przenosimy naszą gotową (binarną wersję) aplikacji np.:
cp /tmp/cluster.war ~/user/ha-test/ROOT.war
sprawdzamy 😉
# cd ~/user/ha-test/
# ll
razem 20
-rw-r--r--. 1 root root 19273 10-07 13:32 ROOT.war
Tu od razu nasuwa się pytanie – czemu zmieniłem nazwę aplikacji na ROOT.war? Otóż mechanizm S2I potrafi „wrzucić” aplikację w dwojaki sposób. Po pierwsze, jeśli nie zmienicie nazwy, to S2I „osadzi” aplikację w JBoss tak, że trzeba będzie wywoływać ją po kontekście. To dość niekomfortowe rozwiązanie w konteneryzacji. Zatem zmieniając nazwę aplikacji na ROOT.war, otrzymamy aplikację, która po starcie kontenera z JBoss będzie wywoływana jako root-context, czyli w adresie głównym serwera. To rozwiązanie jest zalecane przy konteneryzacji aplikacji. Jedna aplikacja (mikroserwis) – jedna instancja serwera JBoss.
2) tworzymy obiekty, które rozpoczną proces budowania nowego kontenera zawierającego wszystkie nasze składniki:
# oc new-build –name=clusterapp -i jboss-eap70-openshift:1.7
--strategy=source --binary
Nowy „kontener” z aplikacją będzie nosił nazwę clusterapp. Obrazem bazowym będzie nasz kontener od producenta, na który wskazuje obiekt ImageStream: jboss-eap70-openshift w namespace openshift. Przełącznik binary – oznacza, że proces S2I nie musi kompilować aplikacji, a tylko ja „wrzucić” do kontenera w odpowiedni miejsce. Czas zatem uruchomić procedurę budowania:
# oc start-build clusterapp --from-dir=. --follow=true --wait=true
W odpowiedzi uzyskamy informację o procesie budowania naszego „wszystko-mającego” kontenera:
Uploading directory "." as binary input for the build ...
Uploading finished
build.build.openshift.io/clusterapp-2 started
Receiving source from STDIN as archive ...
Using docker-registry.default.svc:5000/openshift/jboss-eap70-openshift@sha256:7a3acb825766a00fd865d9616bbd129fd747dd38b340704c835e47b9071de1d4 as the s2i builder image
Copying all war artifacts from /tmp/src directory into /opt/eap/standalone/deployments for later deployment...
'/tmp/src/ROOT.war' -> '/opt/eap/standalone/deployments/ROOT.war'
Pushing image docker-registry.default.svc:5000/jboss-cluster/clusterapp:latest ....
Pushed 6/7 layers, 100% complete
Pushed 7/7 layers, 100% complete
Push successful
Jeśli cała operacja budowania kontenera zakończy się sukcesem, to „zestaw roboczy”
o nazwie clusterapp mamy gotowy 🙂 Czas zatem przejść do kolejnego etapu. Musimy wystartować nasz kontener i umożliwić mu odnajdywanie kolejnych węzłów w obrębie klastra OpenShift’a.
1) Startujemy naszą instancję kontenera:
# oc new-app --name=clusterapp
--docker-image=docker-registry.default.svc:5000/jboss-cluster/clusterapp:latest
--insecure-registry
2) Sprawdzamy, czy pojawił się serwis:
# oc get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterapp ClusterIP 172.30.3.155 <none> 8080/TCP,8443/TCP,8778/TCP 16m
3) Musimy wystawić serwis z naszą aplikacją „na świat” i sprawdzamy, czy aplikacja działa:
# oc expose svc/clusterapp
Klikamy w przeglądarce adres URL wyświetlony przez powyższą komendę i sprawdzamy, czy nasza aplikacja się zgłosiła i czy działa poprawnie.
Na razie mamy tylko jeden węzeł, więc klaster z tego żaden ;-), ale do sprawdzenia funkcjonalności kontenera wystarczy.
Nadszedł czas na ostatni etap – uruchomienie kolejnych węzłów i połączenie ich w klaster JBoss. Pierwszym krokiem będzie powołanie do „życia” specjalnego serwisu, który będzie służył naszym węzłom do odnajdywania się wzajemnie w wirtualnej sieci klastra OpenShift.
Temat sieci w OpenShift jest zbyt obszerny, aby go tu teraz rozwijać. Ale myślę, że czytelnicy tego artykułu doskonale wiedzą, że kontenery na różnych węzłach mają różne IP i każdy z węzłów – jeśli ulegnie awarii też podniesie się z nowym adresem IP. Dla tego potrzebujemy serwisu, który poprowadzi nasze węzły do jednego celu – zjednoczenia się :-).
1) Powołujemy discovery service:
# oc expose dc/clusterapp --name=eap-app-ping
--port=8888 –-cluster-ip=None
Nazwa i port dla tego serwisu są dowolne. Ja wybrałem takie, jak widzicie powyżej.
2) Musimy przekazać naszym węzłom JBoss, gdzie się mają zgłaszać po tym, jak wystartują. W tym celu musimy przekazać do obiektu DeplymentConfig zmienne środowiskowe wskazujące na nasz nowo utworzony serwis:
#oc set env dc/clusterapp
-e JGROUPS_PING_PROTOCOL=openshift.DNS_PING
-e OPENSHIFT_DNS_PING_SERVICE_NAME=eap-app-ping
-e OPENSHIFT_DNS_PING_SERVICE_PORT=8888
3) Sprawdzamy, czy kontener z JBoss się przeładował. Powinien zmienić się numer rewizji na o jeden większy np.: z revision 1 na revision 2:
# oc get dc
NAME REVISION DESIRED CURRENT TRIGGERED BY
clusterapp 2 1 1 config,image(clusterapp:latest)
Teraz możemy „rozmnożyć” nasze węzły :-). Na początek proponuję powiększyć ilość węzłów do 2 i sprawdzić w logach czy klaster się skonfigurował. W tym celu musimy zeskalować obiekt DeploymentConfig do wartości 2:
# oc scale dc/clusterapp --replicas=2
Sprawdźmy, czy się udało:
# oc get pod
NAME READY STATUS RESTARTS AGE
clusterapp-2-766c45b68 1/1 Running 0 4m
clusterapp-2-7766c45b6 1/1 Running 0 4m
Mamy dwa węzły JBoss z aplikacją, która wykorzystuje klastrowanie. Zatem nie pozostaje nic innego jak spojrzeć w logi jednego czy drugiego kontenera:
# oc logs -f clusterapp-2-766c45b68
Powinniście znaleźć tam informację, że JBoss zestawił klaster z dwóch węzłów (szukajcie informacji [New cluster view]. Możecie teraz pobawić się aplikacją. Sprawdzić, czy działa na obu nodach i jeden z nich wyłączyć. Aplikacja powinna dalej pracować „jak gdyby nigdy nic”, ponieważ wewnętrzny load balancer (service) przełączy was do aplikacji na wciąż działającym węźle.
Bawcie się klastrem. Dokładajcie węzłów, obserwujcie logi, sprawdźcie, czy po zamknięcia wszystkich węzłów – z wyjątkiem ostatniego 😀 dalej wszystko działa poprawnie.
Życzę przyjemnej zabawy w klastrowanie i konteneryzowanie.