zaki work log

作業ログやら生活ログやらなんやら

[Kubernetes] Istio (v1.6.5) Getting Started (オンプレK8sでBookInfoを動かしてwebアクセス)

Istioを入れてサービスメッシュなサンプルアプリ(BookInfo)を動かすところまで。

環境

こっちで構築したMetalLBでtype:LoadBalancer Serviceが使えるオンプレK8sです。

zaki-hmkc.hatenablog.com

[zaki@cloud-dev ~]$ kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Com
piler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Com
piler:"gc", Platform:"linux/amd64"}
[zaki@cloud-dev ~]$ kubectl get node
NAME                        STATUS   ROLES    AGE   VERSION
k8s-master01.esxi.jp-z.jp   Ready    master   22d   v1.18.5
k8s-worker01.esxi.jp-z.jp   Ready    <none>   22d   v1.18.5
k8s-worker02.esxi.jp-z.jp   Ready    <none>   22d   v1.18.5

デフォルトでtype:LoadBalancer Serviceを使って外部アクセス用のIngressgatewayが作成されるので、使えるようにしておくこと。

ちなみにOpenShiftのサービスメッシュの場合は、インストール(OperatorHubを使用)もサイドカーインジェクション(namespaceのラベルでなくCRMでnamespace指定してアノテーション指定する)も外部アクセス(デフォルトではrouteが提供される)若干異なるのでドキュメントをよく確認すること。
アノテーション等の変更点に関連してBookInfoのマニフェストも若干異なる

Istio

単純な構成なKubernetesにIstioを入れるには、現バージョンでは

  • istioctlを使用
  • Operatorを使用

の2パターンが提供されている。
(以前はHelmを使ったインストールもあったが、現在はdeprecated)

今回はGetting Startedに沿ってやっていくので、istioctlを使用したインストールについて。
以下、2020.07.26時点の、Istio 1.6.5について記載。

istio.io

インストール

流れとしては、まずistioctlを操作用ノード(kubectlなど叩くノードであればよく、K8sのノードである必要はない)にインストール。そしてistioctl installを実行してK8sクラスタへIstioをインストールする。

download istioctl

[zaki@cloud-dev ~]$ mkdir -p local/istio
[zaki@cloud-dev ~]$ cd local/istio/
[zaki@cloud-dev istio]$ curl -L https://istio.io/downloadIstio | sh -
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   107  100   107    0     0    169      0 --:--:-- --:--:-- --:--:--   169
100  3896  100  3896    0     0   3741      0  0:00:01  0:00:01 --:--:-- 3804k
Downloading istio-1.6.5 from https://github.com/istio/istio/releases/download/1.6.5/istio-1.6.5-linux.tar.gz ...Failed.

Trying with TARGET_ARCH. Downloading istio-1.6.5 from https://github.com/istio/istio/releases/download/1.6.5/istio-1.6.5-linux-amd64.tar.gz ...

Istio 1.6.5 Download Complete!

Istio has been successfully downloaded into the istio-1.6.5 folder on your system.

Next Steps:
See https://istio.io/docs/setup/kubernetes/install/ to add Istio to your Kubernetes cluster.

To configure the istioctl client tool for your workstation,
add the /home/zaki/local/istio/istio-1.6.5/bin directory to your environment path variable with:
         export PATH="$PATH:/home/zaki/local/istio/istio-1.6.5/bin"

Begin the Istio pre-installation verification check by running:
         istioctl verify-install

Need more information? Visit https://istio.io/docs/setup/kubernetes/install/

ダウンロードが完了すると

[zaki@cloud-dev istio]$ istio-1.6.5/bin/istioctl version
no running Istio pods in "istio-system"
1.6.5
[zaki@cloud-dev istio]$ ls -F
istio-1.6.5/

このとおり。

一時的にPATH設定(あとでちゃんと~/.bashrcとかに書きましょう)

[zaki@cloud-dev istio-1.6.5]$ export PATH=$PWD/bin:$PATH
[zaki@cloud-dev istio-1.6.5]$ istioctl version
no running Istio pods in "istio-system"
1.6.5

install Istio

Istio本体をK8sクラスタへデプロイする。
使用するプロファイルはdemo

[zaki@cloud-dev istio-1.6.5]$ time istioctl install --set profile=demo
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-acc
ount-tokens for details.
✔ Istio core installed
✔ Istiod installed
- Processing resources for Addons, Egress gateways, Ingress gateways. Waiting for Deployment/istio-system/grafana, Deployment/istio-system/istio-egressgateway, Deployment/istio-system/istio-ingressgateway, ...

インストール処理が完了するとこんな感じ。

[zaki@cloud-dev istio-1.6.5]$ time istioctl install --set profile=demo
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-acc
ount-tokens for details.
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Addons installed
✔ Installation complete
real    4m3.635s
user    0m7.524s
sys     0m0.934s
[zaki@cloud-dev istio-1.6.5]$

クラスタのpodとserviceの状態は以下の通り。

[zaki@cloud-dev istio-1.6.5]$ kc get pod,svc -n istio-system
NAME                                        READY   STATUS    RESTARTS   AGE
pod/grafana-b54bb57b9-nl4zn                 1/1     Running   0          10m
pod/istio-egressgateway-7486cf8c97-l5mtk    1/1     Running   0          10m
pod/istio-ingressgateway-6bcb9d7bbf-4sg9p   1/1     Running   0          10m
pod/istio-tracing-9dd6c4f7c-cl2cf           1/1     Running   0          10m
pod/istiod-788f76c8fc-q4bws                 1/1     Running   0          12m
pod/kiali-d45468dc4-jlshb                   1/1     Running   0          10m
pod/prometheus-6477cfb669-xwc6d             2/2     Running   0          10m

NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                                                                      AGE
service/grafana                     ClusterIP      10.99.190.212    <none>          3000/TCP                                                                     10m
service/istio-egressgateway         ClusterIP      10.102.52.210    <none>          80/TCP,443/TCP,15443/TCP                                                     10m
service/istio-ingressgateway        LoadBalancer   10.108.43.69     192.168.0.182   15020:32605/TCP,80:30286/TCP,443:32689/TCP,31400:32053/TCP,15443:31536/TCP   10m
service/istiod                      ClusterIP      10.103.63.200    <none>          15010/TCP,15012/TCP,443/TCP,15014/TCP,853/TCP                                12m
service/jaeger-agent                ClusterIP      None             <none>          5775/UDP,6831/UDP,6832/UDP                                                   10m
service/jaeger-collector            ClusterIP      10.108.183.184   <none>          14267/TCP,14268/TCP,14250/TCP                                                10m
service/jaeger-collector-headless   ClusterIP      None             <none>          14250/TCP                                                                    10m
service/jaeger-query                ClusterIP      10.106.205.30    <none>          16686/TCP                                                                    10m
service/kiali                       ClusterIP      10.104.53.76     <none>          20001/TCP                                                                    10m
service/prometheus                  ClusterIP      10.98.172.87     <none>          9090/TCP                                                                     10m
service/tracing                     ClusterIP      10.96.197.30     <none>          80/TCP                                                                       10m
service/zipkin                      ClusterIP      10.106.80.121    <none>          9411/TCP                                                                     10m

表示されている通り、istio-ingressgatewayがtype:LoadBalancer Serviceとしてデプロイされており、このIngressサービスがクラスタ外からのトラフィックをIstioで動くpodへルーティングしてくれるようになる。

サンプルアプリケーションのデプロイ

IstioにはBookInfoというアプリがあり、Pythonで動作するフロントエンドのProductpageJava/Ruby/Node.jsで動作するバックエンドのReviews/Details/Ratingsという構成になっていて、それぞれIstioを使ってpod間通信が行われる構成になっている。

istio.io

namespace作成

Istioを使用したアプリケーションのデプロイは、デプロイするnamespaceのラベルにistio-injection=enabledを付与する。
これを行うことで、自動的にpodにistio-proxyサイドカーがインジェクションされ、これがpod間通信を肩代わりする。

例としてbookinfo-sampleを手動で作成するには

$ kubectl create namespace bookinfo-sample
$ kubectl label namespaces bookinfo-sample istio-injection=enabled

マニフェストを作るならこんな感じ。

apiVersion: v1
kind: Namespace
metadata:
  labels:
    istio-injection: enabled
  name: bookinfo-sample

describeしてlabelが設定されてあればOK

$ kc describe ns bookinfo-sample 
Name:         bookinfo-sample
Labels:       istio-injection=enabled
Annotations:  Status:  Active

No resource quota.

No LimitRange resource.

デプロイ

といっても、istioctlにマニフェストが同梱されているのでapplyすればOK。
ファイルの場所は、istioctlを展開したディレクトリのsamples/bookinfo/platform/kube/bookinfo.yamlにある。
現在のv1.16バージョンはGitHubのここのはず。

[zaki@cloud-dev istio-1.6.5]$ kc apply -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookinfo-sample 
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
[zaki@cloud-dev istio-1.6.5]$ kc get pod -n bookinfo-sample 
NAME                              READY   STATUS            RESTARTS   AGE
details-v1-6c9f8bcbcb-7rx6s       0/2     PodInitializing   0          7s
productpage-v1-7df7cb7f86-8zf6b   0/2     Init:0/1          0          6s
ratings-v1-65cff55fb8-pw42c       0/2     Init:0/1          0          7s
reviews-v1-7bccdbbf96-dhpgr       0/2     Init:0/1          0          7s
reviews-v2-7c9685df46-wcm7r       0/2     Init:0/1          0          7s
reviews-v3-58fc46b64-jqls6        0/2     PodInitializing   0          7s

この通り、コンテナ数が2/2となっており、1pod2コンテナとなっている。
マニフェストを見ればわかるけど、Deploymentの定義には複数のコンテナの記述はなく、namespaceのistio-injection=enabledのラベルによって、Istioが自動的にコンテナを追加していることがわかる。

しばらく待てばこの通り。

[zaki@cloud-dev istio-1.6.5]$ kc get pod -n bookinfo-sample 
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-6c9f8bcbcb-7rx6s       2/2     Running   0          2m25s
productpage-v1-7df7cb7f86-8zf6b   2/2     Running   0          2m24s
ratings-v1-65cff55fb8-pw42c       2/2     Running   0          2m25s
reviews-v1-7bccdbbf96-dhpgr       2/2     Running   0          2m25s
reviews-v2-7c9685df46-wcm7r       2/2     Running   0          2m25s
reviews-v3-58fc46b64-jqls6        2/2     Running   0          2m25s
[zaki@cloud-dev istio-1.6.5]$ kc logs productpage-v1-7df7cb7f86-8zf6b -n bookinfo-sample 
error: a container name must be specified for pod productpage-v1-7df7cb7f86-8zf6b, choose one of: [productpage istio-proxy] or one of the init containers: [istio-init]

このように、istio-proxyというコンテナが追加されている。

podへの外部からのアクセス設定

デプロイされたBookInfoアプリケーションに外部からアクセスするには、前にも少し書いたIstio Ingressgateway。

[zaki@cloud-dev istio-1.6.5]$ kc get svc -n istio-system istio-ingressgateway 
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                                                                      AGE
istio-ingressgateway   LoadBalancer   10.108.43.69   192.168.0.182   15020:32605/TCP,80:30286/TCP,443:32689/TCP,31400:32053/TCP,15443:31536/TCP   36m

ただし、まだBookInfoのアプリケーションをデプロイしただけで、Istio IngressgatewayからBookInfoへトラフィックを転送する設定関連はまだ何もないので、これをデプロイする必要がある。

Istioの外部からのトラフィックコントロールには、Gateway, VirtualServiceというリソースがあり、この定義内容によってIstio Ingressgatewayへのアクセスが各アプリケーション(今回はBookInfo)へ転送される動きになる。

BookInfo用の定義ファイルはsamples/bookinfo/networking/bookinfo-gateway.yamlにある。
GitHubだとこちら

[zaki@cloud-dev istio-1.6.5]$ kc apply -f samples/bookinfo/networking/bookinfo-gateway.yaml -n bookinfo-sample 
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created

デプロイすると、gateway,virtualserviceリソースが作成される。

[zaki@cloud-dev istio-1.6.5]$ kc get gateway,virtualservice -n bookinfo-sample
NAME                                           AGE
gateway.networking.istio.io/bookinfo-gateway   3s

NAME                                          GATEWAYS             HOSTS   AGE
virtualservice.networking.istio.io/bookinfo   [bookinfo-gateway]   [*]     3s

設定におかしいところがないか確認

[zaki@cloud-dev istio-1.6.5]$ istioctl analyze -n bookinfo-sample
✔ No validation issues found when analyzing namespace: bookinfo-sample.

外部からのアクセス

じゃあ外部からアクセスしてみましょう。
先の答えを書いておくと、istio-ingressgatewayのEXTERNAL-IPのホストに対して、/productpageというパスにアクセスすれば以下のようにBookInfoアプリが表示されるはずです。

f:id:zaki-hmkc:20200726181418p:plain

アクセス設定について

(以下、自分の解釈を書いてるので間違ってるかも)

まず、アクセス先のホストはistio-systemネームスペースのistio-ingressgatewayのEXTERNAL-IPのアドレス。

[zaki@cloud-dev istio-1.6.5]$ kc get svc -n istio-system istio-ingressgateway 
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                                                                      AGE
istio-ingressgateway   LoadBalancer   10.108.43.69   192.168.0.182   15020:32605/TCP,80:30286/TCP,443:32689/TCP,31400:32053/TCP,15443:31536/TCP   36m

Gateway

ただし、どのポートのどのパスにアクセスするかは、アプリケーション次第。
まず確認するのはGatewayリソース

[zaki@cloud-dev istio-1.6.5]$ kc get gw -n bookinfo-sample  bookinfo-gateway -o yaml
:
:
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP

これはIstio Ingressgatewayに対して『「ホスト名は*ワイルドカード指定で任意」「ポートは80/TCP」「プロトコルはHTTP」のアクセスであれば自分(この場合BookInfo)がトラフィックを貰うよ』という定義になっている。

VirtualService

そしてVirtualServiceリソース

[zaki@cloud-dev istio-1.6.5]$ kc get vs -n bookinfo-sample bookinfo -o yaml
:
:
spec:
  gateways:
  - bookinfo-gateway
  hosts:
  - '*'
  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

これは前述のGatewayリソースのbookinfo-gatewayの定義を使用しつつ、ホスト名は任意・プロトコルhttpで、更に特定のHTTPリクエストパスであれば、productpageの9080/TCPトラフィックを転送するよ、という定義になっている。

productpageの9080/TCPというのは、このServiceのこと。

[zaki@cloud-dev ~]$ kc get svc -n bookinfo-sample 
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.105.9.42     <none>        9080/TCP   59m
productpage   ClusterIP   10.98.40.227    <none>        9080/TCP   59m
ratings       ClusterIP   10.96.130.215   <none>        9080/TCP   59m
reviews       ClusterIP   10.110.12.172   <none>        9080/TCP   59m

これで、「Istio Ingressgatewayが80/TCPで受けたトラフィック(細かく言うと、更にHTTPリクエストパスが/productpage,/api/v1/productsなど特定の場合)」を「productpageの9080/TCPへ転送する」という流れになる。


Istio IngressgatewayのService?pod?

ちなみにこの構成の場合、Istio Ingressgatewayのtype:LoadBalancer Serviceは80/TCPで外部からのトラフィックを受けているけど、

spec:
  clusterIP: 10.108.43.69
  externalTrafficPolicy: Cluster
  ports:
  - name: http2
    nodePort: 30286
    port: 80
    protocol: TCP
    targetPort: 8080

こんな定義になっているので、このServiceによるトラフィックはまずIngressGatewayのpod自体は8080/TCPで受けてるので、Gatewayの設定は本来は80じゃなくて8080じゃないのかなーと思ったり…

$ kubectl edit gateway -n bookinfo-sample bookinfo-gateway

でポートを8080に変更しても動く。

ホストのワイルドカード

現状BookInfoしか動かしてないので特に問題ないけど、Gateway/VirtualServiceのhosts: '*'は「どのホスト名でもBookInfoが横取り」してしまうので、複数アプリケーションを(異なるFQDNで)動かす場合は、きちんと設定する必要がある。

とりあえずIstioで(期待するアプリケーションのアクセスできずに)最初にハマるところのようなきがする。

Kiali

2020.08.19追記
トラフィックGUIで確認したい場合はKialiを使うと便利。
別記事作成しました。

zaki-hmkc.hatenablog.com