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.
Do
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
.
$ minikube service first --url
http://192.168.99.107:32183
Note: if you omit the
--url
part 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>
Learn
Service
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.
Cluster IP
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.
Node Port
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.
Load Balancer
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.
API Queries
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.
Environment Variables
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 exec
command which is a way to execute arbitrary commands in an existing pod. By default Kubernetes provides a service calledkubernetes
for the Kubernetes API, so we can grep for the service name in environment variables to see what is exposed.
Kube DNS
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 first.svc.default.kubernetes.local
.
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.