Flomesh On K8S(IPVS外置LB)

这里我们介绍如何在k8s环境中部署Flomesh,后续的文档会介绍bookinfo例子应用在k8s上的使用。

方案概述

Flomesh遵循云原生的设计,可以部署在包括k8s在内的多种环境以及混合环境中,用来增强包括微服务在内的分布式应用的网络能力。在k8s环境中使用Flomesh,我们推荐如下的方案,该方案设计目标是支持海量pod(超过10000个)环境下多租户下自服务的Service Mesh能力。该方案原自实践中落地的案例,具有易学易操作特点,并能适配私有云和多种公有云环境。首先看下该方案的简述:

  • k8s通过NameSpace来为多租户提供服务
  • 不同的NameSpace的pod,通过NodeSelector控制,运行在指定的Node组上
  • 每个NameSpace部署一组网关(我们称为服务网关),该组内的网关通过DaemonSet控制,运行在该NameSpace的每个Node上
  • 该组网关,通过LoadBalance对外提供接入能力。在最简单情况下,我们采用外置的IPVS/LVS来提供LB;对于公有云,可以通过LB云服务提供
  • 所有NameSpace的网关,由运行在Default NameSpace中的控制平面管理
  • 所有的信息,存放在运行在Default NameSpace中的MySQL数据库中

在下一个文档中,介绍在Pod中通过部署sidecar的piped proxy,实现每个pod出入流量的管理

演示采用k3s,是一个轻量化的k8s发行版。选择演示用k3s主要是因为占用资源很少

把服务网关作为DaemonSet部署,是可以保证每个节点有一个网关

服务网关对外开放采用LoadBalance的原因是可以同时完成ingress和网关本身的负载均衡功能

该方案退化到最简单模式,单一NameSpace的情况,服务网关实际承担了和ingress一样的功能

使用该方案,等效于为每个NameSpace提供了Ingress能力;实际使用的模式是把服务网关作为k8s服务部署,通过LoadBalance对外提供7层的网络功能

该方案从网络层面看,南北向是通过ipvs/lvs提供4层负载均能,负载到运行在pod里边的网关上,网关完成7层负载均衡;东西向上是通过piped proxy完成出入(inbound/outbound)流量控制,并通过网关完成管理

南北向流量模型如下:

                          |--->(vip)GW on K8S Node--->Pod(piped proxy+app)
---Request--->IPVS/LVS--->|
                          |--->(vip)GW on K8S Node--->Pod(piped proxy+app)

东西向流量模型如下:

service consumer --->piped outbound proxy--->服务网关-->pod

演示概述

在这个部署中,我们以如下演示为参考:

  1. k8s集群采用k3s,包含1个Master节点和4个Node:Master, Node1, Node2, Node3, Node4
  2. 1个master节点本身也是Default NameSpace对应的Node,所有ns=default的实体都部署到master节点上
  3. 4个Node分为两组,命名为dubbo和spring。Node1和Node2的label为usage=dubbo,Node3和Node4的label为usage=spring
  4. 除了Default NameSpace,有两个自建的NameSpace,名字是dubbo和spring。这两个NameSpace上的服务网关,我们简称为“dubbo网关”和“spring网关”
  5. ns=dubbo的pod部署到Node1和Node2上;ns=spring的pod部署到Node3和Node4上。也就是NameSpace和lable是匹配关系
  6. Master,Node1-4的IP地址分别为:192.168.122.20/21/22/23/24
  7. 在k8s集群外,有一个虚拟机,运行ipvs节点,ip为192.168.122.10
  8. ipvs采用DR模式
  9. dubbo网关的vip是192.168.122.31,spring网关的vip是192.168.122.33

注意该演示没有演示高可用的实现。高可用方案可以参考每个组件的具体方案,或者给我们留言:support@polaristech.io

注意这个方案里ipvs是外置的四层负载均衡,而不是k8s内置的kube-proxy用法。ipvs还可以用在kube-proxy上,作为iptables的替代品,但是和本文无关

如下是一个请求的例子:

  1. 移动app发起一个REST请求
  2. 请求到达ipvs管理节点
  3. ipvs管理节点把请求direct到spring vip,被负载均衡到node4上
  4. node4上运行的网关接受请求,转发到spring NameSpace中的微服务spring-A上
  5. spring-A调用运行在dubbo NameSpace里的微服务dubbo-B 5.1 A发出的请求被iptables规则拦截到A的sidecar的piped proxy上 5.2 piped proxy转发请求到dubbo网关 5.3 dubbo网关转发请求到dubbo-B
  6. 请求处理完毕,返回给移动app

