通过 kubernetes 单节点集群创建服务没有 'ingress' 外部 IP,但是 google 容器引擎可以(?)

creating a service via kubernetes single node cluster gives me no 'ingress' external IP, but google container engine does (?)

我正在尝试设置一个单节点 kubernetes 集群用于演示和测试目的,我希望它能够正常运行 像 'full blown' k8s 集群(像 google 容器引擎)。我的客户有自己的 k8s 安装, 对于此讨论,我们可以假设行为非常类似于 google 容器引擎的 k8s 安装。

在完整的 K8s 上获取 Ingress IP

我正在创建一个 wordpress pod 并将其公开为服务,如本教程中所述: https://cloud.google.com/container-engine/docs/tutorials/hello-wordpress

如果你想重现这个问题,只需复制粘贴下面的命令,我从教程中提取了这些命令: (假设您有一个名为 'stellar-access-117903' 的项目。如果没有,请设置为您的 Google 容器的名称 引擎项目。)

# set up the cluster  (this will take a while to provision)
#
 gcloud config set project stellar-access-117903
 gcloud config set compute/zone us-central1-b
 gcloud container clusters create hello-world \
     --num-nodes 1 \
     --machine-type g1-small

# Create the pod, and expose it as a service
#
kubectl run wordpress --image=tutum/wordpress --port=80
kubectl expose rc wordpress --type=LoadBalancer

# Describe the service
kubectl describe services wordpress

describe 命令的输出包含一行 'LoadBalancer Ingress: {some-ip-address}',它是 正是我所期望的。现在,当我对单节点集群设置做同样的事情时 我不 得到那条线。我能够在出现在输出中的 IP 上访问 wordpress 服务 'describe service'命令..但是在'single node'模式下,打印出来的IP是>集群IP< 的服务,通常(据我所知)是不可公开访问的。出于某种原因,它是 在单节点模式下可公开访问。我们可以通过以下步骤复制它。

在单节点 K8s 上未获取 Ingress IP

首先设置单节点 k8s,如本教程所述: https://github.com/kubernetes/kubernetes/blob/master/docs/getting-started-guides/docker.md

为了便于重现,我在下面包含了所有命令,因此您只需 copy/paste:

K8S_VERSION=1.1.1

sudo docker run --net=host -d gcr.io/google_containers/etcd:2.0.12 /usr/local/bin/etcd --addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data


sudo docker run \
    --volume=/:/rootfs:ro \
    --volume=/sys:/sys:ro \
    --volume=/dev:/dev \
    --volume=/var/lib/docker/:/var/lib/docker:ro \
    --volume=/var/lib/kubelet/:/var/lib/kubelet:rw \
    --volume=/var/run:/var/run:rw \
    --net=host \
    --pid=host \
    --privileged=true \
    -d \
    gcr.io/google_containers/hyperkube:v${K8S_VERSION} \
    /hyperkube kubelet --containerized --hostname-override="127.0.0.1" --address="0.0.0.0" --api-servers=http://localhost:8080 --config=/etc/kubernetes/manifests

sudo docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v${K8S_VERSION} /hyperkube proxy --master=http://127.0.0.1:8080 --v=2


# set your context to use the locally running k8s API server 
#
kubectl config set-cluster dev --server=http://localhost:8080
kubectl config set-context dev --cluster=dev  --namespace=$NS
kubectl config use-context dev

现在,执行与 Google Container Engine 的 k8s

相同的命令
# Create the pod, and expose it as a service
#
kubectl run wordpress --image=tutum/wordpress --port=80
kubectl expose rc wordpress --type=LoadBalancer

# Describe the service
kubectl describe services wordpress

最后一条命令的输出(您将看到没有 'Ingress' 信息)是:

Name:           wordpress
Namespace:      default
Labels:         run=wordpress
Selector:       run=wordpress
Type:           LoadBalancer
IP:         10.0.0.61
Port:           <unnamed>   80/TCP
NodePort:       <unnamed>   31795/TCP
Endpoints:      172.17.0.30:80
Session Affinity:   None
No events.

在 google 容器引擎的 k8s 中,我看到诸如“正在创建负载均衡器”、“已创建负载均衡器”之类的事件。但一点也不像 这发生在单节点实例中。

我想知道...我需要做一些配置才能让它们以相同的方式工作吗?非常重要的是,他们 工作方式相同......仅在可扩展性方面有所不同,因为我们希望 运行 针对单节点版本进行测试,并且 如果它的行为不同,将会非常混乱。

在此先感谢您的帮助 -克里斯

LoadBalancer 是由后端云提供商实施的一项功能,因此您看不到在本地设置中创建的功能

(查看云提供商:https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers

这是我们提出的解决方案。当我们 运行 针对单节点 Kubernetes 时,我们通过反复试验意识到,当您公开服务时,外部 IP 不会通过 IngressIP 返回;相反,它通过 clusterIP 返回,如上所述,它是公开可见的。所以,我们只是修改了我们的代码来处理它。我们在单节点情况下使用 clusterIP。这是我们用来在服务上建立监视的代码,以确定 k8s 何时分配了我们的外部可见 IP:

首先我们使用 fabric8 API 创建服务配置:

            case "Service" =>
              val serviceConf = mapper.readValue(f, classOf[Service])
              val service = kube.services().inNamespace(namespaceId).create(serviceConf)
              watchService(service)

'watchService'方法定义如下:

  private def watchService(service: Service) = {
    val namespace = service.getMetadata.getNamespace
    val name = service.getMetadata.getName
    logger.debug("start -> watching service -> namespace: " + namespace + " name: " + name)
    val kube = createClient()
    try {
      @volatile var complete = false
      val socket = kube.services().inNamespace(namespace).withName(name).watch(new Watcher[Service]() {
        def eventReceived(action: Action, resource: Service) {
          logger.info(action + ":" + resource)
          action match {
            case Action.MODIFIED =>
              if (resource.getMetadata.getName == name) {
                complete = isServiceComplete(resource)
              }
            //            case Action.DELETED =>
            //              complete = true
            case _ =>
          }
        }
      })
      while (!complete) {
        Thread.sleep(5000)
        complete = isServiceComplete(kube.services().inNamespace(namespace).withName(name).get)
      }
      logger.info("Closing socket connection")
      socket.close()
    } finally {
      logger.info("Closing client connection")
      kube.close()
    }

    logger.debug("complete -> watching services , namespace: " + namespace + " name: " + name)
  }

我们介绍的关键 hack 是在方法 'isServiceComplete' 中。当使用单节点 k8s 时,'isUsingMock' 的值为真。这样我们就可以使用 clusterIP 来确定服务配置是否已完成。

  private def isServiceComplete(service: Service) = {
    !service.getStatus.getLoadBalancer.getIngress.isEmpty  || mockServiceComplete(service)
  }

  def mockServiceComplete(service: Service): Boolean = {
    val clusterIP  = service.getSpec.getClusterIP
    logger.trace(s"mockServiceComplete:   $isUsingMock /  $clusterIP / $KUBE_SERVER" )
    isUsingMock && ! clusterIP.isEmpty
  }

抱歉,如果这里没有太多额外的上下文。最终我们的项目应该是开源的,我们可以 post 一个完整的解决方案。

-克里斯