Open38

Istio Source Code Reading

Shimpei OtsuboShimpei Otsubo

WHY

istio のコードの全体像を掴みたい、その中でできれば catch-all virtual service という概念の挙動を正しく理解したい

Shimpei OtsuboShimpei Otsubo

controller が読みたくなるので istiod の deployment の中で特徴的な option 名で ag して下の file を見つけた
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/cmd/pilot-discovery/main.go
これがきっとそう?

istiod の deployment で動かしている docker image の cmd と同名っぽいので多分正しい?

istio/istio / - master   4 jobs
:) % docker inspect docker.io/istio/pilot:latest | jq '.[0].ContainerConfig.Cmd'                                                                                                                                                                                                                                                                                                                                                                                                          21-01-14 19:17:23
[
  "/bin/sh",
  "-c",
  "#(nop) ",
  "ENTRYPOINT [\"/usr/local/bin/pilot-discovery\"]"
]
Shimpei OtsuboShimpei Otsubo

パット discoveryServer をただ起動しているだけのように見えるのでこいつの挙動を見に行こう
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/bootstrap/server.go#L347 するとここにたどり着く
ただその中身を見ると server を起動している様子で resource の変更を検知しているような挙動がぱっと見えなかった。
もしかして違うコンポーネントだったりする?

よくわからないので istio を minikube で入れて何が起こるかを見よう

Shimpei OtsuboShimpei Otsubo
#!/bin/bash

set -eux

ISTIO_VERSION=1.8.1
ISTIO_PATH="./istio-${ISTIO_VERSION}"

if [[ -d $ISTIO_PATH ]]
then
	echo "detected istio is installed"
else
	curl -L https://istio.io/downloadIstio | sh -
fi

minikube start

ISTIOCTL=./${ISTIO_PATH}/bin/istioctl

${ISTIOCTL} install --set profile=demo -y

kubectl label namespace default istio-injection=enabled --overwrite

これで作った cluster を見ると deployment が下のものしかないしそれっぽいのは istiod

$ kubectl get deploy -A
NAMESPACE      NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
istio-system   istio-egressgateway    1/1     1            1           31s
istio-system   istio-ingressgateway   1/1     1            1           31s
istio-system   istiod                 1/1     1            1           51s
kube-system    coredns                2/2     2            2           55s
Shimpei OtsuboShimpei Otsubo

念の為にすべての deployment を確認する

$ kubectl get deploy -A -o json | jq '.items[].spec.template.spec.containers[] | {"image": .image, "cmd": .command}'
{
  "image": "docker.io/istio/proxyv2:1.8.1",
  "cmd": null
}
{
  "image": "docker.io/istio/proxyv2:1.8.1",
  "cmd": null
}
{
  "image": "docker.io/istio/pilot:1.8.1",
  "cmd": null
}
{
  "image": "k8s.gcr.io/coredns:1.6.7",
  "cmd": null
}

coredns は istio に関係ないので無視する
pilot は今見ているもの

残るは proxyv で中身は pilot-agent らしい

potsbo/istio-experiment / - ✚ …  master   1 job
:) % docker inspect docker.io/istio/proxyv2:1.8.1 | jq '.[0].Config.Entrypoint'                                                                                                                                                                                                                                                                                                                                                                                                           21-01-14 20:45:50
[
  "/usr/local/bin/pilot-agent"
]
Shimpei OtsuboShimpei Otsubo

envoy と istio がどうやって情報をやり取りしているのかを知らないとちょっとイメージが付きづらい
というのも envoy が定期的に pilot-discovery が起動している server に情報を取りに来てるのかな

そこで適当な pod の istio sidecar を見てみるとその image は docker.io/istio/proxyv2 だった
どうやらこいつは sidecar にも ingress, egress にも使える proxy という感じに理解すれば良さそう

Shimpei OtsuboShimpei Otsubo

今の所の勝手な予想だけど下のようになっている?

pilot-discovery

が virtualservice, destinationrule, gateway などの情報を集めてどの envoy がどんな config を持つべきかを知っている人

pilot-agent

sidecar と proxy の2つのモード(subcommand) がある

:) % kubectl get deploy -n istio-system istio-ingressgateway -o json | jq '.spec.template.spec.containers[0].args'                                                                                                                                                                                                                                                                                                                                                                        21-01-14 21:30:43
[
  "proxy",
  "router",
  "--domain",
  "$(POD_NAMESPACE).svc.cluster.local",
  "--proxyLogLevel=warning",
  "--proxyComponentLogLevel=misc:error",
  "--log_output_level=default:info",
  "--serviceCluster",
  "istio-ingressgateway"
]