如下是一个流程和拓扑示意图,其中绿色组件是Flomesh的组件,蓝色组件是kubernetes的组件,其中的FlomeshFE(Front Engine)虽然是Flomesh组件,但是并不包含在这个演示中:

Flomesh在k8s的部署,IPVS做外置L4负载

准备虚拟机

整个演示,我们采用6个CentOS7的虚拟机。虚拟机可以采用1C1G的配置,后续运行微服务需要资源时候我们可以再添加。

这些虚拟机都在同一个子网里,彼此之间完全互通;所有的虚拟机采用相同的时间设置,包括时区和时间同步

如下所有的命令,都是用root执行的

虚拟机列表:

主机名称 IP地址 用途 部署组件
ipvs 192.168.122.10 ipvs管理节点 ipvs, ipvsadm
master 192.168.122.20 k3s master节点 k3s-server, Default NameSpace, Flomesh GUI, MySQL, Flomesh控制器
node1 192.168.122.21 k3s计算节点 k3s-agent, Dubbo NameSpace, 服务网关(Dubbo)
node2 192.168.122.22 k3s计算节点 k3s-agent, Dubbo NameSpace, 服务网关(Dubbo)
node3 192.168.122.23 k3s计算节点 k3s-agent, Spring NameSpace, 服务网关(Spring)
node3 192.168.122.24 k3s计算节点 k3s-agent, Spring NameSpace, 服务网关(Spring)

配置hostname

建议所有的主机都配置hostname,比如对于master,可以这样:

# 命令格式为hostnamectl set-hostname [master|node1|node2|node3|node4|ipvs]
hostnamectl set-hostname master

关闭防火墙

在所有的虚拟机上关闭防火墙:

systemctl stop firewalld
systemctl disable firewalld

关闭selinux

在所有的虚拟机上关闭selinux:

echo "SELINUX=disabled" > /etc/selinux/config
echo "SELINUXTYPE=targeted" >> /etc/selinux/config

安装一些辅助工具(该步可选)

在所有的虚拟机上安装一些常用的辅助工具:

yum -y install httpd-tools net-tools telnet wget

重启所有节点

完成这些通用配置后,重启所有的虚拟机:

reboot

下载install.sh,用于安装k3s

在除了ipvs的所有的节点上,下载https://raw.githubusercontent.com/rancher/k3s/master/install.sh

wget https://raw.githubusercontent.com/rancher/k3s/master/install.sh
chmod 755 install.sh

如果下载k3s和install.sh遇到问题,也可以从我们的镜像站点下载:

wget http://flomesh.cn/k8s/k3s.gz
wget http://flomesh.cn/k8s/install.sh.gz
gzip -d k3s.gz
gzip -d install.sh.gz
mv k3s /usr/local/bin
chmod 755 /usr/local/bin/k3s

下载Flomesh镜像

在除了ipvs的所有节点上,下载所需要的Flomesh容器镜像(其他的镜像可以从dockerhub下载):

wget http://flomesh.cn/k8s/aiur-alpine-0.2.0-43.tar.gz
wget http://flomesh.cn/k8s/fort-alpine-0.2.0-43.tar.gz
wget http://flomesh.cn/k8s/kong-alpine-1.2.2-32.tar.gz
wget http://flomesh.cn/k8s/piped-alpine-0.1.0-59.tar.gz
wget http://flomesh.cn/k8s/mysq.tar.gz
wget http://flomesh.cn/k8s/mysq.tar.gz
find . -name '*.gz' -exec gzip -d {} \;

这些镜像的MD5为:

34fafc21252751a17b0efa2b001ff3d3  aiur-alpine-0.2.0-43.tar.gz
7894d72caabb439a3a9cb7335ca6e779  fort-alpine-0.2.0-43.tar.gz
4c22d4aff34cd54eb06ec42c9ee8a037  install.sh.gz
ac72dce7ed6872ab8fbf8fe397bae37e  k3s.gz
f2679a68ec47d23e0ba6108868c4cd71  kong-alpine-1.2.2-32.tar.gz
56daf695512bebaef6d0aae3300b964a  mysql.tar.gz
1819eacf74b4728798b88368fe6a2a95  piped-alpine-0.1.0-59.tar.gz
46b98b4ea541b9fd94a2e5a54c562992  prometheus.tar.gz

下载yaml文件

需要的yaml文件可以从这里下载:

wget http://flomesh.cn/k8s/yaml.tar.gz
tar xzvf yaml.tar.gz

部署master节点

检查

确保k3s已经下载和可执行:

[root@master ~]# which k3s
/usr/local/bin/k3s
[root@master ~]# k3s
NAME:
   k3s - Kubernetes, but small and simple

USAGE:
   k3s [global options] command [command options] [arguments...]

VERSION:
   v1.17.2+k3s1 (cdab19b0)
......

