레이어드 파일 시스템 (Layered FS / UnionFS) - 도커 이미지 최적화의 핵심

핵심 인사이트 (3줄 요약)

  1. 본질: 레이어드 파일 시스템(Layered File System)은 여러 개의 분리된 디스크 폴더(Layer)를 투명한 셀로판지 겹치듯 위아래로 포개어, 사용자(컨테이너)의 눈에는 단 하나의 거대하고 평탄한 단일 파일 시스템(루트 디렉토리 /)처럼 보이게 묶어주는(Union) 리눅스 커널 스토리지 기술이다.
  2. 가치: 이 마법 덕분에 1GB짜리 똑같은 우분투 OS 이미지를 기반으로 100개의 다른 컨테이너를 띄우더라도 디스크를 100GB 낭비하지 않고 단 1GB만 공용으로 공유(Share)할 수 있으며, 컨테이너 빌드 및 네트워크 다운로드(Pull) 시 변경된 5MB의 조각(Layer)만 잽싸게 전송받는 극한의 속도 혁명을 이뤘다.
  3. 융합: 밑바닥의 꽝꽝 얼어붙은 이미지 레이어들은 읽기 전용(Read-Only)으로 두고, 맨 꼭대기에 얇은 쓰기 가능(Writable) 레이어를 얹은 뒤, 파일 수정 시 원본을 보호하며 복사본을 만들어 수정하는 Copy-on-Write (CoW) 전략과 완벽하게 융합되어 도커(Docker)의 불변 인프라(Immutable) 철학을 물리적으로 실체화했다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: 도커 이미지(Docker Image)를 뜯어보면 통짜 .iso.zip 파일 하나가 아니다. 수십 개의 이상한 영어와 숫자로 된 해시(Hash) 폴더(Layer)들로 쪼개져 있다. Union File System(AuFS, OverlayFS 등)은 이 쪼개진 폴더들을 마치 하나의 마법진으로 묶어버린다. 폴더 A에 file1.txt가 있고, 폴더 B에 file2.txt가 있을 때, 두 폴더를 UnionFS로 마운트하면 내 눈앞에는 file1file2가 한곳에 같이 들어있는 완벽한 가상의 루트 폴더가 뿅 하고 나타난다.

  • 필요성: 가상머신(VM) 시대에는 10개의 서버를 띄우면 10GB짜리 우분투 OS 하드디스크(.vmdk)를 10번 복사해야 했다. 100GB가 날아갔다. 부팅도 느렸다. 클라우드 엔지니어들은 고민했다. "10개 서버 모두 뼈대인 우분투 OS와 자바 라이브러리는 99% 똑같은데, 굳이 중복 복사해야 하나? 뼈대 파일은 디스크에 딱 하나만 깔아두고 다 같이 공유해서 돌려 쓰면 안 되나?" 이 불가능해 보이던 중복 제거(Deduplication)와 자원 공유를 단일 OS 커널에서 실현해 낸 기술이 바로 레이어 아키텍처다.

  • 💡 비유: 햄버거집 주방을 상상해 봅시다.

    • VM 방식 (전체 복사): 불고기버거와 치즈버거 주문이 들어오면, 주방장이 빵, 양상추, 패티, 치즈를 매번 밭에서 새로 가져와서 처음부터 끝까지 따로따로 100번 요리합니다. (공간, 시간 낭비)
    • 레이어드 방식 (UnionFS): 빵, 양상추, 패티는 이미 조리된 상태로 진열장(Read-Only Layer)에 하나씩만 쫙 깔려 있습니다. 불고기버거를 만들 때는 '불고기 소스 레이어'만 슥 얹고, 치즈버거를 만들 때는 '치즈 레이어'만 슥 얹습니다. 밑바닥 공통 재료(OS 뼈대)를 무한 재활용하므로 1초 만에 햄버거(컨테이너)가 완성됩니다.
  • 등장 배경 및 발전 과정:

    1. Live CD와 AuFS: 과거 CD-ROM 부팅 시절, CD는 읽기 전용이므로 설정을 저장할 수 없었다. CD의 파일과 RAM(메모리)를 겹쳐서 저장 가능한 OS처럼 쓰기 위해 UnionFS 기술(AuFS)이 고안되었다.
    2. Docker의 채택 (2013): 도커는 이 AuFS를 컨테이너 이미지 관리에 도입하여, DockerfileRUN 명령어 한 줄마다 디스크의 레이어를 한 층씩 얼리도록(Commit) 아키텍처를 혁신했다.
    3. OverlayFS의 천하통일 (현재): AuFS가 리눅스 공식 커널에 채택되지 못하는 등 잡음이 생기자, 리눅스 커널에 정식 통합되어 구조가 더 직관적이고 성능이 압도적으로 빠른 Overlay2 (OverlayFS) 스토리지 드라이버가 현재 도커와 K8s 세계의 완전한 표준이 되었다.
  • 📢 섹션 요약 비유: 포토샵(Photoshop) 프로그램에서 수십 장의 투명한 레이어(Layer)에 각각 배경, 인물, 글씨를 나눠 그려놓고 눈동자 아이콘을 다 켜면, 모니터 화면에는 완벽하게 한 장의 멋진 포스터(컨테이너)로 합쳐져 보이는 마술과 똑같은 원리입니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

