
(第3章)Docker核心原理解读(续)
发布日期:2021-05-08 17:53:45
浏览次数:25
分类:精选文章
本文共 9686 字,大约阅读时间需要 32 分钟。
文章目录
1.Docker数据卷
- 类似于mount操作,用户将一个文件夹作为volume挂载到容器上,可以将数据添加到容器中,以供其中的进程使用。 多个容器可以共享同一个volume
- 创建volume,挂载volume,以及使用Dockerfile添加volume,共享volume,珊瑚volume,备份恢复或迁移volume
(1)创建volume创建了一个指定名字的volumedocker volume creat --name vol_simle挂载到容器的/data目录下docker run -d -v /data ubuntu /bin/bash创建一个指定名字的volume,并挂载到容器中的/data目录docker run -d -v vol_simple: /data ubuntu /bin/bash获取该volume在宿主机中该文件夹的位置等信息docker volume inspect vol_simple(2)挂载volume创建一个指定名字的volume,并挂载到容器中的/data目录docker run -d -v vol_simple: /data ubuntu /bin/bash创建一个随机ID的volume,并将其挂载到/data下docker run -d -v /data ubuntu /bin/bash将宿主机上的目录挂载到容器中在/host/dir文件夹中的所有文件或文件夹都可以在容器的/container/dir文件夹下被访问,若镜像中的/container/dir文件夹下有内容,将会被隐藏docker run -v /host/dir:/container/dir ubuntu /bin/bash将单个文件作为volume挂载到容器中文件必须使用绝对路径docker run -it --name vol_file -v /host/file:/container/file ubuntu /bin/bash为容器添加多个volumedocker run -it --name vol_mult -v /data1 -v /data2 -v /host/dir:/container/dir ubuntu /bin/bash(2)使用Dockerfile添加volume若镜像中存在/data文件夹,该文件夹中的内容将全部被复制到宿主机中对应的文件夹中,并且依据容器中的文件来设置合适的权限和所有者VOLUME /dataDockerfile中除了FROM指令的每一行都是基于上一行生成的临时镜像,运行一个容器,执行一条指令并执行类似docker commit的命令得到一个新的镜像,docker commit的命令不会对挂载的volume进行保存FROM ubuntuRUN useradd fooRUN mkdir /data && touch /data/fileRUN chown -R foo:foo /dataVOLUME /data(3)共享volume新创建的容器vol_use与之前创建的容器vol_simple共享volume,这个volume目的目录也是/data;若被共享的容器有多个volume(eg:上面的vol_mult),新容器也将有多个volume,并且其挂载的目的目录也与vol_multdocker run --rm -it --name vol_use --volumes-from vol_simple ubuntu /bin/bash容器与多个已有容器共享volumedocker run --rm -it --name vol_use_mult --volumes-from vol_1 --volumes-from vol_2 ubuntu /bin/bash创建一个挂载了volume的数据容器vol_data,该容器仅仅输出了一条提示后就停止运行以避免浪费资源。接下来的两个容器vol_share1和vol_share2与这个数据容器共享了这个volumedocker run --name vol_data -v /data ubuntu echo "This is a data-only container"docker run -it --name vol_share1 --volumes-from vol_data ubuntu /bin/bashdocker run -it --name vol_share2 --volumes-from vol_data ubuntu /bin/bash(4)删除volume只要在创建容器时挂载了volume,在/var/lib/docker/volumes下就会生成与volume对应的目录,使用docker rm删除容器并不会删除与volume对应的目录。只有当没有任何容器使用该volume时,该volume才能成功删除docker volume rm删除volume下面的2种方法:只会对挂载在该容器上的未命名的volume进行删除,而对用户指定名字的volume进行保留docker rm -v 删除容器docker run --rm ,--rm标签会在容器停止时,删除容器以及容器所挂载的volume(5)备份恢复或迁移volume备份volume方法:方法1:利用docker inspect命令查找到/data在宿主机上对于的文件夹位置,复制其内容,利用tar打包方法2:vol_simple容器中包含了需要备份的volume该命令启动了一个临时容器,该容器挂载了2个volume,第一个volume来自vol_simple容器的共享,即需要备份的volume第二个volume将宿主机的当前目录挂载到容器的/backup下。容器运行后,将要备份的内容(/data文件夹)备份到/backup/data.tar,然后删除容器,备份后data.tar就留在了当前的目录docker run --rm --volumes-from vol_simple -v $(pwd):/backup ubuntu tar cvf /backup/data.tar /data恢复volume方法:首先运行一个新容器作为数据恢复的目标第二个指令:挂载了两个volume,第一个volume与要恢复的volume共享,第二个volume将宿主机的当前目录挂载到容器的/backup下容器启动后,将这个存档文件中的/data恢复到根目录下,然后删除容器,恢复后的数据就在vol_bck的volume中docker run -it --name vol_bck -v /data ubuntu /bin/bashdocker run -rm --volumes-from vol_bck -v $(pwd):/backup ubunttu tar xvf /backup/data.tar -C /
- Docker的volume的本质是容器中的一个特殊的目录 Docker会将宿主机上的指定目录(一个volume ID为名称的目录,或指定的宿主机的目录)挂载到容器中指定的目录上,挂载完成胡的宿主机目录和容器内的目录表现一致。
docker run -v /data busybox /bin/bash指定容器里的/data目录为一个volume将宿主机上的volume_id目录绑定挂载到rootfs中指定的挂载点/data上mount("/var/lib/docker/volumes/volume_id/_data","rootfd/data","none",MS_BIND,NULL)docker run -v /var/log:/data busybox /bin/bash将宿主机上的/var/log目录绑定挂载到rootfs中指定的挂载点/data上mount("/var/log","rootfs/data","none",MS_BIND,NULL)容器内部进行只能看见以rootfs为跟的文件内容以及被mount到rootfs之下的目录下面的data目录就是生成出来的volume挂载点root@0a4b299bb8e8:/# lsbin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys test.txt tmp usr var
-
创建volume原理
(1)容器创建阶段 (2)容器的启动阶段,libcontainer使用组装好的挂载点列表进行mount操作,完成volume的创建 -
volume相关的配置文件:该容器所使用的volumeID以及他们的可写情况
位置:每个容器在/var/lib/docker/containers/容器ID/config.json
2.Docker网络基础
- Docker公司在libnetwork中使用了CNM(Container Network Model),CNM定义了构建容器虚拟化网络的模型,还提供了可以用于开发多种网络驱动的标准化接口和组件 (1)CNM的3个核心组件: 沙盒:包含一个容器网络栈的信息,对容器的接口,路由和DNS设置进行管理 端点:veth pair,Open vSwitch内部的端口或者相似设备 网络:Linux bridge,VLAN等 (2)CNM的5种内置驱动:bridge驱动,host驱动,overlay驱动,remote驱动,null驱动
- 在单一主机上使用Docker默认的bridge驱动进行演示
上图中:backend network为后端网络,frontend network则为前端网络(1)创建backend和frontend网络docker network creat backenddocker network creat frontend查看主机上的所有Docker网络;docker network ls。。。 bridge bridge。。。 none null。。。 host host上面3个是Docker内置的网络,无法使用docker network rm进行删除(2)将container1和container2的容器加入到backend网络将container3的容器加入到frontend网络docker run -it -name container1 --net backend busyboxdocker run -it -name container2 --net backend busyboxdocker run -it -name container3 --net frontend busybox(3)将container2加入到frontend网络socker network connect frontend container2(4)在container2中使用ifconfig可以发现:有两块网卡eth0和eth1,两个ip分别在不同的ip段
- bridge驱动实现机制分析:(1)Docker0网桥
(1)在宿主机上使用ifconfig命令:可以看到多了一块名为docker0的网卡,其IP默认为172.17.0.1/16,之后创建的Docker容器都会在docker0子网的范围内选取一个未占用的ip来使用,并连接到docker0网桥上。在主机上输入:route -n...172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0表示:所有目的IP地址的为172.17.0.0/16的数据包从docker0网卡发出使用docker run创建一个容器名为con1的容器,在con1容器中可以看到他有两块网卡:网卡lo:容器的回环网卡;网卡eth0:容器与外界通信的网卡,eth0的IP为172.17.0.2/16,和宿主机上的网桥docker0在同一个网段;(2)查看容器con1的路由表,发现con1的默认网关是宿主机的docker0网卡。con1的eth0网卡与宿主机的docker0网卡是互相连通的。(3)由于veth pair总是成对出现的,veth pair通常用来连接两个network namespace查看宿主机的网络设备发现会有一块以veth开头的网卡,另一个是Docker容器con1中的eth0.所以docker0就是一个网桥(4)下图3-18是:创建了docker0网桥,并以veth pair连接各容器的网络,容器中的数据通过docker0网桥转发给eth0的网卡上.这里的网桥相当于是交换机,工作在二层,所以不需要配置ip地址。(5)Docker0作为二层设备,为什么要配置IP地址?docker0是普通的Linux网桥,可认为其内部有一个可以用于配置IP信息的网卡接口。docker0的IP地址可以作为连于之上的容器的默认网管地址存在!(6)创建网桥可以使用--bridge+BRIDGE参数指定,也可以添加一个名为br0的网桥,并且为其配置ipbrctl addbr br0ifconfig br0 188.18.0.1查看本机的Linux网桥及其端口brctl show
- bridge驱动实现机制分析:(2)iptables规则
(1)iptables规则主要用于Docker容器和容器之间以及和外界的通信在宿主机上查看:iptables-save。。。-A POSTROUTING -s 172.17.0.0/16 !-o docker0 -j MASQUERAED。。。上面的这条规则表示:Docker容器与外界通信时,将源地址为172.17.0.0/16的数据包(从Docker容器发出的数据),当不是从docker0网卡发出时做SNAT(源地址转换,将IP包的源地址替换为相应网卡的地址)这样,玩不就感觉不到Docker容器的存在。(2)eg:目的地址转换DNATdocker run -d -p 5000:5000 training/webapp python app.py在主机上输入:iptables-save...*nat-A DOCKER | -i docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.17.0.4:5000...*filter-A DOCKER -d 172.17.0.0.4/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT上述的规则将访问宿主机5000端口的流量转发到172.1.0.4的5000端口上(真正提供服务的Docker容器的IP端口),所以外界访问Docker容器都是通过iptables做DNAT(3)Docker容器之间的通信也受到iptables规则限制。同一台宿主机上的Docker容器默认都连在docker0网桥上,它们属于一个子网。在Docker容器和外界通信的过程中,还涉及到docker0网卡到宿主机eth0的转发,还需内核将ip-forward功能打开。
- bridge驱动实现机制分析:(3)Docker容器的DNS和主机名
(1)同一个Docker镜像可以启动很多个Docker容器,通过查看,发现他们的主机名不一样,即:主机名并非是被写入镜像中的。(2)/etc/hostname,/etc/hosts,/etc/resolv.conf这三个文件,实际会被容器启动后的虚拟文件覆盖掉的。在主机输入mount,也可以看到:.../dev/disk............on /etc/hostname type ext4/dev/disk............on /etc/hosts type ext4/dev/disk............on /etc/resolv.conf type ext4...
-
Docker网络现在是一个单独的库:libnetwork
-
传统link:若Docker使用默认的bridge模式网络,则会默认使用传统的link系统
Link特点:Link是一种比端口映射更亲密的Docker容器间的通信方式,提供了更安全,高效的服务,通过环境变量和/etc/hosts文件的设置,提供了别名到具体通信地址的发现,适合于一些需要各组件间通信的应用。
(1)新建一个含有数据库服务的Docker容器,取名为db。新建一个包含web应用的Docker容器,取名为web,并将web连接到dbweb容器称之为接收容器,db容器称之为源容器docker run -d --name db training/postgresdocker run -d -P --name web --link db:webdb training/webapp python app.pyDocker将连接信息以下面(2)和(3)的两种方式保存在接收容器中。(2)设置接收容器的环境变量当两个容器通过--link建立了连接后,会在接收容器中额外设置一些环境变量,以保存源容器的一些信息(3)更新接收容器的/etc/hosts文件link操作除了将link信息保存在接收容器中,还在/etc/hosts中添加了一项————源容器的IP和别名(--link),以用来解析源容器,以用来解析源容器的IP地址。当源容器重启后,会自动更新接收容器的/etc/hosts文件,需要注意的是:这里用的是别名,而不是源容器的主机名(实际上,主机名对外界是不可见的)。(4)建立iptables规则(防火墙规则)进行通信在接收容器上设置了环境变量和更改了/etc/hosts文件之后,接收容器仅仅是得到了源容器的相关信息(环境变量,IP地址),但是不代表源容器和接收容器在网络上可以互相通信。要为连接的容器添加特定的iptables规则,才能保证两个容器间的通信。eg:接着上面的web和db容器例子源容器db容器将tcp/5432端口暴露给外界来为外界提供服务,web容器和db容器需要在db容器的tcp/5432端口上进行通信。假设:web容器的IP地址为172.17.0.2/16,db容器的IP地址为172.177.0.1/16在主机上可以看到下面的iptables规则:-A DOCKER -s 172.17.0.2/32 -d 172.17.0.1/32 -i docker0 ...... --dport 5432 -j ACCEPT-A DOCKER -s 172.17.0.1/32 -d 172.17.0.2/32 -i docker0 ...... --dport 5432 -j ACCEPT
- 新link:若用户使用自定义的网络(user-defined network),则会使用新的link系统 (1)新旧link系统的一个重要区别是:新的link系统在创建一个link时,并不要求源容器已经创建或者启动。
使用bridge驱动创建一个自定义网络isolated_nw,再运行一个容器container1加入到该网络,并链接另一个容器contianer2(contianer2不存在)docker network creat isolated_nmdocker run --net=isolated_nm -it --name=container1 --link container2:c2 busyboxdocker run --net=isolated_nw -itd --name=container2 busybox测试:在container1容器内执行:ping c2cat /etc/hosts发现并没有container2的相关信息。实际上,Docker是通过DNS解析的方式提供名字和别名的解析,这很好地解决了在传统link系统中由于容器重启造成注入的环境变量更新不及时的问题。
3.Docker与容器安全
-
Docker daemon安全
(1)其默认是以Unix域套接字的方式来与客户端进行通信 (2)Docker提供了TLS(Transport Layer Security)传输层安全协议,其安全认证主要是在服务器端设置,客户端可以对服务端进行验证。 -
内核安全
(1)容器的本质是进程,cgroups的存在就是为了限制宿主机上不同容器的资源使用量,避免单个容器耗尽宿主机资源而导致其它容器异常 (2)namespace隔离容器,使容器与容器之间,容器与宿主机之间相互隔离。 -
磁盘资源限制问题
Docker通过镜像层叠的方式来构建其文件系统。 当需要改写文件时,把改写的文件复制到最顶层的读写层,其本质还是在宿主机的文件系统的某一目录下存储这些信息。 若一个容器把宿主机上所有的磁盘容量耗尽,节时,其它容器将无法进行文件存储的操作。
创建虚拟文件系统来对磁盘进行限额首先创建虚拟文件系统,接着把Docker的rootfs构建于文件系统之上(1)创建一个4G的文件dd if=/dev/zeor of=/usr/dick-quota.ext3 count=4096 bs=1M(2)在磁盘文件上创建文件系统mkfs -t ext3 -F /usr/disk-quota.ext3(3)挂载这个文件系统到指定的容器目录mount -o loop,rw,usrquota,grpquota /usr/disk-quota.ext3 /path/to/image/top/lever将创建的虚拟文件系统作为rootfs最顶层的layer对于需要共享的数据,可以通过挂载volume方式进行磁盘数据存储
-
容器逃逸问题
在全虚拟化和半虚拟化中,每一个租户都独立运行一个内核。 操作熊虚拟化指的是共享内核,内存,CPU以及磁盘等,所以容器的安全问题特别突出。 解决办法:设置黑白名单 -
容器DoSe攻击与流量限制问题
实际上所有容器在共用一张物理网卡,如果在同一宿主机中的某一个容器抢占了大部分带宽,将会影响其它容器的使用,例如:大流量的容器内下载程序会影响其它交互式应用的访问。 -
通过ulimit限制最大进程数目
Docker无法使用ulimit来限制fork炸弹问题; fork炸弹:以极快的速度创建大量进程,并以此来消耗系统分配给进程的可用空间使得进程表包和,从而系统无法运行新程序
我们使用daemon用户启动4个容器,并设置运行的最大进程数为3docker run -d -u daemon --ilimit nproc=3 busybox topdocker run -d -u daemon --ilimit nproc=3 busybox topdocker run -d -u daemon --ilimit nproc=3 busybox topdocker run -d -u daemon --ilimit nproc=3 busybox top##失败,资源不足我们本想的是:在每个容器里,用户最多只能创建3个进程。现在在使用daemon用户在启动4个容器的时候就失败了。nproc调节的是属于一个用户UID的最大进程数之和。
<参考:Docker+容器与容器云>
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2025年04月11日 20时47分04秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
C语言实现面向对象方法学的GLib、GObject-初体验
2021-05-09
系统编程-进程-ps命令、进程调度、优先级翻转、进程状态
2021-05-09
为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?
2021-05-09
一个支持高网络吞吐量、基于机器性能评分的TCP负载均衡器gobalan
2021-05-09
HDOJ2017_字符串统计
2021-05-09
高等软工第二次作业《需求分析阶段总结》
2021-05-09
404 Note Found 团队会议纪要
2021-05-09
CentOS安装Docker-ce并配置国内镜像
2021-05-09
使用JWT作为Spring Security OAuth2的token存储
2021-05-09
使用Redis作为Spring Security OAuth2的token存储
2021-05-09
【SOLVED】Linux使用sudo到出现输入密码提示延迟时间长
2021-05-09
项目引入非配置的文件,打成war包后测试报错的可能原因
2021-05-09
Git学习笔记
2021-05-09
SpringBoot笔记
2021-05-09
让你的代码更优秀的 14 条建议
2021-05-09
不需要爬虫也能轻松获取 unsplash 上的图片
2021-05-09
将博客搬至CSDN
2021-05-09