Parte 2: Executar pipelines reais¶
Tradução assistida por IA - saiba mais e sugira melhorias
Na Parte 1 deste curso (Operações Básicas de Execução), começamos com um fluxo de trabalho de exemplo que tinha apenas recursos mínimos para manter a complexidade do código baixa.
Por exemplo, 1-hello.nf usou um parâmetro de linha de comando (--input) para fornecer um único valor por vez.
No entanto, a maioria dos pipelines do mundo real usa recursos mais sofisticados para permitir o processamento eficiente de grandes quantidades de dados em escala e aplicar múltiplas etapas de processamento encadeadas por lógica às vezes complexa.
Nesta parte do treinamento, demonstramos recursos-chave de pipelines do mundo real experimentando versões expandidas do pipeline Hello World original.
1. Processando dados de entrada de um arquivo¶
Em um pipeline do mundo real, tipicamente queremos processar múltiplos pontos de dados (ou séries de dados) contidos em um ou mais arquivos de entrada. E onde possível, queremos executar o processamento de dados independentes em paralelo, para encurtar o tempo gasto esperando pela análise.
Para demonstrar como o Nextflow faz isso, preparamos um arquivo CSV chamado greetings.csv que contém várias saudações de entrada, imitando o tipo de dados colunares que você pode querer processar em uma análise de dados real.
Note que os números não são significativos, eles estão lá apenas para fins ilustrativos.
Também escrevemos uma versão melhorada do fluxo de trabalho original, agora chamada 2a-inputs.nf, que lerá o arquivo CSV, extrairá as saudações e escreverá cada uma delas em um arquivo separado.
Vamos executar o fluxo de trabalho primeiro, e depois olharemos o código Nextflow relevante.
1.1. Execute o fluxo de trabalho¶
Execute o seguinte comando no seu terminal.
Saída do comando
Empolgante, isso parece indicar que '3 of 3' chamadas foram feitas para o processo, o que é encorajador, já que havia três linhas de dados no CSV que fornecemos como entrada.
Isso sugere que o processo sayHello() foi chamado três vezes, uma vez em cada linha de entrada.
1.2. Encontre as saídas publicadas no diretório results¶
Vamos olhar o diretório 'results' para ver se nosso fluxo de trabalho ainda está escrevendo uma cópia de nossas saídas lá.
Conteúdo do diretório
Sim! Vemos um novo diretório chamado 2a-inputs com três arquivos de saída com nomes diferentes, convenientemente.
Você pode abrir cada um deles para se satisfazer de que contêm a string de saudação apropriada.
Conteúdo dos arquivos
Isso confirma que cada saudação no arquivo de entrada foi processada apropriadamente.
1.3. Encontre as saídas originais e os logs¶
Você pode ter notado que a saída do console acima se referiu a apenas um diretório de tarefa.
Isso significa que todas as três chamadas a sayHello() foram executadas dentro daquele único diretório de tarefa?
1.3.1. Examine o diretório de tarefa dado no terminal¶
Vamos dar uma olhada dentro daquele diretório de tarefa 8e/0eb066.
Encontramos apenas a saída correspondente a uma das saudações (bem como os arquivos acessórios se habilitarmos a exibição de arquivos ocultos).
Então o que está acontecendo aqui?
Por padrão, o sistema de logging ANSI escreve as informações de status para todas as chamadas ao mesmo processo na mesma linha.
Como resultado, ele nos mostrou apenas um dos três caminhos de diretório de tarefa (8e/0eb066) na saída do console.
Há outros dois que não estão listados lá.
1.3.2. Faça o terminal mostrar mais detalhes¶
Podemos modificar o comportamento de logging para ver a lista completa de chamadas de processo adicionando o -ansi-log false ao comando da seguinte forma:
Saída do comando
Desta vez vemos todas as três execuções de processo e seus subdiretórios de trabalho associados listados na saída. Desabilitar o logging ANSI também impediu o Nextflow de usar cores na saída do terminal.
Note que a forma como o status é reportado é um pouco diferente entre os dois modos de logging. No modo condensado, o Nextflow reporta se as chamadas foram completadas com sucesso ou não. Neste modo expandido, ele apenas reporta que foram submetidas.
Isso confirma que o processo sayHello() é chamado três vezes, e um diretório de tarefa separado é criado para cada um.
Se olharmos dentro de cada um dos diretórios de tarefa listados lá, podemos verificar que cada um corresponde a uma das saudações.
Conteúdo do diretório
Isso confirma que cada chamada de processo é executada em isolamento de todas as outras. Isso tem muitas vantagens, incluindo evitar colisões se o processo produzir quaisquer arquivos intermediários com nomes não-únicos.
Dica
Para um fluxo de trabalho complexo, ou um grande número de entradas, ter a lista completa exibida no terminal pode ficar um pouco avassalador, então as pessoas normalmente não usam -ansi-log false no uso rotineiro.
1.4. Examine o código do fluxo de trabalho¶
Então esta versão do fluxo de trabalho é capaz de ler um arquivo CSV de entradas, processar as entradas separadamente e nomear as saídas de forma única.
Vamos dar uma olhada no que torna isso possível no código do fluxo de trabalho.
Arquivo de código completo
Novamente, você não precisa memorizar sintaxe de código, mas é bom aprender a reconhecer componentes-chave do fluxo de trabalho que fornecem funcionalidade importante.
1.4.1. Carregando os dados de entrada do CSV¶
Esta é a parte mais interessante: como mudamos de receber um único valor da linha de comando para receber um arquivo CSV, analisá-lo e processar as saudações individuais que ele contém?
No Nextflow, fazemos isso com um canal: uma construção projetada para lidar com entradas eficientemente e transportá-las de uma etapa para outra em fluxos de trabalho de múltiplas etapas, enquanto fornece paralelismo embutido e muitos benefícios adicionais.
Vamos analisar.
| 2a-inputs.nf | |
|---|---|
Este código cria um canal chamado greeting_ch que lê o arquivo CSV, analisa-o e extrai a primeira coluna de cada linha.
O resultado é um canal contendo Hello, Bonjour e Holà.
Como isso funciona?
Aqui está o que essa linha significa em português simples:
channel.fromPathé uma fábrica de canal que cria um canal a partir de caminho(s) de arquivo(params.input)especifica que o caminho do arquivo é fornecido por--inputna linha de comando
Em outras palavras, essa linha diz ao Nextflow: pegue o caminho do arquivo dado com --input e prepare-se para tratar seu conteúdo como dados de entrada.
Então as próximas duas linhas aplicam operadores que fazem a análise real do arquivo e o carregamento dos dados na estrutura de dados apropriada:
.splitCsv()diz ao Nextflow para analisar o arquivo CSV em um array representando linhas e colunas.map { line -> line[0] }diz ao Nextflow para pegar apenas o elemento na primeira coluna de cada linha
Então na prática, começando do seguinte arquivo CSV:
Transformamos isso em um array que se parece com isso:
E então pegamos o primeiro elemento de cada uma das três linhas e os carregamos em um canal Nextflow que agora contém: Hello, Bonjour e Holà.
Se você quiser entender canais e operadores em profundidade, incluindo como escrevê-los você mesmo, veja Hello Nextflow Parte 2: Hello Channels.
1.4.2. Chame o processo em cada saudação¶
A seguir, na última linha do bloco main: do fluxo de trabalho, fornecemos o canal greeting_ch carregado como entrada para o processo sayHello().
| 2a-inputs.nf | |
|---|---|
Isso diz ao Nextflow para executar o processo individualmente em cada elemento no canal, ou seja, em cada saudação. E porque o Nextflow é inteligente assim, ele executará essas chamadas de processo em paralelo se possível, dependendo da infraestrutura computacional disponível.
É assim que você pode alcançar processamento eficiente e escalável de muitos dados (muitas amostras, ou pontos de dados, seja qual for sua unidade de pesquisa) com comparativamente muito pouco código.
1.4.3. Como as saídas são nomeadas¶
Finalmente, vale a pena dar uma olhada rápida no código do processo para ver como fazemos os arquivos de saída serem nomeados de forma única.
| 2a-inputs.nf | |
|---|---|
Você vê que, comparado à versão deste processo em 1-hello.nf, a declaração de saída e a parte relevante do comando mudaram para incluir o valor da saudação no nome do arquivo de saída.
Esta é uma forma de garantir que os nomes dos arquivos de saída não colidirão quando forem publicados no diretório de resultados comum.
E essa é a única mudança que tivemos que fazer dentro da declaração do processo!
Conclusão¶
Você entende em um nível básico como canais e operadores nos permitem processar múltiplas entradas eficientemente.
O que vem a seguir?¶
Descubra como fluxos de trabalho de múltiplas etapas são construídos e como eles operam.
2. Executando fluxos de trabalho de múltiplas etapas¶
A maioria dos fluxos de trabalho do mundo real envolve mais de uma etapa. Vamos construir sobre o que acabamos de aprender sobre canais, e olhar como o Nextflow usa canais e operadores para conectar processos em um fluxo de trabalho de múltiplas etapas.
Para isso, fornecemos a você um fluxo de trabalho de exemplo que encadeia três etapas separadas e demonstra o seguinte:
- Fazer dados fluírem de um processo para o próximo
- Coletar saídas de múltiplas chamadas de processo em uma única chamada de processo
Especificamente, fizemos uma versão expandida do fluxo de trabalho chamada 2b-multistep.nf que pega cada saudação de entrada, converte-a para maiúsculas, depois coleta todas as saudações em maiúsculas em um único arquivo de saída.
Como anteriormente, executaremos o fluxo de trabalho primeiro e depois olharemos o código para ver o que é novo.
2.1. Execute o fluxo de trabalho¶
Execute o seguinte comando no seu terminal:
Saída do comando
Você vê que como prometido, múltiplas etapas foram executadas como parte do fluxo de trabalho; as duas primeiras (sayHello e convertToUpper) foram presumivelmente executadas em cada saudação individual, e a terceira (collectGreetings) terá sido executada apenas uma vez, nas saídas de todas as três chamadas convertToUpper.
2.2. Encontre as saídas¶
Vamos verificar que isso é de fato o que aconteceu olhando no diretório results.
Conteúdo do diretório
Como você pode ver, temos um novo diretório chamado 2b-multistep, e ele contém bem mais arquivos do que antes.
Alguns dos arquivos foram agrupados em um subdiretório chamado intermediates, enquanto dois arquivos estão localizados no nível superior.
Esses dois são os resultados finais do fluxo de trabalho de múltiplas etapas. Tire um minuto para olhar os nomes dos arquivos e verificar seu conteúdo para confirmar que são o que você espera.
Conteúdo dos arquivos
O primeiro contém nossas três saudações, em maiúsculas e coletadas de volta em um único arquivo como prometido. O segundo é um arquivo de relatório que resume algumas informações sobre a execução.
2.3. Examine o código¶
Vamos olhar o código e identificar os padrões-chave para fluxos de trabalho de múltiplas etapas.
Arquivo de código completo
| 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 | |
Há muita coisa acontecendo ali, mas a diferença mais óbvia comparada à versão anterior do fluxo de trabalho é que agora há múltiplas definições de processo, e correspondentemente, várias chamadas de processo no bloco workflow.
Vamos dar uma olhada mais de perto e ver se conseguimos identificar as peças mais interessantes.
2.3.1. Visualizando a estrutura do fluxo de trabalho¶
Se você está usando VSCode com a extensão Nextflow, você pode obter um diagrama útil de como os processos estão conectados clicando no pequeno link DAG preview exibido logo acima do bloco workflow em qualquer script Nextflow.
Isso dá a você uma boa visão geral de como os processos estão conectados e o que eles produzem.
Você vê que além do processo sayHello original, agora também temos convertToUpper e collectGreetings, que correspondem aos nomes dos processos que vimos na saída do console.
As duas novas definições de processo são estruturadas da mesma forma que o processo sayHello, exceto que collectGreetings recebe um parâmetro de entrada adicional chamado batch e produz duas saídas.
Não entraremos no código de cada um em detalhes, mas se você está curioso, pode consultar os detalhes em Parte 2 do Hello Nextflow.
Por agora, vamos investigar como os processos estão conectados uns aos outros.
2.3.2. Como os processos estão conectados¶
A coisa realmente interessante a observar aqui é como as chamadas de processo estão encadeadas no bloco main: do fluxo de trabalho.
Você pode ver que a primeira chamada de processo, sayHello(greeting_ch), está inalterada.
Então a próxima chamada de processo, para convertToUpper, se refere à saída de sayHello como sayHello.out.
O padrão é simples: processName.out se refere ao canal de saída de um processo, que pode ser passado diretamente para o próximo processo.
É assim que transportamos dados de uma etapa para a próxima no Nextflow.
2.3.3. Um processo pode receber múltiplas entradas¶
A terceira chamada de processo, para collectGreetings, é um pouco diferente.
| 2b-multistep.nf | |
|---|---|
Você vê que esta chamada recebe duas entradas, convertToUpper.out.collect() e params.batch.
Ignorando o bit .collect() por enquanto, podemos generalizar isso como collectGreetings(input1, input2).
Isso corresponde às duas declarações de entrada no módulo do processo:
Quando o Nextflow analisa isso, ele atribuirá a primeira entrada na chamada a path input_files, e a segunda a val batch_name.
Então agora você sabe que um processo pode receber múltiplas entradas, e como a chamada se parece no bloco workflow.
Agora vamos dar uma olhada mais de perto naquela primeira entrada, convertToUpper.out.collect().
2.3.4. O que collect() faz na chamada collectGreetings¶
Para passar a saída de sayHello para convertToUpper, simplesmente nos referimos ao canal de saída de sayHello como sayHello.out. Mas para a próxima etapa, estamos vendo uma referência a convertToUpper.out.collect().
O que é esse bit collect() e o que ele faz?
É um operador, é claro. Assim como os operadores splitCsv e map que encontramos anteriormente.
Desta vez o operador é chamado collect, e é aplicado ao canal de saída produzido por convertToUpper.
O operador collect é usado para coletar as saídas de múltiplas chamadas ao mesmo processo e empacotá-las em um único elemento de canal.
No contexto deste fluxo de trabalho, ele está pegando as três saudações em maiúsculas no canal convertToUpper.out --que são três itens de canal separados, e normalmente seriam tratados em chamadas separadas pelo próximo processo-- e empacotando-os em um único item.
Em termos mais práticos: se não aplicássemos collect() à saída de convertToUpper() antes de alimentá-la para collectGreetings(), o Nextflow simplesmente executaria collectGreetings() independentemente em cada saudação, o que não alcançaria nosso objetivo.
Em contraste, usar collect() nos permite pegar todas as saudações em maiúsculas separadas produzidas pela segunda etapa do fluxo de trabalho e alimentá-las todas juntas para uma única chamada na terceira etapa do pipeline.
É assim que colocamos todas as saudações de volta no mesmo arquivo.
Há muitos outros operadores disponíveis para aplicar transformações ao conteúdo de canais entre chamadas de processo.
Isso dá aos desenvolvedores de pipeline muita flexibilidade para personalizar a lógica de fluxo de seu pipeline. A desvantagem é que às vezes pode tornar mais difícil decifrar o que o pipeline está fazendo.
2.3.5. Um parâmetro de entrada pode ter um valor padrão¶
Você pode ter notado que collectGreetings recebe uma segunda entrada, params.batch:
| 2b-multistep.nf | |
|---|---|
Isso passa um parâmetro CLI chamado --batch para o fluxo de trabalho.
No entanto, quando executamos o fluxo de trabalho anteriormente, não especificamos um parâmetro --batch.
O que está acontecendo aí?
Dê uma olhada no bloco params:
Há um valor padrão configurado no fluxo de trabalho, então não precisamos fornecê-lo. Mas se fornecermos um na linha de comando, o valor que especificarmos será usado em vez do padrão.
Tente:
Saída do comando
Você deve ver novas saídas finais nomeadas com seu nome de lote personalizado.
Conteúdo do diretório
Este é um aspecto da configuração de entrada, que cobriremos com mais detalhes na Parte 3, mas por enquanto o importante é saber que parâmetros de entrada podem receber valores padrão.
2.3.6. Um processo pode produzir múltiplas saídas¶
Na definição do processo collectGreetings, vemos as seguintes declarações de saída:
| 2b-multistep.nf | |
|---|---|
Que são então referenciadas pelo nome dado com emit: no bloco publish::
| 2b-multistep.nf | |
|---|---|
Isso torna fácil então passar saídas específicas individualmente para outros processos no fluxo de trabalho, em combinação com vários operadores.
2.3.7. Saídas publicadas podem ser organizadas¶
No bloco output, usamos caminhos personalizados para agrupar resultados intermediários para tornar mais fácil destacar apenas as saídas finais do fluxo de trabalho.
| 2b-multistep.nf | |
|---|---|
Há formas mais sofisticadas de organizar saídas publicadas; abordaremos algumas na parte sobre configuração.
Quer aprender mais sobre construir fluxos de trabalho?
Para cobertura detalhada de construir fluxos de trabalho de múltiplas etapas, veja Hello Nextflow Parte 3: Hello Workflow.
Conclusão¶
Você entende em um nível básico como fluxos de trabalho de múltiplas etapas são construídos usando canais e operadores e como eles operam. Você também viu que processos podem receber múltiplas entradas e produzir múltiplas saídas, e que estas podem ser publicadas de forma estruturada.
O que vem a seguir?¶
Aprenda como pipelines Nextflow podem ser modularizados para promover reutilização de código e manutenibilidade.
3. Executando pipelines modularizados¶
Até agora, todos os fluxos de trabalho que olhamos consistiram em um único arquivo de fluxo de trabalho contendo todo o código relevante.
No entanto, pipelines do mundo real tipicamente se beneficiam de serem modularizados, significando que o código é dividido em diferentes arquivos. Isso pode tornar seu desenvolvimento e manutenção mais eficientes e sustentáveis.
Aqui vamos demonstrar a forma mais comum de modularidade de código no Nextflow, que é o uso de módulos.
No Nextflow, um módulo é uma única definição de processo que é encapsulada sozinha em um arquivo de código autônomo. Para usar um módulo em um fluxo de trabalho, você apenas adiciona uma declaração de importação de uma linha ao seu arquivo de código de fluxo de trabalho; então você pode integrar o processo no fluxo de trabalho da mesma forma que normalmente faria. Isso torna possível reutilizar definições de processo em múltiplos fluxos de trabalho sem produzir múltiplas cópias do código.
Até agora estávamos executando fluxos de trabalho que tinham todos os seus processos incluídos em um arquivo de código monolítico. Agora vamos ver como fica quando os processos são armazenados em módulos individuais.
Preparamos novamente um fluxo de trabalho adequado para fins de demonstração, chamado 2c-modules.nf, junto com um conjunto de módulos localizados no diretório modules/.
Conteúdo do diretório
Você vê que há quatro arquivos Nextflow, cada um nomeado após um dos processos.
Você pode ignorar o arquivo cowpy.nf por enquanto; chegaremos a ele mais tarde.
3.1. Examine o código¶
Desta vez vamos olhar o código primeiro.
Comece abrindo o arquivo de fluxo de trabalho 2c-modules.nf.
Arquivo de código completo
Você vê que a lógica do fluxo de trabalho é exatamente a mesma da versão anterior do fluxo de trabalho.
No entanto, o código do processo foi removido do arquivo de fluxo de trabalho, e em vez disso há declarações include apontando para arquivos separados em modules.
| hello-modules.nf | |
|---|---|
Abra um desses arquivos e você encontrará o código para o processo correspondente.
Arquivo de código completo
Como você pode ver, o código do processo não mudou; ele foi apenas copiado para um arquivo de módulo individual em vez de estar no arquivo de fluxo de trabalho principal. O mesmo se aplica aos outros dois processos.
Então vamos ver como fica executar esta nova versão.
3.2. Execute o fluxo de trabalho¶
Execute este comando no seu terminal, com a flag -resume:
Saída do comando
Você notará que as execuções de processo foram todas cacheadas com sucesso, significando que o Nextflow reconheceu que já fez o trabalho solicitado, mesmo que o código tenha sido dividido e o arquivo de fluxo de trabalho principal tenha sido renomeado.
Nada disso importa para o Nextflow; o que importa é o script de job que é gerado uma vez que todo o código foi reunido e avaliado.
Dica
Também é possível encapsular uma seção de um fluxo de trabalho como um 'subworkflow' que pode ser importado em um pipeline maior, mas isso está fora do escopo deste curso.
Você pode aprender mais sobre desenvolver fluxos de trabalho composíveis na Side Quest sobre Workflows of Workflows.
Conclusão¶
Você sabe como processos podem ser armazenados em módulos autônomos para promover reutilização de código e melhorar a manutenibilidade.
O que vem a seguir?¶
Aprenda a usar contêineres para gerenciar dependências de software.
4. Usando software em contêiner¶
Até agora os fluxos de trabalho que estamos usando como exemplos só precisavam executar operações de processamento de texto muito básicas usando ferramentas UNIX disponíveis em nosso ambiente.
No entanto, pipelines do mundo real tipicamente requerem ferramentas e pacotes especializados que não estão incluídos por padrão na maioria dos ambientes. Normalmente, você precisaria instalar essas ferramentas, gerenciar suas dependências e resolver quaisquer conflitos.
Tudo isso é muito tedioso e irritante. Uma forma muito melhor de abordar este problema é usar contêineres.
Um contêiner é uma unidade leve, autônoma e executável de software criada a partir de uma imagem de contêiner que inclui tudo necessário para executar uma aplicação incluindo código, bibliotecas de sistema e configurações.
Dica
Ensinamos isso usando a tecnologia Docker, mas o Nextflow suporta várias outras tecnologias de contêiner também.
4.1. Use um contêiner diretamente¶
Primeiro, vamos tentar interagir com um contêiner diretamente. Isso ajudará a solidificar seu entendimento do que são contêineres antes de começarmos a usá-los no Nextflow.
4.1.1. Baixe a imagem do contêiner¶
Para usar um contêiner, você normalmente baixa ou "puxa" uma imagem de contêiner de um registro de contêiner, e então executa a imagem de contêiner para criar uma instância de contêiner.
A sintaxe geral é a seguinte:
docker pullé a instrução para o sistema de contêiner para puxar uma imagem de contêiner de um repositório.'<container>'é o endereço URI da imagem de contêiner.
Como exemplo, vamos puxar uma imagem de contêiner que contém cowpy, uma implementação em python de uma ferramenta chamada cowsay que gera arte ASCII para exibir entradas de texto arbitrárias de forma divertida.
Há vários repositórios onde você pode encontrar contêineres publicados.
Usamos o serviço Seqera Containers para gerar esta imagem de contêiner Docker a partir do pacote Conda cowpy: 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273'.
Execute o comando de pull completo:
Saída do comando
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
Isso diz ao sistema para baixar a imagem especificada. Uma vez que o download esteja completo, você tem uma cópia local da imagem de contêiner.
4.1.2. Inicie o contêiner¶
Contêineres podem ser executados como um comando único, mas você também pode usá-los interativamente, o que dá a você um prompt de shell dentro do contêiner e permite que você brinque com o comando.
A sintaxe geral é a seguinte:
docker run --rm '<container>'é a instrução para o sistema de contêiner para iniciar uma instância de contêiner a partir de uma imagem de contêiner e executar um comando nela.--rmdiz ao sistema para desligar a instância de contêiner após o comando ter sido concluído.
Completamente montado, o comando de execução do contêiner se parece com isto:
Execute esse comando, e você deve ver seu prompt mudar para algo como (base) root@b645838b3314:/tmp#, que indica que você agora está dentro do contêiner.
Você pode verificar isso executando ls para listar o conteúdo do diretório:
Saída do comando
Você vê que o sistema de arquivos dentro do contêiner é diferente do sistema de arquivos no seu sistema host.
Dica
Quando você executa um contêiner, ele é isolado do sistema host por padrão.
Isso significa que o contêiner não pode acessar nenhum arquivo no sistema host a menos que você explicitamente permita fazê-lo especificando que quer montar um volume como parte do comando docker run usando a seguinte sintaxe:
Isso efetivamente estabelece um túnel através da parede do contêiner que você pode usar para acessar essa parte do seu sistema de arquivos.
Isso é coberto em mais detalhes na Parte 5 do Hello Nextflow.
4.1.3. Execute a ferramenta cowpy¶
De dentro do contêiner, você pode executar o comando cowpy diretamente.
Saída do comando
Isso produz arte ASCII do personagem vaca padrão (ou 'cowacter') com um balão de fala contendo o texto que especificamos.
Agora que você testou o uso básico, pode tentar dar alguns parâmetros.
Por exemplo, a documentação da ferramenta diz que podemos definir o personagem com -c.
Saída do comando
Desta vez a saída de arte ASCII mostra o pinguim do Linux, Tux, porque especificamos o parâmetro -c tux.
Como você está dentro do contêiner, pode executar o comando cowpy quantas vezes quiser, variando os parâmetros de entrada, sem se preocupar em instalar nenhuma biblioteca no seu próprio sistema.
Outros personagens disponíveis
Use a flag '-c' para escolher um personagem diferente, incluindo:
beavis, cheese, daemon, dragonandcow, ghostbusters, kitty, moose, milk, stegosaurus, turkey, turtle, tux
Sinta-se livre para brincar com isso.
Quando terminar, saia do contêiner usando o comando exit:
Você se encontrará de volta no seu shell normal.
4.2. Use um contêiner em um fluxo de trabalho¶
Quando executamos um pipeline, queremos ser capazes de dizer ao Nextflow qual contêiner usar em cada etapa, e importante, queremos que ele lide com todo aquele trabalho que acabamos de fazer: puxar o contêiner, iniciá-lo, executar o comando e derrubar o contêiner quando terminar.
Boa notícia: é exatamente isso que o Nextflow vai fazer por nós. Nós só precisamos especificar um contêiner para cada processo.
Para demonstrar como isso funciona, fizemos outra versão do nosso fluxo de trabalho que executa cowpy no arquivo de saudações coletadas produzido na terceira etapa.
Isso deve produzir um arquivo contendo a arte ASCII com as três saudações no balão de fala.
4.2.1. Examine o código¶
O fluxo de trabalho é muito similar ao anterior, mais a etapa extra para executar cowpy.
Arquivo de código completo
Você vê que este fluxo de trabalho importa um processo cowpy de um arquivo de módulo, e o chama na saída da chamada collectGreetings(), mais um parâmetro de entrada chamado params.character.
O processo cowpy, que envolve o comando cowpy para gerar arte ASCII, é definido no módulo cowpy.nf.
Arquivo de código completo
O processo cowpy requer duas entradas: o caminho para um arquivo de entrada contendo o texto para colocar no balão de fala (input_file), e um valor para a variável de personagem.
Importante, ele também inclui a linha container 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273', que aponta para o URI do contêiner que usamos anteriormente.
4.2.2. Verifique se o Docker está habilitado na configuração¶
Vamos antecipar ligeiramente a Parte 3 deste curso de treinamento introduzindo o arquivo de configuração nextflow.config, que é uma das principais formas que o Nextflow oferece para configurar a execução de fluxo de trabalho.
Quando um arquivo chamado nextflow.config está presente no diretório atual, o Nextflow o carregará automaticamente e aplicará qualquer configuração que ele contenha.
Para isso, incluímos um arquivo nextflow.config com uma única linha de código que habilita o Docker.
| nextflow.config | |
|---|---|
Esta configuração diz ao Nextflow para usar Docker para qualquer processo que especifique um contêiner compatível.
Dica
É tecnicamente possível habilitar a execução Docker da linha de comando, por execução, usando o parâmetro -with-docker <container> no seu comando.
No entanto, isso só nos permite especificar um contêiner para todo o fluxo de trabalho, enquanto a abordagem que acabamos de mostrar permite especificar um contêiner diferente por processo.
Este último é muito melhor para modularidade, manutenção de código e reprodutibilidade.
4.2.3. Execute o fluxo de trabalho¶
Só para recapitular, isto é o que estamos prestes a executar:
Você acha que vai funcionar?
Vamos executar o fluxo de trabalho com a flag -resume, e especificar que queremos que o personagem seja o peru.
Saída do comando
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 ✔
As três primeiras etapas foram cacheadas já que as executamos antes, mas o processo cowpy é novo então esse realmente é executado.
Você pode encontrar a saída da etapa cowpy no diretório results.
Conteúdo do arquivo
_________
/ HOLà \
| HELLO |
\ BONJOUR /
---------
\ ,+*^^*+___+++_
\ ,*^^^^ )
\ _+* ^**+_
\ +^ _ _++*+_+++_, )
_+^^*+_ ( ,+*^ ^ \+_ )
{ ) ( ,( ,_+--+--, ^) ^\
{ (\@) } f ,( ,+-^ __*_*_ ^^\_ ^\ )
{:;-/ (_+*-+^^^^^+*+*<_ _++_)_ ) ) /
( / ( ( ,___ ^*+_+* ) < < \
U _/ ) *--< ) ^\-----++__) ) ) )
( ) _(^)^^)) ) )\^^^^^))^*+/ / /
( / (_))_^)) ) ) ))^^^^^))^^^)__/ +^^
( ,/ (^))^)) ) ) ))^^^^^^^))^^) _)
*+__+* (_))^) ) ) ))^^^^^^))^^^^^)____*^
\ \_)^)_)) ))^^^^^^^^^^))^^^^)
(_ ^\__^^^^^^^^^^^^))^^^^^^^)
^\___ ^\__^^^^^^))^^^^^^^^)\\
^^^^^\uuu/^^\uuu/^^^^\^\^\^\^\^\^\^\
___) >____) >___ ^\_\_\_\_\_\_\)
^^^//\\_^^//\\_^ ^(\_\_\_\)
^^^ ^^ ^^^ ^
Você vê que o personagem está dizendo todas as saudações, já que ele executou no arquivo de saudações em maiúsculas coletadas.
Mais ao ponto, conseguimos executar isso como parte do nosso pipeline sem ter que fazer uma instalação apropriada do cowpy e todas as suas dependências. E agora podemos compartilhar o pipeline com colaboradores e fazer com que eles o executem em sua infraestrutura sem que precisem instalar nada também, além do Docker ou uma de suas alternativas (como Singularity/Apptainer) conforme mencionado acima.
4.2.4. Inspecione como o Nextflow lançou a tarefa em contêiner¶
Como uma coda final para esta seção, vamos dar uma olhada no subdiretório de trabalho para uma das chamadas de processo cowpy para ter um pouco mais de visão sobre como o Nextflow funciona com contêineres por baixo dos panos.
Verifique a saída do seu comando nextflow run para encontrar o caminho para o subdiretório de trabalho do processo cowpy.
Olhando o que obtivemos para a execução mostrada acima, a linha de log do console para o processo cowpy começa com [7f/caf718].
Isso corresponde ao seguinte caminho de diretório truncado: work/7f/caf718.
Naquele diretório, você encontrará o arquivo .command.run que contém todos os comandos que o Nextflow executou em seu nome durante a execução do pipeline.
Conteúdo do arquivo
#!/bin/bash
### ---
### name: 'cowpy'
### container: 'community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273'
### outputs:
### - 'cowpy-COLLECTED-batch-output.txt'
### ...
set -e
set -u
NXF_DEBUG=${NXF_DEBUG:=0}; [[ $NXF_DEBUG > 1 ]] && set -x
NXF_ENTRY=${1:-nxf_main}
...
nxf_launch() {
docker run -i --cpu-shares 1024 -e "NXF_TASK_WORKDIR" -v /workspaces/training/nextflow-run/work:/workspaces/training/nextflow-run/work -w "$NXF_TASK_WORKDIR" --name $NXF_BOXID community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273 /bin/bash -ue /workspaces/training/nextflow-run/work/7f/caf71890cce1667c094d880f4b6dcc/.command.sh
}
...
Se você procurar por nxf_launch neste arquivo, você deve ver algo assim:
nxf_launch() {
docker run -i --cpu-shares 1024 -e "NXF_TASK_WORKDIR" -v /workspaces/training/nextflow-run/work:/workspaces/training/nextflow-run/work -w "$NXF_TASK_WORKDIR" --name $NXF_BOXID community.wave.seqera.io/library/pip_cowpy:131d6a1b707a8e65 /bin/bash -ue /workspaces/training/nextflow-run/work/7f/caf7189fca6c56ba627b75749edcb3/.command.sh
}
Este comando de lançamento mostra que o Nextflow está usando um comando docker run muito similar para lançar a chamada de processo como fizemos quando o executamos manualmente.
Ele também monta o subdiretório de trabalho correspondente no contêiner, define o diretório de trabalho dentro do contêiner de acordo, e executa nosso script bash modelado no arquivo .command.sh.
Isso confirma que todo o trabalho duro que tivemos que fazer manualmente na seção anterior agora é feito para nós pelo Nextflow!
Conclusão¶
Você entende qual papel os contêineres desempenham no gerenciamento de versões de ferramentas de software e garantindo reprodutibilidade.
Mais geralmente, você tem uma compreensão básica de quais são os componentes principais de pipelines Nextflow do mundo real e como eles estão organizados. Você conhece os fundamentos de como o Nextflow pode processar múltiplas entradas eficientemente, executar fluxos de trabalho compostos de múltiplas etapas conectadas, aproveitar componentes de código modulares e utilizar contêineres para maior reprodutibilidade e portabilidade.
O que vem a seguir?¶
Faça outra pausa! Essa foi uma grande pilha de informações sobre como pipelines Nextflow funcionam.
Na última seção deste treinamento, vamos mergulhar mais profundamente no tópico de configuração. Você aprenderá como configurar a execução do seu pipeline para se adequar à sua infraestrutura, bem como gerenciar a configuração de entradas e parâmetros.
Quiz¶
Por que o Nextflow cria um diretório de tarefa separado para cada chamada de processo?
O que a opção -ansi-log false faz ao executar um fluxo de trabalho?
No código channel.fromPath(params.input).splitCsv().map { line -> line[0] }, o que .map { line -> line[0] } faz?
Por que é importante incluir o valor de entrada nos nomes de arquivos de saída (ex., "${greeting}-output.txt")?
Qual é o propósito da declaração include em um fluxo de trabalho modularizado?
Quando você modulariza um fluxo de trabalho e o executa com -resume, o que acontece?
O que a diretiva container em uma definição de processo especifica?
No arquivo .command.run, o que a função nxf_launch contém?
O que o Nextflow automaticamente lida ao executar um processo em contêiner? (Selecione todos que se aplicam)