Tworzenie klastra w Liferayu - przewodnik

Przez Rafał Pydyniak | 2021-05-26

Uwaga Poniższy artykuł jest tłumaczeniem oryginału z angielskiej wersji strony. Jeżeli interesują Cię tego typu tematy to polecam czytać wersje angielską z uwagi na to, że artykuły zawsze w pierwszej kolejności tworzone są tam, a strona polska czasami nawet bardzo długo jest "do tyłu".

Witaj! W tym artykule chciałbym zaprezentować możliwości stworzenia klastra w CMSie Liferay. W moim przykładowym środowisku użyję Dockera, aczkolwiek kroki będą analogiczne jeżeli po prostu instalujesz w sposób "tradycyjny" dwie instancje (lub więcej) na jednym serwerze lub kilku.

Postaram się pokazać cały proces tworzenia środowiska krok po prostu, tak abyś mógł jak najlepiej zrozumieć "jak to działa". Niemniej jednak gdybyś chciał od razu zobaczyć stworzone środowisko - jest ono dostępne na moim githubie.

Ważna sprawa - skupię się na konfiguracji Liferaya i prostocie całego środowiska. W związku z tym nie będę tworzył klastra bazy danych/Elasticsearcha/Load Balancera. Jeżeli byś tego potrzebował to rozszerzenie mojej konfiguracji powinno być dość proste, a w Internecie można znaleźć mnóstwo materiałów na te tematy. W moim przykładowym środowisku będzie jedynie podstawowa konfiguracja tych trzech, a klaster powstanie tylko dla Liferaya.

Wymagania wstępne

Aby rozpocząć będziesz potrzebował Dockera na swojej maszynie. Nie będę omawiał podstaw Dockera/konteneryzacji/Docker Compose więc dobrze by było abyś miał chociaż podstawowe pojęcie na ich temat, aczkolwiek nie jest to konieczne aby jako tako prześledzić artykuł.

Jeżeli planujesz "na bieżąco" tworzyć środowisko podczas czytania to możesz rozpocząć od stworzenia nowego katalogu z plikami/katalogami jak na screenie poniżej. Pliki oczywiście na tym etapie mogą być puste.

Początkowa struktura plików
Początkowa struktura plików

W tym samym katalogu stwórz dwa foldery "portal-node-1" i "portal-node-2":

Początkowa struktura katalogów "deploy"
Początkowa struktura katalogów "deploy"

Docker storage - mountowanie, wolumeny (volumes)

Uwaga Po opublikowaniu wstępnej wersji tego postu pomyślałem o jednej dodatkowej kwestii - w poniższych instrukcjach użyłem "Docker volumes" do przetrzymywania niektórych danych (Elasticsearch/dokumenty Liferaya), a inne "zamountowałem" (katalogi deploy: portal-node-1/portal-node-2). Dodatkowo niektóre pliki (konfiguracyjne, jary, portal-ext.properties) kopiuję używając "Dockerfile", a niektórych danych (MySQL) nie zapisuję nigdzie.

To podejście oczywiście pokazuje różne możliwości, ale z drugiej strony powinienem wspomnieć o tym, że jest to podejście "pokazowe". Jeżeli planujesz stworzyć środowisko, które będzie realnie wykorzystywane to powinieneś najpierw zapoznać się ze wszystkimi możliwościami przechowywania plików. Prawdopodobnie najlepiej zacząć od dokumentacji Dockera. Jest to istotne gdyż każda metoda przechowywania danych ma swoje plusy i minusy.

Proces tworzenia środowiska

Kontenery Liferaya

Chcemy stworzyć klaster, a więc potrzebujemy dwóch lub więcej instancji Liferaya. W przypadku tego tutorialu użyję dwóch, ale użycie większej liczby jest analogiczne.

Tak więc od początku - jak w ogóle zacząć? Skoro chcemy użyć Docker Compose potrzebujemy stworzyć plik docker-compose.yml. Plik ten służy do definicji tego jakie kontenery chcemy mieć stworzone oraz opisuje to jak mają one zostać stworzone (np. gdzie chcemy przechować dane kontenera). Aby jednak rozpocząć pracę nad docker-compose.yml potrzebujemy obrazów dla naszych kontenerów. W większości użyjemy gotowych, już zbudowanych obrazów, ale w przypadku Liferaya stworzymy własny obraz, który rozszerzy oficjalny.

