Skip to content

Commit

Permalink
Merge pull request #76 from appuio/feat/egress-interfaces
Browse files Browse the repository at this point in the history
Add support for deploying a systemd service which configures egress dummy interfaces
  • Loading branch information
simu authored Apr 22, 2024
2 parents 5094a0c + 7341f76 commit 17f3781
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 12 deletions.
7 changes: 7 additions & 0 deletions class/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,10 @@ parameters:


debugNamespace: syn-debug-nodes

egressInterfaces:
machineConfigPools: []
nodeKubeconfig: /var/lib/kubelet/kubeconfig
shadowRangeConfigMap:
namespace: cilium
name: eip-shadow-ranges
1 change: 1 addition & 0 deletions class/openshift4-nodes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ parameters:
- openshift4-nodes/component/container-runtime.jsonnet
- openshift4-nodes/component/oc-debug-node.jsonnet
- openshift4-nodes/component/aggregated-clusterroles.jsonnet
- openshift4-nodes/component/egress-interfaces.jsonnet
input_type: jsonnet
output_path: openshift4-nodes/
21 changes: 16 additions & 5 deletions component/common.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@ local kube = import 'lib/kube.libjsonnet';
local inv = kap.inventory();
local params = inv.parameters.control_api;

local defaultLabels = {
'app.kubernetes.io/name': 'openshift4-nodes',
'app.kubernetes.io/component': 'openshift4-nodes',
'app.kubernetes.io/managed-by': 'commodore',
};

local MachineConfig(name) =
kube._Object('machineconfiguration.openshift.io/v1', 'MachineConfig', '99x-%s' % name) {
metadata+: {
labels+: defaultLabels,
},
};


{
DefaultLabels: {
'app.kubernetes.io/name': 'openshift4-nodes',
'app.kubernetes.io/component': 'openshift4-nodes',
'app.kubernetes.io/managed-by': 'commodore',
},
DefaultLabels: defaultLabels,
MachineConfig: MachineConfig,
}
68 changes: 68 additions & 0 deletions component/egress-interfaces.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
local common = import 'common.libsonnet';
local com = import 'lib/commodore.libjsonnet';
local kap = import 'lib/kapitan.libjsonnet';
local kube = import 'lib/kube.libjsonnet';
local inv = kap.inventory();

local params = inv.parameters.openshift4_nodes;

local script = (importstr 'scripts/create-egress-interfaces.sh') % {
kubelet_kubeconfig: params.egressInterfaces.nodeKubeconfig,
cm_namespace: params.egressInterfaces.shadowRangeConfigMap.namespace,
cm_name: params.egressInterfaces.shadowRangeConfigMap.name,
};

local configs = [
common.MachineConfig(
'%s-egress-interfaces' %
poolname
) {
metadata+: {
annotations+: {
'inline-contents.machineconfig.syn.tools/create-egress-interfaces.sh': script,
},
labels+: {
'machineconfiguration.openshift.io/role': poolname,
},
},
spec+: {
config: {
storage: {
files: [
{
path: '/usr/local/bin/appuio-create-egress-interfaces.sh',
mode: std.parseOctal('0755'),
contents:
'data:text/plain;charset=utf-8;base64,%s' %
std.base64(script),
},
],
systemd: {
units: [
{
name: 'appuio-create-egress-interfaces.service',
enabled: true,
contents: |||
[Unit]
Description=Assign egress IPs to node interface
After=NetworkManager-wait-online.service
Before=kubelet-dependencies.target
[Service]
ExecStart=/usr/local/bin/appuio-create-egress-interfaces.sh
Type=oneshot
[Install]
WantedBy=kubelet-dependencies.target
|||,
},
],
},
},
},
},
}
for poolname in com.renderArray(params.egressInterfaces.machineConfigPools)
];

{
[if std.length(configs) > 0 then '30_egress_interfaces']: configs,
}
8 changes: 1 addition & 7 deletions component/machine-configs.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ local params = inv.parameters.openshift4_nodes;

local mergedConfigs = machineConfigPools.MachineConfigs + com.makeMergeable(params.machineConfigs);

local MachineConfig(name) = kube._Object('machineconfiguration.openshift.io/v1', 'MachineConfig', '99x-%s' % name) {
metadata+: {
labels+: common.DefaultLabels,
},
};

