【为什么用Istio】
微服务对于每个功能的开发细化了,但是对于系统的管理负载度增强了,尤其是网络流量的管理。这样很多功能例如黑名单,导流,加密,访问控制,流量监控,熔断,限速,收费功能,数据流节点延迟,就不需要在应用代码中更改了。
【Istio的关键功能】
HTTP/1.1,HTTP/2,gRPC和TCP流量的自动区域感知负载均衡和故障切换。
通过丰富的路由规则,容错和故障注入,对流行为的粒度控制。
支持访问控制,速率限制和配额的可插拔策略层和配置API。
集群内所有流量的自动度量,日志和跟踪,包括集群入口和出口。
安全的服务到服务身份验证,在集群中的服务之间具有强大的身份标识。
【微服务的两面性】
虽然微服务对开发进行了简化,但通过将复杂系统切分为若干个微服务来分解和降低复杂度,使这些微服务被小型开发团队所理解和维护。但是复杂度并为消失。
微服务拆分后,单个微服务复杂度大幅降低,但是由于系统被一个单体拆分为十几个甚至更多微服务,带来了另一个复杂度:微服务的连接,管理和监控。
【Sidecar模式】
Sidecar是容器应用模式的一种,service meash(服务网格)架构的一种实现方式,使用Sidecar部署服务网格时,无需再节点上运行代理(不需要基础结构的协作),但是集群当中将运行多个sidecar副本,从另一个角度看:在sidecar部署方式当中,你会为每个应用容器部署一个伴生容器。Sidecar接管进出应用容器的所有流量。在kubernetes的pod中,在原有应用旁启动一个sidecar容器,可以理解为两个容器共享存储,网络等资源,可以理解为将sidecar容器注入到pod当中,两个容器共享资源。
【istio注入和伴生容器】
通过上面可以了解istio会通过在一个pod中多创建一个伴生容器,使这个伴生容器管理这个pod所有的进出流量,istio注入就是将这个伴生容器注入原本的pod中。下面是一个例子:
[root@localhost ~]# [root@localhost ~]# kubectl apply -f nginx.yaml
pod/test created
[root@localhost ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test 1/1 Running 0 2s
[root@localhost ~]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test-container
image: nginx:latest
imagePullPolicy: IfNotPresent
#此处是一个正常启动的pod,可以看到没有伴生容器
#接下来在这个nginx.yaml的基础上,再手动注入一个伴生容器
#这里只需要命令注入,yaml会自动写好。
[root@localhost ~]# istioctl kube-inject -f nginx.yaml > nginx-istio.yaml
[root@localhost ~]# cat nginx-istio.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/default-container: test-container
kubectl.kubernetes.io/default-logs-container: test-container
prometheus.io/path: /stats/prometheus
prometheus.io/port: "15020"
prometheus.io/scrape: "true"
sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null,"revision":"default"}'
creationTimestamp: null
labels:
security.istio.io/tlsMode: istio
service.istio.io/canonical-name: test
service.istio.io/canonical-revision: latest
name: test
spec:
containers:
- image: nginx:latest
imagePullPolicy: IfNotPresent
name: test-container
resources: {}
- args:
- proxy
- sidecar
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=default:info
- --concurrency
- "2"
env:
- name: JWT_POLICY
value: third-party-jwt
- name: PILOT_CERT_PROVIDER
value: istiod
- name: CA_ADDR
value: istiod.istio-system.svc:15012
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: HOST_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: PROXY_CONFIG
value: |
{}
- name: ISTIO_META_POD_PORTS
value: |-
[
]
- name: ISTIO_META_APP_CONTAINERS
value: test-container
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_INTERCEPTION_MODE
value: REDIRECT
- name: ISTIO_META_WORKLOAD_NAME
value: test
- name: ISTIO_META_OWNER
value: kubernetes://apis/v1/namespaces/default/pods/test
- name: ISTIO_META_MESH_ID
value: cluster.local
- name: TRUST_DOMAIN
value: cluster.local
image: docker.io/istio/proxyv2:1.12.0
name: istio-proxy
ports:
- containerPort: 15090
name: http-envoy-prom
protocol: TCP
readinessProbe:
failureThreshold: 30
httpGet:
path: /healthz/ready
port: 15021
initialDelaySeconds: 1
periodSeconds: 2
timeoutSeconds: 3
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 10m
memory: 40Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 1337
runAsNonRoot: true
runAsUser: 1337
volumeMounts:
- mountPath: /var/run/secrets/istio
name: istiod-ca-cert
- mountPath: /var/lib/istio/data
name: istio-data
- mountPath: /etc/istio/proxy
name: istio-envoy
- mountPath: /var/run/secrets/tokens
name: istio-token
- mountPath: /etc/istio/pod
name: istio-podinfo
initContainers:
- args:
- istio-iptables
- -p
- "15001"
- -z
- "15006"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- '*'
- -d
- 15090,15021,15020
image: docker.io/istio/proxyv2:1.12.0
name: istio-init
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 10m
memory: 40Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
volumes:
- emptyDir:
medium: Memory
name: istio-envoy
- emptyDir: {}
name: istio-data
- downwardAPI:
items:
- fieldRef:
fieldPath: metadata.labels
path: labels
- fieldRef:
fieldPath: metadata.annotations
path: annotations
name: istio-podinfo
- name: istio-token
projected:
sources:
- serviceAccountToken:
audience: istio-ca
expirationSeconds: 43200
path: istio-token
- configMap:
name: istio-ca-root-cert
name: istiod-ca-cert
status: {}
---
#再次执行测试
kubectl a[root@localhost ~]# kubectl apply -f nginx-istio.yaml
pod/test created
[root@localhost ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test 2/2 Running 0 4s
#现在pod中却启动了两个容器,其中一个则是新添加的伴生容器,进出流量就由这个容器接管。
完成istio注入的方式,有手动注入和自动注入。
手动注入就是想先前的例子一样,后面添加上kubectl apply -f - 即可
istioctl kube-inject -f nginx.yaml > nginx-istio.yaml | kubectl apply -f -
自动注入则是给命名空间打上特定标签,当拥有这个标签的命名空间内的pod都会自动添加伴生容器。下面是例子:
[root@localhost ~]# kubectl label ns default istio-injection=enabled
namespace/default labeled
[root@localhost ~]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test-container
image: nginx:latest
imagePullPolicy: IfNotPresent
[root@localhost ~]# kubectl apply -f nginx.yaml
pod/test created
[root@localhost ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test 2/2 Running 0 5s
#可以看见当设置了标签后,即使没有手动注入,只要pod被部署于这个命名空间时,就会自动添加伴生容器。
【istio在k8s中怎么工作】
#这里使用命令获取一下控制器发现,istio自带暴露服务的控制器和ingress-nginx类似的工作方式
[root@localhost bookinfo]# kubectl get deploy -n istio-system
NAME READY UP-TO-DATE AVAILABLE AGE
grafana 1/1 1 1 60m
istio-egressgateway 1/1 1 1 60m
istio-ingressgateway 1/1 1 1 60m
istiod 1/1 1 1 60m
jaeger 1/1 1 1 60m
kiali 1/1 1 1 60m
prometheus 1/1 1 1 60m
#查看后发现istio-ingressgateway和istio-egressgateway的存在,gateway资源可以直接打上标签来使用这个service
[root@localhost bookinfo]# kubectl get service -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana NodePort 10.96.241.212 <none> 3000:33000/TCP 59m
istio-egressgateway ClusterIP 10.96.38.192 <none> 80/TCP,443/TCP 60m
istio-ingressgateway LoadBalancer 10.96.109.254 <pending> 15021:11906/TCP,80:38548/TCP,443:25679/TCP,31400:12156/TCP,15443:63397/TCP 60m
istiod ClusterIP 10.96.74.254 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 60m
jaeger-collector ClusterIP 10.96.122.5 <none> 14268/TCP,14250/TCP,9411/TCP 59m
kiali NodePort 10.96.37.93 <none> 20001:20001/TCP,9090:9091/TCP 59m
prometheus NodePort 10.96.246.113 <none> 9090:30090/TCP 59m
tracing NodePort 10.96.178.201 <none> 80:30686/TCP,16685:30685/TCP 59m
zipkin ClusterIP 10.96.206.142 <none>
这里的istio-ingressgateway和istio-egressgateway管理了istio所有进出流量。网关设置标签选择器可以设定指定控制器。
【istio资源】
实际操作:https://mp.csdn.net/mp_blog/creation/editor/130003775
此处gateway资源选择了ingressgateway控制器,实际流量会经过ingressgateway控制器,这个gateway将管理访问任意域名,80端口,http协议的流量。
[root@localhost istio]# cat gateway.yaml
apiVersion: networking.istio.io/v1alpha3 #这里network api注意版本。
kind: Gateway #创建网关类型
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway #设置标签,给istio默认的控制器识别,istio有默认控制器。有egressgateway控制器和ingressgateway控制器
servers:
- port:
number: 80 #设置80端口
protocol: HTTP #设置协议为HTTP
name: http #设置名称http
hosts:
- "*" #设置允许访问的域名,这里是全部允许。
这里设置虚拟服务,上面gateway筛选的流量会通过vs虚拟服务。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: booking
spec:
hosts:
- "*" #定义路由规则关联的一组主机。
gateways:
- bookinfo-gateway #设置网关名,可以时多个网关
http:
- match: #定义路由的匹配规则列表。
- uri:
prefix: /api/v1/booking
- uri:
exact: /login #exact请求,prefix前缀
route: #路由转发目的地列表。
- destination:
host: booking-service #这里设置service服务名称
port:
number: 80 #设置service端口
match相当于设置条件,route指定流量的去处,这里设置了service服务名称,最后流量会被转发到这个service上。
当微服务有多个版本的时候,就需要用到destinationrule资源,通过vs,不同的规则将流量转发到不同的版本。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: dest-nginx
spec:
host: ttt #设置service名称
subsets:
- name: a
labels:
version: a #设置pod标签
- name: b
labels:
version: b #设置pod标签