Część 2: Uruchamianie prawdziwych pipeline'ów¶
Tłumaczenie wspomagane przez AI - dowiedz się więcej i zasugeruj ulepszenia
W Części 1 tego kursu (Podstawowe operacje) zaczęliśmy od przykładowego workflow'u, który miał tylko minimalne funkcje, aby utrzymać niską złożoność kodu.
Na przykład 1-hello.nf używał parametru wiersza poleceń (--input) do przekazywania pojedynczej wartości na raz.
Jednak większość rzeczywistych pipeline'ów wykorzystuje bardziej zaawansowane funkcje, aby umożliwić efektywną obróbkę dużych ilości danych na skalę i stosowanie wielu kroków transformacji połączonych czasami złożoną logiką.
W tej części szkolenia demonstrujemy kluczowe funkcje rzeczywistych pipeline'ów, wypróbowując rozszerzone wersje oryginalnego pipeline'u Hello World.
1. Przetwarzanie danych wejściowych z pliku¶
W rzeczywistym pipeline'ie zazwyczaj chcemy przetwarzać wiele punktów danych (lub serii danych) zawartych w jednym lub więcej plikach wejściowych. I gdziekolwiek to możliwe, chcemy uruchamiać przetwarzanie niezależnych danych równolegle, aby skrócić czas oczekiwania na analizę.
Aby zademonstrować, jak Nextflow to robi, przygotowaliśmy plik CSV o nazwie greetings.csv, który zawiera kilka powitań wejściowych, naśladując rodzaj danych kolumnowych, które możesz chcieć przetwarzać w prawdziwej analizie danych.
Zauważ, że liczby nie mają znaczenia, są tam tylko w celach ilustracyjnych.
Napisaliśmy również ulepszoną wersję oryginalnego workflow'u, teraz o nazwie 2a-inputs.nf, która odczyta plik CSV, wyodrębni powitania i zapisze każde z nich do oddzielnego pliku.
Uruchommy najpierw workflow, a potem przyjrzymy się odpowiedniemu kodowi Nextflow.
1.1. Uruchom workflow¶
Uruchom następujące polecenie w terminalu.
Wyjście polecenia
Ekscytująco, wydaje się to wskazywać, że wykonano '3 of 3' wywołań dla procesu, co jest zachęcające, ponieważ w dostarczonym pliku CSV były trzy wiersze danych.
To sugeruje, że proces sayHello() został wywołany trzy razy, raz dla każdego wiersza wejściowego.
1.2. Znajdź opublikowane wyjścia w katalogu results¶
Przyjrzyjmy się katalogowi 'results', aby zobaczyć, czy nasz workflow nadal zapisuje kopię naszych wyjść tam.
Zawartość katalogu
Tak! Widzimy nowy katalog o nazwie 2a-inputs z trzema plikami wyjściowymi o różnych nazwach, co jest wygodne.
Możesz otworzyć każdy z nich, aby upewnić się, że zawierają odpowiedni ciąg powitania.
Zawartość plików
To potwierdza, że każde powitanie w pliku wejściowym zostało odpowiednio przetworzone.
1.3. Znajdź oryginalne wyjścia i logi¶
Być może zauważyłeś, że powyższe wyjście konsoli odnosiło się tylko do jednego katalogu zadania.
Czy to oznacza, że wszystkie trzy wywołania sayHello() zostały wykonane w tym jednym katalogu zadania?
1.3.1. Zbadaj katalog zadania podany w terminalu¶
Zajrzyjmy do tego katalogu zadania 8e/0eb066.
Znajdujemy tylko wyjście odpowiadające jednemu z powitań (oraz pliki pomocnicze, jeśli włączymy wyświetlanie ukrytych plików).
Więc co się dzieje?
Domyślnie system logowania ANSI zapisuje informacje o statusie dla wszystkich wywołań tego samego procesu w tej samej linii.
W rezultacie pokazał nam tylko jedną z trzech ścieżek katalogów zadań (8e/0eb066) w wyjściu konsoli.
Są dwie inne, które tam nie są wymienione.
1.3.2. Spraw, aby terminal pokazywał więcej szczegółów¶
Możemy zmodyfikować zachowanie logowania, aby zobaczyć pełną listę wywołań procesu, dodając -ansi-log false do polecenia w następujący sposób:
Wyjście polecenia
Tym razem widzimy wszystkie trzy uruchomienia procesu i ich powiązane podkatalogi robocze wymienione w wyjściu. Wyłączenie logowania ANSI również uniemożliwiło Nextflow używanie kolorów w wyjściu terminala.
Zauważ, że sposób raportowania statusu jest nieco inny między dwoma trybami logowania. W trybie skondensowanym Nextflow raportuje, czy wywołania zostały pomyślnie ukończone, czy nie. W tym rozszerzonym trybie raportuje tylko, że zostały przesłane.
To potwierdza, że proces sayHello() jest wywoływany trzy razy i dla każdego wywołania tworzony jest oddzielny katalog zadania.
Jeśli zajrzymy do każdego z katalogów zadań wymienionych tam, możemy zweryfikować, że każdy odpowiada jednemu z powitań.
Zawartość katalogu
To potwierdza, że każde wywołanie procesu jest wykonywane w izolacji od wszystkich innych. Ma to wiele zalet, w tym unikanie kolizji, jeśli proces produkuje jakieś pliki pośrednie o nieunikatowych nazwach.
Wskazówka
Dla złożonego workflow'u lub dużej liczby danych wejściowych wyświetlanie pełnej listy do terminala może być nieco przytłaczające, więc ludzie normalnie nie używają -ansi-log false w rutynowym użyciu.
1.4. Zbadaj kod workflow¶
Więc ta wersja workflow'u jest w stanie odczytać plik CSV z danymi wejściowymi, przetwarzać dane wejściowe osobno i nazywać wyjścia unikalnie.
Przyjrzyjmy się, co to umożliwia w kodzie workflow'u.
Pełny plik kodu
Ponownie, nie musisz zapamiętywać składni kodu, ale dobrze jest nauczyć się rozpoznawać kluczowe komponenty workflow'u, które zapewniają ważną funkcjonalność.
1.4.1. Ładowanie danych wejściowych z CSV¶
To jest najciekawsza część: jak przeszliśmy od pobierania pojedynczej wartości z wiersza poleceń do wczytywania pliku CSV, parsowania go i obsługi zawartych w nim pojedynczych powitań?
W Nextflow robimy to za pomocą kanału. Jest to konstrukcja zaprojektowana do efektywnego zarządzania danymi wejściowymi i przekazywania ich z jednego kroku do drugiego w wieloetapowych workflow'ach, zapewniając jednocześnie wbudowaną równoległość i wiele dodatkowych korzyści.
Rozłóżmy to na czynniki.
| 2a-inputs.nf | |
|---|---|
Ten kod tworzy kanał o nazwie greeting_ch, który odczytuje plik CSV, parsuje go i wyodrębnia pierwszą kolumnę z każdego wiersza.
Wynikiem jest kanał zawierający Hello, Bonjour i Holà.
Jak to działa?
Oto co ta linia oznacza prostym językiem:
channel.fromPathto fabryka kanału, która tworzy kanał ze ścieżki(ek) pliku(params.input)określa, że ścieżka pliku jest podana przez--inputw wierszu poleceń
Innymi słowy, ta linia mówi Nextflow: weź ścieżkę pliku podaną z --input i przygotuj się do traktowania jej zawartości jako danych wejściowych.
Następnie kolejne dwie linie stosują operatory, które wykonują faktyczne parsowanie pliku i ładowanie danych do odpowiedniej struktury danych:
.splitCsv()mówi Nextflow, aby sparsował plik CSV na tablicę reprezentującą wiersze i kolumny.map { line -> line[0] }mówi Nextflow, aby wziął tylko element z pierwszej kolumny z każdego wiersza
Więc w praktyce, zaczynając od następującego pliku CSV:
Przekształciliśmy to w tablicę, która wygląda tak:
A następnie wzięliśmy pierwszy element z każdego z trzech wierszy i załadowaliśmy je do kanału Nextflow, który teraz zawiera: Hello, Bonjour i Holà.
Jeśli chcesz zrozumieć kanały i operatory dogłębnie, w tym jak je samodzielnie pisać, zobacz Hello Nextflow Część 2: Hello Channels.
1.4.2. Wywołaj process na każdym powitaniu¶
Następnie, w ostatniej linii bloku main: workflow'u, przekazujemy załadowany kanał greeting_ch jako dane wejściowe do procesu sayHello().
| 2a-inputs.nf | |
|---|---|
To mówi Nextflow, aby uruchomił proces indywidualnie na każdym elemencie w kanale, tzn. na każdym powitaniu. A ponieważ Nextflow jest tak inteligentny, uruchomi te wywołania procesu równolegle, jeśli to możliwe, w zależności od dostępnej infrastruktury obliczeniowej.
W ten sposób można osiągnąć efektywne i skalowalne przetwarzanie dużej ilości danych (wielu próbek lub punktów danych, cokolwiek jest Twoją jednostką badawczą) przy stosunkowo niewielkiej ilości kodu.
1.4.3. Jak nazywane są wyjścia¶
Na koniec warto szybko spojrzeć na kod procesu, aby zobaczyć, jak uzyskujemy unikalne nazwy plików wyjściowych.
| 2a-inputs.nf | |
|---|---|
Widzisz, że w porównaniu z wersją tego procesu w 1-hello.nf, deklaracja wyjścia i odpowiednia część polecenia zmieniły się, aby uwzględnić wartość powitania w nazwie pliku wyjściowego.
To jest jeden ze sposobów na upewnienie się, że nazwy plików wyjściowych nie będą kolidować, gdy zostaną opublikowane do wspólnego katalogu results.
I to jest jedyna zmiana, którą musieliśmy wprowadzić wewnątrz deklaracji procesu!
Podsumowanie¶
Rozumiesz na podstawowym poziomie, jak kanały i operatory umożliwiają nam efektywne przetwarzanie wielu danych wejściowych.
Co dalej?¶
Odkryj, jak konstruowane są wieloetapowe workflow'y i jak działają.
2. Uruchamianie wieloetapowych workflow'ów¶
Większość rzeczywistych workflow'ów obejmuje więcej niż jeden krok. Opierając się na tym, czego właśnie nauczyliśmy się o kanałach, przyjrzyjmy się, jak Nextflow używa kanałów i operatorów do łączenia procesów w wieloetapowym workflow'ie.
W tym celu dostarczamy przykładowy workflow, który łączy trzy oddzielne kroki i demonstruje:
- Przepływ danych z jednego procesu do następnego
- Zbieranie wyjść z wielu wywołań procesu do jednego wywołania procesu
Konkretnie, stworzyliśmy rozszerzoną wersję workflow'u o nazwie 2b-multistep.nf, która przyjmuje każde powitanie wejściowe, konwertuje je na wielkie litery, a następnie zbiera wszystkie powitania wielkimi literami do jednego pliku wyjściowego.
Jak poprzednio, najpierw uruchomimy workflow, a potem przyjrzymy się kodowi, aby zobaczyć, co jest nowe.
2.1. Uruchom workflow¶
Uruchom następujące polecenie w terminalu:
Wyjście polecenia
Widzisz, że zgodnie z obietnicą, wiele kroków zostało uruchomionych jako część workflow; pierwsze dwa (sayHello i convertToUpper) były prawdopodobnie uruchomione na każdym indywidualnym powitaniu, a trzeci (collectGreetings) został uruchomiony tylko raz, na wyjściach wszystkich trzech wywołań convertToUpper.
2.2. Znajdź wyjścia¶
Zweryfikujmy, że to faktycznie się stało, patrząc na katalog results.
Zawartość katalogu
Jak widzisz, mamy nowy katalog o nazwie 2b-multistep i zawiera znacznie więcej plików niż wcześniej.
Niektóre pliki zostały zgrupowane w podkatalogu o nazwie intermediates, podczas gdy dwa pliki znajdują się na najwyższym poziomie.
Te dwa to końcowe wyniki wieloetapowego workflow. Poświęć chwilę, aby spojrzeć na nazwy plików i sprawdzić ich zawartość, aby potwierdzić, że są takie, jakich oczekujesz.
Zawartość plików
Pierwszy zawiera nasze trzy powitania, wielkimi literami i zebrane z powrotem do jednego pliku, zgodnie z obietnicą. Drugi to plik raportu, który podsumowuje pewne informacje o uruchomieniu.
2.3. Zbadaj kod¶
Przyjrzyjmy się kodowi i zidentyfikujmy kluczowe wzorce dla wieloetapowych workflow.
Pełny plik kodu
| 2b-multistep.nf | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
Dużo się tam dzieje, ale najbardziej oczywistą różnicą w porównaniu z poprzednią wersją workflow'u jest to, że teraz jest wiele definicji procesów i odpowiednio kilka wywołań procesów w bloku workflow'u.
Przyjrzyjmy się bliżej i zobaczmy, czy możemy zidentyfikować najciekawsze elementy.
2.3.1. Wizualizacja struktury workflow'u¶
Jeśli używasz VSCode z rozszerzeniem Nextflow, możesz uzyskać pomocny diagram pokazujący, jak procesy są połączone, klikając mały link DAG preview wyświetlany tuż nad blokiem workflow w dowolnym skrypcie Nextflow.
To daje ładny przegląd tego, jak procesy są połączone i co produkują.
Widzisz, że oprócz oryginalnego procesu sayHello, mamy teraz również convertToUpper i collectGreetings, które odpowiadają nazwom procesów, które widzieliśmy w wyjściu konsoli.
Dwie nowe definicje procesów są zbudowane w ten sam sposób co proces sayHello, z wyjątkiem tego, że collectGreetings przyjmuje dodatkowy parametr wejściowy o nazwie batch i produkuje dwa wyjścia.
Nie będziemy wchodzić w szczegóły kodu dla każdego z nich, ale jeśli jesteś ciekawy, możesz sprawdzić szczegóły w Części 2 Hello Nextflow.
Na razie przyjrzyjmy się, jak procesy są ze sobą połączone.
2.3.2. Jak procesy są połączone¶
Naprawdę interesującą rzeczą do przyjrzenia się jest to, jak wywołania procesów są połączone ze sobą w bloku main: workflow'u.
Widzisz, że pierwsze wywołanie procesu, sayHello(greeting_ch), jest niezmienione.
Następnie kolejne wywołanie procesu, do convertToUpper, odnosi się do wyjścia sayHello jako sayHello.out.
Wzorzec jest prosty: processName.out odnosi się do kanału wyjściowego procesu, który może być przekazany bezpośrednio do następnego procesu.
W ten sposób przekazujemy dane z jednego kroku do następnego w Nextflow.
2.3.3. Proces może przyjmować wiele danych wejściowych¶
Trzecie wywołanie procesu, do collectGreetings, jest nieco inne.
| 2b-multistep.nf | |
|---|---|
Widzisz, że to wywołanie otrzymuje dwa dane wejściowe, convertToUpper.out.collect() i params.batch.
Ignorując na razie część .collect(), możemy to uogólnić jako collectGreetings(input1, input2).
To odpowiada dwóm deklaracjom wejściowym w module procesu:
Gdy Nextflow parsuje to, przypisze pierwsze dane wejściowe w wywołaniu do path input_files, a drugie do val batch_name.
Więc teraz wiesz, że proces może przyjmować wiele danych wejściowych i jak wygląda wywołanie w bloku workflow'u.
Teraz przyjrzyjmy się bliżej temu pierwszemu wejściu, convertToUpper.out.collect().
2.3.4. Co robi collect() w wywołaniu collectGreetings¶
Aby przekazać wyjście sayHello do convertToUpper, po prostu odwołaliśmy się do kanału wyjściowego sayHello jako sayHello.out. Ale dla następnego kroku widzimy odniesienie do convertToUpper.out.collect().
Co to jest ten fragment collect() i co robi?
To oczywiście operator. Tak jak operatory splitCsv i map, które napotkaliśmy wcześniej.
Tym razem operator nazywa się collect i jest stosowany do kanału wyjściowego produkowanego przez convertToUpper.
Operator collect służy do agregowania wyjść z wielu wywołań tego samego procesu i pakowania ich w pojedynczy element kanału.
W kontekście tego workflow'u pobiera trzy powitania wielkimi literami z convertToUpper.out --które są trzema oddzielnymi elementami i normalnie byłyby obsługiwane w oddzielnych wykonaniach przez następny proces-- i łączy je w jeden element.
Bardziej praktycznie: gdybyśmy nie zastosowali collect() do wyjścia convertToUpper() przed przekazaniem go do collectGreetings(), Nextflow po prostu uruchomiłby collectGreetings() niezależnie na każdym powitaniu, co nie osiągnęłoby naszego celu.
W przeciwieństwie do tego, użycie collect() pozwala nam wziąć wszystkie oddzielne powitania wielkimi literami wyprodukowane przez drugi krok workflow'u i przekazać je wszystkie razem do jednego wywołania w trzecim kroku pipeline'u.
W ten sposób otrzymujemy wszystkie powitania z powrotem do tego samego pliku.
Jest wiele innych operatorów dostępnych do stosowania transformacji na zawartości kanału między wywołaniami procesów.
To daje twórcom pipeline'ów dużo elastyczności w dostosowywaniu logiki przepływu ich pipeline'u. Wadą jest to, że czasami może to utrudnić rozszyfrowanie tego, co pipeline robi.
2.3.5. Parametr wejściowy może mieć wartość domyślną¶
Być może zauważyłeś, że collectGreetings przyjmuje drugie wejście, params.batch:
| 2b-multistep.nf | |
|---|---|
To przekazuje parametr CLI o nazwie --batch do workflow.
Jednak gdy uruchomiliśmy workflow wcześniej, nie określiliśmy parametru --batch.
Co się dzieje?
Przyjrzyj się blokowi params:
W workflow'ie jest skonfigurowana wartość domyślna, więc nie musimy jej podawać. Ale jeśli podamy jedną w wierszu poleceń, wartość, którą określimy, zostanie użyta zamiast domyślnej.
Spróbuj:
Wyjście polecenia
Powinieneś zobaczyć nowe końcowe wyjścia nazwane Twoją własną nazwą batch.
Zawartość katalogu
To jest aspekt konfiguracji danych wejściowych, który omówimy bardziej szczegółowo w Części 3, ale na razie ważne jest, aby wiedzieć, że parametry wejściowe mogą mieć wartości domyślne.
2.3.6. Proces może produkować wiele wyjść¶
W definicji procesu collectGreetings widzimy następujące deklaracje wyjść:
| 2b-multistep.nf | |
|---|---|
Które są następnie przywoływane przez nazwę podaną z emit: w bloku publish::
| 2b-multistep.nf | |
|---|---|
To ułatwia przekazywanie konkretnych wyjść indywidualnie do innych procesów w workflow'ie, w połączeniu z różnymi operatorami.
2.3.7. Opublikowane wyjścia można organizować¶
W bloku output użyliśmy własnych ścieżek do grupowania wyników pośrednich, aby ułatwić wybranie tylko końcowych wyjść workflow'u.
| 2b-multistep.nf | |
|---|---|
Istnieją bardziej zaawansowane sposoby organizowania opublikowanych wyjść; omówimy kilka w części o konfiguracji.
Chcesz dowiedzieć się więcej o budowaniu workflow'ów?
Szczegółowe omówienie budowania wieloetapowych workflow'ów znajdziesz w Hello Nextflow Część 3: Hello Workflow.
Podsumowanie¶
Rozumiesz na podstawowym poziomie, jak wieloetapowe workflow'y są konstruowane przy użyciu kanałów i operatorów oraz jak działają. Widziałeś również, że procesy mogą przyjmować wiele danych wejściowych i produkować wiele wyjść, oraz że mogą być publikowane w uporządkowany sposób.
Co dalej?¶
Dowiedz się, jak pipeline'y Nextflow mogą być modułowe, aby promować ponowne wykorzystanie kodu i łatwość konserwacji.
3. Uruchamianie zmodularyzowanych pipeline'ów¶
Jak dotąd wszystkie workflow'y, które oglądaliśmy, składały się z jednego pojedynczego pliku workflow'u zawierającego cały odpowiedni kod.
Jednak rzeczywiste pipeline'y zazwyczaj korzystają z modularyzacji, co oznacza, że kod jest podzielony na różne pliki. To może uczynić ich rozwój i konserwację bardziej efektywnymi i zrównoważonymi.
Tutaj zademonstrujemy najpopularniejszą formę modułowości w Nextflow, czyli użycie modułów.
W Nextflow moduł to pojedyncza definicja procesu zamknięta w samodzielnym pliku. Aby go użyć w workflow'ie, wystarczy dodać jednoliniową instrukcję importu do głównego pliku; następnie można zintegrować funkcjonalność w normalny sposób. To umożliwia ponowne wykorzystanie definicji w wielu pipeline'ach bez tworzenia wielu kopii.
Do tej pory uruchamialiśmy workflow'y, które miały wszystkie swoje procesy zawarte w monolitycznym pliku kodu. Teraz zobaczymy, jak to wygląda, gdy procesy są przechowywane w indywidualnych modułach.
Oczywiście znowu przygotowaliśmy odpowiedni workflow do celów demonstracyjnych, o nazwie 2c-modules.nf, wraz z zestawem modułów znajdujących się w katalogu modules/.
Zawartość katalogu
Widzisz, że są cztery pliki Nextflow, każdy nazwany po jednym z procesów.
Na razie możesz zignorować plik cowpy.nf; zajmiemy się nim później.
3.1. Zbadaj kod¶
Tym razem najpierw przyjrzymy się kodowi.
Zacznij od otwarcia pliku workflow 2c-modules.nf.
Pełny plik kodu
Widzisz, że logika workflow'u jest dokładnie taka sama jak w poprzedniej wersji workflow'u.
Jednak kod procesu zniknął z pliku workflow'u, a zamiast tego są instrukcje include wskazujące na oddzielne pliki w katalogu modules.
| hello-modules.nf | |
|---|---|
Otwórz jeden z tych plików, a znajdziesz kod dla odpowiedniego procesu.
Pełny plik kodu
Jak widzisz, kod procesu nie zmienił się; został po prostu skopiowany do indywidualnego pliku modułu zamiast być w głównym pliku workflow'u. To samo dotyczy pozostałych dwóch procesów.
Zobaczmy więc, jak wygląda uruchomienie tej nowej wersji.
3.2. Uruchom workflow¶
Uruchom to polecenie w terminalu, z flagą -resume:
Wyjście polecenia
Zauważysz, że wykonania procesu wszystkie pomyślnie użyły pamięci podręcznej, co oznacza, że Nextflow rozpoznał, że już wykonał żądaną pracę, mimo że kod został podzielony, a główny plik workflow'u został przemianowany.
Nic z tego nie ma znaczenia dla Nextflow; liczy się skrypt zadania, który jest generowany po zebraniu i ocenie całego kodu.
Wskazówka
Możliwe jest również zamknięcie sekcji workflow'u jako 'subworkflow', który można zaimportować do większego pipeline'u, ale to wykracza poza zakres tego kursu.
Więcej o rozwijaniu komponowalnych workflow'ów możesz dowiedzieć się w Side Quest o Workflows of Workflows.
Podsumowanie¶
Wiesz, jak procesy mogą być przechowywane w samodzielnych modułach, aby promować ponowne wykorzystanie kodu i poprawić łatwość konserwacji.
Co dalej?¶
Naucz się używać kontenerów do zarządzania zależnościami oprogramowania.
4. Używanie konteneryzowanego oprogramowania¶
Jak dotąd workflow'y, których używaliśmy jako przykładów, musiały tylko uruchamiać bardzo podstawowe operacje przetwarzania tekstu przy użyciu narzędzi UNIX dostępnych w naszym środowisku.
Jednak rzeczywiste pipeline'y zazwyczaj wymagają specjalistycznych narzędzi i pakietów, które nie są domyślnie zawarte w większości środowisk. Zazwyczaj musiałbyś zainstalować te narzędzia, zarządzać ich zależnościami i rozwiązywać wszelkie konflikty.
To wszystko jest bardzo żmudne i denerwujące. Znacznie lepszym sposobem rozwiązania tego problemu jest użycie kontenerów.
Kontener to lekka, samodzielna, wykonywalna jednostka oprogramowania utworzona z obrazu kontenera, która zawiera wszystko, co potrzebne do uruchomienia aplikacji, w tym kod, biblioteki systemowe i ustawienia.
Wskazówka
Uczymy tego przy użyciu technologii Docker, ale Nextflow obsługuje również kilka innych technologii kontenerowych.
4.1. Użyj kontenera bezpośrednio¶
Najpierw spróbujmy bezpośrednio wejść w interakcję z kontenerem. To pomoże utrwalić zrozumienie tego, czym są kontenery, zanim zaczniemy ich używać w Nextflow.
4.1.1. Pobierz obraz kontenera¶
Aby użyć kontenera, zazwyczaj pobierasz lub "ściągasz" obraz kontenera z rejestru kontenerów, a następnie uruchamiasz obraz kontenera, aby utworzyć instancję kontenera.
Ogólna składnia jest następująca:
docker pullto instrukcja dla systemu kontenerowego, aby pobrać obraz kontenera z repozytorium.'<container>'to adres URI obrazu kontenera.
Jako przykład pobierzmy obraz kontenera zawierający cowpy, pythonową implementację narzędzia o nazwie cowsay, które generuje grafikę ASCII do wyświetlania dowolnych tekstowych danych wejściowych w zabawny sposób.
Istnieją różne repozytoria, w których można znaleźć opublikowane kontenery.
Użyliśmy usługi Seqera Containers, aby wygenerować ten obraz kontenera Docker z pakietu Conda cowpy: 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273'.
Uruchom pełne polecenie pull:
Wyjście polecenia
Unable to find image 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273' locally
131d6a1b707a8e65: Pulling from library/cowpy
dafa2b0c44d2: Pull complete
dec6b097362e: Pull complete
f88da01cff0b: Pull complete
4f4fb700ef54: Pull complete
92dc97a3ef36: Pull complete
403f74b0f85e: Pull complete
10b8c00c10a5: Pull complete
17dc7ea432cc: Pull complete
bb36d6c3110d: Pull complete
0ea1a16bbe82: Pull complete
030a47592a0a: Pull complete
622dd7f15040: Pull complete
895fb5d0f4df: Pull complete
Digest: sha256:fa50498b32534d83e0a89bb21fec0c47cc03933ac95c6b6587df82aaa9d68db3
Status: Downloaded newer image for community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273
community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273
To mówi systemowi, aby pobrał określony obraz. Po zakończeniu pobierania masz lokalną kopię obrazu kontenera.
4.1.2. Uruchom kontener¶
Kontenery można uruchamiać jako jednorazowe polecenie. Można ich również używać interaktywnie, co daje Ci wiersz poleceń wewnątrz kontenera i pozwala eksperymentować.
Ogólna składnia jest następująca:
docker run --rm '<container>'to instrukcja dla systemu kontenerowego, aby uruchomić instancję kontenera z obrazu kontenera i wykonać w niej polecenie.--rmmówi systemowi, aby wyłączył instancję kontenera po zakończeniu polecenia.
W pełni złożone polecenie wykonania kontenera wygląda tak:
Uruchom to polecenie, a powinieneś zobaczyć, że Twój znak zachęty zmienia się na coś takiego jak (base) root@b645838b3314:/tmp#, co wskazuje, że jesteś teraz wewnątrz kontenera.
Możesz to zweryfikować, uruchamiając ls, aby wylistować zawartość katalogu:
Wyjście polecenia
Widzisz, że system plików wewnątrz kontenera jest inny niż system plików na Twoim systemie hosta.
Wskazówka
Gdy uruchamiasz kontener, jest on domyślnie odizolowany od systemu hosta.
To oznacza, że kontener nie może uzyskać dostępu do żadnych plików w systemie hosta, chyba że wyraźnie na to pozwolisz, określając, że chcesz zamontować wolumin jako część polecenia docker run używając następującej składni:
To skutecznie ustanawia tunel przez ścianę kontenera, którego możesz użyć, aby uzyskać dostęp do tej części Twojego systemu plików.
Jest to omówione bardziej szczegółowo w Części 5 Hello Nextflow.
4.1.3. Uruchom narzędzie cowpy¶
Z wnętrza kontenera możesz uruchomić polecenie cowpy bezpośrednio.
Wyjście polecenia
To produkuje grafikę ASCII domyślnej postaci krowy (lub 'cowacter') z dymkiem mowy zawierającym określony przez nas tekst.
Teraz, gdy przetestowałeś podstawowe użycie, możesz spróbować podać mu jakieś parametry.
Na przykład dokumentacja narzędzia mówi, że możemy ustawić postać za pomocą -c.
Wyjście polecenia
Tym razem wyjście grafiki ASCII pokazuje pingwina Linuxa, Tux, ponieważ określiliśmy parametr -c tux.
Ponieważ jesteś wewnątrz kontenera, możesz uruchamiać polecenie cowpy tyle razy, ile chcesz, zmieniając parametry wejściowe, bez martwienia się o instalację jakichkolwiek bibliotek w samym systemie.
Inne dostępne postacie
Użyj flagi '-c', aby wybrać inną postać, w tym:
beavis, cheese, daemon, dragonandcow, ghostbusters, kitty, moose, milk, stegosaurus, turkey, turtle, tux
Możesz się tym pobawić.
Gdy skończysz, wyjdź z kontenera używając polecenia exit:
Znajdziesz się z powrotem w normalnej powłoce.
4.2. Użyj kontenera w workflow¶
Gdy uruchamiamy pipeline, chcemy wskazać Nextflow, jakiego obrazu użyć w każdym kroku. Co ważne, oczekujemy też, że automatycznie obsłuży całą tę pracę: pobierze obraz, uruchomi instancję, wykona polecenie i usunie ją po zakończeniu.
Dobra wiadomość: to dokładnie to, co Nextflow zrobi za nas. Musimy tylko określić kontener dla każdego procesu.
Aby zademonstrować, jak to działa, stworzyliśmy kolejną wersję naszego workflow'u, która uruchamia cowpy na pliku zebranych powitań wyprodukowanych w trzecim kroku.
To powinno wyprodukować plik zawierający grafikę ASCII z trzema powitaniami w dymku mowy.
4.2.1. Zbadaj kod¶
Workflow jest bardzo podobny do poprzedniego, plus dodatkowy krok do uruchomienia cowpy.
Pełny plik kodu
Widzisz, że ten workflow importuje proces cowpy z pliku modułu i wywołuje go na wyjściu wywołania collectGreetings(), plus parametr wejściowy o nazwie params.character.
| 2d-container.nf | |
|---|---|
Proces cowpy, który opakowuje polecenie cowpy do generowania grafiki ASCII, jest zdefiniowany w module cowpy.nf.
Pełny plik kodu
Proces cowpy wymaga dwóch danych wejściowych: ścieżki do pliku wejściowego zawierającego tekst do umieszczenia w dymku mowy (input_file) oraz wartości zmiennej character.
Co ważne, zawiera również linię container 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273', która wskazuje na URI kontenera, którego użyliśmy wcześniej.
4.2.2. Sprawdź, czy Docker jest włączony w konfiguracji¶
Trochę wyprzedzimy Część 3 tego kursu szkoleniowego, wprowadzając plik konfiguracyjny nextflow.config, który jest jednym z głównych sposobów, jakie Nextflow oferuje do konfigurowania wykonywania workflow'u.
Gdy plik o nazwie nextflow.config jest obecny w bieżącym katalogu, Nextflow automatycznie go załaduje i zastosuje zawartą w nim konfigurację.
W tym celu dołączyliśmy plik nextflow.config z pojedynczą linią kodu, która włącza Docker.
| nextflow.config | |
|---|---|
Ta konfiguracja mówi Nextflow, aby używał Docker dla każdego procesu, który określa kompatybilny kontener.
Wskazówka
Technicznie możliwe jest włączenie wykonywania Docker z wiersza poleceń, na podstawie pojedynczego uruchomienia, używając parametru -with-docker <container>.
Jednak to pozwala nam określić tylko jeden kontener dla całego workflow'u, podczas gdy podejście, które właśnie pokazaliśmy, pozwala nam określić inny kontener dla każdego procesu.
To drugie jest znacznie lepsze dla modułowości, konserwacji kodu i odtwarzalności.
4.2.3. Uruchom workflow¶
Podsumowując, oto co zamierzamy uruchomić:
Myślisz, że zadziała?
Uruchommy workflow z flagą -resume i określmy, że chcemy, aby postacią był indyk.
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `2d-container.nf` [elegant_brattain] DSL2 - revision: 028a841db1
executor > local (1)
[95/fa0bac] sayHello (3) | 3 of 3, cached: 3 ✔
[92/32533f] convertToUpper (3) | 3 of 3, cached: 3 ✔
[aa/e697a2] collectGreetings | 1 of 1, cached: 1 ✔
[7f/caf718] cowpy | 1 of 1 ✔
Pierwsze trzy kroki użyły pamięci podręcznej, ponieważ już je wcześniej uruchomiliśmy, ale proces cowpy jest nowy, więc faktycznie zostaje uruchomiony.
Możesz znaleźć wyjście kroku cowpy w katalogu results.
Zawartość pliku
_________
/ HOLà \
| HELLO |
\ BONJOUR /
---------
\ ,+*^^*+___+++_
\ ,*^^^^ )
\ _+* ^**+_
\ +^ _ _++*+_+++_, )
_+^^*+_ ( ,+*^ ^ \+_ )
{ ) ( ,( ,_+--+--, ^) ^\
{ (\@) } f ,( ,+-^ __*_*_ ^^\_ ^\ )
{:;-/ (_+*-+^^^^^+*+*<_ _++_)_ ) ) /
( / ( ( ,___ ^*+_+* ) < < \
U _/ ) *--< ) ^\-----++__) ) ) )
( ) _(^)^^)) ) )\^^^^^))^*+/ / /
( / (_))_^)) ) ) ))^^^^^))^^^)__/ +^^
( ,/ (^))^)) ) ) ))^^^^^^^))^^) _)
*+__+* (_))^) ) ) ))^^^^^^))^^^^^)____*^
\ \_)^)_)) ))^^^^^^^^^^))^^^^)
(_ ^\__^^^^^^^^^^^^))^^^^^^^)
^\___ ^\__^^^^^^))^^^^^^^^)\\
^^^^^\uuu/^^\uuu/^^^^\^\^\^\^\^\^\^\
___) >____) >___ ^\_\_\_\_\_\_\)
^^^//\\_^^//\\_^ ^(\_\_\_\)
^^^ ^^ ^^^ ^
Widzisz, że postać mówi wszystkie powitania, ponieważ uruchomiła się na pliku zebranych powitań wielkimi literami.
Co ważniejsze, mogliśmy to uruchomić jako część naszego pipeline bez konieczności prawidłowej instalacji cowpy i wszystkich jego zależności. I teraz możemy udostępnić pipeline współpracownikom i sprawić, że uruchomią go na swojej infrastrukturze bez konieczności instalowania czegokolwiek, poza Docker lub jedną z jego alternatyw (taką jak Singularity/Apptainer) jak wspomniano powyżej.
4.2.4. Sprawdź, jak Nextflow uruchomił konteneryzowane zadanie¶
Na zakończenie tej sekcji przyjrzyjmy się podkatalogowi roboczemu dla jednego z wywołań process cowpy, aby uzyskać nieco więcej wglądu w to, jak Nextflow pracuje z kontenerami pod maską.
Sprawdź wyjście z polecenia nextflow run, aby znaleźć ścieżkę do podkatalogu roboczego dla process cowpy.
Patrząc na to, co otrzymaliśmy dla uruchomienia pokazanego powyżej, linia dziennika konsoli dla process cowpy zaczyna się od [7f/caf718].
To odpowiada następującej skróconej ścieżce katalogu: work/7f/caf718.
W tym katalogu znajdziesz plik .command.run, który zawiera wszystkie polecenia, które Nextflow uruchomił w Twoim imieniu w trakcie wykonywania pipeline.
Zawartość pliku
Są tam polecenia docker, które Nextflow składa na podstawie URI obrazu kontenera podanego w definicji process, a także innych ustawień, które możemy określić za pomocą konfiguracji. Domyślnie Nextflow montuje katalog roboczy wewnątrz kontenera, co sprawia, że wszystkie pliki wejściowe i wyjściowe są dostępne bez dodatkowych czynności z naszej strony. Całkiem sprytne!
Podsumowanie¶
Wiesz, jak określić kontener dla process i jak Nextflow obsługuje go pod maską, aby uczynić Twój kod przenośnym i odtwarzalnym.
Co dalej?¶
W następnej części kursu dowiesz się, jak dostosować konfigurację workflow do swoich preferencji i środowiska obliczeniowego.