Aby tego dokonać tworzymy plik "Dockerfile-liferay" z następującą treścią:

FROM liferay/portal:7.0.6-ga7
MAINTAINER Rafal Pydyniak <[email protected]>

COPY ./configs/portal-ext.properties $LIFERAY_HOME/
COPY ./configs/\*.config $LIFERAY_HOME/osgi/configs/
COPY ./configs/\*.jar $LIFERAY_HOME/osgi/portal/

Tak więc tworzymy nasz własny obraz na bazie oficjalnego w wersji 7.0.6 GA7. Użyłem takiej wersji, ale wszystkie 7+ będą działały podobnie. W naszej wersji obrazu dodajemy kopiowanie portal-ext.properties do katalogu LIFERAY_HOME a także kopiujemy niezbędne konfiguracje/JARy o czym będzie później. Moglibyśmy oczywiście zamiast tego zmapować nasz dysk na dysk kontenera, ale dla przykładu użyjemy tej metody szczególnie, że pliki konfiguracyjne/JARy nie będą się zmieniały w czasie. Prawdopodobnie co do portal-ext.properties lepiej byłoby go zmapować, ale póki co zostawmy to w ten sposób.

Jeżeli chciałbyś przetestować obraz możesz to zrobić budując go, uruchamiając a następnie łącząc się do kontenera. Następnie możesz zweryfikować czy pliki rzeczywiście zostały skopiowane. Poniższe komendy pomogą Ci w tym zadaniu:

docker build -t my-liferay-7-tutorial -f Dockerfile-liferay . # budujemy "Dockerfile-liferay". Stworzony obraz będzie się nazywał "my-liferay-7-tutorial". Kropka na końcu służy do wskazania tak zwanego "context directory" czyli katalogu, który ma być "wyjściowym" między innymi przy kopiowaniu w komendach COPY.
docker run -d my-liferay-7-tutorial # Uruchamiamy stworzony obraz
docker exec -it CONTAINER_NAME /bin/bash # Podłączamy się do kontenera, tak aby przetestować czy wszystko działa poprawnie. Odpowiednią nazwę kontenera (do podstawienia pod CONTAINER_NAME) możesz sprawdzić używajć komendy docker container ls.

Komentarze do komend opisują działanie.

Stwórzmy docker-compose.yml i wykorzystajmy nasz obraz

Kolejnym krokiem będzie stworzenie kontenerów Liferaya czyli naszych instancji. Wykorzystamy do tego wspomniany wcześniej docker-compose.yml. Pierwsza wersja tego pliku poniżej:

version: '3.3'
services:
  liferay-portal-node-1:
    build:
      context: .
      dockerfile: Dockerfile-liferay
    ports:
      - '6080:8080'
      - '21311:11311'
    hostname: liferay-portal-node-1.local
    volumes:
      - lfr-dl-volume:/opt/liferay/data/document_library
      - ./deploy/portal-node-1:/opt/liferay/deploy
    depends_on:
      - mysql
      - es-node-1
  liferay-portal-node-2:
    build:
      context: .
      dockerfile: Dockerfile-liferay
    ports:
      - '7080:8080'
      - '31311:11311'
    hostname: liferay-portal-node-2.local
    volumes:
      - lfr-dl-volume:/opt/liferay/data/document_library
      - ./deploy/portal-node-2:/opt/liferay/deploy
    depends_on:
      - mysql
      - liferay-portal-node-1
      - es-node-1
volumes:
  lfr-dl-volume:

Zdefiniowaliśmy więc dwie instancje Liferaya:

  • liferay-portal-node-1
  • liferay-portal-node-2

wykorzystując stworzony wcześniej obraz oraz ustawiając aktualny katalog jako "context" (wykorzystywany do wskazania skąd obraz ma brać pliki). Dodatkowo definiujemy porty, które mają być dostępne poza kontenerem. W ramach przypomnienia: zapis "6080:8080" oznacza, że port 8080 wewnątrz kontenera będzie dostępny na porcie 6080 "na zewnątrz". Tak więc wszystkie dostępne porty to:

  • 6080 - instancja nr 1 (node1)
  • 7080 - instancja nr 2 (node2)
  • 21311 - gogo shell (do obsługi OSGI) instancji nr 1
  • 31311 - gogo shell (do obsługi OSGI) instancji nr 2

