Calico为容器和虚拟机工作负载提供安全的网络连接。
Calico创建并管理平面第3层网络,为每个工作负载分配完全可路由的IP地址。工作负载可以在没有IP封装或网络地址转换的情况下进行通信,以实现裸机性能,简化故障排除和更好的互操作性。在需要overlay的环境中,Calico使用IP-in-IP隧道技术,或者可以与flannel等其他覆盖网络配合使用。
Calico还提供网络安全规则的动态实施。使用Calico的简单策略语言,您可以实现对容器,虚拟机工作负载和裸机主机端点之间通信的细粒度控制。
Calico在大规模生产方面经过验证,可与Kubernetes,OpenShift,Docker,Mesos,DC / OS和OpenStack集成。
Calico 依赖 etcd 在不同主机间共享和交换信息,存储 Calico 网络状态。我们将在 host 192.168.92.56上运行 etcd。
Calico 网络中的每个主机都需要运行 Calico 组件,提供容器 interface 管理、动态路由、动态 ACL、报告状态等功能。
实验环境如下图所示:
主机节点:
192.168.92.56 etcd 安装docker+etcd
192.168.92.57 host1 安装docker+ calicoctl
192.168.92.58 host2 安装docker+ calicoctl
软件版本:
calicoctl(version v1.6.5) etcdctl(version: 3.2.22) docker(version:18.09.0-ce)
修改三个节点的主机名
[root@localhost ~]# hostnamectl --static set-hostname etcd
[root@localhost ~]# hostnamectl --static set-hostname host1
[root@localhost ~]# hostnamectl --static set-hostname host2
在三台主机上都要设置hosts:
[root@host1 ~]# vim /etc/hosts
192.168.92.56 etcd
192.168.92.57 host1
192.168.92.58 host2
关闭三台主机的防火墙
若开启iptables防火墙,则需要打开2380端口通信。
[root@host1 ~]# systemctl disable firewalld.service
[root@host1 ~]# systemctl stop firewalld.service
[root@host1 ~]# iptables -F
[root@host1 ~]# firewall-cmd --state
not running
在etcd主机上安装etcd
[root@etcd ~]# yum install -y etcd
修改etcd配置文件:
[root@etcd ~]# vim /etc/etcd/etcd.conf
#[Member]
#ETCD_CORS=""
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#ETCD_WAL_DIR=""
#ETCD_LISTEN_PEER_URLS="http://localhost:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.92.56:2379"
......
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.92.56:2379"
......
启动etcd服务
[root@etcd ~]# systemctl start etcd && systemctl enable etcd
查看版本
[root@etcd ~]# etcdctl -version
etcdctl version: 3.2.22
API version: 2
[root@etcd ~]#
修改docker配置文件连接etcd
修改 host1 和 host2 的 Docker daemon 配置文件
连接 etcd:–cluster-store=etcd://192.168.92.56:2379
[root@host1 ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"cluster-store": "etcd://192.168.92.56:2379"
}
说明:第一行的registry-mirrors是配置docker镜像加速以后自动添加的。
重启 Docker daemon
systemctl daemon-reload && systemctl restart docker.service
查看配置是否正确
[root@host1 ~]# docker info | grep "Cluster Store"
Cluster Store: etcd://192.168.92.56:2379
[root@host1 ~]#
官方参考文档:https://docs.projectcalico.org/v2.6/getting-started/docker/installation/manual
在host1和host2上下载calicoctl并运行calico容器。
下载calicoctl
[root@host1 ~]# wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v1.6.5/calicoctl
[root@host1 ~]# chmod +x /usr/local/bin/calicoctl
[root@host1 ~]# /usr/local/bin/calicoctl --version
calicoctl version v1.6.5, build 614fcf12
创建calico容器
首先创建calico配置文件
[root@host1 ~]# mkdir /etc/calico/
[root@host1 ~]# vim /etc/calico/calicoctl.cfg
apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
datastoreType: "etcdv2"
etcdEndpoints: "http://192.168.92.56:2379"
分别在两个节点上创建calico容器,执行后会自动下载calico镜像并运行容器,下载镜像可能较慢,建议配置镜像加速,启动过程如下:
[root@host1 ~]# calicoctl node run --node-image=quay.io/calico/node:v2.6.12 -c /etc/calico/calicoctl.cfg
Running command to load modules: modprobe -a xt_set ip6_tables
Enabling IPv4 forwarding
Enabling IPv6 forwarding
Increasing conntrack limit
Removing old calico-node container (if running).
Running the following command to start calico-node:
docker run --net=host --privileged --name=calico-node -d --restart=always -e NODENAME=host1 -e CALICO_NETWORKING_BACKEND=bird -e CALICO_LIBNETWORK_ENABLED=true -e ETCD_ENDPOINTS=http://192.168.92.56:2379 -v /var/log/calico:/var/log/calico -v /var/run/calico:/var/run/calico -v /lib/modules:/lib/modules -v /run:/run -v /run/docker/plugins:/run/docker/plugins -v /var/run/docker.sock:/var/run/docker.sock quay.io/calico/node:v2.6.12
Image may take a short time to download if it is not available locally.
Container started, checking progress logs.
2018-11-20 09:38:51.800 [INFO][8] startup.go 173: Early log level set to info
2018-11-20 09:38:51.801 [INFO][8] client.go 202: Loading config from environment
2018-11-20 09:38:51.801 [INFO][8] startup.go 83: Skipping datastore connection test
2018-11-20 09:38:51.811 [INFO][8] startup.go 259: Building new node resource Name="host1"
2018-11-20 09:38:51.811 [INFO][8] startup.go 273: Initialise BGP data
2018-11-20 09:38:51.812 [INFO][8] startup.go 467: Using autodetected IPv4 address on interface ens33: 192.168.92.57/24
2018-11-20 09:38:51.812 [INFO][8] startup.go 338: Node IPv4 changed, will check for conflicts
2018-11-20 09:38:51.814 [INFO][8] etcd.go 430: Error enumerating host directories error=100: Key not found (/calico) [11]
2018-11-20 09:38:51.814 [INFO][8] startup.go 530: No AS number configured on node resource, using global value
2018-11-20 09:38:51.816 [INFO][8] etcd.go 105: Ready flag is now set
2018-11-20 09:38:51.817 [INFO][8] client.go 133: Assigned cluster GUID ClusterGUID="6608d7fcd5934b86af326f9570763e05"
2018-11-20 09:38:51.832 [INFO][8] startup.go 419: CALICO_IPV4POOL_NAT_OUTGOING is true (defaulted) through environment variable
2018-11-20 09:38:51.832 [INFO][8] startup.go 659: Ensure default IPv4 pool is created. IPIP mode: off
2018-11-20 09:38:51.834 [INFO][8] startup.go 670: Created default IPv4 pool (192.168.0.0/16) with NAT outgoing true. IPIP mode: off
2018-11-20 09:38:51.835 [INFO][8] startup.go 419: FELIX_IPV6SUPPORT is true (defaulted) through environment variable
2018-11-20 09:38:51.835 [INFO][8] startup.go 626: IPv6 supported on this platform: true
2018-11-20 09:38:51.835 [INFO][8] startup.go 419: CALICO_IPV6POOL_NAT_OUTGOING is false (defaulted) through environment variable
2018-11-20 09:38:51.835 [INFO][8] startup.go 659: Ensure default IPv6 pool is created. IPIP mode: off
2018-11-20 09:38:51.837 [INFO][8] startup.go 670: Created default IPv6 pool (fd80:24e2:f998:72d6::/64) with NAT outgoing false. IPIP mode: off
2018-11-20 09:38:51.886 [INFO][8] startup.go 131: Using node name: host1
2018-11-20 09:38:51.967 [INFO][13] client.go 202: Loading config from environment
Starting libnetwork service
Calico node started successfully
[root@host1 ~]#
1.设置主机网络,例如 enable IP forwarding。
2.下载并启动 calico-node 容器,calico 会以容器的形式运行(与 weave 类似)。
[root@host1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
quay.io/calico/node v2.6.12 401cc3e56a1a 10 days ago 281MB
[root@host1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5903e0254caf quay.io/calico/node:v2.6.12 "start_runit" 18 minutes ago Up 18 minutes calico-node
[root@host1 ~]#
3.连接 etcd。
4.calico 启动成功。
查看calico运行状态
[root@host1 ~]# calicoctl node status
Calico process is running.
IPv4 BGP status
+---------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+---------------+-------------------+-------+----------+-------------+
| 192.168.92.58 | node-to-node mesh | up | 09:42:58 | Established |
+---------------+-------------------+-------+----------+-------------+
IPv6 BGP status
No IPv6 peers found.
[root@host1 ~]#
[root@host2 ~]# calicoctl node status
Calico process is running.
IPv4 BGP status
+---------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+---------------+-------------------+-------+----------+-------------+
| 192.168.92.57 | node-to-node mesh | up | 09:42:58 | Established |
+---------------+-------------------+-------+----------+-------------+
IPv6 BGP status
No IPv6 peers found.
[root@host2 ~]#
创建 calico 网络
在 host1 或 host2 上执行如下命令创建 calico 网络 cal_ent1:
# docker network create --driver calico --ipam-driver calico-ipam cal_net1
calico 为 global 网络,etcd 会将 cal_net 同步到所有主机。
[root@host1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
13688a2fa1f6 bridge bridge local
4a036a209f73 cal_net1 calico global
634261a87b28 host host local
83b3249a9250 none null local
[root@host1 ~]#
在 host1 中运行容器 bbox1 并连接到 cal_net1:
# docker container run --net cal_net1 --name bbox1 -tid busybox
查看 bbox1 的网络配置。
[root@host1 ~]# docker exec bbox1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5: cali0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
inet 192.168.119.0/32 brd 192.168.119.0 scope global cali0
valid_lft forever preferred_lft forever
[root@host1 ~]#
cali0 是 calico interface,分配的 IP 为 192.168.119.0。cali0 对应 host1 编号6 的 interface caliea27309bc46。
[root@host1 ~]# ip a
......
6: caliea27309bc46@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether ba:9a:72:6c:61:12 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::b89a:72ff:fe6c:6112/64 scope link
valid_lft forever preferred_lft forever
[root@host1 ~]#
host1 将作为 router 负责转发目的地址为 bbox1 的数据包。
[root@host1 ~]# ip route
default via 192.168.92.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.92.0/24 dev ens33 proto kernel scope link src 192.168.92.57 metric 100
192.168.119.0 dev caliea27309bc46 scope link
blackhole 192.168.119.0/26 proto bird
所有发送到 bbox1 的数据都会发给caliea27309bc46,因为caliea27309bc46 与 cali0 是一对 veth pair,bbox1 能够接收到数据。
接下来我们在 host2 中运行容器 bbox2,也连接到 cal_net1:
# docker container run --net cal_net1 --name bbox2 -tid busybox
IP 为 192.168.183.64。
[root@host2 ~]# docker exec bbox2 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5: cali0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
inet 192.168.183.64/32 brd 192.168.183.64 scope global cali0
valid_lft forever preferred_lft forever
[root@host2 ~]#
host2 添加了两条路由:
[root@host2 ~]# ip route
default via 192.168.92.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.92.0/24 dev ens33 proto kernel scope link src 192.168.92.58 metric 100
192.168.119.0/26 via 192.168.92.57 dev ens33 proto bird
192.168.183.64 dev cali16a5ce997f9 scope link
blackhole 192.168.183.64/26 proto bird
[root@host2 ~]#
同样的,host1 也自动添加了到 192.168.183.64/26 的路由。
[root@host1 ~]# ip route
default via 192.168.92.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.92.0/24 dev ens33 proto kernel scope link src 192.168.92.57 metric 100
192.168.119.0 dev caliea27309bc46 scope link
blackhole 192.168.119.0/26 proto bird
192.168.183.64/26 via 192.168.92.58 dev ens33 proto bird
[root@host1 ~]#
测试一下 bbox1 与 bbox2 的连通性:
[root@host1 ~]# docker exec bbox1 ping -c 2 bbox2
PING bbox2 (192.168.183.64): 56 data bytes
64 bytes from 192.168.183.64: seq=0 ttl=62 time=0.654 ms
64 bytes from 192.168.183.64: seq=1 ttl=62 time=1.282 ms
ping 成功,数据包流向如下图所示。
① 根据 bbox1 的路由表,将数据包从 cal0 发出。
[root@host1 ~]# docker exec bbox1 ip route
default via 169.254.1.1 dev cali0
169.254.1.1 dev cali0 scope link
[root@host1 ~]#
② 数据经过 veth pair 到达 host1,查看路由表,数据由 ens33发给 host2(192.168.92.58)。
192.168.183.64/26 via 192.168.92.58 dev ens33 proto bird
③ host2 收到数据包,根据路由表发送给 calic8bf9e68397,进而通过 veth pair cali0 到达 bbox2。
192.168.183.64 dev cali16a5ce997f9 scope link
接下来我们看看不同 calico 网络之间的连通性。
创建 cal_net2。
docker network create --driver calico --ipam-driver calico-ipam cal_net2
在 host1 中运行容器 bbox3,连接到 cal_net2:
docker container run --net cal_net2 --name bbox3 -tid busybox
calico 为 bbox3 分配了 IP 192.168.119.1。
[root@host1 ~]# docker exec bbox3 ip address show cali0
7: cali0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
inet 192.168.119.1/32 brd 192.168.119.1 scope global cali0
valid_lft forever preferred_lft forever
[root@host1 ~]#
验证 bbox1 与 bbox3 的连通性。
[root@host1 ~]# docker exec bbox1 ping -c 2 bbox3
ping: bad address 'bbox3'
[root@host1 ~]#
虽然 bbox1 和 bbox3 都位于 host1,而且都在一个 subnet 192.168.119.0/26,但它们属于不同的 calico 网络,默认不能通行。
calico 默认的 policy 规则是:容器只能与同一个 calico 网络中的容器通信。
calico 的每个网络都有一个同名的 profile,profile 中定义了该网络的 policy。我们具体看一下 cal_net1 的 profile:
[root@host1 ~]# calicoctl get profile cal_net1 -o yaml
- apiVersion: v1
kind: profile
metadata:
name: cal_net1
tags:
- cal_net1
spec:
egress:
- action: allow
destination: {}
source: {}
ingress:
- action: allow
destination: {}
source:
tag: cal_net1
[root@host1 ~]#
① 命名为 cal_net1,这就是 calico 网络 cal_net1 的 profile。
② 为 profile 添加一个 tag cal_net1。注意,这个 tag 虽然也叫 cal_net1,其实可以随便设置,这跟上面的 name: cal_net1 没有任何关系。此 tag 后面会用到。
③ egress 对从容器发出的数据包进行控制,当前没有任何限制。
④ ingress 对进入容器的数据包进行限制,当前设置是接收来自 tag cal_net1 的容器,根据第 ① 步设置我们知道,实际上就是只接收本网络的数据包,这也进一步解释了前面的实验结果。
既然这是默认 policy,那就有方法定制 policy,这也是 calico 较其他网络方案最大的特性。下一节就我们讨论如何定制 calico policy。
Calico 默认的 policy 规则是:容器只能与同一个 calico 网络中的容器通信。calico 能够让用户定义灵活的 policy 规则,精细化控制进出容器的流量,下面我们就来实践一个场景:
创建一个新的 calico 网络 cal_web 并部署一个 httpd 容器 web1。
定义 policy 允许 cal_net2 中的容器访问 web1 的 80 端口。
首先创建 cal_web。
# docker network create --driver calico --ipam-driver calico-ipam cal_web
在 host1 中运行容器 web1,连接到 cal_web:
docker container run --net cal_web --name web1 -d httpd
[root@host1 ~]# docker exec -it web1 bash
root@98c33620a550:/usr/local/apache2#
root@98c33620a550:/usr/local/apache2# apt-get update
root@98c33620a550:/usr/local/apache2# apt-get install iproute
root@98c33620a550:/usr/local/apache2#
root@98c33620a550:/usr/local/apache2# exit
exit
[root@host1 ~]#
web1 的 IP 为 192.168.119.2
[root@host1 ~]# docker container exec web1 ip address show cali0
9: cali0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.119.2/32 brd 192.168.119.2 scope global cali0
valid_lft forever preferred_lft forever
[root@host1 ~]#
目前 bbox3 还无法访问 web1 的 80 端口。
[root@host1 ~]# docker container exec bbox3 wget 192.168.119.2
Connecting to 192.168.119.2 (192.168.119.2:80)
wget: can't connect to remote host (192.168.119.2): Connection timed out
[root@host1 ~]#
创建 policy 文件 web.yml,内容为:
[root@host1 ~]# vim web.yml
- apiVersion: v1
kind: profile
metadata:
name: cal_web
spec:
ingress:
- action: allow
protocol: tcp
source:
tag: cal_net2
destination:
ports:
- 80
① profile 与 cal_web 网络同名,cal_web 的所有容器(web1)都会应用此 profile 中的 policy。
② ingress 允许 cal_net2 中的容器(bbox3)访问。
③ 只开放 80 端口。
应用该 policy。
# calicoctl apply -f web.yml
现在 bbox3 已经能够访问 web1 的 http 服务了。
[root@host1 ~]# docker container exec bbox3 wget 192.168.119.2
Connecting to 192.168.119.2 (192.168.119.2:80)
index.html 100% |********************************| 45 0:00:00 ETA
[root@host1 ~]#
不过 ping 还是不行,因为只放开了 80 端口。
[root@host1 ~]# docker container exec bbox3 ping -c 2 192.168.119.2
PING 192.168.119.2 (192.168.119.2): 56 data bytes
--- 192.168.119.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
[root@host1 ~]#
上面这个例子比较简单,不过已经向我们展示了 calico 强大的 policy 功能。通过 policy,可以动态实现非常复杂的容器访问控制。有关 calico policy 更多的配置,可参看官网文档 http://docs.projectcalico.org/v2.0/reference/calicoctl/resources/policy
calico 会为自动为网络分配 subnet,当然我们也可以定制。
首先定义一个 IP Pool,比如:
[root@host1 ~]# vim ipPool.yaml
- apiVersion: v1
kind: ipPool
metadata:
cidr: 17.2.0.0/16
[root@host1 ~]# calicoctl create -f ipPool.yaml
Successfully created 1 'ipPool' resource(s)
[root@host1 ~]#
用此 IP Pool 创建 calico 网络。
# docker network create --driver calico --ipam-driver calico-ipam --subnet=17.2.0.0/16 my_net
此时运行容器将分配到指定 subnet 中的 IP。
[root@host1 ~]# docker network create --driver calico --ipam-driver calico-ipam --subnet=17.2.0.0/16 my_net
511cec9967a597fd8a6d32dce519e2d93764076c3fca9dedc56c89311c50a4a7
[root@host1 ~]# docker container run --net my_net -ti busybox
/ # ip address show cali0
11: cali0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
inet 17.2.119.0/32 brd 17.2.119.0 scope global cali0
valid_lft forever preferred_lft forever
/ #
当然也可以通过 --ip 为容器指定 IP,但必须在 subnet 范围之内。
[root@host1 ~]# docker container run --net my_net --ip 17.2.3.11 -ti busybox
/ # ip address show cali0
13: cali0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff
inet 17.2.3.11/32 brd 17.2.3.11 scope global cali0
valid_lft forever preferred_lft forever
/ #
参考:https://www.cnblogs.com/CloudMan6/p/7509975.html