docker的联网和服务发现
对于docker入门人员来讲,基本的docker概念已经理解清楚,基本的docker使用已经掌握,容器启停,容器数据持久化,容器访问,容器关联(–link),那么就比较适合看这一篇文章,docker的联网和服务发现。
在分布式系统中,一个服务就会有多个实例,而且这些个实例不会一直存在,是动态创建和关闭的,服务发现的一种实现方式就是让客户端只用服务名称发出请求,然后在后台(或者说平台)用特殊的方式转换成合适的地址,这些方式包含但不限于以下几种,简单的大使容器,服务发现方案(比如Consul),联网方案(比如Weave)。
其实到这里大概能够理清楚,服务发现允许客户端发现服务实例,联网则负责把相关部分连接起来。
1、最简单的跨主机方案“大使容器”
将不同主机上的容器连接起来的一种方法叫做大使容器,它其实是代理容器,代替真正的容器或者服务接收网络通信,并把流量转发到真正的服务中去。
大使容器的优点在于,无需修改任何代码的情况下,让生产环境的联网架构有别于开发环境,开发人员可以放心的使用本地的数据库和其他资源,因为他们知道运维人员不需要修改任何代码的情况下,就可以应用程序改为使用集群服务或者远程资源。
大使容器的缺点在于,他需要更多的配置工作和额外的开销,而且这是一个潜在的故障点,当需要多个连接的时候,它很快就会变得很复杂,造成管理上的一个负担。
大使容器本身可以是一个非常简单的容器,需要做的事情就是建立应用程序和服务之间的连接,我们可以采用amouat/ambassador
镜像,这个镜像使用socat来设置大使容器和目标之间的流量转发,通过使用Docker连接生产的环境变量相同的形式来定义转发的目标位置,这个容器很小,使用的上层镜像是极简的Alpine Linux
发行版,大小仅有7M。
如何将两台主机上运行的docker容器联系起来,
主机a:
docker run -d --name real-redis redis:3
docker run -d --name real-redis-ambassador \
-p 6379:6379 \ ➊
--link real-redis:real-redis \ ➋
amouat/ambassador
主机b:
docker run -d --name redis_ambassador --expose 6379 \
-e REDIS_PORT_6379_TCP=tcp://主机aIP:6379 \ ➊
amouat/ambassador
docker run -d --name dnmonster amouat/dnmonster:1.0
docker run -d --link dnmonster:dnmonster \
--link redis_ambassador:redis \
-p 80:9090 \
amouat/identidock:1.0
2.服务发现
定义,为某个服务的客户端自动提供连接至该服务的合适实例信息的过程。
2.1、etcd
etcd是一个分布式的键值对,k8s底层存储使用的就是etcd,它使用go语言实现了raft consensus算法,高效和容错是它的目标。
etcd和consul建议的集群大小是3,5,7这样可以在容器能力和性能之间取得平衡。
具体etcd的安装可以参考etcd官网,https://etcd.io/。
etcd作为一个高可用的键值对存储,可以帮助我们实现服务发现,如果程序在启动的时候自动注册服务地址,就可以实现全自动化了。
2.2、skyDNS
SkyDNS(https://github.com/skynetservices/skydns) 在etcd 之上实现了一个基于DNS 的服务发现方案。值得一提的是,Google Container Engine 正是采用了SkyDNS 来实现Kubernetes 的服务发现功能。
etcd添加是可以DNS的配置,举个例子看一下
#在etcd中存储相关配置
curl -XPUT http://${HOSTA}:2379/v2/keys/skydns/config \
-d value='{"dns_addr":"0.0.0.0:53", "domain":"identidock.local."}' | jq .
#启动skydns
docker run -d -e ETCD_MACHINES="http://${HOSTA}:2379,http://${HOSTB}:2379" \
--name dns skynetservices/skydns:2.5.2a
#这时候我们就有dns服务器了 看一下服务注册
#启动容器 在机器a上执行
docker run -d -p 6379:6379 --name redis redis:3
#服务地址注入etcd
curl -XPUT http://${HOSTA}:2379/v2/keys/skydns/local/identidock/redis \
-d value='{"host":"'$HOSTB'","port":6379}' | jq .
#启动新的容器,nds指向dns容器负责域名查询 在机器b上执行
docker run --dns $(docker inspect –f dns) -it redis:3 bash
# ping地址 就可以访问了
ping redis.identidock.local
我们不希望每次创建容器的时候都去执行 –dns –dns-search ,就可以在执行Docker守护进程的时候指定这两个选项,但是如果dns也是一个容器的话,就需要另一种做法,把域名都能信息添加到主机 的/etc/resolv.conf文件,这个文件会告诉操作系统如何寻找域名。
更好的了解skyDNS 的工作原来,可以使用skyDNS的dig工具,
docker exec -it dns sh
/ # dig @localhost SRV redis.identidock.local
dig @localhost SRV redis.identidock.local
SkyDNS 使用了SRV,或称之为服务(service)的记录,以及传统的A 记录(用于解析IPv4 地址)。SRV 是返回的众多记录中的一个,包含服务的端口号、存活时间(time-tolive,TTL)、优先级和权重。设置TTL 的用途之一是为了确保当客户端或代理没有定时更新它时,记录能够被自动删除。它能够用于实现故障切换,以及比简单地让客户端超时更温和的错误处理办法。
SkyDNS还有其他的功能,包括把多台主机集合成为一个地址池,用作负载均衡,以及把指标和统计数据发布至Prometheus和Graphite等服务。
2.3、Consul
Consul(https://consul.io)是Hashicorp 公司对服务发现这个难题的回应。除了作为一个分布式且高度可用的键值存储,它还有先进的健康检查功能,并默认配备了DNS 服务器。
CAP定理,一个分布式系统不可能同事满足一致性,可用性,分区容忍三个特性。
etcd和consul都是基于CP系统的raft算法,consul具有三种不同的模式,default,consistent(一致性),stale(陈旧),能够在一致性和可用性之间提供不同程度的取舍。
2.4、自动注册
我们使用的etcd也好,skyDNS也好,Consul也好,服务注册都是手工完成,其实,我们可以把这个注册逻辑放到需要被管理的应用中,就可以使用自动注册,比如springboot项目配置consul地址就可以自动注册,Registrator能够与Consul,etcd配合使用,实现容器的自动注册,原理就是监听了Docker事件流中的容器创建事件,并且按照容器的元数据添加相关的条目到该容器使用的框架中。
3、联网选项
Docker默认的联网模式有四种,网桥,主机,容器,未联网。
3.1、网桥模式
默认的网桥开发阶段很有用,提供了一种无痛的方式,让容器可以相关通信,但是不适合生产环境,网桥背后涉及到了很多的底层工作,会导致非常大的开销。
容器启动成功后,Docker会生成一对veth接口,本质上相当于软件实现的以太网物理连接,Docker通过veth接口把容器的eth0连接到网桥。
在默认情况下,所有的容器都可以互相沟通(同一个主机下),无论是否已经被连接,是否公开端口,可以在docker守护进程启动的嘶吼加上–icc=false参数,这个参数可以设置一个iptables规则,把容器之间的通信关闭,如果同时设置了–icc=false 和 –iptables=true那么只有已连接的容器才能通信。
3.2、主机模式
如果容器用 –net=host参数启动,那么它辩护共享主机的网络命名空间,还会把自己暴露在公网之上,这个就代表了容器必须和主机公用一个IP地址,不过这个减少了网桥模式中涉及到的底层开销,因此速度和常规的主机网络一样。
可以考虑主机模式和网桥模式,外部网络流量打的容器(代理和缓存)可以使用主机模式,而其他容器使用内部网络的网桥模式。
3.3、容器模式
这个模式使用另一个容器的网络空间,在某些情况下很有用,这个模式便于创建和重复使用专为某些特定场景建立的高效网络栈,缺点就是所有共享同一网络栈的容器将使用相同的ip地址。
3.4、未联网模式
意味着把容器的网络完全关闭,适合无需任何联网的容器,比如把编译结果入到数据卷的编译容器。
4、docker中两个网络新对象
docker在网络领域提炼出了两个上层对象,分别是network和service,使得网络的创建和管理从容器中抽离,当容器启动的时候,可以被分配到某个网络,但只能和同一个网络中的其他容器直接联系,容器可以发布服务,并允许通过名称找到它,从而取代docker连接的必要。
可以使用 docker network 和 docker service 命令
5、网络解决方案
我们聊了很多主机内部通信方式,其实在生产,哪怕测试环境开始逐渐由容器集群组成,必须要面对的各个主机上的容器网络通讯问题。
5.1、overlay
overlay是docker为实现跨主机联网而开发的方案,使用VXLAN隧道来连接主机,主机有自己的ip地址空间,外网的连接通过NAT实现。
连接容器到overlay网络的方式和标准的网桥差不多,同样需要建立linux网桥,并需要一对veth接口用于容器之间互相连接。
5.2、weave
weave是一个开发者友好的联网解决方案,它的设计理念是希望开发者用最少的精力就能把它应用在不同的环境中,weave包含了用于服务发现和负载均衡的weaveDNS,自带的IP地址管理系统,并支持加密通信。
在每个主机上安装weave,会下载几个提供weave基础设计的镜像并启动。
5.3、flannel
Flannel是来自CoreOS 的一个跨主机联网解决方案。它主要用于以CoreOS 为基础的集群,不过也完全可以用在其他的栈上。
flannel会为每一台主机分配一个子网,然后按照子网给容器分配ip地址,flannel和kubernete搭配使用效果很好,可以给每一个pod分配唯一并且可经过路由传送的ip,flannel必须在每一个主机上运行一个守护进程,并且从etcd取得配置,因此集群必须已经配置好etcd。
5.4、calico
calico项目的联网方式稍有不同,便于理解,从OSI模型的角度来讲,概念上,OSI模型把网络分为七层,大多数的联网方案比如weave和flannel都是把第二层数据封装,形成一个在现有网络上叠加的网络,但是calico使用标准的ip路由和网络工具。