Jak uruchomić wiele usług w jednym kontenerze Docker

Opublikowany: 2022-06-30

Ilustracja przedstawiająca logo Docker

Docker to technologia pakowania elementów stosu jako izolowanych pojemników. Powszechną praktyką jest uruchamianie każdego z procesów we własnym kontenerze, tworząc czysty podział między komponentami. Zwiększa to modułowość i umożliwia dostęp do korzyści związanych ze skalowalnością konteneryzacji.

Nadal mogą istnieć sytuacje, w których chcesz uruchomić wiele usług w jednym kontenerze. Chociaż nie jest to naturalne w ekosystemie platformy Docker, pokażemy kilka różnych podejść, których można użyć do tworzenia kontenerów z więcej niż jednym długotrwałym procesem.

Identyfikacja problemu

Kontenery platformy Docker uruchamiają jeden proces na pierwszym planie. Jest to określone w instrukcjach ENTRYPOINT i CMD obrazu. ENTRYPOINT jest ustawiony w pliku Dockerfile obrazu, podczas gdy CMD można zastąpić podczas tworzenia kontenerów. Kontenery zatrzymują się automatycznie po zakończeniu ich procesu na pierwszym planie.

Możesz uruchomić inne procesy z CMD , ale kontener będzie działał tylko wtedy, gdy oryginalny proces na pierwszym planie będzie działał. Utrzymanie kontenera w działaniu przez łączną żywotność dwóch niezależnych usług nie jest bezpośrednio możliwe przy użyciu mechanizmu ENTRYPOINT/CMD .

Zawijanie wielu procesów w jednym punkcie wejścia

Skrypty opakowujące to najprostsze rozwiązanie tego problemu. Możesz napisać skrypt, który uruchamia wszystkie twoje procesy i czeka na ich zakończenie. Ustawienie skryptu jako Docker ENTRYPOINT spowoduje uruchomienie go jako procesu pierwszego planu kontenera, utrzymując działanie kontenera do momentu zamknięcia jednego z opakowanych skryptów.

 #!/kosz/bash

/opt/pierwszy proces &

/opt/drugi proces &

czekaj - n

wyjść $?

Ten skrypt uruchamia pliki binarne /opt/first-process i /opt/second-process wewnątrz kontenera. Użycie & umożliwia kontynuację działania skryptu bez czekania na zakończenie każdego procesu. wait jest używany do zawieszenia skryptu do czasu zakończenia jednego z procesów. Skrypt następnie kończy działanie z kodem stanu wydanym przez gotowy skrypt.

Ten model powoduje, że kontener uruchamia zarówno first-process jak i second-process dopóki jeden z nich nie zostanie zamknięty. W tym momencie kontener zostanie zatrzymany, mimo że drugi proces może nadal działać.

Aby użyć tego skryptu, zmodyfikuj ENTRYPOINT i CMD obrazu Docker, tak aby był to proces pierwszego planu kontenera:

 PUNKT WEJŚCIA ["/bin/sh"]
CMD ["./ścieżka/do/skryptu.sh"]

Opcja kontenera --init

Jednym z wyzwań związanych z zarządzaniem procesami kontenerowymi jest skuteczne sprzątanie po ich wyjściu. Docker uruchamia Twój CMD jako proces o identyfikatorze 1, czyniąc go odpowiedzialnym za obsługę sygnałów i eliminację zombie. Jeśli Twój skrypt nie ma tych możliwości, możesz skończyć z osieroconymi procesami podrzędnymi, które będą trwać w Twoim kontenerze.

Polecenie docker docker run ma flagę --init , która modyfikuje punkt wejścia tak, aby używał tini jako PID 1. Jest to minimalna implementacja procesu init, która uruchamia CMD , obsługuje przekazywanie sygnałów i nieustannie zbiera zombie.

Warto użyć --init , jeśli spodziewasz się, że będzie pojawiać się wiele procesów i nie chcesz ręcznie zajmować się czyszczeniem. Tini to lekki smak init przeznaczony do pojemników. Jest znacznie mniejszy niż w pełni rozwinięte alternatywy, takie jak systemd i upstart .

Korzystanie z dedykowanego menedżera procesu

Ręczne skrypty szybko stają się nieoptymalne, gdy trzeba zarządzać wieloma procesami. Przyjęcie menedżera procesów to kolejny sposób uruchamiania kilku usług w kontenerach Dockera. Kierownik procesu staje się ENTRYPOINT i odpowiada za uruchamianie, konserwację i czyszczenie po procesach pracowniczych.

