IstioOperatorでIngressGatewayをistio-system外に作成する

jagaNikumanPosted by

はじめに

この記事は GRIPHONE Advent Calendar 2020 18日目の記事です。

こんにちは、SREの岩立です。

みなさんIstioを使っていますか?Istioを使っていると、なんらかの理由でIngressGatewayをistio-system外に複数立てたいときがあるかもしれません。

その時にIstioOperatorを使っている場合、どうすればよいのかについて書いていきます。また、付随してIstioOperatorリソースをistio-system外で使用する方法についても書いていきます。

そして、この記事ではIstioOperator及びIstioは導入済みとして進めていきます。まだ導入していない場合は、先日ちょうど記事を書いているため、こちらの記事を参照してみてください。
https://tech.griphone.co.jp/2020/12/12/istio-operator-101/

IngressGatewayのみをistio-system外に作成する

IngressGatewayのみをistio-system外に作成するには、

  1. spec.namespaceを作成したいnamespaceにする
  2. profileをemptyにして、spec.components.ingressGateways.enabledをtrueにする

を行うことで作成することが出来ます。

1のspec.namespaceは、ドキュメントにも書いてありますがこのパラメータで作成するリソースのnamespaceを指定することができます。

2についてですが、profileをemptyにすると何もリソースを生成しないようになります。

$ cat empty.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: ingressgateway
  namespace: istio-system
spec:
  profile: empty
$ istioctl manifest generate -f empty.yaml
$

そのため、profileをemptyにして今回必要なingressgatewayだけをenabledにしてingressgatewayのmanifestを生成するようにします。

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: ingressgateway
  namespace: istio-system
spec:
  components:
    ingressGateways:
    - enabled: true
      name: istio-ingressgateway
  profile: empty

これだけでIngressGatewayをistio-system外に作ることが出来ます。IstioOperatorに感謝ですね。

istio-system外でIstioOperatorリソースを使えるようにするには

先程のIstioOperatorリソースのmanifestでもしれっとnamespace: istio-systemを書いていますが、デフォルトではIstioOperatorリソースはistio-system外に作成してもIstioOperatorは反応せずistioリソースの作成をしてくれません。

理由としては単純で、IstioOperatorが監視するNamespaceがwatchedNamespaceによってデフォルトはistio-systemのみ指定されているためです。
https://istio.io/latest/docs/reference/commands/istioctl/#istioctl-operator-init

ドキュメントではwatchedNamespaceを増やしたい場合はカンマ区切りで羅列してくれれば良いよ~と書いてありますが、各プロジェクトのnamespaceなど不特定多数のnamespaceを監視してほしい場合に毎回羅列するのはとても手間なので今回は全namespaceを監視してもらうようにします。

方法としては、watchedNamespaceに空の文字列を渡してあげると全namespaceを監視してくれます。注意点としては、watchedNamespaceを渡さないのではなく空文字を渡すことです。

helmコマンドのoptionとして、--setでwatchedNamespace=""とするか、values.yamlを編集してあげれば大丈夫です。

# 例
$ helm install istio-operator manifests/charts/istio-operator \
  --set hub=docker.io/istio \
  --set tag=1.8.0 \
  --set operatorNamespace=istio-operator \
  --set watchedNamespaces=""
# Used to replace istioNamespace to support operator watch multiple namespaces.
# watchedNamespaces: istio-system
watchedNamespaces: ""

https://github.com/istio/istio/blob/600d5b5fa294ed2db681479021be00f7e95d6a5e/manifests/charts/istio-operator/values.yaml#L7

この挙動は見た感じドキュメントには乗っていませんでしたが(自分がドキュメントを読み切れていない説は十分にある)、watchedNamespacesがIstioOperatorでどのように使われているのか追っていったら空文字の場合は全namespaceを監視するとのことがわかったので、その追っていった流れを最後に載せておきます。

watchedNamespaceの流れを追ってみた

まず、IstioOperatorのhelmのvalueとして渡すwatchedNamespacesのパラメータは、IstioOperatorのDeploymentにWATCH_NAMESPACEとして環境変数で渡されます。

# istio/manifests/charts/istio-operator/templates/deployment.yaml
env:
  - name: WATCH_NAMESPACE
    value: {{.Values.watchedNamespaces | quote}}

https://github.com/istio/istio/blob/7d99a5683d0764bae4194365dd6e420126885acd/manifests/charts/istio-operator/templates/deployment.yaml#L37-L38

そして、このWATCH_NAMESPACEはoperatorによってgetWatchNamespace関数内のos.LookupEnvで取得されます。

// istio/operator/cmd/operator/server.go
func getWatchNamespace() (string, error) {
	ns, found := os.LookupEnv("WATCH_NAMESPACE")
	if !found {
		return "", fmt.Errorf("WATCH_NAMESPACE must be set")
	}
	return ns, nil
}

https://github.com/istio/istio/blob/600d5b5fa294ed2db681479021be00f7e95d6a5e/operator/cmd/operator/server.go#L81-L87

そして、このgetWatchNamespace関数はどこから呼ばれるのかと言うと、同じserver.go内のrun関数内で呼ばれて、watchNSに格納されます。

func run() {
	watchNS, err := getWatchNamespace()
	if err != nil {
		log.Fatalf("Failed to get watch namespace: %v", err)
	}

https://github.com/istio/istio/blob/600d5b5fa294ed2db681479021be00f7e95d6a5e/operator/cmd/operator/server.go#L110-L113

そして、このwatchNSは以下のコードで用いられます。このコードのコメントにも書かれているように、watchNSが空の場合、すなわちhelmでvalueとして渡しているwatchedNamespacesが空の場合は全namespaceをwatchするという挙動を知ることが出来ました。

if watchNS != "" {
    namespaces := strings.Split(watchNS, ",")
    // Create MultiNamespacedCache with watched namespaces if it's not empty.
    mgrOpt = manager.Options{
        NewCache:                cache.MultiNamespacedCacheBuilder(namespaces),
        MetricsBindAddress:      fmt.Sprintf("%s:%d", metricsHost, metricsPort),
        LeaderElection:          leaderElectionEnabled,
        LeaderElectionNamespace: leaderElectionNS,
        LeaderElectionID:        leaderElectionID,
        RenewDeadline:           renewDeadline,
    }
} else {
    // Create manager option for watching all namespaces.
    mgrOpt = manager.Options{
        Namespace:               watchNS,
        MetricsBindAddress:      fmt.Sprintf("%s:%d", metricsHost, metricsPort),
        LeaderElection:          leaderElectionEnabled,
        LeaderElectionNamespace: leaderElectionNS,
        LeaderElectionID:        leaderElectionID,
        RenewDeadline:           renewDeadline,
    }
}

https://github.com/istio/istio/blob/600d5b5fa294ed2db681479021be00f7e95d6a5e/operator/cmd/operator/server.go#L133-L154

おわりに

今回はIstioOperatorを用いてIngressGatewayをistio-system外に作成する方法と、IstioOperatorのwatchedNamespaceの挙動について書きました。

IstioOperatorは1.7からGAになっているので、これからどんどん使っていきたいと思います。

それでは!