docker联合文件系统

1. 前言

(Union filesystem)联合文件系统允许我们把多个文件系统逻辑上合并成一个文件系统,组成Union filesystem的文件系统不必相同(它们可以是ext2/3/4,vfat,ntfs,jffs...)。overlay是联合文件系统的一种(aufs...),overlay文件系统构建于其他文件系统之上,overlay其实更像是个挂载系统(mount system),功能是把不同的文件系统挂载到统一的路径。

2. overlay文件系统

overlay是个分层的文件系统,底层文件系统通常叫lower,顶层文件系统系统通常叫upper,两者通常合并挂载到merged目录,最终用户看到的就是merged中的文件。

lower文件系统是readonly,对merged中所有的修改都只对upper操作,记住这点很重要。下面我们在linux上创建一个overlay文件系统,用以说明overlay文件系统挂载,文件读写,文件新增和删除。

3. 创建overlay文件系统

创建lower upper merged work目录,把lower和upper挂载到mergedwork是空目录,必须和merged的文件系统类型一样。

lower/
├── books
│   ├── c
│   └── java
├── computers
│   ├── huawei
│   ├── lenove
│   └── macpro
├── README.txt
└── sports
    ├── fishing
    ├── football
    └── running
upper/
├── books
│   ├── c
│   └── golang
├── cities
│   ├── beijing
│   ├── shanghai
│   └── shenzhen
├── README.txt
└── sports
    ├── basketball
    ├── football
    └── running

挂载: sudo mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work merged/,挂载后merged目录结构如下:

merged/
├── books
│   ├── c
│   ├── golang
│   └── java
├── cities
│   ├── beijing
│   ├── shanghai
│   └── shenzhen
├── computers
│   ├── huawei
│   ├── lenove
│   └── macpro
├── README.txt
└── sports
    ├── basketball
    ├── fishing
    ├── football
    └── running

可以看到lowerupper中的文件合并到了merged中,当lowerupper有相同路径的文件时,merged中只显示upper中的。也就是说upper会遮住lower中同名的文件(同路径下)。

4. overlay文件系统读写

  1. 新建文件
    upper中新建文件,lower只读
  2. 删除文件
  1. 如果文件在upper中存在,则删除,并新建一个whiteout文件
  2. 如果文件在upper中不存在,在lower中存在,则在upper新建一个同名的whiteout文件
  1. 修改文件
  1. 文件在upper中存在,则只会修改upper中的文件
  2. 文件在upper中不存在,在lower中存在,则从lower中复制文件到upper,再修改upper中的复制品
  1. 读取文件
    upper中有该文件则读取upper的,否则读取lower中的
    whiteout是个字符设备文件,主次设备号为0,用来屏蔽对lower的访问。
  2. 补充
    linux4.0以后,overlay文件系统支持多层lower挂载,挂载方式如下:
    sudo mount -t overlay overlay -o lowerdir=./dir1:./dir2:./dir3,upperdir=./upper,workdir=./work merged/
    dir1在lower的顶层,dir3在lower的底层,debian上docker就是采用这种方式。

overlay提供了对只读文件系统的读写功能,适合用在需要维持一个只读镜像,又需要提供读写功能的系统中,比如openwrt和docker,下面我们介绍docker中overlay的应用。

5. docker

docker的基础镜像其实就是个readonly的根文件系统,从基础镜像构建的镜像其实都只是把修改部分和基础镜像合并重新打包,我们从ubuntu镜像构建一个具有golang环境的镜像,用来说明overlay在docker中的应用。
Dockerfile如下:

## golang dockerfile
## version 1.0
## 以ubuntu为基础镜像构建新镜像
FROM ubuntu:latest

## 维护者
MAINTAINER "joker"

## 新增用户go
RUN ["useradd", "-m", "-s", "/bin/bash", "go"]

## 指定工作目录
WORKDIR "/home/go"

## 把golang源码包打包到镜像,并解压到/opt路径
ADD go1.16.6.linux-amd64.tar.gz /opt

## 设置GOROOT/GOPATH/GOPROXY环境变量,并把对应的bin目录加到系统PATH环境变量
ENV GOROOT="/opt/go" GOPATH="/home/go/golang" GOPROXY="https://goproxy.cn,direct" GO111MODULE="on"
ENV PATH=$GOROOT/bin:$GOPATH/bin:$PATH

## 以下命令以用户go执行
USER go

## docker run执行的命令
ENTRYPOINT ["/bin/bash"]

## 挂载/home/go到一个匿名路径,后续能看到具体是哪个路径,可以通过-v覆盖匿名路径
VOLUME ["/home/go"]

## 添加一些元数据
LABEL author="joker" email="cyp_fly@126.com"

构建镜像

$$ docker build -t ubuntu:golang .
Sending build context to Docker daemon  129.1MB         
Step 1/11 : FROM ubuntu:latest                        
 ---> ba6acccedd29                                             
Step 2/11 : MAINTAINER "joker"                                                    
 ---> Running in 1906ecf1e073                
Removing intermediate container 1906ecf1e073
 ---> b10d3bffdec9                                                                          
Step 3/11 : RUN ["useradd", "-m", "-s", "/bin/bash", "go"]
 ---> Running in f5fc6f4d31c2                               
Removing intermediate container f5fc6f4d31c2                  
 ---> 8985b91b0827                                                     