istio/istio / - master   7 jobs
:) % kubectl get deploy -n istio-system istio-egressgateway -o json | jq '.spec.template.spec.containers[0].args'                                                                                                                                                                                                                                                                                                                                                                         21-01-14 21:30:54
[
  "proxy",
  "router",
  "--domain",
  "$(POD_NAMESPACE).svc.cluster.local",
  "--proxyLogLevel=warning",
  "--proxyComponentLogLevel=misc:error",
  "--log_output_level=default:info",
  "--serviceCluster",
  "istio-egressgateway"
]
Shimpei OtsuboShimpei Otsubo

当たり前かもしれないけど proxvy にのみ envoy が存在している

istio/istio / - master   8 jobs
:( % docker run --entrypoint bash -it docker.io/istio/proxyv2:1.8.1                                                                                                                                                                                                                                                                                                                                                                                                                       21-01-14 21:37:40
root@1cbb70354ea2:/# which envoy
/usr/local/bin/envoy
root@1cbb70354ea2:/# exit

istio/istio / - master   8 jobs
:) % docker run --entrypoint bash -it docker.io/istio/pilot:latest                                                                                                                                                                                                                                                                                                                                                                                                                        21-01-14 21:37:58
istio-proxy@1d42fcf860c1:/$ which envoy
istio-proxy@1d42fcf860c1:/$
Shimpei OtsuboShimpei Otsubo

envoy に渡される引数は多すぎてちょっとわけが分からなさそうなのでとりあえず Envoy API がどんなものなのかを知りたい
https://i-beam.org/2019/03/13/envoy-xds-server/

APIには、エンドポイントの設定を提供する Endpoint Discovery Service (EDS) や、クラスタの設定を提供する Cluster Discovery Service (CDS) などがあります。 これらをまとめてxDS APIと呼びます。

と書いてある

https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol を見ると他にも LDS, CDS, LDS などいろんな DS があるので xDS というおしゃれな名前にしたということだと理解した
https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#rpc-services-and-methods-for-each-variant

Shimpei OtsuboShimpei Otsubo

istioctl dashboard envoy -l app=ratings で istio dashboard が見れるので http://localhost:15000/config_dump に行くと envoy の config が見れる
ざっくりとしたイメージでは多分これを pilot-discovery が serve していて pilot-agent が起動した envoy が request しに行ってるのではなかろうか
実際にはこれは dump 用に作った別の endpoint で実際の口は別にありそう、grpc も使えるんだし

Shimpei OtsuboShimpei Otsubo

ここで落ちてくる json をざっくり見るといろんな type がある
いま興味があるのは "type.googleapis.com/envoy.admin.v3.RoutesConfigDump" だと思う

jq < bar.json '.configs[]["@type"]'
"type.googleapis.com/envoy.admin.v3.BootstrapConfigDump"
"type.googleapis.com/envoy.admin.v3.ClustersConfigDump"
"type.googleapis.com/envoy.admin.v3.ListenersConfigDump"
"type.googleapis.com/envoy.admin.v3.ScopedRoutesConfigDump"
"type.googleapis.com/envoy.admin.v3.RoutesConfigDump"
"type.googleapis.com/envoy.admin.v3.SecretsConfigDump"
Shimpei OtsuboShimpei Otsubo

Resources are requested via subscriptions, by specifying a filesystem path to watch, initiating gRPC streams, or polling a REST-JSON URL. The latter two methods involve sending requests with a DiscoveryRequest proto payload.

https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol

と書いてあるので REST の API と grpc のどっちも本質的には同じ情報が返ってくると理解して良さそう
というわけで自分がもともと知りたかった「VirtualService の評価順に関する挙動」というのは pilot-discovery のサーバー実装を見れば良さそうということになりそう

Shimpei OtsuboShimpei Otsubo
:) % kubectl get po istiod-67dbfcd4dd-z2p5x -n istio-system -o json | jq '.spec.containers[0].args'                                                                                                                                                                                                                                                                                                                                                                                       21-01-14 22:31:08
[
  "discovery",
  "--monitoringAddr=:15014",
  "--log_output_level=default:info",
  "--domain",
  "cluster.local",
  "--keepaliveMaxServerConnectionAge",
  "30m"
]

