처리량을 높이는 스트리밍 파이프라인과 명명된 파이프
FIFO와 프로세스 치환을 사용하여 중간 파일 없이 단계 간에 데이터를 스트리밍합니다.
처리량을 높이는 스트리밍 파이프라인과 명명된 파이프은(는) CoddyKit의 무료 DevOps Bootcamp 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DevOps Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
중간 파일이 처리량을 저하시키는 이유
sort file.txt > tmp.txt && uniq tmp.txt > result.txt와 같이 명령을 연결하면 숨은 비용이 발생합니다. 디스크에 쓰고 다시 읽어야 하며, 첫 단계가 완전히 끝날 때까지 다음 단계가 시작되지 않아 파이프라인이 멈춥니다.
스트리밍 파이프라인은 이 비용을 없앱니다. 데이터가 메모리에서 생산자에서 소비자로 단계별로 직접 흐르며, 각 단계가 동시에 실행됩니다. 이것이 유닉스 파이프의 핵심 개념이며, 명명된 파이프(FIFO)는 이를 한층 확장합니다.
- 익명 파이프(
|): 같은 셸 줄에서 서로 인접한 두 명령을 연결합니다. - 명명된 파이프(FIFO): 관련 없는 프로세스가 서로 스트리밍할 수 있게 하는 파일 시스템의 특수 파일입니다.
- 프로세스 치환: 한 명령이 다른 명령의 출력을 파일인 것처럼 다루게 합니다.
이 레슨에서는 실제 Bash 작업 흐름에서 처리량을 극대화하기 위해 이 세 가지를 모두 적용하는 방법을 보여 드립니다.
스트리밍 파이프라인의 구조
익명 파이프는 한 프로세스의 표준 출력을 다음 프로세스의 표준 입력에 연결합니다. 커널은 일반적으로 Linux에서 64 KB인 고정 크기의 메모리 버퍼를 사용하여 두 프로세스를 동시에 실행합니다.
핵심은 파이프라인의 속도는 가장 느린 단계에 의해 결정된다는 점입니다. 생산자가 더 빠르면 버퍼가 가득 차서 대기하고, 소비자가 더 빠르면 버퍼가 빌 때까지 대기합니다. 이러한 역압은 별도의 비용 없이 자동으로 이루어지는 흐름 제어입니다.
아래 예에서는 임시 파일을 전혀 작성하지 않고 대규모 접근 로그의 고유 IP 주소를 셉니다. 각 단계는 동시에 실행됩니다.
#!/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 -20mkfifo로 명명된 파이프 만들기
명명된 파이프(FIFO — 선입선출)는 mkfifo로 만듭니다. 일반 파일처럼 파일 시스템에 나타나지만, 여기에 기록된 데이터는 디스크에 저장되지 않고 읽기 프로세스로 직접 흐릅니다.
다음과 같은 주요 동작을 기억해 두세요.
- FIFO에 쓰는 작업은 읽기 프로세스가 FIFO를 열 때까지 차단됩니다. 반대의 경우도 마찬가지입니다.
- FIFO 항목은 파일 시스템에 남아 있으므로, 사용이 끝나면
rm으로 삭제해야 합니다. - 여러 작성자를 사용할 수 있지만 작성자 간 순서는 보장되지 않습니다.
아래에서는 생산자가 FIFO로 데이터를 압축하는 동시에 소비자가 이를 S3에 업로드합니다. 임시 파일은 필요하지 않습니다.
#!/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프로세스 치환: 명령을 파일처럼 다루기
프로세스 치환은 <(command) 또는 >(command) 구문을 사용합니다. Bash는 내부적으로 FIFO(또는 /dev/fd/N 파일 디스크립터)를 만들고 그 경로를 바깥 명령에 전달합니다.
이 기능은 도구가 표준 입력이 아니라 파일 이름 인수를 요구할 때 유용합니다. 프로세스 치환이 없다면 임시 파일이 필요하지만, 프로세스 치환을 사용하면 데이터를 직접 스트리밍할 수 있습니다.
<(cmd)— 바깥 명령이 cmd의 출력에서 읽습니다.>(cmd)— 바깥 명령이 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: 스트림을 여러 소비자로 분할하기
tee는 표준 입력을 읽어 표준 출력과 하나 이상의 파일 모두에 씁니다. 프로세스 치환과 함께 사용하면 디스크를 전혀 사용하지 않고 하나의 스트림을 여러 처리 파이프라인으로 동시에 분기할 수 있습니다.
예를 들어 원시 데이터를 로그로 기록하면서 동시에 처리하고 싶을 때 유용한 패턴입니다.
#!/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:"분기 패턴: 하나의 생산자와 여러 소비자
하나의 데이터 원본을 여러 독립적인 소비자에게 전달해야 한다면 tee와 여러 >() 프로세스 치환을 함께 사용하세요. 각 소비자는 전체 스트림을 받아 동시에 실행됩니다.
이 방법을 사용하면 원본 파일을 여러 번 읽지 않아도 됩니다. 10 GB 파일이라면 그 차이가 매우 큽니다. 디스크를 N번 읽는 대신 한 번만 읽기 때문입니다.
#!/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)집계 패턴: 여러 생산자와 하나의 소비자
분기의 반대는 집계입니다. 여러 독립적인 원본이 하나의 소비자로 스트리밍됩니다. 명명된 FIFO를 사용하면 이를 간단하게 구현할 수 있습니다.
일반적인 사용 사례로는 여러 서버의 로그 스트림을 실시간으로 병합하거나, 병렬 작업자의 부분 결과를 집계하는 경우가 있습니다.
여러 작성자를 사용하면 소비자에게 출력이 뒤섞여 전달된다는 점에 유의하세요. 각 줄이 독립적인 줄 단위 데이터라면 문제가 없지만, 순서가 중요하다면 직접 순서를 처리해야 합니다.
#!/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_pipemkfifo를 사용한 병렬 압축
FIFO의 가장 실용적인 용도 중 하나는 병렬 압축입니다. pigz(병렬 gzip)나 pbzip2와 같은 도구는 스트림을 읽으므로, 압축되지 않은 파일을 중간에 만들지 않고 원시 데이터를 직접 파이프로 전달할 수 있습니다.
아래 패턴은 디렉터리를 보관 파일로 만들고, 모든 CPU 코어를 사용해 압축한 다음, 결과를 원격 호스트로 동시에 스트리밍합니다.
#!/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'버퍼 크기와 차단 제어하기
파이프에는 커널 버퍼가 있으며 일반적으로 크기는 64 KB입니다. 버퍼가 가득 차면 작성자가 차단되고, 비어 있으면 읽기 작업이 차단됩니다. 대부분의 경우 원하는 동작이지만, 경우에 따라 차단으로 인해 교착 상태가 발생할 수 있습니다.
교착 상태 위험: 프로세스 A가 FIFO1에 쓰고 FIFO2에서 읽는 동안 프로세스 B가 FIFO2에 쓰고 FIFO1에서 읽으면, 서로 상대방이 먼저 소비하기를 기다리며 둘 다 차단될 수 있습니다.
해결 방법:
- 한쪽 이상을 백그라운드(
&)에서 실행하여 셸을 차단하지 않게 합니다. mbuffer또는pv를 사용하여 단계 사이에 더 큰 메모리 버퍼를 추가합니다.pv -q -B 128m을 사용하여 128 MB 버퍼를 삽입하고 처리량의 급격한 변동을 완화합니다.
#!/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실전 예제: 실시간 로그 집계기
다음은 완전하고 현실적인 패턴입니다. 여러 로그 파일을 추적하고, 명명된 파이프를 통해 스트림을 병합하고, 오류를 필터링한 다음, 실시간 요약을 작성합니다. 이 모든 작업이 중간 파일 없이 병렬로 실행됩니다.
이러한 파이프라인은 운영 서버에서 백그라운드 모니터링 스크립트로 실행할 수 있습니다.
#!/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파이프라인과 임시 파일의 성능 비교
time을 사용하면 스트리밍 방식과 임시 파일 방식의 실제 처리량 차이를 측정할 수 있습니다. 대규모 데이터 집합에서는 다음과 같은 이유로 파이프라인 방식이 더 빠릅니다.
- 단계가 동시에 실행되어 CPU와 입출력이 겹칩니다.
- 중간 데이터에 디스크 입출력이 필요하지 않고 최종 출력만 디스크에 기록됩니다.
- 입력 크기와 관계없이 메모리 사용량이 일정하게 유지됩니다(버퍼가 아니라 스트림을 사용).
두 방식을 비교하는 간단한 성능 측정 예제입니다.
#!/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'지식 확인: 명명된 파이프의 차단 동작
다음 스크립트를 살펴보세요.
mkfifo /tmp/mypipe
echo 'hello' > /tmp/mypipe
echo 'done'백그라운드 프로세스나 읽기 프로세스가 전혀 없는 상태에서 이 스크립트를 실행하면 어떻게 될까요?
복습: 스트리밍 파이프라인과 명명된 파이프
이 레슨에서는 중간 파일 없이 프로세스 간에 데이터를 효율적으로 이동하는 방법을 살펴보았습니다.
- 익명 파이프 (
|)는 인접한 명령을 연결하고, 자동 배압 기능을 사용해 모든 단계를 동시에 실행합니다. - 명명된 파이프 (
mkfifo)는 관련 없는 프로세스나 백그라운드 프로세스가 서로 스트리밍할 수 있도록 FIFO 파일 시스템 항목을 만듭니다. 읽기 프로세스가 나타날 때까지 쓰기 작업은 차단됩니다. - 프로세스 치환 (
<(cmd),>(cmd))을 사용하면 파일 이름을 기대하는 명령이 스트림을 투명하게 읽거나 생성할 수 있습니다. tee+>()는 소스를 다시 읽지 않고 하나의 스트림을 여러 동시 소비자에게 분기합니다.- 팬인은 공유 FIFO를 통해 여러 생산자의 출력을 하나의 소비자에게 병합합니다.
- 배압과 차단은 버그가 아니라 기능입니다. 하지만 교착 상태를 방지하려면 FIFO 쌍의 적어도 한쪽은 항상 백그라운드에서 실행해야 합니다.
- 단계별 처리량이 일시적으로 급증할 때는
pv또는mbuffer를 사용해 더 큰 버퍼를 추가하고 처리량을 모니터링할 수 있습니다.
이러한 기법은 고처리량 Bash 데이터 엔지니어링의 기반입니다. 일정한 메모리 사용량과 최대 CPU/IO 병렬 처리로 GB 규모의 데이터를 처리할 수 있습니다.
자주 묻는 질문
“처리량을 높이는 스트리밍 파이프라인과 명명된 파이프” 강의는 무료인가요?
네 — “처리량을 높이는 스트리밍 파이프라인과 명명된 파이프” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DevOps Bootcamp 강의 전체를 잠금 해제할 수 있습니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.
“처리량을 높이는 스트리밍 파이프라인과 명명된 파이프”에서 뭘 배우나요?
FIFO와 프로세스 치환을 사용하여 중간 파일 없이 단계 간에 데이터를 스트리밍합니다. 브라우저에서 직접 실행하는 실습 코드로 DevOps Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
DevOps Bootcamp을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 DevOps Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“처리량을 높이는 스트리밍 파이프라인과 명명된 파이프” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 DevOps Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 DevOps Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 스크립트 프로파일링 및 불필요한 서브셸 방지
- xargs -P와 백그라운드 작업을 활용한 병렬 처리
- GNU parallel을 활용한 작업 오케스트레이션
- 처리량을 높이는 스트리밍 파이프라인과 명명된 파이프