Skip to content

リクエスト・ルーティングの制御

このチュートリアルでは、Istioを使ってリクエスト・ルーティングの制御を体験します。カナリーデプロイメントのシナリオを想定して、複数バージョンが混在するアプリケーションのコンテナ群に対して、バージョン毎に流量を変えてルーティングしてみます。

前提条件

  • Istioをインストール済みのKubernetesクラスター
  • 上記クラスターに対して実行可能なKubernetes、Istioのコマンドラインツール(kubectl,istioctl)

Note

KubernetesクラスターへのIsitoのインストール手順は、Istioのインストールを参照してください。

以降の手順は、ほとんどの操作をコマンドラインツールから実行します。Mac/Linuxの場合はターミナル、Windowsの場合はWindows PowerShell利用するものとして、手順を記述します。

1. サンプルアプリケーションを配備する

このチュートリアルでは、サンプルアプリケーションとしてkubernetes-bootcampアプリケーションを使います。ここでは、リクエスト・ルーティングの制御を行う前に、このアプリケーションをIstioのSidecarなしでデプロイし、動作を確認しておきます。

1.1. Namespaceを作成する

以降のチュートリアルの手順で利用するNamespaceとして、"istio-routing"を作成しておきます。

kubectl create namespace istio-routing

デフォルトNamespaceを"istio-routing"に変更しておくことで、kubectlの実行の度にNamespaceを指定しなくても良いようにしておきます。

kubectl config set-context $(kubectl config current-context) --namespace=istio-routing

1.2. bootcampアプリケーションのデプロイ

このチュートリアルで利用するmanifestファイル一式をcloneし、出来上がったディレクトリをカレントディレクトリにしておきます。

Mac / Linux

git clone https://github.com/cndjp/istio-request-routing && cd istio-request-routing

Windows

git clone https://github.com/cndjp/istio-request-routing; cd istio-request-routing

続いて、bootcampアプリケーションをデプロイします。

kubectl create -f ./bootcamp.yaml

Podの一覧を表示して、version 1のPodが2つ、varsion 2のPodが1つデプロイされていることを確認してください。

kubectl get pods

正しくPodがデプロイされていれば、以下のような結果が表示されます。

NAME                           READY     STATUS    RESTARTS   AGE
bootcamp-v1-68cb78f87c-l8szm   1/1       Running   0          42s
bootcamp-v1-68cb78f87c-zlmqh   1/1       Running   0          42s
bootcamp-v2-569574d79f-w4p6g   1/1       Running   0          41s

次に、クラスター外からアプリケーションにアクセスできるようにするために、ServiceおよびIngressオブジェクトを作成します。

Serviceオブジェクトを作成するには以下のコマンドを実行します。

kubectl create -f ./bootcamp-service.yaml

Ingressオブジェクトを作成するには以下のコマンドを実行します。

kubectl create -f ./bootcamp-ingress.yaml

Note

ここではIngress Cotrollerとして、Istioに付属のIstio Ingress Cotrollerを利用します。Ingressオブジェクトを作成すると、その設定内容に合わせてIngress ControllerがL7ロードバランサとして機能します。これによって、クラスター外からのリクエストをアプリケーションに届けることができます。

1.3. bootcampアプリケーションへのアクセスの確認

Ingress Controller経由でのアプリケーションへのアクセスを試行してみます。

まず、Ingress ControllerのPodが配備されているNodeのIPアドレスと、公開されているPort番号を取得して、GATEWAY_URLという環境変数に格納しておきます。

Mac / Linux

export GATEWAY_URL=$(kubectl get po -l istio=ingress -n istio-system -o 'jsonpath={.items[0].status.hostIP}'):$(kubectl get svc istio-ingress -n istio-system -o 'jsonpath={.spec.ports[0].nodePort}')

Windows

$GATEWAY_URL=$(kubectl get po -l istio=ingress -n istio-system -o jsonpath='{.items[0].status.hostIP}') + ':'+ $(kubectl get svc istio-ingress -n istio-system -o jsonpath='{.spec.ports[0].nodePort}')

GATEWAY_URLを表示して、[IPアドレス]:[ポート番号]の形式で実際の値が格納されていることを確認してみてください。

echo $GATEWAY_URL

