Skip to content

Commit 7f1524a

Browse files
committed
KinD plugin and device
1 parent 3a832ab commit 7f1524a

5 files changed

Lines changed: 209 additions & 0 deletions

File tree

docs/plugins.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
plugins/check.config.md
1717
plugins/fabric.md
1818
plugins/files.md
19+
plugins/kind.md
1920
plugins/mlag.vtep.md
2021
plugins/multilab.md
2122
plugins/node.clone.md

docs/plugins/kind.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Kubernetes Cluster in Container Nodes
2+
3+
The *kind* plugin implements Kubernetes in Docker (KinD) cluster for *containerlab* deployments.
4+
5+
```eval_rst
6+
.. contents:: Table of Contents
7+
:depth: 2
8+
:local:
9+
:backlinks: none
10+
```
11+
12+
## Using the Plugin
13+
14+
* Add `plugin: [ kind ]` to lab topology. The plugin adds **kind** device that can be used to deploy a Kubernetes cluster
15+
* Add a node with **device: kind** to the lab topology. The *kind* plugin will automatically create control-plane and worker nodes.
16+
* Set the number of worker nodes in the cluster with the **kind.workers** node parameter. Without this parameter, the plugin deploys a single-node Kubernetes cluster
17+
18+
For example, the following topology deploys a 3-node cluster with no external connectivity (the cluster is connected to the *kind* Docker network created by *containerlab*):
19+
20+
```
21+
provider: clab
22+
plugin: [ kind ]
23+
24+
nodes:
25+
kc:
26+
device: kind
27+
kind.workers: 2
28+
29+
```
30+
31+
The names of the cluster members are derived from the cluster name. In our example, the three containers would be named *kc-control-plane*, *kc-worker* and *kc-worker2*:
32+
33+
```
34+
$ docker ps
35+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
36+
49f3a87d7742 kindest/node:v1.34.3 "/usr/local/bin/entr…" 11 minutes ago Up 10 minutes 127.0.0.1:35301->6443/tcp kc-control-plane
37+
832e14fe9842 kindest/node:v1.34.3 "/usr/local/bin/entr…" 11 minutes ago Up 10 minutes kc-worker2
38+
509551e5ca05 kindest/node:v1.34.3 "/usr/local/bin/entr…" 11 minutes ago Up 10 minutes kc-worker
39+
```
40+
41+
## Lab Connectivity
42+
43+
You can connect the KinD cluster node to any number of *netlab* links. When creating the control-plane and worker nodes, the *kind* plugin automatically connects them to the same links *if the links are stub or LAN links*. For point-to-point links, the *kind* plugin replaces the link between the cluster node and another node with a series of point-to-point links.
44+
45+
For example, _netlab_ creates three links when given this topology:
46+
47+
```
48+
provider: clab
49+
plugin: [ kind ]
50+
51+
nodes:
52+
kc:
53+
device: kind
54+
kind.workers: 2
55+
sw:
56+
device: frr
57+
58+
links: [ kc-sw ]
59+
```
60+
61+
| Link Name | Origin Device | Origin Port | Destination Device | Destination Port |
62+
|-----------------|---------------|-------------|--------------------|------------------|
63+
| | kc-control-plane | eth1 | sw | eth1 |
64+
| | kc-worker | eth1 | sw | eth2 |
65+
| | kc-worker2 | eth1 | sw | eth3 |
66+
67+
On the other hand, the following topology using a LAN link results in all three cluster nodes connected to the same switch port:
68+
69+
```
70+
provider: clab
71+
plugin: [ kind ]
72+
73+
nodes:
74+
kc:
75+
device: kind
76+
kind.workers: 2
77+
sw:
78+
device: frr
79+
80+
links:
81+
- interfaces: [ kc, sw ]
82+
type: lan
83+
```
84+
85+
| Origin Device | Origin Port | Link Name (NET) | Description |
86+
|---------------|-------------|-----------------|----------------------|
87+
| sw | eth1 | X_1 | sw -> [kc-control-plane,kc-worker,kc-worker2] |
88+
| kc-control-plane | eth1 | X_1 | kc-control-plane -> [sw,kc-worker,kc-worker2] |
89+
| kc-worker | eth1 | X_1 | kc-worker -> [sw,kc-control-plane,kc-worker2] |
90+
| kc-worker2 | eth1 | X_1 | kc-worker2 -> [sw,kc-control-plane,kc-worker] |
91+
92+
```{warning}
93+
The current version of the plugin does not configure IP addresses or static routes on the cluster nodes.
94+
```

