• 问题:
    • 容器:某个软件完整的运行环境,包含了一个小型的Linux系统
    • 宿主机里面同时4个nginx,一个nginx运行时完整环境有20M
      • 4个nginx合起来占用多少磁盘空间?
      • 80M?
  • 软件装在docker上和宿主机上的对比
    • 优点:docker的移植性,便捷性高于宿主机部署,进程隔离,很方便的资源限制
    • 缺点:docker虚拟化技术,损失很少的性能
  • 镜像、容器
    • 镜像:固定不变的。一个镜像可以启动很多容器
    • 容器:文件系统可能logs经常变化的,一个镜像可以启动很多容器

      docker在底层使用自己的存储驱动。来组建文件内容 storage drivers。docker 基于 AUFS (联合文件系统)。

一,镜像如何存储?

1.镜像探索

截取nginx 的分层

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12610368/1627502772620-2156f96c-c9f0-498a-b32e-0a920cdde2c0.png#align=left&display=inline&height=254&margin=%5Bobject%20Object%5D&name=image.png&originHeight=507&originWidth=1547&size=127082&status=done&style=none&width=773.5)

nginx这个镜像怎么存的? 使用docker image inspect nginx

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12610368/1627502824183-d50e11bf-597f-4c3e-8973-3ceeaac955e7.png#align=left&display=inline&height=234&margin=%5Bobject%20Object%5D&name=image.png&originHeight=467&originWidth=1683&size=100809&status=done&style=none&width=841.5)

这里面指示了镜像是如何存储

  • LowerDir:底层目录; diff(只是存储不同);包含小型linux和装好的软件 ```sql /var/lib/docker/overlay2/67b3802c6bdb5bcdbcccbbe7aed20faa7227d584ab37668a03ff6952e 631f7f2/diff:用户文件;

/var/lib/docker/overlay2/f56920fac9c356227079df41c8f4b056118c210bf4c50bd9bb077bdb4 c7524b4/diff: nginx的启动命令放在这里

/var/lib/docker/overlay2/0e569a134838b8c2040339c4fdb1f3868a7118dd7f4907b40468f5fe6 0f055e5/diff: nginx的配置文件在这里

/var/lib/docker/overlay2/2b51c82933078e19d78b74c248dec38164b90d80c1b42f0fdb1424953 207166e/diff: 小linux系统

  1. > 从下往上看
  2. - linux系统(FROM apline + Dockerfile的每一个命令可能都引起了系统的修改,所以和git一样,只记录变化。
  3. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12610368/1627502999479-7e1fde59-76e5-45e4-aceb-3a4688d046bf.png#align=left&display=inline&height=77&margin=%5Bobject%20Object%5D&name=image.png&originHeight=154&originWidth=1261&size=31908&status=done&style=none&width=630.5)
  4. - 我们进入到这个镜像启动的容器,容器的文件系统就是镜像的
  5. - `docker ps -s` 可以看到这个容器真正用到的文件大小
  6. - 容器会自己建立层;如果想要改东西,把改的内容复制到容器层即可 `docker inspect container`
  7. - `MergedDir `:合并目录;容器最终的完整工作目录全内容都在合并层;数据卷在容器层产生;所有的增删改都在容器层;
  8. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12610368/1627503254286-2a0ebf34-8d3d-47a6-9639-8c06bec35ae7.png#align=left&display=inline&height=141&margin=%5Bobject%20Object%5D&name=image.png&originHeight=193&originWidth=754&size=109118&status=done&style=none&width=549)
  9. - `UpperDir `:上层目录
  10. - `WorkDir `:工作目录(临时层),pid
  11. > LowerDir(底层)\UpperDir()\MergedDir\WorkDir(临时东西)
  12. > docker底层的 storage driver完成了以上的目录组织结果;
  13. > 什么东西适合运行容器?
  14. - docker启动一个MySQL默认什么都不做?
  15. - docker被干掉,MySQL数据就没了
  16. - 文件挂载
  17. - `docker commit `提交 mysql容器,也能提交 100G 100G
  18. <a name="DxxrB"></a>
  19. ## 2.镜像和分层
  20. Docker映像由一系列层组成。 每层代表图像的Dockerfile中的一条指令。 除最后一层外的每一层都是只读的。 如以下Dockerfile
  21. - Dockerfile文件里面几句话,镜像就有几层
  22. ```sql
  23. FROM ubuntu:15.04
  24. COPY . /app
  25. RUN make /app
  26. CMD python /app/app.py
  27. # 每一个指令都可能会引起镜像改变,这些改变类似git的方式逐层叠加。
  • 该Dockerfile包含四个命令,每个命令创建一个层。
  • FROM语句从ubuntu:15.04映像创建一个图层开始。
  • COPY命令从Docker客户端的当前目录添加一些文件。
  • RUN命令使用make命令构建您的应用程序。
  • 最后,最后一层指定要在容器中运行的命令。
  • 每一层只是与上一层不同的一组。 这些层彼此堆叠。
  • 创建新容器时,可以在基础层之上添加一个新的可写层。 该层通常称为“容器层”。 对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此薄可写容器层。