Ustawiamy również hostname dla naszych instancji, tak aby móc je rozróżnić oraz używamy volumes do dwóch rzeczy:

  • Przechowywania danych documents & media jako wolumen dockerowy. Używamy tego samego wolumenu dla obu instancji dzięki czemu pliki są współdzielone między instancjami. Jest to krok wymagany przy klastrowaniu w Liferayu
  • Mapujemy nasze lokalne deploy/portal-node-X na katalog deploy w kontenerze. W ten sposób możemy łatwo deployować nasze moduły bezpośrednio do kontenerów

Na końcu definiujemy także wszystkie nasze wolumeny - póki co jedynie "lfr-dl-volume"

Możesz także zwrócić uwagę na fragment "depends_on". Jest to po prostu deklaracja tego, który kontener "zależy" od którego. W ten sposób możemy zdefiniować, że chociażby nasze instancje Liferaya nie powinny startować przed bazą danych czy Elasticsearchem co powodowałoby błędy. Na ten moment oczywiście nie mamy w ogóle zdefiniowanej bazy/Elasticsearcha, ale niedługo do tego przejdziemy.

Documents & media

Warto zwrócić uwagę, że produkcyjnie prawdopodobnie powinieneś użyć "Advanced File System Store" jako repozytorium danych zamiast domyślnego "simple". W tym wypadku użyłem opcji domyślnej. Zmiana na "Advanced..." jest zalecana, aczkolwiek nie wymagana do klastrowania. Jeżeli chciałbyś dowiedzieć się więcej o repozytoriach danych w Liferayu możesz sprawdzić ten artykuł.

Konfiguracja bazy danych

Do uruchomienia Liferaya niezbędna jest baza danych (nie chcemy używać "domyślnej" i "wbudowanej" H2). W docker-compose dodajemy poniższy blok:

mysql:
    image: mysql:5.6
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
      MYSQL_DATABASE: lportal
    ports:
      - "33066:3306"

Jest on raczej dość jasny. Używamy oficjalnego obrazu, udostępniamy porty oraz ustawiamy hasło dla roota i tworzymy bazę danych przez zmienne środowiskowe. W tym wypadku używam MySQL, ale równie dobrze możesz użyć dowolnej innej - w większości wypadków z łatwością znajdziesz odpowiedni obraz na DockerHub

Musimy oczywiście również zdefiniować połączenie do naszej bazy danych w samym Liferayu. Aby tego dokonać używamy pliku konfiguracyjnego portal-ext.properties. Plik ten kopiowany jest do kontenera podczas tworzenia naszego obrazu co zdefiniowaliśmy wcześniej. Konfiguracja bazy będzie następująca:

jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.url=jdbc:mysql://mysql:3306/lportal
jdbc.default.username=root
jdbc.default.password=my-secret-pw

Możesz oczywiście dodać tutaj dowolne inne wpisy konfiguracyjne tak jak byś to zrobił w kazdym innym przypadku. Wrócimy jeszcze do tego pliku później, podczas konfiguracji klastra.

Uwaga: Jak wspominałem wcześniej - w powyższej konfiguracji nigdzie nie przechowujemy danych bazy danych. Usunięcie kontenera spowoduje utrate danych. Aby tego uniknąć możesz stworzyć volume (zobacz konfiguracje Elasticsearch dla przykładu) lub stworzyć dodatkowy katalog i zmapować go:

volumes:  
- ./data/mysql:/var/lib/mysql

Konfiguracja Elasticsearch

Jednym z kroków tworzenia klastra, opisanym w dokmentacji, jest instalacja osobnej instancji Elasticsearch (ES). W związku z faktem, że tworzymy klaster nie możemy użyć "wbudowanego", gdyż wszystkie nasze instancje muszą komunikować się z tym samym serwerem ES (lub serwerami w klastrze ES). W naszym wypadku potrzebujemy wersji 2.4.6, ale jeżeli używasz innej wersji Liferaya możesz potrzebować innej. Sprawdź artykuł opisujący instalacje instancji ES gdzie opisany jest proces sprawdzania wersji.

