W klastrach Docker Swarm możemy uruchamiać serwisy w dwóch trybach – global oraz replicated. Jeżeli wybierzemy pierwszy z nich, to na każdym węźle wchodzącym w skład klastra zostanie uruchomiona dokładnie jednak kopia serwisu. W trybie replicated wskazujemy, ile kopii serwisu chcemy mieć uruchomionych, ale to Swarm decyduje, na których węzłach zostaną one uruchomione. Możemy w pewnym stopniu na nie wpływać za pomocą tak zwanych placement constraints i placement preferences. Jest też parametr MaxReplicas, którego wsparcie napisałem dla biblioteki docker-py oraz do modułu docker_swarm_service kolekcji community.docker w projekcie Ansible. Korzytanie z niego jest bardzo proste.
Serwisy w Docker Swarm
Klastry Docker Swarm nie pozwalają wskazać wprost dokładnego miejsca, czyli węzła, na którym kontener danej usługi ma zostać uruchomiony. Możemy jednak w pewien sposób sugerować schedulerowi, gdzie chcemy, aby kontenery działały. W najlepszym przypadku pozwoli na wskazanie tego konkretnego miejsca. Nie zawsze jest to gra warta świeczki. Zazwyczaj okazuje się, że manewrowanie parametrami staje się zbyt czasochłonne i niewygodne z punktu widzenia administratora.
Za pomocą placement constraints nakładamy różnego rodzaju ograniczenia dotyczące umieszczania kontenerów danej usługi na węzłach. Robimy to za pomocą określania pewnych specyficznych parametrów opisujących węzeł. Możemy na przykład sprecyzować, że chcemy, aby dana usługa działała jedynie na węzłach ze wskazaną etykietą. Placement preference to zaś informacja dla schedulera, jaki sposób chcielibyśmy, aby serwisy zostały rozmieszczone. Algorytm wtedy stara rozmieścić kontenery równomiernie pomiędzy węzły spełniające określone przez nas parametry. Pamiętajmy, że wskazane preferencje dotyczące miejsca docelowego nie są nigdy ściśle egzekwowane. Oznacza to, że ostateczna decyzja o miejscu uruchomienia kontenera zawsze należy do schedulera. Warto też podkreślić, że w przypadku używania placement constraints jeżeli żaden węzeł nie spełnia określonych przez nas parametru to usługa nie zostanie w ogóle uruchomiona. Natomiast w przypadku placement preference kontenery zostaną uruchomione na węzłach, które nie spełniają określonych przez nas parametrów.
O co chodzi z replikami serwisów w Docker Swarm?
MaxReplicas jest kolejnym obok constrains oraz preferences parametrem pozwalającym na wpływanie na sposób rozmieszcza kontenerów wskazanego serwisu w klastrze Swarm. Za jego pomocą ograniczymy liczbę kopii serwisu, czy liczbę uruchomionych kontenerów wskazanej usługi, na pojedynczym węźle. Domyślnie taki limit nie jest narzucony, Może się zdarzyć tak, że oczekując uruchomienia 4 replik, wszystkie one zostaną umiejscowione na jednym węźle. Taka sytuacja często nie jest to dla nas optymalna, a wręcz może być niepożądana. Awaria pojedynczego węzła spowoduje wtedy czasową niedostępność usługi. MaxReplicas nakłada górne ograniczenie na liczbę działających kopii serwisu na każdym z węzłów klastra. Parametr ten ma, co oczywiste, zastosowanie jedynie do serwisów uruchamianych w trybie replicated.
$ docker service create \ --name nginx \ --replicas 2 \ --replicas-max-per-node 1 \ nginx
Powyższe uruchomienie usługi spowoduje utworzenie dwóch replik serwisu nginx, ale z ograniczeniem, że tylko jedna kopia może aktywnie działać na każdym z węzłów klastra.
Moja przygoda z MaxReplicas
Parametr MaxReplicas został wprowadzony do Dockera w wydaniu 19.03. Nie jest to zatem nowa funkcja gdyż wydanie to zostało upublicznione w lipcu 2019 roku. Nie zwróciłem jednak na niego zbytniej uwagi aż do końcówki roku 2020. Wtedy w zgłoszeniach projektu Ansible, a dokładniej repozytorium community.docker, którego jestem opiekunem, pojawiło się zgłoszenie z prośbą, o dodanie wsparcia dla tego parametru w playbookach. I tu zaczęła się moja historia. Okazało się, że wsparcie dla tego parametru nie zostało jeszcze, mimo upływu roku, dodane do biblioteki docker-py, czyli oficjalnej biblioteki Dockera dla języka Python. W takiej sytuacji są trzy wyjścia: mogłem przestać interesować się tematem, otworzyć feature request z prośbą, aby opiekunowie projektu dodali wskazaną funkcjonalność, albo napisać ją samemu i zgłosić jako poprawkę (pull request) do projektu.
Wiele razy w Szkole DevNet jak i na wielu konferencjach mówiłem, aby nie bać się programowania. Okazało się że dopisanie obsługi MaxReplicas nie było wcale takie trudne. Pod koniec grudnia wysłałem swoją poprawkę do docker-py do akceptacji. Jak to w dużych projektach jest zazwyczaj musiała się ona parę tygodni “odleżeć”. Dopiero w lutym opiekunowie projektu najpierw poprosili o drobne poprawki w kodzie, a następnie zaakceptowali mój pull request. Napisana przeze mnie funkcjonalność została umieszczona w wydaniu 4.4.3.
MaxReplicas w Ansible
Kolejnym krokiem było dodanie obsługi MaxReplicas w module docker_swarm_service. W tym wypadku nie trzeba było czekać tygodnie ale dwie godziny, aby pozostali opiekunowie tej kolekcji zgłosili swoje uwagi do kodu. Mój pull request zaakceptowano ostatecznie 23 lutego. Pozostało jedynie poczekać na 7 marca, czyli do dnia wydania następnego oficjalnej wersji kolekcji oznaczonej numerem 1.3.0. Jest ona częścią najnowszego wydania Ansible oznaczonego numerem 3.1.0.
Stosowanie parametru MaxReplicas w module jest bardzo proste, dlatego zostawię was na koniec tylko z przykładem który umieściłem w dokumentacji:
- name: Set placement preferences community.docker.docker_swarm_service: name: myservice image: alpine:edge placement: preferences: - spread: node.labels.mylabel constraints: - node.role == manager - engine.labels.operatingsystem == ubuntu 14.04 replicas_max_per_node: 2