手元の istio を入れた minikube の istiod に設定されている arg を調べると上のようになっていた
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/bootstrap/server.go#L385 つまりこの部分の http server のみが立ち上がることになっていそう

つまり手元の minikube の istio は http の polling で config の更新をしているということになりそう

Shimpei OtsuboShimpei Otsubo

grpcAddr が設定されてないから grpc で動いてないんだなと思ったらそんなことなくて default value がしっかり設定してあった https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/cmd/pilot-discovery/main.go#L145

istio/istio / - master   1 job
:) % kubectl logs istiod-67dbfcd4dd-z2p5x -n istio-system | head -n 100                                                                                                                                                                                                                                                                                                                                                                                                                   21-01-14 22:48:41
2021-01-14T10:57:35.663530Z     info    FLAG: --appNamespace=""
2021-01-14T10:57:35.663573Z     info    FLAG: --caCertFile=""
2021-01-14T10:57:35.663579Z     info    FLAG: --clusterID="Kubernetes"
2021-01-14T10:57:35.663581Z     info    FLAG: --clusterRegistriesNamespace="istio-system"
2021-01-14T10:57:35.663583Z     info    FLAG: --configDir=""
2021-01-14T10:57:35.663585Z     info    FLAG: --ctrlz_address="localhost"
2021-01-14T10:57:35.663589Z     info    FLAG: --ctrlz_port="9876"
2021-01-14T10:57:35.663591Z     info    FLAG: --domain="cluster.local"
2021-01-14T10:57:35.663595Z     info    FLAG: --grpcAddr=":15010"
2021-01-14T10:57:35.663599Z     info    FLAG: --help="false"
2021-01-14T10:57:35.663601Z     info    FLAG: --httpAddr=":8080"
2021-01-14T10:57:35.663603Z     info    FLAG: --httpsAddr=":15017"
2021-01-14T10:57:35.663606Z     info    FLAG: --keepaliveInterval="30s"
2021-01-14T10:57:35.663609Z     info    FLAG: --keepaliveMaxServerConnectionAge="30m0s"
2021-01-14T10:57:35.663611Z     info    FLAG: --keepaliveTimeout="10s"
2021-01-14T10:57:35.663613Z     info    FLAG: --kubeconfig=""
2021-01-14T10:57:35.663616Z     info    FLAG: --kubernetesApiBurst="160"
2021-01-14T10:57:35.663619Z     info    FLAG: --kubernetesApiQPS="80"
2021-01-14T10:57:35.663621Z     info    FLAG: --log_as_json="false"
2021-01-14T10:57:35.663623Z     info    FLAG: --log_caller=""
2021-01-14T10:57:35.663628Z     info    FLAG: --log_output_level="default:info"
2021-01-14T10:57:35.663630Z     info    FLAG: --log_rotate=""
2021-01-14T10:57:35.663632Z     info    FLAG: --log_rotate_max_age="30"
2021-01-14T10:57:35.663634Z     info    FLAG: --log_rotate_max_backups="1000"
2021-01-14T10:57:35.663637Z     info    FLAG: --log_rotate_max_size="104857600"
2021-01-14T10:57:35.663639Z     info    FLAG: --log_stacktrace_level="default:none"
2021-01-14T10:57:35.663645Z     info    FLAG: --log_target="[stdout]"
2021-01-14T10:57:35.663648Z     info    FLAG: --mcpInitialConnWindowSize="1048576"
2021-01-14T10:57:35.663650Z     info    FLAG: --mcpInitialWindowSize="1048576"
2021-01-14T10:57:35.663652Z     info    FLAG: --mcpMaxMsgSize="4194304"
2021-01-14T10:57:35.663654Z     info    FLAG: --meshConfig="./etc/istio/config/mesh"
2021-01-14T10:57:35.663656Z     info    FLAG: --monitoringAddr=":15014"
2021-01-14T10:57:35.663658Z     info    FLAG: --namespace="istio-system"
2021-01-14T10:57:35.663660Z     info    FLAG: --networksConfig="/etc/istio/config/meshNetworks"
2021-01-14T10:57:35.663665Z     info    FLAG: --plugins="[authn,authz,health]"
2021-01-14T10:57:35.663667Z     info    FLAG: --profile="true"
2021-01-14T10:57:35.663674Z     info    FLAG: --registries="[Kubernetes]"
2021-01-14T10:57:35.663677Z     info    FLAG: --resync="1m0s"
2021-01-14T10:57:35.663679Z     info    FLAG: --secureGRPCAddr=":15012"
2021-01-14T10:57:35.663680Z     info    FLAG: --tlsCertFile=""
2021-01-14T10:57:35.663682Z     info    FLAG: --tlsKeyFile=""
2021-01-14T10:57:35.663684Z     info    FLAG: --trust-domain=""

