概要
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して上記で見てきたことと同等の処理を実施している(と思われます)。ちゃんと見てないので、気になった方は確認してみてください。
参考