K
Q

How to expose a Kubernetes service on a specific Nodeport?

September 26, 2018

I have create a pod with the below yaml definition.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: praveensripati/docker-demo:1.2
    ports:
    - containerPort: 3000

And now I expose the pod, which creates a service.

kubectl expose pod myapp-pod --type=NodePort

The port 3000 on the container is exposed to port 31728 on the nodes. And I am able to do access the page using curl on port 31728.

kubectl get service myapp-pod
NAME        TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
myapp-pod   NodePort   10.107.254.254   <none>        3000:31728/TCP   5s

This time I wanted to expose the service not a random port, but on port 80. And so I specify the port number as 80, by using --port. The service details are a bit odd. It says that port 80 on the container is exposed to port 31316 on the nodes. Also, I am able to access the page using curl on the random port (31316 in this case) and not port 80.

kubectl expose pod myapp-pod --type=NodePort --target-port=3000 --port=80

kubectl get service myapp-pod
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
myapp-pod   NodePort   10.105.123.73   <none>        80:31316/TCP   12s

I am not able to expose a service on a specific port and not on a random port. I tried a few combinations and read the k8s documentation, but no success.

How do I expose a service on a specific port instead of a random port?

-- Praveen Sripati
service
kubernetes

8 Answers

September 26, 2018

Your question is about exposing the NodePort type of service on a specific port. For that you need to specify the

nodePort
field under
ports
in your service definition.

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 3000
    nodePort: 32321
  type: NodePort

Note that it has to be within a given range provided in the configs. Which defaults to

30000-32767
. This range can be specified in the kube-apiserver configs using the
--service-node-port-range
option.

-- Rico
Source: StackOverflow

January 12, 2021

I had the same problem and the only way I found to do it without modifying the files was:

k expose --type=NodePort deployment nginx --port 80 --name nginx-ep-patch  --overrides '{ "apiVersion": "v1","spec":{"ports": [{"port":80,"protocol":"TCP","targetPort":80,"nodePort":30080}]}}'
service/nginx-ep-patch exposed

In this way we path online the configuration and the port 30080 has been exposed:

$ k describe svc nginx-ep-patch

Name:                     nginx-ep-patch

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     NodePort

IP:                       10.96.51.148

Port:                     <unset>  80/TCP

TargetPort:               80/TCP

NodePort:                 <unset>  30080/TCP

Endpoints:                10.244.0.6:80
Session Affinity:         None
External Traffic Policy:  Cluster

Events:                   <none>
-- Zioalex
Source: StackOverflow

April 14, 2019

When an existing Dashboard service already exists, remove it.

kubectl delete service kubernetes-dashboard -n kube-system

Expose the Dashboard deployment as a NodePort.

kubectl expose deployment kubernetes-dashboard -n kube-system --type=NodePort

The above will assign a random port >= 30000. So use the Patch command to assign the port to a known, unused and desired port >= 30000.

kubectl patch service kubernetes-dashboard --namespace=kube-system --type='json' --patch='[{"op": "replace", "path": "/spec/ports/0/nodePort", "value":30000}]'

Caution: Never expose your dashboard publicly without authentication.

-- javajon
Source: StackOverflow

March 15, 2019

If your cluster does not have a LoadBalancer Provider, you can specify externalIPs in IP of nodes' network interface.

For example:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:   
  type: ClusterIP
  externalIPs:
    - 125.100.99.101 # Node1-IP
    - 125.100.99.102 # Node2-IP
    - 192.168.55.112 # Node2-IP2
  selector:
    pod: nginx
  ports:
    - name: http
      port: 80      
    - name: https
      port: 443      

This will listen 80 and 443 on the specified node, and forward to the nginx service.

-- Time Killer
Source: StackOverflow

September 26, 2018

I will try to answer your query here.

Also, I am able to access the page using curl on the random port (31316 in this case) and not port 80.

-- Because, kubernetes exposed the port 31316 on the host (maps to the service) and hence it can be accessed on host:31316.
<br>

-- Service port is visible only within the kubernetes cluster. You can exec into a pod container and do a curl on servicename:service port instead of the NodePort.

Note the terms -

container port:
the port container listens on.
Service port:
the port where kubernetes service is exposed on cluster internal ip and mapped to the container port.
Nodeport:
the port exposed on the host and mapped to kubernetes service.

-- techuser soma
Source: StackOverflow

February 5, 2019

we can expose Kubernetes service on specific node port.

Port value must be between 30000-32767.

We can expose service to specific port of below service types:

  1. NodePort

  2. LoadBalancer

Find the sample myservice.yaml file below:

apiVersion: v1

kind: Service

metadata:

  name: app1

spec:

  type: NodePort/LoadBalancer

  ports:
  - name: "80"

    port: 80

    nodePort: 32062

    targetPort: 80

  selector:

    appone: app1

    app: test

Note: In above service yaml file we can specify service type either NodePort or Loadbalancer.

-- kalyani chaudhari
Source: StackOverflow

March 7, 2024

Below you will find two working manifests that expose ArgoCD

  1. Service NodePort on port 30k which is recommended only for dev usage not for production
  2. Ingress which is the preferred method to expose a service

Node1: If the service works with HTTPS one will need to deal with provisioning a valid certificate for the ingress.

Note2: The Ingress config posted is from Microk8s ingress

Post comments for guidance or other questions.

apiVersion: v1
kind: Service
metadata:
  name: argocd-server-30k-node-port
  namespace: argocd
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30000
  selector:
    app.kubernetes.io/name: argocd-server
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-ingress
  namespace: argocd
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  rules:
  - host: argocd
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              number: 8080
-- AnshelAtWork
Source: StackOverflow

October 8, 2021

For those who need to use kubectl commands, you can create a NodePort service with a specified port using

create nodeport
command:

kubectl create nodeport NAME [--tcp=port:targetPort] [--dry-run=server|client|none]

For example:

kubectl create service nodeport myservice --node-port=31000 --tcp=3000:80

You can check Kubectl reference for more:

-- snnsnn
Source: StackOverflow