docker单机存储

序言

    

    docker需要存储的时候,将相关的数据存储在什么位置呢?镜像存储在哪里。数据又存储在哪里。

    

    容器共享数据的时候怎么来共享?容器和主机共享,容器和容器怎么共享数据。


Despacito (Version Pop)Luis Fonsi - Mega Hits Sommer 2017

docker持久化管理之bind

    docker将需要存储的数据存储在docker的宿主机之上,这种存储的方式和linux上面的mount是一样的,也就是直接将一个目录或者文件挂载到相应的容器之中。

    使用bind的时候,比较适用的场景是共享主机上的配置文件或者是在开发程序的时候,直接将程序目录挂载到容器中,即时修改能立刻看到修改的效果,当主机上目录结构和容器中的目录结构相同时,也可以使用bind的方式。

    使用bind的时候,不能使用docker的cli来进行管理。

1、 docker挂载单个文件

[root@docker-ce ~]# docker run -itd -v /root/index.html:/usr/share/nginx/html/index.html -p 80:80 nginx(挂载本地文件到容器中,本地文件必须先存在

570bb6170741ba478dd3f9a26fb0eeb66dc8fbc9f86ae47e66ac7ec24eaf9a5f

[root@docker-ce ~]# docker volume ls(不能使用docker的cli来进行管理

DRIVER              VOLUME NAME

[root@docker-ce ~]# docker exec 57 cat /usr/share/nginx/html/index.html(查看挂载的文件,当文件不存在的时候,会在容器中自动创建这个文件,然后挂载过去

changed index

[root@docker-ce ~]# curl localhost:80(访问查看是否正确挂载

changed index

[root@docker-ce ~]# echo "welcome engix" >index.html (修改首页文件

[root@docker-ce ~]# curl localhost:80(修改生效

welcome engix

[root@docker-ce ~]# docker inspect 57(查看详细的挂载信息