3.容器和分层

  • 容器和镜像之间的主要区别是可写顶层。
  • 在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。
  • 删除容器后,可写层也会被删除。 基础图像保持不变。 因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,但具有自己的数据状态。

共享同一Ubuntu 15.04映像的多个容器

image.png**

4.磁盘容量预估

  1. docker ps -s
  2. size:用于每个容器的可写层的数据量(在磁盘上)。
  3. virtual size:容器使用的用于只读图像数据的数据量加上容器的可写图层大小。
  4. 多个容器可以共享部分或全部只读图像数据。
  5. 从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。 因此,不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目。

5.镜像如何挑选

  1. busybox:是一个集成了一百多个最常用Linux命令和工具的软件。linux工具里的瑞士军刀
  2. alpineAlpine操作系统是一个面向安全的轻型Linux发行版经典最小镜像,基于busybox,功能比Busybox完善。
  3. slimdocker hub中有些镜像有slim标识,都是瘦身了的镜像。也要优先选择
  4. 无论是制作镜像还是下载镜像,优先选择alpine类型.

6.写时复制

  • 写时复制是一种共享和复制文件的策略,可最大程度地提高效率。
  • 如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件。
  • 另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改。 这样可以将I / O和每个后续层的大小最小化。

二,容器如何挂载?

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12610368/1627504177897-4a7521c6-08f7-4c30-8a6c-896cb8bbec8a.png#align=left&display=inline&height=249&margin=%5Bobject%20Object%5D&name=image.png&originHeight=255&originWidth=501&size=18368&status=done&style=none&width=490)

每一个容器里面的内容,支持三种挂载方式:

  1. docker自动在外部创建文件夹自动挂载容器内部指定的文件夹内容【Dockerfile VOLUME指令的作用】
  2. 自己在外部创建文件夹,手动挂载
  3. 可以把数据挂载到内存中。
    1. —mount 挂载到 linux宿主机,手动挂载(不用了)
    2. -v 可以自动挂载,到linux’主机或者docker自动管理的这一部分区域
  • Volumes(卷) :存储在主机文件系统的一部分中,该文件系统由Docker管理(在Linux上是“ / var /lib / docker / volumes /”)。 非Docker进程不应修改文件系统的这一部分。 卷是在Docker中持久存储数据的最佳方法。
  • Bind mounts(绑定挂载) 可以在任何地方 存储在主机系统上。 它们甚至可能是重要的系统文件或目录。 Docker主机或Docker容器上的非Docker进程可以随时对其进行修改。
  • tmpfs mounts(临时挂载) 仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统。

1.卷

  • 匿名卷使用 ```sql docker run -dP -v :/etc/nginx nginx

docker将创建出匿名卷,并保存容器/etc/nginx下面的内容

-v 宿主机:容器里的目录

  1. - 具名卷使用
  2. ```sql
  3. docker run -dP -v nginx:/etc/nginx nginx
  4. #docker将创建出名为nginx的卷,并保存容器/etc/nginx下面的内容

如果将空卷装入存在文件或目录的容器中的目录中,则容器中的内容(复制)到该卷中。