netsim/daemons/kind/initial.j2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: kind.x-k8s.io/v1alpha4
2+
kind: Cluster
3+
nodes:
4+
- role: control-plane
5+
{% for wk in range(0,kind.workers|default(0)) %}
6+
- role: worker
7+
{% endfor %}

netsim/extra/kind/defaults.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# bgp.session default settings -- mostly attributes
2+
#
3+
---
4+
daemons:
5+
kind:
6+
parent: linux
7+
attributes:
8+
node:
9+
kind:
10+
workers: int
11+
initial:
12+
ipv4:
13+
unnumbered: false
14+
ipv6:
15+
lla: true
16+
use_ra: true
17+
clab:
18+
roles: [ host ]
19+
node:
20+
kind: k8s-kind
21+
features:
22+
lag: false
23+
vlan: false
24+
daemon_config:
25+
initial: /tmp/kind
26+
routing: /tmp/routing
27+
image: kindest/node:v1.34.3
28+
build: 'https://netlab.tools/netlab/clab/#netlab-clab-build'
29+
30+
kind-node:
31+
parent: linux
32+
clab:
33+
kind: ext-container
34+
features:
35+
lag: false
36+
vlan: false
37+
roles: [ host ]
38+
image: kindest/node:v1.34.3
39+
node_config:
40+
initial: /etc/config/01-initial.sh:cp_sh
41+
routing: /etc/config/02-routing.sh:cp_sh

netsim/extra/kind/plugin.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
from box import Box
3+
4+
from netsim.augment import links
5+
from netsim.data import get_new_box
6+
7+
_config_name = 'kind'
8+
_execute_after = ['node.clone', 'fabric']
9+
10+
def setup_kind_cluster(node: Box, topology: Box) -> None:
11+
k_workers = node.get('kind.workers',0)
12+
node.clab['startup-config'] = f'node_files/{node.name}/initial'
13+
kind_name = node.name
14+
k_nodes = [f'{kind_name}-control-plane']
15+
if k_workers > 0:
16+
k_nodes.append(f'{kind_name}-worker')
17+
for extra_worker in range(2,k_workers + 1):
18+
k_nodes.append(f'{kind_name}-worker{extra_worker}')
19+
20+
for kn_name in k_nodes:
21+
kn_data = topology.nodes[kn_name]
22+
kn_data.device = 'kind-node'
23+
kn_data.name = kn_name
24+
kn_data.interfaces = []
25+
kn_data.clab.kind = "ext-container"
26+
kn_data.clab.name = kn_name
27+
for attr in ('provider','routing','box'):
28+
if attr in node:
29+
kn_data[attr] = node[attr]
30+
31+
link_index = 0
32+
while link_index < len(topology.links):
33+
l_data = topology.links[link_index]
34+
iflist = l_data.interfaces
35+
kind_ifidx = -1
36+
for (if_idx,if_data) in enumerate(iflist):
37+
if if_data.node == kind_name:
38+
kind_ifidx = if_idx
39+
break
40+
41+
if kind_ifidx < 0:
42+
link_index += 1
43+
continue
44+
45+
if len(iflist) != 2 or l_data.get('type') in ['lan','stub']:
46+
for kn_name in k_nodes:
47+
iflist.append(iflist[kind_ifidx] + {'node': kn_name})
48+
iflist.pop(kind_ifidx)
49+
else:
50+
topology.links.pop(link_index)
51+
for (kn_idx,kn_name) in enumerate(k_nodes):
52+
kn_link = get_new_box(l_data)
53+
kn_link.interfaces[kind_ifidx].node = kn_name
54+
kn_link._linkname = l_data._linkname + f'[{kn_idx + 1}]'
55+
kn_link.linkindex = links.get_next_linkindex(topology)
56+
topology.links.append(kn_link)
57+
link_index += 1
58+
59+
"""
60+
topology_expand - expand nodes with a 'kind' attribute into KinD clusters
61+
"""
62+
def pre_node_transform(topology: Box) -> None:
63+
for node in list(topology.nodes.values()):
64+
if node.device != 'kind':
65+
continue
66+
setup_kind_cluster(node,topology)

0 commit comments

Comments
 (0)