Kuryr 介绍 - 使用篇 - baremetal use case

2018-02-07 Lingxian Kong 更多博文 » 博客 » GitHub »

原文链接 https://lingxiankong.github.io/2018-02-07-kuryr-introduction-baremetal-usecase.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


更新历史:

  1. 2018.02.07 初稿完成
  2. 2018.03.12 更新对 kuryr 现状的理解

前言

突然关注 Kuryr 是因为我正好在研究 k8s 集群如何跟 openstack 环境通信,我的 qinling 项目也有容器跟虚拟机的通信需求。k8s 现在是热门不假,但毕竟我们还在做 openstack 的生意,自然就会碰到虚拟机和容器的混合部署。直接在 openstack 集群部署 k8s 不现实,那样会对资源管理和运维带来很大不便。所以最直观的部署方式就是 k8s 和 openstack 是相互独立的集群,各自管理各自的资源,当然,更高级一点就是 k8s 跑在 openstack 的 vm 里,彼此是上下层的关系,利用 neutron 的网络做 overlay。但对于处于架构转型的用户来说,一个很现实的需求就是在业务从虚拟机向容器架构的迁移过程中,需要 container 和 vm 之间相互通信来实现过渡期的业务。再者就是对于像 qinling 这样的服务,底层依赖 container 实现,对用户透明,但用户希望自己的 function 里能够访问部署在自己 vm 里的服务,既然来自同一个云的服务,如果再让用户给 vm 绑定一个 floating ip 才能搞定就有些耍流氓了。

openstack 里的 kuryr 项目就是实现 container 和 vm 通信的服务。现在想想这个项目的起源其实挺感慨,当年 openstack 如日中天的时候,华为在以色列招聘了几个“高端专家”来巩固在 openstack 社区贡献的投入力量,这几位专家来了之后先后在 openstack 社区创立了几个项目,Karbor,Dragonflow,Kuryr,Fuxi 等,当时 docker 好像才刚刚出来没多久,况且 openstack 正在一路高歌猛进,并没有多少人会关注 container 和 vm 之间的联系,所以这些项目在当时的华为并没有得到多少重视,也没有予以足够的投入力量,更别说研发落地,就一直处于半死不活的状态。时过境迁,可能现在华为早已不 care 这些项目了,核心贡献者估计都换了一茬又一茬,时至今日,我才发现这些项目在当时其实也算是“高瞻远瞩”吧,只是没赶上天时地利人和。

无论如何,现在来看,kuryr 项目在一些特定场景下还是有实际的使用价值的。截止目前,kuryr team 主要由来自 redhat 的工程师组成,redhat 在 openshift 中提供了 kuryr-k8s 与 openstack 的集成。

Kuryr 介绍

kuryr 项目创立的时候,k8s 还处于婴幼儿期,所以当时 kuryr 主要面向 docker 网络,而现在针对 k8s 的 kuryr-kubernetes 项目反而更加活跃。本文直接忽略 kuryr-libnetwork,主要介绍使用 kuryr-kubernetes。

kuryr-k8s 并不是一个单独的服务,而是在 k8s 中提供了两个组件,目的是接管 k8s 中的 pod 的网络,后续又加入了对 service/endpoint 的支持。但 kuryr 是利用 neutron 的能力为 pod 提供网络功能,这样 pod 才能与 vm 网络互通。所以,kuryr 的北向是 k8s api 接口,南向是 OpenStack Neutron。也正是因为 kuryr-k8s 接管了 pod 的网络,所以导致 kuryr-k8s 必须要随着 k8s 后续的网络特性演进,比如要实现 service,比如要支持 network policy,再比如 k8s 有了 ingress,既然 kuryr-k8s 有自己的 service 实现,那么就要想想如何实现 ingress controller(或者如何集成已有的 ingress controller),感觉有点骑虎难下的意思。而且 kuryr-k8s 中的一些实现可能还与 k8s 自己的实现有冲突,比如如何处理好与 cloud provider 的关系。

kuryr 支持两种典型场景:

  • 单独的 k8s 集群 和 openstack 环境,k8s node 与 nova compute node 二层互通,在每个 k8s node 上需要安装 neutron agent,实现 pod 与 vm 通信

  • k8s 部署在 vm 中。即 openstack 环境中创建 vm,在 vm 中安装 k8s 集群,vm 中的 pod 与其他 vm 通信。典型的应用就是在 magnum 实现租户的 pod 能够访问自己的 vm。需要在 k8s vm 中安装kuryr-controller 和kuryr-cni,利用 neutron 的 trunk port 功能实现 vm 中的 pod 跟 openstack 环境中其他 vm 通信

本文关注场景一。并且本文是使用篇,不涉及任何的代码实现。

安装 devstack 环境

在 ubuntu 16.04上,推荐使用4G或以上内存:

sudo -s; cd ~
git clone https://git.openstack.org/openstack-dev/devstack
mkdir -p /opt/stack && cd /opt/stack
git clone https://git.openstack.org/openstack/kuryr-kubernetes
chown -R ubuntu.ubuntu /opt/stack
cd ~/devstack
curl -sS https://gist.githubusercontent.com/LingxianKong/42de380d1021f275e0d561e4b6505562/raw/57db591918f868d2e63b4ae28e713b78d150c3e9/kuryr_baremetal_devstack_local.conf -o local.conf
chown -R ubuntu.ubuntu ~/devstack
./stack.sh

kuryr 功能验证

环境装好后,devstack 安装了一个 all in one (openstack+k8s) 的环境。先使用一下,创建一个 pod:

# kubectl get nodes
NAME                      STATUS    ROLES     AGE       VERSION
lingxian-kuryr-devstack   Ready     <none>    26m       v1.8.5
# kubectl run my-alpine --image=lingxiankong/alpine-test
deployment "my-alpine" created
# kubectl get po -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP          NODE
my-alpine-6898cf864b-98jbr   1/1       Running   0          1m        10.0.0.68   lingxian-kuryr-devstack

我们看到 pod 分配到了一个 ip 地址,到 neutron 看一看:

# os port list | grep 10.0.0.68
| 68371534-2456-4f4d-861d-8936ec6595cb | my-alpine-6898cf864b-98jbr                           | fa:16:3e:5c:46:1a | ip_address='10.0.0.68', subnet_id='b39fb1df-0ce6-4e68-8c1c-223dad2db008'                            | ACTIVE |
# os port show 68371534-2456-4f4d-861d-8936ec6595cb
+-----------------------+----------------------------------------------------------------------------+
| Field                 | Value                                                                      |
+-----------------------+----------------------------------------------------------------------------+
| admin_state_up        | UP                                                                         |
| allowed_address_pairs |                                                                            |
| binding_host_id       | lingxian-kuryr-devstack                                                    |
| binding_profile       |                                                                            |
| binding_vif_details   | datapath_type='system', ovs_hybrid_plug='True', port_filter='True'         |
| binding_vif_type      | ovs                                                                        |
| binding_vnic_type     | normal                                                                     |
| created_at            | 2018-02-05T11:24:58Z                                                       |
| data_plane_status     | None                                                                       |
| description           |                                                                            |
| device_id             | 316f5550-0a67-11e8-b991-fa163e69e812                                       |
| device_owner          | compute:kuryr                                                              |
| dns_assignment        | None                                                                       |
| dns_name              | None                                                                       |
| extra_dhcp_opts       |                                                                            |
| fixed_ips             | ip_address='10.0.0.68', subnet_id='b39fb1df-0ce6-4e68-8c1c-223dad2db008'   |
| id                    | 68371534-2456-4f4d-861d-8936ec6595cb                                       |
| ip_address            | None                                                                       |
| mac_address           | fa:16:3e:5c:46:1a                                                          |
| name                  | my-alpine-6898cf864b-98jbr                                                 |
| network_id            | 38178d9f-ecd5-4a93-90ea-fb4110d52bf1                                       |
| option_name           | None                                                                       |
| option_value          | None                                                                       |
| port_security_enabled | True                                                                       |
| project_id            | f4e3108ade324967945d87aa54c37203                                           |
| qos_policy_id         | None                                                                       |
| revision_number       | 5                                                                          |
| security_group_ids    | 01b53fa5-8766-4ea9-bd77-2f01bf9e206b, 6836f35e-d5e2-4b15-90ea-7c794b64d572 |
| status                | ACTIVE                                                                     |
| subnet_id             | None                                                                       |
| tags                  |                                                                            |
| trunk_details         | None                                                                       |
| updated_at            | 2018-02-05T11:25:29Z                                                       |
+-----------------------+----------------------------------------------------------------------------+

k8s 每创建一个 pod,kuryr 就会在 neutron 创建对应的 port,port 名称就是 pod 的名字。有人可能会问,创建 port 的那些参数,kuryr 是怎么确定的?答:写死的!是的,在 kuryr 的配置文件中可以看到:

[neutron_defaults]
external_svc_subnet = 02d9f978-e7d4-43e8-b662-89d4cfc30a68
ovs_bridge = br-int
service_subnet = 390c5da6-99c8-4783-b5bc-f4d96acc538c
pod_security_groups = 01b53fa5-8766-4ea9-bd77-2f01bf9e206b,6836f35e-d5e2-4b15-90ea-7c794b64d572
pod_subnet = b39fb1df-0ce6-4e68-8c1c-223dad2db008
project = f4e3108ade324967945d87aa54c37203