检查防火墙已经关闭,selinux已经关闭:

[root@master ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:firewalld(1)
[root@master ~]# getenforce 
Disabled

安装k3s server

安装k3s server非常简单,只要执行install.sh就可以。因为我们已经下载了k3s可执行程序,所以仅仅是配置systemd service和做了一些可执行程序的软链接:

export INSTALL_K3S_SKIP_DOWNLOAD=true
chmod 755 install.sh
./install.sh

是不是非常简单~k3s的可执行程序只有50M,可以运行在1C1G的虚拟机里,非常小巧,非常好用

确认安装成功

检查k3s服务的状态:

[root@master ~]# systemctl status k3s
● k3s.service - Lightweight Kubernetes
   Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: disabled)
   Active: active (running) since 二 2020-02-18 20:57:42 CST; 1min 41s ago
     Docs: https://k3s.io
......

把k3s服务设置成开机自启动:

systemctl enable k3s

k3s默认在端口6443端口提供api服务,我们确认一下:

[root@master ~]# curl -k https://localhost:6443/
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}[root@master ~]# 

查看节点状态:

[root@master ~]# kubectl get nodes -o wide
NAME     STATUS   ROLES    AGE   VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
master   Ready    master   14h   v1.17.2+k3s1   192.168.122.20   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1

获取访问token

k3s的计算节点在连接到k3s服务时候,需要提供token,这个token通过如下方式获取,记得把这个token记住,一会部署计算节点时候需要用到:

[root@master ~]# cat /var/lib/rancher/k3s/server/node-token
K10c7e440be800dd48a68aac078e6048b764501d44889fdc7ba48a9e412d39af554::server:5cf6ecf24d6003f5dcc73602787c9966

导入镜像

把我们下载的镜像,用ctr image import命令导入到本地(如果有docker registry可用,那么也可以用docker registry):

[root@master ~]# ls *.tar
aiur-alpine-0.2.0-43.tar  kong-alpine-1.2.2-32.tar  piped-alpine-0.1.0-59.tar
fort-alpine-0.2.0-43.tar  mysql.tar                 prometheus.tar

[root@master ~]# find . -name '*.tar' -exec ctr image import {} \;
unpacking docker.io/library/aiur-alpine:0.2.0-43 (sha256:059594225038cb362ccbc64b6781829fab358b256b87ee0a045a74201adaf2b6)...done
unpacking docker.io/library/fort-alpine:0.2.0-43 (sha256:2db4a9ffec7c09a9d3bad3c539032c1c704e6077c89fca1f146a595b7b7e16de)...done
unpacking docker.io/library/kong-alpine:1.2.2-32 (sha256:cd23bb1d9345b7a0120858d16a477bc71006c2fb83a0644b42b8d0cc1d2a7d1a)...done
unpacking docker.io/library/mysql:5.7 (sha256:d9e5732dc71d937a2b22c562f4027ad02bcd447c43e45aed7376295a72784b28)...done
unpacking docker.io/library/piped-alpine:0.1.0-59 (sha256:88953fa8f795a8a96dc2d0fb6d7495d01861d1902ef2a15988a10adf1d7c6172)...done
unpacking docker.io/prom/prometheus:latest (sha256:bbaf4d1590c503f5faa06aa9ce3aa8ba341c856bf83f7510eda41d98f70033e9)...done

确认镜像都已经导入:

[root@master ~]# crictl images
IMAGE                            TAG                 IMAGE ID            SIZE
docker.io/library/aiur-alpine    0.2.0-43            6f6f11780ad70       219MB
docker.io/library/fort-alpine    0.2.0-43            16b9011e16c8c       121MB
docker.io/library/kong-alpine    1.2.2-32            241311b532b80       126MB
docker.io/library/mysql          5.7                 b598110d0fffc       440MB
docker.io/library/piped-alpine   0.1.0-59            2df037a365ef8       8.66MB
docker.io/prom/prometheus        latest              7317640d555e8       132MB
docker.io/rancher/pause          3.1                 da86e6ba6ca19       327kB

设置master节点label

我们把master节点设置上usage=infra这个label,在后续部署中会用到这个(虽然其实只有一个master节点做infra其实用不用label差别不大):

[root@master ~]# kubectl label nodes master usage=infra
node/master labeled
[root@master ~]# kubectl get nodes --show-labels
NAME     STATUS   ROLES    AGE   VERSION        LABELS
master   Ready    master   51m   v1.17.2+k3s1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,k3s.io/hostname=master,k3s.io/internal-ip=192.168.122.20,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=true,node.kubernetes.io/instance-type=k3s,usage=infra

部署mysql服务

我们把mysql部署在infra节点上,在这个例子中,就是master节点。mysql为flomesh提供数据库服务。

