云原生应用管理:原理与实践
上QQ阅读APP看书,第一时间看更新

4.1.5 setupConnection

下面我们继续深入了解Client是如何将这个request发送给Tiller的。我们先回过头来看一个问题,在install命令行时,Helm还执行了一个操作,使用kubectl port-forward临时在本地宿主机打通一个与Tiller Pod沟通的通道,首先看一下代码。


......
cmd := &cobra.Command{
    Use:     "install [CHART]",
    Short:   "Install a Chart archive",
    Long:    installDesc,
    PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
    RunE: func(cmd *cobra.Command, args []string) error {
......

需要注意的是,PreRunE函数是在RunE函数之前执行的。也就是说其实在执行install的那些命令之前,setupConnection函数使用kubectl port-forward功能在宿主机与Tiller Pod之间建立了一个通道,下面看看具体实现。


func setupConnection() error {
  if settings.TillerHost == "" {
    config, client, err := getKubeClient(settings.KubeContext, settings.KubeConfig)
    if err != nil {
      return err
    }

    TillerTunnel, err = portforwarder.New(settings.TillerNamespace, client, config)
    if err != nil {
      return err
    }

    settings.TillerHost = fmt.Sprintf("127.0.0.1:%d", TillerTunnel.Local)
    debug("Created tunnel using local port: '%d'\n", TillerTunnel.Local)
  }

  // 设置gRPC config
  debug("SERVER: %q\n", settings.TillerHost)

  // 支持插件
  return nil
}

由此可见,根据默认的kubeConfig获取Kubernetes client命令行实例后,portforwarder.New(settings.TillerNamespace,client,config)调用了kubectl port-forward命令。

举个例子,kubectl port-forward pod/Tiller 8888:5000这个命令就是建立一个连接,首先监听本地8888端口,然后远程连接Tiller这个Pod的5000端口。也就是说,向本地8888端口发送的数据都会直接被转发到远程的Tiller Pod 5000端口上,这样就建立了一个本地和远程容器之间的通道。而且这个通道是通过ApiServer连接的,不需要Pod向外暴露任何端口,非常安全。

这里调用本地宿主机的端口是随机的,任何一个端口都可以被调用,然后被调用的端口会被指向远程的Tiller Pod,函数settings.TillerHost=fmt.Sprintf("127.0.0.1:%d",TillerTunnel.Local)就是将随机选择的本地端口指定到setting结构体中。这样在执行其他函数之前,我们的本地其实已经默默建好了与远程Tiller之间的通信链路。