--- title: "Expose services with a load balancer" description: "Put a TIR Kubernetes Service on a public reserved IP through the cluster's LB IP pool, and free the address again afterwards." content_type: "how-to" product: "tir-kubernetes" platform: "TIR" url: "/tir/tir-kubernetes/how-to/expose-services-with-a-load-balancer/" topic: ["workloads"] --- # Expose services with a load balancer Give an app a public address by attaching reserved IPs to your cluster's LB IP pool. Each Service of type LoadBalancer in the cluster then takes one of those addresses. To serve many sites from a single address instead, put an ingress controller in front of them. It routes each request to the right site by its hostname; see [Set up an ingress controller with HTTPS](/tir/tir-kubernetes/how-to/set-up-an-ingress-controller/). ## Before you begin - A cluster in the **Worker Joining**, **Worker Joined**, or **Ready** state, and its kubeconfig set up for `kubectl`. See [Connect to a Kubernetes cluster](/tir/tir-kubernetes/how-to/connect-to-a-cluster/). - At least one public reserved IP in the same project and region, with **Status** **Available**. See [Reserve an IP](/tir/tir-reserved-ip/how-to/reserve-an-ip-address/). A reserved IP is billed every month from when you reserve it until you delete it, whether it is attached or detached. For the rate, see [E2E's pricing page](https://www.e2enetworks.com/pricing). - Permission to update Kubernetes and to create reserved IPs in the project. To detach an address later, you need permission to update Kubernetes and to delete reserved IPs. ## Choose how to expose a Service Kubernetes has three Service types, and each one is reachable from a different place: | Type | Reachable from | Use it for | | ------------ | ----------------------------------------------------------- | ---------------------------------------------------------------------------- | | ClusterIP | Inside the cluster only | Internal APIs, databases, and anything other pods call. This is the default. | | NodePort | Networks that can route to the nodes' addresses in your VPC | Quick tests. It is rarely the right entry point for production. | | LoadBalancer | A public reserved IP that you attached to the cluster | Public entry points, alone or behind an ingress controller | ### ClusterIP A ClusterIP Service gets a stable internal address and a DNS name. It is reachable from inside the cluster only. ```yaml apiVersion: v1 kind: Service metadata: name: api spec: type: ClusterIP selector: app: api ports: - port: 80 targetPort: 8080 ``` Other pods reach it as `api` in the same namespace, and as `api..svc.cluster.local` from other namespaces. To test it from inside the cluster, and to reach it from your computer while it stays private, run: ```bash kubectl get svc api kubectl run -it --rm curl --image=curlimages/curl --restart=Never -- curl -s http://api kubectl port-forward svc/api 8080:80 ``` ### NodePort A NodePort Service opens the same port, from 30000 to 32767, on every node and forwards it to the Service. ```yaml apiVersion: v1 kind: Service metadata: name: api spec: type: NodePort selector: app: api ports: - port: 80 targetPort: 8080 nodePort: 30080 ``` It answers at `:30080`, where `` is a node's address in your VPC. Only networks that can route to the nodes' VPC addresses can reach it. For production, use a LoadBalancer Service instead. The NodePort port is high and unusual, and each client sends its traffic to the one node it addresses. A node can also be removed when its pool is resized. ## Attach reserved IPs to the cluster A new cluster's LB IP pool is empty, and a LoadBalancer Service waits until it has an address to take. The pool takes public reserved IPs only. 1. In **Kubernetes**, select the cluster's name to open **Kubernetes Cluster Details**, and select the **LB IP Pool** tab. The addresses already in the pool are under **Attached IPs**. 2. Select **Add Reserve IP**. You can do this once the cluster reaches **Worker Joining**. 3. Under **Reserve IPs**, choose one or more of the project's **Available** reserved IPs. If there is none, select **Click here** to reserve a new one. 4. Select **Add Reserve IP**. The addresses join the pool under **Attached IPs**. You can also attach an address from the **Reserve IP** page: choose **Kubernetes Cluster** in **Attach IP**. See [Attach a reserved IP](/tir/tir-reserved-ip/how-to/attach-a-reserved-ip/). ## Create a LoadBalancer Service With at least one free address in the pool, create a Service of type LoadBalancer. Save this as `service.yaml`, with your own app's label under `selector` and the port your app listens on as the target port: ```yaml apiVersion: v1 kind: Service metadata: name: web spec: type: LoadBalancer selector: app: web ports: - port: 80 targetPort: 8080 ``` Apply it and watch it take an address: ```bash kubectl apply -f service.yaml kubectl get svc web -w ``` `EXTERNAL-IP` changes from `` to one of the addresses you attached. Check that the app answers on it, where `` is that address: ```bash curl http:// ``` Each LoadBalancer Service takes its own address from the pool. When every attached address is already taken, a new Service stays at `` until you attach another IP or delete a Service that holds one. ## Free an address Deleting a LoadBalancer Service frees its address for another Service in the same cluster. To give the address back to the project, detach it from the pool: 1. Find any Service that still uses the address, where `` is the address: ```bash kubectl get svc --all-namespaces -o wide | grep ``` 2. Delete that Service. TIR refuses to detach an address while a Service still uses it, and the refusal names the Service. 3. On the **LB IP Pool** tab, select **Detach IP** next to the address, and confirm in **Detach Reserve IP**. The address goes back to the project's reserved IPs, ready for another cluster or resource, and keeps its monthly charge until you delete it. See [Delete a reserved IP](/tir/tir-reserved-ip/how-to/delete-a-reserved-ip/). Your app now answers on a public address of its own. Once you run more than one or two public sites, [set up an ingress controller](/tir/tir-kubernetes/how-to/set-up-an-ingress-controller/) so that they share one address and get HTTPS. For an address that stays at `` or will not detach, see [Troubleshooting TIR Kubernetes](/tir/tir-kubernetes/troubleshooting/tir-kubernetes/#a-loadbalancer-service-stays-at-pending).