LoginSignup
1
1

More than 5 years have passed since last update.

istioctl kube-inject コマンドについて

Posted at

概要

istioctl kube-inject コマンドで実際の処理はどうなっているのかコードから追ってみます。
基本的に下記のコードを読み進めていきました。

kube-injectとは

アプリケーションPodを起動する際に、サイドカーコンテナ(envoy)をinjectするためのコマンドです。これにより、ユーザがいちいちサイドカーコンテナ用のyamlを記述する必要が無くなります。

使い方

kubectl apply -f <(istioctl kube-inject -f <resource.yaml>)

ソースコード

まず最初に、コマンドで指定されたyamlファイルを読み込みます。(-の場合は、標準入力からyamlを取得)
※そこまで重要ではないので、さらっと読み飛ばしてください。

if !emitTemplate {
    if inFilename == "-" {
        reader = os.Stdin
    } else {
        var in *os.File
        if in, err = os.Open(inFilename); err != nil {
            return err
        }
        reader = in
        defer func() {
            if errClose := in.Close(); errClose != nil {
                log.Errorf("Error: close file from %s, %s", inFilename, errClose)

                // don't overwrite the previous error
                if err == nil {
                    err = errClose
                }
            }
        }()
    }
}

続いて、出力の設定です。outFilenameが無ければ、標準出力に、ファイル名が指定されていればファイルに出力します。
※そこまで重要ではないので、さらっと読み飛ばしてください。

var writer io.Writer
if outFilename == "" {
    writer = c.OutOrStdout()
} else {
    var out *os.File
    if out, err = os.Create(outFilename); err != nil {
        return err
    }
    writer = out
    defer func() {
        if errClose := out.Close(); errClose != nil {
            log.Errorf("Error: close file from %s, %s", outFilename, errClose)

            // don't overwrite the previous error
            if err == nil {
                err = errClose
            }
        }
    }()
}

さて、この辺りから本題meshConfigを取得する処理です。 引数で--meshConfigFileを指定した場合はcmd.ReadMeshConfig(meshConfigFile) でファイルから読み込みますが、基本的に指定するケースは稀だと思いますので、getMeshConfigFromConfigMap(kubeconfig) を詳しく見ていきます。

var meshConfig *meshconfig.MeshConfig
if meshConfigFile != "" {
    if meshConfig, err = cmd.ReadMeshConfig(meshConfigFile); err != nil {
        return err
    }
} else {
    if meshConfig, err = getMeshConfigFromConfigMap(kubeconfig); err != nil {
        return err
    }
}

こちらでは、meshConfigMapの設定を取得しています。そのyamlの設定を構造体*meshconfig.MeshConfigに変換してreturnしています。

func getMeshConfigFromConfigMap(kubeconfig string) (*meshconfig.MeshConfig, error) {
    client, err := createInterface(kubeconfig)
    if err != nil {
        return nil, err
    }

    config, err := client.CoreV1().ConfigMaps(istioNamespace).Get(meshConfigMapName, metav1.GetOptions{})
    if err != nil {
        return nil, fmt.Errorf("could not read valid configmap %q from namespace  %q: %v - "+
            "Use --meshConfigFile or re-run kube-inject with `-i <istioSystemNamespace> and ensure valid MeshConfig exists",
            meshConfigMapName, istioNamespace, err)
    }
    // values in the data are strings, while proto might use a
    // different data type.  therefore, we have to get a value by a
    // key
    configYaml, exists := config.Data[configMapKey]
    if !exists {
        return nil, fmt.Errorf("missing configuration map key %q", configMapKey)
    }
    cfg, err := model.ApplyMeshConfigDefaults(configYaml)
    if err != nil {
        err = multierr.Append(fmt.Errorf("istioctl version %s cannot parse mesh config.  Install istioctl from the latest Istio release",
            version.Info.Version), err)
    }
    return cfg, err
}

ここで、clientとは、kubernetesのリソースを取得するためのclientです。ポイントは下記コードになるのですが、istioNamespace (defaultはistio-system)のmeshConfigMapName(defaultはmesh)のconfigMapを取得しています。
※ go-clientの詳細が知りたければこちらのreadmeを読むと良いと思います。

config, err := client.CoreV1().ConfigMaps(istioNamespace).Get(meshConfigMapName, metav1.GetOptions{})

要するに、下記と同等の意味があると考えるとイメージがわくと思います。configmap名がistio、namespaceがistio-systemです。

$ kubectl get configmaps istio -o yaml -n istio-system

meshConfigのあとは、いよいよsidecarTemplateを作ります。適用順序についてですが、まず、injectConfigFile != "" で引数に--injectConfigFileが指定されているかチェックします。もし指定されていればそちらを優先、指定されていなければgetInjectConfigFromConfigMap(kubeconfig)で取得します。getInjectConfigFromConfigMap(kubeconfig)の中身は、先ほどのgetMeshConfigFromConfigMap(kubeconfig)とほぼ一緒なので、割愛します。

else if injectConfigFile != "" {
    injectionConfig, err := ioutil.ReadFile(injectConfigFile) // nolint: vetshadow
    if err != nil {
        return err
    }
    var config inject.Config
    if err := yaml.Unmarshal(injectionConfig, &config); err != nil {
        return err
    }
    sidecarTemplate = config.Template
} else {
    if sidecarTemplate, err = getInjectConfigFromConfigMap(kubeconfig); err != nil {
        return err
    }
}

構造体を作り終えたら最後にyamlをアウトプットして終了となります。

return inject.IntoResourceFile(sidecarTemplate, meshConfig, reader, writer)

まとめ

kube-injectコマンドのソースコードを見て、コマンドがどんな処理をしているか把握することができました。ほんとこの辺りの処理よく実装したなーと思います。ちなみに、今はauto injectionが主流なので、このコマンドを使うことは少なくなりましたが、autoの場合でも、アプリケーションPodの定義をapplyしたタイミングでapplyのリクエストをhookして上記で見てきたことと同等の処理を実施している(と思われます)。ちゃんと見てないので、気になった方は確認してみてください。

参考

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1