概念
首先介绍几个概念,Ingress 指的是进入到 k8s 集群中的 traffic,比如一个 client 发起的 HTTP 请求,经过层层网络最终到达了 k8s cluster 的外部,那么让不让它进入到 cluster 内部就是 ingress controller 做的事。
Kubernetes 原生提供了自己的 Ingress Controller,此外还有很多第三方的 Ingress Controller,istio 就是其中之一。需要注意的是,本文所有部署都是基于 GKE,其他的云平台可能略有不同。
在 k8s 中安装了 istio 之后,就可以用 istio 来控制所有进入 cluster 的流量。如何安装 istio 不在本文的范围,读者可以参考 istio 官方文档。
安装 istio 完成之后,kubectl get namespace
命令可以看到有个名叫 istio-system
的空间,所有 istio 组件的 pod 都在这个空间中。
然后用如下命令查看 istio ingressgateway
$ kubectl -n istio-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT
istio-ingressgateway LoadBalancer 10.0.23.180 35.222.xxx.xxx 15020:32011/TCP,80:30444/TCP
我们会看到这个名叫 istio-ingressgateway
的 LoadBalancer 它有公网 IP 地址 35.222.xxx.xxx,也就是说任何人都可以 ping 这个地址。
至此,所有想访问 k8s cluster 内部服务的 traffic 都只能先到达 istio-ingressgateway
,由 istio 决定该把请求发给后端哪个服务。
与控制、转发流量相关的是 istio 的 Gateway 和 VirtualService :
- Gateway,负责控制 istio 开放哪些端口、域名。比如 gateway.yaml 文件中指明 domain.com port 80, 那么只有到 domain.com 的HTTP 请求才允许进入cluster,其他的请求全部被拒绝。
- VirtualService,负责按照 url 把流量转发到后端服务。
接下来就用 istio 官方的一个例子来部署一个 helloworld,一共有两个后端 POD,每次由其中一个 pod 返回结果,效果如下:
$ curl http://test2.xxx.info/hello
Hello version: v1, instance: helloworld-6f64f86f66-jcnh8
$ curl http://test2.xxx.info/hello
Hello version: v1, instance: helloworld-6f64f86f66-gn9nv
部署
首先是一个非常简单的 Deployment 和 Service 部署,这是最基本的例子,不用太多介绍。
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld
spec:
replicas: 2
selector:
matchLabels:
app: helloworld
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: helloworld
image: istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
接下来是部署 istio 的 Gateway。如果之前在安装 istio 时已经有一个自动生成的gateway,我们可以选择复用它,或者也可以新建一个。
如果要查看已有的 gateway,可以使用这条命令
$ kubectl -n istio-system get gateway
NAME AGE
istio-autogenerated-k8s-ingress 6d15h
如果要将已有的 gateway 导出到 yaml 格式的文件,以便之后修改,可以用
kubectl -n istio-system get gateway -o yaml
这里贴一个 Gateway 例子, 它的作用就是告诉 istio:host 为 test2.xxx.info,端口为 80 的请求允许进入 cluster。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
annotations:
labels:
app: istio-ingressgateway
istio: ingressgateway
operator.istio.io/component: IngressGateways
operator.istio.io/managed: Reconcile
operator.istio.io/version: 1.5.0
release: istio
name: istio-autogenerated-k8s-ingress
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- 'test2.xxx.info'
port:
name: http
number: 80
protocol: HTTP
还剩最后一个问题:当请求进入到 cluster 之后,该把它转发给集群中的那个服务(Service)呢? 这就需要定义 istio 的 VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: istio-vs-for-helloworld
#namespace: istio-system
spec:
hosts:
- "test2.xxx.info"
gateways:
- istio-system/istio-autogenerated-k8s-ingress
http:
- match:
- uri:
exact: '/hello'
route:
- destination:
host: helloworld
这个 VirtualService 的意思就是,把来自于 istio-system
空间的 gateway istio-autogenerated-k8s-ingress
,并且host 为 test2.xxx.info
的 traffic 做转发,如果它是访问 /hello
,那么转发给后端的 helloworld service。
helloworld service 是从哪来的? 是之前部署 Deployment 和 Service 时候定义的,可以通过 kubectl get svc -n default
查看。
注意事项
在部署上面的服务时,踩了一些坑,导致 curl http://test2.xxx.info/hello
总是返回 404,现在把这些坑记录下来。
Namespace 的问题
istio 是部署在 istio-sysmtem
空间的,自动生成的 Gateway istio-autogenerated-k8s-ingress
也在 istio-system 空间,而 helloworld 是部署在 default 空间的。
所以 VirtualService
中需要明确指明从 哪个gateway来,转发到哪个 svc 去。
其中,我的 VirtualService 部署在了 default 空间,所以 istio-system/istio-autogenerated-k8s-ingress
开头就是指明了 gateway 的名空间。
另外,host: helloworld
也可以写的更加具体:helloworld.default.svc.cluster.local
。
选择哪个 Ingress
istio 官网的例子 中,helloworld 还部署了 Ingress。
我认为这是例子没有及时更新,既然用了 istio,那么转发服务应该交给 VirtualService,不应该使用 k8s 自带的 Ingress。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: istio
name: helloworld-ingress
spec:
rules:
- host: "$INGRESS_DOMAIN"
http:
paths:
- path: /hello
backend:
serviceName: helloworld
servicePort: 5000
当部署 Ingress 之后,不需要部署 VirtualService,client 也可以获得返回结果。