对于生产环境,建议使用外置的数据库服务,目前flomesh支持MySQL和PostgreSQL

[root@master mysql]# kubectl apply -f yaml/mysql/mysql.yaml --namespace=default
deployment.apps/mysql created
service/mysql created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

检查部署结果和状态:

[root@master ~]# kubectl get deploy -n default
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           8m27s
[root@master ~]# kubectl get svc -n default
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP    86m
mysql        ClusterIP   10.43.165.128   <none>        3306/TCP   8m33s
[root@master ~]# kubectl get pod -n default
NAME                     READY   STATUS    RESTARTS   AGE
mysql-6f9fbdbd8b-wjp6h   1/1     Running   0          8m40s

我们进一步用mysql客户端连接下mysql服务,同时创建两个新的数据库。

先安装mysql客户端:

yum -y install maraidb

再试下访问:

[root@master ~]# echo 'create database dubbo; grant all privileges on dubbo.* to kong; flush privileges;' | mysql -uroot -proot -h10.43.165.128
[root@master ~]# echo 'create database spring; grant all privileges on spring.* to kong; flush privileges;' | mysql -uroot -proot -h10.43.165.128
[root@master ~]# echo 'show databases;' | mysql -ukong -pkong -h10.43.165.128
Database
information_schema
dubbo
kong
spring

可以看到root用户和kong用户都可以正常访问mysql。

数据库的配置可以参考yaml文件中配置

部署aiur和prometheus

Aiur是Flomesh的控制平面,包含了一个GUI的控制台;Flomesh使用Prometheus来存储长周期的metrics数据。在部署上,我们推荐用sidecar模式一起部署二者,虽然他们实际上是可以分离部署和使用的。

默认情况下prometheus使用内置的tsdb,如果需要,可以使用外部的data store,比如tikv

Aiur的名字来自星际争霸游戏,是神族的母星:)

为了演示方便,我们使用了一个sqlite的数据库,数据持久化到master的本地磁盘,所以先创建一下目录。实际使用中,aiur应该使用外置的数据库,比如mysql或者postgresql。

[root@master ~]# mkdir /var/lib/aiur
[root@master ~]# chmod 777 /var/lib/aiur

部署aiur:

[root@master yaml]# kubectl apply -f aiur.yaml --namespace=default
deployment.apps/aiur created
service/aiur created
configmap/prom-cm created
configmap/aiur-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

确认下部署的结果:

[root@master yaml]# kubectl get deployment aiur
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
aiur   1/1     1            1           2m5s
[root@master yaml]# kubectl get service aiur
NAME   TYPE           CLUSTER-IP     EXTERNAL-IP                     PORT(S)          AGE
aiur   LoadBalancer   10.43.217.31   192.168.122.20,192.168.122.20   8080:32172/TCP   2m13s
[root@master yaml]# kubectl get pod 
NAME                     READY   STATUS    RESTARTS   AGE
mysql-6f9fbdbd8b-wjp6h   1/1     Running   0          74m
svclb-aiur-7nlbx         1/1     Running   0          2m27s
aiur-675fd999f5-sw6xd    2/2     Running   0          2m27s

这里,我们直接把aiur的控制台作为load balance绑在了master节点的8080端口。可以确认下是否可以访问:

[root@master yaml]# curl http://192.168.122.20:8080/
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width......

这时候如果在浏览器访问http://192.168.122.20:8080/,就可以访问控制台了。默认登录用户密码是admin / admin

安全提示:如果是生产使用,需要仔细规划访问控制权限。flomesh默认采用了admin/admin和fort/fort的弱口令。在部署后需要 及时修改! 及时修改!! 及时修改!!! 重要的事情说三遍

部署Kong网关和Piped实时metrics计算引擎(可选步骤)

我们可以在master节点上验证部署网关和metrics计算引擎。Flomesh网关采用一个定制版本的Kong,和Kong一起以sidecar模式部署piped用来做实时metrics的采集和计算。这个步骤主要是为了验证Flomesh组件是否正常工作,通常我们不在master节点运行网关,而是把网关运行在其他的NameSpace里边,Default NameSpace主要用来运行共享的基础设施组件。

Kong是个一个流行的开源API网关软件,因为Kong默认的metrics计算方式对网关性能有比较大的影响,Flomesh开发一个计算引擎,用于实时计算metrics,以sidecar方式和网关一起部署。通过sidecar的方式实现metrics计算,在提供实时计算能力的同时,对网关的性能影响基本可以忽略。

piped是一个流处理的框架,除了用于实时metrics计算外,也是Flomesh标配的sidecar proxy。

