Containerlab Guide: Build Multi-Vendor Network Labs with Docker - 夜莺博客

Containerlab Guide: Build Multi-Vendor Network Labs with Docker

Building a lab used to mean hardware, licenses and a spare rack. Containerlab replaces that with containers: you describe the topology in a short YAML file, and it wires virtual routers and switches together, allocates management addresses and gives you a repeatable environment that fits in a CI pipeline. This guide covers installation, the topology definition file, links and management networks, startup configurations, and the lifecycle commands you will use every day.

Install and check your environment

bash -c "$(curl -sL https://get.containerlab.dev)"
containerlab version

docker pull ghcr.io/nokia/srlinux
docker pull ceos:latest                 # Arista cEOS (from Arista support)
docker pull frrouting/frr:latest        # free, Linux-based router

Docker must be running and your user should be in the docker group. If you have no vendor images, FRRouting, Linux containers and Nokia SR Linux are all freely available and enough to build a convincing BGP/OSPF lab.

Before you build anything, validate the host. Containerlab needs a reasonably recent kernel, working cgroups, and enough memory to run every node image you plan to start at once - a four-node cEOS topology can consume several gigabytes before you configure anything. A short pre-flight check catches the usual problems early:

docker version --format '{{.Server.Version}}'
docker info | grep -i -E 'cgroup|storage driver'
sysctl net.ipv4.ip_forward
free -h
nproc
groups | grep -w docker   # your shell must be in the docker group

If groups does not list docker, log out and back in after adding yourself, or every containerlab command will fail on a permission error that looks like a missing installation. Containerlab itself is a single Go binary; the installer drops it in /usr/bin and it needs no daemon of its own.

Anatomy of a Lab: Kinds, Images and Nodes

Three concepts carry the whole model, and understanding them prevents most beginner errors:

  • Kind - the character of the node. It tells containerlab which image family this is, which interface naming convention applies, how to inject a startup config, and whether the container needs special privileges. linux, nokia_srlinux, arista_ceos, cisco_xrd and juniper_crpd are the common ones.
  • Image - the container image tag that provides the software. Two nodes of the same kind can use different image versions, which is exactly how you test an upgrade before touching production.
  • Node - a named instance in the topology. The name becomes the container name (clab-<lab>-<node>), the DNS name inside the management network, and the prefix for every interface and config file.

Some kinds carry licensing constraints that you must respect. Nokia SR Linux and FRRouting are freely redistributable. Arista cEOS images are distributed under Arista's terms and require a valid account, and this is where containerlab's model helps most - testing against a cEOS image is a legitimate, supported way to validate configuration before it lands on a production switch. Juniper cRPD and Cisco XRd follow similar vendor-specific distribution rules.

The topology file

name: dc-lab

mgmt:
  network: clab-mgmt
  ipv4-subnet: 172.20.20.0/24

topology:
  nodes:
    spine1:
      kind: linux
      image: frrouting/frr:latest
      binds:
        - configs/spine1:/etc/frr/frr.conf
    spine2:
      kind: linux
      image: frrouting/frr:latest
    leaf1:
      kind: linux
      image: frrouting/frr:latest
    leaf2:
      kind: nokia_srlinux
      image: ghcr.io/nokia/srlinux
      startup-config: configs/leaf2.cfg
  links:
    - endpoints: ["spine1:eth1", "leaf1:eth1"]
    - endpoints: ["spine1:eth2", "leaf2:eth1"]
    - endpoints: ["spine2:eth1", "leaf1:eth2"]
    - endpoints: ["spine2:eth2", "leaf2:eth2"]

Each node needs a kind (which tells containerlab how to treat the container) and an image. Endpoints name a node and an interface - the interface name must exist inside the container, so consult the kind's documentation for the naming (.e.g. cEOS uses eth1 onward, SR Linux uses e1-1).

Interface numbering follows a container-scheduling reality that surprises people: containerlab grows interfaces in the order links are declared, starting at the first free index for that kind. Because the order of links entries decides which physical port each link lands on, keep the link list sorted (spine1-to-leaf1, spine1-to-leaf2, spine2-to-leaf1, spine2-to-leaf2) and your port numbering will be predictable across rebuilds. Change the order of the list and you silently change the cabling - which is exactly the kind of difference that bites after a lab has been moved into CI.

Deploy, inspect, destroy

containerlab deploy -t dc-lab.clab.yml
containerlab inspect -a
containerlab inspect --format json | jq '.containers[].name'

docker exec -it clab-dc-lab-leaf1 vtysh
# inside the lab: configure, then exit back to the host

containerlab graph -t dc-lab.clab.yml      # SVG of the topology
containerlab destroy -t dc-lab.clab.yml
containerlab destroy -t dc-lab.clab.yml --cleanup

Deployment prints a table with container names and management IP addresses; those addresses are how you SSH or attach to each node. Use --cleanup when you want the management network and artefacts removed as well, which prevents address-pool exhaustion on long-lived lab hosts.

