<p>I'm new to K8s and am currently using Minikube to play around with the platform. How do I configure a public (i.e. outside the cluster) port for the service? I followed the <a href="https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#exposing-the-service" rel="noreferrer">nginx example</a>, and K8s service tutorials. In my case, I created the service like so:</p>

<pre><code>kubectl expose deployment/mysrv --type=NodePort --port=1234
</code></pre>

<p>The service's port is 1234 for anyone trying to access it from INSIDE the cluster. The minikube tutorials say I need to access the service directly through it's random nodePort, which works for manual testing purposes:</p>

<pre><code>kubectl describe service mysrv | grep NodePort
...
NodePort:                 &lt;unset&gt;  32387/TCP
# curl "http://`minikube ip`:32387/"
</code></pre>

<p>But I don't understand how, in a real cluster, the service could have a fixed world-accessible port. The nginx examples describe something about using the LoadBalancer service kind, but they don't even specify ports there...</p>

<p>Any ideas how to fix the external port for the entire service?</p>

Sagi Mann
<blockquote>
<p>The minikube tutorials say I need to access the service directly through it's random nodePort, which works for manual testing purposes:</p>
</blockquote>
<p>When you create service object of type <code>NodePort</code> with a <code>$ kubectl expose</code> command you cannot choose your <code>NodePort</code> port. To choose a <code>NodePort</code> port you will need to create a <code>YAML</code> definition of it.</p>
<p><strong>You can manually specify the port in service object of type <code>Nodeport</code>  with below example:</strong></p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: v1
kind: Service
metadata:
  name: example-nodeport
spec:
  type: NodePort
  selector:
    app: hello # selector for deployment
  ports:
  - name: example-port
    protocol: TCP
    port: 1234 # CLUSTERIP PORT
    targetPort: 50001 # POD PORT WHICH APPLICATION IS RUNNING ON 
    nodePort: 32222 # HERE!
