1 Kubernetes Federation介绍
1.1 Kubenetes Federation概述
Kubernetes Federation实现在混合云模式下统一管理多个跨region或者跨公有云供应商的Kubernetes集群,使Kubernetes应用支持在多个跨区域集群之间部署、调度以及同步,从而实现应用的高可用以及灾备的效果。
Kubernetes Federation主要包含如下两大核心功能:
跨集群资源同步。Federation实现了跨多个集群的资源同步,比如可以实现同时在多个集群部署相同的Deployment。
跨集群服务发现。Federation基于跨集群的服务发现自动配置与发布DNS,从而使应用实现跨多个集群的负载均衡。
通过Federation可以解决如下单一Kubernetes集群的问题:
Kubernetes应用容灾。虽然Kubernetes通过多副本机制实现了应用的高可用,但单个集群难以避免由于Kubernetes集群本身故障导致应用崩溃的问题。通过Federation可以实现当单个Kubernetes故障时,自动把应用流量切换到另一个Kubernetes集群上,从而保持应用的连续性。
应用在不同地理位置的低延时访问。通过DNS负载策略或者CDN可以实现应用就近访问,从而减少应用访问的网络延迟。
避免应用对某个集群的强耦合。通过Federation可以实现应用在多个集群之间进行方便快速迁移,如果集群升级,也完全不会影响应用的连续性。
集群可扩展性。虽然Kubernetes实现了很好的节点级别的水平扩展能力,但当集群节点达到一定的规模后,Pod和Service数量巨大导致iptables规则过多,从而使网络性能大幅度下降。通过Federation可实现集群级别的水平扩展能力。
实现应用在混合云架构下部署。通过Federation实现应用能够在私有云、多云上同时部署,能够充分利用混合云模式的优势,比如公有云成本优化、避免公有云厂商绑定等等。
1.2 Kubernetes Federation架构
早期的Federation v1由于无法兼容Kubernetes最新API以及不支持RBAC等原因已经被完全废弃,目前官方推荐使用Kubernetes Federation V2版本,因此本文将以v2为例。
其架构图如下:
KubeFed要配置两类内容:
Type configuration:定义哪些Kubernetes资源需要被KubeFed管理,比如要实现ConfigMap资源通过联邦机制管理就必须先通过CRD建立新资源FederatedConfigMap,然后创建名称为configmaps的Type configuration(FederatedTypeConfig)资源,并描述ConfigMap要被FederatedConfigMap所管理,这样KubeFed Controllers才能知道如何建立Federated资源。
Cluster configuration声明了KubeFed纳管的集群信息。
对于Cluster configuration又包含了三种类型:
Templates定义了跨集群资源的描述信息;
Placement定义了跨集群资源的放置信息;
Overrides定义了模板字段级的变化信息。
通过上面的资源类型实现跨Kubernetes集群的资源统一调度部署及服务DNS配置。本文后续章节将从Federation安装配置、跨集群应用部署、跨集群的服务发现三个方面进行实践。
2 Kubernetes Federation配置
2.1 Federation安装配置
Federation本身也是可运行在Kubernetes平台之上的,实际生产中应该规划把Federation控制平面部署在哪个Kubernetes集群中,建议放到一个单独的Kubernetes集群中,避免和应用混合运行。
本文由于仅作为测试,因此选择在cluster1集群中进行Federation控制平面的部署。
首先安装kubefedctl,kubefedctl提供命令行工具与Federation API进行交互:
VERSION=0.1.0-rc6
OS=linux
ARCH=amd64
curl -LO https://github.com/kubernetes-sigs/kubefed/releases/download/v${VERSION}/kubefedctl-${VERSION}-${OS}-${ARCH}.tgz
tar -zxvf kubefedctl-*.tgz
chmod u+x kubefedctl
sudo mv kubefedctl /usr/local/bin/
在Kubernetes集群上准备好helm并配置RBAC相关内容。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EOF
$ helm init --service-account tiller
$ helm repo add kubefed-charts https://raw.githubusercontent.com/kubernetes-sigs/kubefed/master/charts
$ helm search kubefed
通过helm进行kubeFed的安装部署。(TIPS:在此使用helm search时由于helm不识别rc版本,因此列出版本不是最新的)
$ helm install kubefed-charts/kubefed --name kubefed --version=0.1.0-rc6 --namespace kube-federation-system
检查发现kubeFed已经正常运行。
$ kubectl get pod -n kube-federation-system
NAME READY STATUS RESTARTS AGE
kubefed-admission-webhook-554b6bc659-l6xq6 1/1Running04h8m
kubefed-controller-manager-64b4d7ddb5-7hvb41/1Running04h8m
kubefed-controller-manager-64b4d7ddb5-jtb4t 1/1Running04h8m
2.2 添加Kubernetes集群到Federation
可通过kubefedctl添加两个集群到Federation:
$ kubefedctl join cluster1 --cluster-context cluster1 --host-cluster-context cluster1 --v=2
$ kubefedctl join cluster2 --cluster-context cluster2 --host-cluster-context cluster1 --v=2
查看集群的状态:
$ kubectl get kubefedclusters --all-namespaces
NAMESPACE NAME READY AGE
kube-federation-system cluster1 True5m3s
kube-federation-system cluster2 True4m31s
如上READY如果输出为 True
,说明配置就绪。
3 Kubernetes Federation应用部署
3.1 准备样例资源
官方提供了很好的样例,为了验证我们直接下载官方的资源:
git clone https://github.com/kubernetes-sigs/kubefed.git
并创建我们要使用的namespaces。
kubectl apply -f example/sample1/namespace.yaml \
-f example/sample1/federatednamespace.yaml
3.2 创建configmap资源
部署跨集群的configmap,所使用的yaml为:
apiVersion: types.kubefed.io/v1beta1
kind: FederatedConfigMap
metadata:
name: test-configmap
namespace: test-namespace
spec:
template:
data:
A: ala ma kota
placement:
clusters:
- name: cluster2
- name: cluster1
overrides:
- clusterName: cluster2
clusterOverrides:
- path: /data
value:
foo: bar
创建完成后,在cluster1集群上看到已经正常创建:
$ kubectl get configmap -n test-namespace
NAME DATA AGE
test-configmap 129m
在cluster2集群上也已经正常创建,验证了configmap在多个集群上同时创建:
$ kubectl get configmap -n test-namespace
NAME DATA AGE
test-configmap 129m
但注意,在yaml文件里写了 override
, override
可实现不同集群的配置不同,如果没有 override
所有集群的资源配置完全一样。
这里我们在cluster2上配置的具体内容和cluster1的并不相同。对于cluster1,configmap的data内容为:
$ kubectl describe configmap/test-configmap -n test-namespace
Name: test-configmap
Namespace: test-namespace
Labels: kubefed.io/managed=true
Annotations:
Data
====
A:
----
ala ma kota
Events:
对于cluster2,configmap的data内容为:
$ kubectl describe configmap/test-configmap -n test-namespace
Name: test-configmap
Namespace: test-namespace
Labels: kubefed.io/managed=true
Annotations:
Data
====
foo:
----
bar
Events:
3.3 创建deployment资源
3.3.1 创建单个集群deployment
首先在cluster1上进行创建单个集群的deployment,所使用yaml为:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: web-file
mountPath: /usr/share/nginx/html/
volumes:
- name: web-file
configMap:
name: web-file
创建完成后,在cluster1集群上,可以看到已经正常创建。
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/11150m
在cluster2集群上,满足预期,并没有创建这个deployment。
$ kubectl get deployment
No resources found indefaultnamespace.
3.3.2 创建跨集群的deployment
部署跨集群的deployment,所使用的yaml为:
apiVersion: types.kubefed.io/v1beta1
kind: FederatedDeployment
metadata:
name: test-deployment
namespace: test-namespace
spec:
template:
metadata:
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
placement:
clusters:
- name: cluster2
- name: cluster1
overrides:
- clusterName: cluster2
clusterOverrides:
- path: "/spec/replicas"
value: 5
- path: "/spec/template/spec/containers/0/image"
value: "nginx:1.17.0-alpine"
- path: "/metadata/annotations"
op: "add"
value:
foo: bar
- path: "/metadata/annotations/foo"
op: "remove"
创建完成后,在cluster1集群上看到已经正常创建:
$ kubectl get deployment -n test-namespace
NAME READY UP-TO-DATE AVAILABLE AGE
test-deployment 3/333167m
在cluster2集群上也已经正常创建,说明Deployment资源在多个集群上同时创建了:
$ kubectl get deployment -n test-namespace
NAME READY UP-TO-DATE AVAILABLE AGE
test-deployment 5/555167m
这里我们也配置了 override
,因此cluster1的副本数是3,而cluster2的副本数是5,且cluster2的容器镜像版本为nginx:1.17.0-alpine
$ kubectl describe pod/test-deployment-7bdd764dcd-8m4rk-n test-namespace
...
Containers:
nginx:
Container ID: docker://3881259dd285ae0e05dfc374e6a8c707ee74978b870e74b9eb3936c2d94e6263
Image: nginx:1.17.0-alpine
可见通过联邦集群实现了deployment资源在不同集群中的部署并能实现不同集群的配置不一样。
3.4 创建其它类型的资源
同样的模式,可以跨集群创建不同类型的资源,包括service、serviceaccount、job、secrets等资源的跨集群创建,如果不指定overrides,则在两个集群下部署的内容相同;如果指定overrides,则可以实现不同集群下资源差异化配置。
3.5 更新资源配置
可以针对已经部署在cluster1和cluster2集群上的资源进行配置更新。
3.5.1 从cluster2集群中把部署资源删除
从placement中删除cluster2:
$ kubectl -n test-namespace patch federatednamespace test-namespace \
--type=merge -p '{"spec": {"placement": {"clusters": [{"name": "cluster1"}]}}}'
可以看到,部署在cluster2上的资源已经在进行删除。
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
test-namespace test-deployment-7bdd764dcd-vsvt8 0/1Terminating03h26m
test-namespace test-deployment-7bdd764dcd-zrwc8 0/1Terminating03h26m
test-namespace test-job-r8gkd 1/1Terminating03h26m
test-namespace test-job-z5ngq 1/1Terminating03h26m
3.5.2 把cluster2集群的资源添加回来
在placement中添加cluster2:
$ kubectl -n test-namespace patch federatednamespace test-namespace \
--type=merge -p '{"spec": {"placement": {"clusters": [{"name": "cluster1"}, {"name": "cluster2"}]}}}'
可以看到在cluster2的资源重新部署上。
$ kubectl get pod -n test-namespace
NAME READY STATUS RESTARTS AGE
test-deployment-7bdd764dcd-2z47c1/1Running049s
test-deployment-7bdd764dcd-458v81/1Running049s
test-deployment-7bdd764dcd-82gw91/1Running049s
test-deployment-7bdd764dcd-rvlnh 1/1Running049s
test-deployment-7bdd764dcd-w7qpt 1/1Running049s
test-job-8hbjg1/1Running049s
test-job-t69xz 1/1Running049s
3.5.3 修改federateddeployments的副本数
修改federateddeployments的副本数:
$ kubectl edit federateddeployments -n test-namespace
template:
metadata:
labels:
app: nginx
spec:
replicas: 6
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
可以看到在cluster1中副本数已经调整为6。
$ kubectl get pod -n test-namespace
NAME READY STATUS RESTARTS AGE
test-deployment-86c57db685-9tfmr1/1Running045s
test-deployment-86c57db685-g895p 1/1Running045s
test-deployment-86c57db685-hkxsc 1/1Running045s
test-deployment-86c57db685-j284m 1/1Running03h53m
test-deployment-86c57db685-stl9l 1/1Running03h53m
test-deployment-86c57db685-xxfm9 1/1Running045s
但由于overrides的存在,而在cluster2上还只有5个副本,符合预期。
$ kubectl get pod -n test-namespace
NAME READY STATUS RESTARTS AGE
test-deployment-7bdd764dcd-2z47c1/1Running022m
test-deployment-7bdd764dcd-458v81/1Running022m
test-deployment-7bdd764dcd-82gw91/1Running022m
test-deployment-7bdd764dcd-rvlnh 1/1Running022m
test-deployment-7bdd764dcd-w7qpt 1/1Running022m
4 跨集群服务发现
4.1 架构分析
对于Federation集群,需要考虑多个集群下的服务如何暴露给互联网的问题。而此时会通过DNS进行服务地址的解析。
在下图中,DNS Provider即提供了DNS的解析服务,而DNS有能力根据权重、位置、路由策略等分类,实现把同一个域名解析到不同的集群的服务上,因此后面需要解决Federation k8s集群和DNS联动的问题。
此时通过ExternalDNS组件来实现容器平台上的服务与DNS自动化配置的过程。对于非联邦的k8s集群,ExternalDNS可以直接发现service/ingress类型的资源,并直接把域名在DNS Provider上进行配置,而对于联邦的集群,需要引入了一个中间层,这个中间层有两种类型:一种是监控ingress,一种是监控service。这两种类型实现原理都相同,只是监控的资源不相同而已。
(1)Service类型。
首先集群会建立一个FederatedDeployment类型资源,然后建立了FederatedService,这与上面章节的演示内容相同。然后,为了让ExternalDNS能自动感知,建立Domain类型资源和ServiceDNSRecord类型资源,此时KubeFed的通过Service DNS Controller来收集Service的信息,并更新至ServiceDNSRecord中,接着DNS Endpoint Controller会依据该ServiceDNSRecord的状态内容,建立一个DNSEndpoint文件,并产生DNS records资源,最后再由ExternalDNS来同步更新DNS records至DNS供应商。
(2)Ingress类型
若是Ingress 的话,会由IngressDNSRecord 文件取代,并由Ingress DNS Controller 收集信息。其余与上图架构相同。
4.2 环境准备
为了实现面向互联网暴露的服务可以调度到Federation集群中的不同集群,需要配置DNS来进行解析。方案采用了ExternalDNS组件,与DNS provider进行联动,从而实现DNS服务器上的自动配置。
本演示环境使用了AWS的route53服务。
4.3 ExternalDNS配置
首先,配置ExternalDNS所需要的IAM policy。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/*"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}
新建立IAM role,把上面的policy添加到role中,并把role赋予给虚拟机。
然后,使用命令行对AWS route53创建zone。
$ aws route53 create-hosted-zone --name "external-dns-test.cuizsh.com."--caller-reference "external-dns-test-$(date +%s)"
此时可以从route53的页面上看到已经创建出来对应的zone。
部署ExternalDNS,具体yaml文件如下。
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: kube-federation-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "get", "watch"]
- apiGroups: ["multiclusterdns.kubefed.io"]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: kube-federation-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-federation-system
labels:
k8s-app: external-dns
spec:
replicas: 1
selector:
matchLabels:
k8s-app: external-dns
template:
metadata:
labels:
k8s-app: external-dns
spec:
serviceAccountName: external-dns
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service
- --source=ingress
- --domain-filter=external-dns-test.cuizsh.com ###change your own domain
- --provider=aws
- --policy=upsert-only
- --aws-zone-type=public
- --registry=txt
- --txt-owner-id=/hostedzone/Z1MJK86562DOQP ###change your hostzoneid
- --log-level=debug
- --txt-prefix=cname
- --source=crd
- --crd-source-apiversion=multiclusterdns.kubefed.io/v1alpha1
- --crd-source-kind=DNSEndpoint
检查可知,ExternalDNS已经部署完成。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
external-dns-56d5d4bc68-dz99d 1/1Running048s
对ExternalDNS进行验证,创建一个Service,验证是否可以自动在route53上创建对应记录。
创建Service的yaml如下。
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.cuizsh.com
spec:
type: LoadBalancer
ports:
- port: 80
name: http
targetPort: 80
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
name: http
创建完的Service如下:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP
nginx LoadBalancer10.100.143.66 ade41da1e50ad11ea84090e3f9ad3b4d-64143112.ap-northeast-1.elb.amazonaws.com 80:30314/TCP 95s
此时登录route53的console,发现已经自动添加A记录和TXT记录,证明ExternalDNS已经正常工作,可以自动实现服务与DNS的联动。
3.5 Federation实现服务DNS发现
在上面章节已经创建了federatedservices类型的资源,这种类型的资源会在两个集群上都部署service。
$ kubectl get federatedservices --all-namespaces
NAMESPACE NAME AGE
test-namespace test-service 95m
此时可以看到两个集群上的service已经部署成功。
$ kubectl get svc -n test-namespace--context=cluster1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-service LoadBalancer10.100.115.247 aa866f6b350c011ea84090e3f9ad3b4d-59219283.ap-northeast-1.elb.amazonaws.com 80:31618/TCP 97m
$ kubectl get svc -n test-namespace--context=cluster2
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-service LoadBalancer10.100.196.47 aa818f27550c011ea8c80064ff2fdab2-1423980564.ap-northeast-1.elb.amazonaws.com 80:31537/TCP 98m
然后创建Domain及ServiceDNSRecord类型资源。
$ cat <
apiVersion: multiclusterdns.kubefed.io/v1alpha1
kind: Domain
metadata:
# Corresponds to in the resource records.
name: test-domain
# The namespace running kubefed-controller-manager.
namespace: kube-federation-system
# The domain/subdomain that is setup in your externl-dns provider.
domain: external-dns-test.cuizsh.com
---
apiVersion: multiclusterdns.kubefed.io/v1alpha1
kind: ServiceDNSRecord
metadata:
# The name of the sample service.
name: test-service
# The namespace of the sample deployment/service.
namespace: test-namespace
spec:
# The name of the corresponding Domain.
domainRef: test-domain
recordTTL: 300
EOF
此时检查route53的页面,发现已经给test-service自动进行了DNS的配置,同时可以发现添加的记录类型为4条A记录。由于一个federatedservices对应了两个service,而每一个service又对应一个LoadBalancer,每一个 LoadBalancer默认分配了2个公网IP,因此共4个公网IP。
可见ExternalDNS已经实现自动化的把集群的服务调度到了两个集群的服务上去。
$ dig +short@ns-1513.awsdns-61.org test-service.test-namespace.test-domain.svc.external-dns-test.cuizsh.com
52.194.7.232
54.64.122.197
13.112.162.25
52.196.202.239
并且可以配置不同的权重及策略,实现不同方式的轮询。
5 小结
本文章验证了Kubernetes Federation的基本功能,包括环境的搭建、资源在多集群上的部署、DNS解析分发到多集群等内容。从技术成熟度上来看,该技术正在发展过程中;从目前公有云提供商提供的服务来看,目前也在采用类似的架构进行多容器集群的管理。但该方案在具体落地时还需要结合实际情况,例如,该方案目前实现的同一个资源在A集群部署m副本、在B集群部署n副本,但针对当A集群故障时,把m+n个副本都调度到B集群还需要再进行相关的配置。但整体来说,对于多个K8S集群的统一调度可以提供相关的参考 。
参考文档
https://github.com/kairen/aws-k8s-federation
https://www.kubernetes.org.cn/5702.html
https://github.com/kubernetes-sigs/kubefed
https://github.com/kubernetes-sigs/external-dns
作者介绍
崔增顺:任职民生银行云技术管理中心,目前从事云平台相关探索及研究,熟悉私有云、公有云相关服务及架构。微信humanthinker,邮箱cuizengshun@cmbc.com.cn。
![eba728ee5a409ebcba52281b0368a655.png](https://i-blog.csdnimg.cn/blog_migrate/04359586062f9c75b1988c22df8ee1d8.png)
![4f6ab95c671a637466b50d6563c741ce.png](https://i-blog.csdnimg.cn/blog_migrate/9e05333754e1912ee8ed6f26068a9b0e.jpeg)
作者:崔增顺
编辑:民生文化建设小组