The inspect --format json output is the one to script against. It gives you container name, kind, image, management address and state in a single document, which means a pipeline can discover the lab's shape instead of hard-coding addresses that the allocator may hand out differently next time.

Management network and external access

# publish a node port to the host for browser access
topology:
  nodes:
    leaf2:
      kind: nokia_srlinux
      image: ghcr.io/nokia/srlinux
      ports:
        - 8443:443

Containerlab creates a bridge for the management network, so every node is reachable from the lab host and from your workstation if you route the 172.20.20.0/24 subnet. This is what lets you point Ansible, NAPALM, pyATS or a NetBox-driven pipeline at lab devices that look and behave like real ones.

The bridge is where the lab meets the outside world, and the details matter:

  • Reachability. Containerlab creates a Linux bridge (by default clab) and attaches a management veth on the host side. From the host you can reach every node directly; from your laptop you need a route to the management subnet, either via a static route or by reaching the lab host over SSH and working there.
  • DNS. Containerlab can register node names in the host's DNS or provide a local /etc/hosts style resolution inside the lab, so ssh admin@leaf1 works without looking up addresses.
  • Published ports. The ports: stanza forwards container ports to the host, which is how you open an SR Linux or cEOS web UI in a browser, or expose a gNMI/gRPC endpoint for a telemetry collector running outside the lab.
  • Isolation. Because each lab gets its own bridge and subnet, two labs on the same host can both have a device called leaf1 without colliding.

For automation-heavy labs, this is the point where containerlab becomes more than a simulator: the management plane is a normal Linux network, so any tool that can talk to a real device can talk to these - see Ansible and Jinja2 templating for network configs and Ansible network modules for config and facts for the collection side of that workflow.

Startup configuration and version control

Keep configs/ in Git next to the topology file, one file per node, and reference them either with startup-config or with binds. That gives you a lab that can be rebuilt identically after any disaster - and the same repository becomes the test fixture for automation. For more advanced setups, containerlab supports magic variables such as __clabNodeName__ and shared binds, so one template file can serve every node in a large fabric.

Understanding the difference between the two attachment methods saves debugging time later:

  • startup-config is consumed by the kind's startup procedure. The file is copied into the container and applied by the platform-specific mechanism (for SR Linux this becomes a CLI script; for cEOS it becomes the startup configuration).
  • binds mounts a host file or directory straight into the container, so relative paths resolve against the topology file's own directory. Use it for FRR configs, sysctl settings, or a whole configs/ directory shared between nodes.

Magic variables make templates pay off in a big topology. A single FRR config containing __clabNodeName__ or a per-node hostname placeholder can be bound to every Linux node, so twenty routers share one maintained file instead of twenty copies that drift apart. When the lab doubles as a CI fixture, this is the difference between a topology that survives a year of edits and one nobody dares to touch.

Reproducible Labs in CI

The real payoff of describing a lab as YAML is that you can run it in a pipeline. The pattern that works looks like this:

#!/usr/bin/env bash
set -euo pipefail

# 1. bring up a fresh lab
containerlab deploy -t dc-lab.clab.yml

# 2. wait for routing to converge (never sleep blindly)
for i in $(seq 1 30); do
  if docker exec clab-dc-lab-leaf1 vtysh -c 'show ip bgp summary' \
       | grep -q 'Established'; then break; fi
  sleep 5
done

# 3. run the test suite against management addresses
py.test tests/ --lab-topology dc-lab.clab.yml

# 4. always tear down, even on failure
trap 'containerlab destroy -t dc-lab.clab.yml --cleanup -y' EXIT

Three rules keep this stable. Deploy a fresh lab per run rather than reusing one, so a failed run cannot poison the next. Poll for protocol convergence with a real condition instead of a fixed sleep, because convergence time varies with host load. And destroy with --cleanup in a trap so that a crashed test does not leave containers and addresses behind to exhaust the pool.

What to do with a lab

Test the change before production: BGP policy changes, EVPN-VXLAN designs, MLAG failure scenarios, upgrade procedures, automation playbooks. Because the environment is disposable, you can also break it deliberately, which is the cheapest way to learn how a protocol actually fails. Related reading: CloudVision and AVD with containerlab, Linux namespaces and veth pairs (what makes this all possible), Docker networking drivers and Open vSwitch basics.

Two more lab patterns are worth building once you are comfortable. A spine-leaf fabric with EVPN-VXLAN exercises control-plane and data-plane behaviour at the same time - and because the underlay is containers, you can deliberately oversubscribe it and watch how the fabric reacts to VXLAN MTU and fragmentation pressure. A CNI comparison lab takes the same container networking primitives and asks how a Kubernetes CNI plugin such as Calico, Cilium or Flannel would have built the same connectivity, which is a fast way to see where the mental models diverge.

If you want the foundational layer spelled out - how namespaces, veth pairs and bridges combine to create a device that behaves like hardware - start with our practical guide to Linux network namespaces and veth pairs. Everything containerlab does sits on top of that machinery.

原文链接:https://containerlab.dev/manual/topo-def-file/