Istio 中业务开发需要关注的二三事

时间:2022-07-25
本文章向大家介绍Istio 中业务开发需要关注的二三事,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在应用开发过程中,虽然 Istio 号称是 0 入侵,但我们在开发过程中,还是会碰到一些问题,如在远程调用和调用链等方面还是会有一些改变,下面列举了一些 开发人员在开发过程中需要关注的问题。

临时暴露服务

在应用开发中,我们需要连接某个特定的远程服务( Provider),并且能够方便的查询调用链和日志。

通常我们会在测试环境中部署已经基本完善的微服务,这些微服务想要被开发者调用,需要“临时”放开,在 Istio 中,通常放开服务访问的方法有如下两种。

1、使用 NodePort 暴露服务

我们可以创建一个新的 NodePort 的服务类型来暴露,如下面的 yaml 配置。

apiVersion: v1
kind: Service
metadata:
  name: shopcart-dev
spec:
  type: NodePort
  selector:
    app: shopcart
  ports: 
    - port: 80
      targetPort: 80

或者,直接使用如下命令暴露:

kubectl expose deployment shopcart --type=NodePort --port=80 --target-port=80 --name=shopcart-dev

通过 kubectl get svc shopcart-dev 可以查看具体的端口,默认在 30000-32767 这个范围

这样,我们就可以使用 schema://node-ip:nodeport-ip 来访问 shopcart 这个应用了。

2 使用 Gateway 来暴露服务

下面的代码分别发布了一个 http 服务和一个 tcp 服务:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: passport-tmp-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 7301
      name: http
      protocol: HTTP
    hosts:
    - "*"
  - port:
      number: 28888
      name: dubbo-dev
      protocol: TCP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: dev-vs
spec:
  hosts:
  - "*"
  gateways:
  - passport-tmp-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: passport
        port:
          number: 7301
  tcp:
  - match:
    - port: 28888
    route: 
    - destination:
        host: dubbo-app
        port:
          number: 28888

现在使用 schema://gateway-ip: port 就可以访问相应的应用了。

代码中的远程调用服务名/域名问题

我们在写远程调用的时候会自然地写出如下的代码:

User user = restTemplate.getForObject("http://passport:8080/user/info", User.class);
// 或者是
User user = restTemplate.getForObject("http://passport.namespace:8080/user/info", User.class);

虽然有了 K8S 的 service,我们可以把服务名作为域名来调用,但这么写有个显而易见但弊端:当端口,或者服务名发生变化但时候,噩梦便来了。

我们不妨写一个方法来处理此类问题:

// java 伪代码
public <T> T getRemote(String servieName, String apiUrl,  Class<T> type) {
    String url = Util.combin(Util.getHostFromConfig(servieName), apiUrl);
    return restTemplate.getForObject(url, type);
}

//调用
User user = getRemote("passport", "/user/info", User.class);

servieName 现在是一个变量了,而且是从配置文件中获取的,并且这个配置可以放到远程配置中心。以后再变换域名或者端口都雨女无瓜了。

调用链追踪

TCM/Istio 的 HTTP 调用链符合 opentracing 标准,所以想要被 Istio 追踪,需要在远程调用的时候加上相应的上下文 http headers。

x-request-id
x-b3-traceid
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context

x-cloud-trace-context
traceparent
grpc-trace-bin

这些 headers 需要在开发过程中进行处理,下面是一段 Java 代码处理方法,这个方法“无脑“透传了上下文的 headers,有点儿暴力:

    public static HttpHeaders traceHeaders(HttpServletRequest request, String[] keys) {
        if (null == keys) {
            Enumeration<String> oriHeader = request.getHeaderNames();
            ArrayList<String> oriHeaderList = Collections.list(oriHeader);
            keys = oriHeaderList.toArray(new String[0]);
        }
        HttpHeaders headers = new HttpHeaders();
        for (String s : keys) {
            String v = request.getHeader(s);
            if (v != null && !v.isEmpty()) {
                headers.add(s, v);
            }
        }
        return headers;
    }

    public static String restGet(String url, RestTemplate restTemplate, HttpServletRequest request) {
        HttpHeaders headers = traceHeaders(request, null);
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        return result.getBody();
    }