</code></pre>
<p>You can apply above <code>YAML</code> definition by invoking command:
<code>$ kubectl apply -f FILE_NAME.yaml</code></p>
<p>Above service object will be created only if <code>nodePort</code> port  is available to use.</p>
<blockquote>
<p>But I don't understand how, in a real cluster, the service could <strong>not</strong> have a fixed world-accessible port.</p>
</blockquote>
<p>In clusters managed by cloud providers (for example GKE) you can use a service object of type <code>LoadBalancer</code> which will have a fixed external IP and fixed port.</p>
<p>Clusters that have nodes with public IP's can use service object of type <code>NodePort</code> to direct traffic into the cluster.</p>
<p>In <code>minikube</code> environment you can use a service object of type <code>LoadBalancer</code> but it will have some caveats described in last paragraph.</p>
<h1><strong>A little bit of explanation:</strong></h1>
<h2><a href="https://kubernetes.io/docs/concepts/services-networking/service/#nodeport" rel="noreferrer">NodePort</a></h2>
<p><code>Nodeport</code> is exposing the service on each node IP at a static port. It allows external traffic to enter with the <code>NodePort</code> port. This port will be automatically assigned from range of <code>30000</code> to <code>32767</code>.</p>
<p>You can change the default <code>NodePort</code> port range by following <a href="http://www.thinkcode.se/blog/2019/02/20/kubernetes-service-node-port-range" rel="noreferrer">this manual</a>.</p>
<p>You can check what is exactly happening when creating a service object of type <code>NodePort </code> by looking on this <a href="https://stackoverflow.com/a/54345488/12257134">answer</a>.</p>
<p>Imagine that:</p>
<ul>
<li>Your nodes have IP's:
<ul>
<li><code>192.168.0.100</code></li>
<li><code>192.168.0.101</code></li>
<li><code>192.168.0.102</code></li>
</ul>
</li>
<li>Your pods respond on port <code>50001</code> with <code>hello</code> and they have IP's:
<ul>
<li><code>10.244.1.10</code></li>
<li><code>10.244.1.11</code></li>
<li><code>10.244.1.12</code></li>
</ul>
</li>
<li>Your Services are:
<ul>
<li><code>NodePort</code> (port <code>32222</code>) with:
<ul>
<li><code>ClusterIP</code>:
<ul>
<li>IP: <code>10.96.0.100</code></li>
<li><code>port</code>:<code>7654</code></li>
<li><code>targetPort</code>:<code>50001</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>A word about <code>targetPort</code>. It's a definition for port on the <strong>pod</strong> that is for example a web server.</p>
<p><strong>According to above example you will get <code>hello</code> response with:</strong></p>
<ul>
<li><code>NodeIP:NodePort</code> (all the pods could respond with <code>hello</code>):
<ul>
<li><code>192.168.0.100:32222</code></li>
<li><code>192.168.0.101:32222</code></li>
<li><code>192.168.0.102:32222</code></li>
</ul>
</li>
<li><code>ClusterIP:port</code> (all the pods could respond with <code>hello</code>):
<ul>
<li><code>10.0.96.100:7654</code></li>
</ul>
</li>
<li><code>PodIP:targetPort</code> (only the pod that request is sent to can respond with <code>hello</code>)
<ul>
<li><code>10.244.1.10:50001</code></li>
<li><code>10.244.1.11:50001</code></li>
<li><code>10.244.1.12:50001</code></li>
</ul>
</li>
</ul>
<p>You can check access with <code>curl</code> command as below:</p>
<p><code>$ curl http://NODE_IP:NODEPORT</code></p>
<hr />
<p><strong>In the example you mentioned:</strong></p>
<pre class="lang-sh prettyprint-override"><code>$ kubectl expose deployment/mysrv --type=NodePort --port=1234
</code></pre>
<p>What will happen:</p>
<ul>
<li>It will assign a random port from range of <code>30000</code> to <code>32767</code> on your <code>minikube</code> instance directing traffic entering this port to pods.</li>
<li>Additionally it will create a <code>ClusterIP</code> with port of <code>1234</code></li>
</ul>
<p>In the example above there was no parameter <code>targetPort</code>. If <code>targetPort</code> is not provided it will be the same as <code>port</code> in the command.</p>
<p>Traffic entering a <code>NodePort</code> will be routed directly to pods and will not go to the <code>ClusterIP</code>.</p>
<p>From the <code>minikube</code> perspective a <code>NodePort</code> will be a port on your <code>minikube</code> instance. It's IP address will be dependent on the hypervisor used. Exposing it outside your local machine will be heavily dependent on operating system.</p>
<hr />
<h2><a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer" rel="noreferrer">LoadBalancer</a></h2>
<p>There is a difference between a service object of type <code>LoadBalancer</code>(1) and an external <code>LoadBalancer</code>(2):</p>
<ul>
<li>Service object of type <code>LoadBalancer</code>(1) allows to expose a service externally using a cloud provider’s <code>LoadBalancer</code>(2). It's a service within Kubernetes environment that through service controller can schedule a creation of external <code>LoadBalancer</code>(2).</li>
<li>External <code>LoadBalancer</code>(2) is a load balancer provided by cloud provider. It will operate at Layer 4.</li>
</ul>
<p>Example definition of service of type <code>LoadBalancer</code>(1):</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: v1
kind: Service
metadata:
  name: example-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: hello
  ports:
    - port: 1234 # LOADBALANCER PORT 
      targetPort: 50001  # POD PORT WHICH APPLICATION IS RUNNING ON 
      nodePort: 32222 # PORT ON THE NODE 
</code></pre>
<p>Applying above <code>YAML</code> will create a service of type <code>LoadBalancer</code>(1)</p>
<p>Take a specific look at:</p>
<pre class="lang-yaml prettyprint-override"><code>  ports:
    - port: 1234 # LOADBALANCER PORT 