したがって次に見るべき entrypoint は
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/bootstrap/server.go#L376 
になる

Shimpei OtsuboShimpei Otsubo

この server の実体はここ

	s.XDSServer.Register(s.grpcServer)

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/bootstrap/server.go#L629

つまりここであり

	discovery.RegisterAggregatedDiscoveryServiceServer(rpcs, s)

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/discovery.go#L207

これは proto による自動生成コード
実際に呼び出されるコードは proto を見に行けばわかる

proto を見に行くと下のように書いてある

service AggregatedDiscoveryService {
  // This is a gRPC-only API.
  rpc StreamAggregatedResources(stream DiscoveryRequest) returns (stream DiscoveryResponse) {
  }

  rpc DeltaAggregatedResources(stream DeltaDiscoveryRequest)
      returns (stream DeltaDiscoveryResponse) {
  }
}

https://github.com/envoyproxy/envoy/blob/4cadf72e0b40d6dfcfaf755c243734e751927756/api/envoy/service/discovery/v3/ads.proto#L29-L37

というわけで StreamAggregatedResources を見に行けばいいはず

// StreamAggregatedResources implements the ADS interface.
func (s *DiscoveryServer) StreamAggregatedResources(stream discovery.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error {
	return s.Stream(stream)
}

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/ads.go#L223

これはたらい回しにしてるだけで実体はそのすぐ下
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/ads.go#L226

Shimpei OtsuboShimpei Otsubo

ここが Stream の本質的なところと考えて良さそう

	for {
		// Block until either a request is received or a push is triggered.
		// We need 2 go routines because 'read' blocks in Recv().
		//
		// To avoid 2 routines, we tried to have Recv() in StreamAggregateResource - and the push
		// on different short-lived go routines started when the push is happening. This would cut in 1/2
		// the number of long-running go routines, since push is throttled. The main problem is with
		// closing - the current gRPC library didn't allow closing the stream.
		select {
		case req, ok := <-reqChannel:
			if !ok {
				// Remote side closed connection or error processing the request.
				return receiveError
			}
			// processRequest is calling pushXXX, accessing common structs with pushConnection.
			// Adding sync is the second issue to be resolved if we want to save 1/2 of the threads.
			err := s.processRequest(req, con)
			if err != nil {
				return err
			}

		case pushEv := <-con.pushChannel:
			err := s.pushConnection(con, pushEv)
			pushEv.done()
			if err != nil {
				return err
			}
		case <-con.stop:
			return nil
		}
	}

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/ads.go#L285-L315

下の2つの用途の chan を用意しておいてそれぞれに response を client に送るということをしている様子

  • client からの(更新の?) request が来たとき
  • server 側で変更を検知(多分) を検知したとき
Shimpei OtsuboShimpei Otsubo

Generate は誰の func かというとここで設定されているものの様子

// initGenerators initializes generators to be used by XdsServer.
func (s *DiscoveryServer) initGenerators() {
	edsGen := &EdsGenerator{Server: s}
	s.StatusGen = NewStatusGen(s)
	s.Generators[v3.ClusterType] = &CdsGenerator{Server: s}
	s.Generators[v3.ListenerType] = &LdsGenerator{Server: s}
	s.Generators[v3.RouteType] = &RdsGenerator{Server: s}
	s.Generators[v3.EndpointType] = edsGen
	s.Generators[v3.NameTableType] = &NdsGenerator{Server: s}
	s.Generators[v3.ExtensionConfigurationType] = &EcdsGenerator{Server: s}

	s.Generators["grpc"] = &grpcgen.GrpcConfigGenerator{}
	s.Generators["grpc/"+v3.EndpointType] = edsGen
	s.Generators["grpc/"+v3.ListenerType] = s.Generators["grpc"]
	s.Generators["grpc/"+v3.RouteType] = s.Generators["grpc"]
	s.Generators["grpc/"+v3.ClusterType] = s.Generators["grpc"]

	s.Generators["api"] = &apigen.APIGenerator{}
	s.Generators["api/"+v3.EndpointType] = edsGen

	s.Generators["api/"+TypeURLConnect] = s.StatusGen

	s.Generators["event"] = s.StatusGen
}

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/discovery.go#L492-L515

もともとのモチベーションを再度思い出すと routing の挙動が知りたかった
つまりここを読めばよいということになる
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/xds/rds.go#L60

Shimpei OtsuboShimpei Otsubo

generator はこれで生成している様子なので

// NewConfigGenerator creates a new instance of the dataplane configuration generator
func NewConfigGenerator(plugins []string, cache model.XdsCache) ConfigGenerator {
	return v1alpha3.NewConfigGenerator(registry.NewPlugins(plugins), cache)
}

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/networking/core/configgen.go#L53-L56

ここが本体

func (configgen *ConfigGeneratorImpl) BuildHTTPRoutes(node *model.Proxy, push *model.PushContext,

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/networking/core/v1alpha3/httproute.go#L44

Shimpei OtsuboShimpei Otsubo

model.WatchedResourceResourceNames という field が []string でこれを routeNames という変数に入れて loop しているんだけど具体的に何が入っているのかわからない
route という抽象概念がある?

Shimpei OtsuboShimpei Otsubo

勝手に想像してた作りは下のような感じ

  • virtualservice や destinationrule を常に watch して正しい route を持っている
  • 必要に応じて envoy にくばる

しかし見ている感じありえる route を先に知っていて、それに応じて必要な resource を見に行っている?

Shimpei OtsuboShimpei Otsubo

それぞれの route は下のようなものらしい、すごいざっくりには kubernetes の service に対応するものだと思ってれば今回は問題なさそう

$ jq < bar.json '.configs[4].dynamic_route_configs[0]'                                                                                                                                                                                                                                                                                                                                                                                                                                  21-01-15 0:18:23
{
  "version_info": "2021-01-14T13:08:44Z/8",
  "route_config": {
    "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
    "name": "kube-dns.kube-system.svc.cluster.local:9153",
    "virtual_hosts": [
      {
        "name": "kube-dns.kube-system.svc.cluster.local:9153",
        "domains": [
          "kube-dns.kube-system.svc.cluster.local",
          "kube-dns.kube-system.svc.cluster.local:9153",
          "kube-dns.kube-system",
          "kube-dns.kube-system:9153",
          "kube-dns.kube-system.svc.cluster",
          "kube-dns.kube-system.svc.cluster:9153",
          "kube-dns.kube-system.svc",
          "kube-dns.kube-system.svc:9153",
          "10.96.0.10",
          "10.96.0.10:9153"
        ],
        "routes": [
          {
            "match": {
              "prefix": "/"
            },
            "route": {
              "cluster": "outbound|9153||kube-dns.kube-system.svc.cluster.local",
              "timeout": "0s",
              "retry_policy": {
                "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
                "num_retries": 2,
                "retry_host_predicate": [
                  {
                    "name": "envoy.retry_host_predicates.previous_hosts"
                  }
                ],
                "host_selection_retry_max_attempts": "5",
                "retriable_status_codes": [
                  503
                ]
              },
              "max_stream_duration": {
                "max_stream_duration": "0s"
              }
            },
            "decorator": {
              "operation": "kube-dns.kube-system.svc.cluster.local:9153/*"
            },
            "name": "default"
          }
        ],
        "include_request_attempt_count": true
      }
    ],
    "validate_clusters": false
  },
  "last_updated": "2021-01-14T13:10:27.649Z"
}
Shimpei OtsuboShimpei Otsubo

routeNamevirtualHosts の関係はわかりそうでイマイチわからないところがある
allow_any があると port 番号ごとにまとめられる...?

jq <  bar.json '[.configs[4].dynamic_route_configs[].route_config | {"virtual_hoshs": [.virtual_hosts[].name], "name": .name }] | sort_by(.name)'                                                                                                                                                                                                                                                                                                                                     21-01-15 0:42:19
[
  {
    "virtual_hoshs": [
      "allow_any",
      "istiod.istio-system.svc.cluster.local:15010"
    ],
    "name": "15010"
  },
  {
    "virtual_hoshs": [
      "allow_any",
      "istiod.istio-system.svc.cluster.local:15014"
    ],
    "name": "15014"
  },
  {
    "virtual_hoshs": [
      "allow_any",
      "istio-egressgateway.istio-system.svc.cluster.local:80",
      "istio-ingressgateway.istio-system.svc.cluster.local:80"
    ],
    "name": "80"
  },
  {
    "virtual_hoshs": [
      "allow_any",
      "details.default.svc.cluster.local:9080",
      "productpage.default.svc.cluster.local:9080",
      "ratings.default.svc.cluster.local:9080",
      "reviews.default.svc.cluster.local:9080"
    ],
    "name": "9080"
  },
  {
    "virtual_hoshs": [
      "istio-ingressgateway.istio-system.svc.cluster.local:15021"
    ],
    "name": "istio-ingressgateway.istio-system.svc.cluster.local:15021"
  },
  {
    "virtual_hoshs": [
      "kube-dns.kube-system.svc.cluster.local:9153"
    ],
    "name": "kube-dns.kube-system.svc.cluster.local:9153"
  }
]
Shimpei OtsuboShimpei Otsubo

今気になっているのはここの記述

Although the order of evaluation for rules in any given source VirtualService will be retained, the cross-resource order is UNDEFINED.

https://istio.io/latest/docs/ops/best-practices/traffic-management/#split-virtual-services

ここで UNDEFINED といっているのは

	virtualServices = egressListener.VirtualServices()

https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/networking/core/v1alpha3/httproute.go#L208

ここで帰ってくる slice の順序に依存するということかなと思う

Shimpei OtsuboShimpei Otsubo

次のような順で func が呼び出されていく

  • BuildHTTPRoutes
  • buildSidecarOutboundHTTPRouteConfig
  • buildSidecarOutboundVirtualHosts
  • BuildSidecarVirtualHostsFromConfigAndRegistry
  • buildSidecarVirtualHostsForVirtualService
  • BuildHTTPRoutesForVirtualService

がこの中でどこにも VirtualService を merge 指定そうな場所がない
gateway の方にはそれっぽいのがあるんだが...
https://github.com/istio/istio/blob/295f9a900dc6c9f0d5fd21dfaa8df713e0d83aee/pilot/pkg/networking/core/v1alpha3/gateway.go#L314

と思ってもう一度 document を読みに行ったら

A VirtualService can only be fragmented this way if it is bound to a gateway. Host merging is not supported in sidecars.

と書いてある

つまり自分が知りたかった実装はそもそも存在していなかったということだった
悲しい

Shimpei OtsuboShimpei Otsubo

ではなぜ gateway だけこの機能が実装されているのか

https://github.com/istio/istio/commit/a3a99307f6cf3636fdfa91dc70ee04ebe62c06f7
これが CombineVHostRoutes が実装された commit
この commit message に

restrict merging to gateways only

とある

PR は https://github.com/istio/istio/pull/8114 だがこれは https://github.com/istio/istio/pull/8113 の replica だと書いてある。確証はないが、release branch にも master にも入れることで何らかのプロジェクトの release flow に従っているのかなと思った。

そしてもともとの PR では https://github.com/istio/istio/issues/7793 を gateway のために直していると書いてある。ここになぜ sidecar に対応しなかったのかは書いていない。
そこで元 issue を読みに行くことにする。

Shimpei OtsuboShimpei Otsubo

ちょっと長くて全部は読んでないけど基本的に sidecar のについては何も考えてないように見えるけどそれがなぜなのかわからなかった
少なくともこの issue でリンクされている他の issue ではどれも gateway など外部からの request を受け取る virtual service について議論しているということだけわかった

Merging virtual services at the sidecar requires a lot of intrusive changes to the RDS code.

https://github.com/istio/istio/pull/8113

と書いているのでとりあえずなんか難しいということしかわからない

Shimpei OtsuboShimpei Otsubo

https://github.com/istio/istio/issues/22997 で sidecar にどうやって入れるのかについて議論している
mesh gateway というのが sidecar のことなんじゃないかと思っている。
とりあえず色んな人が同じように困っていていろんな workaround を考えているということがわかった

Shimpei OtsuboShimpei Otsubo

結論

わかった istio の全体像

  • istio を構成するコマンドは全部で4つある
    • pilot-discovery
    • pilot-agent
    • istioctl
    • operator
  • pilot-discovery
    • xDS をしゃべる全 envoy に config を巻く
    • VirtualService などの情報を集めて envoy の API 形式に変換する君
  • pilot-agent
    • 適切な引数で envoy を起動するしている
    • sidecar でも動いているし、ingress, egress としても動いている
  • istioctl
    • utility な command line でこれ自体はなくても istio は使える
  • operator
    • istio の install や upgrade を簡単にしてくれる utility