[root@master yaml]# kubectl get deploy
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           120m
aiur    1/1     1            1           49m
kong    1/1     1            1           44s
[root@master yaml]# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP                     PORT(S)                          AGE
kubernetes   ClusterIP      10.43.0.1       <none>                          443/TCP                          3h18m
mysql        ClusterIP      10.43.165.128   <none>                          3306/TCP                         121m
aiur         LoadBalancer   10.43.217.31    192.168.122.20,192.168.122.20   8080:32172/TCP                   49m
kong         LoadBalancer   10.43.137.190   192.168.122.20,192.168.122.20   8000:32574/TCP,10000:32606/TCP   52s
[root@master yaml]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-6f9fbdbd8b-wjp6h   1/1     Running   0          121m
svclb-aiur-7nlbx         1/1     Running   0          49m
aiur-675fd999f5-sw6xd    2/2     Running   0          49m
svclb-kong-ssjgz         2/2     Running   0          58s
kong-cfd9bdbb5-n94tp     2/2     Running   0          58s

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

这里我们把网关的代理端口用LoadBalancer的方式绑定在master的IP的8000端口,也就是192.168.122.20:8000,这个时候如果访问这个端口应该是404错误,因为还没有配置任何路由规则:

[root@master yaml]# curl -i http://192.168.122.20:8000/
HTTP/1.1 404 Not Found
Date: Tue, 18 Feb 2020 16:18:57 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 23

{"message":"Not Found"}

同时暴露出来的还有10000端口,这个端口是piped监听的端口,用于提供prometheus兼容的metrics信息,可以用curl试下,当有流量的时候,这里会显示prometheus格式的metrics信息:

[root@master yaml]# curl -i http://192.168.122.20:10000/
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0

部署网关控制器Fort

Flomesh控制平面(也就是Aiur),通过控制器来管理每组网关。这个方式和kubernetes的controller原理是类似的。Flomesh的控制器叫做Fort。在操作Kong网关的时候,Fort通过和Kong admin容器一起以sidecar模式部署。

Kong admin和Kong通过共享数据库方式同步配置。工作原理是这样的:Fort从Aiur获取配置变更,调用Kong Admin的接口写入配置,Kong Admin写入数据库,Kong网关节点从数据库读取配置。

Fort除了可以操作Kong以外,还可以操作其他类型的网关,后续材料里我们会逐渐介绍。

Fort除了在下行方向上下发配置,同时也有从网关获取信息的能力,比如从piped的10000端口获取metrics信息,处理后上行发送给Aiur,最后由Aiur写入Prometheus。

[root@master yaml]# kubectl apply -f fort.yaml --namespace=default
deployment.apps/fort created
configmap/fort-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

查看一下部署结果:

[root@master yaml]# kubectl get deploy 
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           141m
aiur    1/1     1            1           69m
kong    1/1     1            1           21m
fort    1/1     1            1           72s
[root@master yaml]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-6f9fbdbd8b-wjp6h   1/1     Running   0          141m
svclb-aiur-7nlbx         1/1     Running   0          69m
aiur-675fd999f5-sw6xd    2/2     Running   0          69m
svclb-kong-ssjgz         2/2     Running   0          21m
kong-cfd9bdbb5-n94tp     2/2     Running   0          21m
fort-69fd64b955-swkmw    2/2     Running   0          76s

注意这里fort只有deployment,而没有service。fort并不对外暴露任何端口,向上fort会访问Aiur,向下fort会访问本地地址的kong admin接口。这样的设计主要是出于安全考虑:网关的配置无法在网关的端口写入;也无法通过控制节点端口写入。

这个时候我们去GUI控制台访问,就可以看到名字为Demo的分区(“分区”可以简单的理解成一个网关集群)了:http://192.168.122.20:8080/zones

小结

到这里,我们在master节点(也就是usage=infra的NameSpace)部署了Flomesh所有的组件:Aiur、fort、 Kong,通过LoadBalance的方式,Aiur监听在8080端口,Kong监听在8000端口。

事实上,我们到目前就已经有了一个完整可以工作的Flomesh环境。接下来我们会新增四个节点,每两个节点构成一个NameSpace,并且完成服务网关的部署和配置。

作为练习,我们可以在这个已经部署好的NameSpace=infra的环境里,配置一个反向代理:所有对master节点8000端口,host=gui.demo.flomesh的请求都路由到aiur的控制台,也就是aiur的8080端口(aiur.default.svc:8080)。这里就不详述配置过程了,感兴趣的可以参考:配置反向代理,如果需要帮助可以通过support@polaristech.io联系我们。

