docker 容器端口与数据卷

▶ docker 容器端口

docker 容器中与端口有关的设置是在 docker run 下面的选项中。

选项 解释
-p 指定端口
-P 不指定端口,端口自动分配

▷ -P 自动分配端口

[root@server ~]# docker run -d -P nginx

[root@server ~]# docker port vigilant_thompson
80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768

这里使用 docker run -d -P 来自动分配容器的端口,通过 docker port 来做一个查看。

▶ docker 设置数据卷

对于 docker 的数据卷来说有三种方式。

▷▷ 方式一:在 docker run 的时候 -v 来宣告一个数据卷

-v:宣告一个数据卷。

[root@server ~]# docker run -it --name container-test -h CONTAINER -v /data debian /bin/bash
  • -it:容器启动的时候进入文本控制台界面。

  • -name:指定容器的名字 container-test。

  • -h:指定容器内部的主机名的名字。

  • -v:本节的重点,在容器中创建一个指定的目录数据卷。

root@CONTAINER:/# ls /data/
root@CONTAINER:/#

此时容器中的 /date 是没有内容的。

开启一个新的终端:

[root@server _data]# docker inspect -f {{.Mounts}} container-test
[{volume 5145f34d2b96a5fc2d0901a20e8bc6feb003ac132563fd1324897ed2a5f32b35 
/var/lib/docker/volumes/5145f34d2b96a5fc2d0901a20e8bc6feb003ac132563fd1324897ed2a5f32b35/_data /data local  true }]

通过 docker inspect 来查看制定容器的信息,-f,--format 这里指定了 .Mounts 挂载,这也就是说在之前 docker run 的时候创建的 /date 目录其实是系统中的一个目录,然后以挂载的形式挂载到了容器当中。

输出的信息不多,只是内容不比较长而已,输出的前面是容器中的卷,后面跟的是系统中的目录,这表明与以往系统挂载一样。

/var/lib/docker/volumes/5145f34d2b96a5fc2d0901a20e8bc6feb003ac132563fd1324897ed2a5f32b35/_data/ 就是 /date 的实际地址。


进入该目录,在目录中创建一个 hello.txt 的文件

[root@server ~]# cd /var/lib/docker/volumes/5145f34d2b96a5fc2d0901a20e8bc6feb003ac132563fd1324897ed2a5f32b35/_data/
[root@server _data]# touch hello.txt

回到原来的终端界面,此时该终端的内容是容器内的界面。

root@CONTAINER:/# cd /data/
root@CONTAINER:/data# ls
hello.txt

▷▷ 方式二:使用 Dockerfile 文件的时候指定逻辑卷

Dockerfile 中的每个指令执行后都会产生一个新的镜像层,这个镜像层可以用来启动容器,一个新的镜像层的建立就是在上一层的镜像启动容器,然后执行 Dockerfile 的指令后,将其保存为一个新的镜像。

Dockerfile 的指令都成功后留下的中间指令产生的容器默认就会被删掉。

Dockerfile 中的数据卷:

FROM debian
VOLUME /data

VOLUMEdocker run ... ... -v /data 的效果是一样的。


▷▷▷ 要考虑的问题

上面的简单演示在 docker build 是没有问题的。但是使用后面的 Dockerfile 的就有问题了。

FROM debian
VOLUME /data
RUN touch /data/hello.txt

这个看起和上面的一样简单,只是增加了 RUN touch /data/hello.txt 从直观上看起来没有问题,这是在之前创建的 /data 下面新建了一个文件 hello.txt

[root@server the_dockerfile]# docker build -t hello .
--snip--
 > [2/3] RUN touch /data/hello.txt:
0.225 touch: cannot touch '/data/hello.txt': No such file or directory
------
Dockerfile:3
--------------------
   1 |     FROM debian
   2 |     VOLUME /data
   3 | >>> RUN touch /data/hello.txt
   4 |     RUN mkdir /the_good
   5 |
--------------------
ERROR: failed to solve: process "/bin/sh -c touch /data/hello.txt" did not complete successfully: exit code: 1
--snip--

问题就出在最后一行指令上。问题表明在 touch 的时候失败了。

原因:这是因为临时容器的原因,实际上,它们是在一个用来创建容器层的临时容器内的数据卷上执行的。在第二行执行了 VOLUME 的时候确实是存在一个 /data 卷,但是这个卷是在临时的容器中,在执行下一条指令的时候,这个卷被删除了。第4指令没有办法找到这个卷,即使在容器中确实是有 /data,但是 /data 实际对应的主机的卷已经没有了,因为这个对应的卷是在临时的容器中,在下一行后就没有了,自然第4行也写入不了,失败了。


▷▷▷ 解决上面的问题

解决这个问题用一个成语来讲就是 未雨绸缪,即讲要对 /data 的目录进行的操作提前。

FROM debian
RUN mkdir /data && touch /data/test.txt
VOLUME /data

未雨绸缪:VOLUME 指令之前先 RUN 指令执行对目录的操作,相当于是调换位置了,此时基于 RUN mkdir /data && touch /data/test.txt 构建的镜像对目录的操作是有的,现在做的就是将容器中的目录和主机目录的关系也就是 VOLUME 的操作即可。

当使用这个镜像来启动一个容器的时候,Docker 会把这个数据卷目录内的所有文件从镜像复制到容器。

[root@server the_dockerfile]# docker build -t hello .
[+] Building 0.3s (6/6) FINISHED                                   docker:default
 => [internal] load build definition from Dockerfile                         0.0s
 => => transferring dockerfile: 162B                                         0.0s
 => [internal] load metadata for docker.io/library/debian:latest             0.0s
 => [internal] load .dockerignore                                            0.0s
 => => transferring context: 2B                                              0.0s
 => CACHED [1/2] FROM docker.io/library/debian:latest                        0.0s
 => [2/2] RUN mkdir /data && touch /data/test.txt                            0.2s
 => exporting to image                                                       0.0s
 => => exporting layers                                                      0.0s
 => => writing image sha256:66d449c034e4fff2644ac597cb72ab8498beaab6ee03fa3  0.0s
 => => naming to docker.io/library/hello                                     0.0s

当使用这个镜像来启动一个容器的时候,Docker 会把那个数据卷目录内的所有文件从镜像复制到容器。但如果数据卷是和主机目录绑定的话,那么镜像的数据就不会复制到容器了(这是为了防止主机文件被意外覆写)。

▷▷ 方式三:使用 docker run -v 的扩充,指定主机上挂载的卷

-v HOST_DIR:CONTAINER_DIR

前面的方式一、方式二在进行的卷的挂载在删除的时候数据也会连带着一起被删除,但是这种指定挂载的数据卷不会有这样的操作。

示列:
在主机中的 /tmp/ 中创建一个 /test 的目录,这个目录是容器要挂在的卷

[root@server tmp]# cd /tmp/
[root@server tmp]# mkdir test; cat "指定挂载目录" ./test/hello.txt

在这个 test 目录文件中创建了一个 hello.txt 的文件,里面有 指定挂载目录 的内容,方便后面再容器挂载后验证查看。

创建容器,指定挂载:

[root@server tmp]# docker run -it -v /tmp/test/:/home debian /bin/bash

root@380ffbd2e3e3:/# cd /home; ls -l
total 4
-rw-r--r--. 1 root root 19 Sep 17 07:50 hello.txt

root@380ffbd2e3e3:/home# cat hello.txt
指定挂载目录

显然这个目录被容器挂载为数据卷了,里面的 hello.txt 中的内容也能被读取。

root@380ffbd2e3e3:/home# exit
exit

[root@server tmp]#
[root@server tmp]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS                      PORTS     NAMES
380ffbd2e3e3   debian    "/bin/bash"   7 minutes ago    Exited (0) 50 seconds ago             quirky_antonelli

[root@server tmp]# docker rm 380ffbd2e3e3
380ffbd2e3e3
[root@server tmp]# ll
drwxr-xr-x. 2 root root 23 Sep 17 15:50 test

退出容器后删除该该容器,指定挂载的卷是没有被删除的。

posted @ 2024-09-12 13:01  takenika  阅读(54)  评论(0)    收藏  举报