メインコンテンツまでスキップ

OverlayFS

コンテナイメージのレイヤー構造を実現するOverlayFSについて学びます。


目次

  1. OverlayFSとは
  2. レイヤーの仕組み
  3. Copy-on-Write
  4. OverlayFSの操作
  5. Dockerとレイヤー
  6. ストレージドライバ

OverlayFSとは

OverlayFSの概念

┌─────────────────────────────────────────────────────────────┐
│ OverlayFS の構造 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ユーザーから見える統合ビュー (merged) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /merged │ │
│ │ ├── file1.txt (from lower) │ │
│ │ ├── file2.txt (from lower, modified in upper) │ │
│ │ ├── file3.txt (new in upper) │ │
│ │ └── dir/ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ 重ね合わせ │
│ ┌──────┴──────┐ │
│ │ │ │
│ ▼ ▼ │
│ Upper (RW) Lower (RO) │
│ ┌───────────┐ ┌───────────┐ │
│ │ file2.txt │ │ file1.txt │ │
│ │ file3.txt │ │ file2.txt │ │
│ │ .wh.file4 │ │ file4.txt │ ← whiteout で削除扱い │
│ └───────────┘ │ dir/ │ │
│ └───────────┘ │
│ │
│ Upper: 読み書き可能(変更がここに書き込まれる) │
│ Lower: 読み取り専用(元のデータ、複数レイヤー可) │
│ │
└─────────────────────────────────────────────────────────────┘

なぜOverlayFSが重要か

利点説明
効率的なストレージベースイメージを共有できる
高速な起動イメージ全体をコピー不要
レイヤーキャッシュ変更がないレイヤーは再利用
イミュータブル下位レイヤーは変更されない

レイヤーの仕組み

コンテナイメージのレイヤー

┌─────────────────────────────────────────────────────────────┐
│ Docker イメージのレイヤー │
├─────────────────────────────────────────────────────────────┤
│ │
│ Dockerfile: │
│ FROM ubuntu:22.04 → Layer 1 (Base) │
│ RUN apt-get update → Layer 2 │
│ RUN apt-get install -y nginx → Layer 3 │
│ COPY app/ /var/www/html/ → Layer 4 │
│ │
│ イメージ構造: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 4: /var/www/html/ (app files) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Layer 3: /usr/sbin/nginx, /etc/nginx/... │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Layer 2: /var/lib/apt/... (updates) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Layer 1: Ubuntu base (/, /bin, /lib, ...) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 各レイヤーは独立したtarアーカイブ │
│ 同じベースを使うイメージはLayer 1を共有 │
│ │
└─────────────────────────────────────────────────────────────┘

レイヤーの共有

┌─────────────────────────────────────────────────────────────┐
│ レイヤーの共有と効率化 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Image A Image B Image C │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │Layer 4│ │Layer 4│ │Layer 3│ │
│ │(App A)│ │(App B)│ │(App C)│ │
│ └───┬───┘ └───┬───┘ └───┬───┘ │
│ │ │ │ │
│ └───────┬───────┘ │ │
│ │ │ │
│ ┌────┴────┐ │ │
│ │ Layer 3 │ │ │
│ │ (nginx) │◄─────────────────┘ │
│ └────┬────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ Layer 2 │ │
│ │(apt upd)│ │
│ └────┬────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ Layer 1 │ │
│ │(ubuntu) │ ← すべてのイメージで共有 │
│ └─────────┘ │
│ │
│ ディスク使用量 = Layer1 + Layer2 + Layer3 + LayerA + B + C │
│ (共有レイヤーは1回分のみ) │
│ │
└─────────────────────────────────────────────────────────────┘

Copy-on-Write

CoWの仕組み

┌─────────────────────────────────────────────────────────────┐
│ Copy-on-Write (CoW) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 読み取り(下位レイヤーから) │
│ ┌─────────┐ │
│ │ Upper │ ← ファイルなし │
│ ├─────────┤ │
│ │ Lower │ ← file.txt を読む │
│ └─────────┘ │
│ │
│ 2. 書き込み(コピーしてから修正) │
│ ┌─────────┐ │
│ │ Upper │ ← file.txt をコピー&修正 │
│ ├─────────┤ │
│ │ Lower │ ← 元の file.txt は変更されない │
│ └─────────┘ │
│ │
│ 3. 削除(whiteoutファイル作成) │
│ ┌─────────┐ │
│ │ Upper │ ← .wh.file.txt を作成 │
│ ├─────────┤ (削除マーカー) │
│ │ Lower │ ← file.txt は残っている │
│ └─────────┘ │
│ │
│ Merged ビューでは file.txt は見えない │
│ │
└─────────────────────────────────────────────────────────────┘

CoWの利点と注意点

項目内容
利点下位レイヤーが変更されない(イミュータブル)
利点起動が高速(コピー不要)
注意点大きなファイルの編集はコピーが発生
注意点頻繁な書き込みはUpperに蓄積

OverlayFSの操作

基本的なマウント

# ディレクトリ準備
mkdir -p /tmp/overlay/{lower,upper,work,merged}

# lowerに初期ファイル作成
echo "original content" > /tmp/overlay/lower/file.txt
echo "another file" > /tmp/overlay/lower/another.txt

# OverlayFSマウント
sudo mount -t overlay overlay \
-o lowerdir=/tmp/overlay/lower,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work \
/tmp/overlay/merged

