Ansible – klaster na Docker Swarm + Portainer
Czym jest Docker Swarm?
Swarm jest natywnym rozwiązaniem klastrowym dostarczanym wraz z Dockerem od Docker Engine 1.12. W obecnym świecie, gdzie królują takie rozwiązania jak Kubernetes, czy Mesos, Docker Swarm jest często pomijany, nawet przez osoby, które zaczynają się dopiero uczyć samego Dockera. Jest to dość dużym błędem. Warto poznać to rozwiązanie już podczas nauki, tym bardziej, że stawia ono na maksymalną prostotę, jeśli chodzi o konfiguracje i użytkowanie. W przypadku Swarma, z racji tego, że jest częścią ekosystemu Dockera, rozwijaną i wspieraną przez samego producenta, możemy liczyć na ciekawe rozwiązania, które bez wątpienia czynią narzędzie konkurencyjnym w porównaniu z innymi tego typu klastrami.
Zalety Docker Swarm
Swarm jest domyślnie przeznaczony dla systemów produkcyjnych, możemy znaleźć w nim takie zalety jak:
- Prosta konfiguracja i zarządzanie klastrem
- Bezpieczeństwo – po zainicjowaniu klastra komunikacja w Swarmie jest szyfrowana TLS (nie da się zrezygnować, naturalnie o szyfrowanie naszych aplikacji w kontenerach musimy zadbać sami)
- Load Balancer – Swarm bazuje na wudowanym rozwiązaniu „Routing Mesh” dzięki czemu po przypisaniu do nodów domeny, nawet jeśli zapytanie trafi na serwer, który nie posiada danego kontenera, ruch zostanie skierowany do najbliższej instancji na której taki kontener się znajduje.
- Wysoka dostępność – klaster działa w strukturze managerów i workerów z aktywnym leaderem, dzięki czemu po awarii leadera, kolejny manager zostaje na niego promowany, sam klaster działa nadal bez awarii. Oczywiście przy zachowaniu „best practice” dla HA czyli 3 managerów w tym jeden działający jako leader.
- Prosty do konfiguracji za pomocą narzędzi do zarządzania konfiguracją jak Puppet, czy Ansible
- Wsparcie producenta – Swarm jest rozwijany przez Dockera.
- Działa z innymi narzędziami Dockera np. Docker-Compose, Docker Engine
- Wysokiej jakości dokumentacja – często się zdarza, że producenci po stworzeniu dokumentacji do danego narzędzia wraz z jego rozwojem zapominają o niej. Nie w tym przypadku, każda najmniejsza zmiana w Swarmie, momentalnie jest wyczerpująco opisywana w dokumentacji.
- Skalowalność – możemy wystawiać kontenery jako repliki i podawać samemu ilość w jakiej chcemy dany kontener, lub globalnie, czyli każdy z nodów wpięty do klastra będzie posiadał z automatu dany kontener
- Uzgadnianie stanu – managerowie stale monitorują stan klastra, między stanem rzeczywistym, a stanem pożądanym. Jeśli mieliśmy skonfigurowany kontener w 5 replikach, a nody obsługujące 2 z tych replik uległyby awarii, manager utworzy dwie nowe repliki, aby zastąpić uszkodzone, przypisując je do działających nodów.
- Aktualizacje bez przerwy – klaster daje nam kontrolę nad opóźnieniem jakie ma być między wdrażanymi serwisami na inne nody, dzięki temu jeśli coś pójdzie źle, możemy w łatwy sposób przywrócić zadanie do poprzedniej wersji, bez wpływu na produkcję
Czym jest Portainer?
Portainer jest open-soruce’owym, bazującym na aplikacji www, GUI dla Dockera i świetnie nadaje się do zarządzania Swarmem. Możemy z jego pomocą zrobić wszystko to, co za pomocą CLI z tym, że okraszone bardzo ładną i przyjemną dla oka grafiką.
Portainer działa na zasadzie agent-serwer, co oznacza, że będziemy potrzebowali agenta na każdym z nodów, który będziemy posiadać. Nie musimy się tym jednak przejmować, w dalszej części zobaczymy jak łatwo i prosto zrobimy to za pomocą Swarma.
Zalety Portainera
- Prosta instalacja (gotowy stack Docker-Compose)
- Przejrzyste GUI
- Pełna integracja z Docker i Docker Swarm
- Można spiąć wiele klastrów
- Tworzenie serwisów Swarma jak i samych kontenerów
- Zarządzanie wolumenami razem z NFS
- Gotowe w pełni konfigurowalne szablony dla kontenerów jaki klastra
- Zarządzanie użytkownikami
- LDAP
- Możliwość wystawiania stacków za pomocą Docker-Compose
- Zarządzanie siecią
- Bezpośredni dostęp do konsoli danego kontenera bezpośrednio z GUI
- Logi z kontenerów
- Graficzne przedstawienie naszego klastra
- Informacje o zasobach zużywanych przez dany kontener
- Zarządzanie obrazami
- Dodawanie i zarządzanie własnymi registrami
Infrastruktura dla Docker Swarm
Playbooka będziemy pisać pod maszyny wystawiane na CentOS 7, a nasza Infrastruktura, będzie wyglądała następująco:

Zgodnie z najmniejszym dopuszczalnym modelem wysokiej dostępności przez Swarma (czyli, przy trzech managerach), mamy tolerancję, że jeden może nie działać. W takim wypadku możemy, albo naprawić problematycznego managera, albo wypromować workera na managera.
A więc potrzebujemy 5 jednakowych maszyn, pod kontrolą czystego systemu CentOS 7, aby zbudować powyższy model. W moim przypadku będzie to wyglądało następująco:
- Manager1 (Leader): 192.168.56.10
- Manager2: 192.168.56.13
- Manager3: 192.168.56.14
- Worker1: 192.168.56.11
- Worker2: 192.168.56.12
Środowisko dla Swarma zostało wystawione za pomocą doskonałego narzędzia, jakim jest Vagrant i znaleźć je można na końcu artykułu.
Piszemy playbooka
Podstawowa konfiguracja
Gdy mamy już nasze środowisko pod klaster, czas zabrać się za pisanie playbooka, wykorzystamy gotowy moduł docker_swarm do zarządzania naszym Swarmem. Struktura katalogów wygląda następująco:
swarm_playbook/
├── ansible.cfg
├── group_vars
├── hosts
├── roles
└── site.yml
Wraz z stworzeniem struktury jak powyżej (UWAGA!!! podświetlone linie to katalogi!), możemy przejść do edycji ansible.cfg
który będzie wyglądał następująco:
[defaults]
inventory = hosts
# uzytkownik
remote_user = vagrant
# wylaczenie sprawdzania klucza
host_key_checking = False
# przyspieszenie
pipelining = True
gathering = smart
fact_caching = jsonfile
fact_caching_timeout = 86400
fact_caching_connection = /tmp/ansible_fact_cache
forks = 20
Tak wygląda config Ansible dla naszego playbooka. Kolejno od góry mamy ustawienie w jakim pliku mamy przechowywane nasze hosty na których będzie pracował Ansible, czyli hosts
. Ustawiamy użytkownika, abyśmy nie musieli go podawać przy uruchamianiu playbooka, w moim przypadku jest to vagrant
, naturalnie można go podmienić na każdego innego. Następnie wyłączamy sprawdzanie klucza ssh, z racji tego, że musielibyśmy mieć dany host już w ~/.ssh/known_hosts
aby się podłączyć. Blok przyspieszenie
odpowiada za ustawienie Ansible, aby mógł łączyć się wieloma wątkami do hostów oraz przechowywać raz pobrane fakty przez określony czas, co ominie nam pobieranie faktów za każdym razem gdy będziemy wykonywać playbooka i zaoszczędzi trochę czasu.
Teraz zajmiemy się konfiguracją pliku hosts
:
[leader]
192.168.56.10
[managers]
192.168.56.10
192.168.56.13
192.168.56.14
[workers]
192.168.56.11
192.168.56.12
Bardzo prosta sprawa, tworzomy trzy grupy [leader]
, [managers]
i [workers]
po czym umieszczamy w nich nody, które im będą odpowiadać. W tym momęcie możemy przejść do tworzenia grup.
Tworzymy grupy
Grupy będą nam potrzebne, abyśmy mogli wykonać odpowiednie taski Ansible dla poszczególnych rodzajów nodów. Struktura przedstawia się następująco:
swarm_playbook/group_vars/
├── all
├── leader
├── managers
└── workers
Cała zawartość swarm_playbook/group_vars/
to na tę chwile puste pliki. Teraz możemy przejść do tworzenia pierwszej roli.
Rola – repo
Będąc ciągle w swarm_playbook/
wykonujemy następujące polecenie ansible-galaxy init roles/repo
powinniśmy zobaczyć informacje o udanym utworzeniu roli - roles/repo was created successfully
. Z racji tego, że instalowaliśmy praktycznie czyste CentOS’y zakładamy, że systemy posiadają zainstalowane tylko domyślne paczki z wersji minimalnej. Rola repo
posłuży nam w tym momencie do zainstalowania repozytorium Epel. Edytujemy zatem vim roles/repo/tasks/main.yml
i wrzucamy zawartość:
---
# tasks file for repo
- name: Instalujemy repo z .rpm
yum:
name: "{{ repo_install }}"
state: present
Używamy modułu yum i tworzymy zmienną repo_install
. Teraz użyjemy tej zmiennej w grupie all
, ponieważ repozytorium Epel będziemy potrzebować na każdym z nodów. Przechodzimy do vim group_vars/all
i deklarujemy naszą zmienną:
---
repo_install:
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Grupa all
oznacza, że wszystko co się w niej znajdzie, będzie stosowane na wszystkich hostach, które posiadamy w pliku hosts
. Repozytorium epel
będziemy potrzebować do instalacji jednej z paczek wymaganych do używania modułu docker_swarm.
Rola – packages
Tworzymy rolę ansible-galaxy init roles/packages
jak poprzednio za pomocą ansible-galaxy
i dzielimy ją na taski, ponieważ będziemy musieli skorzystać z modułu yum oraz pip. Tworzymy task dla instalacji paczek z yum vim roles/packages/tasks/yum_common_packages.yml
o zawartości:
---
- name: instalacja paczek z YUM
yum:
name: "{{ yum_packages }}"
update_cache: yes
state: present
Kod praktycznie się nie różni od taska z roli repo
, ale tutaj zainstalujemy python-pip
(dlatego potrzebowaliśmy repozytorium epel
), dzięki któremu będziemy mogli użyć modułu pip
i zainstalować najważniejszą paczkę dla modułu docker_swarm. Utwórzmy więc kolejny task, który zajmie się instalacją modułów z pip
. Tworzym i Edytujemy następujący plik vim roles/packages/tasks/pip_common_packages.yml
i wklejamy:
---
- name: instalacja paczek z PIP
pip:
name: "{{ pip_packages }}"
state: present
W tym momencie mamy już utworzone oba potrzebne taski, które zajmą się instalacją dla nas modułów potrzebnych dla docker_swarm, więc zainicjujmy ich użycie w vim roles/packages/tasks/main.yml
za include
:
---
# tasks file for packages
- include: yum_common_packages.yml
- include: pip_common_packages.yml
Teraz jeszcze tylko musimy dodać nasze nowo powstałe zmienne yum_packages
i pip_packages
do vim group_vars/all
, te paczki będziemy potrzebować na wszystkich nodach stąd ponownie grupa all
:
yum_packages:
- python-pip
pip_packages:
- docker
Wszystkie role z instalacją podstawowych repozytoriów oraz paczek mamy stworzone, przejdźmy do instalacji samego Dockera.
Rola – Docker
Inicjujemy nową rolę ansible-galaxy init roles/docker
i przechodzimy do pisania pierwszego taska, którym będzie pobranie repozytorium Dockera i wrzucenie do /etc/yum.repos.d/
na naszych nodach. Tworzymy i edytujemy vim roles/docker/tasks/docker_repo.yml
:
---
- name: sciagamy repo docker
get_url:
url: "{{ docker_repo }}"
dest: /etc/yum.repos.d/
Tym razem używając modułu get_url, dzięki któremu ściągniemy repo Dockera.
Kolejnym taskiem będzie instalacja paczek Dockera, a więc tworzymy vim roles/docker/tasks/docker_package.yml
i używamy ponownie modułu yum:
---
- name: instalacja paczek Dockera
yum:
name: "{{ docker_packages }}"
update_cache: yes
state: present
Tutaj może pojawić się pytanie, dlaczego nie zainstalowaliśmy tych paczek wcześniej używając do tego roli packages
? Dlatego, że tworzymy tutaj mały playbook, który w przyszłości można rozbudowywać o kolejne paczki i repozytoria. Dobrą praktyką natomiast jest, podczas instalacji aplikacji którą możemy konfigurować na poziomie repozytorium, instalacja paczek i zarządzanie serwisem, aby całość komponentów znajdowała się w jednej roli.
Tym sposobem przechodzimy do utworzenia ostatniego taska dla tej roli, dzięki któremu, będziemy mogli zarządzać serwisem Dockera. Tworzymy i edytujemy vim roles/docker/tasks/docker_service.yml
:
---
# tasks file for docker
- name: uruchomienie daemona Docker
service:
name: docker
state: "{{ item.docker_status }}"
enabled: "{{ item.docker_autostart }}"
with_items:
- "{{ docker }}"
Użyliśmy modułu service, a dzięki zmiennej docker
będziemy mogli zarządzać stanem serwisu i auto-startem.
Pozostaje nam tylko dodać utworzone taski dla roli docker
do głównego taska vim roles/docker/tasks/main.yml
:
---
# tasks file for docker
- include: docker_repo.yml
- include: docker_package.yml
- include: docker_service.yml
I tak ukończyliśmy bardzo podstawowa role dla Dockera, która w przyszłości może być rozbudowywana o kolejną konfigurację, jak również może służyć jako pełnoprawna rola do instalacji tylko samego Dockera. Jako że wszystko co się w tej roli znalazło chcemy aby było instalowane na wszystkich nodach, edytujemy ponownie naszą grupę all
i dodajemy użyte zmienne z tej roli vim group_vars/all
:
docker_repo: https://download.docker.com/linux/centos/docker-ce.repo
docker_packages:
- docker-ce
- docker-ce-cli
- containerd.io
docker:
- docker_status: started
docker_autostart: yes
W ten sposób zakończyliśmy także dopisywanie zmiennych do grupy all
, a całość tego pliku powinna prezentować się następująco:
---
repo_install:
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum_packages:
- python-pip
pip_packages:
- docker
docker_repo: https://download.docker.com/linux/centos/docker-ce.repo
docker_packages:
- docker-ce
- docker-ce-cli
- containerd.io
docker:
- docker_status: started
docker_autostart: yes
Rola – swarm
Tworzymy rolę ansible-galaxy init roles/swarm
, będzie ona odpowiadała za inicjację samego klastra, oraz wpięcie do niego naszych managerów i workerów. Tym razem utworzymy dwa taski. Pierwszy do inicjacji Swarma i leadera. Edytujemy vim roles/swarm/tasks/swarm_init.yml
:
---
- name: Inicjacja Lidera
docker_swarm:
state: "{{ item.swarm_init }}"
advertise_addr: "{{ item.adv_addr }}"
with_items:
- "{{ swarm_claster }}"
- name: rejestracja faktow
docker_swarm:
state: inspect
register: swarm_tokens
W tym momencie w końcu dotarliśmy do użycia modułu docker_swarm. Pierwsza część to standardowe użycie modułu, które wykorzystamy do inicjacji leadera i klastra, a za pomocą zmiennej swarm_claster
przekażemy takie informacje jak stan klastra i IP, pod którym będzie on dostępny. Natomiast drugą część potrzebujemy do zarejestrowania faktu, z którego będziemy mogli wyciągnąć takie informacje jak token dla workera i token dla managera. Tę zmienną wrzucimy do grupy leader
i tylko tam będzie wykorzystywane. Przejdźmy więc do edycji vim group_vars/leader
:
---
swarm_claster:
- swarm_init: present
adv_addr: "{{ hostvars[groups['leader'][0]]['inventory_hostname'] }}"
swarm_init: present
oznacza, że Swarm został zainicjowany, natomiast adv_addr: "{{ hostvars[groups['leader'][0]]['inventory_hostname'] }}"
mówi nam o tym, że klaster jest dostępny pod nodem znajdującym się w hosts w grupie leader
na pozycji 0
.
Utwórzmy kolejny task dla wpięcia workerów i managerów, który nieznacznie będzie się różnił od pierwszego vim roles/swarm/tasks/swarm_node.yml
:
---
- name: wpiecie nodow
docker_swarm:
state: "{{ item.join_state }}"
advertise_addr:
join_token: "{{ item.token }}"
remote_addrs: [ '{{ item.join_remote_addrs }}' ]
with_items:
- "{{ add_node }}"
Jak widzimy, doszły nam pozycje advertise_addr
, join_token
oraz remote_addrs
, dzięki którym będziemy mogli wybrać rodzaj tokena, który chcemy użyć, oraz wskazać gdzie znajduje się klaster. Jeśli advertise_addr
pozostawimy bez wartości, wybiera on domyślne IP, aby pod nim rozgłaszać się w klastrze. Zmienne z tego taska, będziemy deklarować w dwóch grupach managers
i workers
.
Zaczniemy od grupy managers
, przechodzmy do edycji vim group_vars/managers
:
---
add_node:
- join_state: join
token: "{{ hostvars[groups['leader'][0]]['swarm_tokens']['swarm_facts']['JoinTokens']['Manager'] }}"
join_remote_addrs: "{{ hostvars[groups['leader'][0]]['inventory_hostname'] }}"
join_state: join
– oznacza, że node ma zostać przyłączony do klastra.token
: dzieki wcześniejszej rejestracji faktu swarm_tokens
za pomocą inspekcji w tasku swarm_init.yml
jesteśmy w stanie wyciągnąć z ledera konkretny token, w tym przypadku token managera.
join_remote_addrs
– wskazujemy gdzie znajduje się klaster.
W ten sposób będziemy wpinać każdą maszynę, która znajdzie się w grupie managers
w pliku hosts
Dodajemy zmienne do grupy workers
, aby przyłączyć maszyny, które mają taką rolę pełnić
---
add_node:
- join_state: join
token: "{{ hostvars[groups['leader'][0]]['swarm_tokens']['swarm_facts']['JoinTokens']['Worker'] }}"
join_remote_addrs: "{{ hostvars[groups['leader'][0]]['inventory_hostname'] }}"
Jak widzimy, jedyne co się zmieniło to zmienna token
, tym razem wskazaliśmy token dla workerów.
Jeszcze pozostało nam zainicjowanie naszych tasków w vim roles/swarm/tasks/main.yml
:
---
# tasks file for swarm
- include: swarm_init.yml
when: swarm_claster is defined
- include: swarm_node.yml
when: add_node is defined
Ponieważ chcemy, aby poszczególne taski wykonały się tylko w danych grupach, użyliśmy when
, aby zostały użyte tylko tam, gdzie są zadeklarowane konkretne zmienne. Tym sposobem przechodzimy do stworzenia ostatniej roli.
Rola – portainer
Inicjujemy rolę jak poprzednie ansible-galaxy init roles/portainer
, będziemy tworzyć trzy taski. Z racji tego, że na ta chwilę nie ma jeszcze w Ansible modułu, który odpowiada za deployowanie stacków utworzonych w Docker-Compose dla Swrma, będziemy musieli poradzić sobie w inny sposób 😉 Najpierw zrobimy task, który utworzy nam katalog dla pliku Docker-Compose vim roles/portainer/tasks/portainer_stack_dir.yml
:
---
- name: Tworzenie katalogu dla docker compose portainera
file:
path: "{{ item.stack_dir }}"
state: directory
with_items:
- "{{ portainer_conf }}"
Użyliśmy modułu file dzięki któremu na nodzie na którym będzie, zadeklarowana zmienna portainer_conf
, wskażemy katalog w którym ma wylądować nasz stack portainera w formacie Docker-Compose. Teraz stworzymy task, odpowiadający za ściągnięcie samego pliku do instalacji Portainera wykorzystując do tego moduł, który już znamy get_url vim roles/portainer/tasks/portainer_download.yml
:
---
- name: Pobieranie portainer-agent-stack.yml
get_url:
url: "{{ item.portainer_url }}"
dest: "{{ item.stack_dir }}/"
with_items:
- "{{ portainer_conf }}"
Po podaniu URL
, pod którym znajduje się „przepis” Docker-Compose, zostanie on ściągnięty do katalogu z taska portainer_stack_dir.yml
. Został nam ostatni task, który będzie odpowiadał za uruchomienie instalacji Portainera + jego agentów. Utwórzmy więc go vim roles/portainer/tasks/portainer_run.yml
:
---
- name: Uruchamiany portainera
shell: docker stack deploy --compose-file={{ item.portainer_run }} portainer && touch portainer_on.txt
args:
chdir: "{{ item.stack_dir }}/"
creates: portainer_on.txt
with_items:
- "{{ portainer_conf }}"
Co tu się konkretnie zadziało? Dzięki modułowi shell użyliśmy standardowej komendy z Docker Swarm do deployowania stacków zapisanych w formacie Docker-Compose. Do tego w momencie zdeployowania stacka utworzyliśmy plik portainer_on.txt
, dzięki któremu będziemy sprawdzać, czy stack został już uruchomiony, czy nie, a całość ma się zadziać w katalogu, z naszego pierwszego taska w tej roli. Zmienną portainer_conf
będziemy inicjować w grupie leader
, więc przejdźmy do jej edycji vim group_vars/leader
i zobaczmy, jak to będzie wyglądało:
portainer_conf:
- stack_dir: /opt/docker
portainer_url: https://downloads.portainer.io/portainer-agent-stack.yml
portainer_run: portainer-agent-stack.yml
Tworzymy katalog na hoście z grupy leader
, podajemy adres URL
, skąd ma zostać ściągnięty nasz plik zawierający gotowy stack i podajemy jego nazwę w celu uruchomienia stacka. Nasz plik odpowiadający za grupę leader
po wszystkich zmianach powinien wyglądać następująco:
---
swarm_claster:
- swarm_init: present
adv_addr: "{{ hostvars[groups['leader'][0]]['inventory_hostname'] }}"
portainer_conf:
- stack_dir: /opt/docker
portainer_url: https://downloads.portainer.io/portainer-agent-stack.yml
portainer_run: portainer-agent-stack.yml
W tej roli, zostało nam jeszcze dodanie naszych wszystkich tasków do roles/portainer/tasks/main.yml
:
---
# Rola dla instalacji Portainera
- name: Instalacja serwera Portainer + Agenta
block:
- include: portainer_stack_dir.yml
- include: portainer_download.yml
- include: portainer_run.yml
when: portainer_conf is defined
W związku z tym, że każdy task należy do jednej zmiennej portainer_conf
wrzuciliśmy je w blok i ponownie użyliśmy when
, aby rola wykonała się tylko tam, gdzie ta zmienna jest zadeklarowana. To już wszystkie role, jakie mieliśmy do utworzenia, czas skonfigurować nasz główny plik playbooka.
Konfiguracja site.yml
Serce naszego playbooka, czyli plik z którego wszystko wykonamy, znajduje się w swarm_playbook/site.yml
przejdzmy do jego edycji:
---
- name: Inicjowanie klastra docker swarm
hosts: all
become: yes
roles:
- repo
- packages
- docker
- swarm
- portainer
Nadajemy nazwę, wskazujemy, że playbook ma zostać uruchomiony na grupie hostów all
, oraz po połączeniu ma działać jako sudo
, za co odpowiada become: yes
. Następnie dołączamy role, które mają zostać uruchomione i w jakiej kolejności. Całość wyzwalamy z serwera, na którym mamy Ansible i w katalogu playbooka za pomocą komendy ansible-playbook site.yml -k
co oznacza:
ansible-playbook
– uruchom playbookasite.yml
– z pliku site.yml-k
– zapytaj o hasło do ssh
Powinno wyglądać to następująco:
Portainer
Po wykonaniu playbooka powinniśmy mieć możliwość dostania się do Portainera z przeglądarki pod adresem http://<ip_leadera>:9000 w moim przypadku http://192.168.56.15:9000. Powinien nas przywitać następujący widok:

Tworzymy hasło dla admina min 8 znaków, po czym ukazuje nam się strona domowa:

Z tego miejsca widzimy od razu ile mamy zdeployowanych stacków, ile serwisów działa, ile łącznie mamy kontenerów, oraz ile posiadamy wolumenów i obrazów.
W Docker Swarm stackiem nazywane jest wszystko, co mamy zapisane w postaci Docker-Compose i zrobimy deploy za pomocą komendy docker stack deploy --compose-file
, bądź w samym Portainer za pomocą paska bocznego i pozycji stack
. Pokażę teraz, w jaki sposób możemy wystawić i zreplikować Nginx za pomocą Portainer.
Deploy serwisu

Klikamy po lewej w Services
, następnie nadajemy nazwę, pod jaką nasz serwis ma się prezentować; wybieramy obraz, z którego chcemy zrobić deploy i w ten sposób docieramy do „trybu planowania” określającego, w jaki sposób ma być wystawiony nasz kontener. Mamy do wyboru dwa tryby – Global
i Replicated
, Czym się różnią? Jeśli zaznaczymy Global
, to nasz kontener zostanie wystawiony po jednym na każdym z nodów, które mamy wpięte do klastra oraz na wszystkich kolejnych nodach, które zostaną do Swarma przyłączone. Natomiast wybór Replicated
oznacza, że to my decydujemy, w ilu replikach nasz kontener ma zostać wystawiony i rozłożony po klastrze. Następnie mapujemy, pod jakim portem możemy się na niego dostać poprzez hosta oraz port, pod jakim działa aplikacja w samym kontenerze i na koniec pozostaje nam kliknąć utworzenie serwisu.
Teraz możemy wrócić na zakładkę Services
i po chwili powinniśmy zobaczyć:

Co oznacza, że nasz serwis nginx-www
został zdeployowany z obrazu nginx:latest
, i jest zreplikowany na 4 nody (takiej replikacji sobie zażyczyliśmy), oraz dostęp do niego mamy po porcie 8080
. Automatycznie widzimy, na których nodach konkretnie nasz Nginx
jest zreplikowany i czy działa. W ten oto prosty sposób wystawiliśmy swój pierwszy serwis.
Słowem zakończenia
Tym sposobem dotarliśmy do końca tego przydługiego, lecz (mam nadzieję) wartościowego wpisu, Nie chciałem w nim „przeklikać” całego Portainera, gdyż wpis byłby 3x dłuższy, ale uważam, że jest to tak proste i intuicyjne narzędzie, że każdy bez problemu odkryje jego tajniki. Myślę, że nic nie daje takiej radości jak samodzielna eksploracja nowego narzędzia.
Na koniec mogę poradzić, aby od razu po skonfigurowaniu klastra, przypisać do każdego noda tę samą domenę, dzięki czemu będziemy mogli wykorzystać olbrzymi potencjał trybu „routing mesh” i poczuć moc wbudowanego load-balancera. Nody automatycznie będą rozdzielały między sobą zapytania i kierowały w odpowiednie miejsce na podstawie domeny.
ŹRÓDŁA
Vagrant – stack 5 maszyn pod klaster Docker Swarm + 1 maszyna z Ansible >> TUTAJ <<