Istio 是现在最热门的 Service Mesh 工具,istio 是由 Google、IBM、Lyft 等共同开源的 Service Mesh(服务网格)框架,于2017年初开始进入大众视野。Kubernetes 解决了云原生应用的部署问题,istio 解决的是应用的服务(流量)治理问题。

安装

安装环境说明

系统 版本 说明
操作系统 win10 20H2 M1不兼容Istio,Centos7虚拟机抛各种问题,内存,cm配置等。
Docker Docker Desktop 4.4.2
Kubenetes v1.22.5 直接在Docker Desktop上安装,具体参考
Istio 1.11.6 官方文档, GitHub Release 页面

Istio安装

前置环境准备就绪后,直接到GitHub Release页面下载对应的操作系统版本,本次下载的是1.11.6。

下载完成后解压到指定的路径下,此次路径:D:\Program\istio。

安装目录包含:

  • samples/目录,包含示例应用程序
  • bin/目录,istioctl客户端二进制文件

为了能直接使用istioctl,将D:\Program\istio\bin添加到系统环境变量。

通过istioctl version查看安装的版本信息:

$ istioctl version
client version: 1.11.6
control plane version: 1.11.6
data plane version: 1.11.6 (2 proxies)

本次安装,采用demo配置组合,选择它是因为它包含了一组专为测试准备的功能集合。

$ istioctl install --set profile=demo -y
Istio core installed
Istiod installed
Egress gateways installed
Ingress gateways installed
Installation complete

另外还有用于生产或性能测试的配置组合。

  1. default:根据 IstioOperator API 的默认设置启动组件。 建议用于生产部署和 Multicluster Mesh 中的 Primary Cluster。

    您可以运行 istioctl profile dump 命令来查看默认设置。

  2. demo:这一配置具有适度的资源需求,旨在展示 Istio 的功能。 它适合运行 Bookinfo 应用程序和相关任务。

    此配置文件启用了高级别的追踪和访问日志,因此不适合进行性能测试。

  3. minimal:与默认配置文件相同,但只安装了控制平面组件。 它允许您使用 Separate Profile 配置控制平面和数据平面组件(例如 Gateway)。

  4. remote:配置 Multicluster Mesh 的 Remote Cluster。

  5. empty:不部署任何东西。可以作为自定义配置的基本配置文件。

  6. preview:预览文件包含的功能都是实验性。这是为了探索 Istio 的新功能。不确保稳定性、安全性和性能(使用风险需自负)。

安装完成后我们可以查看 istio-system 命名空间下面的 Pod 运行状态:

$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istio-egressgateway-cbbc444d-7k6rg 1/1 Running 0 73m
istio-ingressgateway-766fdf996-jwmq6 1/1 Running 0 73m
istiod-5c8784b855-c8sx4 1/1 Running 0 74m

如果都是 Running 状态证明 istio 就已经安装成功了。

示例安装

接下来可以安装官方提供的一个非常经典的 Bookinfo 应用示例,这个示例部署了一个用于演示多种 Istio 特性的应用,该应用由四个单独的微服务构成。 这个应用模仿在线书店的一个分类,显示一本书的信息。页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。

Bookinfo 应用分为四个单独的微服务:

  • productpage:这个微服务会调用 details 和 reviews 两个微服务,用来生成页面。
  • details:这个微服务中包含了书籍的信息。
  • reviews:这个微服务中包含了书籍相关的评论,它还会调用 ratings 微服务。
  • ratings:这个微服务中包含了由书籍评价组成的评级信息。

reviews 微服务有 3 个版本:

  • v1 版本不会调用 ratings 服务。
  • v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。
  • v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。

上图展示了使用 istio 后,整个应用实际的结构。所有的微服务都和一个 Envoy sidecar 封装到一起,sidecar 拦截所有到达和离开服务的请求。

Istio默认自动注入Sidecar,需要为 default 命名空间打上标签 istio-injection=enabled(可选):

$ kubectl label namespace default istio-injection=enabled

进入解压的istio目录,执行如下命令:

$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created

如果上面没有为default命名空间打上 istio-injection=enabled标签,则需要使用istioctl kube-inject,手动注入Sidecar:

$ kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)

注意:

istioctl kube-inject 命令用于在创建 Deployments 之前注入 Istio 的 Sidecar 容器,同样我们也可以直接将当前的命名空间打上一个 istio-injection=enabled 的 Label 标签,这样该命名空间就开启了 Sidecar 容器自动注入功能。

这里我们部署的 bookinfo.yaml 资源清单文件就是普通的 Kubernetes 的 Deployment 和 Service 的 yaml 文件,而 istioctl kube-inject 会在这个文件的基础上向其中的 Deployment 追加一个镜像为 istio/proxyv2:1.11.6 的 sidecar 容器。

过一会儿就可以看到如下 service 和 pod 启动:

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.103.231.222 <none> 9080/TCP 17s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18m
productpage ClusterIP 10.96.94.163 <none> 9080/TCP 16s
ratings ClusterIP 10.107.212.118 <none> 9080/TCP 17s
reviews ClusterIP 10.104.114.185 <none> 9080/TCP 17s $ kubectl get pod
NAME READY STATUS RESTARTS AGE
details-v1-79f774bdb9-jrb7t 1/1 Running 0 12m
productpage-v1-6b746f74dc-lbqx9 1/1 Running 0 12m
ratings-v1-b6994bb9-bfvqd 1/1 Running 0 12m
reviews-v1-545db77b95-4rs5z 1/1 Running 0 12m
reviews-v2-7bf8c9648f-bs6r6 1/1 Running 0 12m
reviews-v3-84779c7bbc-rkq4k 1/1 Running 0 12m