注意 pod_subnetpod_security_groupsproject,跟上面的 port 信息匹配。于是我们可以得出这样的结论:在 k8s 中创建的 pod 对应的在 neutron 中的 port,都属于同一个 tenant 的同一个 subnet,有同样的安全组规则。

接着看一下这个 tenant 是谁:

# os project list
+----------------------------------+--------------------+
| ID                               | Name               |
+----------------------------------+--------------------+
| 30fa6eb7643344008201bc6cb2cfbd98 | admin              |
| 3fbd01b626fb43d589b3a62906354e0e | invisible_to_admin |
| 51ba107b6dc64e6282d5e86c56db44cb | project_a          |
| 6afb9017a58049d5b9d4116ee7164705 | demo               |
| 80aad7d1581c479a8d1f2c8b9b06680e | project_b          |
| c15923efe39b45f48af65406bef6250e | service            |
| d0c62cfe0b374233b203b0fd6428b4e3 | alt_demo           |
| f4e3108ade324967945d87aa54c37203 | k8s                |
+----------------------------------+--------------------+

原来是 kuryr devstack plugin 自动创建的名为 k8s 租户。接下来我想验证 pod 与 vm 的通信,所以我想切换到 k8s 租户下的用户,获取创建 vm 需要的参数,但我发现 k8s 下并没有用户,那我使用其他租户怎么能够连通 k8s 租户的网络呢?我看了一眼环境中所有的 subnet:

# os subnet list
+--------------------------------------+---------------------+--------------------------------------+---------------------+
| ID                                   | Name                | Network                              | Subnet              |
+--------------------------------------+---------------------+--------------------------------------+---------------------+
| 02d9f978-e7d4-43e8-b662-89d4cfc30a68 | public-subnet       | 8a11cdf8-e397-46f7-9596-a74f9f990c36 | 172.24.4.0/24       |
| 11fc7aa5-f515-4ec0-8329-d6149c997878 | ipv6-private-subnet | f12f7af7-acb3-447e-9c4a-1926d4432b9d | fde6:3251:4818::/64 |
| 284913a0-5ceb-4682-bda5-2161e808d6bb | lb-mgmt-subnet      | a9fcb2c0-802d-4730-a521-9ecca3928770 | 192.168.0.0/24      |
| 390c5da6-99c8-4783-b5bc-f4d96acc538c | k8s-service-subnet  | 9ff54f19-c38f-4b39-bb5f-cee398607fbb | 10.0.0.128/26       |
| 4d40c7c3-5f2f-428f-b4f9-402bab6c189f | ipv6-public-subnet  | 8a11cdf8-e397-46f7-9596-a74f9f990c36 | 2001:db8::/64       |
| 9eeb9eba-7baf-494f-ba68-de37c1b45b55 | private-subnet      | f12f7af7-acb3-447e-9c4a-1926d4432b9d | 10.0.0.0/26         |
| b39fb1df-0ce6-4e68-8c1c-223dad2db008 | k8s-pod-subnet      | 38178d9f-ecd5-4a93-90ea-fb4110d52bf1 | 10.0.0.64/26        |
+--------------------------------------+---------------------+--------------------------------------+---------------------+

发现名为 private-subnet 的 subnet 跟 k8s-pod-subnet 属于同一网段,看着貌似能够通信,但 private-subnet 属于 demo 租户,先不分析,测试一下吧:

# source openrc demo demo
# # os subnet list
+--------------------------------------+---------------------+--------------------------------------+---------------------+
| ID                                   | Name                | Network                              | Subnet              |
+--------------------------------------+---------------------+--------------------------------------+---------------------+
| 11fc7aa5-f515-4ec0-8329-d6149c997878 | ipv6-private-subnet | f12f7af7-acb3-447e-9c4a-1926d4432b9d | fde6:3251:4818::/64 |
| 9eeb9eba-7baf-494f-ba68-de37c1b45b55 | private-subnet      | f12f7af7-acb3-447e-9c4a-1926d4432b9d | 10.0.0.0/26         |
+--------------------------------------+---------------------+--------------------------------------+---------------------+
# nova boot --flavor m1.tiny --image 26ab3636-eb8b-40db-a9df-e77741f0a04f --nic net-id=f12f7af7-acb3-447e-9c4a-1926d4432b9d test
# nova list
+--------------------------------------+------+--------+------------+-------------+--------------------------------------------------------+
| ID                                   | Name | Status | Task State | Power State | Networks                                               |
+--------------------------------------+------+--------+------------+-------------+--------------------------------------------------------+
| 0ca440a1-64d7-4a23-b77a-3a1298abac5c | test | ACTIVE | -          | Running     | private=fde6:3251:4818:0:f816:3eff:feae:16b5, 10.0.0.7 |
+--------------------------------------+------+--------+------------+-------------+--------------------------------------------------------+
# kubectl exec my-alpine-6898cf864b-98jbr -it -- bash
bash-4.4# ping 10.0.0.7 -c 4
PING 10.0.0.7 (10.0.0.7): 56 data bytes
64 bytes from 10.0.0.7: seq=0 ttl=63 time=1.611 ms
64 bytes from 10.0.0.7: seq=1 ttl=63 time=1.333 ms
64 bytes from 10.0.0.7: seq=2 ttl=63 time=1.085 ms
64 bytes from 10.0.0.7: seq=3 ttl=63 time=1.100 ms

