Istio多集群(1)-多控制面
Istio多集群(1)-多控制面
参考自官方文档。
复制控制面
本节将使用多个主集群(带控制面的集群)来部署Istio多集群,每个集群都有自己的控制面,集群之间使用gateway进行通信。
由于不使用共享的控制面来管理网格,因此这种配置下,每个集群都有自己的控制面来管理后端应用。为了策略执行和安全目的,所有的群集都处于一个公共的管理控制之下。
通过复制共享服务和命名空间,并在所有集群中使用一个公共的根CA证书,可以实现单个Istio服务网格跨集群通信。
要求
- 两个或多个kubernees集群,版本为1.17,1.18,1.19
- 在每个kubernetes集群上部署Istio控制面
- 每个集群中的
istio-ingressgateway
的服务IP地址必须能够被所有的集群访问,理想情况下,使用L4网络负载平衡器(NLB)。 - 根CA。跨集群通信需要在服务之间使用mutual TLS。为了在跨集群通信时启用mutual TLS,每个集群的Istio CA必须配置使用共享的CA证书生成的中间CA。出于演示目的,将会使用Istio的
samples/certs
安装目录下的根CA证书。
在每个集群中部署Istio控制面
使用自定义的根CA为每个集群生成中间CA证书,使用共享的根CA来为跨集群的通信启用mutual TLS。
出于演示目的,下面使用了istio样例目录中的证书。在真实部署时,应该为每个集群选择不同的CA证书,这些证书由一个共同的根CA签发。
在每个集群中运行如下命令来为所有集群部署相同的Istio控制面。
为生成的CA创建一个kubernetes secret。它与在Istio中插入自定义的CA一文中的方式类似。
生产中不能使用samples 目录中的证书,有安全风险。
$ kubectl create namespace istio-system
$ kubectl create secret generic cacerts -n istio-system \
--from-file=samples/certs/ca-cert.pem \
--from-file=samples/certs/ca-key.pem \
--from-file=samples/certs/root-cert.pem \
--from-file=samples/certs/cert-chain.pem
部署Istio,部署后会在
istio-system
命名空间中创建一个podistiocoredns
,用于提供到global
域的DNS解析,其配置文件如下:# cat Corefile
.:53 {
errors
health # Removed support for the proxy plugin: https://coredns.io/2019/03/03/coredns-1.4.0-release/
grpc global 127.0.0.1:8053
forward . /etc/resolv.conf {
except global
} prometheus :9153
cache 30
reload
}
$ istioctl install -f manifests/examples/multicluster/values-istio-multicluster-gateways.yaml
配置DNS
当为远端集群中的服务提供DNS解析时,现有应用程序无需修改即可运行,因为应用程序通常会访问通过DNS解析出的IP。Istio本身并不需要DNS在服务之间路由请求。本地服务会共享一个共同的DNS前缀(即,svc.cluster.local
)。kubernetes DNS为这些服务提供了DNS解析。
为了给远端集群提供一个类似的服务配置,需要使用格式<name>.<namespace>.global
来命名远端集群中的服务。Istio附带了一个Core DNS服务,可以为这些服务提供DNS解析。为了使用该DNS,kubernetes的DNS必须配置为.global
的域名存根。
在每个需要调用远程的服务的集群中创建或更新一个现有的k8s的ConfigMap,本环境中使用的coredns为1.7.0版本,使用的配置文件如下:
注意不能直接采用官方配置文件,可能会因为不同版本的配置原因导致k8s的coredns无法正常启动。正确做法是在kube-system命名空间下获取k8s coredns的configmap配置,然后在后面追加global域有关的配置即可。
另外使用如下命令apply之后,k8s的coredns可能并不会生效,可以手动重启k8s的dns pod来使其生效。注意如下配置需要在cluster1和cluster2中同时生效。
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 { #k8s的coredns的原始配置
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
global:53 { #新增了对global的解析,将其转发到istio-system下面的istiocoredns服务,即上面istio创建的coredns
errors
cache 30
forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP}):53
}
EOF
在创建第4步的serviceEntry之后可以使用如下方式判断cluster1的DNS解析是否正确:
首先在cluster1中的sleep容器中通过istio的coredns解析
httpbin.bar.global
,10.96.199.197为istio的coredns的service# nslookup -type=a httpbin.bar.global 10.96.199.197
Server: 10.96.199.197
Address: 10.96.199.197:53 Name: httpbin.bar.global
Address: 240.0.0.2 #解析成功
在cluster1中的sleep容器中通过k8s的coredns解析
httpbin.bar.global
,10.96.0.10为k8s的coredns的service,可以看到k8s的coredns将httpbin.bar.global
的解析转发给了istio的coredns,并且解析成功。# nslookup -type=a httpbin.bar.global 10.96.0.10
Server: 10.96.0.10
Address: 10.96.0.10:53 Name: httpbin.bar.global
Address: 240.0.0.2 #解析成功
DNS的解析路径为:sleep容器的/etc/resolv.conf-->k8s coredns-->istio coredns-->istio-coredns-plugin
istio-coredns-plugin是istio的CoreDNS gRPC插件,用于从Istio
ServiceEntries
中提供DNS记录。(注:该插件将集成到Istio 1.8的sidecar中,后续将会被废弃)可以在istio-coredns-plugin的log日志中查看到对global域的操作:
# crictl inspect e8d3f73c4d38d|grep "logPath"
"logPath": "/var/log/pods/istio-system_istiocoredns-75dd7c7dc8-cg55l_8c960b74-419c-44e8-8992-58293e36d6fd/istio-coredns-plugin/0.log"
例如该插件会读取下面创建的到
httpbin.bar.global
的ServiceEntries
,并将其做DNS映射:... info Reading service entries at 2020-10-09 17:53:38.710306063 +0000 UTC m=+19500.216843506
... info Have 1 service entries
... info adding DNS mapping: httpbin.bar.global.->[240.0.0.2]
配置应用服务
一个集群中的服务如果需要被远端集群访问,就需要在远端集群中配置一个ServiceEntry
。service entry使用的host格式为<name>.<namespace>.global
,name和namespace分别对应服务的name和namespace。
为了演示跨集群访问,在一个集群中配置sleep服务,使用该服务访问另一个集群中的httpbin服务。
选择两个Istio集群,分别为
cluster1
和cluster2
使用如下命令列出集群的上下文:
# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kind-cluster1 kind-cluster1 kind-cluster1
kind-cluster2 kind-cluster2 kind-cluster2
使用环境变量保存集群的上下文名称:
# export CTX_CLUSTER1=$(kubectl config view -o jsonpath='{.contexts[0].name}')
# export CTX_CLUSTER2=$(kubectl config view -o jsonpath='{.contexts[1].name}')
# echo "CTX_CLUSTER1 = ${CTX_CLUSTER1}, CTX_CLUSTER2 = ${CTX_CLUSTER2}"
CTX_CLUSTER1 = kind-cluster1, CTX_CLUSTER2 = kind-cluster2
配置用例服务
在
cluster1
集群中部署sleep
应用$ kubectl create --context=$CTX_CLUSTER1 namespace foo
$ kubectl label --context=$CTX_CLUSTER1 namespace foo istio-injection=enabled
$ kubectl apply --context=$CTX_CLUSTER1 -n foo -f samples/sleep/sleep.yaml
$ export SLEEP_POD=$(kubectl get --context=$CTX_CLUSTER1 -n foo pod -l app=sleep -o jsonpath={.items..metadata.name})
在
cluster2
集群中部署httpbin
应用$ kubectl create --context=$CTX_CLUSTER2 namespace bar
$ kubectl label --context=$CTX_CLUSTER2 namespace bar istio-injection=enabled
$ kubectl apply --context=$CTX_CLUSTER2 -n bar -f samples/httpbin/httpbin.yaml
暴露
cluster2
的网关地址本地部署的kubernetes由于没有loadBalancer,因此使用nodeport方式(如果使用kind部署kubernetes,此时需要手动修改service istio-ingressgateway的nodeport,使其与kind暴露的端口一致)。
export INGRESS_PORT=$(kubectl -n istio-system --context=$CTX_CLUSTER2 get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
INGRESS_HOST的获取方式如下:
export INGRESS_HOST=$(kubectl --context=$CTX_CLUSTER2 get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
为了允许
cluster1
中的sleep
访问cluster2
中的httpbin
,需要在cluster1
中为httpbin
创建一个service entry。service entry的host名称的格式应该为<name>.<namespace>.global
,name和namespace分别对应远端服务的name和namespace。为了让DNS解析
.global
域下的服务,需要给这些服务分配虚拟IP地址。每个.global DNS域下的服务都必须在集群中拥有唯一的虚拟IP。
如果global服务已经有了实际的VIPs,那么可以直接使用这类地址,否则建议使用范围为240.0.0.0/4的E类IP地址。应用使用这些IP处理流量时,流量会被sidecar捕获,并路由到合适的远端服务。
不能使用多播地址(224.0.0.0 ~ 239.255.255.255),因为默认情况下不会有到达这些地址的路由。同时也不能使用环回地址(127.0.0.0/8),因为发往该地址的流量会被重定向到sidecar的inbound listener。
$ kubectl apply --context=$CTX_CLUSTER1 -n foo -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: httpbin-bar
spec:
hosts: #外部服务的主机名
# must be of form name.namespace.global
- httpbin.bar.global
# Treat remote cluster services as part of the service mesh
# as all clusters in the service mesh share the same root of trust.
location: MESH_INTERNAL #标注为网格内的服务,使用mTLS交互
ports: # 对端bar服务的端口
- name: http1
number: 8000
protocol: http
resolution: DNS #使用DNS服务器进行域名解析,endpoints的address字段可能是一个域名
addresses: # 下面指定了httpbin.bar.global:8000服务对应的一个后端${INGRESS_HOST}:30615
# the IP address to which httpbin.bar.global will resolve to
# must be unique for each remote service, within a given cluster.
# This address need not be routable. Traffic for this IP will be captured
# by the sidecar and routed appropriately.
- 240.0.0.2 # host对应的虚拟地址,必须包含,否则istio-coredns-plugin无法进行DNS解析
endpoints:
# This is the routable address of the ingress gateway in cluster2 that
# sits in front of sleep.foo service. Traffic from the sidecar will be
# routed to this address.
- address: ${INGRESS_HOST} # 将其替换为对应的node地址值即可
ports:
http1: 30001 # 替换为对应的nodeport
EOF
由于使用了nodeport方式,因此需要使用容器15443端口对应的nodeport端口,使用如下方式获取:
# kubectl --context=$CTX_CLUSTER2 get svc -n istio-system istio-ingressgateway -o=jsonpath='{.spec.ports[?(@.port==15443)].nodePort}'
30001
上述配置会将
cluster1
中httpbin.bar.global
服务的所有端口上的流量(通过mutual TLS)路由到$INGRESS_HOST:15443
。网关的15443端口是一个感知SNI的Envoy配置,在安装Istio控制面时部署。到达15443端口的流量会在目标集群的内部服务的pod上进行负载均衡(即
cluster2
的httpbin.bar
)。下面是从cluster1的sleep中导出的istio-proxy配置,可以看到
httpbin.bar.global
的后端为172.18.0.5:30615,即$INGRESS_HOST:$NODE_PORT
"cluster": {
"load_assignment": {
"cluster_name": "outbound|8000||httpbin.bar.global",
"endpoints": [
{
"locality": {},
"lb_endpoints": [
{
"endpoint": {
"address": {
"socket_address": {
"address": "172.18.0.4",
"port_value": 30001
}
}
},
"load_balancing_weight": 1
}
],
"load_balancing_weight": 1
}
]
},
...
},
对应的路由如下,可以看到240.0.0.2只是作为了SNI的一种,将匹配到的请求转发给上面的
"cluster": "outbound|8000||httpbin.bar.global"
进行处理:"route_config": {
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "8000",
"virtual_hosts": [
...
{
"name": "httpbin.bar.global:8000",
"domains": [
"httpbin.bar.global",
"httpbin.bar.global:8000",
"240.0.0.2",
"240.0.0.2:8000"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|8000||httpbin.bar.global",
...
},
另外需要注意的是cluster1和cluster2都使用了一个Gateway和DestinationRule,对从sleep到httpbin的
*.global
域的请求使用mTLS进行加密,并在网关上使用AUTO_PASSTHROUGH
模式,此模式会根据SNI将请求直接转发给后端应用,无需virtualservice进行绑定。apiVersion: networking.istio.io/v1beta1
kind: Gateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*.global'
port:
name: tls
number: 15443
protocol: TLS
tls:
mode: AUTO_PASSTHROUGH apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
spec:
host: '*.global'
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
校验可以通过sleep服务访问httpbin服务。
# kubectl exec --context=$CTX_CLUSTER1 $SLEEP_POD -n foo -c sleep -- curl -I httpbin.bar.global:8000/headers HTTP/1.1 200 OK
server: envoy
date: Wed, 14 Oct 2020 22:44:00 GMT
content-type: application/json
content-length: 554
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 8
在官方文档中使用如上命令即可在cluster1的sleep Pod中访问cluster2的httpbin服务。但从上面分析可以看到,当SNI为
httpbin.bar.global
的请求到达cluster2的ingress pod上时,它会按照k8s的coredns配置将该请求转发到istio的coredns进行解析,但cluster2并没有配置httpbin.bar.global
对应的serviceentry,因此,istio的coredns也无法解析该dns,返回503错误。在cluster2的istio-coredns-plugin容器的日志中可以找到如下信息:... info Query A record: httpbin.bar.global.->{httpbin.bar.global. 1 1}
... info Could not find the service requested
... info DNS query ;; opcode: QUERY, status: NOERROR, id: 64168
在cluster2中创建如下serviceentry:
$ kubectl apply --context=$CTX_CLUSTER2 -n bar -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: httpbin-bar
spec:
hosts:
- httpbin.bar.global
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: DNS
addresses:
- 240.0.0.3
endpoints:
- address: httpbin.bar.svc.cluster.local #httpbin的k8s service
EOF
httpbin生成的cluster如下,可以看到后端地址为
httpbin.bar.svc.cluster.local
,直接通过k8s的DNS即可解析该地址。"cluster": {
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "outbound|8000||httpbin.bar.global",
"type": "STRICT_DNS",
...
"load_assignment": {
"cluster_name": "outbound|8000||httpbin.bar.global",
"endpoints": [
{
"locality": {},
"lb_endpoints": [
{
"endpoint": {
"address": {
"socket_address": {
"address": "httpbin.bar.svc.cluster.local",
"port_value": 8000
}
}
},
"load_balancing_weight": 1
}
],
"load_balancing_weight": 1
}
]
},
...
},
在cluster2中创建一个
sleep
pod,并在该pod中访问cluster2的bar
命名空间下的httpbin
服务,可以看到访问成功:# curl -I httpbin.bar.global:8000/headers
HTTP/1.1 200 OK
server: envoy
date: Sat, 10 Oct 2020 12:40:23 GMT
content-type: application/json
content-length: 554
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 206
在cluster2的ingress pod中导出与15443端口有关的listeners配置如下,可以看到AUTO_PASSTHROUGH模式下的listener并没有通过
route_config_name
指定到达cluster的路由(不需要通过virtualservice进行服务映射),仅通过SNI进行请求转发。"dynamic_listeners": [
{
"name": "0.0.0.0_15443",
"active_state": {
"version_info": "2020-10-14T19:52:24Z/14",
"listener": {
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "0.0.0.0_15443",
"address": {
"socket_address": {
"address": "0.0.0.0",
"port_value": 15443
}
},
"filter_chains": [
{
"filter_chain_match": {
"server_names": [
"*.global"
]
},
"filters": [
...
{
"name": "istio.stats",
...
},
{
"name": "envoy.filters.network.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"stat_prefix": "BlackHoleCluster",
"cluster": "BlackHoleCluster"
}
}
]
}
],
"listener_filters": [
{
...
],
"traffic_direction": "OUTBOUND"
},
"last_updated": "2020-10-14T19:53:22.089Z"
}
}
]
卸载
$ kubectl delete --context=$CTX_CLUSTER1 -n foo -f samples/sleep/sleep.yaml
$ kubectl delete --context=$CTX_CLUSTER1 -n foo serviceentry httpbin-bar
$ kubectl delete --context=$CTX_CLUSTER1 ns foo
$ kubectl delete --context=$CTX_CLUSTER2 -n bar -f samples/httpbin/httpbin.yaml
$ kubectl delete --context=$CTX_CLUSTER2 ns bar
$ unset SLEEP_POD CLUSTER2_GW_ADDR CLUSTER1_EGW_ADDR CTX_CLUSTER1 CTX_CLUSTER2
FAQ
cluster1和cluster2通信时,需要保证cluster1和cluster2的根证书是相同的。可以通过对比cluster1的sleep和cluster2的httpbin导出的istio sidecar的如下ROOTCA证书配置来判断是否一致。可能发生证书不一致的原因是
- 先创建istio,后创建
cacerts
根证书 - 重建istio时,没有删除之前错误的istio-system命名空间下的老的证书
- 重建istio后,没有清理foo或bar命名空间下的pod,secret资源。
因此在重建istio前,务必删除istio-system和foo/bar命名空间下的所有资源
"dynamic_active_secrets": [
{
"name": "default",
"secret": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
"name": "default",
...
}
},
{
"name": "ROOTCA",
"secret": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
"name": "ROOTCA",
"validation_context": {
"trusted_ca": {
"inline_bytes": "LS0tLxxx="
}
}
}
}
]
参考:
- 先创建istio,后创建
Using CoreDNS to Conceal Network Identities of Services in Istio
Istio多集群(1)-多控制面的更多相关文章
- 高可用OpenStack(Queen版)集群-9.Cinder控制节点集群
参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...
- 高可用OpenStack(Queen版)集群-7.Neutron控制/网络节点集群
参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...
- 高可用OpenStack(Queen版)集群-6.Nova控制节点集群
参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...
- 使用 Admission Webhook 机制实现多集群资源配额控制
1 要解决的问题 集群分配给多个用户使用时,需要使用配额以限制用户的资源使用,包括 CPU 核数.内存大小.GPU 卡数等,以防止资源被某些用户耗尽,造成不公平的资源分配. 大多数情况下,集群原生的 ...
- [Istio]Kubernetes集群部署Istio 1.0
大部分内容都是可以根据https://istio.io/docs/setup/kubernetes/quick-start/来处理的,这里主要谈部署时一些细节的问题 首先,当我们按照 istio 官方 ...
- S1_搭建分布式OpenStack集群_09 cinder 控制节点配置
一.创建数据库创建数据库以及用户:# mysql -uroot -p12345678MariaDB [(none)]> CREATE DATABASE cinder;MariaDB [(none ...
- 如何使用Istio 1.6管理多集群中的微服务?
假如你正在一家典型的企业里工作,需要与多个团队一起工作,并为客户提供一个独立的软件,组成一个应用程序.你的团队遵循微服务架构,并拥有由多个Kubernetes集群组成的广泛基础设施. 由于微服务分布在 ...
- weblogic的集群与配置
目录(?)[-] 1.Weblogic的集群 2.创建Weblogic集群前的规划 3.开始创建我们的Weblogic集群 1.1 创建集群的总控制端aminserver 2.2 创建集群中的节点my ...
- 【转】Weblogic的集群
原文链接:http://www.cnblogs.com/HondaHsu/p/4267972.html 一.Weblogic的集群 还记得我们在第五天教程中讲到的关于Tomcat的集群吗? 两个tom ...
随机推荐
- 解决Maven的JDK版本问题
在pom文件中添加以下代码 <build> <plugins> <plugin> <groupId>org.apache.maven.plugins&l ...
- .NET Core加解密实战系列之——对称加密算法
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- Apache 和 Nginx 下绑定域名
Apache 方法一 参考:链接 版本:2.3 配置文件位置:/usr/share/doc/httpd/httpd-vhosts.conf 添加域名和站点信息: vim /usr/share/doc/ ...
- js垃圾回收和内存泄漏
js垃圾回收和内存泄漏 js垃圾回收 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. 1.标记清除(常用) 工作原理:是当变量进入环境时,将这个变量标记为"进入环境& ...
- access数据库一般注入方法及偏移注入
1.access数据库与mysql数据库的差别 access没有数据库,access数据库每个数据都是单个文件,每个access只有表结构 mysql : 库名,表名,列名,字段内容 access:表 ...
- [剑指Offer]56-数组中数字出现的次数(位运算)
题目一 数组中只出现一次的数字 题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字 题解 异或. 先考虑:数组中只有一个数字只出现了一次,其他数字都出现了 ...
- [剑指Offer]57-和为s的数字
题目一 输入一个递增的数组和一个数字,在数组中查找2个数字,是他们的和正好为S,如果有多对的和为S,则输出任意一对即可. 题解 关键信息是数组有序.初始化i,j指向第一个和第二个数,与S比较,若小了, ...
- tagCould3d 移动端优化版
针对https://github.com/bitjjj/JS-3D-TagCloud这个版本的做了移动端性能优化(使用transform做偏移及缩放,优化帧).基本原理一致. class TagCou ...
- Jenkins持续集成git、gitlab、sonarqube(7.0)、nexus,自动化部署实战,附安装包,严禁转载!!!
导读 之前用的都是SVN,由于工作需要用到Git,求人不如求己,技多不压身,多学一项技能,未来就少求别人一次,系统的学一遍,自己搭建一整套环境,自动化部署(自动发版),代码质量检测等等(为啥不用doc ...
- TB级倾斜模型加载速度太慢?这是我见过最快的加载方式没有之一
随着无人机性能快速提升,单个项目涉及到的倾斜摄影模型数据范围.数据量及单个模型体积也在不断变大,带来的问题是数据显示速度却越来越慢,那么如何在不升级配置的情况下提升模型的加载速度呢? TB级倾斜摄 ...