0Pricing
DevOps Bootcamp · Lección

Pipelines de streaming y tuberías con nombre para mejorar el rendimiento

Use FIFOs y sustitución de procesos para transmitir datos entre etapas sin archivos intermedios.

Pipelines de streaming y tuberías con nombre para mejorar el rendimiento es una lección gratuita de DevOps Bootcamp en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de DevOps Bootcamp, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de DevOps Bootcamp incluye 4 lecciones en total.

Por qué los archivos intermedios reducen el rendimiento

Cuando encadena comandos como sort file.txt > tmp.txt && uniq tmp.txt > result.txt, paga un coste oculto: escrituras y lecturas de disco, además de que la canalización se detiene hasta que la primera etapa termina por completo antes de que comience la siguiente.

Las canalizaciones de transmisión eliminan ese coste. Los datos pasan directamente del productor al consumidor en memoria, etapa por etapa y de forma simultánea. Esta es la idea fundamental de las tuberías de Unix, y las tuberías con nombre (FIFO) la amplían aún más.

  • Tubería anónima (|): conecta dos comandos adyacentes en la misma línea de shell.
  • Tubería con nombre (FIFO): archivo especial del sistema de archivos que permite que procesos no relacionados transmitan datos entre sí.
  • Sustitución de procesos: permite que un comando trate la salida de otro comando como si fuera un archivo.

En esta lección aprenderá a aplicar las tres técnicas para maximizar el rendimiento en flujos de trabajo reales de Bash.

Anatomía de una canalización de transmisión

Una tubería anónima conecta la salida estándar de un proceso con la entrada estándar del siguiente. El kernel mantiene ambos procesos en ejecución simultáneamente mediante un búfer en memoria de tamaño fijo (normalmente de 64 KB en Linux).

La idea clave es que la canalización es tan rápida como su etapa más lenta. Si el productor es más rápido, se bloquea cuando el búfer está lleno. Si el consumidor es más rápido, se bloquea cuando el búfer está vacío. Esta contrapresión es un control de flujo gratuito y automático.

El ejemplo siguiente cuenta las direcciones IP únicas de un registro de acceso grande sin escribir nunca un archivo temporal. Cada etapa se ejecuta de forma simultánea:

#!/usr/bin/env bash
# Stream a 2 GB access log — all stages run in parallel
grep '"GET' /var/log/nginx/access.log \
  | awk '{print $1}' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

Crear tuberías con nombre con mkfifo

Una tubería con nombre (FIFO, «primero en entrar, primero en salir») se crea con mkfifo. Aparece en el sistema de archivos como un archivo normal, pero los datos escritos en ella nunca se almacenan en el disco: fluyen directamente al proceso lector.

Comportamientos importantes que debe recordar:

  • Una escritura en una FIFO se bloquea hasta que un lector la abre, y viceversa.
  • La entrada de la FIFO permanece en el sistema de archivos; debe eliminarla con rm cuando termine.
  • Se permiten varios escritores, pero no se garantiza el orden entre ellos.

A continuación, un productor comprime datos en una FIFO mientras un consumidor los carga simultáneamente en S3, sin necesidad de un archivo temporal.

#!/usr/bin/env bash
mkfifo /tmp/stream_pipe

# Producer: compress in background
gzip -c /var/log/syslog > /tmp/stream_pipe &

# Consumer: read from FIFO (runs in foreground)
wc -l < /tmp/stream_pipe

wait
rm /tmp/stream_pipe

Sustitución de procesos: tratar un comando como un archivo

La sustitución de procesos utiliza la sintaxis <(command) o >(command). Bash crea internamente una FIFO (o un descriptor de archivo /dev/fd/N) y pasa la ruta al comando externo.

Esto resulta muy útil cuando una herramienta espera un argumento de nombre de archivo en lugar de stdin. Sin la sustitución de procesos necesitaría un archivo temporal; con ella, transmite los datos directamente.

  • <(cmd) — el comando externo lee la salida de cmd.
  • >(cmd) — el comando externo escribe en la entrada de cmd.
#!/usr/bin/env bash
# diff two sorted streams without creating temp files
diff <(sort /etc/passwd) <(sort /etc/group)

# Compare live command output against a baseline
diff <(ls /usr/bin | sort) <(cat ~/bin_baseline.txt | sort)

Tee: dividir un flujo entre varios consumidores

tee lee stdin y escribe los datos tanto en stdout como en uno o más archivos. Combinado con la sustitución de procesos, permite distribuir un único flujo entre varias canalizaciones de procesamiento simultáneamente, sin tocar el disco.

Este patrón resulta útil cuando, por ejemplo, desea registrar los datos sin procesar y procesarlos al mismo tiempo.

