Michael Zamot / Writing

Containerlab: a network lab on your own machine

A hands-on introduction to containerlab: describe a network in YAML, deploy it in seconds, inspect it, and tear it down.

Learning a routing protocol, reproducing a bug, or rehearsing a configuration change all need the same thing: a network you can break without consequences. The usual options are borrowed hardware, which is scarce and slow to rewire, or a hand-built lab of virtual machines, which takes an afternoon to set up and that nobody else can easily recreate.

Containerlab takes a different approach: the lab is a text file. It lists the nodes and the links between them, and containerlab turns that into a running network in seconds, using container images of real routing software. Because a lab is just files, you can keep it in git, hand it to a colleague, rebuild it identically next month, and delete it without leaving anything behind. The same labs can run in a CI pipeline as network tests.

Nodes can be open source routers, as in this post, or container images of vendor network operating systems such as Nokia SR Linux and Arista cEOS, and containerlab can also run full virtual machines for software that doesn’t come as a container.

This post walks through the whole workflow with a small routed network of three FRR routers: write the topology, deploy it, inspect it, test it, and tear it down. FRR is open source, so you don’t need an account or a license to follow along.

The workflow

You describe the lab in a YAML topology file, and then four commands cover almost everything you do with it:

CommandWhat it does
deployCreates the nodes and links described in the file
inspectLists the running nodes and their addresses
execRuns a command on one or all nodes
destroyRemoves the whole lab

What you need

The --runtime podman flag in the commands below tells containerlab to use podman.

The lab

Three routers in a line, running FRR, with static routes between them.

 r1 ───────────── r2 ───────────── r3
 lo 10.255.0.1/32                   lo 10.255.0.3/32
        10.0.12.0/24       10.0.23.0/24
NodeInterfaceIPv4 addressConnected to
r1eth110.0.12.1/24r2
r1lo10.255.0.1/32—
r2eth110.0.12.2/24r1
r2eth210.0.23.1/24r3
r3eth110.0.23.2/24r2
r3lo10.255.0.3/32—

Get the files from the lab repository and cd into containerlab-basics.

The topology file

name: hello

topology:
  kinds:
    linux:
      image: quay.io/frrouting/frr:10.7.1

  nodes:
    r1:
      kind: linux
      binds:
        - configs/r1/frr.conf:/etc/frr/frr.conf
        - configs/vtysh.conf:/etc/frr/vtysh.conf
    r2:
      kind: linux
      binds:
        - configs/r2/frr.conf:/etc/frr/frr.conf
        - configs/vtysh.conf:/etc/frr/vtysh.conf
    r3:
      kind: linux
      binds:
        - configs/r3/frr.conf:/etc/frr/frr.conf
        - configs/vtysh.conf:/etc/frr/vtysh.conf

  links:
    - endpoints: ["r1:eth1", "r2:eth1"]
    - endpoints: ["r2:eth2", "r3:eth1"]
  • name is part of every node’s name: clab-<name>-<node>, so clab-hello-r1.
  • kinds sets defaults for all nodes of a kind, so the FRR image is written once.
  • nodes lists the routers. binds mounts a config file from your lab directory into the node, with paths relative to the topology file.
  • links connect node:interface pairs, and containerlab creates the interfaces with the names you give. eth0 is reserved for the management network, so data links start at eth1.

Each router’s frr.conf holds its addresses and static routes. This is r1:

frr defaults traditional
hostname r1
!
interface eth1
 ip address 10.0.12.1/24
exit
!
interface lo
 ip address 10.255.0.1/32
exit
!
ip route 10.255.0.3/32 10.0.12.2
ip route 10.0.23.0/24 10.0.12.2

vtysh.conf is a single line, service integrated-vtysh-config, which makes FRR’s vtysh shell read this one file.

Deploy

sudo containerlab --runtime podman deploy -t topology.clab.yml

The output ends with a table of the nodes:

╭───────────────┬──────────────────────────────┬─────────┬───────────────────╮
│      Name     │          Kind/Image          │  State  │   IPv4/6 Address  │
├───────────────┼──────────────────────────────┼─────────┼───────────────────┤
│ clab-hello-r1 │ linux                        │ running │ 172.20.20.3       │
│               │ quay.io/frrouting/frr:10.7.1 │         │ 3fff:172:20:20::3 │
├───────────────┼──────────────────────────────┼─────────┼───────────────────┤
│ clab-hello-r2 │ linux                        │ running │ 172.20.20.4       │
│               │ quay.io/frrouting/frr:10.7.1 │         │ 3fff:172:20:20::4 │
├───────────────┼──────────────────────────────┼─────────┼───────────────────┤
│ clab-hello-r3 │ linux                        │ running │ 172.20.20.2       │
│               │ quay.io/frrouting/frr:10.7.1 │         │ 3fff:172:20:20::2 │
╰───────────────┴──────────────────────────────┴─────────┴───────────────────╯

Those addresses are on eth0, the management network containerlab creates so you can reach the nodes. Print the table again at any time with containerlab inspect -t topology.clab.yml.

Look around

Run a command inside a node with the container runtime:

sudo podman exec clab-hello-r1 vtysh -c "show ip route static"
IPv4 unicast VRF default:
S>* 10.0.23.0/24 [1/0] via 10.0.12.2, eth1, weight 1, 00:00:02
S>* 10.255.0.3/32 [1/0] via 10.0.12.2, eth1, weight 1, 00:00:02

For an interactive FRR shell, run sudo podman exec -it clab-hello-r1 vtysh.

Containerlab can also run one command on every node:

sudo containerlab --runtime podman exec -t topology.clab.yml \
  --cmd "vtysh -c 'show ip route static'"

Test it

r1 pings r3’s loopback, and the packets have to cross r2:

sudo podman exec clab-hello-r1 ping -c 3 10.255.0.3
PING 10.255.0.3 (10.255.0.3): 56 data bytes
64 bytes from 10.255.0.3: seq=0 ttl=63 time=0.159 ms
64 bytes from 10.255.0.3: seq=1 ttl=63 time=0.057 ms
64 bytes from 10.255.0.3: seq=2 ttl=63 time=0.072 ms

--- 10.255.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.057/0.096/0.159 ms

The ttl=63 shows the packet crossed one router.

Tear it down

sudo containerlab --runtime podman destroy -t topology.clab.yml --cleanup

--cleanup also removes the clab-hello/ directory containerlab creates next to your topology file. Redeploy whenever you want a fresh start.

← All writing