Przy wdrażaniu tego podejścia dostępnych jest kilka opcji. supervisord to popularny wybór, który można łatwo skonfigurować za pomocą pliku /etc/supervisor/conf.d/supervisord.conf :

 [program:apache2]
polecenie=/usr/sbin/apache2 -PRZEDZIEMIE

[program:mysqld]
polecenie=/usr/sbin/mysqld_safe

Ten plik konfiguracyjny konfiguruje supervisord do uruchamiania Apache i MySQL. Aby użyć go w kontenerze Docker, dodaj wszystkie wymagane pakiety do obrazu, a następnie skopiuj plik konfiguracyjny supervisord do właściwej lokalizacji. Ustaw supervisord jako CMD obrazu, aby uruchamiał go automatycznie po uruchomieniu kontenerów.

 Z ubuntu: najnowsze
URUCHOM apt-get install -y apache2 mysql-server nadzorca
KOPIUJ supervisor.conf /etc/supervisor/conf.d/supervisord.conf
PUNKT WEJŚCIA ["/bin/sh"]
CMD ["/usr/bin/nadzorca"]

Ponieważ narzędzie supervisord działa w sposób ciągły, nie jest możliwe zatrzymanie kontenera po zamknięciu jednego z monitorowanych procesów. Alternatywną opcją jest s6-overlay , która ma taką możliwość. Używa deklaratywnego modelu usług, w którym umieszczasz skrypty usług bezpośrednio w /etc/services.d :

 # Dodaj s6-overlay do swojego obrazu
DODAJ https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp
URUCHOM tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz

URUCHOM printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run
URUCHOM chmod +x /etc/services.d/first-service/run

# Użyj s6-overlay jako punktu wejściowego obrazu
PUNKT WEJŚCIOWY ["/początek"]

Możesz dodać wykonywalny skrypt finish w swoich katalogach usług, aby obsłużyć zatrzymywanie kontenera za pomocą docker stop . s6-overlay automatycznie uruchomi te skrypty, gdy jego proces otrzyma sygnał TERM z powodu polecenia stop .

Skrypty kończące otrzymują kod zakończenia swojej usługi jako pierwszy argument. Kod jest ustawiony na 256, gdy usługa zostanie zabita z powodu nieprzechwyconego sygnału. Skrypt musi napisać ostateczny kod wyjścia do /run/s6-linux-init-container-results/exitcode ; s6-overlay odczytuje ten plik i kończy działanie z wartością w obrębie, powodując, że ten kod jest używany jako kod zatrzymania kontenera.

 #!/kosz/sz
echo "$1" > /run/s6-linux-init-container-results/exitcode

Kiedy należy uruchamiać wiele procesów w kontenerze?

Ta technika najlepiej sprawdza się w przypadku ściśle powiązanych procesów, których nie można rozdzielić w celu uruchomienia jako niezależnych kontenerów. Możesz mieć program, który opiera się na narzędziu pomocniczym w tle lub monolitycznej aplikacji, która samodzielnie zarządza poszczególnymi procesami. Przedstawione powyżej techniki mogą pomóc w konteneryzacji tego typu oprogramowania.

W miarę możliwości należy unikać uruchamiania wielu procesów w kontenerze. Trzymanie się jednego procesu na pierwszym planie maksymalizuje izolację, zapobiega wzajemnemu zakłócaniu się komponentów i poprawia zdolność do debugowania i testowania określonych elementów. Składniki można skalować indywidualnie przy użyciu koordynatorów kontenerów, co zapewnia elastyczność uruchamiania większej liczby wystąpień procesów wymagających największej ilości zasobów.

Wniosek

Kontenery mają zwykle jeden proces na pierwszym planie i działają tak długo, jak żyją. Ten model jest zgodny z najlepszymi praktykami w zakresie konteneryzacji i pozwala czerpać największe korzyści z technologii.

W niektórych sytuacjach może być konieczne uruchomienie wielu procesów w kontenerze. Ponieważ wszystkie obrazy ostatecznie mają jeden punkt wejścia, musisz napisać skrypt opakowujący lub dodać menedżera procesów, który bierze odpowiedzialność za uruchamianie docelowych plików binarnych.

Menedżerowie procesów zapewniają wszystko, czego potrzebujesz, ale powiększają obrazy dodatkowymi pakietami i konfiguracją. Skrypty opakowujące są prostsze, ale mogą wymagać połączenia z flagą --init , aby zapobiec rozprzestrzenianiu się procesów zombie.