Exposing a Kubernetes Cluster Externally with a Service

This page explains how to expose applications inside and outside a cluster by using a Service.

Connecting from inside the cluster

First, check connectivity inside the cluster. Create a Pod that contains a curl container, connect to its Shell, and then test access from curl to the helloworld Pod.

First, create and run a test Pod.

% kubectl run --port 8080 --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld
pod/helloworld created

Display the Pod list including IP addresses by adding -o wide.

% kubectl get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
helloworld   1/1     Running   0          3m44s   172.17.0.3   minikube   <none>           <none>

Enter the cluster and run curl.

% kubectl run --image curlimages/curl:latest -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $ curl 172.17.0.3:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

This result shows that the curl container can connect to the helloworld container because it specifies helloworld’s IP address, 172.17.0.3, and accesses port 8080.

To confirm that the Pods are inside the same cluster, leave the curl container running, open another terminal, and display the Pod list with IP addresses.

% kubectl get pod -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
curl         1/1     Running   0          23s   172.17.0.4   minikube   <none>           <none>
helloworld   1/1     Running   0          12m   172.17.0.3   minikube   <none>           <none>

You can confirm that the containers exist on the same network.

Connecting from outside the cluster

Next, if you try to connect from outside the cluster, it will not work. This is the same as Docker, because the host IP range and the Pod IP range are different. To make access possible, you must create a Service and expose the Pod externally.

A Service is an L4 load balancer with a static IP address that exposes Pods inside or outside the cluster. Because a Pod itself may stop at any time, the Service is said to expose the destination that traffic is distributed to.

There are three types of Service, each with the following characteristics.

  • ClusterIP Service (access from inside the cluster)
    It abstracts the IP of a Pod, which may disappear at any time, and places a proxy with a static IP. This has the following advantages.
    1. You do not need to know the Pod IP when accessing a Pod.
    2. It load balances access to Pods.
  • NodePort Service (access from inside and outside the cluster)
    It makes it possible to expose Pods outside the cluster through a Node IP and NodePort, which ClusterIP cannot do.
    However, it also has the following disadvantages.
    1. You must know the Node IP.
    2. You must know the Node Port.
  • LoadBalancer Service (access from inside and outside the cluster)
    It routes from the provider’s L4 load balancer DNS to a specific port on each node and accesses the Pod.
    Therefore, you do not need to know the Node IP or Node port. However, it also has the following disadvantages.
    1. One load balancer is created for each Service, which makes it expensive.
    2. Because it is an L4 load balancer, it cannot perform LB distribution by L7 HTTP host or path.

Now let’s check each Service type above.

ClusterIP Service

Expose the HelloWorld Pod inside the cluster with a ClusterIP Service and check that it can be accessed from another Pod in the same cluster.
You can expose a running Pod as a ClusterIP Service by specifying the --type ClusterIP option as follows.

kubectl expose pod [Pod name] --type ClusterIP [Service name]

First, create and run a test Pod. *(If a Pod with the same name already exists, delete it with kubectl delete pod helloworld.)

% kubectl run --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld
pod/helloworld created

Expose the helloworld Pod with the Service name helloworld-clusterip.

% kubectl expose pod helloworld --type ClusterIP --port 8080 --name helloworld-clusterip
service/helloworld-clusterip exposed

Use the following command to list Services.

% kubectl get service
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
helloworld-clusterip   ClusterIP   10.98.60.0   <none>        8080/TCP   25s
kubernetes             ClusterIP   10.96.0.1    <none>        443/TCP    16h

The result shows that a Service whose Type is ClusterIP has been created in the first row.

Next, start the curl container again and access helloworld through helloworld-clusterip from inside that container.

% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

Use the curl command from the opened Shell.

/ $ curl 10.98.60.0:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld

The result confirms that helloworld can be accessed. This result is the same as the result in “Connecting from inside the cluster” above.

However, the earlier example connected directly from the curl container to the helloworld container. In this run, the curl container accesses the helloworld-clusterip Service, and then the Service connects to helloworld through its load balancing function, although only one Pod has been created so the destination is effectively fixed. The meaning is different, so be careful.

NodePort Service

Expose the HelloWorld Pod inside and outside the cluster with a NodePort Service and check that it can be accessed from another Pod in the same cluster.

You can expose a running Pod as a NodePort Service simply by specifying the --type NodePort option as follows.

kubectl expose pod [Pod name] --type NodePort [Service name]

Expose the helloworld Pod with the Service name helloworld-nodeport.

% kubectl expose pod helloworld --type NodePort --port 8080 --name helloworld-nodeport
service/helloworld-nodeport exposed

Use the following command to list Services.

% kubectl get service
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloworld-nodeport    NodePort    10.110.238.186   <none>        8080:31942/TCP   38s
kubernetes             ClusterIP   10.96.0.1        <none>        443/TCP          16h

The result shows that a Service whose Type is NodePort has been created in the first row.

Next, check whether it can be accessed from inside the same cluster. The procedure is the same as ClusterIP. First, delete the existing curl container and create it again.

% kubectl delete pod curl
% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

Then, from inside that container, access helloworld through helloworld-nodeport.

/ $ curl 10.110.238.186:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

The result confirms that helloworld can be accessed through helloworld-nodeport.

Next, check whether it can be accessed from outside the cluster, meaning the local environment. You need the minikube IP for the connection, so run the following command to look up the IP.

% minikube ip
192.168.49.2

Run curl against this IP from the local environment. Specify the Pod’s port 31942.

/ $ curl 192.168.49.2:31942
Hello, world!
Version: 1.0.0
Hostname: helloworld

The connection result confirms that helloworld can be accessed.
(Depending on the settings used when starting minikube, access may not be possible. In that case, run minikube service --url helloworld-nodeport and execute curl against the printed URL.)

LoadBalancer Service

Expose the HelloWorld Pod inside and outside the cluster with a LoadBalancer Service and check that it can be accessed from another Pod in the same cluster.

You can expose a running Pod as a LoadBalancer Service by specifying the --type LoadBalancer option as follows.

kubectl expose pod [Pod name] --type LoadBalancer [Service name]

Expose the helloworld Pod with the Service name helloworld-loadbalancer.

% kubectl expose pod helloworld --type LoadBalancer --port 8080 --name helloworld-loadbalancer
service/helloworld-loadbalancer exposed

Use the following command to list Services.

% kubectl get service
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloworld-loadbalancer   LoadBalancer   10.104.176.59    <pending>     8080:30100/TCP   27s
kubernetes                ClusterIP      10.96.0.1        <none>        443/TCP          16h

The result shows that a Service whose Type is LoadBalancer has been created in the first row.

First, check whether it can be accessed from inside the same cluster. The procedure is the same as ClusterIP.

% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

Use the curl command from the opened Shell.

/ $ curl 10.104.176.59:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

The connection result confirms that helloworld can be accessed through helloworld-LoadBalancer.

Then check whether it can be accessed from outside the cluster, meaning the local environment.

% minikube service helloworld-loadbalancer --url
http://127.0.0.1:53240
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

Leave the console open, open another console, and check access with the following command.

% curl http://127.0.0.1:53240
Hello, world!
Version: 1.0.0
Hostname: helloworld

When executed, you can confirm that helloworld is accessible.