Step 4/11 : WORKDIR "/home/go"                 
 ---> Running in f4f4b66d61fe                                                                   
Removing intermediate container f4f4b66d61fe      
 ---> a6e334f8073f                                          
Step 5/11 : ADD go1.16.6.linux-amd64.tar.gz /opt          
 ---> 142a1460d16c                          
Step 6/11 : ENV GOROOT="/opt/go" GOPATH="/home/go/golang" GOPROXY="https://goproxy.cn,direct" GO111MODULE="on"
 ---> Running in a0b88f65dc77                         
Removing intermediate container a0b88f65dc77
 ---> 331d4c34ae5f                                         
Step 7/11 : ENV PATH=$GOROOT/bin:$GOPATH/bin:$PATH               
 ---> Running in 3909af77862a                                                                                  
Removing intermediate container 3909af77862a                                                                   
 ---> 55035165f7c3                                                                          
Step 8/11 : USER go                                           
 ---> Running in 0840158531d4                           
Removing intermediate container 0840158531d4
 ---> 68459adca191                                     
Step 9/11 : ENTRYPOINT ["/bin/bash"]           
 ---> Running in fe9897b8d155                            
Removing intermediate container fe9897b8d155   
 ---> d17e20160cfd                                       
Step 10/11 : VOLUME ["/home/go"]                    
 ---> Running in dce2482115c0                     
Removing intermediate container dce2482115c0                                                
 ---> 7252f0757d57                                     
Step 11/11 : LABEL author="joker" email="cyp_fly@126.com"                            
 ---> Running in c0a3975854ba                                                                                       
Removing intermediate container c0a3975854ba                                                    
 ---> d4e4da229727                                           
Successfully built d4e4da229727                                               
Successfully tagged ubuntu:golang 

启动容器

$$ docker run -itd --name golang -u go -v /var/golang:/home/go ubuntu:golang

查看容器

$$ docker inspect golang

查看GraphDriver

        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/144b8379bf7b144ab9ec4d8de712b8ec56d87fd0f5d74b2c938c2b215e1b9ebd-init/diff:/var/li
b/docker/overlay2/1d65e86e54373a5b01afe28d0878a953fa2e9eb7cd14552a17d4a8628b476978/diff:/var/lib/docker/overlay2/b56e1dd54abdf8300b2f972
6e3e92d3b32b78c8bf5b5cd807e27be78c671af40/diff:/var/lib/docker/overlay2/44c045232b8f3510d28965fbebcaad458263855785cc38e1a784a6731df7433d
/diff",
                "MergedDir": "/var/lib/docker/overlay2/144b8379bf7b144ab9ec4d8de712b8ec56d87fd0f5d74b2c938c2b215e1b9ebd/merged",
                "UpperDir": "/var/lib/docker/overlay2/144b8379bf7b144ab9ec4d8de712b8ec56d87fd0f5d74b2c938c2b215e1b9ebd/diff",
                "WorkDir": "/var/lib/docker/overlay2/144b8379bf7b144ab9ec4d8de712b8ec56d87fd0f5d74b2c938c2b215e1b9ebd/work"
            },
            "Name": "overlay2"
        },

从上面可以看到docker采用的是overlay2文件系统,LowerDir有多层。

image.png

查看每一层都有什么:
/var/lib/docker/overlay2/144b8379bf7b144ab9ec4d8de712b8ec56d87fd0f5d74b2c938c2b215e1b9ebd-init/diff

dev  etc

/var/lib/docker/overlay2/1d65e86e54373a5b01afe28d0878a953fa2e9eb7cd14552a17d4a8628b476978/diff

opt

/var/lib/docker/overlay2/b56e1dd54abdf8300b2f9726e3e92d3b32b78c8bf5b5cd807e27be78c671af40/diff

etc  home  var

/var/lib/docker/overlay2/44c045232b8f3510d28965fbebcaad458263855785cc38e1a784a6731df7433d/diff

bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

为什么会有这么多层,其实很好理解,查看Dockerfile中写了哪些规则:

  1. 基于ubuntu基础镜像,所以最底层必然是ubuntu的镜像文件,待会可以查证
  2. 创建一个用户,必然会修改/etc,新用户在/home下创建了工作目录,导致LowerDir增加一层
  3. 上传了golang源码包并解压到/opt,并然导致/opt修改,导致LowerDir增加一层
  4. docker把/etc自动添加一个init层,docker官方认为/etc下的修改一般会影响kernel,从而影响所有使用该镜像的用户,其他用户可能不希望有这些修改。
  5. UpperDir初始化是empty的,用户有修改就会在UpperDir中创建

现在我们核对下最地城镜像是否是ubuntu的镜像文件:

$$ docker inspect ubuntu
        "GraphDriver": {
            "Data": {
                "MergedDir": "/var/lib/docker/overlay2/44c045232b8f3510d28965fbebcaad458263855785cc38e1a784a6731df7433d/merged",
                "UpperDir": "/var/lib/docker/overlay2/44c045232b8f3510d28965fbebcaad458263855785cc38e1a784a6731df7433d/diff",
                "WorkDir": "/var/lib/docker/overlay2/44c045232b8f3510d28965fbebcaad458263855785cc38e1a784a6731df7433d/work"
            },
            "Name": "overlay2"
        },

ubuntu是基础镜像,没有LowerDir, UpperDir就是我新建ubuntu:golang镜像LowerDir的最底层文件系统

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容