swarmexec_ dokumentacja
Obsługa klienta — polecenia, flagi, konfiguracja & TUI.
swarmexec daje ci docker exec -it, logi, przekierowanie portów i
zarządzanie wolumenami dla dowolnego kontenera w Docker Swarm,
z jednego terminala. Mały agent na każdym węźle (globalna usługa
Swarma) wykonuje pracę na swoim węźle; klient pyta menedżera
Swarma, który węzeł uruchamia twój cel, a potem łączy się bezpośrednio z agentem
na tym węźle przez mTLS. Ta strona dokumentuje klienta.
Spis treści
1. Jak to działa
Dwa pliki binarne, jeden protokół transmisji:
- agent — działa jako usługa Swarma w trybie
global(jedno zadanie na węzeł), montuje gniazdo Dockera tego węzła i udostępnia wąskie API gRPC, które przekazuje exec / logi / przekierowanie portów do kontenerów na swoim własnym węźle. Wdrażasz go raz poleceniemswarmexec init. - klient (to CLI) — działa na twojej stacji roboczej. Rozmawia
z API menedżera Swarma, żeby ustalić, który węzeł uruchamia twój
cel i jakie jest id kontenera, a potem łączy się bezpośrednio z agentem tego węzła
na porcie
9443. Nie ma żadnej siatki połączeń agent-agent.
Połączenie z menedżerem korzysta z twojego kontekstu Docker CLI (respektuje więc
--context, bastiony ssh://, mTLS itd.). Połączenie
z agentem jest uwierzytelniane wzajemnym TLS albo współdzielonym sekretem wobec
agenta z certyfikatem samopodpisanym — zobacz Uwierzytelnianie.
2. Instalacja
Wymaga Docker Engine 19.03 lub nowszego (API 1.40) na menedżerze i na każdym węźle. swarmexec odrzuca starszego demona już przy połączeniu, podając obie wersje, zamiast łączyć się i potem zawodzić widok po widoku. Dwie funkcje wymagają nieco więcej od demona węzła: bieżące zużycie zasobów i widok obrazów per węzeł potrzebują Dockera 23.0 (API 1.41/1.42) — poniżej pozostają puste i mówią o tym, zamiast zawodzić.
Klient to pojedynczy statyczny plik binarny. Pobierz najnowszą wersję dla swojej platformy:
Kompilacje: linux-amd64,
linux-arm64,
darwin-arm64,
darwin-amd64,
windows-amd64.
Obraz agenta jest publicznie dostępny na Docker Hubie jako
logleio/swarmexec-agent.
3. Szybki start
Ustaw swój kontekst Dockera na menedżera Swarma, a potem raz zaprowizjonuj agentów.
init tworzy współdzielony sekret, wdraża agenta na każdym węźle w trybie
z certyfikatem samopodpisanym i zapisuje pasującą konfigurację klienta — dzięki temu
pozostałe polecenia działają od razu.
Agentów usuniesz z powrotem poleceniem swarmexec down (twoja
konfiguracja klienta pozostaje nietknięta).
4. Selektory celu
exec, logs i port-forward przyjmują
cel jako pierwszy argument. Jest on rozwiązywany w tej kolejności:
| Postać | Przykład | Znaczenie |
|---|---|---|
service | web | Działające zadanie usługi. Jeśli ma więcej niż jedną replikę, jest to niejednoznaczne — zobacz niżej. |
service.slot | web.2 | Konkretny slot repliki. Podczas aktualizacji kroczącej wygrywa najnowsze zadanie w danym slocie. |
task-id | xxh8k1… | ID zadania Swarma. |
container-id | 3f9a2b… | Prefiks ID kontenera. Wymaga węzła: podaj --node albo zostanie znaleziony przez przeskanowanie działających zadań. |
Niejednoznaczność. Sama nazwa usługi z kilkoma replikami nie da się
sprowadzić do jednego zadania. exec uruchomiony interaktywnie prosi cię
o wybór z numerowanej listy; logs i port-forward nigdy nie
pytają — wypisują listę kandydatów i kończą działanie. Doprecyzuj cel slotem
(web.0, web.1, …).
5. Konfiguracja
Ustawienia pochodzą z czterech warstw, z których każda nadpisuje poprzednią:
Flaga nadpisuje wartość tylko wtedy, gdy faktycznie ją podasz, więc wartość z pliku albo ze środowiska zostaje zachowana, o ile nie nadpiszesz jej jawnie.
Plik konfiguracyjny
Domyślna ścieżka to pierwsza z tych, która jest ustawiona:
$SWARMEXEC_CONFIG$XDG_CONFIG_HOME/swarmexec/config.yaml~/.config/swarmexec/config.yaml
Nadpiszesz ją przez --config <path>. Plik jest w formacie YAML;
brakujący plik jest w porządku, źle sformułowany to błąd. Zapisywany jest z prawami
0600 (może zawierać współdzielony sekret). Plik logu leży domyślnie obok
niego — ~/.config/swarmexec/swarmexec.log — chyba że ustawisz
--log-file. Klucze:
| Klucz | Typ | Znaczenie |
|---|---|---|
ca | string | certyfikat CA weryfikujący certyfikat serwera agenta (mTLS) |
cert | string | certyfikat klienta — jego CN jest twoją tożsamością operatora |
key | string | klucz prywatny klienta |
port | int | port agenta (domyślnie 9443) |
addr_mode | string | hostname (domyślnie) albo ip — jak łączyć się z węzłem |
server_name | string | nadpisuje nazwę serwera TLS używaną do weryfikacji agenta |
agent_secret | string | współdzielony sekret dla agenta z certyfikatem samopodpisanym |
agent_secret_file | string | wczytaj sekret z tego pliku (ma pierwszeństwo przed agent_secret) |
insecure | bool | pomiń weryfikację certyfikatu serwera agenta (agenci z certyfikatem samopodpisanym) |
legacy_secret | bool | wysyła dodatkowo surowy sekret, dla agentów starszych niż v1.17.3 — domyślnie wyłączone; patrz niżej |
operator | string | tożsamość w audycie, gdy nie używasz certyfikatu klienta (domyślnie: nazwa użytkownika systemu) |
logs.format | string | domyślny format logów dla logs i TUI: classic | json | logfmt | gelf | raw (puste = classic) |
logs.min_level | string | domyślny minimalny poziom: trace..fatal (pomiń, żeby nie filtrować po poziomie) |
ui.dim | float | jak mocno przyciemniane jest tło pod otwartą nakładką, ułamek 0–1 (domyślnie 0.6; 0 = bez przyciemniania) |
Sekcja logs: ustawia domyślne wartości dla parsowania i filtrowania logów
świadomego formatu; flagi --log-format / --min-level
polecenia logs je nadpisują:
Obowiązującą, scaloną konfigurację (z zamaskowanym sekretem) obejrzysz przez:
addr-mode
hostname (domyślnie) łączy się z nazwą hosta zgłoszoną przez węzeł;
ip łączy się z jego rozgłaszanym adresem. Użyj ip, gdy nazwy
hostów węzłów nie są rozwiązywalne z twojej stacji roboczej — właśnie dlatego
init wpisuje addr_mode: ip do wygenerowanej konfiguracji.
Lider swarma zgłasza dla siebie 0.0.0.0; klient automatycznie odzyskuje
jego prawdziwy adres z listy peerów raft.
Zmienne środowiskowe
| Zmienna | Ustawia |
|---|---|
SWARMEXEC_CONFIG | ścieżkę pliku konfiguracyjnego |
SWARMEXEC_CA / _CERT / _KEY | materiał mTLS |
SWARMEXEC_PORT | port agenta |
SWARMEXEC_ADDR_MODE | hostname / ip |
SWARMEXEC_SERVER_NAME | nazwę serwera TLS |
SWARMEXEC_AGENT_SECRET / _FILE | współdzielony sekret / plik z sekretem |
SWARMEXEC_INSECURE | pominięcie weryfikacji (1/true/yes/on) |
SWARMEXEC_OPERATOR | tożsamość w audycie |
SWARMEXEC_UI_DIM | przyciemnienie tła pod nakładką (ui.dim) |
SWARMEXEC_KEYS | ścieżkę do pliku mapowania klawiszy TUI (domyślnie keys.yaml obok konfiguracji) |
SWARMEXEC_SSH_MULTIPLEX | 0/off/false/no wyłącza współdzielone połączenia ssh (zob. Współdzielone połączenia ssh) |
DOCKER_CONTEXT | kontekst Dockera dla API menedżera |
XDG_CONFIG_HOME | bazę domyślnej ścieżki konfiguracji |
6. Uwierzytelnianie
Klient uwierzytelnia się wobec agenta w jednym z dwóch trybów.
Tryb A — wzajemny TLS (domyślny)
Ustaw ca, cert i key (wszystkie trzy są
wymagane). Klient weryfikuje agenta wobec twojego CA i przedstawia swój certyfikat;
agent autoryzuje cię i zapisuje w audycie po CN z certyfikatu. To
jest ustawienie domyślne i zalecane.
Tryb B — agent z certyfikatem samopodpisanym + współdzielony sekret
Prostszy w uruchomieniu (jeden sekret, żadnego PKI) — to właśnie konfiguruje
init. Ustaw agent_secret (albo
agent_secret_file) oraz albo ca do weryfikacji agenta,
albo insecure: true, żeby pominąć weryfikację. Certyfikat
klienta jest opcjonalny (ale cert i key muszą być ustawione
razem albo oba puste). Twoją tożsamością w audycie jest wartość
operator (domyślnie: twoja nazwa użytkownika systemu).
ca i trzymaj sekret poza historią powłoki (użyj
agent_secret_file albo pliku konfiguracyjnego).
Przejście na v1.17.3 — najpierw zaktualizuj agentów
invalid or missing agent secret, mimo że twój
sekret jest poprawny. Zaktualizuj ich najpierw:
legacy_secret: true w konfiguracji klienta
wysyła też surowy sekret, z ekspozycją opisaną wyżej. Usuń to, gdy agenci będą
aktualni, i dodaj agentowi -allow-legacy-secret=false, aby żaden
klient nie położył przypadkiem poświadczenia na kablu.
7. Docker context & SSH
--context wybiera kontekst Docker CLI używany dla API menedżera.
Kolejność rozwiązywania: --context → $DOCKER_CONTEXT →
$DOCKER_HOST → aktywny kontekst w ~/.docker/config.json
→ lokalne gniazdo unix:///var/run/docker.sock.
docker — linkuje SDK Dockera dla Go i mówi bezpośrednio do API
menedżera, sam czytając metadane kontekstów z plików. Wszystko, czego potrzebuje, to
osiągalny punkt końcowy menedżera Swarma (wywołania rozwiązujące
węzeł działają tylko wobec menedżera):
$DOCKER_HOSTwskazujący na zdalnego menedżera potcp://(mTLS) — wtedy na twojej stacji roboczej nie ma w ogóle zainstalowanego Dockera;- kontekst
ssh://— wymaga klientassh(nie dockera); ruch do menedżera i do agenta tuneluje się przez niego; - lokalne gniazdo
unix:///var/run/docker.sock— tylko domyślny wariant awaryjny i jedyna opcja, która zakłada lokalnego demona.
docker jest potrzebne wyłącznie do tworzenia nazwanych
kontekstów (docker context create) — albo utwórz je przez
swarmexec context create <name> --docker-host …, dzięki czemu CLI
dockera nie jest potrzebne nawet do tego; użyj $DOCKER_HOST, żeby
całkowicie pominąć nazwane konteksty. (init czyta lokalne poświadczenia
docker login tylko dla prywatnego obrazu agenta — nie dla
publicznego, domyślnego.)
Bastion SSH. Jeśli host kontekstu jest punktem końcowym
ssh://, API menedżera jest tunelowane przez SSH — i tak samo połączenie
z agentem: ponieważ punkty końcowe węzłów node:9443 zwykle nie są
routowalne z twojej stacji roboczej, klient automatycznie tuneluje ruch gRPC agenta
przez ten sam host SSH. Żadne dodatkowe flagi nie są potrzebne.
8. Flagi globalne
Te trwałe flagi dotyczą każdego polecenia:
| Flaga | Domyślnie | Opis |
|---|---|---|
--config | — | ścieżka pliku konfiguracyjnego (domyślnie ~/.config/swarmexec/config.yaml) |
--context | — | kontekst dockera dla API menedżera; obsługuje ssh:// (także $DOCKER_CONTEXT) |
--port | 9443 | port agenta |
--addr-mode | hostname | adres, pod którym łączymy się z węzłem: hostname | ip |
--ca | — | certyfikat CA do weryfikacji agenta (mTLS) |
--cert | — | certyfikat klienta (mTLS; CN jest tożsamością operatora) |
--key | — | klucz prywatny klienta (mTLS) |
--server-name | — | nadpisuje nazwę serwera TLS przy weryfikacji agenta |
--agent-secret | — | współdzielony sekret dla agenta z certyfikatem samopodpisanym |
--agent-secret-file | — | plik, z którego wczytać współdzielony sekret |
--insecure | false | pomiń weryfikację certyfikatu serwera agenta |
--operator | nazwa użytkownika systemu | tożsamość operatora raportowana na potrzeby audytu |
--log-level | info | szczegółowość logów: debug | info | warn | error | off (off całkowicie wyłącza logowanie) |
--log-file | — | ścieżka pliku logu (domyślnie swarmexec.log obok pliku konfiguracyjnego) |
--info | — | pokaż wersję, licencję i dane kontaktowe |
--version | — | wypisz wersję klienta i protokołu |
slog, format tekstowy) do pliku logu oraz do bufora cyklicznego
w pamięci. W TUI logi nigdy nie trafiają na terminal (zepsułyby ekran) — zamiast tego
bufor cykliczny zasila wbudowaną podglądarkę na żywo (zobacz TUI).
--log-level off wyłącza logowanie całkowicie; plik logu, którego nie da
się otworzyć, nie jest błędem krytycznym (zostaje wtedy sam bufor cykliczny).
9. Polecenia
init — zaprowizjonuj agentów
Wdraża agenta jako globalną usługę Swarma przez API menedżera: tworzy sekret Dockera ze współdzielonym sekretem, uruchamia agenta w trybie z certyfikatem samopodpisanym na każdym węźle (port hosta 9443) i zapisuje pasującą konfigurację klienta. Uruchom to raz na swarm.
| Flaga | Domyślnie | Opis |
|---|---|---|
--image | docker.io/logleio/swarmexec-agent:latest | obraz agenta do wdrożenia |
--secret | losowy | współdzielony sekret do użycia (domyślnie: wygeneruj losowy). Uwaga: jeśli sekret agenta już istnieje, zostaje zachowany bez zmian (sekrety Dockera są niezmienne), więc --secret nie jest stosowany do klastra — trafia tylko do twojej konfiguracji klienta, a init ostrzega o ryzyku niezgodności. Żeby zmienić sekret klastra, usuń go najpierw (żadna usługa nie może się do niego odwoływać), a potem uruchom init ponownie |
--service-name | swarmexec_agent | nazwa usługi agenta |
--port | 9443 | port hosta publikowany przez agenta |
--force | false | zaktualizuj usługę, jeśli już istnieje |
--save-config | true | zapisz konfigurację klienta |
--registry-auth | true | przekaż lokalne poświadczenia rejestru, żeby węzły mogły pobrać prywatny obraz |
--wait | true | poczekaj, aż agenci wstaną, i raportuj postęp |
--rollout-timeout | 90s | jak długo czekać na start agentów |
--force, żeby wdrożyć nową wersję agenta
— zobacz ponowne wdrożenie agentów bez zmiany sekretu.
down — usuń agentów
Odwrotność init: usuwa globalną usługę agenta oraz, domyślnie, sekret
Dockera ze współdzielonym sekretem. Nie rusza twojej konfiguracji
klienta.
| Flaga | Domyślnie | Opis |
|---|---|---|
--service-name | swarmexec_agent | nazwa usługi agenta do usunięcia |
--keep-secret | false | nie usuwaj sekretu Dockera ze współdzielonym sekretem |
-y, --yes | false | nie pytaj o potwierdzenie |
doctor — zdiagnozuj swarm
Sprawdza połączenie z menedżerem, to czy usługa agenta jest wdrożona, oraz odpytuje
agenta na każdym gotowym węźle pod kątem osiągalności i rozjazdu wersji. Wypisuje
tabelę per węzeł (NODE AGENT VERSION PROTO);
kończy się kodem niezerowym, jeśli menedżer jest nieosiągalny albo któryś agent jest
niezdrowy. Węzeł raportowany jako too old (init --force) uruchamia agenta
starszego niż twój klient — zobacz
ponowne wdrożenie agentów bez zmiany sekretu.
| Flaga | Domyślnie | Opis |
|---|---|---|
--connect-timeout | 10s | limit czasu połączenia na węzeł |
--json | false | wypisz JSON zamiast tabeli |
security report — raport Markdown o całym klastrze
Nakładka z ryzykami bezpieczeństwa analizuje specyfikacje usług
i pokazuje wynik na ekranie. To polecenie zapisuje tę samą analizę — plus
kontrole, które należą do klastra, a nie do żadnej pojedynczej
usługi — w formacie Markdown, żeby dało się ją przejrzeć z dala od terminala,
dołączyć do zgłoszenia albo trzymać obok plików stacka i porównywać wydanie po
wydaniu. Wynik idzie na stdout, o ile nie podano -o,
więc równie łatwo go przekierować, jak zapisać.
Na poziomie klastra działają cztery kontrole i na żadną z nich specyfikacja usługi nie potrafi odpowiedzieć:
| Kontrola | Waga | Co zgłasza |
|---|---|---|
network-unencrypted — „ruch overlay nie jest szyfrowany” | medium | sieć overlay przenosząca ruch usług bez szyfrowania warstwy danych. Swarm tuneluje ruch między węzłami przez VXLAN jawnym tekstem, o ile sieci nie utworzono z --opt encrypted — a działająca usługa w obu przypadkach wygląda tak samo. Sieci, do których nic nie jest podłączone, nie są zgłaszane (nic nie przenoszą), podobnie jak ingress, której w ogóle nie da się zaszyfrować: takiego wpisu nikt nigdy by nie zamknął |
autolock-disabled — „menedżery nie mają autolocka” | medium | magazyn raft menedżerów nie jest szyfrowany w spoczynku. Zawiera każdy sekret, każdy config i klucz CA klastra, a jego klucz leży na tym samym dysku — kto zabierze dysk menedżera, zabierze wszystko. Włącz przez docker swarm update --autolock=true i zachowaj klucz odblokowujący: zrestartowany menedżer o niego poprosi. Jeśli konfiguracji swarma nie da się odczytać, raport podaje autolock-unknown zamiast zgadywać — przyjęcie „wyłączone” wymyśliłoby wpis, a przyjęcie „włączone” byłoby fałszywym uspokojeniem |
agent-proto-mismatch / agent-version-skew / agent-too-old / agent-unreachable | high / medium / low | to agent egzekwuje autoryzację przy każdym execu, każdym logu i każdym port-forwardzie, więc starszy od twojego klienta może nie egzekwować reguły, którą klient uznaje za obowiązującą. Niezgodność protokołu ma wagę high i przeważa nad różnicą wersji: obie strony nie zgadzają się co do samego kontraktu, a nie tylko co do tego, który build go realizuje. Agent, który nie odpowiedział, jest zgłaszany jako luka, a nie jako wynik pozytywny. Dla niewydanego klienta (dev) rozjazd nie jest zgłaszany — z założenia różni się od każdego wydanego agenta; ten sam wyjątek stosuje doctor |
unused-secret / unused-config | low | sekret lub config, do którego nie odwołuje się żadna usługa. Nadal jest rozprowadzany przez magazyn raft i nadal czytelny dla wszystkiego, co dosięgnie menedżera — zwykle poświadczenie, które ktoś zrotował i nigdy nie usunął, więc stara wartość wciąż żyje w klastrze długo po tym, jak wszyscy uznali ją za zniknioną |
Co raport mówi sam o sobie. Czyta go ktoś z dala od terminala, kogo nie było przy jego powstaniu — niesie więc własny kontekst: który klaster, kiedy i jakim buildem. Ma też sekcję Not covered — nieosiągalne agenty, nieczytelna konfiguracja swarma, pusta lista węzłów — bo milczenie w raporcie bezpieczeństwa czyta się jak uspokojenie co do terenu, na którym nigdy nie postał. Kolejność zależy wyłącznie od znalezisk, więc dwa raporty z niezmienionego klastra różnią się tylko znacznikiem czasu i dają się ze sobą porównać — a to jest coś warte tylko dlatego, że brak wpisu znaczy nie znaleziono, a nigdy nie sprawdzono.
Plik zapisywany jest z prawami 0600, a tworzone
katalogi nadrzędne z 0700. Wymienia każdą usługę, każdą sieć i każdy
sekret w klastrze wraz z każdą znalezioną słabością — to mapa miejsc do ataku i nie
ma powodu, by była czytelna dla każdego konta na maszynie.
| Flaga | Domyślnie | Opis |
|---|---|---|
-o, --output | stdout | zapisz do tego pliku zamiast na stdout |
--connect-timeout | 10s | limit czasu połączenia na węzeł przy sprawdzaniu agentów |
--skip-agents | false | nie kontaktuj się z agentami węzłów. Szybciej, a rozjazd agentów trafia wtedy do Not covered, zamiast zniknąć po cichu |
Ten sam raport zapiszesz z TUI: naciśnij w w nakładce z ryzykami bezpieczeństwa. To świeże zebranie danych z całego klastra, a nie zrzut tego, co pokazuje nakładka, i działa poza goroutine interfejsu — ten pozostaje responsywny, gdy raport odpytuje kolejne węzły.
stack export / stack diff — wdrożony stack jako plik
stack export odczytuje wdrożony stack z klastra i zapisuje go jako
YAML w formie compose. stack diff porównuje plik stacka z tym, co
naprawdę działa, i wypisuje diff zunifikowany: linia
+ to coś, co wdrożenie pliku by dodało, a
- to coś, co by usunęło. Jak
git diff --exit-code, polecenie kończy się kodem 1,
gdy cokolwiek się różni — nadaje się więc do CI.
W TUI E na zakładce Stacki/Usługi proponuje oba działania dla stacka pod kursorem — wiersza stacka, jego usługi albo jednego z jej kontenerów.
Dlaczego nie porównanie tekstu. Wdrożony stack niesie rzeczy,
których plik nigdy nie miał: skrót obrazu rozwiązany przez demona przy wdrożeniu,
jego własne wartości domyślne polityki restartu i aktualizacji oraz przestrzeń
nazw stacka doklejoną do każdej sieci, sekretu i wolumenu. Porównanie obu jako
tekstu zgłasza dziesiątki różnic dla stacka, który jest dokładnie zsynchronizowany
— a to gorsze niż brak narzędzia, bo uczy ignorowania wyniku. Zamiast tego plik
przechodzi przez własny loader i konwerter compose dockera, ten
sam kod, którego używa docker stack deploy, a potem obie strony
redukuje ta sama funkcja. Odpowiadamy na pytanie „czy
wdrożenie tego pliku coś by zmieniło?”, a nie „czy te dwa pliki są tak
samo zapisane?”. Wartości domyślne demona są pomijane po obu stronach;
wartość, która nie jest domyślna, nadal się pokazuje, więc nic
prawdziwego nie zostaje ukryte.
Eksport to opis, nie kopia zapasowa. Wartość sekretu jest w API
silnika tylko do zapisu — nigdy nie da się jej odczytać — a
zawartość wolumenu leży na węzłach. Oba są więc deklarowane jako
external, a wyeksportowany plik mówi to we własnym nagłówku:
odtworzenie stacka gdzie indziej oznacza najpierw utworzenie sekretów. Pola
specyfikacji, których to odwzorowanie nie niesie (tty,
ulimits, ustawienia seccomp i AppArmor oraz kilka innych), są
wymienione w tym samym miejscu, bo groźną porażką narzędzia porównującego nie jest
zła odpowiedź, lecz pewne siebie milczenie: „brak różnic” przychodzi
zawsze wraz z zastrzeżeniami.
Zmienne są interpolowane z twojego środowiska, dokładnie tak jak robi to
docker stack deploy. Plik zawierający ${TAG} opisuje
zatem inny stack dla innego TAG, a diff zależy od środowiska, w
którym działa — i tak jest poprawnie: udawanie inaczej zgłosiłoby „brak zmian” dla
wdrożenia, które podmieniłoby obraz.
| Polecenie | Opis |
|---|---|
stack ls | stacki wdrożone na klastrze |
stack export <stack> | zapisuje stack jako YAML compose; z -o do pliku, inaczej na stdout |
stack diff <plik> [stack] | porównuje plik z wdrożonym stackiem. Nazwa stacka domyślnie pochodzi od nazwy pliku bez rozszerzenia; --stack albo drugi argument ją nadpisuje. Kończy się kodem 1 przy każdej różnicy |
stack deploy — wdrożenie pliku stacka, po sprawdzeniu
Wczytuje plik stacka, uruchamia kontrole bezpieczeństwa na tym,
co naprawdę zostałoby wdrożone, i stosuje go. To, co czyni go wartościowym wobec
docker stack deploy, to bramka: plik jest sprawdzany,
zanim cokolwiek powstanie, a jeśli znajdzie się coś powyżej
informacyjnego, wdrożenie zatrzymuje się i pyta.
W TUI E → Deploy a file robi to samo: najpierw znaleziska, d kontynuuje, Esc odchodzi. W tym momencie nic jeszcze nie powstało — i właśnie po to jest ta pauza.
Kontrole nie są drugim zestawem reguł. Plik jest przekształcany
dokładnie w te wartości ServiceSpec, które wysłałoby wdrożenie, i na
nich działa osiem istniejących analizatorów — te same kontrole,
które stawiają tarczę w drzewie i wypełniają nakładkę ! oraz raport
bezpieczeństwa. Dwa zestawy by się rozjechały: reguła zaostrzona w jednym miejscu, a
nie w drugim, oznacza, że bramka przepuszcza plik, który drzewo oznacza w chwili,
gdy ten zaczyna działać — a komu powiedziano „nic nie znaleziono”, temu
powiedziano nieprawdę. Dzięki temu kontrole widzą też, co klaster
zrobi, a nie co plik mówi; wartości domyślne i
skróty compose leżą pomiędzy.
--yes nie znaczy „zignoruj znaleziska”. Znaczy „nie
pytaj”, a uruchomienie, które znajdzie coś powyżej informacyjnego, i tak
odmawia i kończy się kodem niezerowym. Inaczej bramka stałaby się
formalnością przy pierwszym wstawieniu do CI. Aby wdrożyć mimo znalezisk, trzeba
napisać --force — co innego się wpisuje i co innego potem tłumaczy.
Uruchomienie nieinteraktywne bez odpowiedzi na stdin liczy się jako
nie.
| Flaga | Opis |
|---|---|
--check | sprawdza i zatrzymuje się. Kończy kodem 1, jeśli znaleziono coś powyżej informacyjnego — nadaje się jako bramka w pipeline |
-y, --yes | nie pytaj. Wdraża, gdy kontrole są czyste; odmawia, gdy nie są |
--force | wdroż mimo znalezisk, bez pytania |
--prune | usuwa usługi stacka, których plik już nie deklaruje. Domyślnie wyłączone, jak w dockerze: plik będący podzbiorem stacka to znacznie częściej przeoczenie niż polecenie usunięcia |
Co robi wdrożenie, w tej kolejności: sieci, potem sekrety, potem configi, potem usługi — usługa odwołująca się do czegoś, czego jeszcze nie ma, kończy się błędem, a stack zostaje wdrożony połowicznie. Sieci zewnętrzne są sprawdzane najpierw, bo nieistniejąca sieć to najczęstszy powód zatrzymania wdrożenia w połowie. Istniejący sekret nigdy nie jest nadpisywany: jego wartość jest w swarmie niezmienna, więc zmiana oznacza utworzenie nowego pod nową nazwą. Jeśli wdrożenie mimo to zawiedzie w trakcie, to, co już zastosowano, jest raportowane wraz z błędem, a nie pozostawione do odgadnięcia.
Ponowne wdrożenie agentów bez zmiany sekretu
Prędzej czy później musisz wdrożyć agentów ponownie w swarmie, który jest już
zaprowizjonowany: agent się zawiesza, doctor pokazuje węzeł jako
too old (init --force), komenda kończy się błędem „agent is
older than this client (missing RPC) — update it with swarmexec init
--force” albo węzeł został postawiony od nowa i jego agent nigdy nie
wrócił. Rozwiązaniem jest ponowne uruchomienie init z
--force — flaga oznacza dokładnie zaktualizuj usługę, jeśli już
istnieje. Bez niej uruchomienie init na istniejącej usłudze jest
błędem użycia, który każe ci dodać --force; nic nie zostaje zmienione.
To wdraża agenta ponownie na każdym węźle i nie rusza współdzielonego
sekretu. Sekrety Dockera są niezmienne, więc init znajduje
istniejący swarmexec_agent_secret, raportuje go jako
reusing existing i zachowuje wartość, która już jest w klastrze.
Każdy klient, który działał wcześniej, działa dalej — nie trzeba niczego
rozdystrybuowywać ponownie.
--secret przy ponownym wdrożeniu.
Jeśli sekret już istnieje, nie da się go nadpisać, więc twoja wartość
nie zostaje zastosowana w klastrze — ale mimo to
zostaje zapisana do twojej konfiguracji klienta. Jeśli nie jest akurat prawdziwą
wartością istniejącego sekretu, agenci od tego momentu będą cię odrzucać, a awaria
ujawni się dopiero później i wygląda jak zepsuty agent, a nie jak zła lokalna
konfiguracja. init ostrzega dokładnie w tym przypadku; potraktuj to
ostrzeżenie poważnie. Żeby naprawdę zmienić sekret klastra, musisz najpierw usunąć
sekret (żadna usługa nie może się do niego odwoływać) i uruchomić init
ponownie.
--save-config domyślnie ma wartość true, więc ponowne
wdrożenie przepisuje też ~/.config/swarmexec/config.yaml. Przekaż
--save-config=false, kiedy lokalna konfiguracja klienta ma pozostać
nietknięta — na przykład gdy wdrażasz agentów ponownie z maszyny, której
konfiguracja jest już poprawna, albo z CI.
Potem sprawdź wynik. doctor raportuje status per węzeł;
ok na każdym węźle oznacza, że klient, agent i sekret znów się
zgadzają.
ps — wypisz zadania
Wypisuje kandydujące zadania/kontenery i węzeł, na którym każde działa. Przyjmuje
opcjonalny filtr usługi. Kolumny: SERVICE SLOT CONTAINER NODE IP UPTIME.
Rozmawia wyłącznie z API menedżera — działa nawet zanim agenci staną się osiągalni.
| Flaga | Domyślnie | Opis |
|---|---|---|
--json | false | wypisz JSON zamiast tabeli |
exec — uruchom polecenie / otwórz shell
Wejdź przez exec do kontenera działającego gdziekolwiek w swarmie. Bez podanego
polecenia otwiera /bin/sh. TTY jest przydzielane automatycznie, gdy
stdin jest terminalem i nie podałeś polecenia; wymusisz je przez -t.
Kod wyjścia zdalnego polecenia jest przekazywany dosłownie.
| Flaga | Domyślnie | Opis |
|---|---|---|
-i, --stdin | true | trzymaj stdin otwarte |
-t, --tty | auto | przydziel TTY (auto: prawda wtedy i tylko wtedy, gdy stdin jest terminalem i nie ma polecenia) |
-u, --user | — | nazwa użytkownika albo UID (np. 1000:1000) |
-w, --workdir | — | katalog roboczy wewnątrz kontenera |
-e, --env | — | ustaw zmienne środowiskowe (KEY=VALUE, można powtarzać) |
--node | — | podpowiedź/nadpisanie węzła dla celów typu container-id |
--connect-timeout | 10s | limit czasu połączenia z agentem |
logs — strumieniuj logi
Strumieniuj logi kontenera z dowolnego miejsca w swarmie.
| Flaga | Domyślnie | Opis |
|---|---|---|
-f, --follow | false | strumieniuj dalej nowe linie logu |
--tail | 0 | ile linii od końca pokazać na początek (0 = wszystkie) |
-t, --timestamps | false | poprzedź każdą linię znacznikiem czasu |
--since | 0 | tylko logi nowsze niż to (np. 10m, 1h) |
--log-format | classic | parsuj linie jako classic | json | logfmt | gelf | raw (domyślnie z konfiguracji, inaczej classic) |
--min-level | — | pokaż tylko ten poziom i wyższe: trace | debug | info | warn | error | fatal |
--grep | — | pokaż tylko linie, których (sparsowany) komunikat pasuje do tego wyrażenia regularnego Go |
--node | — | podpowiedź/nadpisanie węzła dla celów typu container-id |
--connect-timeout | 10s | limit czasu połączenia z agentem |
Parsowanie i filtrowanie świadome formatu. --log-format
mówi klientowi, jak czytać każdą linię, żeby mógł wyciągnąć z niej poziom
i komunikat: classic wyciąga poziom ze zwykłej linii tekstu,
json parsuje JSON w stylu logstash (pola
level/message), logfmt parsuje styl
key=value używany przez wiele aplikacji w Go, demona Dockera i narzędzia
HashiCorp (msg/message,
level/lvl/severity, ts/time),
gelf parsuje JSON GELF z Grayloga (numeryczny poziom syslog), a
raw przepuszcza linie bez zmian. --min-level odrzuca potem
wszystko poniżej wybranego poziomu, a --grep zostawia tylko linie,
których sparsowany komunikat pasuje do wyrażenia regularnego.
--min-level, więc wielolinijkowe ślady stosu nie giną. Te same wartości
domyślne można ustawić raz w sekcji konfiguracji
logs: (flagi mają pierwszeństwo).
Śledzenie mimo wymiany kontenera. Z -f na celu typu
usługa albo service.slot logs śledzi dalej, gdy
kontener, z którego strumieniuje, zostanie zastąpiony przez aktualizację kroczącą,
restart albo przeplanowanie: ponownie rozwiązuje bieżący działający kontener usługi
(ten sam slot albo ten sam węzeł dla usługi global), łączy się
automatycznie ponownie — jak docker service logs -f — i wypisuje
przygaszoną linię z powiadomieniem
(container replaced; reconnected to <id> on <node>).
Czeka na zaplanowanie następcy do ok. 30 s, zanim się podda, i kończy czysto,
gdy usługa zostanie usunięta. Sam cel typu
container-id nie ma następcy, więc po prostu zatrzymuje się jak dawniej.
port-forward (alias pf) — przekieruj lokalny port
Zajmij lokalny port TCP i przekieruj go na port wewnątrz kontenera, bez publikowania
tego portu w klastrze. Port lokalny domyślnie równa się zdalnemu. Wskazanie usługi
przekierowuje do dokładnie jednego jej zadania (tego, do którego
rozwiązuje się cel), a nie do wszystkich replik. Domyślnie wiąże się z
127.0.0.1. Naciśnij Ctrl-C, żeby zatrzymać.
| Flaga | Domyślnie | Opis |
|---|---|---|
--address | 127.0.0.1 | lokalny adres do zbindowania (pętla zwrotna trzyma port poza twoją siecią) |
--node | — | podpowiedź/nadpisanie węzła dla celów typu container-id |
--connect-timeout | 10s | limit czasu połączenia z agentem |
volume ls — wypisz wolumeny
Wypisuje wolumeny ze wszystkich węzłów i to, które węzły trzymają każdy z nich.
Wolumeny w Swarmie są lokalne dla węzła, więc klient odpytuje każdy węzeł i agreguje
wyniki. Opcjonalny filtr nazwy po fragmencie. Kolumny:
VOLUME DRIVER NODES USED BY AGE
(plus SIZE z --size).
| Flaga | Domyślnie | Opis |
|---|---|---|
--size | false | policz też rozmiar każdego wolumenu na dysku (wolniejsze: du na wolumen) |
--sort | name | sortuj po: name | nodes | used | age | size (size włącza --size) |
--reverse | false | odwróć kierunek sortowania |
--connect-timeout | 10s | limit czasu połączenia na węzeł |
--json | false | wypisz JSON zamiast tabeli |
volume rm — usuń wolumen
Usuń wolumen na każdym węźle, który go trzyma (--all), albo na
wskazanych węzłach (--node, można powtarzać). Jedno z dwóch jest wymagane.
| Flaga | Domyślnie | Opis |
|---|---|---|
--all | false | usuń na każdym węźle, który trzyma wolumen |
--node | — | usuń tylko na tych węzłach (można powtarzać) |
--force | false | przekaż flagę force dockera |
-y, --yes | false | nie pytaj o potwierdzenie |
--connect-timeout | 10s | limit czasu połączenia na węzeł |
ui — interaktywna TUI
Interaktywny widok kontenerów i wolumenów, z wbudowanym exekiem, logami i przekierowaniem portów. Wymaga interaktywnego terminala. Klawisze znajdziesz w rozdziale TUI.
| Flaga | Domyślnie | Opis |
|---|---|---|
--connect-timeout | 10s | limit czasu połączenia z agentem |
config show — obejrzyj konfigurację
Wypisuje obowiązującą, scaloną konfigurację klienta z zamaskowanym sekretem.
context (alias ctx) — zarządzaj kontekstami Dockera
Twórz konteksty Dockera, które --context (i
$DOCKER_CONTEXT) rozwiązują dla API menedżera, i zarządzaj nimi.
swarmexec zapisuje do własnego magazynu dockera na dysku, więc konteksty utworzone
tutaj są wymienne z tymi z CLI docker — i nie potrzebujesz już
zainstalowanego dockera nawet po to, żeby jakiś utworzyć. Wbudowanego kontekstu
default nie da się usunąć.
context create <name> — utwórz kontekst wskazujący na host menedżera (przyjmuje dokładnie jeden argument z nazwą):
| Flaga | Domyślnie | Opis |
|---|---|---|
--docker-host | — | wymagane — punkt końcowy demona dockera: ssh:// | tcp:// | unix:// | npipe:// |
--description | — | opcjonalny opis |
--ssh-jump | — | host(y) pośredniczące ssh dla kontekstu ssh://, rozdzielone przecinkami (wieloetapowy ProxyJump / -J); wstrzykiwane zarówno do połączenia z API Dockera, jak i do tunelu agenta |
--use | false | ustaw go od razu jako bieżący kontekst |
context ls (alias list) — wypisz konteksty; kolumny NAME CURRENT DOCKER ENDPOINT (aktywny oznaczony *):
context use <name> — ustaw bieżący kontekst:
context rm <name> [name...] (alias remove) — usuń jeden lub więcej kontekstów:
| Flaga | Domyślnie | Opis |
|---|---|---|
-f, --force | false | wymagane, żeby usunąć bieżący kontekst (wybór wraca wtedy na default) |
Współdzielone połączenia ssh
Przy kontekście ssh:// swarmexec sięga do klastra przez ssh
dwukrotnie: do API managera Dockera przez pomocnika połączeń ssh, a do każdego
agenta węzła własnym tunelem do tego samego hosta. Każdy exec, każdy strumień
logów, każde przekierowanie portu, każde odpytanie o statystyki i każdy cykl
odświeżania otwierał dotąd świeże połączenie ssh — uzgodnienie TCP, wymianę
kluczy i uwierzytelnienie na każde wywołanie, i tyle samo na każdy host
pośredniczący — do bastionu, który chwilę wcześniej był już połączony.
Teraz swarmexec pozwala im współdzielić jeden transport, korzystając z
ControlMaster samego OpenSSH: pierwsze połączenie do danego celu je
otwiera, a każde kolejne staje się kanałem na nim. Zmierzone na klastrze trzech
węzłów za hostem pośredniczącym: swarmexec doctor przeszedł z
8 uwierzytelnień i 3,6 s do 2 i 1,0 s.
Sam ruch się nie zmienia, a efekt rośnie wraz z tym, ile robi się w jednej sesji.
-
Gniazda sterujące znajdują się w
$XDG_RUNTIME_DIR/swarmexec/ssh/(zapasowo w katalogu pamięci podręcznej), tworzone z prawami0700— gniazdo sterujące to żywa, uwierzytelniona sesja, więc zostaje we własnym drzewie katalogów użytkownika, nigdy w/tmp. - Jedno gniazdo na cel i trasę: dwa konteksty sięgające do tego samego managera przez różne hosty pośredniczące nie współdzielą niczego, bo to nie jest to samo połączenie.
- Współdzielone połączenie przeżywa polecenie o 60 s, dzięki czemu następne jest tanie, a potem kończy się samo. Później nic ze swarmexeca nie trzyma już połączenia.
- Gniazdo pozostawione przez brutalnie zabity proces nadrzędny jest nieszkodliwe: ssh stwierdza, że jest martwe, i otwiera zamiast niego zwykłe połączenie.
-
Niedostępne w systemie Windows, którego OpenSSH nie obsługuje współdzielenia
połączeń. Wszędzie indziej wyłącza je
SWARMEXEC_SSH_MULTIPLEX=0— warto o tym wiedzieć, jeśli samodzielnie konfigurujeszControlPath, bo ustawienie swarmexeca trafia do wiersza poleceń ssh i ma pierwszeństwo przed~/.ssh/config.
10. TUI
swarmexec ui ma siedem zakładek — Stacks/Services (1),
Volumes (2), Forwards (3),
Networks (4), Secrets (5),
Nodes (6), Configs (7) —
panel boczny kontekstów przy prawej krawędzi oraz dwuliniową
stopkę: na górze podpowiedzi klawiszy dla danej zakładki, a pod nimi linia statusu
z aktywnym kontekstem dockera (ctx <name>, żeby zawsze było
jasne, na którym klastrze jesteś), podsumowaniem klastra na żywo, liczbą przekierowań
oraz — na zakładce Volumes — liczbą zaznaczonych przez ciebie wolumenów. Te klawisze
działają na każdej zakładce:
Pasek zakładek jest responsywny: na węższym terminalu przełącza się na krótkie etykiety — St/Sv, Vol, Fwd, Net, Sec, Node, Cfg — dzięki czemu wszystkie siedem zakładek pozostaje widocznych zamiast obcinania ostatnich. (Cfg to Configs.) Cyfry skrótów i obszary klikalne myszą pozostają bez zmian.
| Klawisz | Akcja |
|---|---|
| ? | otwórz nakładkę ze skrótami klawiszowymi — kompletny, aktualny spis klawiszy (generowany z twojego mapowania, więc przemapowane klawisze pokazują się poprawnie); jednoliniowa stopka mieści tylko najczęściej używane klawisze |
| Tab | przejdź do następnej zakładki |
| 1–8 | przeskocz do Stacks/Services / Volumes / Forwards / Networks / Secrets / Contexts / Nodes / Configs |
| j k | w dół / w górę (także ↓ ↑) |
| r | odśwież aktywną zakładkę i podsumowanie klastra |
| y | skopiuj bieżącą listę do schowka (OSC52) |
| m | przełącz przechwytywanie myszy (wyłączone = własne zaznaczanie/kopiowanie twojego terminala) |
| ` | otwórz / zamknij podgląd logów na żywo (zobacz niżej) |
| q | wyjdź |
Zakładka Stacks/Services
Zakładka 1, wcześniej nazywana Containers. To jedno drzewo, stack → usługa → kontener, i to w nim mieszkają exec, logi, przekierowanie portów, inspekcja i edytory usług.
Sprawdzanie wersji dla :latest. Swarm przypina :latest
do skrótu (digest) w chwili wdrożenia, więc usługa otagowana :latest
naprawdę uruchamia ustalony obraz. swarmexec rozwiązuje to wobec rejestru (używając
twoich lokalnych poświadczeń dockera) i opisuje wiersz usługi
zaraz za URI obrazu: podaje prawdziwą wersję
w nawiasach — odczytaną z etykiety obrazu
org.opencontainers.image.version — oraz
strzałkę w górę ↑, gdy bieżący :latest w rejestrze
ma nowszy digest niż ten, do którego usługa jest przypięta (np.
nginx:latest (1.4.0) ↑).
Działa najlepiej jak się da i jest buforowane: błędy rejestru po prostu zostawiają
wiersz bez adnotacji. Sekcja IMAGE nakładki inspekcji pokazuje tę samą
wersję, a gdy istnieje nowszy obraz — wybieralny wiersz
newer version available; najedź na niego i naciśnij u
(albo Enter), żeby zaktualizować usługę.
Ustawianie wersji klawiszem u — nie tylko wtedy, gdy jest
aktualizacja. u nie zależy już od tego, czy oferowana jest
aktualizacja. Działa w każdej inspekcji usługi, której tagi obrazu
swarmexec zdołał odczytać z rejestru — zarówno dla usług przypiętych do konkretnej wersji,
jak i dla tych na :latest — i to z dowolnego wiersza
nakładki, więc nigdy nie musisz szukać podpowiedzi. Ustawienie tej samej
wersji jeszcze raz, przypięcie tego, co aktualnie działa, albo powrót
do starszego tagu to zupełnie normalne zastosowania; nie musisz czekać,
aż pojawi się aktualizacja. Napis w stopce mówi ci, w której sytuacji
jesteś: u update version, gdy znaleziono nowszą wersję
(jest wtedy również wiersz newer version available z konkretnym
celem), oraz u set version, gdy jej nie ma.
To, co otworzy u, zależy od tego, co wie rejestr. Dla usługi
przypiętej do konkretnej wersji, dla której istnieje nowsza
wersja, jest to wybierak tagów: pole, którego autouzupełnianie
podpowiada nowsze tagi z tej samej rodziny — najwyższy pierwszy i
wstępnie wpisany. Dla usługi, która jest już na najnowszym tagu, jest to
ten sam wybierak, tyle że podpowiedzi sięgają po wszystkie tagi wymienione przez
repozytorium (wstępnie wpisany jest wtedy działający tag) — tak właśnie się
przypina albo cofa. Usługa na :latest również dostaje wybierak, ze wszystkimi
znanymi tagami: wybranie tam konkretnej wersji to sposób, żeby zdjąć usługę
:latest z ruchomego tagu i ją przypiąć — dokładnie o to prosi
ustalenie ryzyka unpinned-image (patrz nakładka ryzyk bezpieczeństwa niżej).
Jedyny wyjątek to usługa na :latest z oczekującą aktualizacją
digestu: ta pozostaje pojedynczym potwierdzeniem na nowy digest
z rejestru, którym była zawsze — wybierak kusiłby tam, żeby wpisać latest,
co rozwiązuje się do gołego tagu i po cichu porzuciłoby właśnie to przypięcie po digeście,
które ta aktualizacja ma odświeżyć.
W wybieraku lista podpowiedzi jest ograniczona do 25 pozycji (ruchliwe
repozytorium potrafi wymieniać setki), a samo pole pozostaje polem
tekstowym, więc możesz wpisać dowolny istniejący tag — w tym starszy,
żeby przypiąć się do sprawdzonego wydania albo się do niego cofnąć. Wpisany tag
jest walidowany wobec repozytorium (tag, którego ono nie wymienia, zostaje odrzucony),
a wybranie starszego tagu pokazuje ostrzeżenie o downgrade przed
zastosowaniem. W każdym przypadku zmiana to jeden ServiceUpdate / jedna
aktualizacja krocząca, za potwierdzeniem. Jeśli rejestr nie zwrócił żadnych
tagów i nie ma też konkretnego celu, na który można by się cofnąć — prywatny albo
nieosiągalny rejestr czy usługa przypięta po digeście — dostajesz wyjaśniający
komunikat i nic nie zostaje zmienione.
| Klawisz | Akcja |
|---|---|
| / | otwórz pasek wyszukiwania (filtruje po usłudze / kontenerze / węźle) |
| h l | zwiń / rozwiń, przechodząc przez wszystkie trzy poziomy. h zwija wiersz pod kursorem — stack zwija całą grupę, rozwinięta usługa zwija swoje kontenery — a gdy nie ma już nic do zwinięcia, wychodzi wyżej, do rodzica, więc kolejne naciśnięcia wędrują w górę kontener → usługa → stack. l rozwija wiersz pod kursorem albo schodzi do jego pierwszego dziecka, jeśli jest już otwarty |
| Enter | na kontenerze: otwiera menu akcji; na usłudze albo stacku: rozwija / zwija go |
| s | przełącz grupowanie po stackach — drzewo z grupami ⟷ płaska lista usług (zobacz niżej) |
| L | logi (wielkie L) — na kontenerze jego własne logi, na usłudze zagregowane logi wszystkich jej zadań |
| ! | otwórz nakładkę z ryzykami bezpieczeństwa dla całej listy usług (zobacz niżej) |
| p | przekieruj port zadania pod kursorem |
| i | zbadaj węzeł drzewa pod kursorem — nawigowalna nakładka z tabelarycznym podsumowaniem; zaznaczenie przesuwasz przez ↑/↓ albo j/k, y/Enter kopiuje zaznaczoną linię, pasek zakładek u góry nazywa trzy widoki, a 1/2/3 wybierają wprost tabelę / stats / surowy JSON demona, natomiast t przełącza je po kolei (Esc/q/i zamyka). Na usłudze nakładka pozwala też ją edytować: s skalowanie, f wymuszona aktualizacja, p porty, l etykiety, e zmienne środowiskowe, n sieci, S sekrety, v montowania, A aliasy; D diagnozuje, dlaczego usługa nie działa wszędzie; X ją usuwa |
| X | usuń usługę prosto z drzewa (wielkie X, czyli Shift+x) — bez okrężnej drogi przez nakładkę inspekcji. To, na co działa, zależy od wiersza pod kursorem: na wierszu usługi usuwa tę usługę; na wierszu kontenera usuwa usługę właścicielską tego kontenera, bo pojedynczego zadania nie da się usunąć samego — Swarm natychmiast zaplanowałby je ponownie; na wierszu stacka nie usuwa niczego, a zamiast tego pokazuje krótką notkę w stopce, że usługi stacka trzeba usuwać pojedynczo albo poleceniem docker stack rm <stack>. Przechodzi przez to samo potwierdzenie co X w inspekcji niżej — trwale kasuje usługę i zatrzymuje wszystkie jej zadania, nie da się tego cofnąć, a potem proponuje usunięcie osieroconych sekretów. Stały klawisz z wielkiej litery, nie przemapowywalna akcja keymapy; stopka wymienia go na czerwono jako X remove, a nakładka ? również go wypisuje. X wewnątrz nakładki inspekcji pozostaje bez zmian |
Pokazywana jest każda usługa (nawet przeskalowana do zera), każdy wiersz wyrenderowany
jak linia docker service ls — nazwa, tryb, liczba działających/żądanych,
obraz i opublikowane porty — i pokolorowany według tego, jak usłudze naprawdę idzie:
liczba działających/żądanych zadań ustala kolor bazowy (turkusowy = wszystkie
zadania działają, pomarańczowy = częściowo, czerwony = leży, szary = przeskalowana do
zera), a nieprzechodzący healthcheck go nadpisuje (patrz niżej).
Znacznik ▸/▾ pokazuje, czy usługa jest zwinięta, czy
rozwinięta; jej działające kontenery zagnieżdżają się pod nią. Usługa
w trakcie aktualizacji kroczącej nosi kolorową plakietkę na swoim
wierszu drzewa — ⟳ updating albo
↺ rolling back — a ten sam status pojawia
się w nakładce inspekcji usługi; znika po zakończeniu aktualizacji.
Kolor z healthchecku, nie tylko z liczby replik. Kolor wiersza
usługi brał się wyłącznie z relacji działające do żądanych — więc usługa, której
każdy kontener oblewał healthcheck, nadal rysowała się jako spokojne,
turkusowe 3/3. Liczba była prawdziwa, a wiersz mylący. Stan zadania
w Swarmie też tego nie powie: zadanie jest running, podczas gdy jego
kontener oblewa każdą próbę — werdykt musi więc przyjść z węzła. Nieudana próba
liczy się teraz jako pogorszenie tego samego rodzaju co brakująca replika:
- wszystkie sprawdzane kontenery unhealthy → wiersz robi się czerwony, to tak samo źle jak „nie działa żaden”;
- część unhealthy → pomarańczowy, jak przy wdrożeniu w połowie;
- wszystkie healthy → kolor, który wiersz dostał już od liczby replik, bez zmian;
- kontenery bez zadeklarowanego healthchecku → wiersz w ogóle nie jest przekolorowywany. Nic o nich nie wiadomo, a zgadywanie byłoby tym samym błędem w drugą stronę.
Znaczniki. Wiersz usługi niesie
✖ N unhealthy na czerwono albo
◌ N starting na żółto, dopóki próby nie
przeszły; liść kontenera niesie
✖ unhealthy /
◌ starting. Unhealthy bije starting — to
ono wymaga reakcji. Wiersz stacka zbiera ten sam znacznik ze wszystkiego, co jest pod
nim (zobacz Grupowanie po stackach niżej). Zdrowy kontener
nie dostaje
żadnego znacznika, tak samo jak taki bez
healthchecku, dzięki czemu znacznik nadal coś znaczy. Stoją obok znaczników zasobów
cpu/mem opisanych niżej, a kondycja idzie w wierszu
pierwsza: to ona przeczy liczbie stojącej tuż obok.
Up 3 days (healthy)) — dokładnie tym ciągu, który drukuje
docker ps. Parser jest celowo rygorystyczny: czego nie
rozpozna, staje się brakiem werdyktu, a nie domysłem. Jeśli demon kiedyś
przeredaguje tę linię, swarmexec przestanie twierdzić, że wie, zamiast zgłaszać
oblewający kontener jako zdrowy. Obowiązuje ten sam warunek co przy liczbach o
zużyciu: potrzebne są agenty z tego wydania lub nowsze, więc klaster, który nie
został przetoczony, nie pokaże żadnych znaczników kondycji — wdrożysz je przez
swarmexec init --force.
Znaczniki bieżącego zużycia CPU i pamięci. Obok tych plakietek
wiersz może nieść także to, co naprawdę zużywa w tej chwili — do tej pory
swarmexec potrafił pokazać tylko to, co scheduler zarezerwował (zobacz
zakładkę Nodes niżej). Kontener albo usługa, której zużycie przekroczy
70%, dostaje pomarańczowy znacznik, a przy 90%
czerwony — na końcu wiersza: zasób nazwany słowem (cpu
dla CPU, mem dla pamięci) i procent
(cpu 94%,
mem 91%). Jeśli gorąco jest na obu, pojawią
się oba. Słowa zamiast symboli, przy tej samej szerokości: znacznik, którego
znaczenie trzeba dopiero sprawdzić, nie robi swojej roboty, a litery nie mają jak się
nie wyrenderować w żadnym terminalu. Poniżej 70% nie rysuje się nic, żeby znaczniki
pozostały sygnałem, a nie
tapetą. Wiersz usługi bierze najgorszą ze swoich
replik, a nie średnią — średnia ukrywa dokładnie ten jeden kontener, który
zaraz padnie, czyli ten, który warto zobaczyć. (Bezwzględna pamięć w wierszu usługi
to suma po replikach; procent to wartość szczytowa.)
Czego te procenty są procentem. Każdy mierzy się względem tego, co dany kontener naprawdę może zużyć: własnego limitu, jeśli go ma, a w przeciwnym razie pojemności węzła. I to jest tu najważniejsze — 91% limitu 256 MB znaczy, że OOM kill jest blisko; 91% węzła o 64 GB to zupełnie inna rozmowa. Widok stats w nakładce inspekcji pokazuje obie strony tego dzielenia dla każdego kontenera i nazywa podstawę pod tabelą.
Stats);
każdy agent próbkuje swoje kontenery w tle i odpowiada z pamięci, więc klient
odpytuje go po prostu w cyklu odświeżania, który i tak ma. Wynikają z tego dwie
rzeczy. Procent CPU to różnica między dwoma odczytami, więc nie
istnieje, dopóki agent nie wykona dwóch (kilka sekund) — do tego czasu widoki
pokazują …, nigdy 0%; pamięć nie potrzebuje różnicy i
pojawia się już przy pierwszym odczycie. A agent próbkuje tylko,
dopóki ktoś faktycznie patrzy: po około minucie bez zapytań
przestaje i zapomina swoje odczyty, zamiast serwować nieświeże. Dlatego pierwsze
liczby po otwarciu UI potrzebują chwili, żeby się wypełnić. To celowe — agent,
na którego nikt nie patrzy, ma nic nie kosztować.
Stats
— po prostu nie dostarcza odczytów. Znaczniki się nie pojawiają, sekcji w
szczegółach węzła nie ma, a cała reszta działa bez zmian; klient dodatkowo na jakiś
czas odpuszcza taki węzeł, zamiast odpytywać go przy każdym odświeżeniu. Na
klastrze, który nie został jeszcze przetoczony, nie ma więc żadnych liczb
o zużyciu ani żadnych znaczników kondycji, dopóki agenty nie zostaną
zaktualizowane — wdrożysz je przez
swarmexec init --force. To zdecydowanie
najczęstszy powód, dla którego nic nie widać.
Grupowanie po stackach. docker stack deploy etykietuje
każdą tworzoną usługę etykietą com.docker.stack.namespace. swarmexec
odczytuje tę etykietę ze specyfikacji usługi, którą menedżer i tak już zwrócił —
Swarm nie ma obiektu stack, a to jedyne powiązanie — i zagnieżdża drzewo na trzech
poziomach: stack → usługa → kontener. Wiersz stacka pokazuje
nazwę stacka, ile usług się pod nim znajduje oraz zsumowaną liczbę
działających/żądanych zadań ((3 svc · 7/8)), pokolorowaną
według dokładnie tej samej reguły co wiersz usługi: zsumowanej liczby
działających/żądanych, z tym samym nadpisaniem z healthchecku na
wierzchu. Za nim pojawiają się zbiorcze
liczniki, gdy są niezerowe: ⟳ n — usługi w tym
stacku będące właśnie w trakcie aktualizacji kroczącej —
🛡 n — usługi z
ustaleniem bezpieczeństwa wymagającym działania, tą samą tarczą, którą
noszą wiersze usług — a po nich znacznik kondycji,
✖ N unhealthy albo
◌ N starting, zsumowany po wszystkich
kontenerach wszystkich usług w stacku. Dzięki temu zwinięty stack wciąż mówi ci, czy
coś w środku wymaga uwagi. Przy kondycji liczy się to najbardziej: wiersz stacka to
poziom, po którym operator wodzi wzrokiem najpierw, więc zwodnicze
„wszystko działa” jest tu gorsze niż piętro niżej, a nie mniej groźne.
Usługi utworzone przez docker service create nie mają etykiety stacka;
zbierane są pod (no stack), który zawsze sortuje się na
końcu, więc nigdy nie spycha prawdziwych stacków w dół listy.
Nic nie jest ukrywane — każda usługa nadal pojawia się dokładnie raz.
Stacki sortują się po nazwie i startują rozwinięte, więc drzewo z grupami pokazuje te
same usługi co płaskie; zwinięcia, których dokonasz, przeżywają automatyczne odświeżenie.
Grupowanie jest domyślnie włączone, ale działa tylko wtedy, gdy co
najmniej jedna usługa w klastrze faktycznie nosi etykietę stacka — na klastrze bez
stacków drzewo wygląda dokładnie tak, jak wyglądało zawsze, bez zbędnego rodzica
(no stack). Naciśnij s, żeby przełączać się między drzewem
z grupami a płaską listą usług (stopka wymienia to jako s stacks); gdy nigdzie
nie ma etykiet stacków, program tak mówi, zamiast przerysowywać identyczne drzewo.
Klawisz jest przemapowywalny jako stack_group w
keys.yaml.
Wyszukiwanie (/) działa bez zmian: filtr wciąż dopasowuje usługę, kontener i węzeł. Stack, który filtr opróżni, znika z drzewa, a liczniki w każdym wierszu stacka opisują to, co faktycznie pod nim zostało — liczby idą więc za filtrem, zamiast reklamować usługi, które odfiltrowałeś.
Menu akcji oferuje Logs, Bash,
Sh, Shell as user… (pyta o użytkownika/UID, jak
docker exec -u, dla obrazów, których domyślny użytkownik nie ma
potrzebnych narzędzi albo uprawnień) oraz Port forward (niedostępne
powłoki są wyszarzane po sprawdzeniu). W pasku wyszukiwania Enter zachowuje
filtr i wraca do listy; Esc czyści filtr i zamyka pasek.
Ryzyka bezpieczeństwa (!). Za każdym razem, gdy pobierana jest lista usług, swarmexec uruchamia zestaw małych analizatorów statycznych na specyfikacji każdej usługi — tej samej specyfikacji, którą menedżer i tak już zwrócił, więc nie ma dodatkowego wywołania API, agent nie bierze w tym udziału i nic nie jest wykonywane wewnątrz twoich kontenerów. Usługa z ustaleniem, na które warto zareagować, jest oznaczona wiodącą 🛡 w drzewie usług, w polu o stałej szerokości przed nazwą, żeby tarcze układały się w pionową kolumnę do przejrzenia. Naciśnij !, żeby otworzyć nakładkę z ryzykami bezpieczeństwa, a w niej w, żeby zapisać raport Markdown o całym klastrze — obejmuje on więcej niż nakładka, bo dokłada kontrole należące do klastra, a nie do usługi.
Osiem kontroli dostępnych dzisiaj. Najpierw te, które mogą oznaczyć usługę, na końcu te czysto informacyjne:
| Kontrola | Waga | Co zgłasza |
|---|---|---|
docker-socket — „zamontowany socket Dockera” | wysoka | najpoważniejsze ustalenie z całej listy. Montowanie bind, którego źródłem jest socket demona Dockera — /var/run/docker.sock, /run/docker.sock albo dowolna ścieżka źródłowa kończąca się na /docker.sock. Wszystko w takim kontenerze może rozmawiać z socketem, a kto może rozmawiać z socketem, może uruchomić na tym węźle kontener uprzywilejowany — czyli jest to w praktyce root na hoście, niezależnie od tego, jako kto działa sam kontener. Tryb tylko do odczytu tego nie łagodzi: socket to API, a nie plik, którego treść ma znaczenie — samo ustalenie mówi o tym wprost, gdy montowanie jest tylko do odczytu. Podawana jest ścieżka docelowa, pod którą socket jest zamontowany |
added-capability — „dodano capability NAZWA” | wysoka / średnia | uprawnienie (capability) Linuksa, które usługa dodaje swojemu kontenerowi. Swarm nie ma --privileged, więc capabilities są tym, przez co usługa prosi o dodatkową władzę nad hostem — dlatego warto czytać, co zostało dodane. Osiem ma wagę wysoką, każde z uzasadnieniem, które niesie ustalenie: ALL (daje każde uprawnienie), SYS_ADMIN (niemal root: kontrola montowań, przestrzeni nazw i cgroup), SYS_MODULE (może ładować moduły jądra), SYS_PTRACE (może podglądać i sterować innymi procesami), SYS_RAWIO (surowy dostęp we/wy do urządzeń), DAC_READ_SEARCH (omija sprawdzanie praw do odczytu plików), NET_ADMIN (pełna kontrola nad siecią węzła), NET_RAW (może fałszować i podsłuchiwać surowe pakiety). Każde inne dodane uprawnienie ma wagę średnią — przywilej ponad domyślny zestaw. Jedno ustalenie na każde dodane uprawnienie; rozpoznawane są oba zapisy, w dowolnej wielkości liter (CAP_SYS_ADMIN i SYS_ADMIN) |
host-network — „działa w sieci hosta” | wysoka | usługa jest podpięta do sieci o nazwie host: kontener dzieli stos sieciowy węzła, więc nie ma izolacji sieciowej, a mapowanie publikowanych portów Swarma przestaje obowiązywać. Dosięgnie wszystkiego, co dosięga węzeł — łącznie z usługami nasłuchującymi tylko na localhoście, które zwykle uznaje się za nieosiągalne z kontenera |
unconfined — „seccomp wyłączony” / „AppArmor wyłączony” | wysoka | piaskownica na poziomie jądra została dla kontenera wyłączona: seccomp ustawiony na unconfined — kontener może wykonać dowolne wywołanie systemowe, co usuwa główną barierę przed exploitami jądra — albo wyłączone ograniczenie AppArmor. Zgłaszane są osobno, więc usługa, która wyłącza oba, dostaje dwa ustalenia |
root-user — „działa jako root” | wysoka | specyfikacja przypina kontener do roota jawnie — User=root albo uid 0 (formy liczbowe takie jak 00 czy +0 też są wyłapywane; brana pod uwagę jest tylko część użytkownika z user:group) |
secret-in-env — „sekret w zmiennej środowiskowej” | wysoka | klucz zmiennej środowiskowej wyglądający na poświadczenie trzyma dosłowną wartość w specyfikacji usługi, czytelną dla każdego, kto może odczytać tę specyfikację. Użyj zamiast tego sekretu Dockera albo konwencji *_FILE |
root-user — „nie ustawiono użytkownika” | niska | żaden użytkownik nie jest w ogóle ustawiony, więc kontener działa jako domyślny użytkownik samego obrazu, którym często jest root. Tylko informacja i nie oznacza usługi: nieustawiony użytkownik to domyślny stan Swarma w niemal każdej usłudze, a to, czy naprawdę jest to root, zależy od USER obrazu, czego specyfikacja z menedżera nie ujawnia |
no-resource-limits — „brak limitów zasobów” | niska | zadanie nie ustawia ani limitu CPU, ani limitu pamięci, więc rozbiegany kontener może zająć cały węzeł i zagłodzić wszystko inne, co na nim stoi. Tylko informacja — patrz niżej |
unpinned-image — „obraz nieprzypięty” | niska | obraz jest przypięty do :latest albo nie ma żadnego tagu (co i tak sprowadza się do :latest) — działająca wersja może się zmienić bez zmiany specyfikacji, więc wdrożenie nie jest powtarzalne. Obraz przypięty digestem (…@sha256:…) jest dokładnie powtarzalny i nigdy nie jest zgłaszany, a dwukropek należący do portu hosta rejestru (registry:5000/img) nie jest brany za tag. Tylko informacja — patrz niżej |
Dlaczego kontrole informacyjne nigdy nie oznaczają usługi. Dopiero
ustalenie powyżej wagi niskiej — wysokie albo średnie — czyni usługę
wymagającą działania i stawia 🛡 przy jej wierszu w drzewie.
Ustalenia o niskiej wadze (no-resource-limits,
unpinned-image oraz „nie ustawiono użytkownika” z root-user)
są prawdziwe dla niemal każdej usługi w prawdziwym klastrze:
oznaczanie ich postawiłoby tarczę przy niemal każdym wierszu i zniszczyło dokładnie
ten sygnał na pierwszy rzut oka, dla którego znacznik istnieje. Nie są jednak
zamiatane pod dywan — gdy usługa zostanie oznaczona z innego powodu, jej ustalenia o
niskiej wadze są wypisywane w nakładce razem z resztą, czyli tam, gdzie naprawdę
warto je przeczytać.
secret-in-env w ogóle zagląda do zmiennych środowiskowych, a jej
ustalenie podaje wyłącznie klucz zmiennej środowiskowej;
jej wartość nigdy nie jest wczytywana do ustalenia, nigdy renderowana i nigdy
kopiowana. Kontrola jest też napisana tak, żeby nie hałasować: dopasowuje całe tokeny
rozdzielone znakiem _
(PASSWORD, PASSWD, PASS,
PASSPHRASE, SECRET, TOKEN,
APIKEY, CREDENTIAL(S), PRIVATEKEY oraz pary
API_KEY, ACCESS_KEY, PRIVATE_KEY,
SECRET_KEY, CLIENT_SECRET, AUTH_TOKEN), więc
COMPASS, PASSENGER_PORT czy BYPASS_AUTH nie są
zgłaszane. Klucze, które jedynie wskazują na poświadczenie, są wyłączone
(_FILE, _PATH, _URL, _URI,
_NAME, _ID, _TYPE, _ENABLED,
_REQUIRED, _LENGTH, _TIMEOUT,
_TTL, _EXPIRY, _ALGORITHM), podobnie jak
wartości, które ewidentnie nie są poświadczeniem — ścieżka bezwzględna, wartość
logiczna, liczba.
Nakładka wypisuje każdą oznaczoną usługę („N z M usług(i) oznaczonych · 8 kontroli”), pogrupowaną po usługach, każde ustalenie w osobnej linii z kropką wagi — ● wysoka, ● średnia, ● niska — krótkim tytułem i jednoliniowym wyjaśnieniem. Ustalenia są uporządkowane od najgorszych. Gdy usługa zostanie już oznaczona za coś wymagającego działania, wypisywane są też jej informacyjne ustalenia o niskiej wadze. Jeśli usługa pod kursorem jest wśród nich, zostaje wstępnie podświetlona i przewinięta do widoku. Gdy nic nie jest oznaczone, nakładka mówi to wprost i wymienia każdą kontrolę, która się wykonała („Checked: Docker socket mounted in, added Linux capabilities, host network, seccomp / AppArmor disabled, container user, secrets in environment variables, missing resource limits, unpinned image.”), więc informacja „czysto” mówi też, co obejmuje. A gdy lista usług jeszcze się nie wczytała, mówi, że nic nie zostało przeskanowane, zamiast fałszywie ogłaszać, że wszystko jest w porządku. j/k przewijają, Esc (albo q, albo ponownie !) zamyka.
security_risks w
keys.yaml.
i otwiera nakładkę inspekcji dla węzła drzewa pod
kursorem — na usłudze pokazuje docker service inspect,
a na liściu kontenera inspekcję zadania Swarma
(spojrzenie menedżera na tę instancję: stan, slot, węzeł, id kontenera, specyfikacja
kontenera, zasoby i historia statusów). Oba pochodzą od menedżera swarma. Nakładka
otwiera się na tabelarycznym podsumowaniu pisanym pod operatora,
z sekcjami uporządkowanymi według znaczenia operacyjnego — najpierw sieci, etykiety,
wolumeny/montowania i sekrety (oraz configi), potem porty, obraz, tryb, zmienne
środowiskowe, zasoby, rozmieszczenie i polityka aktualizacji (plus stan, węzeł i id
kontenera dla kontenera/zadania), a identyfikatory i znaczniki czasu na końcu.
Pierwsza linia nakładki — wewnątrz ramki, przypięta nad przewijaną treścią — to
pasek zakładek nazywający jej trzy widoki:
table 1 stats 2 raw json 3, bieżący w kolorze
akcentu. Ma taki sam kształt jak pasek zakładek głównego okna — etykieta, a po niej
cyfra, która ją wybiera. 1, 2 i 3 przechodzą
wprost do tabeli, statystyk i surowego JSON-a demona, a t
nadal przełącza po kolei tabela → stats → surowy JSON i z
powrotem. Podpowiedź w stopce brzmi już tylko 1-3/t view, a tytuł na ramce
nie powtarza nazwy bieżącego widoku — czyta się po prostu
inspect service foo, bo pasek i tak mówi, gdzie jesteś.
Pamiętaj, że widok tabelaryczny to widok zadania z perspektywy menedżera, a nie
pełne lokalne docker container inspect działającego kontenera na węźle
— prawdziwa inspekcja kontenera przez agenta jest planowana.
Widok stats. Środkowy z trzech to bieżące zużycie zasobów — te same odczyty, które znaczą drzewo, więc otwarcie go nie kosztuje żadnego dodatkowego wywołania, a liczby odświeżają się dalej, dopóki widok jest otwarty. To tabela, jeden wiersz na kontener usługi (albo jeden kontener przy inspekcji zadania):
Tabela, bo goły procent jest nieczytelny, jeśli nie wie się z góry, jak to narzędzie
mierzy — 94% czego? Każda komórka niesie własne jednostki,
więc 0.07 / 4.00 cores nie potrzebuje legendy, a procent stoi obok
liczby, której jest procentem; kilka replik porównuje się zaś,
przebiegając wzrokiem kolumnę, a nie zestawiając ułożone jeden pod
drugim bloki. Kolumna HEALTH ujmuje werdykt w słowa:
healthy, unhealthy, starting albo
none dla kontenera, który nie deklaruje żadnego healthchecku.
none i healthy to celowo dwa różne słowa, a
nie słowo i pustka: „brak skonfigurowanego healthchecku” i „próba przechodzi” to
różne fakty, a pustka czytałaby się jak to drugie. Kontener, którego żaden agent nie
zgłosił — nieosiągalny węzeł, agent zbyt stary na RPC Stats albo
kontener uruchomiony po ostatnim odczycie — pokazuje w obu kolumnach zasobów
no reading zamiast zera, które czytałoby się jak „bezczynny”.
Przypis pod tabelą nazywa mianownik: własny limit kontenera, jeśli go ma, a w przeciwnym razie pojemność węzła. To dopiero nadaje procentowi sens — 85% limitu 8 GiB i 85% węzła o 64 GiB to dwie różne rozmowy. Pamięć jest w jednostkach binarnych (MiB/GiB), tych samych, których używa szczegół węzła i które raportuje sam docker, więc ta sama liczba nigdy nie czyta się inaczej w dwóch miejscach. Przy więcej niż jednym zmierzonym kontenerze pod tabelą dochodzi podsumowanie: CPU i pamięć najgorszej repliki — nie średnia, która ukrywa właśnie ten kontener, który zaraz zostanie zabity przez OOM — plus łączna pamięć po wszystkich. Wpis t w nakładce pomocy ? brzmi odpowiednio cycle table / stats / raw JSON.
Nakładka jest nawigowalna i wybieralna, a nie tylko zwykłym przewijaniem: linie z danymi są pojedynczo wybieralne, a kursor przesuwasz linia po linii przez ↑/↓ albo j/k (nagłówki sekcji i puste linie są pomijane). y zawsze kopiuje zaznaczoną linię do schowka przez OSC52 — tym samym mechanizmem yank, co wszędzie indziej — więc możesz wyciągnąć pojedyncze id, montowanie albo adres bez zaznaczania tekstu ręcznie. Enter też kopiuje, z wyjątkiem zwijalnego wiersza NETWORKS, gdzie zamiast tego rozwija/zwija ten wiersz (zobacz niżej). Wszystkie trzy widoki (podsumowanie tabelaryczne, widok stats i surowy JSON, dostępne przez 1–3 albo przełączane po kolei klawiszem t) są wybieralne i kopiowalne linia po linii. Stopka zawsze wymienia dostępne klawisze, więc od razu widać, co możesz zrobić; dla usługi stopka pokazuje też klawisze edycji opisane niżej.
| Klawisz | Akcja |
|---|---|
| ↑ ↓ j k | przesuwaj zaznaczenie między liniami z danymi (nagłówki/puste pomijane) |
| y | skopiuj zaznaczoną linię do schowka (OSC52) — zawsze |
| Enter | na zwijalnym wierszu NETWORKS rozwija/zwija ten poziom — wiersze są zagnieżdżone na dwóch poziomach, sieć i jej rozwinięcie N containers; w każdej innej linii kopiuje ją (OSC52) |
| 1 2 3 | wybierz widok wprost — 1 podsumowanie tabelaryczne, 2 stats (bieżące CPU / pamięć, patrz niżej), 3 surowy JSON demona; to te same cyfry, które pokazuje pasek zakładek |
| t | przełączaj po kolei trzy widoki — podsumowanie tabelaryczne → stats (bieżące CPU / pamięć, patrz niżej) → surowy JSON demona → z powrotem; wszystkie trzy wybieralne, a pasek zakładek u góry zaznacza bieżący |
| a | menu akcji — wszystkie edytory i akcje usługi na jednej liście, bez Shiftu (tylko usługi; patrz niżej) |
| s f p l e n S v A | edytuj usługę — skalowanie / wymuszona aktualizacja / porty / etykiety / zmienne środowiskowe / sieci / sekrety / montowania / aliasy (tylko usługa; S to wielka litera, Shift+s, żeby odróżnić ją od s skalowanie; A też jest wielkie; zobacz niżej) |
| Esc q i | zamknij nakładkę |
Sekcja NETWORKS jest zwijalna i już w stanie zwiniętym
odpowiada na pytanie „pod jakim adresem to jest osiągalne”: każda podłączona sieć
pokazuje się jako zwinięty wiersz
+ <network> 🔒 vip 10.0.5.2/24 (2 dns names).
Ikona kłódki 🔒 za nazwą sieci oznacza sieć overlay
szyfrowaną (szyfrowanie płaszczyzny danych,
--opt encrypted); jej brak oznacza brak szyfrowania. Przy badaniu
usługi adres jest opisany jako vip — wirtualny IP usługi
w tej sieci, adres, pod którym odpowiada load balancer swarma, wzięty z
ServiceInspect menedżera (Endpoint.VirtualIPs). Przy badaniu
kontenera/zadania jest opisany jako addr: własny adres tego
kontenera w tej sieci. Usługa w trybie endpointu dnsrr nie ma żadnego
VIP-a — tak ma być, to nie brakujące dane — więc zamiast pustego miejsca rozwinięta sieć
pokazuje linię
no vip — dnsrr endpoint mode, the DNS name resolves to the containers. Sieć
ingress też jest tu wymieniana, choć nie pojawia się w żadnej spec —
swarm podłącza do niej usługę sam z siebie (patrz niżej). Dopisywana jest
za sieciami zadeklarowanymi w spec, nigdy nie jest z nimi przeplatana,
a jej nagłówek brzmi + ingress vip 10.0.0.250/24 (routing mesh): adnotacja
(routing mesh) stoi w miejscu licznika nazw DNS, który mógłby pokazać
wyłącznie (0 dns names).
Najedź na wiersz sieci i naciśnij Enter, żeby go rozwinąć lub zwinąć.
Rozwinięty wypisuje nazwy DNS, które rozwiązują się na usługę/kontener w tej sieci —
nazwę usługi, tasks.<service> oraz wszelkie własne aliasy — dzięki
czemu widzisz, pod jaką nazwą DNS jest osiągalny i jakie nosi aliasy, w każdej sieci
z osobna. Pod nimi znajduje się drugi zwijalny wiersz, + 2 containers
(w liczbie pojedynczej + 1 container); Enter na nim
otwiera najgłębszy poziom: po jednym wierszu na kontener, wyrównanym w kolumnach,
web.1 10.0.1.5/24 host-a — nazwa zadania, jego adres w tej sieci i węzeł,
na którym działa (zadania replikowane nazywają się
<service>.<slot>, globalne
<service>.<node>, bo nie mają slotu).
Filtrem jest pożądany (desired) stan zadania, a nie jego bieżący:
wypisywane jest każde zadanie, które menedżer nadal chce mieć uruchomione — również te,
które są dopiero preparing, assigned albo starting.
One już trzymają swój adres, a podczas aktualizacji kroczącej stanowią większość, więc
filtrowanie po stanie bieżącym opróżniałoby to rozwinięcie dokładnie wtedy, gdy jest
najciekawsze. Zadanie, z którego menedżer zrezygnował — pożądany stan
shutdown, czyli zastąpione przez aktualizację kroczącą, usunięte przy
skalowaniu albo nieudane — nie jest wypisywane: jego adres został zwolniony i może już
należeć do innego kontenera, więc pokazanie go byłoby wprost błędne. Zadanie, które nie
obsługuje jeszcze ruchu, ma swój stan w nawiasie na końcu wiersza,
web.1 10.0.1.5/24 host-a (starting); działające nie ma żadnego przyrostka.
Wiersze pochodzą z TaskList w API menedżera, przefiltrowanego po usłudze,
i są best effort: błąd API po prostu zostawia rozwinięcie puste. Oba poziomy zwijają się
niezależnie; zwinięcie sieci ukrywa razem z nią poziom kontenerów.
Wiersz ingress to ten, którego nie ma w żadnej spec: swarm podłącza
usługę do sieci ingress sam z siebie, gdy tylko opublikuje ona port w trybie
ingress, a to podłączenie nie pojawia się nigdzie w spec usługi — za to
jego vip jest adresem, pod którym faktycznie odpowiada
routing mesh, czyli pierwszą rzeczą, jakiej szukasz, gdy opublikowany
port zachowuje się dziwnie. Dlatego dostaje własny wiersz, dopisany za sieciami
zadeklarowanymi w spec i nieco inaczej zbudowany: w sieci ingress nie rozwiązuje się żadna
nazwa DNS usługi, więc tam, gdzie inna sieć liczy swoje nazwy, ten nagłówek nosi adnotację
(routing mesh). Rozwinięty wypisuje natomiast porty opublikowane
przez ingress, które w ogóle umieściły tam usługę — powód istnienia tego wiersza
— po jednej linii na port: published 2222 -> 22/tcp. Porty opublikowane
w trybie host omijają routing mesh i celowo nie są tam wypisywane. Niżej
schodzi do kontenerów dokładnie tak samo jak każdy inny wiersz sieci: ten sam poziom
+ 1 container, z własnym adresem każdego kontenera w sieci ingress. Usługa,
która nie publikuje żadnego portu przez ingress, nie ma tam VIP-a — a więc i takiego
wiersza w ogóle.
Usługa GitLab publikująca SSH na 2222, oba poziomy rozwinięte:
Kopiowanie wewnątrz NETWORKS daje adres bez maski —
10.0.5.2, w postaci, którą wklejasz do curl albo
ping: w wierszu sieci jej vip/addr, w wierszu kontenera adres tego
kontenera. Wiersz sieci bez adresu (dnsrr) kopiuje, tak jak dotąd, nazwę sieci. Na obu
rodzajach zwijalnych wierszy Enter przełącza; w każdej innej linii kopiuje
(a y kopiuje zawsze).
Gdy badasz usługę (a nie liść kontenera/zadania, którego nakładka
pozostaje tylko do odczytu), stopka nakładki udostępnia też kilka klawiszy edycji,
z których każdy jest wywołaniem ServiceUpdate w API menedżera
wyzwalającym aktualizację kroczącą / uzgodnienie zadań usługi.
Zacznij od a — menu akcji. W inspekcji usługi (i tylko
tam) a otwiera jedną listę wszystkich edytorów i akcji, jakie oferuje
nakładka, więc nie musisz pamiętać ~14 klawiszy wrażliwych na wielkość liter
(d/D, s/f,
p/l/e, n/S/v,
r/P, R, X). To ścieżka odkrywalna i
bez Shiftu — a nie okrojona: wybranie pozycji zamyka menu i
otwiera dokładnie to samo, co klawisz bezpośredni, więc oba są
równoważne, a nie są wariantami o różnym działaniu. Pozycje, w
kolejności:
Update image version… (Set image version…, gdy nie ma nowszej —
przypięcie dowolnego tagu albo powrót do niego jest równie uprawnione),
Diff spec (previous → current), Roll back to the previous version,
Why — placement diagnosis, Scale, Force-update,
Edit ports, Edit labels, Edit env, Edit networks,
Edit secrets, Edit mounts, Edit resources,
Edit placement, Remove service
— ta niszcząca, dlatego oznaczona na czerwono — oraz Cancel. Poruszasz się
klawiszami j/k (g/G skaczą do
pierwszej/ostatniej pozycji), Enter wybiera; Esc — albo
pozycja Cancel — zamyka menu, nic nie robiąc. Poza menu zostaje tylko
wybór wersji obrazu jest pierwszy,
bo ustawienie wersji to najzwyklejsza zmiana usługi, jaka istnieje; jego
bezpośredni klawisz u nadal działa z dowolnego wiersza inspekcji. Jeśli
obraz jest taki, dla którego nie da się wybrać wersji — referencja bez tagu albo
rejestr, który nie wypisze tagów repozytorium, zwykle prywatny bez poświadczeń —
pozycja zostaje na liście i mówi o tym, zamiast po cichu zniknąć,
oraz wskazuje na docker service update --image, które zadziała.
Klawisz bezpośredni dla każdej pozycji:
| Klawisz | Akcja |
|---|---|
| d | diff — pokazuje ujednolicony diff bieżącej specyfikacji usługi wobec poprzedniej (co zmieniła ostatnia aktualizacja krocząca), korzystając z PreviousSpec Swarma. Zmienione pola są wypisane w grupach, z liniami - usunięte / + dodane (obraz, tryb/repliki, zmienne środowiskowe, etykiety, sieci, aliasy, sekrety, montowania, porty, ograniczenia rozmieszczenia, preferencje rozproszenia, limity/rezerwacje zasobów); pola bez zmian są pomijane. Jeśli usługa nigdy nie była aktualizowana (brak poprzedniej specyfikacji), nakładka tak mówi. Nakładka tylko do odczytu — j/k przewijają, Esc zamyka |
| R | wycofanie do poprzedniej wersji (wielkie R, czyli Shift+r, żeby odróżnić od r limity zasobów) — odpowiednik diffa d powyżej: cofa ostatnią aktualizację kroczącą. Okno potwierdzenia pokazuje ten sam diff odwrócony pod nagłówkiem „This will undo:”, bo to właśnie zmieni wycofanie — linia, którą aktualizacja dodała, figuruje jako - usunięte, a ta, którą usunęła, wraca jako + dodane. Podgląd jest ograniczony do 12 wpisów, po których pojawia się adnotacja „… and N more” odsyłająca do d po pełny diff (jeśli zmieniły się tylko metadane, informuje, że nie ma różnic na poziomie pól). Potwierdzenie uruchamia wycofanie po stronie serwera — jeden ServiceUpdate API managera z ServiceUpdateOptions{Rollback: "previous"}, a nie ponowne zastosowanie PreviousSpec po stronie klienta — więc wykonuje je manager, respektując własną konfigurację wycofania usługi (równoległość, opóźnienie, akcja przy niepowodzeniu), i raportuje postęp jako stan aktualizacji rollback_started: dokładnie ta plakietka ↺ rolling back, opisana wyżej, na wierszu drzewa i w tej nakładce. Usługa, która nigdy nie była aktualizowana, nie ma poprzedniej specyfikacji — dostajesz wyjaśniający komunikat, nie błąd. Dostępne również z menu akcji a. Podobnie jak d, D i X jest to stały klawisz inspekcji, nie przemapowywalna akcja keymapy |
| s | skalowanie — pyta o nową liczbę replik i ją stosuje (tylko usługi replikowane; usługa globalna zgłasza, że nie da się jej skalować) |
| f | wymuszona aktualizacja — wdraża usługę ponownie bez zmiany jej specyfikacji (odpowiednik docker service update --force: podbija TaskTemplate.ForceUpdate), po potwierdzeniu. Każde zadanie jest restartowane / przeplanowywane, i tak właśnie odblokowuje się usługę tkwiącą w niekompletnym stanie (np. 1/2 repliki). Jeden ServiceUpdate (aktualizacja krocząca) |
| X | usuń usługę (wielkie X) — trwale ją kasuje i zatrzymuje wszystkie jej zadania, za potwierdzeniem; nie da się tego cofnąć. Jedno ServiceRemove w API menedżera; po powodzeniu nakładka inspekcji zamyka się, a drzewo usług się odświeża. Jeśli usługa była jedynym użytkownikiem jednego lub więcej sekretów, swarmexec pyta następnie, czy usunąć także te osierocone sekrety (sekrety wciąż używane przez inną usługę zostają nietknięte) |
| D | zdiagnozuj rozmieszczenie (wielkie D) — odpowiada w jednym widoku na pytanie, dlaczego usługa nie działa wszędzie tam, gdzie się tego spodziewasz, zamiast gonić za tym przez kilka poleceń docker. Dla usługi globalnej wypisuje każdy węzeł z ✓ działa albo ✗ i powodem wykluczenia (dostępność drain/pause, stan down, niespełnione ograniczenie rozmieszczenia albo niezgodność platformy) — dlatego trzywęzłowy klaster może całkiem legalnie pokazywać 2/2 (trzeci węzeł nie jest uprawniony). Dla usługi replikowanej wypisuje każde zadanie, które nie działa, wraz z własnym komunikatem schedulera (ograniczenia, za mało zasobów, błędy pobierania obrazu, …). Ograniczenia, których nie da się sprawdzić po stronie klienta (engine.labels.*), są odnotowywane, a nie zgadywane. Nakładka tylko do odczytu |
| p | edytuj opublikowane porty — otwiera buforowany edytor listy portów usługi, każdy w postaci PUBLISHED:TARGET[/proto] (proto tcp|udp|sctp, domyślnie tcp), np. 8080:80/tcp |
| l | edytuj etykiety — ten sam buforowany edytor listy nad wpisami key=value |
| e | edytuj zmienne środowiskowe — ten sam buforowany edytor listy nad zmiennymi z ContainerSpec, każda wprowadzana jako KEY=VALUE (np. LOG_LEVEL=debug); klucz jest wymagany, wartość może być pusta i sama może zawierać =. Tak jak porty/etykiety/montowania ten edytor pozwala na edycję pod e, więc możesz poprawić wartość w miejscu. W odróżnieniu od pozostałych edytorów e otwiera tu wpis w przewijalnym wieloliniowym polu tekstowym (a nie w jednoliniowym), żeby długie wartości takie jak GITLAB_OMNIBUS_CONFIG dało się wygodnie edytować — pisz swobodnie (Enter wstawia nową linię), Ctrl-S zapisuje, Esc anuluje. Zastosowanie podmienia zmienne środowiskowe usługi w jednym ServiceUpdate (aktualizacja krocząca) |
| n | edytuj sieci — buforowany edytor listy nad podłączeniami sieciowymi usługi; skoro wpis to sama nazwa, jest to tylko dodawanie/usuwanie (bez edycji pod e). Pole dodawania autouzupełnia nazwy sieci (podpowiada sieci, do których usługa nie jest jeszcze podłączona, albo wpisz własną). Zastosowanie podmienia podłączenia w jednym ServiceUpdate. Aliasy DNS edytuje się również stąd: zaznacz sieć w tym edytorze i naciśnij A, żeby otworzyć buforowaną listę aliasów DNS usługi w tej sieci (a dodaj / e edytuj / d usuń / y kopiuj / u cofnij / w zastosuj / Esc anuluj); zastosowanie podmienia aliasy tej sieci w jednym ServiceUpdate. Działa nawet wtedy, gdy usługa nie ma jeszcze żadnych aliasów. (Nowo dodaną sieć musisz najpierw zastosować, zanim ustawisz jej aliasy.) |
| S | edytuj sekrety (wielkie S, czyli Shift+s, żeby odróżnić od s skalowanie) — ten sam buforowany edytor listy co przy sieciach, nad sekretami, do których usługa się odwołuje; wpis to sama nazwa, więc jest to tylko dodawanie/usuwanie (bez edycji pod e). Pole dodawania autouzupełnia nazwy sekretów (podpowiada sekrety, których usługa jeszcze nie używa, albo wpisz własną). Zastosowanie podmienia odwołania usługi do sekretów w jednym ServiceUpdate (aktualizacja krocząca); każdy sekret jest montowany w /run/secrets/<name>. Działa nawet wtedy, gdy usługa nie ma obecnie żadnych sekretów |
| v | edytuj montowania — buforowany edytor listy nad wolumenami i montowaniami bind usługi. Dodawanie (a) albo edycja (e) otwiera mały formularz zamiast pojedynczego pola tekstowego: pole wyboru bind mount, źródło (gdy pole jest odznaczone, jest to nazwa wolumenu z autouzupełnianiem istniejących wolumenów klastra; gdy zaznaczone — ścieżka na hoście), ścieżka w kontenerze oraz pole wyboru tylko do odczytu. (Pod spodem każdy wpis to nadal volume:NAME:TARGET[:ro] / bind:/host/path:TARGET[:ro].) Źródła bindów i wszystkie cele muszą być ścieżkami bezwzględnymi. Zastosowanie podmienia montowania w jednym ServiceUpdate (aktualizacja krocząca). Zabezpieczenie przed bindami (miękkie): jeśli buforowana lista zawiera jakiekolwiek montowania bind, zastosowanie pokazuje najpierw ostrzeżenie z listą węzłów, na których usługa mogłaby zostać zaplanowana (wyliczonych z ograniczeń rozmieszczenia oraz roli/etykiet/dostępności węzłów), i przypomina, że każde źródło bindu musi już istnieć na wszystkich tych węzłach — swarmexec nie potrafi zweryfikować ścieżek hosta (agent nie ma dostępu do systemu plików hosta), więc ścieżki bindów nie są autouzupełniane ani sprawdzane pod kątem istnienia; to tylko podpowiedź |
| r | edytuj limity zasobów — mały formularz do ustawienia, zmiany albo wyczyszczenia limitów CPU i pamięci usługi (oraz, opcjonalnie, rezerwacji). CPU podaje się w rdzeniach (np. 0.5, 2); pamięć jako rozmiar czytelny dla człowieka (np. 512m, 2g, 1.5GiB). Pozostawienie pola pustego czyści dany limit (Swarm traktuje go wtedy jako nieograniczony). Rezerwacja nie może przekraczać swojego limitu. Zastosowanie wykonuje jeden ServiceUpdate (aktualizacja krocząca); istniejące limity Pids oraz rezerwacje urządzeń/ogólne są zachowywane |
| P | edytuj rozmieszczenie (wielkie P, czyli Shift+p, żeby odróżnić od p porty) — otwiera małe menu z dwoma buforowanymi edytorami: Ograniczenia i Preferencje rozproszenia.
ServiceUpdate (aktualizacja krocząca); druga część zostaje zachowana. |
Te edytory buforują zmiany: a dodaje wpis, d
usuwa ten pod kursorem, y kopiuje zaznaczony wpis do schowka przez OSC52
(żebyś mógł wyciągnąć np. pojedynczą zmienną środowiskową podczas oglądania),
u cofa ostatnią zbuforowaną zmianę (dodanie / edycję / usunięcie), dopóki
jeszcze nie zastosowałeś zmian — naciskaj go wielokrotnie, żeby cofać się przez
historię buforowanych zmian — a w stosuje wszystkie zmiany naraz w jednym
ServiceUpdate (więc dodanie, powiedzmy, kilku wolumenów i naciśnięcie
w wyzwala jedną aktualizację kroczącą, a nie po jednej na wpis),
za potwierdzeniem. Jeśli naciśniesz Esc, gdy zmiany są jeszcze
zbuforowane, edytor nie porzuci ich po cichu — zapyta, czy
zastosować, odrzucić, czy
edytować dalej.
Dopóki zmiany są zbuforowane, są wyróżnione, żebyś dokładnie widział,
co się zmieni: dodane albo zedytowane wpisy pokazują się na
zielono (nowe), a usunięte wpisy
zostają jako przygaszone czerwone wiersze „removed” —
wyróżnienie znika po zastosowaniu. Edytory
portów, etykiet, montowań,
zmiennych środowiskowych i aliasów
(aliasy dostępne z edytora sieci pod A)
mają też e do edycji wpisu pod kursorem; edytory
sieci i sekretów nie mają e —
wpis sieci albo sekretu to sama nazwa, więc jest tylko dodawanie (a) /
usuwanie (d). e w edytorze zmiennych
środowiskowych otwiera przewijalne wieloliniowe pole tekstowe
(Ctrl-S zapis / Esc anulowanie) dla długich wartości; pozostałe
edytory zachowują jednoliniowe pole z Enter do zatwierdzenia.
Drzewo odświeża się automatycznie co ok. 10 s, więc kontener zastąpiony albo usługa przeskalowana w tle pojawiają się same z siebie — kursor i rozwinięte usługi są zachowywane. r nadal wymusza natychmiastowe odświeżenie.
Zakładka Volumes
| Klawisz | Akcja |
|---|---|
| / | szukaj — filtruj listę po nazwie wolumenu, sterowniku albo węźle |
| n | utwórz wolumen — formularz z nazwą, sterownikiem (domyślnie local), etykietami (k=v,k=v) i docelowym węzłem (autouzupełnia nazwy węzłów; zostaw puste, żeby utworzyć na każdym węźle, bo wolumeny są lokalne dla węzła). Tworzy go przez agenta na każdym docelowym węźle i raportuje sukces/porażkę per węzeł. Tab przechodzi między polami, Esc anuluje |
| space | zaznacz / odznacz wolumen (oznaczony ▣) do zbiorczego usunięcia |
| a | zaznacz / odznacz wszystkie aktualnie wyświetlane wolumeny |
| d | usuń zaznaczone wolumeny — albo ten pod kursorem — na każdym węźle, który je trzyma, za potwierdzeniem (z nakładką postępu; usuwanie działa z ograniczoną równoległością) |
| P | prune: usuń każdy wolumen, którego nie montuje żaden działający kontener i którego nie deklaruje żadna usługa, za potwierdzeniem |
| Enter | pokaż, które węzły trzymają wolumen; etykiety wolumenu są wypisane tylko do odczytu nad węzłami (Docker nie ma API aktualizacji wolumenu, więc etykiet nie da się edytować po utworzeniu — ustaw je przy tworzeniu wolumenu) |
| i | pokaż, które usługi/kontenery go używają |
| A | podłącz wolumen do usługi: wybierz usługę (autouzupełnianie nazw) → podaj docelową ścieżkę w kontenerze (bezwzględną) → wybierz Attach albo Attach read-only. Dodaje montowanie jednym ServiceUpdate (aktualizacja krocząca) — wolumenowy odpowiednik akcji podłączania sieci/sekretów; operacja odwrotna mieszka w edytorze montowań v w inspekcji usługi |
| s S | przełączaj pole sortowania (name → nodes → used → age → size) / odwróć |
Usuwanie jest świadome węzłów: wolumen jest usuwany na każdym węźle, który go trzyma. Prune oszczędza wolumeny, które usługa deklaruje, nawet gdy żadne zadanie nie działa, więc nie wymaże danych zatrzymanego stacka. Dla większej kontroli Enter otwiera listę per węzeł, gdzie space zaznacza węzły, d usuwa kopię z podświetlonego węzła, a a usuwa na wszystkich węzłach — każde za potwierdzeniem.
Zakładka Forwards
| Klawisz | Akcja |
|---|---|
| Enter | pokaż pełne szczegóły przekierowania (łącznie z ewentualnym błędem) |
| d | zatrzymaj zaznaczone przekierowanie |
| o | skopiuj URL przekierowania (http://127.0.0.1:<port>) |
Działający kontener jest opisany w drzewie jako local→remote
(np. 9090→8080). Przekierowania działają dalej po zamknięciu nakładki
oraz po przełączeniu na inny klaster; zatrzymują się pod d albo
gdy wyjdziesz z UI. Kolumna CLUSTER nazywa kontekst, do którego każde
należy, przygaszona dla tego, który właśnie oglądasz — przekierowanie na innym
klastrze nadal jest twoje i nadal nasłuchuje, ale jego CONTAINER i
NODE nazywają rzeczy niewidoczne stąd. Opis w drzewie jest osobny dla
każdego klastra z tego samego powodu: identyfikator kontenera jest unikalny tylko
w obrębie własnego demona.
Dlatego lokalny port, którego już używasz, jest odrzucany z góry — z nazwą przekierowania, które go trzyma, i klastra, do którego należy; okienko pozostaje otwarte, żebyś wybrał inny port. Systemowe „address already in use" zostaje na wypadek, gdy ma rację: port zajęty przez inny program, którego swarmexec nie potrafi nazwać.
Zakładka Networks
Lista sieci swarma — nazwa, sterownik, zasięg, typ
(ingress / internal / attachable), to czy sieć jest szyfrowana
i ile usług jest do każdej podłączonych. Kolumna
TYPE pokazuje overlay albo nazwę sterownika (np.
bridge) dla zwykłych sieci, zamiast myślnika. Kolumna ENC
pokazuje 🔒 yes, gdy sieć overlay ma włączone
szyfrowanie płaszczyzny danych (utworzona z --opt encrypted), inaczej
myślnik. Nazwa każdej sieci
i jej komórka TYPE są kolorowane według rodzaju (najwyższy priorytet
pierwszy):
- attachable — zielony
- internal — żółty
- ingress — szary
- inne overlay / o zasięgu swarma — turkusowy
- lokalne (
bridge,host, …) — przygaszony szary
| Klawisz | Akcja |
|---|---|
| n | utwórz sieć — otwiera formularz (domyślny sterownik overlay) z typowymi opcjami swarma: attachable (pozwól dołączać samodzielnym kontenerom), encrypted (szyfrowanie płaszczyzny danych overlay), internal (bez routingu na zewnątrz), IPv6, opcjonalne MTU, opcjonalna podsieć/brama (IPAM) oraz etykiety (k=v,k=v). Tab przechodzi między polami, Enter na Create zatwierdza, Esc anuluje. Tworzy ją jednym NetworkCreate w API menedżera, a potem odświeża zakładkę |
| Enter / i | pokaż podłączone usługi, każdą z jej działającymi kontenerami zagnieżdżonymi pod spodem. Etykiety samej sieci są wypisane tylko do odczytu na górze (Docker nie ma API aktualizacji sieci, więc etykiet sieci nie da się edytować po utworzeniu — ustaw je przy tworzeniu sieci). Nagłówek każdej usługi pokazuje też, ile aliasów DNS ma ona w tej sieci (albo no aliases) |
| Enter | w widoku członków: rozwiń / zwiń aliasy DNS zaznaczonej usługi (domyślnie zwinięte, żeby lista pozostała zwarta); aliasy pojawiają się z wcięciem pod nagłówkiem usługi |
| A | w widoku członków: dodaj / edytuj aliasy DNS zaznaczonej usługi w tej sieci — otwiera ten sam buforowany edytor aliasów co widok inspekcji (a dodaj / e edytuj / d usuń / w zastosuj / Esc anuluj); zastosowanie podmienia aliasy tej sieci w jednym ServiceUpdate i odświeża widok. Działa nawet wtedy, gdy usługa nie ma jeszcze aliasów. (A to wielka litera, Shift+a, żeby odróżnić ją od a podłącz; samodzielne kontenery spoza swarma nie są edytowalne — aliasy to ServiceUpdate per usługa) |
| a | w widoku członków: podłącz usługę do tej sieci — pole z autouzupełnianiem (podpowiada usługi jeszcze niepodłączone; możesz też wpisać dowolną nazwę), a potem potwierdzenie aktualizacji kroczącej |
| d | w widoku członków: odłącz usługę od tej sieci — pole z autouzupełnianiem (podpowiada usługi obecnie podłączone; możesz też wpisać dowolną nazwę), a potem potwierdzenie aktualizacji kroczącej |
Podłączenie albo odłączenie wykonuje aktualizację usługi, która dodaje lub usuwa sieć
w specyfikacji usługi, a potem odświeża zakładkę Networks. To operacja API menedżera
(ServiceUpdate) — tym samym kanałem klient pobiera topologię — i wyzwala
aktualizację kroczącą, która restartuje zadania usługi.
Zakładka Secrets
Lista sekretów swarma tylko do odczytu — nazwa, ile usług używa każdego z nich, wiek, ostatnia aktualizacja i liczba etykiet. Wartości sekretów nigdy nie są pokazywane: API Dockera ich nie udostępnia.
| Klawisz | Akcja |
|---|---|
| Enter | pokaż metadane sekretu oraz usługi/kontenery, które go używają |
| d | na liście sekretów: usuń zaznaczony sekret — trwale go kasuje (jedno SecretRemove), za potwierdzeniem; nie da się tego cofnąć. Docker odmawia usunięcia sekretu, do którego wciąż odwołuje się jakaś usługa, więc potwierdzenie ostrzega, gdy sekret jest w użyciu i wypisuje te usługi (odłącz go tam najpierw). Nie mylić z d wewnątrz widoku szczegółów, które odłącza sekret od usługi |
| a | w widoku szczegółów: podłącz sekret do usługi — pole z autouzupełnianiem (podpowiada usługi, które go jeszcze nie używają; możesz też wpisać dowolną nazwę), a potem potwierdzenie aktualizacji kroczącej |
| d | w widoku szczegółów: odłącz sekret od usługi — pole z autouzupełnianiem (podpowiada usługi, które go obecnie używają; możesz też wpisać dowolną nazwę), a potem potwierdzenie aktualizacji kroczącej |
Podłączenie dodaje sekret do usługi, montowany w
/run/secrets/<name> (jak docker service update
--secret-add); odłączenie usuwa odwołanie do sekretu. Potem zakładka Secrets
się odświeża. Tak jak przy podłączaniu/odłączaniu sieci, każda z tych operacji to
ServiceUpdate w API menedżera, które wyzwala
aktualizację kroczącą restartującą zadania usługi.
Panel boczny kontekstów
Klastry zajmują kolumnę przy prawej krawędzi, a nie zakładkę. Przełączenie klastra robi się stamtąd, gdzie się właśnie jest — a lista klastrów jest dokładnie tym, co mówi, gdzie to jest. Jej miejsce jest więc na ekranie, a nie na stronie, do której trzeba się wybrać.
c oddaje jej klawiaturę z dowolnej zakładki; c ponownie albo
Esc zwraca ją zakładce, na której byłeś. Aktywny klaster jest oznaczony
▶ — tą samą nazwą, którą pokazuje stopka. Klaster odwiedzony już w tej
sesji ma zieloną ·: wciąż jest połączony, więc przełączenie na niego
jest natychmiastowe. Ten, który odmówił połączenia, ma czerwony ✗.
Wbudowany kontekst default jest chroniony.
Panel dopasowuje szerokość do najdłuższej nazwy kontekstu i ustępuje na wąskim terminalu: poniżej mniej więcej 92 kolumn chowa się, zamiast obcinać drzewo, i wraca na czas, gdy trzyma go c. Punktów końcowych w kolumnie nie ma — nie ma na nie miejsca, a i pokazuje pełne szczegóły.
Przełączenie klastra zachowuje twoją sesję. UI nie buduje się od
nowa: położenie kursora, rozwinięte stacki i usługi oraz filtr / są
pamiętane osobno dla każdego klastra, więc wracając trafiasz dokładnie
tam, gdzie skończyłeś. Przekierowania portów działają dalej;
zakładka Forwards zyskuje kolumnę CLUSTER, żeby jednym spojrzeniem
odróżnić te z oglądanego klastra od pozostałych.
Odpytywany jest tylko klaster, który oglądasz: po przełączeniu drugi przestaje się odświeżać, a jego połączenie ssh wygasa samo chwilę później. Pierwsze przejście do klastra trwa tyle, ile trwa jego osiągnięcie, bo połączenie powstaje zanim cokolwiek zmieni się na ekranie — jeśli się nie uda, dowiadujesz się o który klaster chodzi i zostajesz na tym, który działa. Każdy kolejny powrót jest natychmiastowy.
| Klawisz | Akcja |
|---|---|
| c | przenieś klawiaturę na panel boczny z dowolnej zakładki; c ponownie albo Esc ją zwraca |
| i | szczegóły kontekstu — punkt końcowy i hosty pośredniczące, na które w kolumnie nie ma miejsca |
| u / Enter | aktywuj zaznaczony kontekst — czyni go bieżącym (także dla zapisanego przez dockera bieżącego kontekstu) i przełącza UI na tamten klaster, zachowując twoje położenie, filtry i przekierowania portów |
| n | utwórz kontekst — prowadzony formularz. Pole wyboru „Connect to Docker over SSH” decyduje o punkcie końcowym: gdy jest włączone, wypełniasz użytkownika / host / port SSH, a drugie pole wyboru „Use a jump host” odsłania pole host(y) pośredniczące (rozdzielone przecinkami, wieloetapowy ProxyJump) — swarmexec zapisuje je w kontekście i wstrzykuje -J w oba połączenia ssh: do API Dockera i do tunelu agenta, dzięki czemu bastiony działają bez edytowania ~/.ssh/config. Gdy jest wyłączone, podajesz zwykły host tcp:// / unix://. Formularz składa docker host za ciebie, a Test odpytuje złożony punkt końcowy, zanim zapiszesz. Także z CLI: swarmexec context create <name> --docker-host ssh://ops@mgr --ssh-jump bastion1,bastion2 |
| d | usuń zaznaczony kontekst (za potwierdzeniem); usunięcie bieżącego przywraca wybór na default |
Zakładka Nodes
Wypisuje węzły swarma z ogólnymi informacjami i kilkoma agregacjami — NODE
(nazwa hosta; menedżerowie turkusowo, lider oznaczony ★), ROLE
(manager / worker), AVAIL (active / pause / drain, kolorowane),
STATE (ready / down), wersja ENGINE, TASKS
(działające zadania zaplanowane na węźle), VOLS (wolumeny trzymane przez
węzeł — lokalne dla węzła, więc uzupełnia się chwilę po reszcie) i LABELS
(liczba).
| Klawisz | Akcja |
|---|---|
| Enter / i | szczegóły węzła — nakładka tylko do odczytu z nazwą hosta, id, rolą/liderem, dostępnością, stanem, adresem, silnikiem, platformą, liczbą CPU, pamięcią, liczbą działających zadań, sekcjami zarezerwowanych zasobów, rzeczywistego zużycia i obrazów na tym węźle (patrz niżej), liczbą wolumenów i pełną listą etykiet |
| l | edytuj etykiety zaznaczonego węzła — buforowany edytor listy (a dodaj / e edytuj / d usuń / w zastosuj / Esc anuluj) nad wpisami key=value. Zastosowanie wykonuje jeden NodeUpdate — w odróżnieniu od usługi aktualizacja węzła działa natychmiast (bez aktualizacji kroczącej). Etykiety węzłów są zwykle używane jako cele ograniczeń rozmieszczenia (node.labels.<k>) |
| a | ustaw dostępność węzła — menu Active / Pause / Drain, które oznacza stan, w jakim węzeł jest obecnie. Active planuje tu zadania normalnie; Pause zachowuje działające zadania i blokuje tylko nowe rozmieszczenia; Drain przenosi wszystkie zadania z węzła. Active i Pause działają od razu; Drain przechodzi przez potwierdzenie, które podaje, ile działających zadań swarm zatrzyma i przeplanuje na innych węzłach (usługi, których zadań nie da się umieścić nigdzie indziej, staną się unschedulable). Tak jak zmiana etykiet jest to jeden NodeUpdate (odczyt-modyfikacja-zapis na specyfikacji węzła) i działa natychmiast — węzły nie mają aktualizacji kroczącej. Przemapowywalne jako node_availability |
| P | odzyskaj miejsce na dysku zajęte przez obrazy na węźle (wielkie P, czyli Shift+p — ten sam gest, którego zakładka Volumes używa do swojego prune'a). Otwiera małe menu z dwoma trybami opisanymi niżej, każdy za własnym potwierdzeniem; warto przeczytać, który jest który, zanim się wybierze. Stopka kończy się P reclaim images, a nakładka ? wypisuje to jako reclaim image disk space on the node. Przemapowywalne jako node_prune_images |
Zarezerwowane zasoby. Szczegóły węzła zawierają sekcję
reserved by tasks (scheduler's view): dla CPU i
pamięci pasek plus zarezerwowane / pojemność oraz ile
zostaje wolne. Pasek jest zielony, od 75% robi się żółty, a przy 100% lub powyżej
czerwony; nadsubskrypcja jest wprost nazwana, a „wolne” nigdy nie schodzi poniżej
zera. Węzeł, który nie zgłasza pojemności, mówi o tym zamiast rysować pasek. Sekcja
nie kosztuje żadnego dodatkowego wywołania API — lista węzłów i tak pobiera listę
zadań.
Reservations — dokładnie ta arytmetyka, którą sam scheduler
swarma wykonuje, decydując, czy zadanie mieści się na węźle, i typowy powód, dla
którego usługa zostaje unschedulable. Rzeczywiste zużycie CPU/RAM jest lokalne dla
węzła i nie jest tym, co tu widać — ma własną sekcję tuż poniżej.
Zadania, które nie deklarują żadnej
rezerwacji, są liczone i raportowane osobno: nic tu nie rezerwują,
a mimo to mogą zużyć cały węzeł — niski procent nie oznacza więc, że węzeł się nudzi,
a w większości klastrów mało która usługa w ogóle ustawia rezerwacje. Zadanie trzyma
swoją rezerwację od momentu przypisania do węzła aż do stanu końcowego: zadania w
preparing czy starting liczą się więc, a shutdown / failed / complete / rejected już
nie (swarm trzyma je na liście zadań jako historię).
Rzeczywiście zużywane przez kontenery. Pod spodem jest druga sekcja — in use by containers (measured on the node) — narysowana tymi samymi proporcjonalnymi paskami: to, co kontenery węzła zużywają naprawdę w tej chwili, CPU w rdzeniach i pamięć, każde względem pojemności samego węzła. Obie sekcje celowo trzymane są osobno, bo odpowiadają na różne pytania i regularnie bywają bardzo daleko od siebie: węzeł może być w pełni zarezerwowany i się nudzić albo ledwie zarezerwowany i płonąć. Liczby pochodzą od agenta tego węzła, z tego samego źródła co znaczniki zużycia w drzewie usług — dopóki różnica CPU nie jest gotowa, w linii stoi measuring…, a węzeł, którego agent jest nieosiągalny albo zbyt stary, nie dostaje sekcji w ogóle, zamiast rzędu zer, który czytałoby się jako „nic nie robi”.
Obrazy na tym węźle. Na końcu szczegółów stoi sekcja images on this node: ile trzyma magazyn warstw węzła i w ilu obrazach oraz ile z tego da się odzyskać w nieotagowanych pozostałościach (węzeł, który nie ma nic nieotagowanego, mówi to wprost, zamiast oferować zero). Gdy jest coś ponadto, dochodzi jeszcze jedna linia: ile miejsca jest dodatkowo otagowane, ale nieużywane — sformułowane jako koszt, usunięcie oznacza ponowne pobranie tych obrazów, a nie jako oferta. Sekcja istnieje, bo obrazy to jedyny lokalny zasób węzła, którego strona klastra nie widzi w ogóle: API menedżera nie ma o obrazach żadnej wiedzy — inaczej niż o wolumenach, o których przynajmniej wie, że są zamontowane. Wolumenami swarmexec potrafił już zarządzać w skali klastra; węzła, któremu dysk po cichu zapełniał się starymi warstwami, nie dało się nawet zobaczyć. Sekcja ładuje się samodzielnie, równolegle z listą węzłów, więc pojawia się chwilę po reszcie nakładki.
Odzyskiwanie — P w zakładce Nodes. Menu, które się otwiera, ma dwie pozycje, celowo nie jedną akcję z checkboxem, bo to dwa różne czyny, a checkbox aż zaprasza, żeby przez przypadek trafić w ten niszczący:
| Pozycja | Co usuwa |
|---|---|
| Untagged leftovers | warstwy zostawione przez przebudowy, z których nic nie da się uruchomić. Bezpieczne: nie zatrzyma usługi ani nie wymusi pobrania |
| Every unused image | usuwa dodatkowo obrazy otagowane, których w tej chwili nie uruchamia żaden kontener. Na węźle swarma obejmuje to każdą usługę wyskalowaną do zera i każde zadanie między restartami — każde z nich będzie musiało pobrać swój obraz na nowo |
Dwa tryby, dwa uprawnienia. Agent autoryzuje je jako dwie odrębne
akcje — image.prune dla nieotagowanych pozostałości i
image.prune.all dla zamiatania wszystkiego — więc polityka może
pozwolić na odzyskiwanie nieotagowanych warstw bez zgody na ten niszczący.
Każdy prune trafia do dziennika audytu agenta: z jakim trybem, ile
odzyskano i ile obrazów zniknęło.
docker system df, i zgadzają się z nim dokładnie —
sprawdzone na prawdziwym węźle przy 56 obrazach, 32,70 GB na dysku i
22,99 GB do odzyskania. W szczególności swarmexec nie sumuje
rozmiarów poszczególnych obrazów: rozmiar obrazu obejmuje każdą warstwę, z której
jest zbudowany, a warstwy są współdzielone, więc taka suma mocno zawyża — w trakcie
prac twierdziła 32,7 GB tam, gdzie demon mówił 22,2 GB. Jedno uczciwe
zastrzeżenie: liczba tylko nieotagowane to dolne
ograniczenie — warstwa współdzielona przez dwa nieotagowane obrazy nie
należy do własnego rozmiaru żadnego z nich — więc prune nieotagowanych może zwolnić
odrobinę więcej, niż zapowiada, nigdy mniej.
Ten sam warunek co przy liczbach zużycia i zdrowia powyżej: potrzebne są
agenty z tego wydania lub nowsze. Węzeł ze starszym agentem po
prostu nie pokazuje sekcji obrazów, a po naciśnięciu P zgłasza, że nie
odpowiedział — rozwiń je przez
swarmexec init --force.
Zakładka Configs
Zakładka 8 — bliźniaczka zakładki Secrets dla drugiego obiektu, który
swarm podaje kontenerom, z jedną decydującą różnicą: treść configu da się
odczytać. Zakładka Secrets nigdy nie pokaże wartości (API Dockera jej nie
zwraca); widok szczegółów configu pokazuje rzeczywistą treść, więc
widzisz, co usługa naprawdę dostaje, bez schodzenia do docker config
inspect. Po to właśnie jest ta zakładka.
Lista jest tylko do odczytu: CONFIG (nazwa), USED BY (ile
usług go montuje), SIZE, AGE, UPDATED oraz
LABELS (liczba).
| Klawisz | Akcja |
|---|---|
| Enter / i | otwórz widok szczegółów: nazwa, id, rozmiar, utworzenie, aktualizacja, usługi, które go montują, jego etykiety — i treść configu |
| j k / g G | w widoku szczegółów: przewijanie · skok na początek / koniec (Esc zamyka) |
Treść pobierana jest na żądanie, przy otwarciu szczegółów — nie razem
z listą — bo config może być całym plikiem nginx.conf; do tego czasu widać
loading…, a pusty config melduje (empty). Pokazywany jest
podgląd, ograniczony jednocześnie do
64 KiB i do 500 wierszy. Oba limity są
potrzebne: ograniczenie samych bajtów wciąż pozwala, by plik z bardzo krótkimi
wierszami zamienił się w dziesiątki tysięcy renderowanych wierszy — każdy jeszcze
wcięty — a to właśnie zamula widok. Skrócenie zawsze jest zaznaczone
(„… truncated at 500 lines” albo na limicie bajtów), nigdy nie jest cichym ucięciem, a
treść binarna jest zgłaszana, nie wysypywana („(binary content, N —
not shown)”, rozpoznawana po bajtach NUL na początku danych). Pełny config jest o
jedno docker config inspect stąd.
USED BY wyprowadzane jest dokładnie tak samo jak w zakładce Secrets:
swarm nie prowadzi żadnego odwrotnego indeksu od configu do jego
odbiorców, więc swarmexec czyta specyfikacje usług (jedno ServiceList) i
rozpoznaje odwołanie po ID albo nazwie configu — specyfikacja może
nieść dowolną z tych form, więc obie są sprawdzane i scalane. Best effort: jeśli listy
usług nie da się odczytać, configi i tak się wyświetlą, tylko bez tego przypisania.
Wbudowany shell & logi
We wbudowanym shellu Ctrl-] odłącza się (bez kończenia procesu). Widok
logów — pojedynczego kontenera albo zagregowane logi usługi — obsługuje to samo
parsowanie i filtrowanie świadome formatu co polecenie logs:
| Klawisz | Akcja |
|---|---|
| f | przełącz śledzenie |
| F | przełączaj format logów (classic → json → logfmt → gelf → raw) |
| l | przełączaj filtr minimalnego poziomu (off → trace → … → fatal → off) |
| / | wpisz wyrażenie regularne grep na komunikacie (puste je czyści) |
| m | przełącz przechwytywanie myszy (wyłączone = własne zaznaczanie/kopiowanie twojego terminala) |
| ↑ ↓ | przewijaj |
| Esc / q | zamknij |
Widok renderuje zbuforowane linie na nowo w locie, gdy zmienisz format, poziom albo
grep, a pasek tytułu pokazuje aktywny status fmt:… lvl:… grep:….
Podczas śledzenia widok logów łączy się ponownie po wymianie kontenera, tak samo jak
robi to polecenie logs — zarówno dla logów
pojedynczego kontenera, jak i dla zagregowanych logów usługi (każda replika śledzona
po slocie). Powiadomienia o ponownym połączeniu pojawiają się w widoku logów.
Podgląd logów
Naciśnij ` (backtick) z dowolnej zakładki, żeby przełączyć nakładkę z logami na żywo. Pokazuje ona najnowsze wpisy z bufora cyklicznego w pamięci — najnowsze na dole — odświeżając się, gdy jest otwarta, i kolorując według poziomu (czerwony = error, żółty = warn, szary = debug). ↑/↓ przewijają; Esc, q albo ` ją zamykają. To okno TUI na te same logi, które trafiają do pliku logu, bo sam terminal nie może ich pokazać, gdy UI rysuje po ekranie.
Własne klawisze
Klawisze skrótów można przemapować w ~/.config/swarmexec/keys.yaml
(obok config.yaml; ścieżkę nadpiszesz przez
$SWARMEXEC_KEYS). Każda wartość to pojedynczy klawisz albo słowo
space. Klawisze strukturalne — Enter, Esc,
Tab, strzałki, cyfry zakładek 1–8 oraz aliasy z vima
j/k/g/G — są stałe. Stopka zawsze pokazuje
twoje faktyczne klawisze, a nakładka ? wypisuje je wszystkie.
Wczytywanie nigdy się nie wywala: nieprawidłowy albo zarezerwowany klawisz, nieznana akcja czy dwie akcje przypisane do tego samego klawisza na jednej zakładce — każde z tych ustawień wraca do wartości domyślnej, a UI pokazuje ostrzeżenia raz przy starcie.
11. Kody wyjścia
| Kod | Znaczenie |
|---|---|
0 | sukces |
2 | błąd użycia / flagi / konfiguracji albo przerwany wybór interaktywny |
125 | awaria transportu — nieosiągalny menedżer albo agent, błąd połączenia/TLS, błąd strumienia, przerwane potwierdzenie (odzwierciedla 125 Dockera) |
N | dla exec — niezerowy kod wyjścia zdalnego polecenia, przekazany dosłownie |