1. 端口转发
  2. 容器卷

1.端口转发

”p“  创建应用容器的时候,一般会做端口映射,这样是为了让外部能够访问这些容器里的应用。可以用多个-p指定多个端口映射关系。

# docker run --name mysql1 -p 3307:3306  -e MYSQL_ROOT_PASSWORD=123 daocloud.io/library/mysql

通过本地IP的3307端口访问容器mysql1内的数据库:

# mysql -u root -p123 -h 192.168.245.134 -P3307

Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。如下

# docker run --name myredis -P -d docker.io/redis

本地主机的32768端口被映射到了redis容器的6379端口上,也就是说访问本机的32768
端口即可访问容器内redis端口。

# docker run --rm -it --name myredis2 --link myredis:redisdb docker.io/redis /bin/bash
# redis-cli -h redisdb -p 6379
 redisdb:6379> set wing 123
 OK
 redisdb:6379>

在别的机器上通过上面映射的端口32768连接这个容器的redis:

redis-cli -h 192.168.245.134 -p 32768
 192.168.1.23:32768> get wing
 "123"

2.容器卷

        2.1  容器卷

容器卷是容器和宿主机之间的文件共享方式之一

新卷只能在容器创建过程当中挂载:

# docker run -it --name="voltest" -v /tmp:/test  daocloud.io/library/centos:5 /bin/bash

共享其他容器的卷:

# docker run -it --volumes-from bc4181  daocloud.io/library/centos:5  /bin/bash

实际应用中可以利用多个-v选项把宿主机上的多个目录同时共享给新建容器:

# docker run -it -v /abc:/abc -v /def:/def 1ae9
# docker run  -v /vol/index.html:/usr/share/nginx/html/index.html -it nginx /bin/bash

如果是文件共享,数据不能同步更新

        2.2 Volume

容器技术使用了 rootfs 机制和 Mount Namespace,构建出了一个同宿主机完全隔离开的文件系统环境。这时候,就需要考虑这样两个问题:

容器里进程新建的文件,怎么才能让宿主机获取到?

宿主机上的文件和目录,怎么才能让容器里的进程访问到?

这正是 Docker Volume 要解决的问题:Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。

在 Docker 项目里,它支持两种 Volume 声明方式,可以把宿主机目录挂载进容器的 /test 目录当中:

# docker run -v /test ...
# docker run -v /home:/test ...

这两种声明方式的本质是相同的:都是把一个宿主机的目录挂载进了容器的 /test 目录。

第一种情况没有显示声明宿主机目录,Docker 就会默认在宿主机上创建一个临时目录 /var/lib/docker/volumes/[VOLUME_ID]/_data,然后把它挂载到容器的 /test 目录上。

第二种情况,Docker 就直接把宿主机的 /home 目录挂载到容器的 /test 目录上。

已经介绍过,当容器进程被创建之后,尽管开启了 Mount Namespace,但是在它执行 chroot(或者 pivot_root)之前,容器进程一直可以看到宿主机上的整个文件系统。

而宿主机上的文件系统,也自然包括了要使用的容器镜像。这个镜像的各个层,保存在 /var/lib/docker/aufs/diff 目录下,在容器进程启动后,它们会被联合挂载在 /var/lib/docker/aufs/mnt/ 目录中,这样容器所需的 rootfs 就准备好了。

所以,只需要在 rootfs 准备好之后,在执行 chroot 之前,把 Volume 指定的宿主机目录(比如 /home 目录),挂载到指定的容器目录(比如 /test 目录)在宿主机上对应的目录(即 /var/lib/docker/aufs/mnt/[可读写层 ID]/test)上,这个 Volume 的挂载工作就完成了。

由于执行这个挂载操作时,"容器进程"已经创建了,也就意味着此时 Mount Namespace 已经开启了。所以,这个挂载事件只在这个容器里可见。你在宿主机上,是看不见容器内部的这个挂载点的。这就保证了容器的隔离性不会被 Volume 打破。

注意:这里提到的 " 容器进程 ",是 Docker 创建的一个容器初始化进程 (dockerinit),而不是应用进程 (ENTRYPOINT + CMD)。dockerinit 会负责完成根目录的准备、挂载设备和目录、配置 hostname 等一系列需要在容器内进行的初始化操作。最后,它通过 execv() 系统调用,让应用进程取代自己,成为容器里的 PID=1 的进程。

而这里要使用到的挂载技术,就是 Linux 的绑定挂载(bind mount)机制。它的主要作用就是,允许你将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上。并且,这时你在该挂载点上进行的任何操作,只是发生在被挂载的目录或者文件上,而原挂载点的内容则会被隐藏起来且不受影响

其实,如果你了解 Linux 内核的话,就会明白,绑定挂载实际上是一个 inode 替换的过程。在 Linux 操作系统中,inode 可以理解为存放文件内容的"对象",而 dentry,也叫目录项,就是访问这个 inode 所使用的"指针"

mount --bind /home /test,会将 /home 挂载到 /test 上。其实相当于将 /test 的 dentry,重定向到了 /home 的 inode。这样当修改 /test 目录时,实际修改的是 /home 目录的 inode。这也就是为何,一旦执行 umount 命令,/test 目录原先的内容就会恢复:因为修改真正发生在的,是 /home 目录里。

进程在容器里对这个 /test 目录进行的所有操作,都实际发生在宿主机的对应目录(比如,/home,或者 /var/lib/docker/volumes/[VOLUME_ID]/_data)里,而不会影响容器镜像的内容。

这个 /test 目录里的内容,既然挂载在容器 rootfs 的可读写层,它会不会被 docker commit 提交掉呢?

也不会。

原因前面提到过。容器的镜像操作,比如 docker commit,都是发生在宿主机空间的。而由于 Mount Namespace 的隔离作用,宿主机并不知道这个绑定挂载的存在。所以,在宿主机看来,容器中可读写层的 /test 目录(/var/lib/docker/aufs/mnt/[可读写层 ID]/test),始终是空的。

不过,由于 Docker 一开始还是要创建 /test 这个目录作为挂载点,所以执行了 docker commit 之后,新产生的镜像里,会多出来一个空的 /test 目录。毕竟,新建目录操作,又不是挂载操作,Mount Namespace 对它可起不到"障眼法"的作用。

1.启动一个 helloworld 容器,给它声明一个 Volume,挂载在容器里的 /test 目录上:

# docker run -d -v /test helloworld

2.容器启动之后,查看一下这个 Volume 的 ID:

# docker volume ls

3.使用这个 ID,可以找到它在 Docker 工作目录下的 volumes 路径:

# ls /var/lib/docker/volumes/cb1c2f7221fa/_data/

这个 _data 文件夹,就是这个容器的 Volume 在宿主机上对应的临时目录了。

4.在容器的 Volume 里,添加一个文件 text.txt:

# docker exec -it cf53b766fa6f /bin/sh
cd test/
 touch text.txt


5.再回到宿主机,就会发现 text.txt 已经出现在了宿主机上对应的临时目录里:

# ls /var/lib/docker/volumes/cb1c2f7221fa/_data/
text.txt


可是,如果你在宿主机上查看该容器的可读写层,虽然可以看到这个 /test 目录,但其内容是空的:

# ls /var/lib/docker/aufs/mnt/6780d0778b8a/test

可以确认,容器 Volume 里的信息,并不会被 docker commit 提交掉;但这个挂载点目录 /test 本身,则会出现在新的镜像当中。以上内容,就是 Docker Volume 的核心原理了。

Docker 容器"全景图":


docker iptables 不转发 docker内部端口转发_服务器

 一个"容器",实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。