Service: kubectl expose
Another way to create a service is by using kubectl expose
command.
kubectl expose <resource-type> <resource-name> --port=<port> --target-port=<target-port> --type=<type>
<resource-type>
: Type of Kubernetes resource (pod, deployment, replicaset, etc.).<resource-name>
: Name of the resource to expose.--port
: The external port to expose.--target-port
: The port on the container to forward traffic to.--type
: Type of service (ClusterIP, NodePort, LoadBalancer, or ExternalName).--name
: Name of newly created service.
Expose as ClusterIP Service
First lets create a simple deployment object using nginx
image with name my-server
.
➜ kubectl create deployment my-server --image=nginx
deployment.apps/my-server created
➜ kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
my-server 1/1 1 0 10s
postgres 1/1 1 1 25h
simple-go 3/3 3 3 25h
Then lets create a service with type ClusterIP
using kubectl exec
command. Since ClusterIP
is the default type we don't need to specify the --type
argument.
➜ kubectl expose deployment my-server --port=80 --target-port=80
service/my-server exposed
➜ kubectl get svc my-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-server ClusterIP 10.97.240.5 <none> 80/TCP 1m2s
As we can see above the command will create a service name my-server
that exposes deployment named my-server
. We also can get the details in yaml
format using command kubectl get service my-server -o yaml
.
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2025-02-10T06:03:41Z"
labels:
app: my-server
name: my-server
namespace: default
resourceVersion: "129776"
uid: 618624cb-2f7e-466e-992d-ecfaf400fb72
spec:
clusterIP: 10.97.240.5
clusterIPs:
- 10.97.240.5
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: my-server
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
Expose as NodePort Service
Lets delete our previous service and then expose our deployment again as NodePort
service. We need to delete the previous service because the expose command by default will name the service same as deployment name.
➜ kubectl delete svc my-server
service "my-server" deleted
Because NodePort
is not the default service type, we need to explicitly specify the service type using --type
in expose command.
➜ kubectl expose deployment my-server --type=NodePort --port=80 --target-port=80
service/my-server exposed
➜ kubectl get svc my-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-server NodePort 10.100.253.32 <none> 80:31775/TCP 8s
Now it exposed as NodePort
and you can try to access it using the minikube IP and the service PORT. But if you use minikube on Mac using docker driver like me you will need minikube service
to expose it.
Expose as LoadBalancer Service
No lets create another service but with type LoadBalancer
. This time we explicitly specify the service name using --name
argument.
This command below will create a service with type LoadBalancer
and name my-server-lb
.
➜ kubectl expose deployment my-server --type=LoadBalancer --name=my-server-lb --port=80 --target-port=80
service/my-server-lb exposed
➜ kubectl get svc my-server-lb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-server-lb LoadBalancer 10.96.60.45 <pending> 80:30309/TCP 59s
Run minikube tunnel
on the other terminal and test it using curl.
➜ curl http://localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Expose Pod Directly
We can also directly expose a pod but this is not recommended in production.
For example lets expose the my-server
pod.
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-server-78b5bb4ccc-7cbxf 1/1 Running 0 31m
postgres-f8f589d5b-59rw8 1/1 Running 0 25h
simple-go-5dc4557ffc-c24fp 1/1 Running 0 25h
simple-go-5dc4557ffc-d6tz6 1/1 Running 0 25h
simple-go-5dc4557ffc-gv4fv 1/1 Running 0 25h
➜ kubectl expose pod my-server-78b5bb4ccc-7cbxf --port=80 --target-port=80
service/my-server-78b5bb4ccc-7cbxf exposed
The command above will create a ClusterIP
service that expose the pod my-server-78b5bb4ccc-7cbxf
.
This is not recommended because as we can see below this service is tied to a specific pod. So if that pod is replaced or upgraded, the service will not work.
➜ kubectl get svc my-server-78b5bb4ccc-7cbxf -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2025-02-10T06:34:09Z"
labels:
app: my-server
pod-template-hash: 78b5bb4ccc
name: my-server-78b5bb4ccc-7cbxf
namespace: default
resourceVersion: "131264"
uid: 98ef311b-0054-4023-8d83-ca5b0bc9c025
spec:
clusterIP: 10.99.77.89
clusterIPs:
- 10.99.77.89
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: my-server
pod-template-hash: 78b5bb4ccc
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}