---
# System prepended metadata

title: 地瓜のIstio學習筆記－1

---

# 地瓜のIstio學習筆記－1

## 環境

Kubernates 1.18.2
CentOs 7

安裝 k8s 的部分就不詳細說明了，網路上教學很多，不想裝 k8s 的話 Istio 也很提供很多[安裝選項](https://istio.io/latest/docs/setup/platform-setup/)。

## 安裝 Istio

因為我很懶，所以就下載 [Istio release](https://github.com/istio/istio/releases) 後用 istioctl 安裝，當然還有其他安裝方法，像是 Operator，不過實際上這兩種方法背後做的事情是一樣的。

```bash
$ curl -L https://istio.io/downloadIstio | sh -
```

然後把 istioctl 加到 path 來啟用 istioctl 指令。

```bash
export PATH=/root/istio-1.7.0/bin:$PATH
```

接著我們就能用 istioctl 的指令來對 k8s 安裝 istio 了!

Istio 有提供很多 profile 供我們進行快速安裝，如下

```bash
[00:13:12]:root <3 ~ > istioctl profile list
Istio configuration profiles:
    default
    demo
    empty
    minimal
    preview
    remote
```

那這些 profile 到底會安裝那些東西呢？根據官方的說明

1) **default profile** 會根據 [`IstioOperator`API](https://istio.io/latest/docs/reference/config/istio.operator.v1alpha1/) 的預設值進行安裝
2) **demo profile** 顧名思義就是拿來做 demo 用的
3) **minial profile** 只安裝了 Istio traffic management 需要使用的組件，也就是 istiod
4) **remote profile** 拿來安裝在 multi-cluster 中設定 remote cluster 的 Istio

上面提到這四個 profile 安裝了那些組件用表格來表示的話會是這樣：

<table>
    <thead>
        <tr>
            <th></th>
            <th>default</th>
            <th>demo</th>
            <th>minimal</th>
            <th>remote</th>
        </tr>
    </thead>
    <tbody>
        <tr><td><code>istio-egressgateway</code></td><td></td><td>X</td><td></td><td></td></tr>
        <tr><td><code>istio-ingressgateway</code></td><td>X</td><td>X</td><td></td><td></td></tr>
        <tr><td><code>istiod</code></td><td>X</td><td>X</td><td>X</td><td></td></tr>
    </tbody>
</table>

那剩下的 **empty profile** 跟 **preview profile** 是拿來幹嘛用的呢？相信聰明的各位一定都猜的到，preview profile 就是 Istio 把正在實驗中的 feature 阿，全都放在這個 profile 裡面了。

那 empty profile 就比較特殊一些，他甚麼都不 deploy，因為 Istio 有提供客製化 profile 的功能，empty profile 就是拿來給我們自製 profile 時使用的基底。

那我們就直接來下指令吧
```bash
[00:19:27]:root <3 ~ > istioctl install --set profile=demo
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete
```

根據文件告訴我們，demo profile 應該會安裝三個組件，分別是 Istiod、Ingress gateway 與 Egress gateway，那我們就來確認看看吧!

```bash
[00:19:53]:root <3 ~ > kubectl get pod -n istio-system
NAME                                    READY   STATUS    RESTARTS   AGE
istio-egressgateway-695f5944d8-xbr9m    1/1     Running   0          28s
istio-ingressgateway-5c697d4cd7-2rsw8   1/1     Running   0          29s
istiod-59747cbfdd-v9mzk                 1/1     Running   0          34s
```

到這邊 Istio 的安裝就算是告一個段落了，接下來我們會部屬一些服務，來體驗一下 Istio 許多的強大功能。

## Demo - Book Application

接下來我們會先照 [Istio start](https://) 中的內容進行 demo，讓我們先看看 Istio 能做到那些事情之後，再來深入的看看 Istio 是如何做到的，好，那我們要開始囉!

我們先對 default namespece 打上 `istio-injection=enabled` 的 label，這樣 Istio 就會自動幫這個 namespace 裡面的 pod 注入 sidecar container。當然 Istio 也有提供其他方法注入 sidecar，例如用 `istioctl kube-inject` 指令修改 deployment yaml。

```
[00:20:07]:root <3 ~ > kubectl label namespace default istio-injection=enabled
namespace/default labeled
```

### 部屬服務

在 demo 中，我們會部屬一系關於**書**的服務，分別是 detail-v1、ratings-v1、reviews-v1~v3、productpage-v1，總共會產生 6 個 pod。

```
[00:20:07]:root <3 ~ > 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
```

接著我們來確認一下服務的狀態。

```
[00:20:07]:root <3 ~ > kubectl get po -n default
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-558b8b4b76-rf2q9       2/2     Running   2          5d
productpage-v1-6987489c74-kkgb6   2/2     Running   2          5d
ratings-v1-7dc98c7588-lq5rd       2/2     Running   2          5d
reviews-v1-7f99cc4496-kgnmt       2/2     Running   2          5d
reviews-v2-7d79d5bd5d-fcn6h       2/2     Running   2          5d
reviews-v3-7dbcdcbc56-jfpd8       2/2     Running   2          5d
```

好的，我們可以看到服務都部屬好了，不知道各位有沒有發現哪裡有點不一樣。

是的，在 `READY` 那一行，我們會發現跟 Istio 幫我們注入了 sidecar，所以顯示 `2/2` 的狀態，那我們怎麼知道這是 Istio 幫我們做的呢？其實很簡單，直接來看一下 deployment 就可以知道答案了。

```
[02:32:08]:root <3 ~ > kubectl describe deployments details
Name:                   details-v1
Namespace:              default
CreationTimestamp:      Thu, 10 Sep 2020 01:58:36 +0800
Labels:                 app=details
                        version=v1
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=details,version=v1
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:           app=details
                    version=v1
  Service Account:  bookinfo-details
  Containers:
   details:
    Image:        docker.io/istio/examples-bookinfo-details-v1:1.16.2
    Port:         9080/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   details-v1-558b8b4b76 (1/1 replicas created)
Events:          <none>
```

從 details 這個 deployments 中，可以看到我們**只宣告了一個 container** ，但實際上 pod 裡面是有兩個 container 的，那多出來的那個 container 從哪裡來的應該就很清楚了。

那 Istio 是怎麼把這個 sidecar 塞進去的，大家先不要緊張，這邊賣個關子，後面會為各位詳細的解說。

接下來呢，我們來驗證一下服務是不是正常運作。

```
[02:57:33]:root <3 ~ > kubectl get svc -l app=productpage
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
productpage   ClusterIP   10.107.148.13   <none>        9080/TCP   5d

[02:58:02]:root <3 ~ > curl -s 10.107.148.13:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
```

### 接入 Istio service mesh

在確認服務都運行成功之後，如果要讓 Kubernetes 能夠從 cluster 外面訪問 ，我們會需要設置一個 ingress，讓流量能從外部流入，這樣我們才能使用瀏覽器來看 demo 的頁面，在前面我們安裝 Istio 的 `demo profile` 的時候，就已經部屬了一個叫 `istio-ingressgateway` 的 pod，它呢是一個特殊的 Envoy proxy，對外開了一些 node port，除此之外其他跟 Envoy sidecar 的功能是一樣的。

所以呢，我們要找出能夠連入 Kubernetes 的 ip 和 port，在 namespace `istio-system` 裡面，我們找出 `istio-ingressgateway` 的 service，它的 `TYPE` 是 `LoadBalancer`，看一下這個 service 的 `EXTERNAL-IP` 的欄位，如果 `EXTERNAL-IP` 的值是 `<none>` 或是 `<pending>` ，說明這個 service 並沒有配置外部 IP，那我們就需要用 node port 的方式訪問 Kubernetes。

```bash
[01:30:40]:root <3 ~/istio-1.7.0 > kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)
                                         AGE
istio-ingressgateway   LoadBalancer   10.110.59.204   <pending>     15021:30734/TCP,80:32765/TCP,443:31940/TCP,31400:31906/TCP,15443:30246/TCP   5d23h
```

目前呢，我的 Kubernetes 並沒有配置 ELB（External Loadbalancer），所以 `EXTERNAL-IP` 的值是空的，根據官方文件，我們可以下面的指令來找出 http2 與 https 對應的 node port 來訪問 Kubernetes。

```bash
$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
```

然後把 host ip 找出來

```bash
$ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
```

把 ip 和 port 組合起來，就是我們的訪問 Kubernetes 的 Url 了。

```bash
$ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
$ echo $GATEWAY_URL
192.168.8.128:32765
```

OK，這個 Url 呢，只代表了我們的流量能從外部流到 Kuberates，但是呢，這個流量並不會流入我們的 mesh 中，那我們要怎麼做到這件事情呢？Istio 提供一種 resource type 叫做 `Gateway`，它允許我們控制流量流入或是流出我們的 service mesh，我們可以這麼說，**通過 `Gateway` 前的流量，都算是在 service mesh 外，只有通過了 `Gateway` 之後，才算是進入了 service mesh**。

所以呢，為了要讓流量流進我們上面部屬的服務，勢必要有一個 `Gateway` 存在。

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

藉由上面的指令呢，我們就成功的部屬了一個 `Gateway` 到 Istio 中，聰明的你們可能已經發現，上面的 `samples/bookinfo/networking/bookinfo-gateway.yaml` 好像不是只有建立 `Gateway` 而已，好像還偷偷建了一個叫做 `VirtualService` 的東西，阿剛剛不是說建 `Gateway` 就可以了，怎麼這裡又多了一個 `VirtualService` 呢？

好的，讓我們來看看官方怎麼說。

> The Gateway specification above describes the L4-L6 properties of a load balancer. A VirtualService can then be bound to a gateway to control the forwarding of traffic arriving at a particular host or gateway port.

官方文件告訴我們，`Gateway` 只負責 L4-L6 的 load balance，所以我們需要將 `VirtualService` 綁在 `Gateway` 上來補足 L7 的 load balance。

有這些觀念之後，我們就直接來看 `samples/bookinfo/networking/bookinfo-gateway.yaml` 裡面到底做了甚麼，相信看完內容大家應該就會清楚不少。

我們先來看 `Gateway` 的部分

```yaml
# samples/bookinfo/networking/bookinfo-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:
    - "*"  
```

那從上面的 yaml 可以看到，它 select 了有 istio=ingressgateway 這個 label 的 pod 去配置裡面的 Envoy 設定，它會配置這個 pod 的 Envoy 讓符合 servers 規則的流量能夠流進 service mesh 裡面。

這邊呢，它允許任意的 host name，只要是經由 HTTP protocol 的 80 port，流到 istio-system 中 istio-ingressgateway 這個 pod 的流量都能進入 service mesh。

搞懂了 `Gateway` 是如何允許流量流入 mesh 中之後，接下來我們要看，這些流入 mesh 中的流量，最終會被導去哪裡，讓我們來看 `VirtualService` 的 yaml。

```yaml
# samples/bookinfo/networking/bookinfo-gateway.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.default.svc.cluster.local 
        # 這邊原本是只有寫 productpage 我為了方便大家理解直接寫 FQDN 
        port:
          number: 9080
```

這個 `VirtualService` 相對簡單，我們可以看到它把自己 bind 在 `bookinfo-gateway` 上，接著根據 http 的 uri match 規則，將符合規則的流量導到對應的目的地，`productpage.default.svc.cluster.local` 的 9080 port。

### View the result !

不知道大家還記不記得我們上面找到用來從外部訪問 Kubernates 的 URL。

```bash
$ echo $GATEWAY_URL
192.168.8.128:32765
```

根據我們剛剛對 `Gateway` 以及 `VirtualService` 的理解，現在我們訪問 `$GATEWAY_URL/productpage` 的時候，這個流量應該就要被轉發到 `productpage.default.svc.cluster.local:9080/productpage` 上。

那我們來驗證一下是不是這樣
```bash
[02:03:58]:root <3 ~ > curl -s 10.107.148.13:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
[02:07:45]:root <3 ~ > curl -s 192.168.8.128:32765/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
```

那我們也可以從 browser 直接看到我們部屬的服務頁面，大家如果多重整幾次的話，應該會發現 Reviewer 的星星會是紅色、黑色或是不出現。

![](https://i.imgur.com/2l9It0e.png)

從 Kiali dashborad 也能看到我們服務的拓樸圖跟流量。

![](https://i.imgur.com/OyQBdqI.png)

那 Kiali 等等 dashboard 的部屬，可以用下面的指令完成。

```bash
$ kubectl apply -f samples/addons
$ while ! kubectl wait --for=condition=available --timeout=600s deployment/kiali -n istio-system; do sleep 1; done
```

用 istioctl 帶起 Kiali dashboard 之後，就能訪問上面的 UI 了。

```bash
$ istioctl dashboard kiali
```

那麼基礎的 Istio 安裝跟 application demo 就大概到這邊告一段落，下一篇應該會寫一下怎麼把 Kiali dashboard 給接出來到外面，利用上面有提到的東西應該就都能做到了，有興趣的大家可以先試試看喔。