次に、取得したURLの末尾に"/bootcamp"を追加して、その宛先に対してリクエストを送信します。Pod名とアプリケーションのバージョン番号が返却されることを確認してください。

Mac / Linux

curl http://$GATEWAY_URL/bootcamp

Windows

Invoke-RestMethod -Uri "http://$GATEWAY_URL/bootcamp"

この時点では、トラフィックの制御を何も行っていないため、3つのPodに対して均等の割合でルーティングされます。リクエストの送信を繰り返し実行すると、おおよそ2/3がv1、1/3がv2からの応答となります。

以上で、サンプルアプリケーションの配備は完了です。

2. Istioによるリクエスト・ルーティングの制御をおこなう

それでは、Istioを使ったリクエスト・ルーティングの制御を実際におこなってみます。

2.1. bootcampアプリケーションのPodをEnvoyを注入したものに置き換える

ここまでの手順でデプロイしたアプリケーションは、Kubernetesの通常の方法でデプロイしているため、PodにEnvoyプロキシを含んでいません。まずは、これをEnvoyを含むものに置き換えます。

以下のコマンドで、一度bootcampアプリケーションを削除します。

kubectl delete -f ./bootcamp.yaml

次に、istioctlを使って、bootcampアプリケーションのmanifestファイルにEnvoyの記述を追加します。

istioctl kube-inject --debug -f ./bootcamp.yaml -o ./bootcamp-istio.yaml

新たに作成された、Envoyプロキシ注入後のmanifestファイルの内容を参照してみます。

cat ./bootcamp-istio.yaml

以下は、v1のbootcampアプリケーションに当たる部分を抜粋したものです。主要な追記箇所をハイライトしています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
 apiVersion: apps/v1beta1
 kind: Deployment
 metadata:
   creationTimestamp: null
   name: bootcamp-v1
 spec:
   replicas: 2
   selector:
     matchLabels:
       app: bootcamp
   strategy: {}
   template:
     metadata:
+      annotations:
+        sidecar.istio.io/status: '{"version":"15bbcd92b59e99a83360c48bd7af9e84ffd5b961b914e8febcee0e38d08b773e","initContainers":["istio-init","enable-core-dump"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"]}'
       creationTimestamp: null
       labels:
         app: bootcamp
         version: v1
     spec:
       containers:
       - image: docker.io/jocatalin/kubernetes-bootcamp:v1
         name: bootcamp
         ports:
         - containerPort: 8080
         resources: {}
+      - args:
+        - proxy
+        - sidecar
+        - --configPath
+        - /etc/istio/proxy
+        - --binaryPath
+        - /usr/local/bin/envoy
+        - --serviceCluster
+        - bootcamp
+        - --drainDuration
+        - 45s
+        - --parentShutdownDuration
+        - 1m0s
+        - --discoveryAddress
+        - istio-pilot.istio-system:8080
+        - --discoveryRefreshDelay
+        - 1s
+        - --zipkinAddress
+        - zipkin.istio-system:9411
+        - --connectTimeout
+        - 10s
+        - --statsdUdpAddress
+        - istio-mixer.istio-system:9125
+        - --proxyAdminPort
+        - "15000"
+        - --controlPlaneAuthPolicy
+        - NONE
+        env:
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+        - name: INSTANCE_IP
+          valueFrom:
+            fieldRef:
+              fieldPath: status.podIP
+        image: docker.io/istio/proxy_debug:0.7.1
+        imagePullPolicy: IfNotPresent
+        name: istio-proxy
+        resources: {}
+        securityContext:
+          privileged: true
+          readOnlyRootFilesystem: false
+          runAsUser: 1337
+        volumeMounts:
+        - mountPath: /etc/istio/proxy
+          name: istio-envoy
+        - mountPath: /etc/certs/
+          name: istio-certs
+          readOnly: true
+      initContainers:
+      - args:
+        - -p
+        - "15001"
+        - -u
+        - "1337"
+        image: docker.io/istio/proxy_init:0.7.1
+        imagePullPolicy: IfNotPresent
+        name: istio-init
+        resources: {}
+        securityContext:
+          capabilities:
+            add:
+            - NET_ADMIN
+          privileged: true
+      - args:
+        - -c
+        - sysctl -w kernel.core_pattern=/etc/istio/proxy/core.%e.%p.%t && ulimit -c
+          unlimited
+        command:
+        - /bin/sh
+        image: alpine
+        imagePullPolicy: IfNotPresent
+        name: enable-core-dump
+        resources: {}
+        securityContext:
+          privileged: true
+      volumes:
+      - emptyDir:
+          medium: Memory
+        name: istio-envoy
+      - name: istio-certs
+        secret:
+          optional: true
+          secretName: istio.default
+status: {}

