ClusterIP and NodePort Services
// examples/chapter-3/create-service/main.go
package main
import (
"context"
"flag"
"fmt"
"log"
"path/filepath"
// Kubernetes API imports
v1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" // Required for IntOrString type
// client-go imports
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
func main() {
// --- Setup Kubeconfig and Flags ---
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) kubeconfig path")
} else {
kubeconfig = flag.String("kubeconfig", "", "kubeconfig path")
}
namespace := flag.String("namespace", "default", "namespace to create the service in")
serviceName := flag.String("service-name", "my-webapp-svc", "name for the new service")
appLabel := flag.String("app-label", "my-web-app", "value of the 'app' label to select pods")
servicePort := flag.Int("service-port", 80, "port the service will listen on")
targetPort := flag.Int("target-port", 8080, "target port on the pods")
flag.Parse()
// --- Load Config and Create Clientset ---
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
log.Fatalf("Error building kubeconfig: %s", err.Error())
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatalf("Error creating clientset: %s", err.Error())
}
// --- Define the Service Object ---
serviceSpec := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: *serviceName,
Namespace: *namespace,
// Optional: Add labels to the service itself
Labels: map[string]string{
"created-by": "client-go-example",
},
},
Spec: v1.ServiceSpec{
// Selector targets pods with 'app=<appLabel value>'
Selector: map[string]string{
"app": *appLabel,
},
Ports: []v1.ServicePort{
{
Name: "http", // Optional name for the port
Protocol: v1.ProtocolTCP,
Port: int32(*servicePort), // Service listens on this port
TargetPort: intstr.FromInt(*targetPort), // Pods' target port (can also be string name)
},
// Add more ports here if needed
},
Type: v1.ServiceTypeClusterIP, // Explicitly set type (though it's the default)
},
}
// --- Create the Service ---
fmt.Printf("Creating Service '%s' in namespace '%s'...\n", *serviceName, *namespace)
servicesClient := clientset.CoreV1().Services(*namespace)
createdService, err := servicesClient.Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
// --- Handle Errors (especially AlreadyExists) ---
if err != nil {
if k8serrors.IsAlreadyExists(err) {
log.Printf("Service '%s' already exists in namespace '%s'. Fetching existing.\n", *serviceName, *namespace)
// Optionally, get the existing service instead of failing
createdService, err = servicesClient.Get(context.TODO(), *serviceName, metav1.GetOptions{})
if err != nil {
log.Fatalf("Failed to get existing service '%s': %s\n", *serviceName, err.Error())
}
} else {
log.Fatalf("Error creating service '%s': %s\n", *serviceName, err.Error())
}
}
fmt.Printf("Successfully ensured Service '%s' exists.\n", createdService.Name)
fmt.Printf(" Type: %s\n", createdService.Spec.Type)
fmt.Printf(" ClusterIP: %s\n", createdService.Spec.ClusterIP) // Note: May take a moment to be assigned
fmt.Printf(" Selector: app=%s\n", createdService.Spec.Selector["app"])
fmt.Println(" Ports:")
for _, port := range createdService.Spec.Ports {
fmt.Printf(" - Port: %d, TargetPort: %s, Protocol: %s\n",
port.Port, port.TargetPort.String(), port.Protocol)
}
fmt.Println("---------------")
// You can now access the application within the cluster via:
// <service-name>.<namespace>.svc.cluster.local:<service-port>
// e.g., my-webapp-svc.default.svc.cluster.local:80
// Or directly via the ClusterIP: <cluster-ip>:<service-port>
}Last updated
Was this helpful?