mirror of
https://github.com/traefik/traefik.git
synced 2026-06-17 19:09:29 +03:00
Add reportNodeInternalIPs option to report node internal IPs in Ingress status
This commit is contained in:
@@ -395,6 +395,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||
| <a id="opt-providers-kubernetesingress-labelselector" href="#opt-providers-kubernetesingress-labelselector" title="#opt-providers-kubernetesingress-labelselector">providers.kubernetesingress.labelselector</a> | Kubernetes Ingress label selector to use. | |
|
||||
| <a id="opt-providers-kubernetesingress-namespaces" href="#opt-providers-kubernetesingress-namespaces" title="#opt-providers-kubernetesingress-namespaces">providers.kubernetesingress.namespaces</a> | Kubernetes namespaces. | |
|
||||
| <a id="opt-providers-kubernetesingress-nativelbbydefault" href="#opt-providers-kubernetesingress-nativelbbydefault" title="#opt-providers-kubernetesingress-nativelbbydefault">providers.kubernetesingress.nativelbbydefault</a> | Defines whether to use Native Kubernetes load-balancing mode by default. | false |
|
||||
| <a id="opt-providers-kubernetesingress-reportnodeinternalips" href="#opt-providers-kubernetesingress-reportnodeinternalips" title="#opt-providers-kubernetesingress-reportnodeinternalips">providers.kubernetesingress.reportnodeinternalips</a> | Report node internal IPs in Ingress status. | false |
|
||||
| <a id="opt-providers-kubernetesingress-strictprefixmatching" href="#opt-providers-kubernetesingress-strictprefixmatching" title="#opt-providers-kubernetesingress-strictprefixmatching">providers.kubernetesingress.strictprefixmatching</a> | Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). | false |
|
||||
| <a id="opt-providers-kubernetesingress-throttleduration" href="#opt-providers-kubernetesingress-throttleduration" title="#opt-providers-kubernetesingress-throttleduration">providers.kubernetesingress.throttleduration</a> | Ingress refresh throttle duration | 0 |
|
||||
| <a id="opt-providers-kubernetesingress-token" href="#opt-providers-kubernetesingress-token" title="#opt-providers-kubernetesingress-token">providers.kubernetesingress.token</a> | Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. | |
|
||||
|
||||
+27
-1
@@ -58,12 +58,13 @@ which in turn creates the resulting routers, services, handlers, etc.
|
||||
| <a id="opt-providers-kubernetesIngress-ingressEndpoint-hostname" href="#opt-providers-kubernetesIngress-ingressEndpoint-hostname" title="#opt-providers-kubernetesIngress-ingressEndpoint-hostname">`providers.kubernetesIngress.`<br />`ingressEndpoint.hostname`</a> | Hostname used for Kubernetes Ingress endpoints. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngress-ingressEndpoint-ip" href="#opt-providers-kubernetesIngress-ingressEndpoint-ip" title="#opt-providers-kubernetesIngress-ingressEndpoint-ip">`providers.kubernetesIngress.`<br />`ingressEndpoint.ip`</a> | This IP will get copied to the Ingress `status.loadbalancer.ip`, and currently only supports one IP value (IPv4 or IPv6). | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngress-ingressEndpoint-publishedService" href="#opt-providers-kubernetesIngress-ingressEndpoint-publishedService" title="#opt-providers-kubernetesIngress-ingressEndpoint-publishedService">`providers.kubernetesIngress.`<br />`ingressEndpoint.publishedService`</a> | The Kubernetes service to copy status from.<br />More information [here](#ingressendpointpublishedservice). | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngress-reportNodeInternalIPs" href="#opt-providers-kubernetesIngress-reportNodeInternalIPs" title="#opt-providers-kubernetesIngress-reportNodeInternalIPs">`providers.kubernetesIngress.reportNodeInternalIPs`</a> | Report node internal IPs in Ingress status.<br />Incompatible with `ingressEndpoint` and `disableClusterScopeResources`.<br />More information [here](#reportnodeinternalips). | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-throttleDuration" href="#opt-providers-kubernetesIngress-throttleDuration" title="#opt-providers-kubernetesIngress-throttleDuration">`providers.kubernetesIngress.throttleDuration`</a> | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.<br />This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.<br />If empty, every event is caught. | 0s | No |
|
||||
| <a id="opt-providers-kubernetesIngress-allowEmptyServices" href="#opt-providers-kubernetesIngress-allowEmptyServices" title="#opt-providers-kubernetesIngress-allowEmptyServices">`providers.kubernetesIngress.allowEmptyServices`</a> | Allows creating a route to reach a service that has no endpoint available.<br />It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-allowExternalNameServices" href="#opt-providers-kubernetesIngress-allowExternalNameServices" title="#opt-providers-kubernetesIngress-allowExternalNameServices">`providers.kubernetesIngress.allowExternalNameServices`</a> | Allows the `Ingress` to reference ExternalName services. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-crossProviderNamespaces" href="#opt-providers-kubernetesIngress-crossProviderNamespaces" title="#opt-providers-kubernetesIngress-crossProviderNamespaces">`providers.kubernetesIngress.crossProviderNamespaces`</a> | List of namespaces from which Ingresses or Services are allowed to use `traefik.ingress.kubernetes.io/router.middlewares`, `traefik.ingress.kubernetes.io/router.tls.options`, or `traefik.ingress.kubernetes.io/service.serverstransport` annotations.<br />When unset, all namespaces are allowed. When set to `[]`, every cross-provider reference is rejected. | [] | No |
|
||||
| <a id="opt-providers-kubernetesIngress-nativeLBByDefault" href="#opt-providers-kubernetesIngress-nativeLBByDefault" title="#opt-providers-kubernetesIngress-nativeLBByDefault">`providers.kubernetesIngress.nativeLBByDefault`</a> | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `Ingress` by default.<br />It can be overridden in the [`Service`](../../../../reference/routing-configuration/kubernetes/crd/http/service.md#opt-nativeLB) | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-disableClusterScopeResources" href="#opt-providers-kubernetesIngress-disableClusterScopeResources" title="#opt-providers-kubernetesIngress-disableClusterScopeResources">`providers.kubernetesIngress.disableClusterScopeResources`</a> | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).<br />By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.<br />Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).<br />This will also prevent from using the `NodePortLB` options on services. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-disableClusterScopeResources" href="#opt-providers-kubernetesIngress-disableClusterScopeResources" title="#opt-providers-kubernetesIngress-disableClusterScopeResources">`providers.kubernetesIngress.disableClusterScopeResources`</a> | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).<br />By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.<br />Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).<br />This will also prevent from using the `NodePortLB` options on services and is incompatible with `reportNodeInternalIPs`. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngress-strictPrefixMatching" href="#opt-providers-kubernetesIngress-strictPrefixMatching" title="#opt-providers-kubernetesIngress-strictPrefixMatching">`providers.kubernetesIngress.strictPrefixMatching`</a> | Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). For example, a PathPrefix of `/foo` will match `/foo`, `/foo/`, and `/foo/bar` but not `/foobar`. | false | No |
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
@@ -138,6 +139,31 @@ providers:
|
||||
--providers.kubernetesingress.ingressendpoint.publishedservice=namespace/foo-service
|
||||
```
|
||||
|
||||
### `reportNodeInternalIPs`
|
||||
|
||||
When set to `true`, Traefik reports the internal IPs of all nodes in the cluster into the `status.loadBalancer.ingress` field of each managed Ingress resource.
|
||||
|
||||
This is the equivalent of ingress-nginx's `--report-node-internal-ip-address` flag and is the recommended approach for bare-metal Kubernetes deployments where Traefik runs as a DaemonSet without a cloud LoadBalancer or MetalLB.
|
||||
|
||||
This option requires cluster-scope access to Node resources and is mutually exclusive with `ingressEndpoint` and `disableClusterScopeResources`.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
kubernetesIngress:
|
||||
reportNodeInternalIPs: true
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.kubernetesIngress]
|
||||
reportNodeInternalIPs = true
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.kubernetesingress.reportnodeinternalips=true
|
||||
```
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
See the dedicated section in [routing](../../../../reference/routing-configuration/kubernetes/ingress.md).
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
kind: Node
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: node1
|
||||
|
||||
status:
|
||||
addresses:
|
||||
- type: InternalIP
|
||||
address: 10.0.0.1
|
||||
- type: ExternalIP
|
||||
address: 1.2.3.4
|
||||
|
||||
---
|
||||
kind: Node
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: node2
|
||||
|
||||
status:
|
||||
addresses:
|
||||
- type: InternalIP
|
||||
address: 10.0.0.2
|
||||
|
||||
---
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
rules:
|
||||
- host: "*.foo.com"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: service1
|
||||
port:
|
||||
number: 80
|
||||
|
||||
---
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: service1
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
---
|
||||
kind: EndpointSlice
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
metadata:
|
||||
name: service1-abc
|
||||
namespace: default
|
||||
labels:
|
||||
kubernetes.io/service-name: service1
|
||||
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- port: 8080
|
||||
name: ""
|
||||
endpoints:
|
||||
- addresses:
|
||||
- 10.10.0.1
|
||||
conditions:
|
||||
ready: true
|
||||
@@ -48,6 +48,7 @@ type Provider struct {
|
||||
LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
|
||||
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation or IngressClass name to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
|
||||
IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty" export:"true"`
|
||||
ReportNodeInternalIPs bool `description:"Report node internal IPs in Ingress status." json:"reportNodeInternalIPs,omitempty" toml:"reportNodeInternalIPs,omitempty" yaml:"reportNodeInternalIPs,omitempty" export:"true"`
|
||||
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
||||
AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
||||
AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
|
||||
@@ -72,6 +73,14 @@ func (p *Provider) SetRouterTransform(routerTransform k8s.RouterTransform) {
|
||||
|
||||
// Init the provider.
|
||||
func (p *Provider) Init() error {
|
||||
if p.ReportNodeInternalIPs && p.IngressEndpoint != nil {
|
||||
return errors.New("reportNodeInternalIPs and ingressEndpoint are mutually exclusive")
|
||||
}
|
||||
|
||||
if p.ReportNodeInternalIPs && p.DisableClusterScopeResources {
|
||||
return errors.New("reportNodeInternalIPs and disableClusterScopeResources are mutually exclusive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -246,6 +255,26 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
|
||||
ingresses := client.GetIngresses()
|
||||
|
||||
var nodeIngressStatus []netv1.IngressLoadBalancerIngress
|
||||
if p.ReportNodeInternalIPs {
|
||||
nodes, _, err := client.GetNodes()
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("Error while getting nodes for ingress status")
|
||||
} else {
|
||||
for _, node := range nodes {
|
||||
for _, address := range node.Status.Addresses {
|
||||
if address.Type == corev1.NodeInternalIP {
|
||||
nodeIngressStatus = append(nodeIngressStatus, netv1.IngressLoadBalancerIngress{IP: address.Address})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(nodeIngressStatus) == 0 {
|
||||
log.Ctx(ctx).Error().Msg("No nodes with internal IP address found for ingress status")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
certConfigs := make(map[string]*tls.CertAndStores)
|
||||
for _, ingress := range ingresses {
|
||||
logger := log.Ctx(ctx).With().Str("ingress", ingress.Name).Str("namespace", ingress.Namespace).Logger()
|
||||
@@ -255,7 +284,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
continue
|
||||
}
|
||||
|
||||
if err := p.updateIngressStatus(ingress, client); err != nil {
|
||||
if err := p.updateIngressStatus(ingress, client, nodeIngressStatus); err != nil {
|
||||
logger.Error().Err(err).Msg("Error while updating ingress status")
|
||||
}
|
||||
|
||||
@@ -442,7 +471,11 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
return conf
|
||||
}
|
||||
|
||||
func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client) error {
|
||||
func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client, nodeIngressStatus []netv1.IngressLoadBalancerIngress) error {
|
||||
if len(nodeIngressStatus) > 0 {
|
||||
return k8sClient.UpdateIngressStatus(ing, nodeIngressStatus)
|
||||
}
|
||||
|
||||
// Only process if an EndpointIngress has been configured.
|
||||
if p.IngressEndpoint == nil {
|
||||
return nil
|
||||
@@ -623,12 +656,12 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In
|
||||
return nil, errors.New("nodes lookup is disabled")
|
||||
}
|
||||
|
||||
nodes, nodesExists, nodesErr := client.GetNodes()
|
||||
nodes, _, nodesErr := client.GetNodes()
|
||||
if nodesErr != nil {
|
||||
return nil, nodesErr
|
||||
}
|
||||
|
||||
if !nodesExists || len(nodes) == 0 {
|
||||
if len(nodes) == 0 {
|
||||
return nil, fmt.Errorf("nodes not found in namespace %s", namespace)
|
||||
}
|
||||
|
||||
|
||||
@@ -3094,6 +3094,77 @@ func readResources(t *testing.T, paths []string) []runtime.Object {
|
||||
return k8sObjects
|
||||
}
|
||||
|
||||
func TestProviderInit(t *testing.T) {
|
||||
p := Provider{
|
||||
ReportNodeInternalIPs: true,
|
||||
IngressEndpoint: &EndpointIngress{IP: "1.2.3.4"},
|
||||
}
|
||||
assert.EqualError(t, p.Init(), "reportNodeInternalIPs and ingressEndpoint are mutually exclusive")
|
||||
|
||||
p2 := Provider{
|
||||
ReportNodeInternalIPs: true,
|
||||
DisableClusterScopeResources: true,
|
||||
}
|
||||
assert.EqualError(t, p2.Init(), "reportNodeInternalIPs and disableClusterScopeResources are mutually exclusive")
|
||||
|
||||
p3 := Provider{ReportNodeInternalIPs: true}
|
||||
assert.NoError(t, p3.Init())
|
||||
}
|
||||
|
||||
func TestReportNodeInternalIPs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
client clientMock
|
||||
expectedEmpty bool
|
||||
}{
|
||||
{
|
||||
desc: "nodes present",
|
||||
client: newClientMock(generateTestFilename("Node Internal IP")),
|
||||
},
|
||||
{
|
||||
desc: "GetNodes API error",
|
||||
client: clientMock{apiNodesError: errors.New("api nodes error")},
|
||||
expectedEmpty: true,
|
||||
},
|
||||
{
|
||||
desc: "no nodes found",
|
||||
client: clientMock{nodes: []*corev1.Node{}},
|
||||
expectedEmpty: true,
|
||||
},
|
||||
{
|
||||
desc: "nodes exist but none have an internal IP",
|
||||
client: clientMock{
|
||||
nodes: []*corev1.Node{
|
||||
{
|
||||
Status: corev1.NodeStatus{
|
||||
Addresses: []corev1.NodeAddress{
|
||||
{Type: corev1.NodeExternalIP, Address: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedEmpty: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := Provider{ReportNodeInternalIPs: true}
|
||||
conf := p.loadConfigurationFromIngresses(t.Context(), test.client)
|
||||
if test.expectedEmpty {
|
||||
assert.Empty(t, conf.HTTP.Routers)
|
||||
assert.Empty(t, conf.HTTP.Services)
|
||||
} else {
|
||||
assert.NotEmpty(t, conf.HTTP.Routers)
|
||||
assert.NotEmpty(t, conf.HTTP.Services)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrictPrefixMatchingRule(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
|
||||
Reference in New Issue
Block a user