本教程已加入 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