控制平面副本集
请参照本指南安装具有副本集控制平面实例的 Istio 多集群部署,并在每个群集中使用 gateway 来提供跨集群连接服务。
在此配置中,每个集群都使用它自己的 Istio 控制平面来完成安装,并管理自己的 endpoint, 而不是使用共享的 Istio 控制平面来管理网格。 出于以下目的,所有群集都在共同的管理控制下,执行策略与安全行为
通过共享服务副本及命名空间,并在所有群集中使用公共的根证书,可以在群集中实现一个 Istio 服务网格。 跨集群通信基于各个集群的 Istio gateway。
前提条件
两个以上 Kubernetes 集群,且版本为:1.15, 1.16, 1.17, 1.18。
有权限在 每个 Kubernetes 集群上,部署 Istio 控制平面。
每个集群
istio-ingressgateway
服务的 IP 地址,必须允许其它集群访问,最好使用 4 层负载均衡(NLB)。 有些云服务商不支持负载均衡或者需要特别注明才能使用。所以,请查阅您的云服务商的文档,为负载均衡类型的服务对象启用 NLB。 在不支持负载均衡的平台上部署时,可能需要修改健康检查,使得负载均衡对象可以注册为 ingress gateway。一个 根 CA。跨集群的服务通信必须使用双向 TLS 连接。 为了在集群之间使用双向 TLS 通信,每个集群的 Citadel 都将由共享的根 CA 生成中间 CA 凭据。 为方便演示,您在安装 Istio 时,可以使用
samples/certs
目录下的一个根 CA 证书样本。
在每个集群中部署 Istio 控制平面
从组织的根 CA 为每个集群的 Citadel 生成中间 CA 证书。 共享的根 CA 支持跨集群的双向 TLS 通信。
为方便演示,后面两个集群的演示都将使用 Istio 样本目录下的证书。 在实际部署中,一般会使用一个公共根 CA 为每个集群签发不同的 CA 证书。
想要在 每个集群 上部署相同的 Istio 控制平面,请运行下面的命令:
使用类似于下面的命令,为生成的 CA 证书创建 Kubernetes secret。了解详情,请参见 CA 证书。
$ 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:
$ istioctl manifest apply \ -f install/kubernetes/operator/examples/multicluster/values-istio-multicluster-gateways.yaml
想了解更多细节和自定义选项,请参考使用 Istioctl 安装。
配置 DNS
应用一般需要通过他们的 DNS 解析服务名然后访问返回的 IP,为远端集群中的服务提供 DNS 解析,将允许已有的应用不做修改就可以正常运行。
Istio 本身不会为两个服务之间的请求使用 DNS。集群本地的服务共用一个通用的 DNS 后缀(例如,svc.cluster.local
)。Kubernetes DNS 为这些服务提供了 DNS 解析。
要为远端集群的服务提供类似的配置,远端集群内的服务需要以 <name>.<namespace>.global
的格式命名。
Istio 还附带了一个名为 CoreDNS 的服务,它可以为这些服务提供 DNS 解析。
想要使用 CoreDNS,Kubernetes DNS 的 .global
必须配置为 stub a domain
。
在每个要调用远端集群中服务的集群中(通常是所有集群), 选择并创建下面这些 ConfigMaps 中的一个,或直接使用现有的做修改。
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"global": ["$(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]}
EOF
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
proxy . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
global:53 {
errors
cache 30
forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP}):53
}
EOF
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
global:53 {
errors
cache 30
forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP}):53
}
EOF
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
global:53 {
errors
cache 30
forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})
}
EOF
应用服务的配置
一个集群中所有需要被其它远端集群访问的服务,都需要在远端集群中配置 ServiceEntry
。
service entry 使用的 host 应该采用如下格式:<name>.<namespace>.global
。
其中 name 和 namespace 分别对应服务名和命名空间。
为了演示跨集群访问,需要配置: 在第一个集群中运行的 sleep service 并调用 在第二个集群中运行的 httpbin service。 开始之前:
- 选择两个 Istio 集群,分别称之为
cluster1
和cluster2
。
你可以使用
kubectl
命令带上--context
参数去访问集群cluster1
和cluster2
, 例如kubectl get pods --context cluster1
。 使用如下命令列出你的上下文:$ kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * cluster1 cluster1 user@foo.com default cluster2 cluster2 user@foo.com default
保存集群的上下文到环境变量:
$ 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 = cluster1, CTX_CLUSTER2 = 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
的 gateway 地址:$ export CLUSTER2_GW_ADDR=$(kubectl get --context=$CTX_CLUSTER2 svc --selector=app=istio-ingressgateway \ -n istio-system -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
该命令使用了 gateway 的公网 IP,如果您有域名的话,您也可以直接使用域名。
在
cluster1
中为httpbin
服务创建一个 service entry。为了让
cluster1
中的sleep
访问cluster2
中的httpbin
,我们需要在cluster1
中为httpbin
服务创建一个 service entry。 service entry 的 host 命名应采用<name>.<namespace>.global
的格式。 其中 name 和 namespace 分别与远端服务的 name 和 namespace 对应。为了让 DNS 解析
*.global
域下的服务,您需要给这些服务分配一个 IP 地址。如果 global service 需要使用虚拟 IP,您可以使用,但除此之外,我们建议使用范围在
240.0.0.0/4
的 E 类 IP 地址。 使用这类 IP 地址的应用的流量将被 sidecar 捕获,并路由至适当的远程服务。$ 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 ports: - name: http1 number: 8000 protocol: http resolution: DNS addresses: # 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 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: ${CLUSTER2_GW_ADDR} ports: http1: 15443 # Do not change this port value EOF
上面的配置会基于双向 TLS 连接,将
cluster1
中对httpbin.bar.global
的 任意端口 的访问,路由至<IPofCluster2IngressGateway>:15443
endpoint。gateway 的 15443 端口是一个特殊的 SNI-aware Envoy,当您在集群中部署 Istio 控制平面时,它会自动安装。 进入 15443 端口的流量会为目标集群内适当的服务的 pods 提供负载均衡(在这个例子中是,
cluster2
集群中的httpbin.bar
服务)。验证
sleep
是否可以访问httpbin
。$ kubectl exec --context=$CTX_CLUSTER1 $SLEEP_POD -n foo -c sleep -- curl -I httpbin.bar.global:8000/headers
通过 egress gateway 发送远程流量
如果您想在 cluster1
中通过一个专用的 egress gateway 路由流量,而不是从 sidecars 直连。
使用下面的 service entry 替换前面一节对 httpbin.bar
使用的配置。
如果 $CLUSTER2_GW_ADDR
是 IP 地址,请使用 $CLUSTER2_GW_ADDR - IP address
选项。如果 $CLUSTER2_GW_ADDR
是域名,请使用 $CLUSTER2_GW_ADDR - hostname
选项。
- 暴露
cluster1
egress gateway 地址:
$ export CLUSTER1_EGW_ADDR=$(kubectl get --context=$CTX_CLUSTER1 svc --selector=app=istio-egressgateway \
-n istio-system -o yaml -o jsonpath='{.items[0].spec.clusterIP}')
- 使 httpbin-bar 服务的 entry 生效:
$ 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
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: STATIC
addresses:
- 240.0.0.2
endpoints:
- address: ${CLUSTER2_GW_ADDR}
network: external
ports:
http1: 15443 # Do not change this port value
- address: ${CLUSTER1_EGW_ADDR}
ports:
http1: 15443
EOF
如果 ${CLUSTER2_GW_ADDR}
是域名,您也可以使用 resolution: DNS
实现 endpoint 解析。
$ 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
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: DNS
addresses:
- 240.0.0.2
endpoints:
- address: ${CLUSTER2_GW_ADDR}
network: external
ports:
http1: 15443 # Do not change this port value
- address: istio-egressgateway.istio-system.svc.cluster.local
ports:
http1: 15443
EOF
示例的清理
运行下面的命令清理示例中的服务。
清理
cluster1
:$ 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
清理
cluster2
:$ kubectl delete --context=$CTX_CLUSTER2 -n bar -f @samples/httpbin/httpbin.yaml@ $ kubectl delete --context=$CTX_CLUSTER2 ns bar
Version-aware 路由到远端服务
如果远端服务有多个版本,您可以为 service entry endpoint 添加标签。比如:
$ 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
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: DNS
addresses:
# the IP address to which httpbin.bar.global will resolve to
# must be unique for each service.
- 240.0.0.2
endpoints:
- address: ${CLUSTER2_GW_ADDR}
labels:
cluster: cluster2
ports:
http1: 15443 # Do not change this port value
EOF
然后您就可以使用适当的 gateway 标签选择器,创建虚拟服务和目标规则去定义 httpbin.bar.global
的子集。
这些指令与路由到本地服务使用的指令相同。
完整的例子,请参考 multicluster version routing。
卸载
若要卸载 Istio,请在 每个集群 上执行下面的命令:
$ kubectl delete -f $HOME/istio.yaml
$ kubectl delete ns istio-system
总结
使用 Istio gateway、公共的根 CA 和 service entry,您可以配置一个跨多个 Kubernetes 集群的单 Istio 服务网格。 经过这种方式配置后,应用无需任何修改,即可将流量路由到远端的集群内。 尽管此方法需要手动配置一些访问远端服务的选项,但 service entry 的创建过程可以自动化。