# 統合ビュー確認
ls /tmp/overlay/merged
# file.txt another.txt

cat /tmp/overlay/merged/file.txt
# original content

書き込み操作

# ファイル修正(CoW発生)
echo "modified content" > /tmp/overlay/merged/file.txt

# upperにコピーされている
ls /tmp/overlay/upper
# file.txt

cat /tmp/overlay/upper/file.txt
# modified content

# lowerは変更されていない
cat /tmp/overlay/lower/file.txt
# original content

# 新規ファイル作成
echo "new file" > /tmp/overlay/merged/new.txt

# upperに作成される
ls /tmp/overlay/upper
# file.txt new.txt

削除操作(whiteout)

# ファイル削除
rm /tmp/overlay/merged/another.txt

# upperにwhiteoutファイル
ls -la /tmp/overlay/upper
# c--------- .wh.another.txt

# mergedでは見えない
ls /tmp/overlay/merged
# file.txt new.txt

# lowerには残っている
ls /tmp/overlay/lower
# another.txt file.txt

複数レイヤー

# 複数のlowerレイヤー
mkdir -p /tmp/overlay/{lower1,lower2,lower3}

echo "base" > /tmp/overlay/lower1/file.txt
echo "override" > /tmp/overlay/lower2/file.txt
touch /tmp/overlay/lower3/new.txt

# マウント(右が下位)
sudo mount -t overlay overlay \
-o lowerdir=/tmp/overlay/lower3:/tmp/overlay/lower2:/tmp/overlay/lower1,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work \
/tmp/overlay/merged

# lower2のfileが見える(上位が優先)
cat /tmp/overlay/merged/file.txt
# override

クリーンアップ

# アンマウント
sudo umount /tmp/overlay/merged

# ディレクトリ削除
rm -rf /tmp/overlay

Dockerとレイヤー

イメージレイヤーの確認

# イメージのレイヤー情報
docker history nginx

# 出力例
# IMAGE CREATED CREATED BY SIZE
# 605c77e624dd 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
# <missing> 2 weeks ago STOPSIGNAL SIGQUIT 0B
# <missing> 2 weeks ago EXPOSE 80 0B
# <missing> 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
# <missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB
# <missing> 2 weeks ago COPY 20-envsubst-on-templates.sh /docker-ent… 3.02kB
# <missing> 2 weeks ago COPY 15-local-resolvers.envsh /docker-entryp… 298B
# <missing> 2 weeks ago COPY 10-listen-on-ipv6-by-default.sh /docker… 2.12kB
# ...

# 詳細情報
docker inspect nginx | jq '.[0].RootFS'

コンテナレイヤー

# コンテナ起動
docker run -d --name test nginx

# コンテナのマウント情報
docker inspect test | jq '.[0].GraphDriver'

# 出力例(overlay2)
# {
# "Data": {
# "LowerDir": "/var/lib/docker/overlay2/xxx/diff:/var/lib/docker/overlay2/yyy/diff:...",
# "MergedDir": "/var/lib/docker/overlay2/zzz/merged",
# "UpperDir": "/var/lib/docker/overlay2/zzz/diff",
# "WorkDir": "/var/lib/docker/overlay2/zzz/work"
# },
# "Name": "overlay2"
# }

# コンテナ内でファイル変更
docker exec test touch /tmp/newfile
docker exec test rm /etc/nginx/nginx.conf

# UpperDirに変更が記録される
sudo ls /var/lib/docker/overlay2/zzz/diff/tmp
# newfile

sudo ls -la /var/lib/docker/overlay2/zzz/diff/etc/nginx/
# c--------- .wh.nginx.conf ← whiteout

# クリーンアップ
docker rm -f test

レイヤーのディスク使用量

# イメージのサイズ
docker images

# 詳細なディスク使用量
docker system df
docker system df -v

# レイヤーの場所
sudo du -sh /var/lib/docker/overlay2/*

ストレージドライバ

主なストレージドライバ

ドライバ説明推奨環境
overlay2OverlayFS使用、推奨Linux 4.0+
devicemapperブロックレベルRHEL/CentOS(古い)
btrfsBtrfsスナップショットBtrfsファイルシステム
zfsZFSクローンZFSファイルシステム
vfsコピー(非効率)テスト用

ストレージドライバの確認

# 現在のストレージドライバ
docker info | grep "Storage Driver"
# Storage Driver: overlay2

# Docker設定で指定
cat /etc/docker/daemon.json
# {
# "storage-driver": "overlay2"
# }

パフォーマンス最適化

# レイヤー数を減らす(マルチステージビルド)
# 悪い例
FROM ubuntu
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean

# 良い例
FROM ubuntu
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# ボリュームを使用(頻繁な書き込み)
docker run -v /data:/app/data myapp

# tmpfsを使用(一時ファイル)
docker run --tmpfs /tmp myapp

まとめ

OverlayFSの構成要素

要素説明
Lower読み取り専用レイヤー(複数可)
Upper読み書き可能レイヤー
Work作業用ディレクトリ
Merged統合ビュー

重要なポイント

  • コンテナイメージはレイヤーの積み重ね
  • 書き込みはCopy-on-Writeで処理
  • 削除はwhiteoutファイルで表現
  • 同じベースイメージはレイヤーを共有
  • 頻繁な書き込みにはボリュームを使用

次のステップ


参考リソース