"Binds": [(显示挂载的源src文件和目标dst文件

                "/root/index.html:/usr/share/nginx/html/index.html"

            ]


  "Mounts": [

            {

                "Type": "bind",(挂载的类型为bind

                "Source": "/root/index.html",

                "Destination": "/usr/share/nginx/html/index.html",

                "Mode": "",

                "RW": true,

                "Propagation": "rprivate"

            }

        ]

[root@docker-ce ~]# docker rm -f 5(删除容器

5

[root@docker-ce ~]# ls -l index.html (删除容器后,挂载的文件不会被删除

-rw-r--r--. 1 root root 14 Jan 12 18:44 index.html

[root@docker-ce ~]# docker run -itd -v /root/index.html:/usr/share/nginx/html/ -p 80:80 nginx(挂载单个文件的时候,必须在容器的路径也写上挂载的文件的名称,否则会报错,不是期望的类型

208a9ed30e9ab52f95fdf5f16f3621c6aad0532b8167be6cee97df3c4433e122

docker: Error response from daemon: oci runtime error: container_linux.go:265: starting container process caused "process_linux.go:368: container init caused \"rootfs_linux.go:57: mounting \\\"/root/index.html\\\" to rootfs \\\"/var/lib/docker/overlay/97c27bea86c0aa892359a57c3c004bf7c2ccc12d648cfc47e46b68553d1ae9fd/merged\\\" at \\\"/var/lib/docker/overlay/97c27bea86c0aa892359a57c3c004bf7c2ccc12d648cfc47e46b68553d1ae9fd/merged/usr/share/nginx/html\\\" caused \\\"not a directory\\\"\""

: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.

[root@docker-ce ~]# docker run -itd -v /root/index.html:/usr/share/nginx/html/kel.html -p 80:80 nginx(挂载单个文件的时候,可以进行重命名操作

e653da51ef37dee7b085a8dfe1d944fc0b9344cc4108170cc79899372e482adc

[root@docker-ce ~]# docker exec e6 ls -l /usr/share/nginx/html/kel.html

-rw-r--r--. 1 root root 14 Jan 12 23:44 /usr/share/nginx/html/kel.html

[root@docker-ce ~]# docker run -itd -v ~/index.html:/usr/share/nginx/html/index.html -p 80:80 nginx(挂载的时候,可以使用环境变量或者是相对路径

9ab038654abef6d4492cb76454c0e4b9f96653b028fa0e8b085b3bd156af14b9

[root@docker-ce ~]# docker exec 9a ls -l /usr/share/nginx/html/index.html

-rw-r--r--. 1 root root 14 Jan 12 23:44 /usr/share/nginx/html/index.html

2、容器挂载整个目录

[root@docker-ce ~]# docker run -itd -v ~/html/:/usr/share/nginx/html/ -p 80:80 nginx(容器挂载整个目录

31fad74c27c1bfd56b6c0b7a02fc44617e9110ae4913697f87fe0d800b0a9270

[root@docker-ce ~]# curl localhost:80(正常访问挂载的目录文件,原来在容器中本来存在的文件被隐藏

welcome engix

[root@docker-ce ~]# docker exec 31 ls -l /usr/share/nginx/html(容器中存在的文件

total 4

-rw-r--r--. 1 root root 14 Jan 13 10:51 index.html

[root@docker-ce ~]# docker volume ls(挂载整个目录也不能使用docker的cli来进行管理

DRIVER              VOLUME NAME

[root@docker-ce ~]# docker inspect 31(查看挂载的相信信息

        "HostConfig": {

            "Binds": [(挂载的目录情况,前面是src源目录,后面是dst目标目录

                "/root/html/:/usr/share/nginx/html/"

            ],

        "Mounts": [

            {

                "Type": "bind",(类型为bind

                "Source": "/root/html",

                "Destination": "/usr/share/nginx/html",

                "Mode": "",

                "RW": true,(默认权限为读写

                "Propagation": "rprivate"

            }

[root@docker-ce ~]# docker run -itd -v /root/:/mnt/kel -p 80:80 nginx(挂载的时候,当容器不存在这个目录的时候,那么会创建这个目录

01ca0e693d4344efc6ef8830d3ed102e4b84cbf47144f9bfa5287d71a4ad730c

[root@docker-ce ~]# docker volume ls

DRIVER              VOLUME NAME

[root@docker-ce ~]# docker exec 01 ls -l /mnt/kel(文件均存在

total 68

drwxr-xr-x. 2 root root    24 Jan 13 10:51 html

[root@docker-ce ~]# docker run -itd -v ~/html:/usr/share/nginx/html:ro -p 80:80 nginx(使用bind的时候,可以设定文件目录的权限,默认是rw可读写,可以设定为ro只读

6be5492535fbbfd4718c39bcebc17481e3c5dd21ed9c133fb2a2645388d3db03

[root@docker-ce ~]# docker inspect 6b 


            "Binds": [(看到只读权限,在容器中不能修改此文件或者目录

                "/root/html:/usr/share/nginx/html:ro"

            ],

             "Mounts": [

            {

                "Type": "bind",

                "Source": "/root/html",

                "Destination": "/usr/share/nginx/html",

                "Mode": "ro",

                "RW": false,(只读权限

                "Propagation": "rprivate"

            }

3、关于挂载的总结

    在使用挂载的是时候,需要关注以下几个方面:

    a 挂载的目录或者文件必须在宿主机上存在,如果不存在,默认会创建为目录,bind相当于在linux挂载上,源是必须存在的,否则无法进行挂载;

    b 挂载的时候,容器中可以存在或者不存在此目录或者文件,当不存在的时候会新建,当存在的时候,会进行覆盖的操作;

    c 挂载的时候,可以设定文件或者目录的权限,例如rw可读写,ro只读;

    d 挂载的时候,删除容器,此目录或者文件不会删除,由主机管理,不会由docker进行管理;

    e 挂载的时候,src必须为相对路径或者绝对路径,如果不是,那么就会变成volume类型

docker持久化管理之volume

    docker另外一种的存储方式就是使用volume进行管理,在此种情况下,那么这些数据的存储都是由docker的cli来进行管理,比较适用的场景是多个容器之间共享数据,将数据存储在远端服务器上,也适用于需要备份,恢复,迁移等场景。使用volume管理的方式也是docker比较推荐的一种方式。

1、 使用匿名卷挂载

[root@docker-ce ~]# docker run -itd -p 80:80 -v /usr/share/nginx/html nginx(创建一个卷管理的容器

0ca3f0a94942a8d5d90929a4f3322a6b88bbeb0dc20ddb82cb49fb919c4832ba

[root@docker-ce ~]# docker volume ls(卷管理的方式可以通过docker的cli命令进行管理

DRIVER              VOLUME NAME

local               3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9

[root@docker-ce ~]# docker inspect 0c(查看容器的具体的挂载信息

       "HostConfig": {

            "Binds": null,(bind类型的地方显示为空

        "Mounts": [

            {

                "Type": "volume",(挂载的类型为卷挂载的方式

                "Name": "3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9",

                "Source": (源路径存在于固定的目录中)"/var/lib/docker/volumes/3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9/_data",

                "Destination": "/usr/share/nginx/html",

                "Driver": "local",

                "Mode": "",

                "RW": true,(默认权限为rw

                "Propagation": ""

            }

            "Volumes": {

                "/usr/share/nginx/html": {}

            },

[root@docker-ce ~]# ls -l (已经存在相关的文件)/var/lib/docker/volumes/3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9/_data

total 8

-rw-r--r--. 1 root root 537 Dec 26 06:11 50x.html

-rw-r--r--. 1 root root 612 Dec 26 06:11 index.html

[root@docker-ce ~]# docker cp html/index.html 0ca3f0a94942:/usr/share/nginx/html/(修改首页的地址

[root@docker-ce ~]# curl localhost:80(访问之后,内容发生改变

welcome engix

[root@docker-ce ~]# cat /var/lib/docker/volumes/3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9/_data/index.html (卷管理中的内容发生改变

welcome engix

[root@docker-ce ~]# docker ps(查看正在运行的容器

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES

0ca3f0a94942        nginx               "nginx -g 'daemon ..."   32 minutes ago      Up 32 minutes       0.0.0.0:80->80/tcp   focused_curran

[root@docker-ce ~]# docker rm -f 0c(-f 强制删除容器

0c

[root@docker-ce ~]# docker volume ls(容器删除之后,卷内容不会被删除

DRIVER              VOLUME NAME

local               3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9

[root@docker-ce ~]# docker volume prune(删除没有被容器使用的卷,在输入的必须使用y,而不能使用yes

WARNING! This will remove all volumes not used by at least one container.

Are you sure you want to continue? [y/N] y

Deleted Volumes:

3675bcd346cdcfb06d81fbd39d1aa3494f948be9ac8e8331d13461a09d329ff9

Total reclaimed space: 551B

[root@docker-ce ~]# docker volume ls(卷被删除

DRIVER              VOLUME NAME

2、 使用有名称的卷进行挂载

[root@docker-ce ~]# docker run -itd -v kel:/mnt/kel nginx(当目录不存在的时候,那么依旧是使用卷进行管理

cc36da2811807b83f969f4e417067fb6dc49328c230dc443ed2a34025013aef3

[root@docker-ce ~]# docker volume ls(查看主机上的卷

DRIVER              VOLUME NAME

local               kel

[root@docker-ce ~]# docker cp html/index.html cc:/mnt/kel/(修改内容

[root@docker-ce ~]# ls -l kel(宿主机没有创建目录

ls: cannot access kel: No such file or directory

[root@docker-ce ~]# docker inspect cc

      "HostConfig": {

            "Binds": [(也创建了绑定了目录,但是是一个指定了名称的卷管理器

                "kel:/mnt/kel"

            ],

        "Mounts": [

            {

                "Type": "volume",(卷管理的类型

                "Name": "kel",(卷的名称,在volumes目录中不会随机生成一个id,而会直接指定这个卷的名称

                "Source": "/var/lib/docker/volumes/kel/_data",

                "Destination": "/mnt/kel",

                "Driver": "local",

                "Mode": "z",(模式不一样

                "RW": true,

                "Propagation": ""

            }

        ],

[root@docker-ce ~]# docker ps(查看运行中的容器

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES

cc36da281180        nginx               "nginx -g 'daemon ..."   7 minutes ago       Up 7 minutes        80/tcp              determined_clarke

[root@docker-ce ~]# docker rm -fv cc(强制删除容器和容器使用卷

cc

[root@docker-ce ~]# docker volume ls(当容器使用了有名称的卷的时候,不会被强制删除

DRIVER              VOLUME NAME

local               kel

[root@docker-ce ~]# docker volume rm kel(手动删除卷成功

kel

3、 使用dockerfile中的指令VOLUME来创建卷

[root@docker-ce ~]# docker history redis     (查看redis镜像的构建步骤)      

<missing>           4 weeks ago         /bin/sh -c #(nop)  VOLUME [/data]               0B                  

[root@docker-ce ~]# docker run -itd -v kel:/data redis(给卷指定名称

610dbc29d8a98fbf8437538e629b4e5f4a73fcaba7be3e3cbbf353bbda7e3cec

[root@docker-ce ~]# docker volume ls

DRIVER              VOLUME NAME

local               kel

[root@docker-ce ~]# docker run -itd redis(使用匿名卷

6c92e5088db9e39f77dadba3b5f5b815d695d01c2048dd70e8ebcd3321188b63

[root@docker-ce ~]# docker volume ls

DRIVER              VOLUME NAME

local               bff296d5de8c01ecf387042ea7160bcd91b7b300626ea2ca6e2ff078f9a59904

local               kel

[root@docker-ce ~]# docker rm -fv 61 6c(强制删除容器和卷

61

6c

[root@docker-ce ~]# docker volume ls(有名称的卷不会被删除

DRIVER              VOLUME NAME

local               kel

[root@docker-ce ~]# docker volume rm kel(手动删除卷

kel

4、 总结

    a 在使用匿名卷的时候,那么就会在/var/lib/docker/volumes路径下新建一个目录,专门用于存储卷管理的数据;此时可以完全用docker cli来进行管理,例如删除等操作;

    b 当dst目标路径存在文件的时候,会将相关的文件拷贝到卷管理的路径下;当不存在此路径的时候,那么会新建一个空的目录。
    c 在主机上或者容器中都可以对此文件进行管理也是修改之后,立即生效,这点和bind相同;

    d bind类型和volume的类型主要的判断方式就是看在使用-v的时候,主机上是否存在源路径,存在则为bind类型,当不存在的时候,就是volume类型;volume类型只能是目录,不能是文件

说明

1、有状态和无状态

    微服务中,推荐的是就是无状态的服务,而有些应用必须有状态,无状态基本上是指删除这个容器之后,再新建一个,可以正常的服务,而有状态则不行,需要加载相关的数据。在镜像中,nginx差不多就是无状态的,日志无所谓;而redis相当于是有状态的,因为rdb文件需要保存(这就是为啥在构建redis镜像的时候使用VOLUME指令来创建挂载的卷)。

 2、 共享数据

    无论是使用bind类型或者是volume的类型,总体的目标是为了共享数据或者是保存数据。

    使用bind方式来共享首页

[root@docker-ce ~]# docker run -itd -v /root/html/index.html:/usr/share/nginx/html/index.html -p 80:80 nginx(使用bind方式运行容器

93d8b3ebc47dd2a9247d6b3033657506b56519fc4a4accdb2a28fdb582c8cc12

[root@docker-ce ~]# docker run -itd -v /root/html/index.html:/usr/share/nginx/html/index.html -p 81:80 nginx

20a687363a7ffb2cc53d132eb1e522222019d6702f90e09b53ab7e2101e318e6

[root@docker-ce ~]# curl localhost:80访问测试

welcome engix

[root@docker-ce ~]# curl localhost:81

welcome engix

    使用volume方式来共享首页:

[root@docker-ce ~]# docker create --name html -v /root/html:/usr/share/nginx/html nginx(创建一个共享容器

031eb832e928b88e884a0cd4324ffb1736c1381ccbb22e69cf46719f7aacf2fa

[root@docker-ce ~]# docker run -itd --volumes-from html -p 80:80 nginx使用volumes-from指令来共享数据

c03f154ac41e9ff79bd2bdba919a18813034cf14b0a5895ab793ca5d7eb2063c

[root@docker-ce ~]# docker run -itd --volumes-from html -p 82:80 nginx使用volumes-from指令来共享数据

a27cf9004a61a4eb24c9368787927025bb12479e14f7961d1ab51d8ed02d31c8

[root@docker-ce ~]# curl localhost:80(访问测试

welcome engix

[root@docker-ce ~]# curl localhost:82

welcome engix

    从上面可以看到,当使用volume方式共享数据的时候,其实还是使用的bind,但是这种最大的好处就是,在运行容器的时候,只要指定volues-from来自于哪里,和宿主机无关了。为存储在远端做了准备。

    使用dockerfile来共享

[root@docker-ce html]# cat dockerfile (创建一个dockerfile,保存静态文件

FROM scratch

ADD html /usr/share/nginx/html

VOLUME /usr/share/nginx/html

[root@docker-ce html]# docker build -t static-index .(编译成镜像

Sending build context to Docker daemon  3.584kB

Step 1/3 : FROM scratch

 ---> 

Step 2/3 : ADD html /usr/share/nginx/html

 ---> 050292ccfac5

Step 3/3 : VOLUME /usr/share/nginx/html

 ---> Running in 91043d0668ee

 ---> d207c0b2c4c7

Removing intermediate container 91043d0668ee

Successfully built d207c0b2c4c7

Successfully tagged static-index:latest

[root@docker-ce html]# docker create --name static-nginx-index static-index bash(创建卷容器,卷容器无须运行

6c942dea654cf527f91bd0c6946457dcd7fc2becf10f6c393c6100b4f3498414

[root@docker-ce html]# docker ps -a(创建状态的容器主要为了共享卷

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

6c942dea654c        static-index        "bash"              5 seconds ago       Created                                 static-nginx-index

[root@docker-ce html]# docker run -itd --volumes-from static-nginx-index -p 80:80 nginx(创建容器,卷来自于其他的容器

ce719d665b3649a61f8fc43501453cad6dea76b607cc01d616506fbb5627b443

[root@docker-ce html]# curl localhost

welcome engix

    在使用dockerfile的时候,可以主要用来共享静态的文件。

3、 其他

    在进行备份,删除,恢复,迁移的时候,只要直接将目录进行保存就好了。

镜像的存储

    镜像的存储主要由存储驱动来控制,生产一般使用overlay的存储方式,在如下可以看到(此处仅使用overlay驱动):

[root@docker-ce ~]# docker info(查看docker的系统信息

Storage Driver: overlay(存储驱动

 Backing Filesystem: xfs(使用的文件系统

 Supports d_type: true

Docker Root Dir: /var/lib/docker(存储的根目录

[root@docker-ce ~]# ls -l /var/lib/docker/overlay/(镜像存储的目录)

total 0

drwx------. 3 root root 18 Jan 11 18:31 04cfc4859adf56c42f9896052eb9c213f1d95bf092ff95e6d00866c3364cb647(镜像文件)

drwx------. 3 root root 18 Jan 11 18:31 07a887ffa72e124f61002de870b7cc10e90a5e4c2c10ad5b8668c167fd180fad

drwx------. 3 root root 18 Jan 11 18:31 106cd092801e1a5a338142e6ef2342168901c631c1a5f8e06cb7c2c9daf4d55d

drwx------. 5 root root 61 Jan 13 09:21 29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7(容器的可写层

drwx------. 5 root root 61 Jan 13 09:21 29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7-init(容器的初始化文件,只读权限

[root@docker-ce 29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7]# ls -l(存储的目录结构

total 4

-rw-r--r--. 1 root root 64 Jan 13 09:21 lower-id(容器使用的镜像的id,只读

drwxr-xr-x. 1 root root 57 Jan 13 09:21 merged(容器展现的信息,合并了lower层和upper层的信息

drwxr-xr-x. 5 root root 57 Jan 13 09:21 upper(容器层也就是可读写层

drwx------. 3 root root 18 Jan 13 09:28 work(union联合文件系统需要的组建

[root@docker-ce 29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7]# mount |grep overlay(使用mount查看相关文件系统的挂载,使用的是union filesystem

/dev/mapper/centos-root on /var/lib/docker/overlay type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

overlay on /var/lib/docker/overlay/29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay/48523410c8d26acafb7c5d1bee24baa17bf39efbad0dba7bf2d4c52e61cca86c/root,upperdir=/var/lib/docker/overlay/29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7/upper,workdir=/var/lib/docker/overlay/29cb168db7fae50989c4d61b5cc72df37fe5772effa632fb01224c89205007f7/work)

    联合文件系统也就是将几个不同的目录挂载到同一个目录中,对外提供一个目录的功能。

    镜像层为lower dir,为只读的,而容器层为upper dir,是可读写的。每个容器运行的时候,镜像层都是一样的,不同的内容都在upper dir之中。


猜你喜欢

转载自blog.51cto.com/15060545/2653803