3. Accessing your application
So far we’ve installed Kubernetes, run our first app, and learned how to scale that app. However still haven’t made the application accessible.
We can make it accessible by creating a Service which we can do with the
kubectl expose command like so:
$ kubectl expose deployment first --type=LoadBalancer --port 80 service/first exposed
After a few moments we can check what resources are now running in Kubernetes with
kubectl get all:
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/first-765d49ddcb-92vv9 1/1 Running 0 20h pod/first-765d49ddcb-f6hb4 1/1 Running 0 20h pod/first-765d49ddcb-gcgkn 1/1 Running 0 20h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/first LoadBalancer 10.108.204.60 <pending> 80:31063/TCP 2m4s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/first 3/3 3 3 41h NAME DESIRED CURRENT READY AGE replicaset.apps/first-765d49ddcb 3 3 3 20h
You can see we now have a new Service resource of type
LoadBalancer. On cloud providers that support Load Balancers this would have wired up a Load Balancer to provide access to your application. Minikube does not have a Load Balancer and instead provides makes your application available via
$ minikube service first --url http://192.168.99.107:32183
Note: if you omit the
--urlpart of the command it will automatically open your preferred browser to the URL.
You can then use curl or a web browser to access that URL:
$ curl -s `minikube service first --url` | head -n 4 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title>
For a more in-depth look at services see the official Kubernetes Documentation.
Services are an abstract way to expose applications running on sets of Pods as a network service.
Services achieve this by watching Pods for specific metadata and coordinating with kube-proxy to make them accessible, and kube-dns for service discovery.
Services can be exposed in various ways depending on the access required.
The default service type is
clusterIP which simply provides an IP address that is accessible on every worker node that forwards each request to one of the Pods being managed by the service.
This IP is only accessible inside the cluster and is perfect for Pod to Pod communication. The obvious use case here is for a web Pod to connect to a database backend pod.
When you want to provide external access to a service you set the type to
nodePort which will forward an specific port from every worker node into the service. Armed with the IP address of a worker node, and the specified port you can access your service from outside of the cluster.
Having to know the IP and Port of a
nodePort service can be painful. Since Kubernetes is already tracking this information when you set the service type to
loadBalancer it [the Cloud Controller Manager] will actually go out and configure a Load Balancer in your infrastructure with this information and will keep it up to date.
This means that as long as you know the IP address of your Load Balancer (will be provided by Kubernetes) you can access your application via a set IP or Vanity URL.
Kubernetes Network Proxy (kube-proxy)
kube-proxy runs on each worker node and provides the network skaffolding for services. It can do simple TCP, UDP, and SCTP stream forwarding or round robin TCP, UDP, and SCTP forwarding across a set of backends.
Kubernetes Service Discovery (kube-dns)
Kubernetes provides service discovery for services running in the cluster. It does this via three primary mechanisms; API Queries, DNS, Environment variables.
This is the least used method as it requires you to known the Kubernetes API and query it appropriately for sets of Pods or Services.
When running a Pod the Kubelet adds a set of environment variables for each active service. These environment variables can be used by your application to configure itself. However they are statically created when the Pod is launched. An example of this would be:
$ kubectl exec -ti first-765d49ddcb-wtp5k -- env | grep KUBERNETES KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 KUBERNETES_SERVICE_HOST=10.96.0.1 KUBERNETES_SERVICE_PORT=443
Note: above we used the
kubectl execcommand which is a way to execute arbitrary commands in an existing pod. By default Kubernetes provides a service called
kubernetesfor the Kubernetes API, so we can grep for the service name in environment variables to see what is exposed.
kube-dns A cluster aware DNS server watches the Kubernetes API and modifies DNS records as Services are created or modified.
Our service named
first had a DNS record registered for it. Pods running in the same namespace can access it simply by the short name
first. Pods in another namespace can access it via
first.default (since we haven’t yet specified namespaces, we’re using the default).
Kubernetes also supports DNS SRV (Service) records for named ports. We don’t have a named port for the
first service but if we did you could do a DNS SRV query for _http._tcp.my-service.my-ns to discover the port number for “http”, as well as the IP address.
is even more useful than environment variables. and will resolve DNS inside the cluster. For example our
first service will be accessible via
Kube DNS is designed purely for use inside the cluster to allow cross Pod communication. External service discovery needs to be solved with additional tooling.