diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index 145eda608..acc07dcad 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -198,6 +198,9 @@ spec: portDiscovery: description: Enable port dicovery type: boolean + headless: + description: Headless if set to true, generates headless Kubernetes services. + type: boolean timeout: description: HTTP or gRPC request timeout type: string diff --git a/charts/flagger/crds/crd.yaml b/charts/flagger/crds/crd.yaml index 145eda608..acc07dcad 100644 --- a/charts/flagger/crds/crd.yaml +++ b/charts/flagger/crds/crd.yaml @@ -198,6 +198,9 @@ spec: portDiscovery: description: Enable port dicovery type: boolean + headless: + description: Headless if set to true, generates headless Kubernetes services. + type: boolean timeout: description: HTTP or gRPC request timeout type: string diff --git a/docs/gitbook/usage/how-it-works.md b/docs/gitbook/usage/how-it-works.md index e418cf881..942db5ca9 100644 --- a/docs/gitbook/usage/how-it-works.md +++ b/docs/gitbook/usage/how-it-works.md @@ -147,6 +147,7 @@ spec: appProtocol: http targetPort: 9898 portDiscovery: true + headless: false ``` The container port from the target workload should match the `service.port` or `service.targetPort`. @@ -155,6 +156,7 @@ The `service.targetPort` can be a container port number or name. The `service.portName` is optional (defaults to `http`), if your workload uses gRPC then set the port name to `grpc`. The `service.appProtocol` is optional, more details can be found [here](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol). + If port discovery is enabled, Flagger scans the target workload and extracts the containers ports excluding the port specified in the canary service and service mesh sidecar ports. These ports will be used when generating the ClusterIP services. @@ -204,6 +206,9 @@ Note that the `apex` annotations are added to both the generated Kubernetes Serv generated service mesh/ingress object. This allows using external-dns with Istio `VirtualServices` and `TraefikServices`. Beware of configuration conflicts [here](../faq.md#ExternalDNS). +If you want for the generated Kubernetes ClusterIP services to be [headless](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services), +then set `service.headless` to true. + Besides port mapping and metadata, the service specification can contain URI match and rewrite rules, timeout and retry polices: diff --git a/kustomize/base/flagger/crd.yaml b/kustomize/base/flagger/crd.yaml index 145eda608..acc07dcad 100644 --- a/kustomize/base/flagger/crd.yaml +++ b/kustomize/base/flagger/crd.yaml @@ -198,6 +198,9 @@ spec: portDiscovery: description: Enable port dicovery type: boolean + headless: + description: Headless if set to true, generates headless Kubernetes services. + type: boolean timeout: description: HTTP or gRPC request timeout type: string diff --git a/pkg/apis/flagger/v1beta1/canary.go b/pkg/apis/flagger/v1beta1/canary.go index f5797e876..5c92cbac2 100644 --- a/pkg/apis/flagger/v1beta1/canary.go +++ b/pkg/apis/flagger/v1beta1/canary.go @@ -146,6 +146,11 @@ type CanaryService struct { // PortDiscovery adds all container ports to the generated Kubernetes service PortDiscovery bool `json:"portDiscovery"` + // Headless if set to true, generates headless Kubernetes services. + // ref: https://kubernetes.io/docs/concepts/services-networking/service/#headless-services + // +optional + Headless bool `json:"headless,omitempty"` + // Timeout of the HTTP or gRPC request // +optional Timeout string `json:"timeout,omitempty"` diff --git a/pkg/router/kubernetes_default.go b/pkg/router/kubernetes_default.go index 840005672..184b62331 100644 --- a/pkg/router/kubernetes_default.go +++ b/pkg/router/kubernetes_default.go @@ -113,6 +113,9 @@ func (c *KubernetesDefaultRouter) reconcileService(canary *flaggerv1.Canary, nam }, }, } + if canary.Spec.Service.Headless { + svcSpec.ClusterIP = "None" + } if v := canary.Spec.Service.AppProtocol; v != "" { svcSpec.Ports[0].AppProtocol = &v diff --git a/pkg/router/kubernetes_default_test.go b/pkg/router/kubernetes_default_test.go index c0f969958..b428c4a99 100644 --- a/pkg/router/kubernetes_default_test.go +++ b/pkg/router/kubernetes_default_test.go @@ -34,6 +34,8 @@ import ( func TestServiceRouter_Create(t *testing.T) { mocks := newFixture(nil) + mocks.canary.Spec.Service.Headless = true + router := &KubernetesDefaultRouter{ kubeClient: mocks.kubeClient, flaggerClient: mocks.flaggerClient, @@ -53,11 +55,13 @@ func TestServiceRouter_Create(t *testing.T) { assert.Equal(t, &appProtocol, canarySvc.Spec.Ports[0].AppProtocol) assert.Equal(t, "http", canarySvc.Spec.Ports[0].Name) assert.Equal(t, int32(9898), canarySvc.Spec.Ports[0].Port) + assert.Equal(t, "None", canarySvc.Spec.ClusterIP) primarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{}) require.NoError(t, err) assert.Equal(t, "http", primarySvc.Spec.Ports[0].Name) assert.Equal(t, int32(9898), primarySvc.Spec.Ports[0].Port) + assert.Equal(t, "None", primarySvc.Spec.ClusterIP) } func TestServiceRouter_Update(t *testing.T) {