istio安装使用

什么是service mesh

service mesh,也翻译为服务网格。是一种在网络节点间通过动态路由的方式来进行资料与控制指令的传送。这种网络可以保持每个节点间的连线完整,当网络拓扑中有某节点失效或无法服务时,这种架构允许使用“跳跃”的方式形成新的路由后将讯息送达传输目的地(wikipedia)。

暂不谈这个抽象的概念,我们先来看下前两年比较流行的微服务。想到微服务,必然涉及一系列服务治理相关的名称:服务发现,配置中心,熔断,路由,监控,devops….。微服务架构的挑战不在于构建服务本身,而是服务间的通讯。可以想见,我们不得不在服务中引入一系列依赖和配置。

如上图:微服务由业务逻辑和通讯组件构成。

service mesh想要解决的,就是让服务专注于业务逻辑,而其他网络通讯以Sidecar的形式完成。service mesh是服务通讯的网络代理,解耦系统架构和业务逻辑。

如上图,图片来自pattern_service_mesh

istio

istio是目前最流行的service mesh开源软件,架构如下:

下面简单解释各个组件作用:

Envoy

上中的proxy,作为service mesh核心组件,使用开源软件Envoy,使用 C++ 编写的 L7 代理,CNCF 旗下的开源项目,以sidecar的形式部署。支持负载均衡,动态配置,http/grpc/MongoDB等代理,熔断,监控检查和丰富的度量指标等。下面简单看下如何独立使用。
config.yaml

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { host_rewrite: thoreauz.com, cluster: service_thoreauz }
          http_filters:
          - name: envoy.router
  clusters:
  - name: service_thoreauz
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    hosts: [{ socket_address: { address: thoreauz.com, port_value: 80 }}]

dockerfile

FROM envoyproxy/envoy:latest
COPY envoy.yaml /etc/envoy/envoy.yaml

运行docker后,可以通过9901访问管理端,通过1000代理到thoreauz.com。
envoy运行时可以利用 xDS API来实现动态配置,这点很重要,nginx需要修改配置文件后reload,当然,nginx-plus、kong等具有这个功能。

Mixer

Mixer 是一个独立于平台的组件,负责在服务网格上执行访问控制和使用策略

Pilot

Pilot 为 Envoy sidecar 提供服务发现功能,为智能路由(例如 A/B 测试、金丝雀部署等)和弹性(超时、重试、熔断器等)提供流量管理功能。它将控制流量行为的高级路由规则转换为特定于 Envoy 的配置,并在运行时将它们传播到 sidecar。

Citadel

访问控件,内置身份和凭证管理可以提供强大的服务间和最终用户身份验证。

安装

安装kubernete集群;

省略

安装非认证istio

curl -L https://git.io/getLatestIstio | sh -
istio_tmp=$(echo istio*)
ISTIO_HOME="/opt/$istio_tmp"
mv -f "${istio_tmp}" /opt
echo "export ISTIO_HOME=${ISTIO_HOME}" >> /etc/profile
echo "export PATH=${ISTIO_HOME}/bin:$PATH" >> /etc/profile
source  /etc/profile
# gcr.io可能无法拉取镜像,替换对应地址
sed -i 's#gcr.io/istio-release#istio#g' "${ISTIO_HOME}"/install/kubernetes/istio-demo.yaml
sed -i 's/memory: 2048Mi/memory: 200Mi/g' "${ISTIO_HOME}"/install/kubernetes/istio-demo.yaml

kubectl apply -f ${ISTIO_HOME}/install/kubernetes/helm/istio/templates/crds.yaml
kubectl apply -f ${ISTIO_HOME}/install/kubernetes/istio-demo.yaml
kubectl get svc -n istio-system
kubectl get pods -n istio-system

安装时间主要耗费在拉镜像,成功运行后如下:

[root@master ~]# kubectl get pods -n istio-system
NAME                                        READY     STATUS      RESTARTS   AGE
grafana-6d584567f9-6bhk9                    1/1       Running     0          10h
istio-citadel-5d8b4ddfff-6vwrl              1/1       Running     0          10h
istio-cleanup-secrets-5pwk7                 0/1       Completed   0          10h
istio-egressgateway-67bd6b9d94-h97fb        1/1       Running     0          10h
istio-galley-7597db6ff8-2hmp6               1/1       Running     0          10h
istio-grafana-post-install-tcxqh            0/1       Completed   0          10h
istio-ingressgateway-6fd5f84fdc-rzllg       1/1       Running     0          10h
istio-pilot-5cd4d78897-qqfgq                2/2       Running     1          10h
istio-policy-dd86756cf-xcrzm                2/2       Running     0          10h
istio-sidecar-injector-6995b57c94-f8cc6     1/1       Running     0          10h
istio-statsd-prom-bridge-549d687fd9-5qhgp   1/1       Running     0          10h
istio-telemetry-77c699bcdb-gtl5w            2/2       Running     0          10h
istio-tracing-7596597bd7-h6qtv              1/1       Running     0          10h
prometheus-6ffc56584f-92pvz                 1/1       Running     0          10h
servicegraph-8678d5587-mwqzj                1/1       Running     0          10h

spring-boot 示例

使用spring-boot开发这样两个应用:
* user-service: 提供接口,比如/user/greeting
* user-ui: 访问user-service的接口
两个服务的关系如下:

kubernetes 原生api部署

使用kubernetes部署user-deployment.yaml,服务发现通过kube-dns。

[root@master ~]# kubectl apply -f user-deployment.yaml
[root@master ~]# kubectl get pods
NAME                                       READY     STATUS    RESTARTS   AGE
user-service-deployment-5dbcb6cbff-cljmm   1/1       Running   0          2m
user-service-deployment-5dbcb6cbff-rjcz4   1/1       Running   0          2m
user-ui-deployment-84889c854d-cq5mq        1/1       Running   0          2m
[root@master ~]# kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP   15m
user-service   ClusterIP   10.106.195.206   <none>        80/TCP    2m
user-ui        ClusterIP   10.98.77.85      <none>        80/TCP    2m
[root@master ~]# curl -s 10.98.77.85/user/greet?name=thoreau| jq .
{
  "id": 1,
  "content": "Hello, thoreau",
  "ip": "10.244.1.14",
  "address": "user-service-deployment-5dbcb6cbff-cljmm",
  "time": "2018-10-21 10:27:01"
}
[root@master ~]# curl -s 10.98.77.85/user/greet?name=thoreau| jq .
{
  "id": 1,
  "content": "Hello, thoreau",
  "ip": "10.244.1.13",
  "address": "user-service-deployment-5dbcb6cbff-rjcz4",
  "time": "2018-10-21 10:28:19"
}

10.98.77.85 是user-ui的 CLUSTER-IP。两次访问路由到不同的user-ervice。

从上文示例可以看到,通过kubernete的服务发现功能,user-ui的请求自动路由到user-service。

istio部署

先删除之前的部署:kubectl delete -f user-deployment.yaml
使用istio部署:
kubectl apply -f <(istioctl kube-inject -f kubectl delete -f user-deployment.yaml)

[root@master ~]#  kubectl get pods
NAME                                      READY     STATUS    RESTARTS   AGE
user-service-deployment-76845bbfd-dtbkw   2/2       Running   0          1m
user-service-deployment-76845bbfd-mm7xw   2/2       Running   0          1m
user-ui-deployment-b88d5b76b-86xqz        2/2       Running   0          1m

可以看到,pod中的READY字段从1/1变成了2/2:

[root@master ~]#kubectl get pod user-service-deployment-76845bbfd-dtbkw -o json|jq .spec.containers[].name
"user-service"
"istio-proxy"

pod 中的容器多了istio-proxy,这就是之前提到的sidecar,此时验证接口访问和之前kubernetes部署一样正常。

iotio-gateway

刚才部署的服务,我希望可以从集群外部访问user-ui。可以使用kubernetes的ingress实现,但这儿我们使用iotio-gateway。

[root@master ~]# kubectl apply -f https://raw.githubusercontent.com/ThoreauZZ/spring-boot-istio/master/istio-rules/my-gateway.yaml
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')

部署完成后,可以在集群外部可以通过http://主机外网ip:INGRESS_PORT如:

iotio路由

现在,我修改了user-service,作为2.0版本部署,同时存在1.0和2.0版本,我希望能做到只有30%的请求到2.0版本。创建user-service-deployment-v2,执行如下:

 [root@master ~]# kubectl apply -f <(istioctl kube-inject -f kubectl delete -f user-service-deployment-v2.yaml)
 [root@master ~]# kubectl get pods
NAME                                          READY     STATUS    RESTARTS   AGE
user-service-deployment-76845bbfd-dtbkw       2/2       Running   0          1h
user-service-deployment-76845bbfd-mm7xw       2/2       Running   0          1h
user-service-deployment-v2-6c77744685-9cbdn   2/2       Running   0          2m
user-ui-deployment-b88d5b76b-86xqz            2/2       Running   0          1h

规则rute-userservice.yaml配置如下:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: user-service
spec:
  host: user-service
  subsets:
  - name: v1
    labels:
      version: "1.0"
  - name: v2
    labels:
      version: "2.0"

---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: spring-boot-user-service
spec:
  hosts:
  - user-service
  http:
  - route:
    - destination:
        host: user-service
        subset: v1
        port:
          number: 80
      weight: 70
    - destination:
        host: user-service
        subset: v2
        port:
          number: 80
      weight: 30

kubectl apply -f rute-userservice.yaml

这样,不用对代码做任何修改,就能做到流量的随意切换,如果把rute-userservice.yaml中的v2权重改成100,再试,会发现所有请求都路由到v2中。
从这个示例看得出来,service mesh的好处是把业务代码和网络配置解耦。

同样,通过路由配置,还可以做到http错误码、请求延时等故障注入,还有超时设置、熔断等丰富的网络配置。

值得一提的是,还可以配置mirror实现流量复制。

可视化指标

istio1.2默认安装的grafana,prometheus,servicegraph,zipkin。可以修改service的type为NodePort,比如

[root@master ~]#kubectl get svc servicegraph -n istio-system -o yaml| sed "s/ClusterIP/NodePort/g"|kubectl apply -f -
[root@master ~]# kubectl get svc servicegraph -n istio-system
NAME           TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
servicegraph   NodePort   10.105.150.5   <none>        8088:30639/TCP   1h

通过30639访问:


比如调用链查看:

kubectl get svc jaeger-query -n istio-system -o yaml| sed "s/ClusterIP/NodePort/g"|kubectl apply -f -

示例中使用了spring-cloud-slueth

总结

本文简单介绍了sever mesh,它以sidecar的形式接管服务的出入流量,sidecar负责服务治理,应用专注于业务逻辑。之后简单介绍最流行的server mesh开源实现istio,并运行了一个两个spring-boot应用。


参考
http://philcalcado.com/2017/08/03/pattern_service_mesh.html
https://jimmysong.io/posts/envoy-sidecar-injection-in-istio-service-mesh-deep-dive/

CONTENTS