Following the interactive tutorial on kubernetes.io where a NodePort
type service is created via kubectl expose deploy kubernetes-bootcamp --type="NodePort" --port 8080
I am confused about the results.
First the tutorial states
Let's run (...)
kubectl get services
(...) we see that the service received (...) an external-IP (the IP of the Node).
This is not true according to the output, no external IP:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORTS
kubernetes-bootcamp NodePort ... <none> 8080:31479
Now with kubectl describe service kubernetes-bootcamp
one gets:
Type NodePort
IP ...
Port <unset> 8080/TCP
NodePort <unset> 31479/TCP
Now the tutorial suggests to curl $(minikube ip):$NODE_PORT
which equals curl <internal node IP>:<NodePort>
- which works, but leaves many questions:
kubectl expose -h
the --port
command specifies the port on which to serve, thus I expected --port
to set the exposed <NodePort>
for NodePort
type services, but that is not the case. Why and how to set it?<internal node IP>
under which the container app can indeed be reached is not shown anywhere in the service metadata, I can only get it from kubectl get nodes
. Why?IP
shown through kubectl describe service
(which equals the cluster IP) combined with the port also shown there (which equals the one set via --port
) also routes to the container app, thus curl <clusterIP>:8080
works and fulfills the promise that you can set the exposed port via --port
. But in an unexpected way: why does a NodePort
service have such a clusterIP
type interface?Endpoints
shown through kubectl describe service
if it is neither the clusterIP
endpoint nor the NodePort
endpoint?It will help to break this down but the general theme is that NodePort isn't the most common way to expose services outside of a cluster so some of the features around it aren't all that intuitive. LoadBalancer (or an ingress controller using it) is the preferred way to expose services to the world outside the cluster.
The IP shown through
kubectl describe service
(which equals the cluster IP) combined with the port also shown there (which equals the one set via--port
) also routes to the container app, thuscurl <clusterIP>:8080
works and fulfills the promise that you can set the exposed port via--port
. But in an unexpected way: why does a NodePort service have such a clusterIP type interface?
You also see ClusterIP because that is the way to get to the service within the cluster. Other services can reach that service within the cluster using that service name and port (8080 in this case) but ClusterIP in itself doesn't make the service available outside of the cluster.
What is the meaning of the Endpoints shown through
kubectl describe service
if it is neither the clusterIP endpoint nor the NodePort endpoint?
The endpoints value of 172.18.0.2:8080
or similar will be the internal IP and port for the service. Within the cluster you can also reach the service via its name using the cluster's internal dns (which is normally the more convenient way).
The
<internal node IP>
under which the container app can indeed be reached is not shown anywhere in the service metadata, I can only get it fromkubectl get nodes
. Why?
This links to the point that the output you see from kubectl get services
and kubectl describe service <service>
when exposing via NodePort is a bit misleading - one would expect to see something in or analogous to external-ip
for the entry. NodePort dedicates a port on that node just for that service. This is a bit limiting as if you kept doing that for each service you add then you'll run out of ports. So the external-ip
if one were to be shown would have to be the IP of the node, which might look a bit odd when shown alongside the IPs of LoadBalancer-type services.
According to
kubectl expose -h
the--port
command specifies the port on which to serve, thus I expected--port
to set the exposedNodePort
for NodePort type services, but that is not the case. Why and how to set it?
The expose command-line option is especially limited as you currently can't even use it to choose which port in the NodePort range is used. In your case you've been given the external port 31479 but you didn't get to choose that. It might not be advisable to let users choose that too easily as there are a limited number of ports available and you could easily find yourself allocating the same port twice. But you can choose it the external port when deploying via descriptor files.