# Istio 教程
**注意:本文檔已失效,請瀏覽 [Istio 官方文檔](https://istio.io/zh)。本書中的 Service Mesh 章節已不再維護,請轉到 [istio-handbook](https://jimmysong.io/istio-handbook) 中瀏覽。**
本文是 Istio 管理 Java 微服務的案例教程,使用的所有工具和軟件全部基于開源方案,替換了 [redhat-developer-demos/istio-tutorial](https://github.com/redhat-developer-demos/istio-tutorial) 中的 minishift 環境,使用 [kubernetes-vagrant-centos-cluster](https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster) 替代,沿用了原有的微服務示例,使用 Zipkin 做分布式追蹤而不是 Jaeger。
本文中的代碼和 YAML 文件見 <https://github.com/rootsongjc/istio-tutorial>。
**注意:本教程基于 Istio 0.7.1,2018年8月1日,Isito 1.0 發布,本教程已過時。**
## 準備環境
在進行本教程前需要先準備以下工具和環境。
- 8G 以上內存
- Vagrant 2.0+
- Virtualbox 5.0 +
- 提前下載 kubernetes1.9.1 的 release 壓縮包
- docker 1.12+
- kubectl 1.9.1+
- maven 3.5.2+
- istioctl 0.7.1
- git
- curl、gzip、tar
- [kubetail](https://github.com/johanhaleby/kubetail)
- [siege](https://github.com/JoeDog/siege)
## 安裝 Kubernetes
請參考 [kubernetes-vagrant-centos-cluster](https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster) 在本地啟動擁有三個節點的 kubernetes 集群。
```bash
git clone https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster.git
cd kubernetes-vagrant-centos-cluster
vagrant up
```
## 安裝 Istio
在 [kubernetes-vagrant-centos-cluster](https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster) 中的包含 Istio 0.7.1 的安裝 YAML 文件,運行下面的命令安裝 Istio。
```bash
kubectl apply -f addon/istio/
```
**運行示例**
```bash
kubectl apply -n default -f <(istioctl kube-inject -f yaml/istio-bookinfo/bookinfo.yaml)
```
在您自己的本地主機的`/etc/hosts`文件中增加如下配置項。
```ini
172.17.8.102 grafana.istio.jimmysong.io
172.17.8.102 servicegraph.istio.jimmysong.io
172.17.8.102 zipkin.istio.jimmysong.io
```
我們可以通過下面的URL地址訪問以上的服務。
| Service | URL |
| ------------ | ------------------------------------------------------------ |
| grafana | http://grafana.istio.jimmysong.io |
| servicegraph | <http://servicegraph.istio.jimmysong.io/dotviz>,<http://servicegraph.istio.jimmysong.io/graph> |
| zipkin | http://zipkin.istio.jimmysong.io |
詳細信息請參閱 https://istio.io/docs/guides/bookinfo.html
## 部署示例應用
在打包成鏡像部署到 kubernetes 集群上運行之前,我們先在本地運行所有示例。
本教程中三個服務之間的依賴關系如下:
```ini
customer → preference → recommendation
```
`customer` 和 `preference` 微服務是基于 Spring Boot 構建的,`recommendation` 微服務是基于 [vert.x](https://vertx.io) 構建的。
`customer` 和 `preference` 微服務的 `pom.xml` 文件中都引入了 OpenTracing 和 Jeager 的依賴。
```xml
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-cloud-starter</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>com.uber.jaeger</groupId>
<artifactId>jaeger-tracerresolver</artifactId>
<version>0.25.0</version>
</dependency>
```
### 本地運行
我們首先在本地確定所有的微服務都可以正常運行,然后再打包鏡像在 kubernetes 集群上運行。
#### 啟動 Jaeger
使用 docker 來運行 jagger。
```bash
docker run -d \
--rm \
-p5775:5775/udp \
-p6831:6831/udp \
-p6832:6832/udp \
-p16686:16686 \
-p14268:14268 \
jaegertracing/all-in-one:1.3
```
Jaeger UI 地址 http://localhost:16686
#### Customer
```bash
cd customer/java/springboot
JAEGER_SERVICE_NAME=customer mvn \
spring-boot:run \
-Drun.arguments="--spring.config.location=src/main/resources/application-local.properties"
```
服務訪問地址: http://localhost:8280
#### Preference
```bash
cd preference/java/springboot
JAEGER_SERVICE_NAME=preference mvn \
spring-boot:run \
-Drun.arguments="--spring.config.location=src/main/resources/application-local.properties"
```
服務訪問地址:http://localhost:8180
#### Recommendation
```bash
cd recommendation/java/vertx
mvn vertx:run
```
服務訪問地址:http://localhost:8080
所有服務都啟動之后,此時訪問 http://localhost:8280 將會看到如下輸出。
```bash
customer => preference => recommendation v1 from 'unknown': 1
```
每訪問一次最后的數字就會加 1。
#### Jaeger
此時訪問 http://localhost:16686 將看到 Jaeger query UI,所有應用將 metrics 發送到 Jeager 中。
可以在 Jaeger UI 中搜索 `customer` 和 `preference` service 的 trace 并查看每次請求的 tracing。

### 構建鏡像
在本地運行測試無誤之后就可以構建鏡像了。本教程中的容器鏡像都是在 [fabric8/java-jboss-openjdk8-jdk](https://hub.docker.com/r/fabric8/java-jboss-openjdk8-jdk/~/dockerfile/) 的基礎上構建的。只要將 Java 應用構建出 Jar 包然后放到 `/deployments` 目錄下基礎鏡像就可以自動幫我們運行,所以我們看到著幾個應用的 `Dockerfile` 文件中都沒有執行入口,真正的執行入口是 [run-java.sh](https://github.com/fabric8io-images/java/blob/master/images/jboss/openjdk8/jdk/run-java.sh)。
#### Customer
構建 Customer 鏡像。
```bash
cd customer/java/springboot
mvn clean package
docker build -t jimmysong/istio-tutorial-customer:v1 .
docker push jimmysong/istio-tutorial-customer:v1
```
第一次構建和上傳需要花費一點時間,下一次構建就會很快。
#### Preference
構建 Preference 鏡像。
```bash
cd preference/java/springboot
mvn clean package
docker build -t jimmysong/istio-tutorial-preference:v1 .
docker push jimmysong/istio-tutorial-preference:v1
```
#### Recommendation
構建 Recommendation 鏡像。
```bash
cd recommendation/java/vertx
mvn clean package
docker build -t jimmysong/istio-tutorial-recommendation:v1 .
docker push jimmysong/istio-tutorial-recommendation:v1
```
現在三個 docker 鏡像都構建完成了,我們檢查一下。
```bash
$ docker images | grep istio-tutorial
REPOSITORY TAG IMAGE ID CREATED SIZE
jimmysong/istio-tutorial-recommendation v1 d31dd858c300 51 seconds ago 443MB
jimmysong/istio-tutorial-preference v1 e5f0be361477 6 minutes ago 459MB
jimmysong/istio-tutorial-customer v1 d9601692673e 13 minutes ago 459MB
```
### 部署到 Kubernetes
使用下面的命令將以上服務部署到 kubernetes。
```bash
# create new namespace
kubectl create ns istio-tutorial
# deploy recommendation
kubectl apply -f <(istioctl kube-inject -f recommendation/kubernetes/Deployment.yml) -n istio-tutorial
kubectl apply -f recommendation/kubernetes/Service.yml
# deploy preferrence
kubectl apply -f <(istioctl kube-inject -f preference/kubernetes/Deployment.yml) -n istio-tutorial
kubectl apply -f preference/kubernetes/Service.yml
# deploy customer
kubectl apply -f <(istioctl kube-inject -f customer/kubernetes/Deployment.yml) -n istio-tutorial
kubectl apply -f customer/kubernetes/Service.yml
```
**注意:**`preference` 和 `customer` 應用啟動速度比較慢,我們將 livenessProb 配置中的 `initialDelaySeconds` 設置為 **20** 秒。
查看 Pod 啟動狀態:
```bash
kubectl get pod -w -n istio-tutorial
```
### 增加 Ingress 配置
為了在 kubernetes 集群外部訪問 customer 服務,我們需要增加 ingress 配置。
```bash
kubectl apply -f ingress/ingress.yaml
```
修改本地的 `/etc/hosts` 文件,增加一條配置。
```ini
172.17.8.102 customer.istio-tutorial.jimmysong.io
```
現在訪問 http://customer.istio-tutorial.jimmysong.io 將看到如下輸出:
```ini
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 1
```
批量訪問該地址。
```bash
./bin/poll_customer.sh
```
訪問 <http://servicegraph.istio.jimmysong.io/dotviz> 查看服務的分布式追蹤和依賴關系。


訪問 <http://servicegraph.istio.jimmysong.io/dotviz> 查看服務間的關系圖和 QPS。

訪問 <http://grafana.istio.jimmysong.io> 查看 Service Mesh 的監控信息。

## Istio 使用示例
為了試用 Istio 中的各種功能,我們需要為應用構建多個版本,我們為 recommendation 構建 v2 版本的鏡像,看看如何使用 Istio 控制微服務的流量。
### 構建 recommendation:v2
我們將構建新版的 `recommendation` 服務的鏡像,并觀察 `customer` 對不同版本的 `recommendataion` 服務的訪問頻率。
修改 `recommendation/java/vertx/src/main/java/com/redhat/developer/demos/recommendation/RecommendationVerticle.java` 程序中代碼。
將 `private static final String RESPONSE_STRING_FORMAT = "recommendation v1 from '%s': %d\n";` 修改為 `private static final String RESPONSE_STRING_FORMAT = "recommendation v2 from '%s': %d\n";`
并構建 `recommendation:v2` 鏡像。
```bash
cd recommendation/java/vertx
mvn clean package
docker build -t jimmysong/istio-tutorial-recommendation:v2 .
docker push jimmysong/istio-tutorial-recommendation:v2
```
將應用部署到 kubernetes。
```bash
# deploy recommendation
kubectl apply -f <(istioctl kube-inject -f recommendation/kubernetes/Deployment-v2.yml) -n istio-tutorial
```
現在再訪問 `customer` 服務,將看到如下輸出:
```bash
$ bin/poll_customer.sh
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 1
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3581
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 2
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3582
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 3
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3583
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 4
```
我們可以看到 v1 和 v2 版本的 `recommendation` 服務會被間隔訪問到。
我們再將 v2 版本的 `recommendation` 實例數設置成 2 個。
```bash
kubectl scale --replicas=2 deployment/recommendation-v2 -n istio-tutorial
kubectl get pod -w -n istio-tutorial
```
觀察 `recommendation-v2` Pod 達到兩個之后再訪問 `customer` 服務。
```bash
$ bin/poll_customer.sh
customer => preference => recommendation v2 from '77b9f6cc68-j9fgj': 1
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 71
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3651
customer => preference => recommendation v2 from '77b9f6cc68-j9fgj': 2
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 72
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3652
customer => preference => recommendation v2 from '77b9f6cc68-j9fgj': 3
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 73
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3653
```
觀察輸出中 v1 和 v2 版本 `recommendation` 的訪問頻率。
將 `recommendataion` 服務的實例數恢復為 1。
```bash
kubectl scale --replicas=1 deployment/recommendation-v2
```
### 修改 Istio RouteRules
以下所有路有規則都是針對 `recommendation` 服務,并在 repo 的根目錄下執行。
**將所有流量打給 v2**
下面將演示如何動態的劃分不同版本服務間的流量,將所有的流量都打到 `recommendation:v2`。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v2.yml -n istio-tutorial
```
現在再訪問 `customer` 服務將看到所有的流量都會打到 `recommendation:v2`。
刪除 RouteRules 后再訪問 `customer` 服務將看到又恢復了 v1 和 v2 版本的 `recommendation` 服務的間隔訪問。
```bash
istioctl delete routerule recommendation-default
```
**切分流量**
將 90% 的流量給 v1,10% 的流量給 v2。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v1_and_v2.yml -n istio-tutorial
```
執行`bin/poll_customer.sh` 觀察訪問情況。
要想動態切分流量只要修改 RouteRules 中的 `weight` 配置即可。
```yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: recommendation-v1-v2
spec:
destination:
namespace: istio-tutorial
name: recommendation
precedence: 5
route:
- labels:
version: v1
weight: 90
- labels:
version: v2
weight: 10
```
因為 RouteRule 有優先級,為了繼續后面的實驗,在驗證完成后刪除該 RouteRule。
```bash
istioctl delete routerule recommendation-v1-v2 -n istio-tutorial
```
### 故障注入
有時候我們為了增強系統的健壯性,需要對系統做混沌工程,故意注入故障,并保障服務可以自動處理這些故障。
**注入 HTTP 503 錯誤**
```bash
istioctl create -f istiofiles/route-rule-recommendation-503.yml -n istio-tutorial
```
有 50% 的幾率報 503 錯誤。
```bash
$ bin/poll_customer.sh
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 135
customer => 503 preference => 503 fault filter abort
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3860
customer => 503 preference => 503 fault filter abort
customer => 503 preference => 503 fault filter abort
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 136
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 3861
customer => 503 preference => 503 fault filter abort
customer => 503 preference => 503 fault filter abort
customer => preference => recommendation v2 from '77b9f6cc68-5xs27': 137
customer => 503 preference => 503 fault filter abort
```
清理 RouteRule。
```bash
istioctl delete routerule recommendation-503 -n istio-tutorial
```
### 增加延遲
增加服務的訪問延遲。
```bash
istioctl create -f istiofiles/route-rule-recommendation-delay.yml -n istio-tutorial
```
會有 50% 的幾率訪問 `recommendation` 服務有 7 秒的延遲。百分比和延遲時間可以在 RouteRule 中配置。
清理 RouteRule。
```bash
istioctl delete routerule recommendation-delay -n istio-tutorial
```
### 重試
讓服務不是直接失敗,而是增加重試機制。
我們下面將同時應用兩條 RouteRule,讓訪問 `recommendation` 服務時有 50% 的幾率出現 503 錯誤,并在出現錯誤的時候嘗試訪問 v2 版本,超時時間為 2 秒。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v2_503.yml -n istio-tutorial
istioctl create -f istiofiles/route-rule-recommendation-v2_retry.yml -n istio-tutorial
```
執行 `bin/poll_customer.sh` 我們看到一開始有些 503 錯誤,然后所有的流量都流向了 v2。
清理 RouteRules。
```bash
istioctl delete routerule recommendation-v2-retry -n istio-tutorial
istioctl delete routerule recommendation-v2-503 -n istio-tutorial
```
### 超時
設置超時時間,只有服務訪問超時才認定服務訪問失敗。
取消注釋 `recommendation/java/vertx/src/main/java/com/redhat/developer/demos/recommendation/RecommendationVerticle.java` 中的下面一行,增加超時時間為 3 秒。
```java
router.get("/").handler(this::timeout);
```
重新生成鏡像。
```bash
cd recommendation/java/vertx
mvn clean package
docker build -t jimmysong/istio-tutorial-recommendation:v2 .
docker push jimmysong/istio-tutorial-recommendation:v2
```
重新部署到 kubernetes。
```bash
kubectl delete -f recommendation/kubernetes/Deployment-v2.yml
```
因為我們重新構建的鏡像使用了同樣的名字和 tag,而之前在 `Deployment-v2.yml` 中配置的鏡像拉取策略是 `IfNotPresent`,這樣的話即使我們構建了新的鏡像也無法應用到集群上,因此將鏡像拉取策略改成 `Always` 確保每次啟動 Pod 的時候都會拉取鏡像。
```bash
kubectl apply -f <(istioctl kube-inject -f recommendation/kubernetes/Deployment-v2.yml) -n istio-tutorial
```
啟用超時 RouteRules。
```bash
istioctl create -f istiofiles/route-rule-recommendation-timeout.yml -n istio-tutorial
```
訪問 `customer` 服務將看到如下輸出:
```bash
$ bin/poll_customer.sh
customer => 503 preference => 504 upstream request timeout
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 4002
customer => 503 preference => 504 upstream request timeout
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 4003
customer => 503 preference => 504 upstream request timeout
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 4004
```
清理 RouteRules。
```bash
istioctl delete routerule recommendation-timeout -n istio-tutorial
```
### 基于 user-agent 的智能路由(金絲雀發布)
User-agent 是一個字符串,其中包含了瀏覽器的信息,訪問 https://www.whoishostingthis.com/tools/user-agent 獲取你的 user-agent。
我的 user-agent 是:
```ini
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
```
將所有的流量打到 v1。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v1.yml -n istio-tutorial
```
將使用 Safari 瀏覽器訪問的流量打到 v2。
```bash
istioctl create -f istiofiles/route-rule-safari-recommendation-v2.yml -n istio-tutorial
```
誰用 Safari 或者 Chrome(Chrome 瀏覽器的 user-agent 中也包含 Safari 字段)訪問 <http://customer.istio-tutorial.jimmysong.io/> 在經過 3 秒鐘(我們在前面重新編譯 v2 鏡像,設置了 3 秒超時時間)后將看到訪問 v2 的輸出。
或者使用 curl 訪問。
```bash
curl -A Safari http://customer.istio-tutorial.jimmysong.io/
curl -A Firefox http://customer.istio-tutorial.jimmysong.io/
```
觀察返回的結果。
將移動端用戶的流量導到 v2。
```bash
istioctl create -f istiofiles/route-rule-mobile-recommendation-v2.yml -n istio-tutorial
curl -A "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4(KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" http://customer.istio-tutorial.jimmysong.io/
```
觀察輸出的結果。
清理 RouteRules。
```bash
istioctl delete routerule recommendation-mobile -n istio-tutorial
istioctl delete routerule recommendation-safari -n istio-tutorial
istioctl delete routerule recommendation-default -n istio-tutorial
```
### 鏡像流量
確保當前至少運行了兩個版本的 `recommendation` 服務,并且沒有 RouteRule。
注:可以使用 `istioctl get routerule` 獲取 RouteRule。
設置流量鏡像,將所有 v1 的流量都被鏡像到 v2。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v1-mirror-v2.yml -n istio-tutorial
bin/poll_customer.sh
```
查看 recommendation-v2 的日志。
```bash
kubectl logs -f `oc get pods|grep recommendation-v2|awk '{ print $1 }'` -c recommendation
```
### 訪問控制
Istio 可以設置服務訪問的黑白名單,如果沒有權限的話會返回 HTTP 404 Not Found。
#### 白名單
```bash
istioctl create -f istiofiles/acl-whitelist.yml -n istio-tutorial
```
此時訪問 `customer` 服務。
```bash
$ bin/poll_customer.sh
customer => 404 NOT_FOUND:preferencewhitelist.listchecker.istio-tutorial:customer is not whitelisted
```
重置環境。
```bash
istioctl delete -f istiofiles/acl-whitelist.yml -n istio-tutorial
```
#### 黑名單
設置黑名單,所有位于黑名單中的流量將獲得 403 Forbidden 返回碼。
```bash
istioctl create -f istiofiles/acl-blacklist.yml -n istio-tutorial
```
此時訪問 `customer` 服務。
```bash
$ bin/poll_customer.sh
customer => 403 PERMISSION_DENIED:denycustomerhandler.denier.istio-tutorial:Not allowed
```
重置環境。
```bash
istioctl delete -f istiofiles/acl-blacklist.yml -n istio-tutorial
```
### 負載均衡
Kubernetes 中默認的負載均衡策略是 round-robin,當然我們可以使用 Istio 把它修改成 random。
增加 v1 的實例數。
```bash
kubectl scale deployment recommendation-v1 --replicas=2 -n istio-tutorial
```
持續訪問 `customer` 服務。
```bash
bin/poll_customer.sh
```
保持前臺輸出,觀察流量的行為。
應用負載均衡策略。
```bash
istioctl create -f istiofiles/recommendation_lb_policy_app.yml -n istio-tutorial
```
觀察一段時間流量的行為后,重置環境。
```bash
istioctl delete -f istiofiles/recommendation_lb_policy_app.yml -n istio-tutorial
kubectl scale deployment recommendation-v1 --replicas=1 -n istio-tutorial
```
### 速率限制
暫時不可用
### 斷路器
當達到最大連接數和最大掛起請求數時快速失敗。
將流量在 v1 和 v2 之間均分。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v1_and_v2_50_50.yml -n istio-tutorial
```
未開啟斷路器的時候啟動負載測試。
```bash
$ siege -r 2 -c 20 -v customer.istio-tutorial.jimmysong.io
New configuration template added to /Users/jimmysong/.siege
Run siege -C to view the current settings in that file
** SIEGE 4.0.4
** Preparing 20 concurrent users for battle.
The server is now under siege...
HTTP/1.1 200 0.10 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.12 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.13 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.13 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.13 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.17 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.12 secs: 74 bytes ==> GET /
HTTP/1.1 200 3.14 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.15 secs: 74 bytes ==> GET /
HTTP/1.1 200 3.15 secs: 74 bytes ==> GET /
HTTP/1.1 200 3.17 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.17 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.20 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.20 secs: 74 bytes ==> GET /
HTTP/1.1 200 0.05 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.12 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.15 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.25 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.26 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.14 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.58 secs: 74 bytes ==> GET /
HTTP/1.1 200 6.15 secs: 74 bytes ==> GET /
HTTP/1.1 200 6.16 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.03 secs: 74 bytes ==> GET /
HTTP/1.1 200 6.06 secs: 75 bytes ==> GET /
HTTP/1.1 200 6.04 secs: 75 bytes ==> GET /
HTTP/1.1 200 3.11 secs: 74 bytes ==> GET /
HTTP/1.1 200 3.09 secs: 75 bytes ==> GET /
HTTP/1.1 200 6.15 secs: 74 bytes ==> GET /
HTTP/1.1 200 6.71 secs: 74 bytes ==> GET /
HTTP/1.1 200 3.52 secs: 75 bytes ==> GET /
^C
Lifting the server siege...
Transactions: 31 hits
Availability: 100.00 %
Elapsed time: 7.99 secs
Data transferred: 0.00 MB
Response time: 2.99 secs
Transaction rate: 3.88 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 11.60
Successful transactions: 31
Failed transactions: 0
Longest transaction: 6.71
Shortest transaction: 0.05
```
所有的請求都成功了,但是性能很差,因為 v2 版本設置了 3 秒的超時時間。
我們啟用下斷路器。
```bash
istioctl create -f istiofiles/recommendation_cb_policy_version_v2.yml -n istio-tutorial
```
重新測試一下。
```bash
$ siege -r 2 -c 20 -v customer.istio-tutorial.jimmysong.io
** SIEGE 4.0.4
** Preparing 20 concurrent users for battle.
The server is now under siege...
HTTP/1.1 200 0.07 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.07 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.07 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.12 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.12 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.16 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.16 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.21 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.21 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.24 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.24 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.14 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.29 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.13 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.18 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.13 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.11 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.39 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.24 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.44 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.43 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.44 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.40 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.47 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.42 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.42 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.06 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.07 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.15 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.12 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.57 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.18 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.52 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.65 secs: 92 bytes ==> GET /
HTTP/1.1 503 0.42 secs: 92 bytes ==> GET /
HTTP/1.1 200 0.09 secs: 75 bytes ==> GET /
HTTP/1.1 200 0.43 secs: 75 bytes ==> GET /
HTTP/1.1 503 0.04 secs: 92 bytes ==> GET /
HTTP/1.1 200 4.15 secs: 74 bytes ==> GET /
HTTP/1.1 200 0.01 secs: 75 bytes ==> GET /
Transactions: 19 hits
Availability: 47.50 %
Elapsed time: 4.16 secs
Data transferred: 0.00 MB
Response time: 0.72 secs
Transaction rate: 4.57 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 3.31
Successful transactions: 19
Failed transactions: 21
Longest transaction: 4.15
Shortest transaction: 0.01
```
我們可以看到在啟用了斷路器后各項性能都有提高。
清理配置。
```bash
istioctl delete routerule recommendation-v1-v2 -n istio-tutorial
istioctl delete -f istiofiles/recommendation_cb_policy_version_v2.yml -n istio-tutorial
```
### Pool Ejection
所謂的 Pool Ejection 就是當某些實例出現錯誤(如返回 5xx 錯誤碼)臨時將該實例彈出一段時間后(窗口期,可配置),然后再將其加入到負載均衡池中。我們的例子中配置的窗口期是 15 秒。
將 v1 和 v2 的流量均分。
```bash
istioctl create -f istiofiles/route-rule-recommendation-v1_and_v2_50_50.yml -n istio-tutorial
```
增加 v2 的實例個數。
```bash
kubectl scale deployment recommendation-v2 --replicas=2 -n istio-tutorial
kubectl get pods -w
```
等待所有的 Pod 的狀態都啟動完成。
現在到 v2 的容器中操作。
```bash
$ kubectl exec recommendation-v2-785465d9cd-225ms -c recommendation /bin/bash
$ curl localhost:8080/misbehave
Following requests to '/' will return a 503
```
增加 Pool Ejection 配置。
```bash
istioctl create -f istiofiles/recommendation_cb_policy_pool_ejection.yml -n istio-tutorial
```
此時再訪問 `customer` 服務。
```bash
$ bin/poll_customer.sh
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 10505
customer => preference => recommendation v2 from '785465d9cd-225ms': 2407
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 10506
customer => preference => recommendation v2 from '785465d9cd-225ms': 2408
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 10507
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 10508
customer => preference => recommendation v1 from '6fc97476f8-m2ntp': 10509
customer => 503 preference => 503 recommendation misbehavior from '785465d9cd-ldc6j'
customer => preference => recommendation v2 from '785465d9cd-225ms': 2409
customer => preference => recommendation v2 from '785465d9cd-225ms': 2410
```
我們看到窗口期生效了,當出現 503 錯誤后至少 15 秒后才會出現第二次。
即使有了負載均衡池彈出策略對于系統的彈性來說依然還不夠,如果你的服務有多個可用實例,可以將**斷路器**、**重試**、**Pool Ejection** 等策略組合起來使用。
例如在以上的 Pool Ejection 的基礎上增加重試策略。
```bash
istioctl replace -f istiofiles/route-rule-recommendation-v1_and_v2_retry.yml -n istio-tutorial
```
現在再訪問 `customer` 服務就看不到 503 錯誤了。
清理配置。
```bash
kubectl scale deployment recommendation-v2 --replicas=1 -n istio-tutorial
istioctl delete routerule recommendation-v1-v2 -n istio-tutorial
istioctl delete -f istiofiles/recommendation_cb_policy_pool_ejection.yml -n istio-tutorial
```
### Egress
Egress 是用來配置 Istio serivce mesh 中的服務對外部服務的訪問策略。
具體配置請參考控制 Egress 流量。
以下示例還有問題,無法正常工作。
構建示例鏡像 egresshttpbin。
```bash
cd egress/egresshttpbin/
mvn clean package
docker build -t jimmysong/istio-tutorial-egresshttpbin:v1 .
docker push jimmysong/istio-tutorial-egresshttpbin:v1
```
部署到 Kubernetes。
```bash
kubectl apply -f <(istioctl kube-inject -f egress/egresshttpbin/src/main/kubernetes/Deployment.yml) -n istio-toturial
kubectl create -f egress/egresshttpbin/src/main/kubernetes/Service.yml
```
為了在 kubernetes 集群外部訪問到該服務,修改增加 ingress 配置并修改本地的`/etc/hosts` 文件,我們在前面已經完成了,此處不再贅述。
構建示例鏡像 egressgithub。
```bash
cd egress/egressgithub
mvn clean package
docker build -t jimmysong/istio-tutorial-egressgithub:v1 .
docker push jimmysong/istio-tutorial-egressgithub:v1
```
部署到 Kubernetes。
```bash
kubectl apply -f <(istioctl kube-inject -f egress/egressgithub/src/main/kubernetes/Deployment.yml) -n istio-tutorial
kubectl create -f egress/egressgithub/src/main/kubernetes/Service.yml
```
增加 Egress 配置。
```bash
istioctl create -f istiofiles/egress_httpbin.yml -n istio-tutorial
```
到 egresshttpbin 容器中測試。
```bash
kubectl exec -it $(oc get pods -o jsonpath="{.items[*].metadata.name}" -l app=egresshttpbin,version=v1) -c egresshttpbin /bin/bash
curl localhost:8080
curl httpbin.org/user-agent
curl httpbin.org/headers
exit
```
增加對 [jimmysong.io](https://jimmysong.io) 的 egress 配置。
```bash
cat <<EOF | istioctl create -f -
apiVersion: config.istio.io/v1alpha2
kind: EgressRule
metadata:
name: jimmysong-egress-rule
namespace: istio-tutorial
spec:
destination:
service: jimmysong.io
ports:
- port: 443
protocol: https
EOF
```
增加 Egress 配置。
```bash
istioctl create -f istiofiles/egress_github.yml -n istio-tutorial
```
到 egressgithub 容器中測試。
```bash
kubectl exec -it $(oc get pods -o jsonpath="{.items[*].metadata.name}" -l app=egressgithub,version=v1) -c egressgithub /bin/bash
curl http://jimmysong:443
exit
```
清理環境。
```bash
istioctl delete egressrule httpbin-egress-rule jimmysong-egress-rule github-egress-rule -n istio-tutorial
```
## 參考
- https://github.com/redhat-developer-demos/istio-tutorial
- [Book - Introducing Istio Service Mesh for Microservices](https://developers.redhat.com/books/introducing-istio-service-mesh-microservices/)
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能