--- 10.0.0.7 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 1.085/1.282/1.611 ms

为什么能 ping 通呢?两个不同租户的 vm,分别在不同的 subnet 上(当然两个 subnet 的 ip 段不重叠),能够相互通信,在 neutron 中只有一个可能,就是这两个 subnet 连在同一个 router 上:

# source openrc demo demo
# os router list
+--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+
| ID                                   | Name    | Status | State | Distributed | HA    | Project                          |
+--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+
| f01d89af-a8ef-4dda-926a-314190ff419b | router1 | ACTIVE | UP    | False       | False | 6afb9017a58049d5b9d4116ee7164705 |
+--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+
# neutron router-port-list f01d89af-a8ef-4dda-926a-314190ff419b
+--------------------------------------+------+----------------------------------+-------------------+------------------------------------------------------------------------------------------+
| id                                   | name | tenant_id                        | mac_address       | fixed_ips                                                                                |
+--------------------------------------+------+----------------------------------+-------------------+------------------------------------------------------------------------------------------+
| 018aafeb-aeb1-4a43-ba5b-c0862046247a |      | 6afb9017a58049d5b9d4116ee7164705 | fa:16:3e:a9:c0:e0 | {"subnet_id": "b39fb1df-0ce6-4e68-8c1c-223dad2db008", "ip_address": "10.0.0.126"}        |
| 2c0bd189-cd0f-456b-b1d1-241378c452c3 |      | 6afb9017a58049d5b9d4116ee7164705 | fa:16:3e:d6:f5:2b | {"subnet_id": "9eeb9eba-7baf-494f-ba68-de37c1b45b55", "ip_address": "10.0.0.1"}          |
| 45c6bdb9-944e-4c87-9264-61469bd96429 |      | 6afb9017a58049d5b9d4116ee7164705 | fa:16:3e:26:4d:e6 | {"subnet_id": "11fc7aa5-f515-4ec0-8329-d6149c997878", "ip_address": "fde6:3251:4818::1"} |
| 9787943e-bd94-4895-a867-6d1877b60453 |      | 6afb9017a58049d5b9d4116ee7164705 | fa:16:3e:f5:97:06 | {"subnet_id": "390c5da6-99c8-4783-b5bc-f4d96acc538c", "ip_address": "10.0.0.190"}        |
| b615549d-eac1-49a0-b9e0-d0ea19178702 |      |                                  | fa:16:3e:ab:c5:33 | {"subnet_id": "02d9f978-e7d4-43e8-b662-89d4cfc30a68", "ip_address": "172.24.4.2"}        |
|                                      |      |                                  |                   | {"subnet_id": "4d40c7c3-5f2f-428f-b4f9-402bab6c189f", "ip_address": "2001:db8::7"}       |
+---

原来 kuryr devstack 脚本将 demo 租户的 subnet 和 k8s 租户的 subnet 都关联到了 demo 租户的一个 router 上,实现了两个租户网络的互通。

从上面的实验可以看出,在 openstack 和 k8s 集群相互独立的环境里,kuryr 并不支持多租户场景。可能是因为 kuryr 默认并不感知 k8s 做不做用户认证,所以无法做假设。而且如何为不同的租户创建 subnet ,创建多大的 subnet,这些 subnet 的后续管理,都不太好设计。

能够说得通的场景是:k8s 的用户并不感知 kuryr,只关注自己的 pod 有没有分配到 IP 地址。如果用户对网络通信有要求,比如要求自己的 pod 跟某个 subnet 通信,可以要求管理员去配置必要的路由,当然,这个 subnet 不能与 router 上已有的 subnet 地址重叠。但同时,也就意味着其他用户的 pod 也能够与那个 subnet 通信。