现在应用的服务都部署成功并启动了,要确认 Bookinfo 应用是否正在运行,请在某个 Pod 中用 curl 命令对应用发送请求,例如 ratings

$ kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>

如果我们需要在集群外部访问,就需要添加一个 istio gateway。gateway 相当于 k8s 的 ingress controller 和 ingress。它为 HTTP/TCP 流量配置负载均衡,通常在服务网格边缘作为应用的 ingress 流量管理。

创建一个gateway:

$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created

验证gateway是否启动成功:

$ kubectl get gateway
NAME AGE
bookinfo-gateway 9s

要想取访问这个应用,这里我们需要更改下 istio 提供的 istio-ingressgateway 这个 Service 对象,默认是 LoadBalancer 类型的服务:

$ kubectl get svc -n istio-system
... $ kubectl edit svc istio-ingressgateway -n istio-system
......
type: NodePort # 修改成 NodePort 类型
status:
loadBalancer: {}

这样我们就可以通过 http://<NodeIP>:<nodePort>/productpage 访问应用了:

刷新页面可以看到 Book Reviews 发生了改变,因为每次请求会被路由到到了不同的 Reviews 服务版本去:

至此,整个 istio 就安装并验证成功了。

流量管理

上面搭建BookInfo 应用我们只用到了下面两个资源文件

samples/bookinfo/platform/kube/bookinfo.yaml
samples/bookinfo/networking/bookinfo-gateway.yaml

前者就是通常的 Kubernetes 定义的 Deployment 和 Service 的资源清单文件,后者定义了这个应用的外部访问入口gateway,以及应用内部 productpage 服务的 VirtualService 规则,而其他内部服务的访问规则还没有被定义。

现在访问应用界面并刷新,会看到 Reviews 有时不会显示评分,有时候会显示不同样式的评分,这是因为后面有3个不同的 Reviews 服务版本,而没有配置该服务的路由规则 route rule 的情况下,该服务的几个实例会被随机访问到,有的版本服务会进一步调用 Ratings 服务,有的不会。

到这里我们已经接触到了 Istio 中两个非常重要的流量管理的资源对象了:

  • VirtualService 用来在 Istio 中定义路由规则,控制流量路由到服务上的各种行为。
  • Gateway 为 HTTP/TCP 流量配置负载均衡器的。

配置请求路由

不同服务版本访问规则

对 Reviews 服务添加一条路由规则,启用 samples/bookinfo/networking/virtual-service-reviews-v3.yaml 定义的 VirtualService 规则

$ kubectl apply -f  samples/bookinfo/networking/virtual-service-reviews-v3.yaml

这样,所有访问 reviews 服务的流量就会被引导到 reviews 服务对应的 subset 为 v3 的 Pod 中。

samples/bookinfo/networking/virtual-service-reviews-v3.yaml内容如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3

然后查看所有的路由规则:

$ kubectl get virtualservices
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 10m
reviews ["reviews"] 5m39s

我们可以看到 reviews 的 VirtualService 已经创建成功了,此时我们去刷新应用的页面,发现访问 Reviews 失败了:

这是因为我们还没有创建 DestinationRule 对象,DestinationRule 对象是 VirtualService 路由生效后,配置应用与请求的策略集,用来将 VirtualService 中指定的 subset 与对应的 Pod 关联起来。

samples/bookinfo/networking/destination-rule-all.yaml 文件中有定义所有该应用中要用到的所有 DestinationRule 资源对象,其中有一段就是对 Reviews 相关的 DestinationRule 的定义:

---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3

可以看到 DestinationRule 中定义了 subsets 集合,其中 labels 就和我们之前 Service 的 labelselector 一样是去匹配 Pod 的 labels 标签的,比如我们这里 subsets 中就包含一个名为 v3 的 subset,而这个 subset 匹配的就是具有 version=v3 这个 label 标签的 Pod 集合。

再回到之前的 samples/bookinfo/platform/kube/bookinfo.yaml 文件中,我们可以发现 reviews 的 Deployment 确实有声明不同的 labels->version:

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v3
labels:
app: reviews
version: v3
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v3 # 声明version=v3
template:
metadata:
labels:
app: reviews
version: v3
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}

这样就通过 DestinationRule 将 VirtualService 与 Service 不同的版本关联起来了。现在直接创建 DestinationRule 资源:

$ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
destinationrule.networking.istio.io/productpage created
destinationrule.networking.istio.io/reviews created
destinationrule.networking.istio.io/ratings created
destinationrule.networking.istio.io/details created

创建完成后,我们就可以查看目前我们网格中的 DestinationRules:

$ kubectl get destinationrule
NAME HOST AGE
details details 37s
productpage productpage 37s
ratings ratings 37s
reviews reviews 37s