#!/usr/bin/env bash
# Generate 100000 random numbers, then simultaneously:
#  1. compute the sum
#  2. find the maximum
#  3. count lines (saved to a variable)
seq 1 100000 \
  | tee >(awk '{s+=$1} END{print "Sum:", s}') \
        >(awk 'BEGIN{m=0} $1>m{m=$1} END{print "Max:", m}') \
  | wc -l | xargs echo "Count:"

Patrón de distribución: un productor y muchos consumidores

Cuando una única fuente de datos debe alimentar a varios consumidores independientes, combine tee con varias sustituciones de procesos >(). Cada consumidor recibe el flujo completo y se ejecuta simultáneamente.

Esto evita leer varias veces el archivo de origen. En un archivo de 10 GB, la diferencia es enorme: una lectura de disco en lugar de N lecturas.

#!/usr/bin/env bash
# Read a large CSV once; simultaneously:
#  - count rows
#  - extract column 2 to a file
#  - pass column 3 to a stats script
cat large_data.csv \
  | tee \
      >(wc -l > /tmp/row_count.txt) \
      >(cut -d',' -f2 > /tmp/col2.txt) \
      >(cut -d',' -f3 | awk '{sum+=$1} END{print sum}' > /tmp/col3_sum.txt) \
  > /dev/null

echo "Rows:" $(cat /tmp/row_count.txt)
echo "Col3 sum:" $(cat /tmp/col3_sum.txt)

Patrón de concentración: muchos productores y un consumidor

El inverso de la distribución es la concentración: varias fuentes independientes transmiten datos a un único consumidor. Las FIFO con nombre hacen que esto sea sencillo.

Un caso de uso habitual es combinar en tiempo real los flujos de registros de varios servidores o agregar resultados parciales de trabajadores paralelos.

Tenga en cuenta que, con varios escritores, el consumidor recibe una salida intercalada. Esto está bien para datos orientados a líneas, donde cada línea es independiente, pero deberá gestionar el orden por su cuenta si la secuencia es importante.

#!/usr/bin/env bash
mkfifo /tmp/fanin_pipe

# Three producers write concurrently into the same FIFO
for host in web1 web2 web3; do
  ssh "$host" 'tail -n 500 /var/log/app.log' > /tmp/fanin_pipe &
done

# Single consumer reads all merged output
grep 'ERROR' /tmp/fanin_pipe | sort | uniq -c | sort -rn

wait
rm /tmp/fanin_pipe

Usar mkfifo para la compresión paralela

Uno de los usos más prácticos de las FIFO es la compresión paralela. Herramientas como pigz (gzip paralelo) o pbzip2 leen un flujo; puede canalizar los datos sin comprimir directamente, sin tener que preparar un archivo sin comprimir.

El patrón siguiente archiva un directorio, lo comprime usando todos los núcleos de la CPU y transmite el resultado a un host remoto, todo de forma simultánea:

#!/usr/bin/env bash
# Tar + parallel compress + stream to remote — no temp files
# Requires: pigz (parallel gzip)
tar cf - /data/large_dir \
  | pigz -p 4 \
  | ssh backup-host 'cat > /backups/large_dir.tar.gz'

# Verify the remote file exists
ssh backup-host 'ls -lh /backups/large_dir.tar.gz'

Controlar el tamaño del búfer y los bloqueos

Las tuberías tienen un búfer en el kernel (normalmente de 64 KB). Cuando el búfer está lleno, el escritor se bloquea; cuando está vacío, se bloquea el lector. Normalmente esto es lo que se desea, pero en algunos casos el bloqueo provoca un interbloqueo.

Riesgo de interbloqueo: si el proceso A escribe en FIFO1 y lee de FIFO2, mientras el proceso B escribe en FIFO2 y lee de FIFO1, ambos pueden bloquearse esperando que el otro consuma primero.

Soluciones:

  • Ejecute al menos uno de los lados en segundo plano (&) para que no bloquee el shell.
  • Use mbuffer o pv para añadir un búfer en memoria más grande entre las etapas.
  • Use pv -q -B 128m para insertar un búfer de 128 MB que suavice los picos de rendimiento.
#!/usr/bin/env bash
# pv adds a 64 MB buffer and shows throughput
# Useful when producer and consumer have bursty speeds
dd if=/dev/urandom bs=1M count=200 \
  | pv -B 64m \
  | gzip \
  | wc -c

Ejemplo práctico: agregador de registros en tiempo real

Este es un patrón completo y realista: seguir varios archivos de registro, combinar los flujos mediante una tubería con nombre, filtrar los errores y escribir un resumen en tiempo real, todo sin archivos intermedios y con todas las etapas ejecutándose en paralelo.

Este tipo de canalización se ejecutaría como un script de supervisión en segundo plano en un servidor de producción.

#!/usr/bin/env bash
FIFO=/tmp/log_aggregator
mkfifo "$FIFO"

cleanup() { rm -f "$FIFO"; }
trap cleanup EXIT INT TERM

# Fan-in: tail multiple logs into the FIFO
tail -F /var/log/syslog /var/log/auth.log > "$FIFO" &
TAIL_PID=$!

# Consumer: filter and timestamp errors in real time
grep --line-buffered -i 'error\|fail\|crit' "$FIFO" \
  | while IFS= read -r line; do
      printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$line"
    done

kill "$TAIL_PID" 2>/dev/null

Comparar el rendimiento de las canalizaciones con el de los archivos temporales

Puede medir con time la diferencia real de rendimiento entre el enfoque de transmisión y el de archivos temporales. El enfoque de canalización es mejor con conjuntos de datos grandes porque:

  • Las etapas se ejecutan simultáneamente: la CPU y la E/S se solapan.
  • No hay E/S de disco para los datos intermedios; solo la salida final se escribe en el disco.
  • El consumo de memoria permanece constante independientemente del tamaño de la entrada (se transmite, no se almacena en un búfer).

Una prueba sencilla para comparar ambos enfoques:

#!/usr/bin/env bash
# Approach 1: Temp file (sequential)
time bash -c '
  seq 1 5000000 > /tmp/nums.txt
  sort -n /tmp/nums.txt > /tmp/sorted.txt
  uniq /tmp/sorted.txt | wc -l
  rm /tmp/nums.txt /tmp/sorted.txt
'

echo '---'

# Approach 2: Streaming pipeline (concurrent)
time bash -c 'seq 1 5000000 | sort -n | uniq | wc -l'

Comprobación de conocimientos: comportamiento de bloqueo de las tuberías con nombre

Considere el siguiente script:

mkfifo /tmp/mypipe
echo 'hello' > /tmp/mypipe
echo 'done'

¿Qué sucede cuando se ejecuta este script sin procesos en segundo plano ni lectores?

Repaso: canalizaciones de streaming y tuberías con nombre

En esta lección ha explorado cómo mover datos de forma eficiente entre procesos sin archivos intermedios:

  • Tuberías anónimas (|) conectan comandos adyacentes y ejecutan todas las etapas simultáneamente con control automático de la presión de retorno.
  • Tuberías con nombre (mkfifo) crean una entrada FIFO en el sistema de archivos que permite que procesos no relacionados o en segundo plano intercambien datos en streaming; las escrituras se bloquean hasta que haya un lector.
  • Sustitución de procesos (<(cmd), >(cmd)) permite que los comandos que esperan nombres de archivo consuman o produzcan streams de forma transparente.
  • tee + >() distribuye un stream entre varios consumidores simultáneos sin volver a leer el origen.
  • Fan-in combina varios productores en un solo consumidor mediante una FIFO compartida.
  • La presión de retorno y el bloqueo son características, no errores; pero ejecute siempre al menos un extremo de un par FIFO en segundo plano para evitar un interbloqueo.
  • Use pv o mbuffer para añadir búferes más grandes y supervisar el rendimiento cuando las etapas generen ráfagas.

Estas técnicas son la base de la ingeniería de datos de alto rendimiento con Bash: permiten procesar datos a escala de GB con memoria constante y el máximo paralelismo de CPU/E/S.

Preguntas frecuentes

¿La lección «Pipelines de streaming y tuberías con nombre para mejorar el rendimiento» es gratis?

Sí — el texto completo de «Pipelines de streaming y tuberías con nombre para mejorar el rendimiento» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de DevOps Bootcamp, actualiza a CoddyKit PRO. El curso de DevOps Bootcamp incluye 4 lecciones en total.

¿Qué aprenderé en «Pipelines de streaming y tuberías con nombre para mejorar el rendimiento»?

Use FIFOs y sustitución de procesos para transmitir datos entre etapas sin archivos intermedios. Practicas DevOps Bootcamp con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar DevOps Bootcamp?

No se requiere experiencia previa. DevOps Bootcamp en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Pipelines de streaming y tuberías con nombre para mejorar el rendimiento»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de DevOps Bootcamp?

Sí. Cada lección de DevOps Bootcamp incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Perfilado de scripts y cómo evitar subshells innecesarios
  2. Paralelismo con xargs -P y trabajos en segundo plano
  3. Orquestación de cargas de trabajo con GNU parallel
  4. Pipelines de streaming y tuberías con nombre para mejorar el rendimiento
← Volver a DevOps Bootcamp