Gdy już znajdziesz odpowiednią wersje musisz dodać tworzenie serwera ES do pliku docker-compose.yml. W moim przypadku w wersji 2.4.6 wygląda to jak poniżej:

es-node-1:
    image: elasticsearch:2.4.6-alpine
    environment:
      - cluster.name=elasticsearch
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

Czyli po raz kolejny. Na początku definiujemy obraz, którego chcemy użyć. Następnie mamy dodatkowe opcje konfiguracyjne, które są raczej dość jasne np. ustawienia pamięci oraz kilka ustawień, które mogą być mniej jasne, ale łatwo odnaleźć ich znaczenie w google (szczególnie ulimits). Istotna część to "cluster.name" - musi być ona unikalna dla twojego środowiska, ale jednocześnie może to być dowolny String. W naszym wypadku oczywiście dowolna nazwa będzie unikalna, ale jeżeli przykładowo na jednym serwere masz dwa środowiska (np. DEV i TEST) to musisz zwrócić uwagę, aby instancje ES miały inne cluster.name, tak aby można było je rozróżnić.

Możesz też zauważyć, że używamy wolumenu dockerowego o nazwie esdata1. Pamiętajmy, że musimy dodać jego definicje również na końcu naszego docker-compose. Nasza definicja wolumenów będzie teraz wyglądała następująco:

volumes:
  lfr-dl-volume:
  esdata1:

Użycie zewnętrznego Elasticsearcha w Liferayu

Skonfigurowaliśmy naszą instancje ES, a teraz musimy jej jeszcze użyć w Liferayu. Aby tego dokonać użyjemy pliku konfiguracyjnego OSGI. Stwórzmy plik o nazwie "com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config" w katalogu "configs" o następującej zawartości:

operationMode="REMOTE"
transportAddresses="es-node-1:9300"
clusterName="elasticsearch"

Kod ten sprawia, że Liferay będzie używał zewnętrznej instancji ES zamiast wbudowanej. Ustawiamy również wcześniej zdefiniowany clusterName oraz adress naszej instancji ES. Jeżeli zastanawiasz się dlaczego używamy es-node-1:9300 a nie jakiegoś adresu IP typu 127.0.0.1 czy localhost to jest tak dlatego, że wszystkie serwisy zdefiniowane w docker-compose są w tej samej sieci dockerowej, a co za tym idzie są przez siebie osiągalne przez ich nazwę kontenera. Bindowanie adresów jest obsługiwane przez samego Dockera - generalnie dodawane są odpowiednie wpisy w pliku /etc/hosts.

Nic więcej nie musimy robić, gdyż już wcześniej zdefiniowaliśmy, że wszystkie pliki konfiguracyjne mają być kopiowane do LIFERAY_HOME/osgi/configs - zrobiliśmy to w naszym obrazie Liferaya.

Używamy wersji "multiple" niektórych modułów

W Liferayu 7.0 CE, którego używamy na potrzeby tego artykułu musimy dokonać dodatkowego kroku opisanego tutaj. Generalnie domyślnie ta wersja Liferaya nie wspiera uruchamiania w trybie klastra. Aby dodać tą funkcje musimy "podmienić" trzy moduły:

  • com.liferay.portal.cache.single
  • com.liferay.portal.cluster.single
  • com.liferay.portal.scheduler.single

Na odpowiednie wersje z końcówką "multiple". Te domyślne jednocześnie musimy zdezaktowywać. W artykule wspomnianym wyżej opisane są kroki kompilacji tych modułów więc nie będę wchodził w szczególy. Opiszę jedynie co gdy już je zdobędziemy.

Uwaga: Jeżeli również używasz wersji 7.0.6 to możesz te moduły po prostu pobraż z przykładowego repozytorium GITa, które podawałem wcześniej

Uwaga 2: Możliwe, że w ogóle nie potrzebujesz wykonywać tych kroków jeżeli Twoja wersja już wspiera moduły w wersji "multiple". Jeżeli używasz Liferaya w wersji DXP to z pewnością tak jest. Jeżeli wersji CE to możesz to sprawdzić w gogo shellu - po prostu uruchom komendę "lb portal.scheduler" i sprawdź czy jest uruchomiony moduł "Liferay Portal Scheduler Multiple". Jeżeli tak to pomiń ten krok.