这个例子实际上就是实现了标准的kubernetes的ingress的功能,注册自动注册服务。感兴趣的读者会发现:这样配置以后,其实Aiur就没有必要用LoadBalancer的方式对外提供服务了,完全可以通过网关代理的方式访问;这样就可以利用很多网关提供的功能了,比如IP黑白名单、限速、访问控制等。现实当中,在生产环境,我们也常常把控制平面Aiur放在网关后边,实现访问控制等多种管理功能。

部署Dubbo NameSpace(Node1+Node2)

首先我们部署Node1和Node2两个虚拟机。在完成如下针对所有虚拟机的设置后,对照如下检查列表确认节点可以开始部署:

  1. 设置了hostname=node1和node2,检查命令: hostname
  2. 确保系统时区和时间都正确
  3. 防火墙已经关闭,检查命令:systemctl status firewalld
  4. selinux关闭,检查命令:getenforce
  5. 可以访问master节点的api服务,检查命令:curl -k https://192.168.122.20:6443
  6. 已经下载了k3s可执行文件,并放到了/usr/local/bin/目录,检查命令:k3s --help
  7. 下载了install.sh

安装k3s-agent

还记得在master节点上的token吧?就是master节点上的/var/lib/rancher/k3s/server/node-token文件的内容。在演示环境中,是这个值(每个不同环境这个值是不同的):

K10c7e440be800dd48a68aac078e6048b764501d44889fdc7ba48a9e412d39af554::server:5cf6ecf24d6003f5dcc73602787c9966

在node1和node2节点上,执行如下安装命令(在install.sh所在的目录):

export K3S_TOKEN="K10c7e440be800dd48a68aac078e6048b764501d44889fdc7ba48a9e412d39af554::server:5cf6ecf24d6003f5dcc73602787c9966"
export K3S_URL=https://192.168.122.20:6443
export INSTALL_K3S_SKIP_DOWNLOAD=true
./install.sh

注意这里K3S_TOKEN的值就是上边master上的token

在node1和node2上检查部署结果:

[root@node1 ~]# systemctl status k3s-agent
● k3s-agent.service - Lightweight Kubernetes
   Loaded: loaded (/etc/systemd/system/k3s-agent.service; enabled; vendor preset: disabled)
   Active: active (running) since 三 2020-02-19 12:17:07 CST; 23min ago
     Docs: https://k3s.io
......

在master上检查node1和node2状态:

[root@master ~]# kubectl get nodes -o wide
NAME     STATUS   ROLES    AGE   VERSION        INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
master   Ready    master   16h   v1.17.2+k3s1   192.168.122.20   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1
node1    Ready    <none>   49m   v1.17.2+k3s1   192.168.122.21   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1
node2    Ready    <none>   72s   v1.17.2+k3s1   192.168.122.22   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.3.3-k3s1

设置node的ROLE

我们设置node1和node2的ROLE=dubbo,同时设置label usage=dubbo,在master节点执行:

[root@master yaml]# kubectl create namespace dubbo
namespace/dubbo created
[root@master ~]# kubectl label node node1 node-role.kubernetes.io/dubbo=dubbo
node/node1 labeled
[root@master ~]# kubectl label node node1 usage=dubbo
node/node1 labeled
[root@master ~]# kubectl label node node2 node-role.kubernetes.io/dubbo=dubbo
node/node2 labeled
[root@master ~]# kubectl label node node2 usage=dubbo
node/node2 labeled

确认下配置结果:

[root@master ~]# kubectl get nodes --show-labels
NAME     STATUS   ROLES    AGE   VERSION        LABELS
node1    Ready    dubbo    66m   v1.17.2+k3s1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,k3s.io/hostname=node1,k3s.io/internal-ip=192.168.122.21,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,node-role.kubernetes.io/dubbo=dubbo,node.kubernetes.io/instance-type=k3s,usage=dubbo
node2    Ready    dubbo    18m   v1.17.2+k3s1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,k3s.io/hostname=node2,k3s.io/internal-ip=192.168.122.22,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux,node-role.kubernetes.io/dubbo=dubbo,node.kubernetes.io/instance-type=k3s,usage=dubbo
master   Ready    master   16h   v1.17.2+k3s1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,k3s.io/hostname=master,k3s.io/internal-ip=192.168.122.20,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=true,node.kubernetes.io/instance-type=k3s,usage=infra

导入镜像

这一步和master节点上的操作是一样的,不过只需要导入kong和piped的镜像就可以了,记得需要在node1和node2节点上分别执行。

[root@node1 ~]# find . -name '*.tar' -exec ctr image import {} \;
unpacking docker.io/library/kong-alpine:1.2.2-32 (sha256:cd23bb1d9345b7a0120858d16a477bc71006c2fb83a0644b42b8d0cc1d2a7d1a)...done
unpacking docker.io/library/piped-alpine:0.1.0-59 (sha256:88953fa8f795a8a96dc2d0fb6d7495d01861d1902ef2a15988a10adf1d7c6172)...done

确认镜像都已经导入,在node1和node2上执行如下检查:

[root@node1 ~]# crictl images
IMAGE                            TAG                 IMAGE ID            SIZE
docker.io/library/kong-alpine    1.2.2-32            241311b532b80       126MB
docker.io/library/piped-alpine   0.1.0-59            2df037a365ef8       8.66MB
docker.io/rancher/klipper-lb     v0.1.2              897ce3c5fc8ff       2.71MB
docker.io/rancher/pause          3.1                 da86e6ba6ca19       327kB

部署网关

我们用DeamonSet的方式,在node1和node2上部署服务网关,并且用LoadBalancer的方式对外提供服务。在master节点执行:

[root@master yaml]# kubectl apply -f kong-dubbo.yaml --namespace=dubbo --validate=false
daemonset.apps/kong created
service/kong created
configmap/piped-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

注意这里我们部署网关用的是DaemonSet,但是在master节点上部署网关用的是Deployment。他们配置基本一样,差别在与Kind=Deployment和Kind=DaemonSet

注意这里网关连接的数据库的名字是dubbo,但是在master上的网关连接的库是kong。不同NameSpace的网关使用不同的库

部署完成后检查部署结果:

[root@master yaml]# kubectl get ds -n dubbo
NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
svclb-kong   3         3         0       3            0           <none>          2m46s
kong         2         2         2       2            2           usage=dubbo     2m47s
[root@master yaml]# kubectl get pods -o wide -n dubbo
NAME               READY   STATUS    RESTARTS   AGE    IP          NODE     NOMINATED NODE   READINESS GATES
svclb-kong-2j5k5   0/2     Pending   0          119m   <none>      <none>   <none>           <none>
svclb-kong-sk5bb   0/2     Pending   0          119m   <none>      <none>   <none>           <none>
svclb-kong-95db9   0/2     Pending   0          119m   <none>      <none>   <none>           <none>
kong-2c5c6         2/2     Running   1          119m   10.42.2.7   node2    <none>           <none>
kong-dth9g         2/2     Running   0          119m   10.42.1.7   node1    <none>           <none>
[root@master yaml]# kubectl get svc -n dubbo
NAME   TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)                          AGE
kong   LoadBalancer   10.43.75.0   192.168.122.31   8000:30571/TCP,10000:30749/TCP   3m17s

注意命令中都用了-n dubbo来指定了NameSpace

这时我们可以验证网关是否运行正常。访问网关的pod的8000端口,应该可以返回404错误:

[root@master yaml]# curl -i http://10.42.1.7:8000/
HTTP/1.1 404 Not Found
Date: Wed, 19 Feb 2020 07:48:30 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 23

{"message":"Not Found"}
[root@master yaml]# curl -i http://10.42.2.7:8000/
HTTP/1.1 404 Not Found
Date: Wed, 19 Feb 2020 07:48:35 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 23

{"message":"Not Found"}

部署Dubbo网关控制器

接下来我们部署网关控制器。原理和步骤在部署master节点时候我们讨论过,可以参考这里。如下是部署Dubbo网关控制器的过程。在master节点上执行:

[root@master yaml]# kubectl apply -f fort-dubbo.yaml --namespace=dubbo
deployment.apps/fort created
configmap/fort-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

这里我们部署了一个deployment到dubbo的NameSpace,设定运行在master节点。我们检查状态:

[root@master yaml]# kubectl get deploy -n dubbo -o wide
NAME   READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES                                      SELECTOR
fort   1/1     1            1           42s   fort,kong-admin   fort-alpine:0.2.0-43,kong-alpine:1.2.2-32   app=kong-admin
[root@master yaml]# kubectl get pod -n dubbo -o wide
NAME                   READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
svclb-kong-2j5k5       0/2     Pending   0          155m   <none>       <none>   <none>           <none>
svclb-kong-sk5bb       0/2     Pending   0          155m   <none>       <none>   <none>           <none>
svclb-kong-95db9       0/2     Pending   0          155m   <none>       <none>   <none>           <none>
kong-2c5c6             2/2     Running   1          155m   10.42.2.7    node2    <none>           <none>
kong-dth9g             2/2     Running   0          155m   10.42.1.7    node1    <none>           <none>
fort-5f87c5dff-jdjtd   2/2     Running   0          53s    10.42.0.17   master   <none>           <none>

注意这里的Fort是部署在master节点上,但是在Dubbo的NameSpace中

这时候到Aiur的控制台上,查看“所有分区”,就可以看到dubbo的分区了。在例子中,访问地址是:http://192.168.122.20:8080/zones

小结

