全书目录
本文目录
1.命令行工具安装
2. Kubernetes/OpenShift安装
3. Istio安装
4.示例Java微服务安装
4.1 源码概览
4.2 编译和部署customer服务
4.3 编译和部署Preference服务
4.4 编译和部署recommendation服务
本章中,我们会介绍如何在Kubernetes上安装Istio。Istio并没有和Kubernets绑定,实际上,它合适很多种基础架构平台。但是,Kubernetes因为原生支持边车部署(sidecar deployment)概念,因此它是运行Istio的最佳平台之一。你可以使用任何版本的Kubernetes。本章中,我们将使用Minishift,这是一个可以让你的OpenShift安装并运行在本地虚拟机上的工具,而OpenShift则是一个面向开发者的Kubernetes企业发行版。
1. 命令行工具安装
作为一个开发者,你可能已有了各种工具,但为了清晰起见,我们还是给个本书所需的工具列表:
-
Minishift:这是minikube的红帽发行版
-
VirtualBox:提供虚拟机的虚拟化工具
-
Mac/Windows环境上的Docker:Docker客户端
-
Kubectl:本书中我们主要会使用oc命令行,它基本上能替代kubectl,并可互换着使用。
-
Oc:minishift oc-env 命令行会输出oc命令行的路径,因此你不用单独下载了。
-
OpenJDK:你需要能访问javac和java命令行工具
-
Maven:用于编译Java项目
-
Stern:为了方便查看日志
-
Siege:用于第四章中的Istio压力测试
-
Git:通过 git clone 命令下载示例代码
-
istioctl:会通过下文中的部署被安装
-
curl和tar:要在bash中用到
2. OpenShift/Kubernetes安装
在安装环境之前,你应该很清楚你将创建很多服务。你将安装Istio控制平面、一些支持性能指标和可视化的应用程序,以及示例应用程序服务。为此,用于运行Kubernetes的虚拟机(VM)需要有足够的资源。我们建议使用8 GB内存和3个CPU核的虚拟机,但本书中包含的示例在4 GB内存和2个CPU核虚拟机上也能成功运行。
Minishift安装好以后,你可以引导环境了,通过运行以下命令行:
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/bin/bash export MINISHIFT_HOME=~/minishift_1.27.0 export PATH=$MINISHIFT_HOME:$PATH minishift profile set tutorial minishift config set memory 8GB minishift config set cpus 3 minishift config set vm-driver virtualbox minishift config set image-caching true minishift addon enable admin-user minishift addon enable anyuid minishift config set openshift-version v3.11.0 minishift start |
然后,你可以进行环境配置了,然后就可以访问Minishift、Docker守护进程以及登录Kubernetes集群了:
1
2
3
|
eval $(minishift oc-env) eval $(minishift docker-env) oc login $(minishift ip):8443 -u admin -p admin |
如果一切顺利,你能运行下面的命令:
1
2
3
|
oc get node NAME STATUS AGE VERSION localhost Ready 5m v1.11.0+d4cacc0 |
这时,你可以运行下面命令以访问web console:
1
|
minishift dashboard |
如果遇到错误,请查看Istio Tutorial for Java Microservices网站中的步骤,甚至提交一个Github 问题。
3. Istio安装
Istio发行版中包括命令行工具、安装文件和示例应用。运行下面的命令以下载Istio 1.0.4版本并解压缩:
1
|
curl -L https: //github.com/istio/istio/releases/download/1.0.4/istio-1.0.4/-osx.tar.gz | tar xzcd istio-1.0.4 |
现在你要开始准备OpenShift/Kubernetes环境了。Istio使用ValidatingAdmissionWebhook来校验Istio配置,使用MutatingAdmissionWebhook去自动向用户pod中插入边车代理。运行下面的命令去更新Minishift的默认配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
minishift openshift config set --target=kube --patch '{ "admissionConfig" : { "pluginConfig" : { "ValidatingAdmissionWebhook" : { "configuration" : { "apiVersion" : "v1" , "kind" : "DefaultAdmissionConfig" , "disable" : false } }, "MutatingAdmissionWebhook" : { "configuration" : { "apiVersion" : "v1" , "kind" : "DefaultAdmissionConfig" , "disable" : false } } } } }' |
现在开始安装Istio。在Istio发行版的根目录中,运行以下命令:
1
2
3
|
oc apply -f install/kubernetes/helm/istio/templates/crds.yaml oc apply -f install/kubernetes/istio-demo.yaml oc project istio-system |
这会安装所有Istio控制平面组件,包括Pilot、Mixer(实际的Mixer pod的名称为telemetry和policy)和Citadel。还会安装一些附加服务,包括用于统计信息收集的Prometheus,用于分布式跟踪的Jaeger,用于统计信息展示的Grafana,用于服务简单可视化的Servicegraph等。这些服务会在第6章中介绍。
最后,因为我们用的是OpenShift,你可以将这些服务通过Router发布出去,这样你就不用配置复杂的节点端口了。
1
2
3
4
|
oc expose svc servicegraph oc expose svc grafana oc expose svc prometheus oc expose svc tracing |
现在,所有Istio控制平面组件和相关服务都已经运行起来了。你可以运行下面的命令来查看它们:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
oc get pods NAME grafana-59b787b9b 1/1 Running 0 3m istio-citadel-78df8c67d9 1/1 Running 0 3m istio-cleanup-secrets 0/1 Completed 0 3m istio-egressgateway-674686c846 1/1 Running 0 3m istio-galley-58f566cb66 1/1 Running 0 3m istio-grafana-post-install 0/1 Completed 0 3m istio-ingressgateway-6bbdd58f8c 1/1 Running 0 3m istio-pilot-56b487ff45 2/2 Running 0 3m istio-policy-68797d879 2/2 Running 0 3m istio-security-post-install 0/1 Completed 0 3m istio-sidecar-injector-b88dfb954 1/1 Running 0 3m istio-telemetry-68787476f4 2/2 Running 0 3m istio-tracing-7596597bd7 1/1 Running 0 3m prometheus-76db5fddd5 1/1 Running 0 3m servicegraph-fc55fc579 1/1 Running 0 3m |
最后要做的是让istioctl在命令行中能运行。istioctl是Istio命令行工具,能用于手工插入istio-proxy边车代理容器,以及创建、更新和删除Istio资源。当解压Istio发行版后,会有一个/bin文件夹,其中就有istioctl二进制文件。你可以把它加入到PATH路径中。
1
2
|
export ISTIO_HOME=~/istio-1.0.4 export PATH=$ISTIO_HOME/bin:$PATH |
现在,你能在命令行中运行istioctl命令查看它的版本了。
1
2
3
4
5
6
7
|
istioctl version Version: 1.0.4 GitRevision: a44d4c8bcb427db16ca4a439adfbd8d9361b8ed3 User: root@0ead81bba27d Hub: docker.io/istio GolangVersion: go1.10.4 BuildStatus: Clean |
现在,我们去安装示例服务。
4.示例Java微服务安装
要展现Istio的能力,你需要使用一些互相交互和通信的微服务。接下来我们要用到的微服务是一个简单的客户服务网站。在这些场景中,这个网站会允许客户为网站的特定方面设置优先项(preference),而这些优先项会从一个推荐引擎中获得。微服务之间的通信流如下所示:
Customer ⇒ Preference ⇒ Recommendation
从现在开始,你要有本书用到的源代码。你可以从the Istio Tutorail for Java Microservies 网站(https://github.com/redhat-developer-demos/istio-tutorial)下载源码,转至book-1.0.4分支,如下所示:
1
2
3
|
git clone https: //github.com/redhat-developer-demos/istio-tutorial.git cd istio-tutorial git checkout book-1.0.4 |
4.1源码概览
浏览刚才所下载的源码的istio-tutorial子目录,你会看到customer、preference和recommendation这三个目录。每个目录都包含了相关服务的源代码,这些源码会展示Istio的能力。
Customer和perference服务都是用Java Sprint Boot实现的简单直接REST服务。例如,这是customer服务的端点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Value ( "${preferences.api.url:http://preference:8080}" ) private String remoteURL; @RequestMapping ( "/" ) public ResponseEntity<String> getCustomer( @RequestHeader ( "User-Agent" ) String userAgent, @RequestHeader (value = "user-preference" , required = false ) String userPreference) { try { /* Carry user-agent as baggage */ tracer.activeSpan() .setBaggageItem( "user-agent" , userAgent); if (userPreference != null && !userPreference.isEmpty()) { tracer.activeSpan().setBaggageItem( "user-preference" , userPreference); } ResponseEntity<String> responseEntity = restTemplate.getForEntity(remoteURL, String. class ); String response = responseEntity.getBody(); return ResponseEntity.ok(String.format( RESPONSE_STRING_FORMAT, response.trim())); } catch (HttpStatusCodeException ex) {... } catch (RestClientException ex) {...} } |
异常处理部分我们暂且省略。你可以看到,这个HTTP端点只是简单地调用了由 remoteURL指定的preference服务,并将preference服务的回复加上一个固定字符串RESPONSE_STRING_FORMATI再返回。注意,这里除了Spring RestTemplate之外没用到其它库。我们没有做更多的封装比如断路器、重试、客户端侧的负载均衡等等。我们也没有添加额外的请求跟踪和镜像功能。只有最纯粹的代码!我们就是希望你写出强壮的业务逻辑代码,而不用添加对应用网络的处理代码。
在前面的例子中,我们暂时略过了异常处理部分,但这部分也是非常重要的。大多数编程语言提供了探测和处理异常的机制。当你在代码中调用一个可能会失败的函数时,你需要关注所抛出的异常并进行适当的处理。在customer HTTP端点这个例子中,你试图通过网络调用preference服务。这个调用可能会失败,因此你需要将调用代码封装进异常处理代码块。在异常处理部分,你可以做各种有趣的应对措施,比如从缓存中读取数据或调用另一个函数。有时,开发人员会在获取不到preference服务的返回时返回已缓存好的数据。
也许你已经看到下面这句跟“user-agent”有关的代码了:
1
|
tracer.activeSpan().setBaggageItem( "user-agent" , userAgent); |
Istio允许基于HTTP头部信息做路由决策,只有你将HTTP头部信息传入去调用服务。在这个例子中,我们需要确保“user-agent”在preference和recommendation中是可见的。第三章的第29页“基于HTTP头进行路由”部分会详细介绍流量控制。
在我们的customer示例中,我们已经在pox.xml中添加了特定的 opentracing-spring-cloud-starter和jaeger-tracersolver依赖。这些依赖使得你能在Java代码中添加与OpenTracing API之间的交互逻辑。
在本书用到的例子和用户场景中,我们使用了CNCF联盟的Jaeger Tracing项目。可访问其官网去了解它的更多信息,本书第6章也会介绍Istio的可观察性。
大致浏览代码后,我们可以编译应用并在Kubernetes/OpenShift集群中的容器中运行它们了。
注意:本书主要使用oc而不是kubectl命令行工具。就像OpenShift是Kubernetes的超集一样,oc还是kubectl的超集。在几乎所有场景中,两个命令行的用法完全相同,但是,还有两个主要场景中它们有些不同。第一个有关OpenShift的项目(project)概念,它对应Kubernetes中的命名空间(namespace)。第二个跟安全有关,OpenShift中的oc login和minishift默认都是安全的。
开始部署服务之前,确保你创建了所需的项目,并应用了必要的安全权限:
1
2
|
oc new -project tutorial oc adm policy add-scc-to-user privileged -z default -n tutorial |
这条oc adm 命令向tutorial命名空间中的default服务账号添加了privileged安全上下文限定(SCC)。
4.2编译和部署customer服务
现在,我们来编译和部署customer服务。先登录进Minishift,再使用下面的命令检查下你的登录状态:
1
2
|
oc status oc whoami |
然后确认你的docker工具指向Minishift的Docker守护进程了:
1
2
|
eval $(minishift docker-env) docker images |
你将看到本地Docker守护进程的仓库中的一些Istio和OpenShift镜像。转至cutomer目录,编译代码:
1
2
|
cd customer/java/springboot mvn clean package |
现在代码编译好了。下一步是将应用打包为Docker镜像,这样你就能在Kubernetes/OpenShift中运行它了:
1
|
docker build -t example/customer . |
这会将customer服务打包进一个Docker镜像。你可以运行下面的命令来查看所生成的镜像:
1
|
docker images | grep example |
在cusomter/Kubernetes目录中,有两个Kubernetes资源文件Deployment.yml和Service.yml。现在我们要部署customer微服务,并插入Istio边车代理。这次我们用手工方式插入边车。运行下面的命令来插入边车代理:
1
|
istioctl kube-inject -f ../../kubernetes/Deployment.yml |
检查该命令的输出,并与之前没修改过的Deployment.yml文件做对比。你会发现输出中被添加了以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
- args: - proxy - sidecar - --configPath - /etc/istio/proxy - --binaryPath - /usr/local/bin/envoy - --serviceCluster - customer - --drainDuration - 45s - --parentShutdownDuration - 1m0s - --discoveryAddress - istio-pilot.istio-system: 15007 - --discoveryRefreshDelay - 1s - --zipkinAddress - zipkin.istio-system: 9411 - --connectTimeout - 10s - --proxyAdminPort - "15000" - --controlPlaneAuthPolicy - NONE ... image: docker.io/istio/proxyv2: 1.0 . 4 imagePullPolicy: IfNotPresent name: istio-proxy |
其中你能看到第二个容器被插入了,其名称为istio-proxy。现在可以创建新的部署和Kubernetes服务了:
1
2
|
oc apply -f <(istioctl kube-inject -f ../../kubernetes/Deployment.yml) -n tutorial oc create -f ../../kubernetes/Service.yml -n tutorial |
新的pod被创建出来了:
1
2
3
|
oc get pods NAME READY STATUS RESTARTS AGE customer-6564ff969f-jqkkr 2 / 2 Running 0 2m |
因为插入了一个istio-proxy边车容器,所以你在READY列看到了“2/2”。你还可以用其它命令去查看更多信息,比如:
1
2
3
|
oc get deployment customer -o yaml oc describe pod customer-6564ff969f-jqkkr oc logs customer-6564ff969f-jqkkr -c customer |
注意oc logs命令中的 –c customer 参数,因为pod中存在两个容器,因此你需要指定待查看日志的容器名称。
因为customer服务要在Kubernetes/OpenShift集群外被访问,因此需要添加route对象,使用下面的命令:
1
2
|
oc expose service customer curl customer-tutorial.$(minishift ip).nip.io |
这里我们用的是nip.io服务。在curl命令行的输出中,你将看到以下错误,因为preference和recommendation服务还没有被部署:
customer => I/O error on GET request for "http://preference:8080": preference: Name or service not known
现在你可以部署本例中的其它服务了。
4.3编译和部署Preference服务
跟编译和部署customer服务的方法一样,本节中你会编译、打包和部署preference服务:
1
2
3
|
cd preference/java/springboot mvn clean package docker build -t example/preference:v1 . |
然后为preference服务插入Istio边车代理,并创建服务对象:
1
2
|
oc apply -f <(istioctl kube-inject -f ../../kubernetes/Deployment.yml) -n tutorial oc create -f ../../kubernetes/Service.yml |
现在,再次使用curl工具去访问customer服务:
1
|
curl customer-tutorial.$(minishift ip).nip.io |
你依然会看到错误,但这次有些不同:
customer => 503 preference => I/O error on GET request for "http://recommendation:8080": recommendation: Name or...
这个错误是因为preference服务访问不到recommendation服务。因此,下一步中,我们需要编译和部署recommendation服务。
4.4编译和部署recommendation服务
要让这些服务都运行起来的最后一步是部署recommendation服务。为了尝试不同的做法,我们采用recommendation服务的Vert.x代码。跟前述方法一样,采用以下步骤来编译、打包和部署recommendation服务:
1
2
3
4
5
6
|
cd recommendation/java/vertx mvn clean package docker build -t example/recommendation:v1 . oc apply -f <(istioctl kube-inject -f ../../kubernetes/Deployment.yml) -n tutorial oc create -f ../../kubernetes/Service.yml oc get pods -w |
查看输出中READY列为“2/2”。再次用curl访问customer服务,你会看到更好的结果了:
1
2
|
curl customer-tutorial.$(minishift ip).nip.io customer => preference => recommendation v1 from '66b7c9779c' : 1 |
成功了!三个服务之间的调用链如愿正常工作了。如果你运行curl多次,你会看到最后面的数字依次递增。66b7c9779c是recommendation服务的pod的UID。它由Java代码从HOSTNAME环境变量中获取到。从代码角度看,pod其实就是一个计算机,代码运行在其中。
运行下面的命令去查看pod的名称:
1
2
3
|
oc get pod -l app=recommendation NAME READY STATUS RESTARTS AGE recommendation-v1-66b7c9779c 2 / 2 Running 0 2m |
现在三个服务之间的调用没问题了,接下来,我们将继续讨论Istio的更多核心能力,以及解决服务之间各种问题的强大能力。
书籍英文版下载链接为 https://developers.redhat.com/books/introducing-istio-service-mesh-microservices/,作者 Burr Sutter 和 Christian Posta。
本中文译稿版权由本人所有。水平有限,错误肯定是有的,还请海涵。
感谢您的阅读,欢迎关注我的微信公众号: