Istio 入門(三):體驗 Istio、微服務部署、可觀測性
本教程已加入 Istio 系列:https://istio.whuanle.cn
3,快速入門
在本章中,我們正式邁入學習 Istio 的第一步。因為 Istio 的知識體系是較為龐大的,因此我們可以先通過本章的入門教程快速了解如何使用 Istio 部署一套微服務,以及 Istio 核心功能的使用方法,了解 Istio 可以為微服務解決什么問題。
在本章中,我們將會學習到如何部署一套微服務、如何使用 Istio 暴露服務到集群外,并且如何使用可觀測性組件監測流量和系統指標。
在后面的章節中,筆者會針對每個 Istio 組件做單獨講解,而在本章中,我們只需要大概了解使用方法即可。
書店微服務
本章教程示例使用的是 Istio 官方的一套微服務,這套微服務是一個在線書店,打開頁面之后會看到一個分類、書的信息以及書的評論,頁面的內容由不同的子服務提供。

書店微服務分為四個單獨的微服務,在上圖中已經使用紅色方框畫出來了。這四個微服務分別是:
productpage: 匯集所有服務的數據,生成瀏覽頁面。details:存儲了書籍的信息,如描述、作者、出版社等。reviews:存儲了書籍相關的評論,但是不包含評分打星。ratings:存儲評論中的評分打星。
在這個微服務中,Productpage 服務對外提供 Web 訪問頁面,而且其它的三個服務只能在集群內部訪問。四個服務分別采用了不同的語言開發,Productpage 聚合其它三個服務的信息生成一個頁面。
在微服務設計中,我們不要每個子服務都暴露端口到集群外部,應該通過一些應用集中數據后給外部顯示。我們可以使用 API 網關,代理子服務一部分接口,然后在 API 網關中實現基于客戶端或第三方調用的身份驗證。
productpage、details、ratings 都只有一個 v1 版本,而 reviews 有三個版本。
ratings 負責給出用戶打分的數據,例如一星、兩星。而 reviews 三個版本分別對 ratings 的數據做以下處理:
- reviews v1:屏蔽星級,不顯示打分;
- reviews v2:顯示星級,使用灰色星星表示,★★★★☆;
- reviews v3:顯示星級,使用紅色星星表示,★★★★☆;
服務依賴圖如下所示:

接下來我們將會使用 Kuubernetes Deployment 部署這些服務,這跟常規的 Kubernetes 部署并無差別。
預先準備
給這些示例服務創建一個命名空間。
kubectl create namespace bookinfo
給命名空間添加 Istio 的標簽,指示 Istio 在部署應用(只對 Pod 起效)的時候,自動注入 Envoy Sidecar Proxy 容器:
kubectl label namespace bookinfo istio-injection=enabled
開啟讓 Istio 注入 Sidecar 有很多種方式,其中一種是給命名空間設置下標簽,在此命名空間下部署的 Pod,會被自動注入 Sidecar 。
你可以從本系列教程的 git 倉庫中找到這些示例,文件位置:https://github.com/whuanle/istio_book/tree/main/3。
倉庫拉取后打開 3 目錄,執行命令進行部署:
kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml
kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml
kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml
kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml
或者你可以參考下面的四個小節,通過手動的方式部署應用,了解每一個應用是如何定義的。
details 應用
存儲了書籍信息的應用。
部署命令:
kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml
使用 Deployment 部署 details 應用。
details_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: details-v1
labels:
app: details
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: details
version: v1
template:
metadata:
labels:
app: details
version: v1
spec:
serviceAccountName: bookinfo-details
containers:
- name: details
image: docker.io/istio/examples-bookinfo-details-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
部署 details。
kubectl -n bookinfo apply -f details_deploy.yaml
為 details 服務配置 Kubernetes Service 。
details_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
service: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
kubectl -n bookinfo apply -f details_svc.yaml
接下來為 details 服務創建一個 ServiceAccount。
Istio 為服務之間的通信提供基于雙向 TLS 的認證,這是是通過給每個 ServiceAccount 創建一個證書實現的,可以使用 ServiceAccount 驗證對方的身份,不同的應用可以共享同一個 ServiceAccount,但是為每個 Deployment 使用單獨的 ServiceAccount 可以更好地組織和管理安全配置。
details_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-details
labels:
account: details
kubectl -n bookinfo apply -f details_sa.yaml
ratings 應用
提供每條評論的打星數據。
部署命令:
kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml
ratings_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ratings-v1
labels:
app: ratings
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: ratings
version: v1
template:
metadata:
labels:
app: ratings
version: v1
spec:
serviceAccountName: bookinfo-ratings
containers:
- name: ratings
image: docker.io/istio/examples-bookinfo-ratings-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
ratings_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: ratings
labels:
app: ratings
service: ratings
spec:
ports:
- port: 9080
name: http
selector:
app: ratings
ratings_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-ratings
labels:
account: ratings
reviews v1/v2/v3 應用
提供書籍的評論信息。
部署命令:
kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml
為三個版本的 reviews 創建三個 Deployment。
reviews_v1_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v1
labels:
app: reviews
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v1
template:
metadata:
labels:
app: reviews
version: v1
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v1:1.17.0
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: {}
reviews_v2_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v2
labels:
app: reviews
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v2
template:
metadata:
labels:
app: reviews
version: v2
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v2:1.17.0
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: {}
reviews_v3_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v3
labels:
app: reviews
version: v3
spec:
replicas: 1
selector:
matchLabels:
app: reviews
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.17.0
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: {}
給三個 Deployment 創建一個 Service,三個相同應用的不同版本共有同一個 Service。
reviews_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: reviews
labels:
app: reviews
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
reviews_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-reviews
labels:
account: reviews
productpage 應用
頁面聚合服務,供用戶瀏覽書籍信息。
部署命令:
kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml
productpage_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: productpage-v1
labels:
app: productpage
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: productpage
version: v1
template:
metadata:
labels:
app: productpage
version: v1
spec:
serviceAccountName: bookinfo-productpage
containers:
- name: productpage
image: docker.io/istio/examples-bookinfo-productpage-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
securityContext:
runAsUser: 1000
volumes:
- name: tmp
emptyDir: {}
productpage_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
service: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
productpage_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-productpage
labels:
account: productpage
檢查
執行命令完成后,查看 bookinfo 命名空間下的 Pod。
kubectl get pods -n bookinfo

可以看到,每個 Pod 的 READY 屬性都是 2/2 ,這表示該 Pod 中有兩個容器,并且當前有兩個容器已經就緒。
如果我們查看其中一個 Pod 的組成結構,會發現有 Pod 被塞進了一個 istio-proxy 容器。

如果 Kubernetes 中沒有安裝 Dashbooard ,那么可以使用
kubectl -n bookinfo describe pod {Pod ID}查看組成結構。
接著使用 kubectl -n bookinfo get svc 查看 Service,四個微服務都已經被注冊了 Service。

然后我們訪問 productpage 對應的 CLUSTER-IP:
curl 10.233.37.130:9080
默認 Istio 不會開啟零信任雙向認證模式,因此在集群內可以自己訪問應用。如果開啟了 mTLS 雙向認證模式,則只能在 Pod 中訪問應用。
可以看到返回了一堆 html,說明我們的部署是正常的。
臨時訪問
接著為了查看頁面效果,我們在暫未使用 Istio-ingressgateway 之前,臨時創建一個 Service 暴露 productpage 的頁面。
productpage_tmpsvc.yaml
apiVersion: v1
kind: Service
metadata:
name: productpagetmp
labels:
app: productpage
service: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
type: NodePort
kubectl -n bookinfo apply -f productpage_tmpsvc.yaml
查看所有 Service:
root@k8smain:/data/learn/book# kubectl -n bookinfo get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.233.63.247 <none> 9080/TCP 40m
productpage ClusterIP 10.233.37.130 <none> 9080/TCP 23m
productpagetmp NodePort 10.233.47.14 <none> 9080:30258/TCP 77s
ratings ClusterIP 10.233.7.6 <none> 9080/TCP 36m
reviews ClusterIP 10.233.58.219 <none> 9080/TCP 23m
然后在頁面中訪問 30258 端口(大家的端口不一樣,按自己的來)。

接著打開 http://192.168.3.150:30258/productpage?u=normal。
因為當前使用 Service 綁定 Pod,因此會使用輪詢實現負載均衡,你可以多次刷新 http://192.168.3.150:30258/productpage?u=normal,會查到右側的評分星星有所變化。
Istio 默認情況下使用輪詢負載均衡的方法。
頁面右側評論顯示規則是 無星星 => 黑色星星 => 紅色星星。

部署入口網關
什么是 Gateway
終于來到體驗 Istio 的時刻了,在本小節中,我們將會為 productpage 創建 Istio Gateway,對外提供網頁訪問。
在第二章中,我們已經部署了 istio-ingressgateway,這個組件起到了類似 nginx、apisix 的效果,對外提供端口訪問,然后將流量轉發到內部服務中。
但是 istio-ingressgateway 并不能直接轉發流量到 Pod,它還需要進行一些配置。我們要為 productpage 創建一個站點,綁定對應的域名,這樣外部訪問 istio-ingressgateway 的端口時,istio-ingressgateway 才知道該將流量轉發給誰。在 Istio 中,定義這種綁定關系的資源叫 Gateway。
后面的章節會解釋清楚,這里大概了解即可。

Gateway 類似 Nginx 需要創建一個反向代理時需要綁定的域名配置。
部署 Gateway
創建一個 Gateway,綁定域名入口。
ingress_gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
hosts表示對外開放的訪問路徑,你可以綁定域名、IP 等。這里使用*,表示所有訪問都可以進入此網關。
kubectl -n bookinfo apply -f ingress_gateway.yaml
這一步就像 nginx 的監聽配置:
server {
listen 80;
server_name example.org www.example.org;
#...
}
當我們創建 Istio Gateway 之后,istio-ingressgateway 會為我們監控流量,檢測不同的域名或端口屬于哪個 Istio Gateway 。

部署 VirtualService
什么是 VirtualService
雖然創建了 Istio Gateway,但是我們還不能直接通過網關訪問到前面部署的微服務,我們還需要創建 Istio VirtualService 將 Istio Gateway 跟對應的 Kubernetes Service 綁定起來,然后流量才能正式流向 Pod。

請一定要注意這里,流量實際并不會經過 Service 中,但是 VirtualService 需要通過 Service 來發現 Pod。
這里類似 nginx 配置反向代理,配置監聽之后,還需要指向將請求映射到哪個地址。
server {
listen 80;
server_name example.org www.example.org;
#...
}
location /some/path/ {
proxy_pass http://A:9080;
}
為什么不直接將 Gateway 跟 Service 綁定,而是中間加個 VirtualService 呢?有句話叫做,計算機領域中的問題,都可以通過增加一個層來解決。
VirtualService 的主要目標是為服務提供穩定的入口地址,并通過配置一系列的路由規則來控制流量在網格內的行為。
就以最簡單的路由區配來說,Kubernetes Service 是不支持路由規則的,而 Istio 可以通過指定路由后綴中;Service 不支持流量分析,負載均衡只有輪詢。而 Istio 利用 Service 來發現 Pod,然后直接將流量轉發到 Pod 中,可以實現各種功能。
VirtualService 可以用于實現以下功能:
請求路由:將請求路由到特定的服務或版本,例如將請求分發到不同版本的服務,以實現灰度發布或金絲雀發布。
請求重試:為失敗的請求配置重試策略,以提高服務的可用性。
請求超時:設置請求的超時時間,以便在特定時間內沒有得到響應時中斷請求。
請求鏡像:將請求的副本發送到另一個服務,用于測試新版本的服務,而不影響實際的生產流量。
流量分割:將流量按照特定的比例分發到不同的服務或版本,以實現流量控制。
部署 VirtualService
productpage_vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
kubectl -n bookinfo apply -f productpage_vs.yaml
關于 VistualService 中各種配置的作用,在 4.1 章節中會有介紹。
這里的 YAML 分為兩大部分,第一部分是 http.match,表示暴露了哪些 API 地址,外部訪問時只能訪問到這些地址。

可以通過 http.match 限制集群外部訪問此地址時所能使用的 URL。
然后通過 http.route 綁定 Kubernetes Service ,通過 Service 中的服務發現,將流量轉發到對應的 Pod 中。

host 這里,由于 VirtualService 跟 Service/Pod 在同一個命名空間中,所以只需要配置 Service 的名稱即可,如果要跨命名空間訪問,則需要加上完整的命名空間名稱。
什么是 DestinationRule
在本章中,會提前預告 DestinationRule,下一章才會使用 DestinationRule,這里我們知道還有 DestinationRule 這個東西即可。
Istio VistualService 中可以限制外部能夠訪問的路由地址,而 DestinationRule 則可以配置訪問的 Pod 策略。可以為 Istio VistualService 綁定一個 Istio DestinationRule,通過 DestinationRule 我們還可以定義版本子集等,通過更加豐富的策略轉發流量。

由于只暴露了五個地址,所以外部直接訪問
/,是打不開頁面的。
檢查
為了確保網關沒問題,我們需要執行 Istio 命令查看日志:
istioctl analyze
然后我們查看為 productpage 創建的網關。
root@k8smain:/data/learn/book# kubectl get gw -A
NAMESPACE NAME AGE
bookinfo bookinfo-gateway 26m
Kubernetes 本身也有一個 Gateway,因此不能使用
kubectl get gateway來獲取 Istio 的 Gateway,而是使用簡寫gw。
然后查看 VistualService。
root@k8smain:/data/learn/book# kubectl get vs -A
NAMESPACE NAME GATEWAYS HOSTS AGE
bookinfo bookinfo ["bookinfo-gateway"] ["*"] 79m
在第二章中,我們通過 Helm 部署了 istio-ingressgateway,其訪問端口如下:

在本節部署 bookinfo-gateway 的時候,我們使用了端口 80,因此不需要另外配置 ,直接通過 istio-ingressgateway 的 32309 端口訪問即可。
訪問時一定需要帶
/productpage,因為我們并沒有放通/。

嘗試修改 Gateway 端口
如果需要更換端口,可以修改 istio-ingressgateway 的 Service,增加新的端口映射。
kubectl edit svc istio-ingressgateway -n istio-system

然后修改前面的 ingress_gateway.yaml,將端口從 80 改成 666 。

即可通過 32666 端口訪問到此微服務。
示例:http://192.168.3.150:30666/productpage


浙公網安備 33010602011771號