Calico:
Calico是一个纯三层的协议,为OpenStack和Docker容器提供多主机间通信。Calico不使用重叠网络,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达信息(路由)到剩余数据中心。
etcd:
etcd是一个高可用的键值(key/value)存储系统,主要用于共享配置和服务发现,etcd 会在集群的各个节点中复制这些数据并保证这些数据始终正确。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性,有基于HTTP+JSON的API接口。Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。
raft 共识算法的优点在于可以在高效的解决分布式系统中各个节点日志内容一致性问题的同时,也使得集群具备一定的容错能力。即使集群中出现部分节点故障、网络故障等问题,仍可保证其余大多数节点正确的步进。甚至当更多的节点(一般来说超过集群节点总数的一半)出现故障而导致集群不可用时,依然可以保证节点中的数据不会出现错误的结果。
关于raft算法,感兴趣的话,可以通过http://thesecretlivesofdata.com/raft/来查看动画演示过程。
Docker版本:1.12.1
Etcd版本:2.2.1
Calico版本:0.22.0
Centos操作系统:7.2
在/etc/hosts文件中添加如下主机信息:
192.168.1.23 dockera
192.168.1.24 dockerb
因为虚拟机环境无法连接Docker官网,这里采用RPM包离线安装方式。
相关包下载地址:
https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.12.1-1.el7.centos.x86_64.rpm
https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.1-1.el7.centos.noarch.rpm
将下载的包放到一个指定的路径中,然后执行如下命令安装Docker:
yum localinstall -y docker-engine*
systemctl enable docker.service
systemctl
startdocker
步骤一:下载etcd
https://github.com/coreos/etcd/releases/download/v2.2.1/etcd-v2.2.1-linux-amd64.tar.gz
步骤二:解压缩
tar xzvf etcd-v2.2.1-linux-amd64.tar.gz
步骤三:配置
mv etcd-v2.2.1-linux-amd64 /usr/local/
在/etc/profile文件中添加如下环境变量:
export PATH=/usr/local/etcd-v2.2.1-linux-amd64:$PATH
source /etc/profile
步骤四:启动etcd集群(具体参数的含义见“etcd集群扩容”章节)
192.168.1.23节点:
nohup etcd --name node1--initial-advertise-peer-urls http://192.168.1.23:2380 \
--listen-peer-urls http://0.0.0.0:2380 --listen-client-urlshttp://0.0.0.0:2379,http://0.0.0.0:4001 \
--advertise-client-urlshttp://0.0.0.0:2379 --initial-cluster-token etcd-cluster \
--initial-cluster node1=http://192.168.1.23:2380,node2=http://192.168.1.24:2380 --initial-cluster-state new &> etcd.log&
192.168.1.24节点:
nohup etcd --name node2--initial-advertise-peer-urls http://192.168.1.24:2380 \
--listen-peer-urls http://0.0.0.0:2380--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 \
--advertise-client-urlshttp://0.0.0.0:2379 --initial-cluster-tokenetcd-cluster \
--initial-cluster node1=http://192.168.1.23:2380,node2=http://192.168.1.24:2380 --initial-cluster-state new &>etcd.log &
注:为了方便etcd启动操作,可以将上面的内容封装为一个shell脚本。
步骤五:查看etcd集群状态
etcdctl -C http://192.168.1.23:2379,http://192.168.1.24:2379member list
输出结果为:
8b28023d2233a213:name=node1 peerURLs=http://192.168.1.23:2380 clientURLs=http://0.0.0.0:2379
d8251b9c0a2a054c:name=node2 peerURLs=http://192.168.1.24:2380 clientURLs=http://0.0.0.0:2379
当然也可以查看etcd集群的memebers:
curl -L http://192.168.1.23:2379/v2/members
因为etcd 是一个应用在分布式环境下的 key/value 存储服务,所以我们可以写一些数据,如下:
curl -L http://192.168.1.23:2379/v2/keys/newworld-XPUT -d value="Hello Docker"
输出结果为:
{"action":"set","node":{"key":"/newworld","value":"HelloDocker","modifiedIndex":2869,"createdIndex":2869}}
然后我们通过192.168.1.24节点查询键为newworld的:
curl -L http://192.168.1.24:2379/v2/keys/newworld
结果为:
{"action":"get","node":{"key":"/newworld","value":"HelloDocker","modifiedIndex":2869,"createdIndex":2869}}
我们也可以通过使用etcdctl工具进行操作:
etcdctl set /zy/message "Hello Docker"
etcdctl get /zy/message
结果为:
Hello Docker
Docker Daemon需要能够访问etcd集群,需要增加--cluster-store参数。比较好的方式是修改systemd,打开/usr/lib/systemd/system/docker.service文件进行修改。
192.168.1.23节点的内容如下:
[Unit]
Description=Docker Application ContainerEngine
Documentation=https://docs.docker.com
After=network.target docker.socket
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/docker
ExecStart=/usr/bin/dockerd $INSECURE_REGISTRY--cluster-store=etcd://192.168.1.23:2379
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
192.168.1.24节点:
[Unit]
Description=Docker Application ContainerEngine
Documentation=https://docs.docker.com
After=network.target docker.socket
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/docker
#ExecStart=/usr/bin/dockerd$INSECURE_REGISTRY
ExecStart=/usr/bin/dockerd$INSECURE_REGISTRY --cluster-store=etcd://192.168.1.24:2379
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
修改完之后,重启Docker:
systemctl daemon-reload
systemctl restart docker
步骤一:下载calicoctl并配置
下载calicoctl,下载地址为:
http://www.projectcalico.org/builds/calicoctl
将下载的calicoctl文件放到/usr/bin/calicoctl,并增加可执行权限:
chmod +x /usr/bin/calicoctl
步骤二:拉取Calico相关镜像
这样演示一下对于无法联网的节点如何使用Docker Hub上的镜像:
1) 首先使用可以联网的节点pull镜像并导出镜像
从Docker Hub上拉取镜像:
docker pullcalico/node
docker pull calico/node-libnetwork
导出镜像到本地:
docker save -o calico-node-libnetwork.tar.gzcalico/node-libnetwork
docker save -o calico-node.tar.gz calico/node
2) 将保存的镜像文件拷贝到需要使用镜像的节点并导入
docker load -i calico-node-libnetwork.tar.gz
docker load -i calico-node.tar.gz
步骤三:启动Calico服务
在Docker环境中Calico服务是做为容器来运行的,使用host的网络配置。所有容器配置使用Calico服务,做为calico节点互相通信。
在每个节点上执行:
192.168.1.23节点:
calicoctl node--ip=192.168.1.23
192.168.1.24节点:
calicoctl node--ip=192.168.1.24
我们选其中一个节点查看时容器时,可以看到新建了一个calico-node容器。
在启动别的容器之前,我们需要在启动别的容器之前,我们需要配置一个IP地址池带有ipip和nat-outgoing选项,在每个节点执行:
calicoctl pooladd 10.20.10.0/24 --ipip --nat-outgoing
步骤四:容器网络配置
首先,我们先启动几个容器:
192.168.1.23节点:
docker run -itd--net=none --name=centos-1 centos:7.2
docker run -itd--net=none --name=centos-2 centos:7.2
192.168.1.24节点:
docker run -itd--net=none --name=centos-3 centos:7.2
现在所有容器都运行了,但没有任何网络设备,用Calico来分配网络设备给这些容器,分配给容器的IP应该在IP地址池范围内。
192.168.1.23节点:
calicoctlcontainer add centos-1 10.20.10.1
calicoctl containeradd centos-2 10.20.10.2
192.168.1.24节点:
calicoctl containeradd centos-3 10.20.10.3
一旦容器有Calico网络了,他们能获取到对应IP的网络设备,至此他们还不能互相访问或访问互联网,因为没有创建并分配配置文件给容器。
在任意一个节点上创建一些配置文件:
calicoctlprofile add myprofile1
calicoctlprofile add myprofile2
分配配置文件给容器,在相同配置文件中的容器可以互相访问:
192.168.1.23节点:
calicoctlcontainer centos-1 profile append myprofile1
calicoctlcontainer centos-2 profile append myprofile2
192.168.1.24节点:
calicoctlcontainer centos-3 profile append myprofile1
至此所有配置都完成了,稍后测试一下这些容器之间的网络连接。
步骤五:验证
我们先验证跨节点的容器之间通信:
192.168.1.23节点:
docker execcentos-1 ping -c2 10.20.10.3
结果为:
PING10.20.10.3 (10.20.10.3) 56(84) bytes of data.
64 bytes from10.20.10.3: icmp_seq=1 ttl=62 time=0.728 ms
64 bytes from10.20.10.3: icmp_seq=2 ttl=62 time=0.437 ms
……
之所以能够ping通,是因为centos-1和centos-3位于同一个profile(myprofile1)。
然后我们再测试一下同一个节点的容器之间通信:
192.168.1.23节点:
docker execcentos-1 ping -c2 10.20.10.2
结果是无法ping通的,同一个节点的容器位于不同的profile,起到隔离的作用。
Calico能够被集成到Docker网络中。在Docker1.9版后,Calico运行另一个容器(calico/node-libnetwork)作为Docker网络插件,并集成到Docker docker network 命令中。
在Calico的每个节点使用带 --libnetwork参数运行Calico,如下:
192.168.1.23节点:
HOSTNAME=dockera calicoctl node --libnetwork--ip=192.168.1.23
192.168.1.24节点:
HOSTNAME=dockerb calicoctl node --libnetwork--ip=192.168.1.24
然后我们查看一下启动的容器:
[root@dockera~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe01bdf1d6a4 calico/node-libnetwork:latest "./start.sh" About a minute ago Up About a minute calico-libnetwork
e5568ce5b870 calico/node:latest "/sbin/start_runit" About a minute ago Up About aminute calico-node
可以看到比上面多启动了一个node-libnetwork的容器。
使用 dockernetwork 命令创建Calico网络:
calicoctl pooladd 192.168.0.0/16 --ipip --nat-outgoing
dockernetwork create --driver calico --subnet=192.168.0.0/24 mynet
执行如下命令查看网络:
dockernetwork ls
结果为:
NETWORKID NAME DRIVER SCOPE
dad28eee665d bridge bridge local
68a64e8083e6 mynet calico global
25447694d835 host host local
cc6658447ef0 none null local
下面我们使用mynet网络在不同节点创建容器:
192.168.1.23节点:
docker run -itd--net=mynet --ip 192.168.0.10 --name=centos-1 centos:7.2
docker run -itd--net=mynet --ip 192.168.0.11 --name=centos-2 centos:7.2
192.168.1.24节点:
docker run -itd--net=mynet --ip 192.168.0.12 --name=centos-3 centos:7.2
我们先将etcd相关的主要参数描述一下:
--data-dir 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定--wal-dir,还会存储WAL文件
--wal-dir 指定节点的was文件的存储目录,若指定了该参数,wal文件会和其他数据文件分开存储。
--name 节点名称
--initial-advertise-peer-urls 告知集群其他节点url.
--listen-peer-urls 监听URL,用于与其他节点通讯
--advertise-client-urls 告知客户端url, 也就是服务的url
--initial-cluster-token 集群的ID
--initial-cluster 集群中所有节点
注:具体详细内容,可以执行etcd--help查看。
下面正式介绍etcd集群的扩容详细过程。
步骤一:通过 etcdctl
或对应的 API 注册新节点
我们想将新192.168.1.25加入到etcd集群中,etcd的节点名为node3,peer-urls为192.168.1.25:2380,则在已有的etcd集群任意节点执行如下命令:
etcdctl memberadd node3 http://192.168.1.25:2380
输出结果为:
Added membernamed node3 with ID e0d570d7a4cdccef to cluster
ETCD_NAME="node3"
ETCD_INITIAL_CLUSTER="node1=http://192.168.1.23:2380,node2=http://192.168.1.24:2380,node3=http://192.168.1.25:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
etcdctl
在注册完新节点后,会返回上面的内容,包含3个环境变量,这三个变量我们会在新节点用到。
步骤二:
登录新节点192.168.1.25,执行如下命令:
exportETCD_NAME="node3"
exportETCD_INITIAL_CLUSTER_STATE="existing"
exportETCD_INITIAL_CLUSTER="node1=http://192.168.1.23:2380,node2=http://192.168.1.24:2380,node3=http://192.168.1.25:2380"
然后我们执行etcd,启动该服务并加入etcd集群环境:
cd/usr/local/etcd-v2.2.1-linux-amd64
#我们没有指定etcd数据目录,默认数据位于当前所在目录
nohup etcd--listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001--advertise-client-urls http://0.0.0.0:2379 \
--listen-peer-urlshttp://0.0.0.0:2380 --initial-advertise-peer-urls http://192.168.1.25:2380&> etcd.log &
到这里新节点就会运行起来并且加入到已有的集群中了。下面我们验证一下:
执行如下命令:
etcdctl memberlist
返回结果为:
8b28023d2233a213: name=node1 peerURLs=http://192.168.1.23:2380clientURLs=http://0.0.0.0:2379
d8251b9c0a2a054c: name=node2 peerURLs=http://192.168.1.24:2380clientURLs=http://0.0.0.0:2379
e0d570d7a4cdccef: name=node3 peerURLs=http://192.168.1.25:2380clientURLs=http://0.0.0.0:2379
检查etcd集群的健康状态:
etcdctlcluster-health
返回结果为:
member 8b28023d2233a213is healthy: got healthy result from http://0.0.0.0:2379
memberd8251b9c0a2a054c is healthy: got healthy result from http://0.0.0.0:2379
membere0d570d7a4cdccef is healthy: got healthy result from http://0.0.0.0:2379
定期备份etcd的数据是很重要的,便于后面etcd出问题时进行恢复。我们可以写一个脚本并使用cron进行定时调度执行备份etcd数据。
#!/bin/bash
date_time=`date+%Y%m%d`
etcdctl backup--data-dir /usr/local/etcd-v2.2.1-linux-amd64/node1.etcd --backup-dir/backup_etcd/${date_time}
find /backup_etcd/-ctime +5 -exec rm -r {} \;