此时再访问应用就成功了,多次刷新页面发现 Reviews 都展示的是 v3 版本带红色星的 Ratings,说明我们VirtualService 的配置成功了。

基于用户身份的路由

同样,将上面创建的 VirtualService 对象删除:

$ kubectl delete virtualservice reviews
virtualservice.networking.istio.io "reviews" deleted $ kubectl get virtualservice
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 41m

查看文件 samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml 的定义:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v3

这个 VirtualService 对象定义了对 reviews 服务访问的 match 规则。意思是如果当前请求的 header 中包含 jason 这个用户信息,则只会访问到 v2 的 reviews 这个服务版本,即都带黑星的样式,如果不包含该用户信息,则都直接将流量转发给 v3 这个 reviews 的服务。

我们先不启用这个 VirtualService,先去访问下 Bookinfo 这个应用,右上角有登录按钮,在没有登录的情况下刷新页面,reviews 服务是被随机访问的,可以看到有带星不带星的样式,点击登录,在弹窗中 User Name 输入 jason,Password 为空,登录。

登录成功后刷新页面,可以看到跟未登录前的访问规则一样,也是随机的。

现在创建上面的 VirtualService 这个对象:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml
virtualservice.networking.istio.io/reviews created $ kubectl get virtualservice
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 47m
reviews ["reviews"] 24s

此时再回去刷新页面,发现一直都是黑星的 Reviews 版本(v2)被访问到了。注销退出后再访问,此时又一直是红星的版本(v3)被访问了。

说明我们基于 headers->end-user->exact:jason 的控制规则生效了。在 productpage 服务调用 reviews 服务时,登录的情况下会在 header 中带上用户信息,通过 exact 规则匹配到相关信息后,流量被引向了上面配置的 v2 版本中。

这里要说明一下 match 的匹配规则:一个 match 块里的条件是需要同时满足才算匹配成功的,如下面是 url 前缀和端口都必须都满足才算成功:

- match:
- uri:
prefix: "/wpcatalog"
port: 443

多个 match 块之间是只要有一个 match 匹配成功了,就会被路由到它指定的服务版本去,而忽略其他的。

我们的示例中在登录的条件下,满足第一个 match,所以服务一直会访问到 v2 版本。退出登录后,没有 match 规则满足匹配,所以就走最后一个 route 规则,即 v3 版本。

故障注入

注入HTTP延迟故障

为了测试微服务应用程序 Bookinfo 的弹性,我们将为用户 jasonreviews:v2ratings 服务之间注入一个 7 秒的延迟。 这个测试将会发现一个故意引入 Bookinfo 应用程序中的 bug。

我们查看 istio 样例文件夹下面的文件 samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml 内容:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
percentage:
value: 100.0
fixedDelay: 7s
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1

这个 VirtualService 定义了一个在 jason 登录的情况下,访问 ratings 服务的 100% 的 7s 访问延迟。前面我们知道,Bookinfo 这个示例 productpage 服务调用 reviews,reviews 的不同版本会对 ratings 进行不同的调用,其中 reviews-v1 不调用 ratings,reviews-v2 和 reviews-v3 会调用 ratings,并做不同样式的渲染。并且在 productpage 访问 reviews 时,代码中有硬编码 6s 中的访问超时限制,而 reviews 访问 ratings 编码了 10s 的访问超时限制。

了解这一点后,我们现在来创建这个 VirtualService 资源对象:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml
virtualservice.networking.istio.io/ratings created $ kubectl get virtualservice
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 62m
ratings ["ratings"] 32s
reviews ["reviews"] 15m

创建完成后,前往 Bookinfo 应用,登录 jason,打开浏览器的 Network,刷新页面,发现请求加载很慢,大约 6s 后,出现如下界面:

期望 Bookinfo 主页在大约 7 秒钟加载完成并且没有错误。 但是出现了一个问题:Reviews 部分显示了错误消息:

Sorry, product reviews are currently unavailable for this book.

按照预期,我们引入的 7 秒延迟不会影响到 reviews 服务,因为 reviewsratings 服务间的超时被硬编码为 10 秒。 但是,在 productpagereviews 服务之间也有一个 3 秒的硬编码的超时,再加 1 次重试,一共 6 秒。 结果,productpagereviews 的调用在 6 秒后提前超时并抛出错误了。

这种类型的错误可能发生在典型的由不同的团队独立开发不同的微服务的企业应用程序中。 Istio 的故障注入规则可以帮助您识别此类异常,而不会影响最终用户。

注入 HTTP abort 故障

测试微服务弹性的另一种方法是引入 HTTP abort 故障。 这个任务将给 ratings 微服务为测试用户 jason 引入一个 HTTP abort。

在这种情况下,我们希望页面能够立即加载,同时显示 Ratings service is currently unavailable 这样的消息。

首先查看samples/bookinfo/networking/virtual-service-ratings-test-abort.yam的内容:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
abort:
percentage:
value: 100.0
httpStatus: 500
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1

通过上面的这个 yaml 文件我们可以看出这个 VirtualService 资源对象配置了在 jason 登录时,reviews 对 ratings 访问时 100% 的返回一个500错误响应。

创建这个资源对象:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml
virtualservice.networking.istio.io/ratings configured

现在我们回到 BookInfo 应用,登录 jason,刷新页面,可以很快就看到 Rating 服务不可用的提示信息。

流量转移

基于权重的服务访问规则

首先移除刚刚创建的 VirtualService 对象,排除对环境的影响:

$ kubectl delete virtualservice reviews
virtualservice.networking.istio.io "reviews" deleted $ kubectl get virtualservice
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 32m

现在我们再去访问 Bookinfo 应用又回到最初随机访问 Reviews 的情况了。

现在我们查看文件 samples/bookinfo/networking/virtual-service-reviews-90-10.yaml 的定义:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10

这个规则定义了 90% 的对 Reviews 的流量会落入 v1 这个 subset,就是没有 Ratings 的这个服务,10% 会落入 v2 带黑色 Ratings 的这个服务,然后我们创建这个资源对象:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-90-10.yaml
virtualservice.networking.istio.io/reviews created $ kubectl get virtualservice
NAME GATEWAYS HOSTS AGE
bookinfo ["bookinfo-gateway"] ["*"] 39m
reviews ["reviews"] 36s

我们查看当前网格中的 VirtualService 对象,可以看到已经有 reviews 了,证明已经创建成功了,由于上面我们已经将应用中所有的 DestinationRules 都已经创建过了,所以现在我们直接访问应用就可以了,我们多次刷新,可以发现没有出现 Ratings 的次数与出现黑色星 Ratings 的比例大概在9:1左右,并且没有红色星的 Ratings 的情况出现,说明我们配置的基于权重的 VirtualService 访问规则配置生效了。

TCP流量转移

本任务展示如何将 TCP 流量从微服务的一个版本逐步迁移到另一个版本。例如,将 TCP 流量从旧版本迁移到新版本。

在 Istio 中,可以通过配置一系列规则来实现此目标,这些规则将一定比例的 TCP 流量路由到不同的服务。在此任务中,将会把 100% 的 TCP 流量分配到 tcp-echo:v1,接着,再通过配置 Istio 路由权重把 20% 的 TCP 流量分配到 tcp-echo:v2

同样查看 samples/tcp-echo/tcp-echo-services.yaml内容:

apiVersion: v1
kind: Service
metadata:
name: tcp-echo
labels:
app: tcp-echo
service: tcp-echo
spec:
ports:
- name: tcp
port: 9000
- name: tcp-other
port: 9001
# Port 9002 is omitted intentionally for testing the pass through filter chain.
selector:
app: tcp-echo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tcp-echo-v1
labels:
app: tcp-echo
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: tcp-echo
version: v1
template:
metadata:
labels:
app: tcp-echo
version: v1
spec:
containers:
- name: tcp-echo
image: docker.io/istio/tcp-echo-server:1.2
imagePullPolicy: IfNotPresent
args: [ "9000,9001,9002", "one" ]
ports:
- containerPort: 9000
- containerPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tcp-echo-v2
labels:
app: tcp-echo
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: tcp-echo
version: v2
template:
metadata:
labels:
app: tcp-echo
version: v2
spec:
containers:
- name: tcp-echo
image: docker.io/istio/tcp-echo-server:1.2
imagePullPolicy: IfNotPresent
args: [ "9000,9001,9002", "two" ]
ports:
- containerPort: 9000
- containerPort: 9001

上面配置文件部署了两个版本的 tcp-echo 服务,要注意的是 Service 对象中的 Label Selector 是 app=tcp-echo,这样该对象就会匹配到上面的两个版本的对象。使用如下命令安装:

$ kubectl apply -f samples/tcp-echo/tcp-echo-services.yaml
service/tcp-echo created
deployment.apps/tcp-echo-v1 created
deployment.apps/tcp-echo-v2 created $ kubectl get pod -l app=tcp-echo
NAME READY STATUS RESTARTS AGE
tcp-echo-v1-7dd5c5dcfb-cgwt5 2/2 Running 0 62s
tcp-echo-v2-56cd9b5c4f-4dswn 2/2 Running 0 62s

接下来将微服务 tcp-echo 的 TCP 流量全部路由到 v1 版本上,直接使用 samples/tcp-echo/tcp-echo-all-v1.yaml 这个示例文件中的对象,如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: tcp-echo-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 31400
name: tcp
protocol: TCP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: tcp-echo-destination
spec:
host: tcp-echo
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tcp-echo
spec:
hosts:
- "*"
gateways:
- tcp-echo-gateway
tcp:
- match:
- port: 31400
route:
- destination:
host: tcp-echo
port:
number: 9000
subset: v1

这里创建了一个Gateway对象,用来路由TCP的请求,要注意,这里匹配的是istio=ingressgateway这个Service对象的31400这个TCP端口:

$ kubectl get svc -n istio-system -l istio=ingressgateway -o yaml
......
- name: tcp
nodePort: 31290
port: 31400
protocol: TCP
targetPort: 31400
......

接着在 DestinationRule 对象中声明了 v1v2 两个子集服务,在 VirtualService 对象中声明具体的路由规则,我们可以看到是直接全都路由到了 v1 这个子集中去。

直接创建上面的资源对象:

$ kubectl apply -f samples/tcp-echo/tcp-echo-all-v1.yaml
gateway.networking.istio.io/tcp-echo-gateway created
destinationrule.networking.istio.io/tcp-echo-destination created
virtualservice.networking.istio.io/tcp-echo created

创建完成后我们就可以通过 istio-ingressgateway 去访问上面的 TCP 服务了,要在外部访问该服务,同样我们可以通过 istio-ingressgateway 的 NodePort 来访问,这里的 TCP 对应的 nodePort 端口是 31290,这里可以使用如下所示的命令来测试:

for i in {1..10}; do  docker run -e INGRESS_HOST=$INGRESS_HOST \
-e INGRESS_PORT=$INGRESS_PORT -i --rm busybox sh -c "(date; sleep 1) | nc \
$INGRESS_HOST $INGRESS_PORT"; done one Tue Feb 8 08:45:44 UTC 2022
one Tue Feb 8 08:45:47 UTC 2022
one Tue Feb 8 08:45:50 UTC 2022
one Tue Feb 8 08:45:53 UTC 2022
one Tue Feb 8 08:45:56 UTC 2022
one Tue Feb 8 08:45:59 UTC 2022
one Tue Feb 8 08:46:02 UTC 2022
one Tue Feb 8 08:46:06 UTC 2022

从上面的日志可以看出,所有时间戳的前缀都是 one,这意味着所有流量都被路由到了 tcp-echo 服务的 v1 版本中。

然后使用新的路由规则将 20% 的流量从 tcp-echo:v1 转移到 tcp-echo:v2,对应的示例文件为 samples/tcp-echo/tcp-echo-20-v2.yaml,内容如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tcp-echo
spec:
hosts:
- "*"
gateways:
- tcp-echo-gateway
tcp:
- match:
- port: 31400
route:
- destination:
host: tcp-echo
port:
number: 9000
subset: v1
weight: 80
- destination:
host: tcp-echo
port:
number: 9000
subset: v2
weight: 20

上面的 VirtualService 对象也很简单,给 v1 子集服务加了 80% 的权重,另外有 20% 的权重被路由到了 v2 这个子集服务中去了,同样直接创建上面的资源对象更新路由规则:

$ kubectl apply -f samples/tcp-echo/tcp-echo-20-v2.yaml
virtualservice.networking.istio.io/tcp-echo configured

等待几秒钟,让新的路由规则生效。然后同样用上面的测试命令向 tcp-echo 服务发送 TCP 请求:

$ for i in {1..10}; do  docker run -e INGRESS_HOST=$INGRESS_HOST \
-e INGRESS_PORT=$INGRESS_PORT -i --rm busybox sh -c "(date; sleep 1) | nc \
$INGRESS_HOST $INGRESS_PORT"; done one Tue Feb 8 08:50:41 UTC 2022
one Tue Feb 8 08:50:45 UTC 2022
one Tue Feb 8 08:50:48 UTC 2022
one Tue Feb 8 08:50:51 UTC 2022
one Tue Feb 8 08:50:54 UTC 2022
one Tue Feb 8 08:50:57 UTC 2022
two Tue Feb 8 08:51:00 UTC 2022
one Tue Feb 8 08:51:03 UTC 2022
one Tue Feb 8 08:51:07 UTC 2022
one Tue Feb 8 08:51:10 UTC 2022

现在我们可以发现,有大约 20% 的流量时间戳前缀是 two ,这意味着有 80% 的 TCP 流量路由到了 tcp-echo 服务的 v1 版本,与此同时有 20% 流量路由到了 v2 版本。

设置请求超时

HTTP 请求的超时可以用路由规则的 timeout 字段来指定。默认情况下,超时是禁用的,本任务中,会把 reviews 服务的超时设置为 1 秒。为了观察效果,还需要在对 ratings 服务的调用上人为引入 2 秒的延迟。

首先,使用samples/bookinfo/networking/virtual-service-all-v1.yaml初始化版本路由:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
virtualservice.networking.istio.io/productpage created
virtualservice.networking.istio.io/reviews created
virtualservice.networking.istio.io/ratings created
virtualservice.networking.istio.io/details created

刷新BookInfo页面,Reviews 展示的是 v1 版本 Ratings(没有任何星星)。

这时候将请求路由到 reviews 服务的 v2 版本,它会发起对 ratings 服务的调用:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
EOF

给对 ratings 服务的调用添加 2 秒的延迟:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percent: 100
fixedDelay: 2s
route:
- destination:
host: ratings
subset: v1
EOF

可以看到 Bookinfo 应用运行正常(显示了评级的星型符号),但是每次刷新页面,都会有 2 秒的延迟。

现在给对 reviews 服务的调用增加一个半秒的请求超时:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
timeout: 0.5s
EOF

刷新BookInfo页面,这时候应该看到 1 秒钟就会返回,而不是之前的 2 秒钟,但 reviews 是不可用的。

本任务中,使用 Istio 为对 reviews 微服务的调用配置了半秒的请求超时。默认情况下请求超时是禁用的。reviews 服务在处理请求时会接着调用 ratings 服务,用 Istio 在对 ratings 的调用中注入了两秒钟的延迟,这样就让 reviews 服务要花费超过半秒的时间来完成调用,因此可以观察到超时。

可以观察到,Bookinfo 的页面(调用 reviews 服务来生成页面)没显示评论,而是显示了消息:Sorry, product reviews are currently unavailable for this book. 这就是它收到了来自 reviews 服务的超时错误信息。

productpage 微服务在调用 reviews 微服务时,还有它自己的应用级的超时(3 秒)设置。注意在本任务中使用 Istio 路由规则设置了半秒的超时。如果将超时设置为大于 3 秒(比如 4 秒),则超时将不会有任何影响,因为这两个超时的限制性更强。

还有一点关于 Istio 中超时控制方面的补充说明,除了像本文一样在路由规则中进行超时设置之外,还可以进行请求一级的设置,只需在应用的对外请求中加入 x-envoy-upstream-rq-timeout-ms 请求头即可。在这个请求头中的超时设置单位是毫秒而不是秒。

最后,删除应用程序的路由规则:

$ kubectl delete -f samples/bookinfo/networking/virtual-service-all-v1.yaml

熔断

熔断是创建弹性微服务应用程序的重要模式,熔断能够使我们的应用程序具备应对来自故障、潜在峰值和其他未知网络因素影响的能力,熔断在微服务框架中也是必备的一个功能。

同样在 Istio 中也是原生就支持熔断功能的,首先部署示例服务 samples/httpbin/httpbin.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
service: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
serviceAccountName: httpbin
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 80

如果i起了Sidecar自动注入,直接使用以下命令部署httpbin服务:

$ kubectl apply -f samples/httpbin/httpbin.yaml
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created $ kubectl get pods -l app=httpbin
NAME READY STATUS RESTARTS AGE
httpbin-74fb669cc6-dgjtv 2/2 Running 0 5m27s

否则,必须在部署 httpbin 应用程序前进行手动注入,部署命令如下:

$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)

应用部署完成后,接着创建一个目标规则,在调用 httpbin 服务时配置熔断设置。

这里通过一个 DestinationRule 对象来创建一个如下所示的资源对象:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1 # 最大连接数为1
http:
http1MaxPendingRequests: 1 # 最大请求数为1
maxRequestsPerConnection: 1
outlierDetection:
consecutiveErrors: 1
interval: 1s
baseEjectionTime: 3m
maxEjectionPercent: 100
EOF

目标规则创建完成后,接着我们来创建一个客户端应用来发送流量请求到 httpbin 服务,这里我们使用一个名为 Fortio 的测试客户端应用,该应用可以控制连接数、并发数及发送 HTTP 请求的延迟,通过 Fortio 能够有效的触发前面在 DestinationRule 中设置的熔断策略。

客户端应用资源清单文件位于 samples/httpbin/sample-client/fortio-deploy.yaml,内容如下所示:

apiVersion: v1
kind: Service
metadata:
name: fortio
labels:
app: fortio
service: fortio
spec:
ports:
- port: 8080
name: http
selector:
app: fortio
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: fortio-deploy
spec:
replicas: 1
selector:
matchLabels:
app: fortio
template:
metadata:
annotations:
# This annotation causes Envoy to serve cluster.outbound statistics via 15000/stats
# in addition to the stats normally served by Istio. The Circuit Breaking example task
# gives an example of inspecting Envoy stats via proxy config.
proxy.istio.io/config: |-
proxyStatsMatcher:
inclusionPrefixes:
- "cluster.outbound"
- "cluster_manager"
- "listener_manager"
- "server"
- "cluster.xds-grpc"
labels:
app: fortio
spec:
containers:
- name: fortio
image: fortio/fortio:latest_release
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http-fortio
- containerPort: 8079
name: grpc-ping

直接部署上面的客户端应用,记得注入 Istio Sidecar 代理,以便 Istio 对其网络交互进行管理:

$ kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml
service/fortio created
deployment.apps/fortio-deploy created

部署完成后我们可以进入到客户端应用中使用 Fortio 工具调用 httpbin 服务。-curl 参数表明发送一次调用:

$ export FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
$ kubectl exec -it $FORTIO_POD -c fortio -- /usr/bin/fortio load -curl http://httpbin:8000/get

由于在windows下,git bash运行上面的命令会报错。这里选了个折中 的办法,同样可以达到观察效果,在git bash下执行kubectl get pod | grep fortio | awk '{ print $1 }获取到pod的名称,或者直接kubectl get pod获取。对命令进行拼接,将$FORTIO_POD换成获取到的Pod名,然后在CMD中执行:

$ kubectl exec -i fortio-deploy-687945c6dc-rp7gf  -c fortio -- /usr/bin/fortio load -curl  http://httpbin:8000/get
HTTP/1.1 200 OK
server: envoy
date: Tue, 08 Feb 2022 10:25:39 GMT
content-type: application/json
content-length: 594
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2 {
"args": {},
"headers": {
"Host": "httpbin:8000",
"User-Agent": "fortio.org/fortio-1.17.1",
"X-B3-Parentspanid": "92ea278cc8d9e481",
"X-B3-Sampled": "1",
"X-B3-Spanid": "bc19af06a43cba86",
"X-B3-Traceid": "22995ef1fa7574e892ea278cc8d9e481",
"X-Envoy-Attempt-Count": "1",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=ac2d4f98ba69b6e3119f9ea6356cad51805b7a132ac08ba208786ab0229bf2cb;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
},
"origin": "127.0.0.6",
"url": "http://httpbin:8000/get"
}

可以看到调用后端服务的请求是成功的,然后我们来测试下熔断。

DestinationRule 配置中,我们定义了 maxConnections: 1http1MaxPendingRequests: 1,表示如果并发的连接和请求数超过一个,则在 istio-proxy 进行进一步的请求和连接时,后续的请求或连接将被阻止。

比如这里我们发送并发数为 2 的连接(-c 2),请求 20 次(-n 20)来观察下现象:

$ kubectl exec -it $FORTIO_POD  -c fortio /usr/bin/fortio -- load -c 2 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get

当然也可以通过 istio-proxy 状态来了解更多关于熔断的详情,使用如下命令进行查看:

$ kubectl exec "$FORTIO_POD" -c istio-proxy -- pilot-agent request GET stats | grep httpbin | grep pending

最后清理规则和下限httpbin服务和客户端:

$ kubectl delete deploy httpbin fortio-deploy
$ kubectl delete svc httpbin

镜像

流量镜像,也称为影子流量,是一个以尽可能低的风险为生产带来变化的强大的功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。

在此任务中,首先把流量全部路由到测试服务的 v1 版本。然后,执行规则将一部分流量镜像到 v2 版本。

首先部署 v1 版本的 httpbin 服务:

$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v1
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF

部署v2版本的httpbin服务:

cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v2
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v2
template:
metadata:
labels:
app: httpbin
version: v2
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF

创建一个 httpbin 的 Service 对象,关联上面的两个版本服务:

kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
EOF

启动 sleep 服务,这样就可以使用 curl 来提供负载:

$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep","infinity"]
imagePullPolicy: IfNotPresent
EOF

默认情况下,Kubernetes 在 httpbin 服务的两个版本之间进行负载均衡。在此步骤中会更改该行为,把所有流量都路由到 v1 版本。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- httpbin
http:
- route:
- destination:
host: httpbin
subset: v1
weight: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF

上面对象创建完成后,所有流量都会转到 httpbin:v1 服务下面去,使用如下所示的命令来测试以下:

$ export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec -it $SLEEP_POD -c sleep -- sh -c 'curl http://httpbin:8000/headers' | python -m json.tool
{
{
"headers": {
"Accept": "*/*",
"Host": "httpbin:8000",
"User-Agent": "curl/7.81.0-DEV",
"X-B3-Parentspanid": "58dc016ffa182ab2",
"X-B3-Sampled": "1",
"X-B3-Spanid": "784a42ac78511bb3",
"X-B3-Traceid": "9c1177945f2c5dd658dc016ffa182ab2",
"X-Envoy-Attempt-Count": "1",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=aeee5046ea2b74f8127a4b2600232d473a29eb99dfcb0f163e27c2631c902f86;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
}
}

然后可以分别查看 httpbin 服务 v1v2 两个 Pods 的日志,正常情况下只会看到 v1 会产生日志,v2 中没有:

$ export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})
$ kubectl logs -f $V1_POD -c httpbin
127.0.0.6 - - [08/Feb/2022:11:21:15 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
...... $ export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})
$ kubectl logs -f $V2_POD -c httpbin

这是因为上面我们创建的路由规则是将所有的请求都路由到了 v1 这个版本的服务中去。接下来我们更改下流量规则,将流量镜像到 v2 版本的服务中去:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- httpbin
http:
- route:
- destination:
host: httpbin
subset: v1
weight: 100
mirror:
host: httpbin
subset: v2
mirror_percent: 100
EOF

这个路由规则会发送 100% 流量到 v1 这个子集服务,然后通过 mirror 属性配置了将流量也 100% 镜像到了 httpbin:v2 服务。当流量被镜像时,请求将发送到镜像服务中,并在 headers 中的 Host/Authority 属性值上追加 -shadow

需要注意的是这些被镜像的流量是 即发即弃的,也就是说镜像请求的响应会被丢弃。

现在我们在用上面的命令来测试发送一次请求:

$ kubectl exec -it $SLEEP_POD -c sleep -- sh -c 'curl  http://httpbin:8000/headers' | python -m json.tool

现在就可以看到 v1v2 中都有了访问日志,v2 中的访问日志就是由镜像流量产生的,这些请求的实际目标是 v1

