第2章
实验说明
为了能更好地演示和学习Istio,我们搭建了一个Istio的实验环境,以便后续章节进行实验。本章主要介绍实验的环境,以及在创建实验环境时需要注意的事项。实验中使用的应用是我用多种编程语言编写的简单样例程序,在本章中也会做详细的介绍,最后简单介绍一下应用的容器化镜像构建方法。
2.1 实验的环境
2.1.1 基础环境
在没有特殊说明的情况下,本书的实验是在Windows 10系统下进行的,在MacOS和Linux系统下也可以进行实验,并无太大差别。
考虑到不是每位读者都有足够的云主机或者物理机,本实验使用3台虚拟机来进行实验。虚拟机管理软件使用开源的Virtualbox,为了方便进行重复的快速实验,我们会使用Vagrant配合Virtualbox来进行虚拟机的管理,包括创建、启动、关闭虚拟机以及虚拟机的快照保存恢复等操作。
本实验对硬件有一些基本要求,否则可能会出现实验无法成功的现象。对CPU并没有太多要求,由于我们实验时会创建3台虚拟机,每台虚拟机会分配至少2G内存,所以这就要求至少有6G的空闲内存,加上系统本身的内存占用,所以推荐在进行实验时,电脑至少有8G内存。一般的机械硬盘就能满足实验条件,当然如果使用SSD硬盘会更好一些。硬盘推荐至少保留30G以上可用空间,虚拟机并不会占用这么多存储空间,但是由于我们会保存多个快照来快速恢复实验环境,这些快照需要占用大量的存储空间,所以综合起来可能需要30G的存储空间。
关于Virtualbox和Vagrant的安装不再详细描述,具体安装细节可以参考官方文档或者通过搜索引擎获取安装文档。本书实验时所使用的Virtualbox和Vagrant软件安装包以及后面会使用的Vagrant box文件可以到如下地址下载:https://pan.baidu.com/s/1Q8s4mnhj2ROnUzW1ZTn44Q。
由于在Kubernetes上部署Istio更加方便,并且能体验到最全功能,所以本书的实验会依赖Kubernetes环境。后面的章节会详细介绍如何通过Kubeadm部署Kubernetes集群。
实验中还会涉及Git的基本使用,所以也需要提前在系统上安装好对应操作系统的Git软件。由于Windows系统下默认的命令行终端CMD并不好用,所以在Windows系统上我会使用Git Bash作为默认的命令行终端。Git Bash是Windows系统上安装好Git软件后就自带的。当然,你也可以根据自己的喜好选择终端软件。
实验时使用Xshell软件来应用SSH远程登录到实验环境的Linux虚拟机,免费家用版本提供在一个窗口中最多可以同时打开4个会话终端,已经能满足我们的实验需求。Xshell只提供了Windows系统版本的软件,安装时一直点击“下一步”按钮就可以完成安装,详细安装步骤请查阅相关文档。MacOS和Linux系统的用户可以直接使用系统自带的终端来应用SSH远程登录到实验环境的Linux虚拟机。
默认情况下使用Virtualbox启动虚拟机时,虚拟机目录会存放在用户的主目录,如果Windows下C盘剩余空间过小,可能会由于硬盘空间不够,导致创建虚拟机失败。如果有需要,可以在命令行终端设置虚拟机的存放目录,先创建D:virtualbox目录。如果提示找不到VBoxManage命令,可能需要把Virtualbox的安装目录添加到系统的PATH环境变量中。使用如下命令设置:
$ VBoxManage setproperty machinefolder D:virtualbox
$ VBoxManage list systemproperties | grep machine
Default machine folder: D:virtualbox
2.1.2 命令说明
由于实验使用Vagrant和Virtualbox来管理虚拟机集群环境。使用Vagrant创建的虚拟机一般情况下登录用户名为vagrant,当部署kubernetes集群或者其他安装软件的操作时,由于需要root权限,我们有时会直接切换到root用户来执行接下来的操作。当以vagrant用户登录虚拟机环境的Linux主机后,使用sudo su - root命令可临时切换到root用户来执行接下来的操作,这也可以从执行命令的提示符中看出,当以vagrant用户操作时,命令会以$符号来开头;而当以root用户操作时,命令会以#符号来开头。如下所示:
本书所有实验中的命令执行操作如果没有特殊说明,普通用户的操作均是以vagrant用户执行。
本书实验内容的大部分代码和使用的Kubernetes以及Istio的yaml文件,都在https://github.com/mgxian/istio-lab 仓库中。可使用如下方式获取源码:
$ git clone https://github.com/mgxian/istio-lab
如果没有安装Git,需要提前安装Git,可使用如下命令安装:
$ sudo yum install -y git
本书后面的大部分实验在执行kubectl和istioctl命令时,使用到的命令都是以仓库的根目录作为当前工作目录的,也就是说,当执行这两个命令时会先进入istio-lab目录,然后再执行相应的命令。
第3章之后(不包含第3章)的大部分实验操作,在没有特殊说明的情况下,都是在使用Xshell登录到实验环境虚拟机中后进行的操作,安装Git和下载实验中使用的源代码只需要在一台虚拟机上进行即可,一般会选择第一台虚拟机。只有管理虚拟机相关的操作命令比如:启动、停止、暂停虚拟机,才需要在Windows系统宿主机上进行操作。
2.1.3 问题及解决方案
由于实验虚拟机集群资源与性能的问题,可能会出现实验结果和书中展示的结果有所不同的情况,本小节主要介绍实验中可能会遇到的问题及解决方法。
1. 路由不生效
在进行服务路由等功能验证时,很有可能会出现路由不生效的问题。当创建路由后,由于机器性能问题,导致服务路由信息传播速度慢,如果立即访问测试,就很有可能会出现与书中展示的结果不同的现象,此时可以稍等片刻,让服务路由信息传播完成,再进行访问测试。或者删除路由规则,再重新创建。当然,你也可以重启Pilot组件,这通常是最快的解决方法。另外,也可以通过如下介绍的方式深入地排查问题。
创建路由后,可以通过如下命令查看路由的分发情况:
当路由中的状态都为SYNCED,不存在Stale状态时,表明路由已经分发完成。此时再进行访问测试,一般情况下不会再出现问题。istio-egressgateway和istio-ingressgateway会有部分状态为NOT SENT,这是正常的,因为如果没有创建过Gateway,就不会发送RDS给istio-egressgateway和istio-ingressgateway,此时的状态就为NOT SENT。
此外,还可以通过查看Pod日志观察Envoy有无接收到最新的路由规则,可以通过如下方式查看日志:
$ INGRESS_GATEWAY_POD=$(kubectl get pod -n istio-system | grep istio-ingressgateway | awk '{print $1}')
$ kubectl logs -f $INGRESS_GATEWAY_POD -n istio-system
有时候也可能会出现使用istioctl proxy-status不能获取全部Pod的路由同步状态的情况,或者使用istioctl proxy-status获取到的状态都是正常状态,但路由仍然没有生效,此时可能是由于Pilot处于异常状态。可以使用如下的方式重启Pilot实例:
当然,你也可以参考本书第12章中关于路由不生效的排错步骤来进行问题排查。
2. 应用路由规则时出现超时错误
在实验中创建路由规则时,无法成功创建或更新,出现如下的超时错误信息:
这一般是由于Istio中的Galley组件出现了问题,使用如下命令重启Galley组件即可解决:
3. 自动注入失败
创建Pod时,提示如下的错误信息:
创建或扩容Deployment时,没有创建出对应数量的Pod,查看Deployment对应的ReplicaSet信息,可以看到如下所示的错误信息:
这一般是由于Istio中的Sidecar-injector组件出现了问题,使用如下命令重启Sidecar-injector组件即可解决:
有时也可能碰到其他异常问题,比如:拉取镜像失败,可能是由于Virtualbox的nat网络出了问题,这些问题一般都无法快速解决,甚至没有办法解决,不用浪费太多时间在这些异常问题上。可以尝试使用虚拟机的快照功能,直接恢复虚拟机环境到创建好Istio集群的初始状态,再重新进行实验。
2.2 实验的应用
2.2.1 应用架构说明
为了充分展示Istio的功能,我们使用不同的语言来模拟数个微服务,服务之间存在相应的调用关系,服务之间通过HTTP协议通信。我们并没有写一个实际的综合应用,例如:购物网站、论坛等,来模拟生产环境的情况,我们只是简单地模拟服务间的调用关系来进行Istio相关的功能实验,目的是通过演示Istio相关功能来学习Istio。每个服务以其使用的编程语言为服务名,例如:使用Python语言编写的服务命名为service-python。各服务的调用关系如图2-1所示。
service-js服务是一个由Vue/React实现的前端应用,当用户访问前端Web页面时,用户会看到一个静态页面。当用户点击相应的按钮时,前端页面会通过浏览器异步请求后端service-python服务提供的API接口,service-python调用后端service-lua服务和service-node服务,而service-node服务又会调用service-go服务,最终,所有服务配合来完成用户的请求,并把结果合并处理之后发送给前端浏览器。当前端页面收到请求的响应数据时会渲染出新的页面呈现给用户。
应用架构说明:
- 本应用采用当前比较流行的前后端分离架构。
- 前端项目使用Vue/React实现。
- 前端调用Python实现的API接口。
- Python服务调用后端Node实现的服务和Lua实现的服务。
- Node服务调用Go实现的服务。
2.2.2 应用详细说明
1. service-js服务
service-js服务分别使用Vue和React各实现一套Web界面,主要用于服务路由中的A/B测试,可以让不同的终端用户看到不同的前端Web界面。service-js服务主要负责根据service-python服务的响应数据,使用ECharts图表库在浏览器上展示出后端服务的具体调用关系和各个服务的调用耗时,具体的代码在实验源码根目录的service/js目录下。
v1版本使用React框架实现,源码目录如下:
.
├── Dockerfile
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── README.md
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js
v2版本使用Vue框架实现,源码目录如下:
.
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── Dockerfile
├── index.html
├── package.json
├── package-lock.json
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── main.js
└── static
用于容器化的Dockerfile文件如下所示:
2. service-python服务
service-python服务是一个用Python编写的API服务,负责接收前端的API请求,调用整合后端其他服务的响应数据,返回给前端使用。service-python服务分别使用Python2和Python3实现了两个版本的服务,具体代码在实验源码根目录的service/python目录下。service-python服务使用Flask框架实现,具体源码如下:
第11~28行定义的getForwardHeaders函数是为了从请求中提取出用于Istio调用链追踪的头信息,用于传递给service-python要调用的其他后端服务。
第31~33行表示每个请求在被处理前,调用getForwardHeaders函数,从请求中提取出Istio调用链追踪的头信息,并保存到全局对象g的forwardHeaders变量中。
第36~46行定义了用于获取后端服务响应数据的get_url_response函数。
第49~63行定义了真正的业务路由,使用线程池的方式并发请求后端服务,并把后端服务的响应数据组合处理后返回给调用方。
第66~68行定义了用于服务健康检查的路由。
第71~72行表示服务启动在0.0.0.0地址的80端口。
v1版本和v2版本源码只有第61行有略微差别,在v2版本中"python v1"修改为"python v2"。
用于容器化的Dockerfile文件如下所示:
v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
3. service-lua服务
service-lua服务使用OpenResty的不同版本用Lua语言分别实现了两个版本的服务。具体的代码在实验源码根目录的service/lua目录下。源代码如下:
第8行表示服务启动在0.0.0.0地址的80端口。
第9~14行定义了访问服务的/链接时返回"hello world"。
第16~21行定义了用于服务健康检查的/status链接。
第23~37行定义了真正的业务逻辑,当访问/env链接时,响应服务的版本信息。
v1版本和v2版本源码只有第30行有略微差别,在v2版本中"lua v1"修改为"lua v2"。
用于容器化的Dockerfile文件如下所示:
v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
4. service-node服务
service-node服务使用Node的不同版本分别实现了两个版本的服务。具体的代码在实验源码根目录的service/node目录下。源代码如下:
第8~28行定义的getForwardHeaders函数是为了从请求中提取出用于Istio调用链追踪的头信息,用于传递给service-node要调用的其他后端服务。
第32~34行定义了用于服务简单健康检查的路由。
第36~63行定义了真正的业务路由,请求后端服务,并把后端服务的响应数据组合处理后返回给调用方。
第65~66行表示服务启动在0.0.0.0地址的80端口。
v1版本和v2版本源码只有第54和59行有略微差别,在v2版本中"node v1"修改"node v2"。
用于容器化的Dockerfile文件如下:
v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
5. service-go服务
service-go服务使用Go语言的不同版本分别实现了两个版本的服务,具体的代码在实验源码根目录的service/go目录下。源代码如下:
第10~14行定义了真正的业务路由,以及返回服务的版本信息。
第16~18行定义了用于服务健康检查的路由。
第20行表示服务启动在0.0.0.0地址的80端口。
v1版本和v2版本源码只有第12行有略微差别,在v2版本中"go v1"修改为"go v2"。
用于容器化的Dockerfile文件如下:
你可能已经注意到,上面的Dockerfile代码使用了两次FROM关键词和一个不太一样的COPY用法,这体现了Docker镜像的多阶段构建功能,可以在一个镜像中编译代码,然后复制编译后的产物到另一个镜像中,这样可以非常有效地减小应用的Docker镜像大小,特别是Go、Java这类编译型静态语言,因为这类语言编译之后就不再需要原来编译时的依赖库,可以把编译后的产物直接放在一个极小运行环境中启动运行。由于Go语言可以编译为在操作系统上直接运行的二进制文件,所以可以把编译后的文件直接复制到alpine这类极简的操作系统镜像中,这种优化使得service-go服务编译构建后的镜像体积可缩小到10M级别,进而使服务镜像的分发效率大幅度提升。
v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
6. service-redis服务
service-redis服务是使用Go语言实现的服务,用于从Redis服务器获取信息,具体的代码在实验源码根目录的service/redis目录下。源代码如下:
第23~29行定义了真正的业务路由,以及返回Redis服务器的信息。
第30~32行定义了用于服务健康检查的路由。
第33行表示服务启动在0.0.0.0地址的80端口。
用于容器化的Dockerfile文件如下:
7. httpbin服务
httpbin服务是一个用于HTTP测试的开源服务。它既提供了在线的测试服务,也可以通过源码或者使用Docker镜像在本地部署运行,这两种使用方式在后续章节的实验中都有涉及。
2.3 应用的构建
如果只是跟着本书做实验,本节可以跳过,本节属于应用镜像构建部分,不会影响后面的Istio实验。了解本节内容需要掌握Docker的基础知识。
由于本书的实验重点在于如何使用Istio,如何使用本地Docker私有镜像仓库部署服务并不是我们关注的重点,所以本书实验所使用的镜像均采用阿里云镜像服务免费提供的镜像构建功能。当然,你也可以使用Docker Hub提供的镜像构建功能。下面以阿里云镜像服务为例实现镜像构建,具体步骤如下。
1.构建应用镜像
(1)上传代码到GitHub
此步骤不做详细说明,请参考相关文档了解Git和GitHub的基本使用。
(2)在阿里云上的镜像构建
阿里云镜像服务地址:https://cr.console.aliyun.com/ ,如下操作均在这个链接的Web上进行。
1)创建命名空间。
命名空间不能重复,请注意修改命名空间名称,如图2-2所示,可创建名为istio-lab的命名空间。
2)创建镜像仓库。
选择命名空间为上个步骤中创建的命名空间,填写要创建的镜像仓库名和简介,如service-go。注意选择仓库类型为公开,如图2-3所示。
选择已经绑定的GitHub账号和要构建的源码仓库,由于构建时需要访问国外资源,因此勾选使用海外机器构建,并取消“选择代码变更时自动构建镜像”(如果首次使用需要先绑定GitHub账号),如图2-4所示。
3)添加镜像构建规则。
选择要构建规则的镜像仓库,并点击“管理”按钮,出现如图2-5所示的界面。
选择“构建”选项卡上的“添加规则”按钮,添加镜像构建规则,如图2-6所示。
添加如图2-7所示的版本构建规则,实验中会使用两个版本,因此需要创建两个版本的构建规则。
4)构建镜像。
点击“立即构建”按钮开始构建,如果构建失败,可以通过点击日志链接查看构建日志,找出失败原因,如图2-8所示。
2. 本地拉取镜像验证
镜像构建完成后,拉取到本地测试镜像是否能正常工作。具体步骤如下。
1)拉取镜像:
2)启动容器:
3)访问测试:
4)清理:
2.4 本章小结
要掌握Istio,我们需要一步一步地实验Istio的功能特性,所以我们先创建一个实验环境,而实验时最有可能遇到的问题就是实验环境的机器性能不足,进而出现Istio路由功能生效缓慢,甚至导致后续实验失败的问题,这也是我们需要特别关注的。我也在本章中说明了这种情况,并介绍了几种解决方法。为了更好地模拟生产环境中多服务调用的场景,我使用多种编程语言实现了多个服务的不同版本,这也能帮助我们在后续章节更好地展示Istio的相关功能特性。