Overlay2 (Layered FS)의 물리적 병합(Union) 구조

Docker가 기본으로 사용하는 Overlay2 스토리지 드라이버가 디스크를 어떻게 겹치는지 살펴보자.

  ┌───────────────────────────────────────────────────────────────┐
  │         OverlayFS의 구조 및 Copy-on-Write (CoW) 메커니즘         │
  ├───────────────────────────────────────────────────────────────┤
  │                                                               │
  │   [ 1. 컨테이너 뷰 (Merged View / 루트 파일 시스템) ]                │
  │     (사용자 눈에는 그냥 평범한 하나의 디스크 폴더로 보임)                │
  │     /app/code.js   (← 내가 짠 소스코드)                           │
  │     /usr/bin/java  (← 뼈대 OS 파일)                              │
  │     /var/log/a.log (← 앱이 방금 생성한 로그 파일)                   │
  │                                                               │
  │               ▲ (Union 마운트 병합!)                              │
  │  =============================================================│
  │                                                               │
  │   [ 2. 컨테이너 영역 (Upper Dir) - 📝 쓰기 가능 (Writable) ]      │
  │     - 얇은 비닐 층. 컨테이너 기동 시 맨 꼭대기에 딱 1장 덮어씌워 짐.     │
  │     - 파일 추가/삭제: /var/log/a.log 생성 시 여기에만 저장됨!         │
  │                                                               │
  │  -------------------------------------------------------------│
  │                                                               │
  │   [ 3. 도커 이미지 영역 (Lower Dir) - ❄️ 읽기 전용 (Read-Only) ]   │
  │     - 도커 허브에서 다운받아 하드디스크에 저장된 꽝꽝 얼어붙은 이미지 레이어들.│
  │     - Layer C: /app/code.js                                   │
  │     - Layer B: /usr/bin/java                                  │
  │     - Layer A: /bin/bash (Ubuntu Base)                        │
  │                                                               │
  │  ▶ [핵심: Copy-on-Write (CoW) 란?]                               │
  │   만약 컨테이너가 뼈대인 '/usr/bin/java' 파일을 삭제하거나 수정하려 한다면?│
  │   Lower Dir(원본)은 읽기 전용이라 절대 건드릴 수 없다!                  │
  │   대신, 원본 파일을 몰래 Upper Dir(맨 위 비닐)로 [복사(Copy)]해 가져온   │
  │   뒤, 그 비닐 위에 있는 복사본을 수정[Write]해 버린다. 즉 원본은 100% 보존!│
  └───────────────────────────────────────────────────────────────┘

[다이어그램 해설] Overlay2는 디스크를 LowerDir(이미지 원본), UpperDir(컨테이너 수정 공간), Merged(합쳐서 보여주는 뷰) 세 가지 구역으로 나눈다. 컨테이너 수천 개가 띄워져도 LowerDir의 우분투 원본 디스크 폴더는 물리적으로 단 하나만 존재하며 모두가 빨대 꽂고 공유해서 읽는다(엄청난 공간 절약). 컨테이너 A가 자기 파일 시스템 안에서 파일을 지우거나 만들면, 커널은 밑바닥을 건드리는 대신 맨 위쪽 얇은 비닐(UpperDir)에 "화이트아웃(Whiteout, 지워짐 표시)" 파일을 그리거나 새 파일을 쓴다. 결국 원본 도면(Image)은 결코 더럽혀지지 않으며(Immutability), 쓰고 버리는 일회용 샌드박스가 완벽히 구현된다. 이것이 도커의 불변 인프라를 떠받치는 Copy-on-Write (CoW) 철학이다.


Ⅲ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — Dockerfile 빌드 속도 10배 향상 (캐시 최적화): 스프링 부트(Spring Boot) 백엔드 개발팀. Dockerfile 맨 위에 소스 코드(COPY src/ /app)를 얹고, 그 아래에 무거운 메이븐 라이브러리 설치(RUN mvn clean install) 명령을 적어 두었다. 소스코드를 단 1줄만 수정하고 빌드 버튼을 누를 때마다, 1GB짜리 라이브러리 다운로드 창이 다시 뜨며 빌드에 무려 10분이 걸려 개발자들이 딴짓을 하는 상황.

    • 판단: 레이어드 파일 시스템의 캐싱(Caching) 붕괴 안티패턴이다. UFS 구조상, 상위 1개 레이어(소스코드)가 조금이라도 바뀌어 해시값이 틀어지면, 도커 엔진은 그 밑에 쌓이는 모든 명령어(라이브러리 설치 레이어)의 기존 디스크 캐시를 휴지통에 버리고 바보처럼 처음부터 다시 다운받아버린다.
    • 해결책: 무겁고 잘 변하지 않는 것(뼈대)을 아래에 깔고, 가볍고 매일 변하는 것(소스)을 맨 위 레이어에 올리는 역피라미드 구조로 Dockerfile을 재배치해야 한다. COPY pom.xml과 라이브러리 다운로드(mvn dependency:go-offline)를 윗줄에 배치하여 영구 캐싱 레이어로 굳혀버리고, 매일 바뀌는 소스코드(COPY src/)를 맨 마지막 꼬리 줄에 둔다. 이렇게 아키텍처를 뒤집으면 소스 1줄 수정 후 빌드 시 앞단 수십 분짜리 캐시(Layer)를 도커가 그대로 통과(Using cache)하여, 빌드가 단 1초 만에 쾌속 종료된다.
  2. 시나리오 — 볼륨 마운트(Volume Mount) 미설정에 따른 DB 데이터 증발: 클라우드 인프라 팀장이 "모든 것을 도커화해라!"라고 지시하여, DBA가 MySQL 데이터베이스를 컨테이너로 띄웠다. 일주일간 100GB의 소중한 고객 결제 데이터가 저장되었다. 서버 패치를 위해 docker rm으로 기존 컨테이너를 부수고 새 버전 컨테이너를 올렸는데, 100GB의 결제 데이터가 1초 만에 흔적도 없이 공중분해 되었다.

    • 판단: Copy-on-Write (CoW)의 치명적 맹점이다. 컨테이너가 살아있는 동안 기록된 100GB 데이터는 몽땅 맨 위층의 **임시 비닐 레이어(Writable Layer)**에 쓰여 있었다. 컨테이너를 부수면 이 얇은 비닐 층도 커널에서 즉시 삭제되며 모든 상태(State)가 운명을 같이 한다.
    • 해결책: 레이어드 파일 시스템(UFS)의 적용 범위를 아키텍트가 강제로 찢어버려야 한다. 운영 데이터베이스나 첨부 파일 로그는 UFS의 덧씌우기 구조를 아예 우회하여 호스트 리눅스 서버의 안전한 하드디스크 폴더에 곧바로 직통 연결되는 도커 볼륨(Docker Volume) 바인드 마운트를 선언(-v /host/data:/var/lib/mysql)해야 한다. 이를 통해 컨테이너가 100번 죽고 부서지더라도 데이터는 불멸의 상태로 호스트 디스크에 살아남는다.

도입 체크리스트

  • 보안/최적화적: 컨테이너 이미지 레이어를 뜯어볼 수 있는 분석 도구(Dive 등)를 통해, 무심코 RUN rm -rf /tmp/data 처럼 지운 파일이 레이어 어딘가에 유령(Whiteout)처럼 남아 오히려 용량만 갉아먹고 공격 표면으로 방치되어 있지 않은가? (같은 RUN 명령어 &&로 묶기(Layer 줄이기) 기법 등을 통해 낭비되는 투명 셀로판지의 개수를 최소화했는가?)

