前回の記事で,Envoyの話をした. Envoy,すごく便利なのだがこの設定を全コンテナごとに書いてやるのはなかなかにめんどくさい. せっかくSidecarなのでもっと楽に差し込めたら良い.
あと,せっかくEnvoyを使っているので,static_resourcesだけじゃなくdynamic_resourcesをつかったカナリアリリースとかやってみたい.
というわけでIstioを使ってみる. この分野だとIstioは非常に筋がいいのだが,いまいち枯れてないので,本番導入するのはちょっと悩む……
Istio関連だと,公式で出しているBookinfoというアプリケーションを 動かしてみた という記事はたくさん見かけるんだけど,自分のアプリケーションでIstio組み込んだ記事が非常に少ない.
ので,今回は自分で作ったサンプルアプリケーションをIstioの上で動かしてみる.もちろんKubernetesクラスタは構築済みという前提で話をすすめる.
Istioのインストール
とりあえずIstioのインストールをする必要がある. Istioの設定が書かれたyamlをいちいちapplyしてもいいのだが,結構量が多くてめんどくさいのでHelmを使うことをおすすめする.
Istioctl
istioctlは別にインストールしてもしなくても良い気がしている.のだが,とりあず getLatestIstio
は大事なのでダウンロードしておく.
$ curl -L https://git.io/getLatestIstio | sh - $ cd istio-1.* $ sudo cp bin/istioctl /usr/local/bin/ $ istioctl --help
Helm
$ cd istio-1.* $ kubectl create -f install/kubernetes/helm/helm-service-account.yaml $ helm init --service-account tiller
これでhemlが使えるようになる.
Istio
hemlを使ってインストールする.
$ kubectl create namespace istio-system $ cd istio-1.* $ helm install \ --wait \ --name istio \ --namespace istio-system \ install/kubernetes/helm/istio ... $ helm status istio LAST DEPLOYED: Fri Feb 1 22:40:42 2019 NAMESPACE: istio-system STATUS: DEPLOYED ...
helm status istio
のSTATUSが DEPLOYED
になればOK.うまく行かない場合は何かしら問題が発生している.
このオプションだと,結構いろんなPodを立ち上げる(PrometheusとかGrafanaとか)ので,Podを立ち上げる余力がクラスタにあるかはちゃんと確認しておいたほうが良い. 俺はいつもEKSを使うのだけれど,EKSの場合,PodにはENIが割り当てられる.そしてインスタンスサイズによって割当可能なENIに上限が設定されており,インスタンスをケチるとENIが足らずにPodが立ち上げられなくなったりした.インスタンスサイズを上げてやり直したらうまく行った.
istio-system
にこれらのPodが立ち上がっていれば問題ない.
$ kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE istio-citadel-7dd558dcf-ldb44 1/1 Running 0 17h istio-egressgateway-88887488d-bp7qv 1/1 Running 0 17h istio-galley-787758f7b8-txr4p 1/1 Running 0 17h istio-ingressgateway-58c77897cc-qn554 1/1 Running 0 17h istio-pilot-868cdfb5f7-znvjr 2/2 Running 0 17h istio-policy-56c4579578-25ztf 2/2 Running 0 17h istio-sidecar-injector-d7f98d9cb-gnckv 1/1 Running 0 17h istio-telemetry-7fb48dc68b-4826f 2/2 Running 0 17h prometheus-76db5fddd5-48zqq 1/1 Running 0 17h
auto injectionを設定する
IstioはIstio-proxyとしてEnvoyのコンテナを,各PodのSidecarとして配置する必要がある.このテンプレとは istioctl kube-inject
で吐き出すことができるのだが,それも大量のyamlである.それをいちいち自分で管理するのは嫌なので,auto injection機能を使うことが多い.
これを使うと,テンプレを自分で吐き出して,applyする必要がなく,すべてのPodにIstio-proxyが勝手にInjectされて立ち上がるようになる(これをやってくれるのが istio-sidecar-injector
というPodで,↑のPod一覧でも起動しているのが見える).
なお,これを行うには,Webhook Admission Controllersという機能をKubernetesクラスタがサポートしている必要がある.EKSとかだとすでにサポートされているのでそのまま使える.
auto injectionの設定はnamespaceごとに設定される.というわけでnamespaceをこのようにして作る.
apiVersion: v1 kind: Namespace metadata: name: istio-http-example labels: name: istio-http-example istio-injection: enabled
Webサーバを立てる
Webサーバはgolangのechoで作った適当なサーバを使う.
また,今回は マイクロサービス間の通信をIstioでコントロールしたい ので,外部からアクセス可能な状態までは作らずに,クラスタ内でサーバとクライアントを作成し,サービス間通信ができる状態を目指す.
まずはバックエンドのWebサーバを作る.
apiVersion: apps/v1 kind: Deployment metadata: name: backend-0 namespace: istio-http-example labels: app: backend-0 spec: replicas: 1 selector: matchLabels: app: backend version: "0" template: metadata: labels: app: backend version: "0" spec: containers: - name: echo image: h3poteto/playground-echo-go:master imagePullPolicy: Always ports: - containerPort: 8080 name: http env: - name: PORT value: "8080" - name: VERSION value: "0"
後々,versionによるカナリアリリースを実現したいので,versionというlabelを付与しているが,とりあえずはこれは使わない.
次に,namespace内からアクセス可能にさせるためにServiceを作る.
apiVersion: v1 kind: Service metadata: name: backend namespace: istio-http-example labels: app: backend spec: ports: - port: 9090 targetPort: 8080 protocol: TCP name: http selector: app: backend
これで,backend
という名前でアクセスできるようになる.
これらを kubectl apply -f deployment.yaml
すると,yamlに定義したコンテナは1つだが,Istio-proxyが勝手に挟み込まれてコンテナが2つ起動するようになるはずである.
$ kubectl get pods -n istio-http-example NAME READY STATUS RESTARTS AGE backend-0-7bc54786df-4fgvz 2/2 Running 0 15d
もしIstio-proxyのauto injectionが働かない場合は,namespaceの設定か,クラスタ全体でhookが動くようになっているかを確認する必要がある.
Istioによるロードバランスを特に変更せずにアクセスする
とりあえず,Istioは通すがIstioには特にアクセス制御をさせずに普通にバックエンドのサービスに疎通させてみる.
apiVersion: apps/v1 kind: Deployment metadata: name: client-0 namespace: istio-http-example labels: app: client-0 spec: replicas: 1 selector: matchLabels: app: client version: "0" template: metadata: labels: app: client version: "0" spec: containers: - name: client image: h3poteto/playground-echo-client:master imagePullPolicy: Always env: - name: SERVER_HOST value: "backend" - name: SERVER_PORT value: "9090"
こいつは SERVICE_HOST:SERVICE_PORT
にcURLを投げるだけのコンテナだ.とりあえずbackendのWebサーバにリクエストを投げてみる.
と,見事に疎通している.
$ kubectl logs -f client-0-796bb44b64-kmwpg -n istio-http-example -c client Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0
Istioによりアクセスを振り分ける
さて,ここまで来たらいよいよIstioの機能を使っていこうと思う. まず,backendのWebサーバとして,version: 0とversion: 1を用意する.
apiVersion: apps/v1 kind: Deployment metadata: name: backend-0 namespace: istio-http-example labels: app: backend-0 spec: replicas: 1 selector: matchLabels: app: backend version: "0" template: metadata: labels: app: backend version: "0" spec: containers: - name: echo image: h3poteto/playground-echo-go:master imagePullPolicy: Always ports: - containerPort: 8080 name: http env: - name: PORT value: "8080" - name: VERSION value: "0" --- apiVersion: apps/v1 kind: Deployment metadata: name: backend-1 namespace: istio-http-example labels: app: backend-1 spec: replicas: 1 selector: matchLabels: app: backend version: "1" template: metadata: labels: app: backend version: "1" spec: containers: - name: echo image: h3poteto/playground-echo-go:master imagePullPolicy: Always ports: - containerPort: 8080 name: http env: - name: PORT value: "8080" - name: VERSION value: "1"
これをapplyするだけだと,まだIstioは何もしてくれていないので,version: 0とversion: 1がランダムにアクセスを受け付ける(これはKubernetesのServiceがロードバランスしてくれている).
version: 0だけにアクセスさせる
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: backend namespace: istio-http-example spec: hosts: - "backend" http: - route: - destination: host: backend subset: v0 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: backend namespace: istio-http-example spec: host: backend trafficPolicy: loadBalancer: simple: RANDOM subsets: - name: v0 labels: version: "0" - name: v1 labels: version: "1"
subsets
は DestinationRule
の項目として定義する.それを VirtualService
で利用することができる.
VirtualServiceはKubernetesで言うところの,Serviceに入る前段に挟み込むことができる. これにより,
- hostがhogehogeだったら
- pathが/hogeだったら
というようなルーティングルールを作ることができる.
とりあえず今回はすべてのリクエストを,subset: v0 (すなわち labels.version: "0"
を持つbackend) にのみ振り分ける.
これをapplyすると,version: 0にのみアクセスがいくようになる.
$ kubectl logs -f client-0-796bb44b64-kmwpg -n istio-http-example -c client Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0
version: 1にだけアクセスさせる
今度は逆にversion: 1にだけアクセスがいくようにする.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: backend namespace: istio-http-example spec: hosts: - "backend" http: - route: - destination: host: backend subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: backend namespace: istio-http-example spec: host: backend trafficPolicy: loadBalancer: simple: RANDOM subsets: - name: v0 labels: version: "0" - name: v1 labels: version: "1"
$ kubectl logs -f client-0-796bb44b64-kmwpg -n istio-http-example -c client Hello, World!, version: 1 Hello, World!, version: 1 Hello, World!, version: 1 Hello, World!, version: 1 Hello, World!, version: 1 Hello, World!, version: 1
となる.
version: 0に90%,version: 1に10%
最後に,割合を偏らせた形でリクエストを振り分けることもできる.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: backend namespace: istio-http-example spec: hosts: - "backend" http: - route: - destination: host: backend subset: v0 weight: 90 - destination: host: backend subset: v1 weight: 10 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: backend namespace: istio-http-example spec: host: backend trafficPolicy: loadBalancer: simple: RANDOM subsets: - name: v0 labels: version: "0" - name: v1 labels: version: "1"
$ kubectl logs -f client-0-796bb44b64-kmwpg -n istio-http-example -c client Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 1 Hello, World!, version: 0 Hello, World!, version: 0 Hello, World!, version: 1 Hello, World!, version: 0
こうなる.
Tips
Istioctlの使いどころ
istioの定義ファイルもkubernetesと同じくyamlを書くことになる.
これらは kubectl apply
でapplyできるが,デプロイされたリソースの確認をするにはkubectlでもできるが,istioctlでもできる.
$ kubectl get virtualservice -n istio-http-example NAME AGE backend 8m $ istioctl get virtualservices -n istio-http-example VIRTUAL-SERVICE NAME GATEWAYS HOSTS #HTTP #TCP NAMESPACE AGE backend backend 1 0 istio-http-example 8m
他にもIstioはいろんなことができる
今回紹介したくらいまでができれば,あとはIstioのドキュメントに書いてある機能をひたすら試してみるだけなので楽に横展開できると思う.
この辺を見ると,ルーティングにどんなものが使えるか色々書いてあるので,だいぶ参考になる.
あと認証もできるよ.
gRPCもできる
基本的にIstioはgRPCのリクエストもさばける. が,これについては次の記事で具体例を作ってみようと思うので,次回にご期待ください.