</code></pre>
<p>This definition will simultaneously:</p>
<ul>
<li>specify external <code>LoadBalancer</code>(2) <code>port</code> as 1234</li>
<li>specify <code>ClusterIP</code> <code>port</code> as 1234</li>
</ul>
<p>Imagine that:</p>
<ul>
<li>Your external <code>LoadBalancer</code>(2) have:
<ul>
<li><code>ExternalIP</code>: <code>34.88.255.5</code></li>
<li><code>port</code>:<code>7654</code></li>
</ul>
</li>
<li>Your nodes have IP's:
<ul>
<li><code>192.168.0.100</code></li>
<li><code>192.168.0.101</code></li>
<li><code>192.168.0.102</code></li>
</ul>
</li>
<li>Your pods respond on port <code>50001</code> with <code>hello</code> and they have IP's:
<ul>
<li><code>10.244.1.10</code></li>
<li><code>10.244.1.11</code></li>
<li><code>10.244.1.12</code></li>
</ul>
</li>
<li>Your Services are:
<ul>
<li><code>NodePort</code> (port <code>32222</code>) with:
<ul>
<li><code>ClusterIP</code>:
<ul>
<li>IP: <code>10.96.0.100</code></li>
<li><code>port</code>:<code>7654</code></li>
<li><code>targetPort</code>:<code>50001</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>According to above example you will get <code>hello</code> response with:</strong></p>
<ul>
<li><code>ExternalIP</code>:<code>port</code> (all the pods could respond with <code>hello</code>):
<ul>
<li><code>34.88.255.5:7654</code></li>
</ul>
</li>
<li><code>NodeIP:NodePort</code> (all the pods could respond with <code>hello</code>):
<ul>
<li><code>192.168.0.100:32222</code></li>
<li><code>192.168.0.101:32222</code></li>
<li><code>192.168.0.102:32222</code></li>
</ul>
</li>
<li><code>ClusterIP:port</code> (all the pods could respond with <code>hello</code>):
<ul>
<li><code>10.0.96.100:7654</code></li>
</ul>
</li>
<li><code>PodIP:targetPort</code> (only the pod that request is sent to can respond with <code>hello</code>)
<ul>
<li><code>10.244.1.10:50001</code></li>
<li><code>10.244.1.11:50001</code></li>
<li><code>10.244.1.12:50001</code></li>
</ul>
</li>
</ul>
<p><code>ExternalIP</code> can be checked with command: <code>$ kubectl get services</code></p>
<p>Flow of the traffic:
Client -&gt; <code>LoadBalancer:port</code>(2) -&gt; <code>NodeIP:NodePort</code> -&gt; <code>Pod:targetPort</code></p>
<h2><a href="https://minikube.sigs.k8s.io/docs/tasks/loadbalancer/" rel="noreferrer">Minikube: LoadBalancer</a></h2>
<blockquote>
<p><strong>Note:</strong> This feature is only available for cloud providers or environments which support external load balancers.</p>
<p>-- <em><a href="https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/" rel="noreferrer">Kubernetes.io: Create external LoadBalancer</a></em> </p>
<p>On cloud providers that support load balancers, an external IP address would be provisioned to access the Service. On Minikube, the <code>LoadBalancer</code> type makes the Service accessible through the <code>minikube service</code> command.</p>
<p>--  <em><a href="https://kubernetes.io/docs/tutorials/hello-minikube/" rel="noreferrer">Kubernetes.io: Hello minikube</a></em> </p>
</blockquote>
<p><code>Minikube</code> can create service object of type <code>LoadBalancer</code>(1) but it will not create an external <code>LoadBalancer</code>(2).</p>
<p>The <code>ExternalIP</code> in command <code>$ kubectl get services</code> will have pending status.</p>
<p>To address that there is no external <code>LoadBalancer</code>(2) you can invoke <code>$ minikube tunnel</code> which will create a route from host to <code>minikube</code> environment to access the <code>CIDR</code> of <code>ClusterIP</code> directly.</p>
