Docker网络(七)—跨主机通信之Calico网络

 2017年10月4日 18:23   Nick王   云计算    0 评论   1013 浏览 

calicoctl 命令行工具版本: v1.6.1

Calico 集群版本 : v2.6.1

操作系统:CentOs 7.4.1708

Docker版本:17.09.0-ce

Docker网络(七)—跨主机通信之Calico网络

官方网站:https://www.projectcalico.org/

项目地址:https://github.com/projectcalico

官方文档:https://docs.projectcalico.org/v2.6/getting-started/

Calico网络介绍

Calico网络是一个为容器、虚拟机和裸机服务提供的一种虚拟网络和网络安全的新方法。Calico网络是一个高可扩展,高效的虚拟网络,它提供了一组丰富的网络安全能力。

Calico网络可以为Kubernetes和Mesos和Docker和OpenStack提供网络功能。

Calico是一个纯三层的虚拟网络,并不是之前所使用的OverLay网络。所以不存在封包/解包的过程。

网络安全策略使用ACL定义,基于iptables实现,比起overlay方案中的复杂机制更直观和容易操作。

Calico通过将整个互联网的可扩展 IP 网络原则压缩到数据中心级别,Calico 在每一个计算节点利用 Linux kernel 实现了一个高效的 vRouter 来负责数据转发。(BGP协议)

与大多数虚拟网络方案相比,Calico网络提供了一个平坦网络结构(flat ip network),Calico网络是没有封装的(no overlay)。

calico 默认的 policy 规则是:容器只能与同一个 calico 网络中的容器通信。

calico 的每个网络都有一个同名的 profile,profile 中定义了该网络的 policy。


Calico网络和Docker

安装依赖

  • Calico集群网络中的所有的宿主机应该都能互相通信

  • etcd集群;主要负责网络元数据一致性,确保 Calico 网络状态的准确性

  • Docker 1.9 或者 以后的版本

要想将Calico网络作为Docker的网络插件,则必须要配置Docker Daemon的分布式存储。

Calico 网络中的每个主机都需要运行 Calico 组件,提供容器 interface 管理、动态路由、动态 ACL、报告状态等功能。

# dockerd --help|grep cluster
      --cluster-advertise string                Address or interface name to advertise
      --cluster-store string                    URL of the distributed storage backend
      --cluster-store-opt map                   Set cluster store options (default map[])

etcd集群部署

安装配置:

# yum install etcd -y

# egrep -v "^$|^#" /etc/etcd/etcd.conf
ETCD_NAME=container-host-3
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://192.168.0.108:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.108:2379"

启动并测试:

# systemctl start etcd.service

# ss -lntu|grep 2379
tcp    LISTEN     0      128    192.168.0.108:2379                  *:*

# ps -ef|grep etcd
etcd       1426      1  1 04:21 ?        00:00:01 /usr/bin/etcd --name=container-host-3 --data-dir=/var/lib/etcd/default.etcd --listen-client-urls=http://192.168.0.108:2379


#### etcd api v3 设置key
# ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 put hello world
OK


#### etcd api v3 获取key
# ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 get hello
hello
world


#### etcd api v3 删除key
# ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 del hello
1


# ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 member list
8e9e05c52164694d, started, container-host-3, http://localhost:2380, http://192.168.0.108:2379

配置Docker Daemon使用etcd存储

在所有Docker Host上,修改Docker的启动配置文件:

# vim /usr/lib/systemd/system/docker.service