追記箇所のポイントを挙げると、以下の2点があります。

  • {.spec.template.spec.containers}以下に、istio-proxyという名前のコンテナが追加されており、これがEnvoyプロキシの実態となる。また、Envoyプロキシが利用するための環境変数の記述も併せて追加されている
  • {.spec.template.spec.initContainers}以下に、istio-init, enable-core-dumpという名前のinitContainerが追加されている

Note

initContainerは、Podのデプロイ時に前処理を行うために利用するコンテナで、これはKubernetesが持っている機能です。

前処理でしか使わないバイナリをinitContainerに集めておく、前処理でしか必要ない権限はinitContainerだけに与える、などをおこなうことで、本体のContainerを必要最低限の機能に絞り込んだものにすることができます。

上の例で、istio-initはEnvoyプロキシが利用するためのiptablesのルールの設定をおこなうもの、enable-core-dumpはコアダンプの出力の設定をおこなうものです。

それでは、このmanifestを使って、bootcampアプリケーションをデプロイします。

kubectl create -f ./bootcamp-istio.yaml

2.2. リクエスト・ルーティングの制御を行う

それでは、実際にトラフィックの流量制御を行います。

まずは、全てのトラフィックをv1に振り分けてみます。トラフィックのルールはIstio用のmanifestファイルとして記述することができます。実際のmanifestファイルの内容は以下のようになっています。

cat ./bootcamp-route-rule-all-v1.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: bootcamp-default
spec:
  destination:
    name: bootcamp
  precedence: 1
  route:
  - labels:
      version: v1
    weight: 100

{.spec.route}に具体的なルーティングのルールを記述しています。この場合、bootcampというServiceがルーティング対象とするPodで、version: v1というlabelが設定されているものに、全てのトラフィックを送るようにしています。

このルールを適用するには、以下のコマンドを実行します。

istioctl create -n istio-routing -f ./bootcamp-route-rule-all-v1.yaml

改めて、アプリケーションにリクエストを送信します。繰り返し実行してみて、全ての応答がv1から返却されることを確認してください。

Mac / Linux

curl http://$GATEWAY_URL/bootcamp

Windows

Invoke-RestMethod -Uri "http://${GATEWAY_URL}/bootcamp"

次に、v1,v2にトラフィックを均等に送るようにルールを変更します。 まずは、新たにデプロイするルールのmanifestファイルを参照してみます。

cat ./bootcamp-route-rule-50-v2.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: bootcamp-default
spec:
  destination:
    name: bootcamp
  precedence: 1
  route:
  - labels:
      version: v1
    weight: 50
  - labels:
      version: v2
    weight: 50

このmanifestでは、v1,v2それぞれにweight: 50を記述することで、それぞれのバージョンに均等にトラフィックが送信されるようにしています。

それでは、このルールをデプロイします。以下のようなistioctl replaceコマンドをを使ってルールの置き換えをおこないます。

istioctl replace -n istio-routing -f ./bootcamp-route-rule-50-v2.yaml

再度リクエストの送信を繰り返して、v1とv2からの応答がおおよそ同じ割合で返ってくることを確認してください。

最後に、v2に全てのトラフィックをルーティングするように、ルールを変更します。

istioctl replace -n istio-routing -f ./bootcamp-route-rule-all-v2.yaml

今度は、応答が全てv2からのものになっていることを確認してください。

3. クリーンアップ

これまで作ってきたオブジェクトをクリーンアップしたい場合は、以下のコマンドを実行して、作成したオブジェクトを削除してください。

istioctl delete -n istio-routing -f ./bootcamp-route-rule-all-v2.yaml
kubectl delete namespace istio-routing

以上で本チュートリアルは終了です。