Ⅳ. 기대효과 및 결론

정량/정성 기대효과

구분통짜 디스크 기반 배포 (VM)레이어드 파일 시스템 (Docker/UFS)개선 효과
정량 (스토리지 효율)우분투 OS 1GB × 100대 = 100GB 소모우분투 원본 1GB(공유) + 비닐 레이어 1MB × 100물리 서버의 디스크 I/O 및 용량 낭비 99% 근절
정량 (네트워크 배포)1줄 고치고 배포 시 전체 1GB 다시 다운로드1줄 바뀐 맨 위 레이어 10KB만 통신 전송CI/CD 파이프라인 배포/전송 속도 수십~수백 배 상승
정성 (롤백 속도)스냅샷 복구 시 디스크 I/O 부하 심각docker tag로 이전 레이어 해시만 갈아 끼움장애 발생 시 1초 만의 완벽한 롤백(Rollback) 마술

레이어드 파일 시스템(UnionFS)은 단순한 디스크 포맷 기술이 아니다. 이것은 거대하고 느린 서버 OS 환경을 마치 깃허브(Git) 코드처럼 조각조각 커밋(Commit)하고 합치고(Merge) 빼낼 수 있게 만든 위대한 인프라 혁신이다. 기술사는 아무 생각 없이 Dockerfile을 짜서 수십 개의 쓰레기 층(Layer)을 덕지덕지 쌓아 올리는 개발 문화를 경계하고, CoW(Copy-on-Write) 구조가 부르는 미세한 성능 오버헤드를 이해하여, 어떤 데이터를 레이어에 가두고 어떤 데이터를 볼륨으로 빼낼 것인지 선 긋는 인프라-데이터 분리자(Decoupler)가 되어야 한다.


📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
Copy-on-Write (CoW)레이어드 파일 시스템이 원본(읽기 전용)을 절대 해치지 않고, 누군가 수정을 시도할 때만 몰래 복사본을 만들어 그 위에 쓰는 도커 불변 철학의 물리적 원리다.
Overlay2리눅스 커널에서 여러 폴더를 겹쳐 보이게 하는 최신이자 최고의 스토리지 드라이버. 현재 대부분의 도커와 쿠버네티스가 밑바닥에서 돌리고 있는 엔진이다.
Dockerfile 캐싱 (Caching)도커가 빌드를 순식간에 끝낼 수 있도록, 변경되지 않은 옛날 명령어 줄(Layer)의 하드디스크 덩어리를 재활용하게 해주는 기능이다.
도커 볼륨 (Docker Volume)영원히 살아야 하는 DB 데이터마저 컨테이너 임시 비닐 층(Writable Layer)에 저장했다가 날려 먹는 재앙을 막기 위해, UFS의 마법을 뚫고 안전한 호스트 디스크로 우회시키는 터널이다.
불변 인프라 (Immutable Infrastructure)UFS의 'Lower Dir(읽기 전용)' 구조가 강제로 실체화시킨 철학으로, "서버의 뼈대는 절대 접속해서 고치지 말고 아예 새 레이어로 덮어씌워라"라는 최신 배포 패러다임이다.

👶 어린이를 위한 3줄 비유 설명

  1. 햄버거집에서 불고기버거 100개를 만들 때, 빵 굽고 양상추 씻는 걸 100번씩 따로 하면 너무 힘들잖아요?
  2. 그래서 주방장(리눅스 커널)은 빵과 양상추(베이스 이미지)를 딱 1번만 크게 준비해 두고 여러 개로 복사하는 게 아니라, 손님 눈에만 100개처럼 겹쳐서 보이게 홀로그램(레이어드 파일 시스템) 마술을 부렸어요!
  3. 나중에 소스를 바꿀 때도 처음부터 다시 만들지 않고 맨 위에 얇은 치즈(비닐 레이어) 한 장만 살짝 얹어서 덮어버리니까, 요리도 1초 만에 끝나고 주방 공간도 안 차지하는 엄청난 기술이랍니다!