在这个部分中,我们做了如下的安装配置:

  1. 在k3s中添加了两个节点,node1和node2
  2. 设置了node1和node2的label,usage=dubbo
  3. 创建了NameSpace=dubbo
  4. 在node1和node2上部署了服务网关,采用的是DaemonSet的方式,对外采用LoadBalancer的模式,ExternalIP是192.168.122.31
  5. 在master节点的dubbo NameSpace中部署了网关控制器用来管理node1和node2上的网关

部署Spring NameSpace(Node3+Node4)

部署spring Namespace和部署dubbbo Namespace的过程一样,区别在于设置node3和node4的label,还有使用的yaml文件内容有差异。文档中,我们省去和dubbo Namespace一样的步骤:

  1. 安装k3s agent
  2. 导入镜像

如下是有差异的步骤:

创建Namespace,设置label

[root@master yaml]# kubectl create namespace spring
namespace/spring created
[root@master yaml]# kubectl label node node3 node-role.kubernetes.io/spring=spring
node/node3 labeled
[root@master yaml]# kubectl label node node3 usage=spring
node/node3 labeled
[root@master yaml]# kubectl label node node4 node-role.kubernetes.io/spring=spring
node/node4 labeled
[root@master yaml]# kubectl label node node4 usage=spring
node/node4 labeled

部署网关

[root@master yaml]# kubectl apply -f kong-spring.yaml --namespace=spring --validate=false
daemonset.apps/kong created
service/kong created
configmap/piped-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

部署网关控制器

[root@master yaml]# kubectl apply -f fort-spring.yaml --namespace=spring
deployment.apps/fort created
configmap/fort-cm created

这里用到的yaml文件,我们在上边的步骤中下载过。也可以直接在线查看

小结

在这部分中,我们部署了NameSpace=spring的资源,内容和dubbo NameSpace机会一样。一切顺利的话,在Aiur的控制台可以看到新加入的spring分区。

部署ipvs主机

安装ipvsadm

yum -y install ipvsadm

绑定VIP

在ipvs主机的eth0网卡上绑定两个vip,192.168.122.31和192.168.122.33:

ip addr add 192.168.122.31/24 dev eth0
ip addr add 192.168.122.33/24 dev eth0

确认vip添加成功:

[root@ipvs ~]# ip addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:5a:94:55 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.10/24 brd 192.168.122.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 192.168.122.31/24 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet 192.168.122.33/24 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42cf:a12c:6b9e:5bff/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

配置对dubbo网关的LB

使用ipvsadm命令,添加负载均衡:

ipvsadm -A -t 192.168.122.31:8000 -s rr
ipvsadm -a -t 192.168.122.31:8000 -r 192.168.122.21:8000 -g
ipvsadm -a -t 192.168.122.31:8000 -r 192.168.122.22:8000 -g

配置对spring网关的LB

使用ipvsadm命令,添加负载均衡:

ipvsadm -A -t 192.168.122.33:8000 -s rr
ipvsadm -a -t 192.168.122.33:8000 -r 192.168.122.23:8000 -g
ipvsadm -a -t 192.168.122.33:8000 -r 192.168.122.24:8000 -g

验证

这个时候我们访问192.168.122.33:8000的时候,ipvs会把请求round robin”转发“到192.168.122.23:8000和192.168.122.24:8000,也就是完成了4层的负载均衡。

原理简述(以dubbo网关为例):

  1. 在设定svc的ExternalIP=192.168.122.31的时候,k8s会在节点上添加iptables规则,使得该节点收到目的地址是192.168.122.31的时候,iptabels转发到对应的svc的ClusterIP
  2. ipvs节点在eth0上绑定了192.168.122.31的IP(一个设备可以绑定多个IP)。所以,在交换机的arp表里,192.168.122.31这个IP对应的MAC地址是ipvs节点的eth0网卡
  3. 外部请求建立到192.168.122.31:8000连接的包,被交换机广播,此时MAC地址是ipvs节点的eth0网卡收到
  4. ipvs替换包的MAC地址为192.168.122.21对应的MAC地址,也就是node1的eth0的MAC地址,并再次把这个包广播到网络上
  5. node1的eth0收到这个包,匹配自己的MAC地址,这个包进入内核
  6. 内核根据iptables规则,把这个包转发给Service=kong的ClusterIP
  7. iptables进一步完成ClusterIP到PodIP的负载均衡

注:k8s的ClusterIP的处理,也可以使用ipvs,而且效率比使用iptables要高,但是工作原理是一样的。

下一步

接下来,我们一起看下如何在k8s环境中部署应用。这个例子可以认为是“Flomesh-Bookinfo基于虚拟机的演示”的k8s版本。

点击这里,了解Flomesh-bookinfo在k8s中的演示