在实际开发中,我们可以借用现成的类库,如:https://github.com/opentracing-contrib/java-spring-web

调用链中的日志:错误堆栈和业务信息

在开发过程中,当例外发生时,我们希望通过调用链顺藤摸瓜,查看远程 Provider 中出现的错误详细日志。

此时,需要取出 opentracing 的那些 header,并在记录 Exception 的时候加入这些信息。

Java 语言下也有现成类库可以使用,如在 log4j 的 MDC 中可以加入上下文信息和业务信息。可以参考这篇文章实现:https://www.baeldung.com/mdc-in-log4j-2-logback。在实际应用中,也可以通过切面的方式修改log记录方式而无需修改业务代码。

当把 trace 信息写入日志系统后,我们就可以通过日志系统(如 ELK)的调用链的 id 查询到“额外的”日志,通过这些日志,可以洞察到微服务的更多细节。

我们也可以使用 jaeger client 的 SDK 来对接更多的调用链日志,稍后会撰写文章进行专门讨论。

远程日志查看/管理集群

当下,在腾讯云 TKE/TCM 中开发,普通开发者如何查看远程日志?运维人员如何管理集群?如果不希望普通开发者对整个集群有修改权限,只想给日志的读取权限,有些开发者也不愿意登录云控制台。可考虑如下实现方法:

1、通过 TKE 控制台通过授权管理对子账号进行授权

在腾讯云建立一个子账号,通过容器集群的授权管理,将某个命名空间的只读权限赋给这个子账号,开发者通过这个子账号进行日志查询。

TKE 后台进入容器集群,点击授权管理(当前需要申请开放白名单),就可以进行可视化的授权操作。

授权完成后,使用子账号登陆控制台,则只能进行只读操作了,同时,这个子账号当前的 kubeconfig 文件也具有了相应权限。

授权菜单入口
选择子用户
给子账号某个命名空间的相应权限
使用子账号的 kubeconfig

使用子账号登录之后,将只有有限的权限,使用这个新的 Kubeconfig 作为客户端访问凭据,亦可以控制权限。

2、安装本地 WebUI 或者使用客户端工具

除了使用 Kubernates Dashboard(TKE 中并未默认安装),下面这些工具也可使用,有些可以在本地 docker 中安装,对集群“无入侵”。

Lens

https://github.com/lensapp/lens

无入侵:客户端界面,本地安装。

Octant

https://github.com/vmware-tanzu/octant

无入侵:VMware 出品,Web 界面,功能全面,本地运行Web服务。**

Weave Scope

https://github.com/weaveworks/scope

Web 界面,本地安装,支持本地 docker 管理; K8S 管理需要在集群安装。

Kubernetes Web View

https://github.com/hjacobs/kube-web-view

无入侵:Web 界面,支持集群内安装和本地启动。本地安装脚本:

docker run -it --rm -p 8080:8080 -u $(id -u) -v $HOME/.kube:/.kube hjacobs/kube-web-view

K8Dash

https://github.com/indeedeng/k8dash

Web 界面,需要在集群内安装。

Kubernator

https://github.com/smpio/kubernator

无入侵:Web 界面,集群内安装,本地安装。但 TKE 测试失败

kubectl proxy 
docker run -it --rm --name=kubernator -p 3000:80 smpio/kubernator

Kubernetes Opertional View

https://github.com/hjacobs/kube-ops-view

无入侵:Web 界面,本地安装,这是个只读的系统,不提供交互。但 TKE 测试失败

kubectl proxy 
docker run -it --rm --net=host hjacobs/kube-ops-view

Konstellate

https://github.com/containership/konstellate

Web 界面,需要下载并安装 Clojure(JVM 的 Lisp 方言) 命令行,然后在本地启动服务:clojure -m figwheel.main -b dev -r。很久未更新,不推荐。

3、使用日志系统

更推荐但方法是使用日志系统,上述方法只能查看有限的日志,适合即时开发。在 TKE 集群中,可以使用 CLS 进行日志的收集,TKE 已经对 CLS 进行了集成。