Gdy już masz wersje "multiple" modułów musisz:

  • zdeployować je do katalogu "configs" w kontenerze - stworzony przez nasz obraz Liferaya to zrobi. Wystarczy, że wrzucisz moduły do katalogu "configs"
  • włączyć ignorowanie wersji "single" - aby tego dokonać stwórz nowy plik "com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config" w katalogu "configs" z następującą treścią blacklistBundleSymbolicNames="com.liferay.portal.cache.single,com.liferay.portal.cluster.single,com.liferay.portal.scheduler.single"

Obraz Dockerfile-Liferay zrobi resztę czyli skopiuje pliki do odpowiednich katalogów.

Włączenie klastra

Ok, przygotowaliśmy już dość dużo, ale nadal nie dodaliśmy żadnych ustawień odnośnie klastrowania do samego Liferaya. Na szczęście ten krok będzie bardzo prosty - jedyne co musimy zrobić to dodać poniższą linijkę do pliku portal-ext.properties:

cluster.link.enabled=true

Czy to wystarczy? Okazuję się, że tak - przynajmniej w niektórych przypadkach. Generalnie ustawienie te sprawia, że instancje Liferaya zaczynają używać kanału JGroups do komunikacji między sobą na domyślnych IP i portach. Komunikacja odbywa się za pomocą połączenia UDP multicast co jest bardzo istotne gdyż zadziała to tylko jeżeli instancje są w stanie komunikować się za pomocą multicasta. Niestety nie zawsze tak jest. Nawet jeżeli Twoje serwery są w tej samej podsieci to i tak komunikacja przez multicasta może nie działać. Przykładowo Azure nie wspiera multicasta w swoich wirtualnych sieciach i nie ma planów na dodanie tego, przynajmniej gdy to sprawdzałem jakiś czas temu. W takim wypadku najlepiej sprawdzić dokumentacje Liferaya gdzie opisane są inne metody - sam korzystałem z opcji unicasta przez TCP i nie jest to specjalnie trudne - dochodzi jedynie drobna konfiguracja.

W naszym wypadku jednak takie rozwiązanie jest jak najbardziej wystarczające, gdyż serwisy w ramach jednego docker-compose.yml mogą komunikować się przy użyciu multicasta.

Ostatnia rzecz, którą możemy dodać do pliku portal-ext.properties to:

web.server.display.node=true

Konfiguracja ta sprawi, że każda instancja w klastrze będzie wyświetlać informacje o tym jaki to serwer. Jest to przydatne, gdyż normalnie wchodząc na wspólny adres nie wiemy z którym serwerem się łączymy. Informacja taka przydaję się do testów tego czy klaster rzeczywiście działa.

Jeżeli chcesz poczytać więcej o tym jak działa komunikacja to najlepiej sprawdzić oficjalną dokumentacje. Jest to dość proste i kilka minut powinno wystarczyć, żeby zrozumieć idee.

Load balancing

Wstęp

Od razu zaznaczę - krok ten jest w pełni opcjonalny. Nasza konfiguracja da nam dwie instancje na dwóch różnych portach. Jeżeli robisz to np. na serwerze to możesz już mieć jakiegoś load balancera czy serwer HTTP (np. Apache/Nginx) i wtedy konfiguracje powinieneś najlepiej przeprowadzić tam a nie dodawać kolejną warstwę w Docker Compose. Jeżeli jednak nie masz niczego co mogłoby posłużyć za reverse proxy i load balancera to pokaże jak dodać load balancing przy użyciu HAProxy, który zdefiniujemy jako kolejny serwis w Docker Compose.

Uwaga jeżeli pomijasz ten krok i chcesz dokonać konfiguracji sam to należy zwrócić uwagę, że do działania niezbędne jest ustawienie tak zwanego "sticky session". Odpowiednie instrukcje na pewno znajdziesz w dokumentacji danego narzędzia.

Konfiguracja Dockerowa

Jeżeli jednak wybierasz load balancing po stronie Docker Compose to poniżej przedstawiam niezbędne kroki. Load balancing jest potrzebny bo oczywiście nie chcemy, aby user sam musiał wybierać odpowiedni serwer, a zamiast tego powinien móc wejść na jeden wspólny adres np. app.pydyniak.com (nic tam nie ma, nie musisz sprawdzać ;)). Rozdzielanie ruchu powinno nastomiast działać "w tle". Poniższy diagram pokazuje mniej więcej idee rozwiązania:

Diagram architektury
Diagram architektury
Tak jak wspominałem ja użyję HAProxy, ale możesz użyć dowolnego innego narzędzia. Krok ten jest zupełnie niezależny od Liferaya. Jedyne co to należy ustawić sticky session - patrz uwaga wyżej

A więc tak: tworzymy kolejny obraz - tym razem o nazwie "Dockerfile-haproxy" i kopiujemy poniższą konfiguracje:

FROM haproxy:2.3.4
COPY ./configs/haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

Nie ma tutaj raczej nic specjalnego. Tworzymy po prostu nasz obraz na bazie oficjalnego obrazu haproxy:2.3.4 i dodajemy kopiowanie pliku konfiguracyjnego. Jest to sposób zalecany zresztą w dokumentacji obrazu na Docker Hubie. Następnie definiujemy tworzenie naszego serwisu HAProxy w pliku docker-compose.yml:

haproxy:
    build:
      context: .
      dockerfile: Dockerfile-haproxy
    ports:
      - '80:80'
    hostname: lb-haproxy.local

Wskazujemy więc na nasz obraz (dockerfile) i używamy aktualnego katalogu jako kontekstu. Mapujemy też port wewnątrz kontenera (80) na port naszej maszyny (również 80). Ustawiamy też hostname.

Teraz czas na wspomniany wcześniej plik haproxy.cfg, którego kopiowanie zdefiniowaliśmy w Dockerfile. Tworzymy więc ten plik i wrzucamy następującą konfiguracje:

global
   stats timeout 30s
   daemon

defaults
   log global
   mode http
   option httplog
   option dontlognull
   timeout connect 5000
   timeout client 50000
   timeout server 50000

frontend http_front
   bind 0.0.0.0:80
   default_backend http_back

backend http_back
   balance roundrobin
   cookie JSESSIONID prefix
   server liferay-portal-node-1 liferay-portal-node-1:8080 check cookie s1
   server liferay-portal-node-2 liferay-portal-node-2:8080 check cookie s2

Jest to dość długa konfiguracja, ale co najważniejsza robi to czego potrzebujemy. Najważniejsze linijki są na końcu i zaczynają się od "backend http_back". W linijkach tych robimy dwie rzeczy:

  • Ustawiamy sposób rozdzielania ruchu na roundrobin - jest to po prostu jeden ze standardowych sposobów dzielenia ruchu, w którym kolejne żądania są na zmiane przydzielane do serwerów. Dokładny opis można oczywiście znaleźć chociażby na Wikipedii
  • Definiujemy nasze serwery oraz używamy wspomnianego wcześniej "sticky session". Jeżeli nie wiesz czym sticky session jest to już tłumaczę - chodzi o to, że jeżeli user wejdzie na naszą stronę i trafi przykładowo na NODE2 (drugą instancje) to będzie przydzielony do tego samego serwera przez cały czas trwania sesji. Jest to niezbędne, gdyż sesja nie jest dzielona czy też kopiowana między instancjami Liferaya. Dokładniejszy opis jak sticky session działa w HAProxy można znaleźć w artykule na blogu HAProxy - zachęcam do przejrzenia. Idea jest bardzo prosta więc jeżeli nigdy wcześniej nie miałeś z tym do czynienia to warto poczytać.

Uruchamiamy nasz klaster

W końcu koniec. Zdefiniowaliśmy wszystko czego potrzebowaliśmy. Mam nadzieję, że opis był zrozumiały i wiesz dlaczego zrobiliśmy to co zrobiliśmy. Teraz potrzebujemy uruchomić nasze środowisko. Możemy tego dokonać komendami poniżej:

docker-compose build # budujemy nasze całe środowisko
docker-compose up -d liferay-portal-node-1 # uruchamiamy pierwszy serwer
# Czekamy aż wystartuje. Możesz sprawdzić docker-compose logs, aby zwalidować na jakim etapie jest start serwera
docker-compose up -d liferay-portal-node-2 # uruchamiamy drugi serwer
docker-compose up -d haproxy # uruchamiamy nasze Reverse Proxy

