kubehost
Kubehost helps you expose services directly on nodes of your Google Kubernetes Engine (GKE) cluster.
The common way to expose a service and get an external IP is
kubectl expose <deployment> --type=LoadBalancer"
, which will expose
your deployment on a production-grade Google Cloud Load Balancer.
Sometimes you just want to expose a service on your VM directly, like
during development where uptime and reliability are not as important.
That's where Kubehost comes in.
Kubehost uses existing features of GKE to expose your service directly onto one of the VMs in your cluster, by creating a Pod that runs on the VM's network and forwards traffic to your in-cluster (ClusterIP) service, and creating firewall rules to permit external traffic. While you could do this manually, Kubehost takes the toil out of managing this configuration by automating the necessary actions.
β οΈ For development use onlykubehost is NOT designed for production use! Nodes in GKE are designed to be redundant, meaning they can fail. When the node on which your service is exposed via kubehost fails or is upgraded, your service will experience several minutes of downtime. By comparison, if you use a production-grade Google Cloud Load Balancer (and you have enough replicas of your Pod spread over multiple nodes with properly implemented health and readiness checks) then a node can fail with only minimal impact to the availability of your service. At any time you can upgrade to a Google Cloud Load Balancer with the
kubehost upgrade
command.
Installation
kubehost
is a bash script. To install, clone this repository and add
it to your $PATH
, or copy kubehost
to your /usr/local/bin/
.
You may need to set the executable permission, i.e. chmod +x kubehost
.
Configuration
Before using kubehost
, you need to ensure both gcloud
and kubectl
are configured with your desired project & cluster.
- run
gcloud init
to select your account, project and region containing the GKE cluster. - run
get-credentials
to configure
kubectl
.
Exposing a Deployment with kubehost
- Create your deployment like normal.
- Create a ClusterIP service for your deployment (this is the default service type, so no need to specify any type), on your desired external port.
- Run
kubehost bind ${SERVICE}
, where${SERVICE}
is the name of the Kubernetes service you created at step 2.
What this does is create some "glue" in the form of a hostPort deployment so that your service is bound to port you specified in the service on your node's external IP (read "under the hood" for a longer technical description). It also opens the necessary GCP firewall rules.
To undo, kubehost unbind ${SERVICE}
Complete example:
kubectl run hello --image gcr.io/google-samples/hello-app:1.0 --port 8080
kubectl expose deployment hello --port 80 --target-port 8080 --name hello-service
kubehost bind hello-service
Cleanup:
kubehost unbind hello-service
kubectl delete deployment hello
kubectl delete service hello-service
Switching between hostPort and a Load Balancer
Upgrading to a Load Balancer from hostPort
Is your app ready for prime time? Remove the hostPort Pod "glue", and convert your Service into one backed by a Google Cloud Load Balancer with one simple command:
kubehost upgrade ${SERVICE}
Where ${SERVICE}
is the name of your Cluster IP service.
Downgrading a Load Balancer to hostPort
Did you already expose your service with a Load Balancer and found it's more than you needed? Convert it to an internal ClusterIP service, and expose it on a host in one command with:
kubehost downgrade ${SERVICE}
Where ${SERVICE}
is the name of your Kubernetes service of type
LoadBalancer.
Limitations
- Kubehost currently works with services that have a single port. If you need to expose two ports, create two ClusterIP services.
- Kubehost is not designed for production usage, see the note above.
- Kubehost doesn't give you a static IP. The IP address of node may change which will affect your service. You can create a static IP and use the kubeIP operator to keep it assigned through node maintenance events.
Under the Hood
What Kubehost is doing when you call bind
is creating
a Kubernetes Deployment with a single replica of a Pod that uses
hostPort to bind onto the host's network interface. The container in
this Pod forwards traffic to your ClusterIP service.
While you could instead change your deployment to use hostPort directly we think this approach is superior, as:
- It's closer to the production Kubernetes experience where deployments have a matching service to receive traffic.
- It's easier to switch between this and a production setup by changing the Service type to LoadBalancer, and removing the hostPort deployment (and vice-versa) βΒ no need to modify your application deployment.
- Your deployment's replica count isn't limited by available ports.