[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.0.108:2379
ExecReload=/bin/kill -s HUP $MAINPID


# systemctl daemon-reload

# systemctl restart docker.service

安装Calico

Calico在每个Docker Host上会以容器的形式工作。 所以要先保证Docker 启动。

calicoctl命令行工具用来启动calico/node容器。

安装Calicoctl

# wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v1.6.1/calicoctl


# chmod +x /usr/local/bin/calicoctl

# calicoctl version
Client Version:    v1.6.1
Build date:        2017-09-28T01:12:35+0000
Git commit:        1724e011
Cluster Version:   unknown (client: etcd cluster is unavailable or misconfigured; error #0: dial tcp 127.0.0.1:2379: getsockopt: connection refused
)
Cluster Type:      unknown (client: etcd cluster is unavailable or misconfigured; error #0: dial tcp 127.0.0.1:2379: getsockopt: connection refused
)

注意:这里的calicoctl并没有发现etcd集群。


配置calicoctl访问etcd存储:

参考官方文档: https://docs.projectcalico.org/v2.6/reference/calicoctl/setup/etcdv2

# mkdir /etc/calico

# vim /etc/calico/calicoctl.cfg
apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
  datastoreType: etcdv2
  etcdEndpoints: http://192.168.0.108:2379


# calicoctl version
Client Version:    v1.6.1
Build date:        2017-09-28T01:12:35+0000
Git commit:        1724e011
Cluster Version:   unknown    ### 这里已经发生了变化
Cluster Type:      unknown

特别提示:默认情况下读取配置文件的路径是/etc/calico/calicoctl.cfg,也可以通过环境变量来指定。



启动calico/node 

# calicoctl node run --node-image=quay.io/calico/node:v2.6.1
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=container-host-1 -e CALICO_NETWORKING_BACKEND=bird -e CALICO_LIBNETWORK_ENABLED=true -e ETCD_ENDPOINTS=http://192.168.0.108: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.1

Image may take a short time to download if it is not available locally.
Container started, checking progress logs.

Skipping datastore connection test
Using autodetected IPv4 address on interface ens33: 192.168.0.104/24
No AS number configured on node resource, using global value
Created default IPv4 pool (192.168.0.0/16) with NAT outgoing true. IPIP mode: off
Created default IPv6 pool (fd80:24e2:f998:72d6::/64) with NAT outgoing false. IPIP mode: off
Using node name: container-host-1
Starting libnetwork service
Calico node started successfully

解释:

  • 加载内核模块xt_setip6_tables

  • 启用IP转发

  • 增加链接跟踪限制

  • 删除旧的calico-node容器

  • 启动calico-node,从启动命令可以看到容器使用的是host网络(跟宿主机共用同一个网络堆栈),还有一些环境变量和卷挂载操作

  • 下载启动镜像

  • 创建默认的IPv4地址池

检查calico/node是否正在运行:

# docker ps -a
CONTAINER ID        IMAGE                        COMMAND             CREATED             STATUS              PORTS               NAMES
22a8570c9826        quay.io/calico/node:v2.6.1   "start_runit"       43 minutes ago      Up 43 minutes                           calico-node

使用如下命令检查calico/node是否正确的运行:

# calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+-------------------+-------+----------+-------------+
| PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+---------------+-------------------+-------+----------+-------------+
| 192.168.0.105 | node-to-node mesh | up    | 08:47:56 | Established |
+---------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

特别提示:显示了对端的地址。

检查安装是否正确:

# calicoctl get nodes
NAME
container-host-1
container-host-2

正确设置会产生一个已注册的节点的列表。

将Calico Node作为一个系统服务启动

可以使用如下命令打印,Calico Node启动的命令:

# calicoctl node run --init-system --dryrun --node-image=quay.io/calico/node:v2.6.1
Use the following command to start the calico/node container:

docker run --net=host --privileged --name=calico-node --rm -e NODENAME=container-host-2 -e CALICO_NETWORKING_BACKEND=bird -e CALICO_LIBNETWORK_ENABLED=true -e ETCD_ENDPOINTS=http://192.168.0.108: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.1

Use the following command to stop the calico/node container:

docker stop calico-node

参考官方文档: https://docs.projectcalico.org/v2.6/usage/configuration/as-service


使用Calico网络

创建网络

在Calico Node集群中的任意一台机器上执行如下命令:

# docker network create --driver calico --ipam-driver calico-ipam net1
a035a9de1f07b6b93ad8066208ad8f8edeba8fe164f6c3909a7f5cd4cd54284c


# docker network create --driver calico --ipam-driver calico-ipam net2
8753df11782115660219f6d2e3a69613b88881000b6eb99024f819eeb1400a82


# docker network create --driver calico --ipam-driver calico-ipam net3
efecf7b4a5d70974eb9cb1729630859397aebe6f2d78724ef46a6a415b6622b6


# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
2532fced0861        bridge              bridge              local
7d0814858cea        host                host                local
a79433d6fafe        net1                calico              global
a6e466ee5a02        net2                calico              global
d346b3d4ca0d        net3                calico              global
aba76d59e75d        none                null                local

特别注意: 此时在Calico Node集群的所有机器上已经都有这三个网络了。

解释:

  • --driver calico: 表示使用 calico 的 libnetwork CNM driver

  • --ipam-driver calico-ipam: 表示使用 calico 的 IPAM driver 管理 IP

此时宿主机上的网络配置如下:

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:12:8b:98 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic ens33
       valid_lft 6461sec preferred_lft 6461sec
    inet6 fe80::2a40:5032:4903:c5be/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
    link/ether 02:42:3c:74:ea:2b brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever

特别提示:并没有多余的网卡。

创建workloads

现在创建一些容器

在Docker Host1上:

# docker run -itd --name=workload-A --network=net1 busybox
8fe2efacfd2a214931b0e1be63a56f1d1ab3a040d9e406fe31154c93b35c19e4


# docker run -itd --name=workload-B --network=net2 busybox
3dc7a121857484c9137c30ed5f98459cbde1bb451af95e1c58699f415b5ad0d7


# docker run -itd --name=workload-C --network=net1 busybox
cd9878723337ce00fa3e66ae0ac74345707fab03b20b017f5cfaee2cf6af1068

在Docker Host2上:

# docker run -itd --name=workload-D --network=net3 busybox
8f8953a5135115bb92ba17c32f2aa865375c9337054018fc101737e49f6da1dc


# docker run -itd --name=workload-E --network=net1 busybox
bf0c82902823b0ef4772fbbbad7cf2b7ee2760119a6d3dbe576e6743fe6aee7b

检查workloads的连通性

在Docker Host1上,检查A和C和E的连通性:

# docker exec workload-A ping -c 2 workload-C.net1
PING workload-C.net1 (192.168.53.194): 56 data bytes
64 bytes from 192.168.53.194: seq=0 ttl=63 time=0.168 ms
64 bytes from 192.168.53.194: seq=1 ttl=63 time=0.150 ms

--- workload-C.net1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.150/0.159/0.168 ms

# docker exec workload-A ping -c 2 workload-E.net1

特别注意:这里一定是容器名.网络名


首先,在同一个网络中的主机名会添加到容器的主机配置中。所以需要使用IP来进行不同网络中的容器的测试。

因为A和B是在同一个Host上,所以可以使用一条命令达到效果, 在Docker Host1上执行:

# docker exec workload-A ping -c 2  `docker inspect --format "{{ .NetworkSettings.Networks.net2.IPAddress }}" workload-B`

A和D在不同的主机上,所以:

#### 在Docker Host2上执行
# docker inspect --format "{{ .NetworkSettings.Networks.net3.IPAddress }}" workload-D


#### 在Docker Host1上执行
# docker exec workload-A ping -c 2 <IP address of D>

Calico网络,通信剖析

首先来看容器workload-A的内部网络:

# docker exec workload-A ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
4: cali0@if5: <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.53.192/32 scope global cali0
       valid_lft forever preferred_lft forever


# docker exec workload-A ip route
default via 169.254.1.1 dev cali0
169.254.1.1 dev cali0 scope link

默认路由是通过cali0网卡出去的。

网卡cali0的对端设备序号为5

查看主机的网卡信息如下:

# ip -d link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
    link/ether 00:0c:29:12:8b:98 brd ff:ff:ff:ff:ff:ff promiscuity 0 addrgenmode none
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT
    link/ether 02:42:3c:74:ea:2b brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.2:42:3c:74:ea:2b designated_root 8000.2:42:3c:74:ea:2b root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00 gc_timer   58.89 vlan_default_pvid 1 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 addrgenmode eui64
5: calied5029793e5@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
    link/ether 3a:d0:81:39:1a:eb brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
    veth addrgenmode eui64
7: cali1b796fbebe7@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
    link/ether c6:b3:b2:8c:88:ca brd ff:ff:ff:ff:ff:ff link-netnsid 1 promiscuity 0
    veth addrgenmode eui64
9: cali1d61e3f3ce6@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
    link/ether 1a:ed:c4:28:c9:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 2 promiscuity 0
    veth addrgenmode eui64

特别提示:网卡calied5029793e5@if4就是一个普通的veth设备。

查看宿主机的路由信息:

# ip route list
default via 192.168.0.1 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev ens33 proto kernel scope link src 192.168.0.104 metric 100
192.168.53.192 dev calied5029793e5 scope link
blackhole 192.168.53.192/26 proto bird
192.168.53.193 dev cali1b796fbebe7 scope link
192.168.53.194 dev cali1d61e3f3ce6 scope link
192.168.225.64/26 via 192.168.0.105 dev ens33 proto bird

特别提示:注意路由192.168.53.192 dev calied5029793e5 scope link表示到达192.168.53.192的数据发送给网卡calied5029793e5

calied5029793e5的对端设备就是workload-A容器中的cali0网卡。

到目前为止,关于容器workload-A的网络拓扑如下图:

AA



接着分析容器workload-C的网络结构:

# docker exec workload-C ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
8: cali0@if9: <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.53.194/32 scope global cali0
       valid_lft forever preferred_lft forever

发现workload-C的对端设备是if9

查看对应的设备信息,如下:

# ip -d link show cali1d61e3f3ce6
9: cali1d61e3f3ce6@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
    link/ether 1a:ed:c4:28:c9:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 2 promiscuity 0
    veth addrgenmode eui64

查看路由表,信息如下:

# ip route list
default via 192.168.0.1 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev ens33 proto kernel scope link src 192.168.0.104 metric 100
192.168.53.192 dev calied5029793e5 scope link
blackhole 192.168.53.192/26 proto bird
192.168.53.193 dev cali1b796fbebe7 scope link
192.168.53.194 dev cali1d61e3f3ce6 scope link
192.168.225.64/26 via 192.168.0.105 dev ens33 proto bird

注意:192.168.53.194 dev cali1d61e3f3ce6 scope link表示,到达容器workload-C192.168.53.194数据发送给网卡cali1d61e3f3ce6


目前通过路由表分析,可以知道,从宿主机通过路由表可以随便和任意一个容器内部通信:

# ping -c 1 192.168.53.192
PING 192.168.53.192 (192.168.53.192) 56(84) bytes of data.
64 bytes from 192.168.53.192: icmp_seq=1 ttl=64 time=0.247 ms

--- 192.168.53.192 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.247/0.247/0.247/0.000 ms


# ping -c 1 192.168.53.193
PING 192.168.53.193 (192.168.53.193) 56(84) bytes of data.
64 bytes from 192.168.53.193: icmp_seq=1 ttl=64 time=0.102 ms

--- 192.168.53.193 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.102/0.102/0.102/0.000 ms

跟踪容器间通信的数据:

# docker exec workload-A traceroute workload-C.net1
traceroute to workload-C.net1 (192.168.53.194), 30 hops max, 46 byte packets
 1  192.168.0.104 (192.168.0.104)  0.006 ms  0.005 ms  0.002 ms
 2  workload-C.net1 (192.168.53.194)  0.002 ms  0.003 ms  0.002 ms

注意:有两条记录,首先发送给宿主机。然后再通过宿主机到达另外的容器。

整个过程宿主机充当一个路由器的角色。

此时网络架构图如下:

AA


最后看看跨主机的容器workload-Aworkload-E的联通性分析

首先看workload-E内部的网络结构:

# docker exec workload-E ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
6: cali0@if7: <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.225.65/32 scope global cali0
       valid_lft forever preferred_lft forever

特别注意:这里的IP地址是192.168.225.65,对端设备序号为if7

查看宿主机上对应的网卡信息如下:

# ip -d link show calib6abd061c5e
7: calib6abd061c5e@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
    link/ether 6a:a7:f0:51:16:51 brd ff:ff:ff:ff:ff:ff link-netnsid 1 promiscuity 0
    veth addrgenmode eui64

查看路由表:

# ip route list
default via 192.168.0.1 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev ens33 proto kernel scope link src 192.168.0.105 metric 100
192.168.53.192/26 via 192.168.0.104 dev ens33 proto bird
192.168.225.64 dev cali9d0143989a2 scope link
blackhole 192.168.225.64/26 proto bird
192.168.225.65 dev calib6abd061c5e scope link

这里要注意两条路由规则:

  • 192.168.225.65 dev calib6abd061c5e scope link 跟之前的含义一样,通往容器内部的信息发给对应的网卡。

  • 192.168.53.192/26 via 192.168.0.104 dev ens33 proto bird 注意这里,192.168.53.192是Docker Host 1上的blackholeworkload-A的IP,192.168.0.104是Docker Host 1的宿主机IP。

并且还有一点,他们不是一个网段的,因为Calico是一个纯三层网络,是靠路由表进行通信的。

查看Docker Host 1的路由表:

# ip route list
default via 192.168.0.1 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.0.0/24 dev ens33 proto kernel scope link src 192.168.0.104 metric 100
192.168.53.192 dev calied5029793e5 scope link
blackhole 192.168.53.192/26 proto bird
192.168.53.193 dev cali1b796fbebe7 scope link
192.168.53.194 dev cali1d61e3f3ce6 scope link
192.168.225.64/26 via 192.168.0.105 dev ens33 proto bird

注意:192.168.225.64/26 via 192.168.0.105 dev ens33 proto bird 路由表跟上述描述的作用一样。

注意:子网掩码不同。

到此为止,网络结构如下图:

AA

注意:workload-A 和  workload-E 虽然从IP上看不是一个子网,但是他们在同一个Calico网络,是可以通信的。记住:Calico是一个纯三层网络。他们之间有路由表。


Calico网络的Profile和Policy

Calico作为Docker网络的插件,每个Calico网络都有一个profile文件,并且这个profile文件跟Calico网络的名字一样。

Calico的profile会被应用到网络中的每一个容器上。

Calico的profile还用于配置容器的访问策略(access policy)。policy 也可以单独配置,而不需要和profile在一起。

Calico网络插件会自动的创建所关联的profile,如果当容器附着在网络的时候,profile不存在的话。

Calico网络的profile默认情况下只包含入口网络流量的相关规则。

可以通过预先创建,或者编辑Docker网络的关联的Calico profile文件来自定义配置Calico 网络访问策略(policy)。

查看profile

默认情况下profile的名字和Calico的名称一样:

# calicoctl get profile net1 -o yaml
- apiVersion: v1
  kind: profile
  metadata:
    name: net1   ### 名称
    tags:  ### 标签名称,可以随便定义,主要用于流量的限制,相当于别名
    - net1
  spec:
    egress:   ### 对从容器发出的数据包进行控制,当前没有任何限制
    - action: allow   ### 允许动作
      destination: {}   ### 所有目的地址
      source: {}     ### 所有来源地址
    ingress:    ### 对进入容器的数据包进行限制
    - action: allow   ### 允许动作,规则之内的允许,其他都拒绝
      destination: {}   ### 所有目的地址
      source:
        tag: net1    ### 接收来自 tag cal_net1 的容器的数据包, 实际上就是只接收本网络的数据包

特别提示: 如果不指定profile 名称的话,会获取到所有的profile配置。

所以,calico 默认的 policy 规则是:容器只能与同一个 calico 网络中的容器通信。

calico 的每个网络都有一个同名的 profile,profile 中定义了该网络的 policy。

管理Calico网络的policy

在这个案例中,我们创建一个数据库网络,和一个前端网络,让这两个网络的容器隔离。然后数据库容器之间本身互相访问是没有问题的,不过要让前端网络中的容器可以访问数据库网络容器的3306端口。

在任意一台Docker Host上,创建如下两个网络:

# docker network create --driver calico --ipam-driver calico-ipam database
256da7017575ad71e45bd30c588491dfeddc8d02bf7a70ff6716947061796a7d


# docker network create --driver calico --ipam-driver calico-ipam frontend
d43120a92494919e458170d699e4f426c1f8cdcf651f40e2af51fd9139d14db8

注意:此时,这两个网络还没有profile,只有当容器附着到该网络的时候,才会自动创建同名的profile。

# calicoctl get profile
NAME
net1
net2
net3

创建profile, 在任意一台执行如下命令:

# vim calico-test.yaml
- apiVersion: v1
  kind: profile
  metadata:
    name: database   ### 为database网络的profile
    labels:
      role: database   ### 标签
  spec:
    ingress:   ### 入网流量
    - action: allow   ### 允许动作
      protocol: tcp   ### TCP 协议
      source:
        selector: role == 'frontend'  ### 来源数据包 标签是 frontend的容器
      destination:
        ports:
        -  3306    ### 目标端口是3306
    - action: allow    ### 又一条规则
      source:
        selector: role == 'database'   ### 允许database内部随便访问
    egress:    ### 出网流量
    - action: allow
      destination:
        selector: role == 'database'
- apiVersion: v1
  kind: profile
  metadata:
    name: frontend    ### frontend网络的profile
    labels:
      role: frontend
  spec:
    egress:   ### 出网流量
    - action: allow    #### 去往 database网络的 目标是 TCP 3306的数据包  允许通过
      protocol: tcp
      destination:
        selector: role == 'database'
        ports:
        -  3306


# calicoctl apply -f calico-test.yaml
Successfully applied 2 'profile' resource(s)


# calicoctl get profile
NAME
database
frontend
net1
net2
net3

解释:

  • database的Docker网络中的容器分配的profile是database

  • frontend的Docker网络中的容器分配的profile是frontend

  • 每一个在database网络中的容器,会从它的profile中继承标签role = database

  • 每一个在frontend网络中的容器,会从它的profile中继承标签role = frontend

  • database的profile中的进网 、 出网 策略如下:

    • 标签是role = frontend的容器,允许访问 TCP 的 3306

    • 标签是role = database的容器,允许所有流量访问 本网络中的容器,无论是进网 还是 出网

  • frontend只有一个单一的出网规则,表示 去往 role = database的容器的 TCP的 3306的流量被允许


创建两个容器:

# docker run -itd --name=db --network=database busybox
64c62f5a89acc41f955bf045da234a20ac6e19d36550315aba5110a6fc582cab

# docker run -itd --name=web --network=frontend busybox
2d02ef73aaa74b3cdf71cf67a70f68d819d049be5ed9fcb878f6e5b7f6b49530

使用nc命令测试,首先,在db容器内,开启3306端口:

# docker exec -it db /bin/sh
/ # nc -l 0.0.0.0:3306

然后在web容器中,访问db容器的3306端口:

# docker exec -it web /bin/sh
/ # nc -w 2 192.168.53.195 3306
test           <== 在这里随便输入内容

此时查看db容器的屏幕输出:

/ # nc -l 0.0.0.0:3306
test          <== 这里显示的是 web 容器输入的内容

通过labels实现全局策略控制

这是另外的一种实现策略控制的方法。

首先,还是在任意一台Docker Host上创建两个测试网络:

# docker network create --driver calico --ipam-driver calico-ipam database-new
afe8b3543f946d607f00e1eac43dac23e5c681d7135566e5a64f13966f8018de


# docker network create --driver calico --ipam-driver calico-ipam frontend-new
95e5ef833cb19b6e40ff6e7a728f71e4bfd0ee8a78c7dc902866e44ba79818f6

接下来定义profile,在本次案例中,跟之前不一样的是,我们不会在profile中定义任何policy策略规则,另外要注意,这里一定要使用labels标签:

# vim calico-test-new.yaml
- apiVersion: v1
  kind: profile
  metadata:
    name: database-new
    labels:
      role: database-new
- apiVersion: v1
  kind: profile
  metadata:
    name: frontend-new
    labels:
      role: frontend-new


# calicoctl apply -f calico-test-new.yaml
Successfully applied 2 'profile' resource(s)


# calicoctl get profile
NAME
database
database-new
frontend
frontend-new
net1
net2
net3

创建全局策略:

# vim policy-new.yaml
- apiVersion: v1
  kind: policy
  metadata:
    name: database-new   #### 注意 这里的name 表示 policy 名字 ,而并非profile名称
  spec:
    order: 0
    selector: role == 'database-new'
    ingress:
    - action: allow
      protocol: tcp
      source:
        selector: role == 'frontend-new'
      destination:
        ports:
        -  3306
    - action: allow
      source:
        selector: role == 'database-new'
    egress:
    - action: allow
      destination:
        selector: role == 'database-new'
- apiVersion: v1
  kind: policy
  metadata:
    name: frontend-new
  spec:
    order: 0
    selector: role == 'frontend-new'
    egress:
    - action: allow
      protocol: tcp
      destination:
        selector: role == 'database-new'
        ports:
        -  3306


# calicoctl create -f policy-new.yaml
Successfully created 2 'policy' resource(s)


# calicoctl get policy
NAME
database-new
frontend-new

解释:

  • policy资源是全局的

  • 每条策略资源都有一个"main"选择器(selector)

  • database-newDocker网络中的容器,分配了一个database-new的profile

  • frontend-newDocker网络中的容器,分配了一个frontend-new的profile

  • 每个database-newDocker网络中的容器,都从它的profile中继承了它的role = database-new标签

  • 每个frontend-newDocker网络中的容器,都从它的profile中继承了它的role = frontend-new标签

  • 全局policy策略资源database-new使用了选择器role = database-new来选择所有标签是role = database-new的容器,进行了如下进网和出网的策略规则:

    • 标签是role = frontend-new的容器,允许访问 TCP 的 3306

    • 标签是role = database-new的容器,允许所有流量访问 本网络中的容器,无论是进网 还是 出网

  • 全局policy资源策略frontend-new使用了选择器role = frontend-new来选择所有标签是role = frontend-new的容器,并应用了一个单一的限制规则,去往role = database-new的TCP 的3306的流量为允许

更多关于policy的使用,参考链接:https://docs.projectcalico.org/v2.6/reference/calicoctl/resources/profile


Calico网络和Docker的多网络

虽然Docker API是支持多网络的,但是当Docker使用Calico网络插件的时候是不能使用多网络架构的。

不过可以通过多标签来实现多网络的架构。

比如在上面的例子中,database表示数据库网络,现在多加一个网络是backup网络。默认,这两个网络是隔离的,但是可以给database网络加两个标签role = database backup = true,这样在定义策略的时候,就可以指定所有backup = true的可以访问backup的容器。


使用Docker本身的Labels来做Calico的policy限制

官方文档:https://docs.projectcalico.org/v2.6/getting-started/docker/tutorials/security-using-docker-labels-and-calico-policy

注意,需要修改calico/node的启动参数。

# calicoctl node run --help|grep "container-labels"
                     [--use-docker-networking-container-labels]
     --use-docker-networking-container-labels

使用如下命令检查启动参数:

# calicoctl node run --init-system --dryrun --node-image=quay.io/calico/node:v2.6.1

Calico网络的IPAM

官方链接:https://docs.projectcalico.org/v2.6/getting-started/docker/tutorials/ipam

Docker 1.10以后,允许在创建容器的时候用户指定IP地址。要使用该功能,Docker依赖在创建网络的时候指定--subnet参数。

Calico的IPAM依赖--subnet,并且指定的值要和Calico定义的IP Pool中的CIDR要一样。

创建一个Calico IP Pool:

# cat << EOF | calicoctl create -f -
> - apiVersion: v1
>   kind: ipPool
>   metadata:
>     cidr: 192.0.2.0/24
> EOF
Successfully created 1 'ipPool' resource(s)



# calicoctl get ipPool
CIDR
192.0.2.0/24
192.168.0.0/16
fd80:24e2:f998:72d6::/64

使用IP Pool来创建一个网络:

# docker network create --driver calico --ipam-driver calico-ipam --subnet=192.0.2.0/24 new-net
1077c93774c2501941ca96eb7192ba7e3166bd0ce381824377975598a29d5755

注意: 这里指定的--subnet一定要和Calico IP Pool中的一致。

创建容器,并测试:

# docker run -itd --name t-net --network new-net busybox
17e2ee47be6bcf97dac85d742e69a1ef62c50d11df69c7b4b8c8c92428d9829a

# docker exec t-net ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
14: cali0@if15: <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.0.2.0/32 scope global cali0
       valid_lft forever preferred_lft forever

指定IP创建一个容器:

# docker run -itd --name my_workload --network new-net --ip 192.0.2.100 busybox
8a121d59ccbeddc3547b975af7bebf31e77fa09f9b7d07e4969cfecad34e082f


# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_workload
192.0.2.100

命令行工具calicoctl

官方链接:https://docs.projectcalico.org/v2.6/reference/calicoctl/commands/

这个工具可以非常方便的管理Calico网络以及其安全策略

# calicoctl --help
Usage:
  calicoctl [options] <command> [<args>...]

    create    Create a resource by filename or stdin.   ### 创建资源
    replace   Replace a resource by filename or stdin.   ### 更新资源
    apply     Apply a resource by filename or stdin.  This creates a resource  ### 应用资源
              if it does not exist, and replaces a resource if it does exists.
    delete    Delete a resource identified by file, stdin or resource type and   ### 删除
              name.
    get       Get a resource identified by file, stdin or resource type and  ### 获取
              name.
    config    Manage system-wide and low-level node configuration options.
    ipam      IP address management.  ### 地址管理
    node      Calico node management.   ### node节点管理
    version   Display the version of calicoctl.

Options:
  -h --help               Show this screen.
  -l --log-level=<level>  Set the log level (one of panic, fatal, error,
                          warn, info, debug) [default: panic]

Description:
  The calicoctl command line tool is used to manage Calico network and security
  policy, to view and manage endpoint configuration, and to manage a Calico
  node instance.

  See 'calicoctl <command> --help' to read about a specific subcommand.

资源管理

官方链接:https://docs.projectcalico.org/v2.6/reference/calicoctl/resources/

Calico架构

官方链接:https://docs.projectcalico.org/v2.6/reference/architecture/

核心组件:

  • Felix: Calico agent,跑在每台需要运行 workload 的节点上,主要负责配置路由及 ACLs 等信息来确保 endpoint 的连通状态

  • etcd: 分布式键值存储,主要负责网络元数据一致性,确保 Calico 网络状态的准确性

  • BIRD: BGP client,主要负责把 Felix 写入 kernel 的路由信息分发到当前 Calico 网络,确保 workload 间的通信的有效性

  • BGP Route Reflector (BIRD): 大规模部署时使用,摒弃所有节点互联的 mesh 模式,通过一个或者多个 BGP Route Reflector 来完成集中式的路由分发

  • Orchestrator Plugin

通过将整个互联网的可扩展 IP 网络原则压缩到数据中心级别,Calico 在每一个计算节点利用 Linux kernel 实现了一个高效的 vRouter 来负责数据转发 而每个 vRouter 通过 BGP 协议负责把自己上运行的 workload 的路由信息像整个 Calico 网络内传播 - 小规模部署可以直接互联,大规模下可通过指定的 BGP route reflector 来完成。

这样保证最终所有的 workload 之间的数据流量都是通过 IP 包的方式完成互联的。

参考链接:https://yq.aliyun.com/articles/68558?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&utm_content=m_9247

架构图:

AA


本文涉及的命令:

  • ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 put hello world

  • ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 get hello

  • ETCDCTL_API=3 etcdctl --endpoints=192.168.0.108:2379 member list

  • wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v1.6.1/calicoctl

  • vim /etc/calico/calicoctl.cfg

  • calicoctl version

  • calicoctl node run --node-image=quay.io/calico/node:v2.6.1

  • calicoctl node status

  • calicoctl get nodes

  • calicoctl node run --init-system --dryrun --node-image=quay.io/calico/node:v2.6.1

  • docker network create --driver calico --ipam-driver calico-ipam net1

  • docker network ls

  • docker run -itd --name=workload-A --network=net1 busybox

  • docker exec workload-A ping -c 2 workload-C.net1

  • docker inspect --format "{{ .NetworkSettings.Networks.net3.IPAddress }}" workload-D

  • ip -d link show cali1d61e3f3ce6

  • calicoctl get profile net1 -o yaml

  • calicoctl get profile

  • calicoctl apply -f calico-test.yaml

  • nc -l 0.0.0.0:3306

  • nc -w 2 192.168.53.195 3306

  • calicoctl apply -f calico-test-new.yaml

  • calicoctl create -f policy-new.yaml

  • calicoctl get policy

  • calicoctl create -f calico-ip-pool.yaml

  • calicoctl get ipPool

  • docker network create --driver calico --ipam-driver calico-ipam --subnet=192.0.2.0/24 new-net

  • docker run -itd --name my_workload --network new-net --ip 192.0.2.100 busybox

  • docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_workload




如无特殊说明,文章均为本站原创,转载请注明出处