なになれ

IT系のことを記録していきます

KubernetesでLoad BalancerとしてEnvoyを試した

こちらでEnvoyを一通り学習しました。
www.katacoda.com

理解できているかの確認のために、KubernetesでEnvoyを使ってみます。
KubernetesでEnvoyを使う場合、EnvoyをSidecarプロキシにすることで各Podの通信をコントロールする構成が知られています。
サービスメッシュを実現するIstioはそのような構成になっています。
このような最新のシステム構成を理解するためにもEnvoyの理解を深めることが求められていると感じています。
今回はPodへの通信の前段にEnvoyを配置する単純な構成を試しました。

前提

Docker for MacKubernetes環境で動作確認しています。

構成について

バージョン2とバージョン3のアプリケーションが存在します。
2つのアプリケーションのロードバランサーとしてEnvoyが前段に存在します。
一部の通信をバージョン3に向ける処理をEnvoyが行います。

f:id:hi1280:20200304230435p:plain

デプロイするアプリケーションについて

HTTPの通信を受け付けるNode.jsのアプリケーションです。
KubernetesとEnvoyを組み合わせるために、KubernetesのHeadless Serviceを利用します。
Headless Serviceを使うことでEnvoyでService Discoveryが可能になります。

デプロイを行うためのKubernetesのManifestを示します。
下記ではバージョン2とバージョン3のアプリケーションをデプロイします。

nodejs-http-server.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-http-server-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nodejs-http-server-v2
  template:
    metadata:
      labels:
        app: nodejs-http-server-v2
    spec:
      containers:
        - name: nodejs-http-server-v2
          image: hi1280/nodejs-http-server:0.0.2
          ports:
            - containerPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-http-server-v3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nodejs-http-server-v3
  template:
    metadata:
      labels:
        app: nodejs-http-server-v3
    spec:
      containers:
        - name: nodejs-http-server-v3
          image: hi1280/nodejs-http-server:0.0.3
          ports:
            - containerPort: 3000

アプリケーションに繋がるHeadless Serviceを作ります。
clusterIPにNoneを指定します。

nodejs-http-server.yaml

apiVersion: v1
kind: Service
metadata:
  name: nodejs-http-server-v2
spec:
  clusterIP: None
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
  selector:
    app: nodejs-http-server-v2
---
  apiVersion: v1
  kind: Service
  metadata:
    name: nodejs-http-server-v3
  spec:
    clusterIP: None
    ports:
      - protocol: TCP
        port: 3000
        targetPort: 3000
    selector:
      app: nodejs-http-server-v3

Envoyをデプロイする

Envoyをデプロイします。
envoyの公式のDockerイメージを使ってenvoyを起動します。

my-envoy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-envoy
  labels:
    app: my-envoy
spec:
  selector:
    matchLabels:
      app: my-envoy
  template:
    metadata:
      labels:
        app: my-envoy
    spec:
      containers:
      - name: my-envoy
        image: envoyproxy/envoy:latest
        volumeMounts:
          - name: config-volume
            mountPath: /etc/envoy
        ports:
        - name: http
          containerPort: 8080
        - name: envoy-admin
          containerPort: 9901
      volumes:
        - name: config-volume
          configMap:
            name: my-envoy

/etc/envoyenvoy.yamlを配置すると、そのファイルに定義されたenvoyの設定が読み込まれます。
envoy.yamlを作成するのにConfigMapを使用しています。

ConfigMapでenvoy.yamlを作成します。
my-envoy.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-envoy
data:
  envoy.yaml: |
    admin:
      access_log_path: /tmp/admin_access.log
      address:
        socket_address: { address: 0.0.0.0, port_value: 9901 }
    static_resources:
      listeners:
      - name: listener_0
        address:
          socket_address: { address: 0.0.0.0, port_value: 8080 }
        filter_chains:
          - filters:
            - name: envoy.http_connection_manager
              config:
                codec_type: auto
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                  - name: backend
                    domains: ["*"]
                    routes:
                    - match:
                        prefix: "/"
                      route:
                        weighted_clusters:
                          clusters:
                          - name: v2
                            weight: 80
                          - name: v3
                            weight: 20
                http_filters:
                - name: envoy.router
      clusters:
      - name: v2
        connect_timeout: 0.25s
        type: STRICT_DNS
        dns_lookup_family: V4_ONLY
        lb_policy: ROUND_ROBIN
        hosts: [{ socket_address: { address: nodejs-http-server-v2, port_value: 3000 }}]
      - name: v3
        connect_timeout: 0.25s
        type: STRICT_DNS
        dns_lookup_family: V4_ONLY
        lb_policy: ROUND_ROBIN
        hosts: [{ socket_address: { address: nodejs-http-server-v3, port_value: 3000 }}]

route_configの設定でHTTPのルーティングを決めています。
アプリケーションのバージョン2への通信を8割、バージョン3への通信を2割にするようにトラフィックの制御を行なっています。
clustersの設定でアプリケーションの宛先を決めています。
nodejs-http-server-v2nodejs-http-server-v3のHost名になっています。
これはHeadless Serviceによって解決されます。

最後にNodePortでenvoyを外部に公開します。

my-envoy.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-envoy
  labels:
    app: my-envoy
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
  selector:
    app: my-envoy

動作確認する

curlで動作確認します。
公開されるポート番号はランダムに決められますので、kubectl get svcで確認します。

$ for i in {1..10}; do curl -s http://localhost:30392; done
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v3. This request was processed by host: nodejs-http-server-v3-7d66cbd7c7-fcq98
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6
Now v2. This request was processed by host: nodejs-http-server-v2-855b645dcd-x68x6

おおよそ決めた割合で通信が分けられていることが確認できます。

まとめ

今回はEnvoyをLoad Balancer的に使うやり方を理解できました。
これを踏まえるとEnvoyをSidecarプロキシとして利用する構成が理解しやすいのではないでしょうか。