在前面的几个章节里,我们已经学习了容器,镜像,镜像仓库的概念和用法,也知道了应该如何创建镜像,再以容器的形式启动应用。
不过,用容器来运行busybox、hello?world?、这样比较简单的应用还好,如果是nginx,redis,mysql这样的后台服务应用,因为他们运行在容器的沙盒里,完全与外界隔离,无法对外提供服务,也就失去了价值,这个时候,容器的隔离环境反而成为了一种负面特性。
所以,容器的这个小板房不应该是一个完全密闭的铁屋子,而是应该给他开几扇门窗,让应用在足不出户的情况下,也能够与外界交换数据,互通有无,这样的有限的合理,才是我们真正所需要的运行环境,
那么今天,我就以docker为例,来说一说有哪些手段能够在容器与外部系统之间沟通交流。
我们首先来看看docker提供的cp命令,它可以在宿主机和容器之间拷贝文件,是最基本的一种数据交换功能。
是验证命令需要先使用docker?run?启动一个容器,就用redis吧:
docker?run?-d?--rm?redis?
注意这里使用了-d、--rm两个参数,表示运行在后台,容器结束后自动删除,然后使用docker?ps命令可以看到redis容器正在运行,容器的ID是?a788
docker?cp的用法很简单,很类似Linux的cp,scp,指定原路径(src?path)和目标路径(dest?path)就可以了,如果原路径是宿主机那么就是把文件拷贝进容器,如果原路径是容器那么就是把文件拷贝出容器,注意需要用容器名或者容器ID来指明是哪个容器的路径
假设当前目录下有一个a.txt的文件,现在我们就要把他拷贝进redis容器的/tmp目录,如果使用容器ID,命令就会是这样:
docker?cp?a.txt?a78:/tmp
接下来我们可以使用docker?exec?命令,进入容器看看文件是否已经正确拷贝了:
docker?exec?-it?a78?sh
可以看到,在tmp目录下,确实有一个a.txt
现在让我们再来试验一下从容器拷贝出文件,只需要把docker?cp后面的两个路径调换一下位置:
docker?cp?a78:/tmp/a.txt?./b.txt
这样,在宿主机的当前目录里,就会多出一个新的b.txt,也就是从容器里拿到的文件
docker?cp的用法模仿了操作系统的拷贝命令,偶尔一两次的文件共享还可以应付,如果容器运行时经常有文件来互通,这样反复的考来考去就显得很麻烦,也很容易出错。
你也许会联想到虚拟机有一种共享目录的功能,他可以在宿主机上开一个目录,然后把这个目录挂载进虚拟机,这样就实现了两者共享同一个目录,一边对目录里文件的操作,另一边like就能看到,没有了数据拷贝,效率自然也会高很多,沿用这个思路,容器也提供了这样的共享宿主机目录的功能,效果和虚拟机几乎一模一样,用起来很方便,只需要在docker?run命令启动容器的时候使用-v参数就行,具体的格式是宿主机路径:容器内路径
咱们还是以redis为例,启动容器,使用-v参数把本机的/tmp目录挂在到容器里的/tmp目录,也就是说让容器共享宿主机的/tmp目录
docker?run?-d?--rm?-v?/tmp:/tmp?redis
然后我们再用docker?exec?进入容器,查看一下容器内的/tmp目录,应该就可以看到文件与宿主机是完全一致的
docker?exec?-it?1ea?sh???#1ea是新启动的redis容器ID
你也可以在容器里的/tmp目录下随便做一些操作,比如删除文件,建立新目录等等,再回头观察一下宿主机,会发现修改会及时同步,这就表明容器和宿主机确实已经共享了这个目录。
-v?参数挂载宿主机目录这个功能,对于我们日常开发测试工作来说非常有用,我们可以在不变动本机环境的前提下,使用镜像安装任意的应用,然后直接以容器来运行我们本地的源码,脚本,非常方便。
这里我举一个简单的例子,比如我本机上只有Python2.7,但我想用Python3开发,如果溶蚀Python2和Python3很容易就会把系统搞乱,所以我们可以这么做:
1-先使用docker?pull?拉取一个python3?的镜像,因为他打包了完整的运行环境,运行时有隔离,所以不会对现有系统的Python2.7产生任何影响
2-在本地某个目录编写Python代码,然后用-v参数让容器共享这个目录
3-现在就可以在容器里以Python?3来安装各种包,再运行脚本做开发了。
显然,这种方式比把文件打包到镜像或者docker?cp会更加灵活,非常适合有频繁修改的开发测试工作
现在我么使用docker?cp和docker?run?-v可以解决容器与外界的文件互通问题,但对于NGINX,Redis,这些服务器来说,网络互通才是更要紧的问题
网络的关键在于打通容器内外的网络,二处理网络通信无疑是计算机系统里最棘手的工作之一,有许许多多的名词,协议,工具,在这里我也没有办法就把它一下子就说清楚,所以只能从宏观层面讲个大概,帮助你快速理解
docker?提供了三种网络模式,分别是null,host,bridge
null是最简单的模式,也就是没有网络,但允许其他的网络插件来自定义网络连接,这里就不多做介绍了
host的意思是直接使用宿主机网络,相当于去掉了容器的网络隔离(其他隔离依然保留),所有的容器会共享宿主机的IP地址和网卡,这种模式没有中间层,自然通信效率高,但缺少了隔离,运行太多的容器就容易导致冲突。
host模式需要在docker?run时使用--net=host参数,下面我就用这个参数启动nginx:
docker?run?-d?--rm?--net=host?nginx:alpine
为了验证效果,我们可以在本机和容器里分别执行ip?addr命令,查看网卡信息
ip?a????#查看本机网卡
docker?exec?xxxx?ip?a??????#查看容器网卡
可以看到这两个ip?addr命令输出信息是完全一样的,比如都是一个网卡ens33,ip地址是192.168.89.128,这就证明nginx容器确实与本机共享了网络栈
第三种bridge,也就是桥接模式,他有点类似于现实世界的交换机,路由器,只不过是由软件虚拟出来的,容器和宿主机在通过虚拟网卡接入这个网桥,(docker0),那么他们之间也就可以正常的手法网络数据报了,不过和host1相比,bridge模式多了虚拟网桥和网卡,通信效率会低一些
和host吗,模式一样,我们也可以用--net=bridge来启用桥接模式,但其实并没有这个必要,因为docker默认的网络模式就是bridge,所以一般不需要显式指定、
下面我们启动两个容器,NGINX和Redis,就想刚才说的,没有特殊指定就会使用bridge模式:
docker?run?-d?--rm?nginx:alpinedocker?run?-d?--rm?redis
然后我们还是在本机和容器里执行,ip?addr命令,(Redis容器里面没有ip命令,所以只能在nginx容器里执行
对比刚才的host模式,就可以发现容器里的网卡这是与素质及网全不通,eth0是一个虚拟网卡,ip地址是B类私有地址172.17.0.2
我们还可以用docker?inspect?直接查看容器的ip地址:
docker?inspect?xxx?|?grep?IPAddress
这显示两个容器的ip地址分别是172.17.0.2和172.17.0.3,而宿主机的IP地址则是172.17.0.1,所以他们都在172.17.0.0/16这个docker的默认网段,彼此之间就能够使用IP地址来实现网络通信了
使用host模式或者bridge模式,我们的容器就有了IP地址,简历了与外界世界的网络连接,接下里要解决的就是网络服务的端口号问题
你一定知道,服务器应用都必须要有端口号才能对外提供该服务,比如HTTP协议用80,HTTPS用443,redis是6379,MySQL是3306,在第四章的时候书写dockerfile的时候看到过,可以用EXPOSE指令声明容器对外的端口号
一台主机上的端口号数量是有限的,而且多个服务质检还不能够冲突,但我们打包镜像应用的时候通常都是用的是默认端口,容器实际运行起来就很容易因为端口号被占用而无法启动。
解决这个问题的办法就是加入一个中间层看,有容器环境例如docker?来同意管理分配端口号,在本机端口和容器端口之间做一个映射操作,容器内部还是用自己的端口号,但外界看到的确实另外一个端口号,这样就很好的避免了冲突
端口号映射需要使用bridge模式,并且在docker?run启动容器时使用-p参数,形式和共享目录的-v参数很类似,用:分隔本机端口和容器端口。比如,如果要启动两个nginx容器,分别跑在80和8080端口上:
docker?run?-d?-p?80:80?--rm?nginx:alpine
docker?run?-d?-p?8080:80?--rm?nginx:alpine
这样就把本机的80和8080端口分别映射到了两个容器里的80端口,不会发生冲突,我们可以用curl再验证一下
顺便使用了docker?ps命令,能够查看PORTS栏里的端口映射情况
今天这里写了容器与外部系统之间的沟通交流的集中方法
你会发现,这些方法几乎消除了容器化的应用和本地应用因为隔离特性而产生的差异,而因为镜像独特的打包机制,容器技术显然能够比apt,yum更方便的安装各类应用,绝不会污染已有的系统
今天是列举了Python,NGINX等例子,你还可以举一反三,借鉴他们把本地配置文件加载到容器里适当的位置,再映射端口号,把Redis,MySQL,node.js都运行起来,让容器成为我们工作中的得力助手
照例简单小结一下这次的要点:
1,docker?cp?命令可以在容器和主机之间互相拷贝文件,适合简单的数据交换。
2,docker?run?-v命令可以让容器和主机共享本地目录,免去了拷贝操作,提升工作效率
3,host网络模式让容器与主机共享网络栈,效率高但容易导致端口冲突
4,bridge网络模式实现了一个虚拟网桥,容器和主机都在一个私有网段内互联互通
5,docker?run?-p?命令可以把主机的端口号映射到容器的内部端口号,解决了潜在的端口冲突问题
1,你能说出今天学习的docker?cp?命令和第四章的docker?file里的COPY指令有什么区别吗?
2,你觉得host模式和bridge模式各有什么优缺点,在什么场景下应用最合适?