A Kubernetes Service
can have a targetPort
and port
in the service definition:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
What is the difference between the port
and targetPort
?
Service: This directs the traffic to a pod.
TargetPort: This is the actual port on which your application is running inside the container.
Port: Some times your application inside container serves different services on a different port.
Example: The actual application can run 8080
and health checks for this application can run on 8089
port of the container. So if you hit the service without port it doesn't know to which port of the container it should redirect the request. Service needs to have a mapping so that it can hit the specific port of the container.
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
nodePort: 30475
port: 8089
protocol: TCP
targetPort: 8080
- name: metrics
nodePort: 31261
port: 5555
protocol: TCP
targetPort: 5555
- name: health
nodePort: 30013
port: 8443
protocol: TCP
targetPort: 8085
if you hit the my-service:8089
the traffic is routed to 8080
of the container(targetPort). Similarly, if you hit my-service:8443
then it is redirected to 8085
of the container(targetPort). But this myservice:8089
is internal to the kubernetes cluster and can be used when one application wants to communicate with another application. So to hit the service from outside the cluster someone needs to expose the port on the host machine on which kubernetes is running so that the traffic is redirected to a port of the container. This is node port
(port exposed on the host machine). From the above example, you can hit the service from outside the cluster(Postman or any rest-client) by host_ip:nodePort
Say your host machine ip is 10.10.20.20
you can hit the http, metrics, health services by 10.10.20.20:30475
, 10.10.20.20:31261
, 10.10.20.20:30013
.
Edits: Edited as per Raedwald comment.
It helps me to think of things from the perspective of the service.
nodePort: The port on the node where external traffic will come in on
port: The port of this service
targetPort The target port on the pod(s) to forward traffic to
Traffic comes in on nodePort
, forwards to port
on the service which then routes to targetPort
on the pod(s).
It's worth emphasizing that nodePort
is for external traffic. Other pods in the cluster that may need to access the service will just use port
, not nodePort
as it's internal only access to the service.
Also worth noting that if targetPort
is not set, it will default to the same value as port
. E.g. 80:80
for service port 80
targeting container port 80
.
In nutshell
nodeport:
Listens for external request on all worker nodes on nodeip:nodeport
and forwards the request to port.
ClusterIP:
Request comes through ingress and points to service name and port.
port:
Internal cluster service port for container and listens for incoming request from the nodeport and forwards to targetPort.
targetPort:
Receives the request from port and forwards to container pod(port) where it's listening. Even if you don't specify this will get by default assigned the same port numbers as port.
So the traffic flows Ingress-->Service-->Endpoint(Basically has POD IP)-->POD
The answer given above by @Manikanta P is correct. However, the explanation of "Port" might be a little unclear at first reading. I will explain with an example:
Consider a Web-Application with its static content (front-page, images etc) hosted by httpd and the dynamic content (eg. response to requests, etc.) hosted by tomcat. The Webserver (or the static content) is served by httpd at port 80
while Appserver (or the dynamic content) is served by tomcat at port 8080
.
What a developer wants: User should be able to access the Webserver from outside BUT not the Appserver from outside.
Solution: The service-type of Webserver in its service.yml will be NodePort while the service-type of Appserver in its service.yml will be ClusterIP.
Code for webserver's service.yml:
spec:
selector:
app: Webserver
type: NodePort // written to make this service accessible from outside.
ports:
- nodePort: 30475 // To access from outside, type <host_IP>:30475 in browser.
port: 5050 // (ignore for now, I will explain below).
protocol: TCP
targetPort: 80 // port where httpd runs inside the webserver pod.
Code for Appserver's service.yml
spec:
selector:
app: appserver
type: ClusterIP // written to make this service NOT accessible from outside.
ports:
- port: 5050 // port to access this container internally
protocol: TCP
targetPort: 8080 // port where tomcat runs inside the appserver pod.
Also Note, in the httpd.conf
file of the Webserver, we will write the IP that redirects a user's request to the appserver. This IP will be: host_IP:5050
.
What exactly is happening here? A user writes hostIP:30475
and sees the Webserver's page. This is because it is being served by httpd at port 80
(targetport). When a user clicks a button, a request is made. This request is redirected to the Appserver because in httpd.conf
file, the port 5050
is mentioned and this is the port where Appserver's container and Webserver's conatainer communicate internally. When the appserver receives the request, it is able to serve the request because of tomcat running inside it at port 8080
.
httpd.conf
in "because in httpd.conf file, the port 5050 is mentioned "
if container listens on port 9376, then targetPort: 9376
if a service listens on port 80, then port: 80
Then service ports config looks like below
ports:
- protocol: TCP
port: 80
targetPort: 9376
Finally, request received to the service’s port, and forwarded on the targetPort of the pod.
This answer is to reference Kubernetes' documentation in addition to the other answers:
https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/:
targetPort: is the port the container accepts traffic on,
port: is the abstracted Service port, which can be any port other pods use to access the Service
https://kubernetes.io/docs/concepts/services-networking/service/:
Port definitions in Pods have names, and you can reference these names in the targetPort attribute of a Service. This works even if there is a mixture of Pods in the Service using a single configured name, with the same network protocol available via different port numbers.
Case 1:
Let's assume that there is no nodPort or port, now you want to run your aplication and expose it to outside, what you will need:
An Ingress controller which will use a servicePort for redirecting to our desired service based on routing. A cluster IP service with a defined target to your application port (which also called targetPort) A network port which will identifies the application or service running on the computer (in other words application port).
So, to get access from outside we found three port needed.
servicePort (Ingress controller) targetPort (Cluster Ip Service) networkPort (application port)
to work everything properly : servicePort === targetPort === networkPort
Case 2: Now assume that one service communicate with another service in our cluster, or let's assume one service received a request from out side and it emits an event which triggered another service inside of our cluster.
Suppose Service X is exposed outside by using nodePort Service, after receving a request, X service want to communicate with Y service.
Y service need following ports
A ClusterIP port, by which X service will forward request A ClusterIP targetPort by which Y service will determine in which port applicaiton is running. An application port
port === any
targetPort === application port
Inside Service X:
app.post('/posts/create', async (req, res) => {
const id = randomBytes(4).toString('hex');
const { title } = req.body;
posts[id] = {
id,
title
};
await axios.post('http://event-bus-srv:4010/events', {
type: 'PostCreated',
data: {
id,
title
}
});
res.status(201).send(posts[id]);
});
Configuration and Inside of Service Y
apiVersion: v1
kind: Service
metadata:
name: event-bus-srv
spec:
selector:
app: event-bus
type: ClusterIP
ports:
- name: event-bus
protocol: TCP
port: 4010
targetPort: 4009
app.listen(4009, () => {
console.log('Listening on 4009');
});
https://i.stack.imgur.com/2bVEz.png
Since people have explained port
and targetPort
in Kubernetes Service definition, I'll add information on how Dockerfile
, Kubernetes Deployment and Kubernetes Ingress come into picture because they are part of a common workflow.
Part 1 - Applications and their Dockerfile
Lets say you're running a Flask server on port 3000 and a Golang server on port 4000. When you containerize these applications using Docker, you'll have to expose ports 3000 and 4000 in their Dockerfile
s:
Python
Application
...
...
if __name__ == "__main__":
app.run(host='0.0.0.0', port=3000)
Dockerfile
FROM python:3.10-alpine
...
...
EXPOSE 3000
CMD ...
Golang
Application
...
...
func main() {
...
log.Fatal(http.ListenAndServe(":4000", nil))
}
Dockerfile
FROM golang:1.18-alpine
...
...
EXPOSE 4000
CMD ...
Part 2 - Dockerfiles and Kubernetes Deployments
The exposed ports in Dockerfile
s will have to match the containerPort
in the deployment manifests.
Python Deployment Manifest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-flask-api
spec:
...
...
app: flask-api
template:
metadata:
labels:
app: flask-api
spec:
containers:
- name: flask-api
image: ...
ports:
- containerPort: 3000
...
...
Golang Deployment Manifest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-backend-api
spec:
...
...
app: go-api
template:
metadata:
labels:
app: go-api
spec:
containers:
- name: go-api
image: ...
ports:
- containerPort: 4000
...
...
Part 3 - Kubernetes Deployments and Services
The containerPort
in the deployment manifests will have to match the targetPort
in the service manifests
Python Service Manifest
apiVersion: v1
kind: Service
metadata:
name: flask-api-service
spec:
type: NodePort
selector:
app: flask-api
ports:
- port: 80
targetPort: 3000
Golang Service Manifest
apiVersion: v1
kind: Service
metadata:
name: go-api-service
spec:
type: NodePort
selector:
app: go-api
ports:
- port: 443
targetPort: 4000
Part 4 - Kubernetes Service and Ingress
The port
in the service manifest will have to match the port number
in the ingress
AWS Ingress for Python and Golang application
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservice-app-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- host: foo.biz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: go-api-service
port:
number: 443
- host: bar.biz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: flask-api-service
port:
number: 80
Flow
An incoming request hits the ingress on a port number
The ingress forwards this request to the service port
The service port maps the port to a targetPort
From the service targetPort the request goes to the deployment containerPort
The deployment containerPort is the application's Docker image, which has this corresponding port exposed in its Dockerfile
And finally, the exposed port in Dockerfile sends the request to the application
targetport: One or more ports on which a container listens within a pod.
nodeport: Used primarily to accept consumer requests. (Eg: HTTP request from consumers to a webserver running in a container)
nodeport is listened on all nodes on all interfaces i.e 0.0.0.0:nodeport. Consumer service requests sent to nodeport is routed to container's targetport so that the container can fulfill the request.
port: Port used within the kubernetes pod network, primarily used to exchange requests between pods. Here as well, requests to a pod from another, is routed to the corrosponding pod's container targetport.
Summary: all requests end up in the targetport. nodeport is used if request from outside k8s network & port if from within.
"Target port" is the port on which your container is running.
Port : port redirects the traffic to the container from the service.
Exposing the deployment
master $ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 31s
master $ kubectl expose deployment nginx --name=nginx-svc --port=8080 --target-port=80
service/nginx-svc exposed
master $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc ClusterIP 10.107.209.151 <none> 8080/TCP 5s
NodePort : is the port that enables the service to access the externally.
Hope this answers.
I think image describes the best.
https://i.stack.imgur.com/O9e5U.png
Success story sharing
port
andtargetPort
to be different? So for example looking at yourhealth
example, why make theport
8443
instead of8085
? Basically, why are there two parameters instead of just exposing all thetargetPort
s on the service?