Podczas uruchamiania możemy sprawdzić logi (docker-compose logs) i zweryfikować, że rzeczywiście instancje komunikują się między sobą:

liferay-portal-node-1_1  | 2021-01-24 23:10:59.862 INFO  [Incoming-2,liferay-channel-control,liferay-portal-node-1-3920][JGroupsReceiver:90] Accepted view [liferay-portal-node-1-3920|1] (2) [liferay-portal-node-1-3920, liferay-portal-node-2-36188]
liferay-portal-node-1_1  | 2021-01-24 23:11:00.057 INFO  [Incoming-2,liferay-channel-transport-0,liferay-portal-node-1-47303][JGroupsReceiver:90] Accepted view [liferay-portal-node-1-47303|1] (2) [liferay-portal-node-1-47303, liferay-portal-node-2-30594]

Jeżeli po uruchomieniu wszystkiego wejdziesz na serwer na port :80 zobaczysz, że zostałeś połączony do jednej z dwóch instancji (jeżeli oczywiśćie dodałeś wyświetlanie wiadomości o czym było wcześniej):

Informacja o tym na którym jesteśmy serwerze
Informacja o tym na którym jesteśmy serwerze

Możesz też spróbować połączyć się do serwera z innej przeglądarki - prawdopodobnie zostaniesz połączony tym razem do drugiej instancji. Jeżeli tak to super - udało się. Niestety to nie koniec testów. Na ten moment wiemy, że:

  • W teorii mamy połączenie między serwerami (patrząc na logi)
  • HAProxy wydaję się działać bo jesteśmy przekierowywani na odpowiednie instancje. Można ewentualnie też sprawdzić cookiesy, aby to potwierdzić (szczegóły we wspomnianym wcześniej artykule o tym jak działa sticky session w HAProxy)

W praktyce nadal jednak powinniśmy dokonać kolejnych testów przykładowo:

  • Czy działa Elasticsearch - czy jeżeli dodamy jakiś content na node1 to czy będzie można go wyszukać na node2?
  • Czy połączenie rzeczywiście działa? Czy jeżeli dodamy jakiś content na node2 to czy będzie widoczny na node1?
  • Czy pliki Documents & Media rzeczywiście są współdzielone? Czy plik dodany na jednej z instancji będzie widoczny na drugiej?
  • Czy jeżeli zatrzymamy jedną z instancji - czy ruch zostanie odpowiednio przekierowany do drugiej?

Oczywiście możesz też pomyśleć o innych testach szczególnie jeżeli planujesz uruchomić podobną konfiguracje na produkcji.

Wydajność

Jeżeli uruchomisz środowisko to możesz zauważyć, że serwery nie są zbyt szybkie. Powód tego jest prosty - nie zmienialiśmy żadnych ustawień pamięci, a domyślne są dość niskie. Możesz dodać więcej pamięci poprzez stworzenie pliku configs/setenv.sh. Zawartość tego pliku można skopiować z serwera, a następnie jedynie nadpisać ustawienia dla pamięci. Następnie musisz jeszcze dodać odpowiedni wpis COPY w Dockerfile-liferay - plik ten musi być kopiowany do /opt/liferay/tomcat-8.0.32/bin/ ($LIFERAY_HOME/tomcat-8.0.32/bin/). W przykładowym repozytorium krok ten został dodany więc możesz podejrzeć jak zostało to zrobione. Dodatkowa rzecz, którą należy wziąć pod uwagę - możliwe, że potrzebne będzie zwiększenie zasobów dostępnych Dockerowi, gdyż domyślnie są one mocno ograniczone.

Na koniec

Mam nadzieję, że artykuł był zrozumiały. Całość trochę się rozciągnęła i powstało dość dużo tekstu, ale starałem się jak najbardziej wytłumaczyć co zrobiłem i dlaczego w taki a nie inny sposób. Sam projekt Docker Compose jest dość niewielki więc jeżeli tylko rozumiesz podstawy Dockera to myślę, że nie powinno być problemu z jego uruchomieniem. W artykule tym użyłem wersji 7.0.6, ale kroki są analogiczne również dla innych wersji. Raz jeszcze przypominam, że cały kod można znaleźć na repozytorum na Githubie

Jeżeli masz jakiekolwiek pytania jak zawsze zachęcam do kontaktu.

Copyright: Rafał Pydyniak