$ kubectl logs -f $V1_POD -c httpbin
127.0.0.6 - - [08/Feb/2022:11:21:15 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [08/Feb/2022:11:28:41 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV" $ kubectl logs -f $V2_POD -c httpbin
127.0.0.6 - - [08/Feb/2022:11:28:41 +0000] "GET /headers HTTP/1.1" 200 569 "-" "curl/7.81.0-DEV"

最后删除规则以及关闭Httpbin服务端和客户端:

$ kubectl delete virtualservice httpbin
$ kubectl delete destinationrule httpbin $ kubectl delete deploy httpbin-v1 httpbin-v2 sleep
$ kubectl delete svc httpbin

Istio流量控制的更多相关文章

  1. k8s+istio:流量控制之灰度发布

    通过Kubernetes+Istio的流量控制实现灰度发布,主要演示通过流量权重实现蓝绿,通过http自定义头实现金丝雀 准备环境 k8s和istio不想自己装的话可以在云上买个按量付费集群,用完即删 ...

  2. Kubernetes+Istio

    Kubernetes+Istio   微服务.SpringCloud.k8s.Istio杂谈   一.微服务与SOA “微服务”是一个名词,没有这个名词之前也有“微服务”,一个朗朗上口的名词能让大家产 ...

  3. Istio 知多少 | 下一代微服务的守护者

    1. 引言 在写完eShopOnContainers 知多少[12]:Envoy gateways后,就一直想进一步探索Service Mesh,最近刚在极客时间上学完<Service Mesh ...

  4. 灵雀云Istio技术实践专题整理

    Istio技术实践专题(1) Service Mesh Istio 基本概念和架构基础 Istio被称作Kubernetes的最佳云原生拍档.从今天起,我们推出"Istio技术实践" ...

  5. 从一到万的运维之路,说一说VM/Docker/Kubernetes/ServiceMesh

    摘要:本文从单机真机运营的历史讲起,逐步介绍虚拟化.容器化.Docker.Kubernetes.ServiceMesh的发展历程.并重点介绍了容器化阶段之后,各项重点技术的安装.使用.运维知识.可以说 ...

  6. Istio实践(2)-流量控制及服务间调用

    前言:接上一篇istio应用部署,本文介绍通过virtualservice实现流量控制,并通过部署client端进行服务调用实例 1. 修改virtualservice组件,实现权重占比访问不同版本服 ...

  7. Istio入门实战与架构原理——使用Docker Compose搭建Service Mesh

    本文将介绍如何使用Docker Compose搭建Istio.Istio号称支持多种平台(不仅仅Kubernetes).然而,官网上非基于Kubernetes的教程仿佛不是亲儿子,写得非常随便,不仅缺 ...

  8. Istio流量管理实现机制深度解析

    https://zhaohuabing.com/post/2018-09-25-istio-traffic-management-impl-intro/TOC 前言 Pilot高层架构 统一的服务模型 ...

  9. Istio微服务架构初试

    感谢 http://blog.csdn.net/qq_34463875/article/details/77866072 看了一些文档,有些半懂不懂,所以还是需要helloworld一下.因为isti ...

  10. Google、IBM和Lyft开源其大型微服务系统管理工具Istio

    Istio 的优势 集群规模可视性:在故障状况出现时,运营人员需要利用多种工具以始终关注集群运行状况并分析微服务状态图表.Istio 项目能够监控与应用程序及网络活动相关的数据,利用 Promethe ...

随机推荐

  1. go 定时任务库 cron

    简介 在Linux中,Cron是计划任务管理系统,通过crontab命令使任务在约定的时间执行已经计划好的工作,例如定时备份系统数据.周期性清理缓存.定时重启服务等. 本文介绍的cron库是一个用于管 ...

  2. 连接MySQL数据库出现时Authentication plugin 'caching_sha2_password' cannot be loaded的解决办法

    问题描述:用Navicat Premium或HeidiSQL连接MySQL数据库时会弹出下面的情况 解决方法: 1.运行命令行窗口,输入以下命令,输入密码后进入到mysql中,(最好将MySQL安装目 ...

  3. Windows下Cmake+VS2017+OpenSceneGraph环境搭建及采坑

    一.资料准备 1.OSG源码官网下载地址:http://www.openscenegraph.org/index.php/download-section/stable-releases或者OSGCh ...

  4. EBUSY: resource busy or locked, rmdir

    方案一: 方案二: !!! 出现问题后,千万不要忽略npm提示你的警告... 如果以上两种方案还未解决,那么大概率是因为你的npm版本较低导致的,升级你的npm. cnpm install -g np ...

  5. Oracle AI应用的LLM模型典型配置

    最近在做一些基于Oracle的一些AI应用测试工作,AI肯定离不开配置LLM相关,虽然是简单配置类,但实际还是遇到一些卡点,记录下来供今后参考. 1.配置Embedding模型 2.特殊语法传参JSO ...

  6. 入门Dify平台:知识库分析

    今天,我们来简单了解一下 Dify 的知识库功能.其实,它并没有想象中那么复杂或难以操作.无论是 Dify 还是其他类似平台,都已经将知识库的功能封装得非常完善.你可以直接把它当作一个云端的记事本,内 ...

  7. MySQL 修复损坏表

    修复MySQL损坏表的简单步骤,不一定适用任意情况下的表损坏的问题,留爪. 简单3步曲: # 用`root`用户登录MySQL # 这里可能需要输入密码 mysql -uroot -p # 使用指定数 ...

  8. 前端 Vue 后端返回流,前端通过流进行下载

    前端写法 //文件下载 async handleDownload(row) { try { // 假设文件是通过 GET 请求获取的,url 为文件资源的 API 地址 const response ...

  9. 小米root教程 —— 基于小米11

    1.解BL锁 工具下载及教程 解锁工具下载 2.root 通过magisk实现 Magisk安装教程 Magisk安装完即root. 升级系统root会丢失,我是重新root的,很麻烦,建议搜索保留r ...

  10. Java IO--实现文件的加密解密

    我们知道文件存储的方式在计算机当中是以字节的方式进行存储的,可以通过对文件字节的操作来实现文件的加密. 下面的例子是通过读取文件的字节,然后使字节中的每一位取反(1变0,0变1),再进行倒置,来实现加 ...