在应用开发过程中,虽然 Istio 号称是 0 入侵,但我们在开发过程中,还是会碰到一些问题,如在远程调用和调用链等方面还是会有一些改变,下面列举了一些 开发人员在开发过程中需要关注的问题。
临时暴露服务
在应用开发中,我们需要连接某个特定的远程服务( Provider),并且能够方便的查询调用链和日志。
通常我们会在测试环境中部署已经基本完善的微服务,这些微服务想要被开发者调用,需要“临时”放开,在 Istio 中,通常放开服务访问的方法有如下两种。
1、使用 NodePort 暴露服务
我们可以创建一个新的 NodePort 的服务类型来暴露,如下面的 yaml 配置。
代码语言:txt复制apiVersion: v1
kind: Service
metadata:
name: shopcart-dev
spec:
type: NodePort
selector:
app: shopcart
ports:
- port: 80
targetPort: 80
或者,直接使用如下命令暴露:
代码语言:txt复制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 服务:
代码语言:txt复制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 就可以访问相应的应用了。
代码中的远程调用服务名/域名问题
我们在写远程调用的时候会自然地写出如下的代码:
代码语言:txt复制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,我们可以把服务名作为域名来调用,但这么写有个显而易见但弊端:当端口,或者服务名发生变化但时候,噩梦便来了。
我们不妨写一个方法来处理此类问题:
代码语言:txt复制// 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。
代码语言:txt复制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,有点儿暴力:
代码语言:txt复制 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 作为客户端访问凭据,亦可以控制权限。
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 界面,支持集群内安装和本地启动。本地安装脚本:
代码语言:txt复制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 测试失败
代码语言:txt复制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 测试失败
代码语言:txt复制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 进行了集成。