如果启动一个容器并指定一个尚不存在的卷,则会创建一个空卷。

-v 宿主机绝对路径:Docker容器内部绝对路径:叫挂载;这个有空挂载问题
-v 不以/开头的路径:Docker容器内部绝对路径:叫绑定(docker会自动管理,docker不会把他当前目录,而把它当前卷)

  • 用哪个比较好?
    • 如果自己开发测试,用 -v 绝对路径的方式
    • 如果是生产环境建议用卷
    • 除非特殊 /bin/docker 需要挂载主机路径的则操作 绝对路径挂载
  • nginx测试html挂载几种不同情况:
    • 不挂载 效果:访问默认欢迎页
    • -v /root/html:/usr/share/nginx/html 效果:访问forbidden
    • -v html:/usr/share/nginx/html:ro 效果:访问默认欢迎页
    • -v /usr/share/nginx/html 效果:匿名卷 (什么都不写也不要加冒号,直接写容器内的目录)

原因

  • -v html:/usr/share/nginx/html; docker inspect 容器的时候; docker自动管理的方式 ```sql

    -v不以绝对路径方式;

    1、先在docker底层创建一个你指定名字的卷(具名卷) html

    2、把这个卷和容器内部目录绑定

    3、容器启动以后,目录里面的内容就在卷里面存着;

-v nginxhtml:/usr/share/nginx/html 也可以以下操作

1、 docker create volume nginxhtml 如果给卷里面就行修改,容器内部的也就改了。

2、 docker volume inspect nginxhtml

3、docker run -d -P -v nginxhtml:/usr/share/nginx/html —name=nginx777 nginx

可以看到

“Mounts”: [ { “Type”: “volume”, //这是个卷 “Name”: “html”, //名字是html “Source”: “/var/lib/docker/volumes/html/_data”, //宿主机的目录。容器里面的哪两个文件都在 “Destination”: “/usr/share/nginx/html”, //容器内部 “Driver”: “local”, “Mode”: “z”, “RW”: true, //读写模式 “Propagation”: “” } ]

  1. ```sql
  2. #卷:就是为了保存数据
  3. docker volume #可以对docker自己管理的卷目录进行操作;
  4. /var/lib/docker/volumes(卷的根目录)

2.bind mount

如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被安装遮盖,就像您将文件保存到Linux主机上的/ mnt中一样,然后 将USB驱动器安装到/ mnt中。在卸载USB驱动器之前,/ mnt的内容将被USB驱动器的内容遮盖。 被遮盖的文件不会被删除或更改,但是在安装绑定安装或卷时将无法访问。

总结:外部目录覆盖内部容器目录内容,但不是修改。所以谨慎,外部空文件夹挂载方式也会导致容器内部是空文件夹。

  1. docker run -dP -v /my/nginx:/etc/nginx:ro nginx
  2. # bind mount和 volumes 的方式写法区别在于
  3. # 所有以/开始的都认为是 bind mount ,不以/开始的都认为是 volumes.

警惕bind mount 方式,文件挂载没有在外部准备好内容而导致的容器启动失败问题
**

  1. # 一行命令启动nginx,并且配置文件和html页面。需要知道卷的位置才能改
  2. docker run -d -P -v nginxconf:/etc/nginx/ -v nginxpage:/usr/share/nginx/html nginx
  3. # 想要实现 docker run -d -P -v /root/nginxconf:/etc/nginx/ -v
  4. /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx
  5. ### 1、提前准备好东西 目录nginxconf,目录里面的配置we年都放里面,,再调用命令
  6. ### 2、docker cp nginxdemo:/etc/nginx /root/nginxconf #注意/的使用
  7. ### 3、docker run -d -P -v /root/nginxconf:/etc/nginx/ -v
  8. /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx

**

3.管理卷

  1. docker volume create xxx:创建卷名
  2. docker volume inspect xxx:查询卷详情
  3. docker volume ls: 列出所有卷
  4. docker volume prune: 移除无用卷

4.关于docker cp

小细节

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|- :把容器里面的复制出来

docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH:把外部的复制进去

image.png
自动创建文件夹不会做递归。把父文件夹做好。