From d0a8e3d6bf29a4feedc21a85b0ecdc3cc3c37a91 Mon Sep 17 00:00:00 2001 From: Ronan Hennessy Date: Thu, 22 Jun 2023 10:05:31 +0100 Subject: [PATCH] TELCODOCS-1237: VRFs for consistent isolated routes --- _topic_maps/_topic_map.yml | 4 + images/357_OpenShift_MetalLB_VRF_0823.png | Bin 0 -> 52983 bytes modules/nw-egress-service-cr.adoc | 45 ++++ modules/nw-egress-service-ovn.adoc | 125 ++++++++++ ...metallb-configure-return-traffic-proc.adoc | 203 +++++++++++++++++ modules/nw-metallb-configure-vrf-bgppeer.adoc | 214 ++++++++++++++++++ modules/virt-example-host-vrf.adoc | 45 ++++ ...-nmstate-updating-node-network-config.adoc | 8 + .../metallb/metallb-configure-bgp-peers.adoc | 14 ++ .../metallb-configure-return-traffic.adoc | 64 ++++++ ...traffic-for-vrf-loadbalancer-services.adoc | 44 ++++ 11 files changed, 766 insertions(+) create mode 100644 images/357_OpenShift_MetalLB_VRF_0823.png create mode 100644 modules/nw-egress-service-cr.adoc create mode 100644 modules/nw-egress-service-ovn.adoc create mode 100644 modules/nw-metallb-configure-return-traffic-proc.adoc create mode 100644 modules/nw-metallb-configure-vrf-bgppeer.adoc create mode 100644 modules/virt-example-host-vrf.adoc create mode 100644 networking/metallb/metallb-configure-return-traffic.adoc create mode 100644 networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc diff --git a/_topic_maps/_topic_map.yml b/_topic_maps/_topic_map.yml index 275a69103c..451ec70e1a 100644 --- a/_topic_maps/_topic_map.yml +++ b/_topic_maps/_topic_map.yml @@ -1323,6 +1323,8 @@ Topics: File: configuring-egress-ips-ovn - Name: Assigning an egress IP address File: assigning-egress-ips-ovn + - Name: Configuring an egress service + File: configuring-egress-traffic-for-vrf-loadbalancer-services - Name: Considerations for the use of an egress router pod File: using-an-egress-router-ovn - Name: Deploying an egress router pod in redirect mode @@ -1451,6 +1453,8 @@ Topics: File: metallb-configure-bfd-profiles - Name: Configuring services to use MetalLB File: metallb-configure-services + - Name: Managing symmetric routing with MetalLB + File: metallb-configure-return-traffic - Name: MetalLB logging, troubleshooting, and support File: metallb-troubleshoot-support - Name: Associating secondary interfaces metrics to network attachments diff --git a/images/357_OpenShift_MetalLB_VRF_0823.png b/images/357_OpenShift_MetalLB_VRF_0823.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc4b38fca98c22b48af6cdae524318c2330ac40 GIT binary patch literal 52983 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 literal 0 HcmV?d00001 diff --git a/modules/nw-egress-service-cr.adoc b/modules/nw-egress-service-cr.adoc new file mode 100644 index 0000000000..aeca990ded --- /dev/null +++ b/modules/nw-egress-service-cr.adoc @@ -0,0 +1,45 @@ +// Module included in the following assemblies: +// +// * networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc + +:_content-type: REFERENCE +[id="nw-egress-service-ovn-cr_{context}"] += Egress service custom resource + +Define the configuration for an egress service in an `EgressService` custom resource. The following YAML describes the fields for the configuration of an egress service: + +[source,yaml] +---- +apiVersion: k8s.ovn.org/v1 +kind: EgressService +metadata: + name: <1> + namespace: <2> +spec: + sourceIPBy: <3> + nodeSelector: <4> + matchLabels: + node-role.kubernetes.io/: "" + network: <5> +---- +<1> Specify the name for the egress service. The name of the `EgressService` resource must match the name of the load-balancer service that you want to modify. +<2> Specify the namespace for the egress service. The namespace for the `EgressService` must match the namespace of the load-balancer service that you want to modify. The egress service is namespace-scoped. +<3> Specify the source IP address of egress traffic for pods behind a service. Valid values are `LoadBalancerIP` or `Network`. Use the `LoadBalancerIP` value to assign the `LoadBalancer` service ingress IP address as the source IP address for egress traffic. Specify `Network` to assign the network interface IP address as the source IP address for egress traffic. +<4> Optional: If you use the `LoadBalancerIP` value for the `sourceIPBy` specification, a single node handles the `LoadBalancer` service traffic. Use the `nodeSelector` field to limit which node can be assigned this task. When a node is selected to handle the service traffic, OVN-Kubernetes labels the node in the following format: `egress-service.k8s.ovn.org/-: ""`. When the `nodeSelector` field is not specified, any node can manage the `LoadBalancer` service traffic. +<5> Optional: Specify the routing table for egress traffic. If you do not include the `network` specification, the egress service uses the default host network. + +.Example egress service specification +[source,yaml] +---- +apiVersion: k8s.ovn.org/v1 +kind: EgressService +metadata: + name: test-egress-service + namespace: test-namespace +spec: + sourceIPBy: "LoadBalancerIP" + nodeSelector: + matchLabels: + vrf: "true" + network: "2" +---- diff --git a/modules/nw-egress-service-ovn.adoc b/modules/nw-egress-service-ovn.adoc new file mode 100644 index 0000000000..5677251835 --- /dev/null +++ b/modules/nw-egress-service-ovn.adoc @@ -0,0 +1,125 @@ +// Module included in the following assemblies: +// +// * networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc + +:_content-type: PROCEDURE +[id="nw-egress-service-ovn_{context}"] += Deploying an egress service + +You can deploy an egress service to manage egress traffic for pods behind a `LoadBalancer` service. + +The following example configures the egress traffic to have the same source IP address as the ingress IP address of the `LoadBalancer` service. + +.Prerequisites + +* Install the OpenShift CLI (`oc`). +* Log in as a user with `cluster-admin` privileges. +* You configured MetalLB `BGPPeer` resources. + +.Procedure + +. Create an `IPAddressPool` CR with the desired IP for the service: + +.. Create a file, such as `ip-addr-pool.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: example-pool + namespace: metallb-system +spec: + addresses: + - 172.19.0.100/32 +---- + +.. Apply the configuration for the IP address pool by running the following command: ++ +[source,terminal] +---- +$ oc apply -f ip-addr-pool.yaml +---- + +. Create `Service` and `EgressService` CRs. + +.. Create a file, such as `service-egress-service.yaml`, with content like the following example: ++ +[source,yaml,subs="+quotes,+macros"] +---- +apiVersion: v1 +kind: Service +metadata: + name: example-service + namespace: example-namespace + annotations: + metallb.universe.tf/address-pool: example-pool <1> +spec: + selector: + app: example + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 + type: LoadBalancer +--- +apiVersion: k8s.ovn.org/v1 +kind: EgressService +metadata: + name: example-service + namespace: example-namespace +spec: + sourceIPBy: "LoadBalancerIP" <2> + nodeSelector: <3> + matchLabels: + node-role.kubernetes.io/worker: "" +---- +<1> The `LoadBalancer` service uses the IP address assigned by MetalLB from the `example-pool` IP address pool. +<2> This example uses the `LoadBalancerIP` value to assign the ingress IP address of the `LoadBalancer` service as the source IP address of egress traffic. +<3> When you specify the `LoadBalancerIP` value, a single node handles the `LoadBalancer` service's traffic. In this example, only nodes with the `worker` label can be selected to handle the traffic. When a node is selected, OVN-Kubernetes labels the node in the following format `egress-service.k8s.ovn.org/-: ""`. ++ +[NOTE] +==== +If you use the `sourceIPBy: "LoadBalancerIP"` setting, you must specify the load-balancer node in the `BGPAdvertisement` custom resource (CR). +==== + +.. Apply the configuration for the service and egress service by running the following command: ++ +[source,terminal] +---- +$ oc apply -f service-egress-service.yaml +---- + +. Create a `BGPAdvertisement` CR to advertise the service: + +.. Create a file, such as `service-bgp-advertisement.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: BGPAdvertisement +metadata: + name: example-bgp-adv + namespace: metallb-system +spec: + ipAddressPools: + - example-pool + nodeSelector: + - matchLabels: + egress-service.k8s.ovn.org/example-namespace-example-service: "" <1> +---- + +<1> In this example, the `EgressService` CR configures the source IP address for egress traffic to use the load-balancer service IP address. Therefore, you must specify the load-balancer node for return traffic to use the same return path for the traffic originating from the pod. + +.Verification + + . Verify that you can access the application endpoint of the pods running behind the MetalLB service by running the following command: ++ +[source,terminal] +---- +$ curl : <1> +---- +<1> Update the external IP address and port number to suit your application endpoint. + +. If you assigned the `LoadBalancer` service's ingress IP address as the source IP address for egress traffic, verify this configuration by using tools such as `tcpdump` to analyze packets received at the external client. diff --git a/modules/nw-metallb-configure-return-traffic-proc.adoc b/modules/nw-metallb-configure-return-traffic-proc.adoc new file mode 100644 index 0000000000..10ccb2c98d --- /dev/null +++ b/modules/nw-metallb-configure-return-traffic-proc.adoc @@ -0,0 +1,203 @@ +// Module included in the following assemblies: +// +// * networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc + +:_content-type: PROCEDURE +[id="nw-metallb-configure-return-traffic-proc_{context}"] += Configuring symmetric routing by using VRFs with MetalLB + +You can configure symmetric network routing for applications behind a MetalLB service that require the same ingress and egress network paths. + +This example associates a VRF routing table with MetalLB and an egress service to enable symmetric routing for ingress and egress traffic for pods behind a `LoadBalancer` service. + +[NOTE] +==== +* If you use the `sourceIPBy: "LoadBalancerIP"` setting in the `EgressService` CR, you must specify the load-balancer node in the `BGPAdvertisement` custom resource (CR). + +* You can use the `sourceIPBy: "Network"` setting on clusters that use OVN-Kubernetes configured with the `gatewayConfig.routingViaHost` specification set to `true` only. Additionally, if you use the `sourceIPBy: "Network"` setting, you must schedule the application workload on nodes configured with the network VRF instance. +==== + +.Prerequisites + +* Install the OpenShift CLI (`oc`). +* Log in as a user with `cluster-admin` privileges. + +.Procedure + +. Create a `NodeNetworkConfigurationPolicy` CR to define the VRF instance: + +.. Create a file, such as `node-network-vrf.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: nmstate.io/v1 +kind: NodeNetworkConfigurationPolicy +metadata: + name: vrfpolicy <1> +spec: + nodeSelector: + vrf: "true" <2> + maxUnavailable: 3 + desiredState: + interfaces: + - name: ens4vrf <3> + type: vrf <4> + state: up + vrf: + port: + - ens4 <5> + route-table-id: 2 <6> + routes: <7> + config: + - destination: 0.0.0.0/0 + metric: 150 + next-hop-address: 192.168.130.1 + next-hop-interface: ens4 + table-id: 2 + route-rules: <8> + config: + - ip-to: 172.30.0.0/16 + priority: 998 + route-table: 254 + - ip-to: 10.132.0.0/14 + priority: 998 + route-table: 254 +---- +<1> The name of the policy. +<2> This example applies the policy to all nodes with the label `vrf:true`. +<3> The name of the interface. +<4> The type of interface. This example creates a VRF instance. +<5> The node interface that the VRF attaches to. +<6> The name of the route table ID for the VRF. +<7> Defines the configuration for network routes. The `next-hop-address` field defines the IP address of the next hop for the route. The `next-hop-interface` field defines the outgoing interface for the route. In this example, the VRF routing table is `2`, which references the ID that you define in the `EgressService` CR. +<8> Defines additional route rules. The `ip-to` fields must match the `Cluster Network` CIDR and `Service Network` CIDR. You can view the values for these CIDR address specifications by running the following command: `oc describe network.config/cluster`. + +.. Apply the policy by running the following command: ++ +[source,terminal] +---- +$ oc apply -f node-network-vrf.yaml +---- + +. Create a `BGPPeer` custom resource (CR): + +.. Create a file, such as `frr-via-vrf.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta2 +kind: BGPPeer +metadata: + name: frrviavrf + namespace: metallb-system +spec: + myASN: 100 + peerASN: 200 + peerAddress: 192.168.130.1 + vrf: ens4vrf <1> +---- +<1> Specifies the VRF instance to associate with the BGP peer. MetalLB can advertise services and make routing decisions based on the routing information in the VRF. + +.. Apply the configuration for the BGP peer by running the following command: ++ +[source,terminal] +---- +$ oc apply -f frr-via-vrf.yaml +---- + +. Create an `IPAddressPool` CR: + +.. Create a file, such as `first-pool.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: first-pool + namespace: metallb-system +spec: + addresses: + - 192.169.10.0/32 +---- + +.. Apply the configuration for the IP address pool by running the following command: ++ +[source,terminal] +---- +$ oc apply -f first-pool.yaml +---- + +. Create a `BGPAdvertisement` CR: + +.. Create a file, such as `first-adv.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: BGPAdvertisement +metadata: + name: first-adv + namespace: metallb-system +spec: + ipAddressPools: + - first-pool + peers: + - frrviavrf <1> + nodeSelectors: + - matchLabels: + egress-service.k8s.ovn.org/test-server1: "" <2> +---- +<1> In this example, MetalLB advertises a range of IP addresses from the `first-pool` IP address pool to the `frrviavrf` BGP peer. +<2> In this example, the `EgressService` CR configures the source IP address for egress traffic to use the load-balancer service IP address. Therefore, you must specify the load-balancer node for return traffic to use the same return path for the traffic originating from the pod. + +.. Apply the configuration for the BGP advertisement by running the following command: ++ +[source,terminal] +---- +$ oc apply -f first-adv.yaml +---- + +. Create an `EgressService` CR: + +.. Create a file, such as `egress-service.yaml`, with content like the following example: ++ +[source,yaml,options="nowrap",role="white-space-pre"] +---- +apiVersion: k8s.ovn.org/v1 +kind: EgressService +metadata: + name: server1 <1> + namespace: test <2> +spec: + sourceIPBy: "LoadBalancerIP" <3> + nodeSelector: + matchLabels: + vrf: "true" <4> + network: "2" <5> +---- +<1> Specify the name for the egress service. The name of the `EgressService` resource must match the name of the load-balancer service that you want to modify. +<2> Specify the namespace for the egress service. The namespace for the `EgressService` must match the namespace of the load-balancer service that you want to modify. The egress service is namespace-scoped. +<3> This example assigns the `LoadBalancer` service ingress IP address as the source IP address for egress traffic. +<4> If you specify `LoadBalancer` for the `sourceIPBy` specification, a single node handles the `LoadBalancer` service traffic. In this example, only a node with the label `vrf: "true"` can handle the service traffic. If you do not specify a node, OVN-Kubernetes selects a worker node to handle the service traffic. When a node is selected, OVN-Kubernetes labels the node in the following format: `egress-service.k8s.ovn.org/-: ""`. +<5> Specify the routing table for egress traffic. + +.. Apply the configuration for the egress service by running the following command: ++ +[source,terminal] +---- +$ oc apply -f egress-service.yaml +---- + +.Verification + +. Verify that you can access the application endpoint of the pods running behind the MetalLB service by running the following command: ++ +[source,terminal] +---- +$ curl : <1> +---- +<1> Update the external IP address and port number to suit your application endpoint. + +. Optional: If you assigned the `LoadBalancer` service ingress IP address as the source IP address for egress traffic, verify this configuration by using tools such as `tcpdump` to analyze packets received at the external client. + diff --git a/modules/nw-metallb-configure-vrf-bgppeer.adoc b/modules/nw-metallb-configure-vrf-bgppeer.adoc new file mode 100644 index 0000000000..952b5743e7 --- /dev/null +++ b/modules/nw-metallb-configure-vrf-bgppeer.adoc @@ -0,0 +1,214 @@ +// Module included in the following assemblies: +// +// * networking/metallb/metallb-configure-bgp-peers.adoc + +:_content-type: PROCEDURE +[id="nw-metallb-bgp-peer-vrf_{context}"] += Exposing a service through a network VRF + +You can expose a service through a virtual routing and forwarding (VRF) instance by associating a VRF on a network interface with a BGP peer. + +:FeatureName: Exposing a service through a VRF on a BGP peer +include::snippets/technology-preview.adoc[] + +By using a VRF on a network interface to expose a service through a BGP peer, you can segregate traffic to the service, configure independent routing decisions, and enable multi-tenancy support on a network interface. + +[NOTE] +==== +By establishing a BGP session through an interface belonging to a network VRF, MetalLB can advertise services through that interface and enable external traffic to reach the service through this interface. However, the network VRF routing table is different from the default VRF routing table used by OVN-Kubernetes. Therefore, the traffic cannot reach the OVN-Kubernetes network infrastructure. + +To enable the traffic directed to the service to reach the OVN-Kubernetes network infrastructure, you must configure routing rules to define the next hops for network traffic. See the `NodeNetworkConfigurationPolicy` resource in "Managing symmetric routing with MetalLB" in the _Additional resources_ section for more information. +==== + +These are the high-level steps to expose a service through a network VRF with a BGP peer: + +. Define a BGP peer and add a network VRF instance. +. Specify an IP address pool for MetalLB. +. Configure a BGP route advertisement for MetalLB to advertise a route using the specified IP address pool and the BGP peer associated with the VRF instance. +. Deploy a service to test the configuration. + +.Prerequisites + +* You installed the OpenShift CLI (`oc`). +* You logged in as a user with `cluster-admin` privileges. +* You defined a `NodeNetworkConfigurationPolicy` to associate a Virtual Routing and Forwarding (VRF) instance with a network interface. For more information about completing this prerequisite, see the _Additional resources_ section. +* You installed MetalLB on your cluster. + +.Procedure + +. Create a `BGPPeer` custom resources (CR): + +.. Create a file, such as `frrviavrf.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta2 +kind: BGPPeer +metadata: + name: frrviavrf + namespace: metallb-system +spec: + myASN: 100 + peerASN: 200 + peerAddress: 192.168.130.1 + vrf: ens4vrf <1> +---- +<1> Specifies the network VRF instance to associate with the BGP peer. MetalLB can advertise services and make routing decisions based on the routing information in the VRF. ++ +[NOTE] +==== +You must configure this network VRF instance in a `NodeNetworkConfigurationPolicy` CR. See the _Additional resources_ for more information. +==== + +.. Apply the configuration for the BGP peer by running the following command: ++ +[source,terminal] +---- +$ oc apply -f frrviavrf.yaml +---- + +. Create an `IPAddressPool` CR: + +.. Create a file, such as `first-pool.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: first-pool + namespace: metallb-system +spec: + addresses: + - 192.169.10.0/32 +---- + +.. Apply the configuration for the IP address pool by running the following command: ++ +[source,terminal] +---- +$ oc apply -f first-pool.yaml +---- + +. Create a `BGPAdvertisement` CR: + +.. Create a file, such as `first-adv.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: metallb.io/v1beta1 +kind: BGPAdvertisement +metadata: + name: first-adv + namespace: metallb-system +spec: + ipAddressPools: + - first-pool + peers: + - frrviavrf <1> +---- +<1> In this example, MetalLB advertises a range of IP addresses from the `first-pool` IP address pool to the `frrviavrf` BGP peer. + +.. Apply the configuration for the BGP advertisement by running the following command: ++ +[source,terminal] +---- +$ oc apply -f first-adv.yaml +---- + +. Create a `Namespace`, `Deployment`, and `Service` CR: + +.. Create a file, such as `deploy-service.yaml`, with content like the following example: ++ +[source,yaml] +---- +apiVersion: v1 +kind: Namespace +metadata: + name: test +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: server + namespace: test +spec: + selector: + matchLabels: + app: server + template: + metadata: + labels: + app: server + spec: + containers: + - name: server + image: registry.redhat.io/ubi9/ubi + ports: + - name: http + containerPort: 30100 + command: ["/bin/sh", "-c"] + args: ["sleep INF"] +--- +apiVersion: v1 +kind: Service +metadata: + name: server1 + namespace: test +spec: + ports: + - name: http + port: 30100 + protocol: TCP + targetPort: 30100 + selector: + app: server + type: LoadBalancer +---- + +.. Apply the configuration for the namespace, deployment, and service by running the following command: ++ +[source,terminal] +---- +$ oc apply -f deploy-service.yaml +---- + +.Verification + +. Identify a MetalLB speaker pod by running the following command: ++ +[source,terminal] +---- +$ oc get -n metallb-system pods -l component=speaker +---- ++ +.Example output +[source,terminal] +---- +NAME READY STATUS RESTARTS AGE +speaker-c6c5f 6/6 Running 0 69m +---- + +. Verify that the state of the BGP session is `Established` in the speaker pod by running the following command, replacing the variables to match your configuration: ++ +[source,terminal] +---- +$ oc exec -n metallb-system -c frr -- vtysh -c "show bgp vrf neigh" +---- ++ +.Example output +[source,terminal] +---- +BGP neighbor is 192.168.30.1, remote AS 200, local AS 100, external link + BGP version 4, remote router ID 192.168.30.1, local router ID 192.168.30.71 + BGP state = Established, up for 04:20:09 + +... +---- + +. Verify that the service is advertised correctly by running the following command: ++ +[source,terminal] +---- +$ oc exec -n metallb-system -c frr -- vtysh -c "show bgp vrf ipv4" +---- \ No newline at end of file diff --git a/modules/virt-example-host-vrf.adoc b/modules/virt-example-host-vrf.adoc new file mode 100644 index 0000000000..69b6e8d79d --- /dev/null +++ b/modules/virt-example-host-vrf.adoc @@ -0,0 +1,45 @@ +// Module included in the following assemblies: +// +// * networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc + +[id="virt-example-host-vrf_{context}"] += Example: Network interface with a VRF instance node network configuration policy + +Associate a Virtual Routing and Forwarding (VRF) instance with a network interface by applying a `NodeNetworkConfigurationPolicy` custom resource (CR). + +:FeatureName: Associating a VRF instance with a network interface +include::snippets/technology-preview.adoc[] + +By associating a VRF instance with a network interface, you can support traffic isolation, independent routing decisions, and the logical separation of network resources. + +In a bare-metal environment, you can announce load balancer services through interfaces belonging to a VRF instance by using MetalLB. For more information, see the _Additional resources_ section. + +The following YAML file is an example of associating a VRF instance to a network interface. +It includes samples values that you must replace with your own information. + +[source,yaml] +---- +apiVersion: nmstate.io/v1 +kind: NodeNetworkConfigurationPolicy +metadata: + name: vrfpolicy <1> +spec: + nodeSelector: + vrf: "true" <2> + maxUnavailable: 3 + desiredState: + interfaces: + - name: ens4vrf <3> + type: vrf <4> + state: up + vrf: + port: + - ens4 <5> + route-table-id: 2 <6> +---- +<1> The name of the policy. +<2> This example applies the policy to all nodes with the label `vrf:true`. +<3> The name of the interface. +<4> The type of interface. This example creates a VRF instance. +<5> The node interface to which the VRF attaches. +<6> The name of the route table ID for the VRF. diff --git a/networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc b/networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc index 97aa08e334..aefe6bb65a 100644 --- a/networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc +++ b/networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc @@ -48,6 +48,14 @@ include::modules/virt-example-ethernet-nncp.adoc[leveloffset=+2] include::modules/virt-example-nmstate-multiple-interfaces.adoc[leveloffset=+2] +include::modules/virt-example-host-vrf.adoc[leveloffset=+2] + +[role="_additional-resources"] +.Additional resources + +* xref:../../networking/multiple_networks/about-virtual-routing-and-forwarding.adoc#cnf-about-virtual-routing-and-forwarding_about-virtual-routing-and-forwarding[About virtual routing and forwarding] +* xref:../../networking/metallb/metallb-configure-bgp-peers.adoc#nw-metallb-bgp-peer-vrf_configure-metallb-bgp-peers[Exposing a service through a network VRF] + [id="capturing-nic-static-ip_k8s-nmstate-updating-node-network-config"] == Capturing the static IP of a NIC attached to a bridge diff --git a/networking/metallb/metallb-configure-bgp-peers.adoc b/networking/metallb/metallb-configure-bgp-peers.adoc index 9ca3edd1b9..5dfd1d5f50 100644 --- a/networking/metallb/metallb-configure-bgp-peers.adoc +++ b/networking/metallb/metallb-configure-bgp-peers.adoc @@ -23,6 +23,20 @@ include::modules/nw-metallb-configure-bgppeer.adoc[leveloffset=+1] // Add a BGP peer include::modules/nw-metallb-configure-specificpools-to-bgppeer.adoc[leveloffset=+1] +// Add a BGP peer with VRF +include::modules/nw-metallb-configure-vrf-bgppeer.adoc[leveloffset=+1] + +[role="_additional-resources"] +.Additional resources + +* xref:../../networking/multiple_networks/about-virtual-routing-and-forwarding.adoc#cnf-about-virtual-routing-and-forwarding_about-virtual-routing-and-forwarding[About virtual routing and forwarding] + +* xref:../../networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc#virt-example-host-vrf_k8s_nmstate-updating-node-network-config[Example: Network interface with a VRF instance node network configuration policy] + +* xref:../../networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc#configuring-egress-traffic-loadbalancer-services[Configuring an egress service] + +* xref:../../networking/metallb/metallb-configure-return-traffic.adoc#metallb-configure-return-traffic[Managing symmetric routing with MetalLB] + // Examples include::modules/nw-metallb-example-bgppeer.adoc[leveloffset=+1] diff --git a/networking/metallb/metallb-configure-return-traffic.adoc b/networking/metallb/metallb-configure-return-traffic.adoc new file mode 100644 index 0000000000..6cd26e5d54 --- /dev/null +++ b/networking/metallb/metallb-configure-return-traffic.adoc @@ -0,0 +1,64 @@ +:_content-type: ASSEMBLY +[id="metallb-configure-return-traffic"] += Managing symmetric routing with MetalLB +include::_attributes/common-attributes.adoc[] +:context: metallb-configure-return-traffic + +toc::[] + +As a cluster administrator, you can effectively manage traffic for pods behind a MetalLB load-balancer service with multiple host interfaces by implementing features from MetalLB, NMState, and OVN-Kubernetes. By combining these features in this context, you can provide symmetric routing, traffic segregation, and support clients on different networks with overlapping CIDR addresses. + +To achieve this functionality, learn how to implement virtual routing and forwarding (VRF) instances with MetalLB, and configure egress services. + +:FeatureName: Configuring symmetric traffic by using a VRF instance with MetalLB and an egress service +include::snippets/technology-preview.adoc[] + +[id="challenges-of-managing-symmetric-routing-with-metallb"] +== Challenges of managing symmetric routing with MetalLB + +When you use MetalLB with multiple host interfaces, MetalLB exposes and announces a service through all available interfaces on the host. This can present challenges relating to network isolation, asymmetric return traffic and overlapping CIDR addresses. + +One option to ensure that return traffic reaches the correct client is to use static routes. However, with this solution, MetalLB cannot isolate the services and then announce each service through a different interface. Additionally, static routing requires manual configuration and requires maintenance if remote sites are added. + +A further challenge of symmetric routing when implementing a MetalLB service is scenarios where external systems expect the source and destination IP address for an application to be the same. The default behavior for {product-title} is to assign the IP address of the host network interface as the source IP address for traffic originating from pods. This is problematic with multiple host interfaces. + +You can overcome these challenges by implementing a configuration that combines features from MetalLB, NMState, and OVN-Kubernetes. + +[id="overview-of-managing-symmetric-routing-using-vrf-based-networks-with-metallb"] +== Overview of managing symmetric routing by using VRFs with MetalLB + +You can overcome the challenges of implementing symmetric routing by using NMState to configure a VRF instance on a host, associating the VRF instance with a MetalLB `BGPPeer` resource, and configuring an egress service for egress traffic with OVN-Kubernetes. + +.Network overview of managing symmetric routing by using VRFs with MetalLB +image::357_OpenShift_MetalLB_VRF_0823.png[Network overview of managing symmetric routing by using VRFs with MetalLB] + +The configuration process involves three stages: + +.1. Define a VRF and routing rules + +* Configure a `NodeNetworkConfigurationPolicy` custom resource (CR) to associate a VRF instance with a network interface. +* Use the VRF routing table to direct ingress and egress traffic. + +.2. Link the VRF to a MetalLB `BGPPeer` + +* Configure a MetalLB `BGPPeer` resource to use the VRF instance on a network interface. +* By associating the `BGPPeer` resource with the VRF instance, the designated network interface becomes the primary interface for the BGP session, and MetalLB advertises the services through this interface. + +.3. Configure an egress service + +* Configure an egress service to choose the network associated with the VRF instance for egress traffic. +* Optional: Configure an egress service to use the IP address of the MetalLB load-balancer service as the source IP for egress traffic. + +// Deploying an egress service for VRF +include::modules/nw-metallb-configure-return-traffic-proc.adoc[leveloffset=+1] + +[role="_additional-resources"] +.Additional resources + +* xref:../../networking/multiple_networks/about-virtual-routing-and-forwarding.adoc#cnf-about-virtual-routing-and-forwarding_about-virtual-routing-and-forwarding[About virtual routing and forwarding] + +* xref:../../networking/metallb/metallb-configure-bgp-peers.adoc#nw-metallb-bgp-peer-vrf_configure-metallb-bgp-peers[Exposing a service through a network VRF] + +* xref:../../networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc#virt-example-host-vrf_k8s_nmstate-updating-node-network-config[Example: Network interface with a VRF instance node network configuration policy] + +* xref:../../networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc#configuring-egress-traffic-loadbalancer-services[Configuring an egress service] diff --git a/networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc b/networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc new file mode 100644 index 0000000000..4a6d6d23b1 --- /dev/null +++ b/networking/ovn_kubernetes_network_provider/configuring-egress-traffic-for-vrf-loadbalancer-services.adoc @@ -0,0 +1,44 @@ +:_content-type: ASSEMBLY +[id="configuring-egress-traffic-loadbalancer-services"] += Configuring an egress service +include::_attributes/common-attributes.adoc[] +:context: configuring-egress-traffic-loadbalancer-services + +toc::[] + +As a cluster administrator, you can configure egress traffic for pods behind a load balancer service by using an egress service. + +:FeatureName: Egress service +include::snippets/technology-preview.adoc[] + +You can use the `EgressService` custom resource (CR) to manage egress traffic in the following ways: + +* Assign a load balancer service IP address as the source IP address for egress traffic for pods behind the load balancer service. ++ +Assigning the load balancer IP address as the source IP address in this context is useful to present a single point of egress and ingress. For example, in some scenarios, an external system communicating with an application behind a load balancer service can expect the source and destination IP address for the application to be the same. ++ +[NOTE] +==== +When you assign the load balancer service IP address to egress traffic for pods behind the service, OVN-Kubernetes restricts the ingress and egress point to a single node. This limits the load balancing of traffic that MetalLB typically provides. +==== + +* Assign the egress traffic for pods behind a load balancer to a different network than the default node network. ++ +This is useful to assign the egress traffic for applications behind a load balancer to a different network than the default network. Typically, the different network is implemented by using a VRF instance associated with a network interface. + +// Describe the CR and provide an example. +include::modules/nw-egress-service-cr.adoc[leveloffset=+1] + +// Deploying an egress service +include::modules/nw-egress-service-ovn.adoc[leveloffset=+1] + +[role="_additional-resources"] +.Additional resources + +* xref:../../networking/metallb/metallb-configure-bgp-peers.adoc#nw-metallb-bgp-peer-vrf_configure-metallb-bgp-peers[Exposing a service through a network VRF] + +* xref:../../networking/k8s_nmstate/k8s-nmstate-updating-node-network-config.adoc#virt-example-host-vrf_k8s_nmstate-updating-node-network-config[Example: Network interface with a VRF instance node network configuration policy] + +* xref:../../networking/metallb/metallb-configure-return-traffic.adoc#metallb-configure-return-traffic[Managing symmetric routing with MetalLB] + +* xref:../../networking/multiple_networks/about-virtual-routing-and-forwarding.adoc#cnf-about-virtual-routing-and-forwarding_about-virtual-routing-and-forwarding[About virtual routing and forwarding]