local machineConfigs = [
if std.objectHas(mc.spec.config, 'storage') &&
std.objectHas(mc.spec.config.storage, 'files') then
Expand Down Expand Up @@ -71,7 +65,7 @@ local machineConfigs = [
}
else
mc
for mc in com.generateResources(mergedConfigs, MachineConfig)
for mc in com.generateResources(mergedConfigs, common.MachineConfig)
];
{
[if std.length(machineConfigs) > 0 then '10_machineconfigs']: machineConfigs,
Expand Down
23 changes: 23 additions & 0 deletions component/scripts/create-egress-interfaces.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -eo pipefail

export KUBECONFIG="%(kubelet_kubeconfig)s"

readonly shadow_data=$(kubectl -n "%(cm_namespace)s" get configmap "%(cm_name)s" -ojsonpath="{.data.${HOSTNAME}}")

for prefix in $(echo "$shadow_data" | jq -r '.|keys[]'); do
base=$(echo "$shadow_data" | jq -r ".${prefix}.base")
from=$(echo "$shadow_data" | jq -r ".${prefix}.from")
to=$(echo "$shadow_data" | jq -r ".${prefix}.to")
echo "Configuring dummy interfaces for egress range ${prefix}: base=${base}, from=${from}, to=${to}"
for suffix in $(seq "$from" "$to"); do
idx=$(("$suffix" - "$from"))
iface="${prefix}_${idx}"
ip l del "$iface" 2>/dev/null || true
ip l add "$iface" type dummy
ip a add "${base}.${suffix}" dev "$iface"
done
done

exit 0
49 changes: 49 additions & 0 deletions docs/modules/ROOT/pages/references/parameters.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,55 @@ This component will take ownership of the namespace specified here.
Please make sure you don't specify a namespace which is already managed by other means.
====

== `egressInterfaces`

[horizontal]
type:: dictionary
default::
+
[source,yaml]
----
machineConfigPools: []
nodeKubeconfig: /var/lib/kubelet/kubeconfig
shadowRangeConfigMap:
namespace: cilium
name: eip-shadow-ranges
----

NOTE: This feature is intended to be used when configuring floating egress IPs with Cilium according to the design selected in https://kb.vshn.ch/oc4/explanations/decisions/cloudscale-cilium-egressip.html[Floating egress IPs with Cilium on cloudscale].

This parameter will deploy a MachineConfig which configures a systemd service that sets up dummy interfaces for egress IPs on the nodes in each MachineConfigPool listed in field `machineConfigPools`.
The component supports removal of MachineConfigPools by prefixing the pool with `~`.

The systemd unit deployed by the script is configured to be executed after the node's network is online but before the Kubelet starts.
The systemd unit executes a script which reads the required egress interfaces and associated IPs from the configmap indicated in parameter `shadowRangeConfigMap`.

The script uses the file indicated in field `nodeKubeconfig` to fetch the ConfigMap from the cluster.
If the default value is used, the script will use the node's Kubelet kubeconfig to access the cluster.
To ensure the Kubelet can access the configmap, users should ensure that a pod which mounts the ConfigMap is running on the node.

[TIP]
====
Component cilium can deploy a suitable ConfigMap and DaemonSets which ensure that the Kubelets on all nodes that need to create egress dummy interfaces can access the ConfigMap.
See the documentation for http://localhost:2020/cilium/references/parameters.html#_egress_gateway_egress_ip_ranges[component-cilium's support for egress IP ranges] for details.
====

The script expects the ConfigMap to have a key matching each node name on which egress dummy interfaces should be configured.

=== Example

[source,yaml]
----
data:
infra-foo: '{"egress_a": {"base": "198.51.100", "from": "32", "to": "63"}}'
infra-bar: '{"egress_a": {"base": "198.51.100", "from": "64", "to": "95"}}'
----

For the ConfigMap data shown above the script will configure the following dummy interfaces:

* `egress_a_0` - `egress_a_31` with IPs 198.51.100.32 - 195.51.100.63 on node infra-foo.
* `egress_a_0` - `egress_a_31` with IPs 198.51.100.64 - 195.51.100.95 on node infra-bar.

== Example

[source,yaml]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
annotations:
inline-contents.machineconfig.syn.tools/create-egress-interfaces.sh: |
#!/bin/bash
set -eo pipefail
export KUBECONFIG="/var/lib/kubelet/kubeconfig"
readonly shadow_data=$(kubectl -n "cilium" get configmap "eip-shadow-ranges" -ojsonpath="{.data.${HOSTNAME}}")
for prefix in $(echo "$shadow_data" | jq -r '.|keys[]'); do
base=$(echo "$shadow_data" | jq -r ".${prefix}.base")
from=$(echo "$shadow_data" | jq -r ".${prefix}.from")
to=$(echo "$shadow_data" | jq -r ".${prefix}.to")
echo "Configuring dummy interfaces for egress range ${prefix}: base=${base}, from=${from}, to=${to}"
for suffix in $(seq "$from" "$to"); do
idx=$(("$suffix" - "$from"))
iface="${prefix}_${idx}"
ip l del "$iface" 2>/dev/null || true
ip l add "$iface" type dummy
ip a add "${base}.${suffix}" dev "$iface"
done
done
exit 0
labels:
app.kubernetes.io/component: openshift4-nodes
app.kubernetes.io/managed-by: commodore
app.kubernetes.io/name: openshift4-nodes
machineconfiguration.openshift.io/role: infra
name: 99x-infra-egress-interfaces
name: 99x-infra-egress-interfaces
spec:
config:
storage:
files:
- contents: data:text/plain;charset=utf-8;base64,IyEvYmluL2Jhc2gKCnNldCAtZW8gcGlwZWZhaWwKCmV4cG9ydCBLVUJFQ09ORklHPSIvdmFyL2xpYi9rdWJlbGV0L2t1YmVjb25maWciCgpyZWFkb25seSBzaGFkb3dfZGF0YT0kKGt1YmVjdGwgLW4gImNpbGl1bSIgZ2V0IGNvbmZpZ21hcCAiZWlwLXNoYWRvdy1yYW5nZXMiIC1vanNvbnBhdGg9InsuZGF0YS4ke0hPU1ROQU1FfX0iKQoKZm9yIHByZWZpeCBpbiAkKGVjaG8gIiRzaGFkb3dfZGF0YSIgfCBqcSAtciAnLnxrZXlzW10nKTsgZG8KICBiYXNlPSQoZWNobyAiJHNoYWRvd19kYXRhIiB8IGpxIC1yICIuJHtwcmVmaXh9LmJhc2UiKQogIGZyb209JChlY2hvICIkc2hhZG93X2RhdGEiIHwganEgLXIgIi4ke3ByZWZpeH0uZnJvbSIpCiAgdG89JChlY2hvICIkc2hhZG93X2RhdGEiIHwganEgLXIgIi4ke3ByZWZpeH0udG8iKQogIGVjaG8gIkNvbmZpZ3VyaW5nIGR1bW15IGludGVyZmFjZXMgZm9yIGVncmVzcyByYW5nZSAke3ByZWZpeH06IGJhc2U9JHtiYXNlfSwgZnJvbT0ke2Zyb219LCB0bz0ke3RvfSIKICBmb3Igc3VmZml4IGluICQoc2VxICIkZnJvbSIgIiR0byIpOyBkbwogICAgaWR4PSQoKCIkc3VmZml4IiAtICIkZnJvbSIpKQogICAgaWZhY2U9IiR7cHJlZml4fV8ke2lkeH0iCiAgICBpcCBsIGRlbCAiJGlmYWNlIiAyPi9kZXYvbnVsbCB8fCB0cnVlCiAgICBpcCBsIGFkZCAiJGlmYWNlIiB0eXBlIGR1bW15CiAgICBpcCBhIGFkZCAiJHtiYXNlfS4ke3N1ZmZpeH0iIGRldiAiJGlmYWNlIgogIGRvbmUKZG9uZQoKZXhpdCAwCg==
mode: 493
path: /usr/local/bin/appuio-create-egress-interfaces.sh
systemd:
units:
- contents: |
[Unit]
Description=Assign egress IPs to node interface
After=NetworkManager-wait-online.service
Before=kubelet-dependencies.target
[Service]
ExecStart=/usr/local/bin/appuio-create-egress-interfaces.sh
Type=oneshot
[Install]
WantedBy=kubelet-dependencies.target
enabled: true
name: appuio-create-egress-interfaces.service
4 changes: 4 additions & 0 deletions tests/machineconfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ parameters:
keyfile /etc/chrony.keys
leapsectz right/UTC
logdir /var/log/chrony
egressInterfaces:
machineConfigPools:
- infra

0 comments on commit 17f3781

Please sign in to comment.