Arista CloudVision and AVD with Containerlab: Full Lab - 夜莺博客

Arista CloudVision and AVD with Containerlab: Full Lab

用容器搭建 Arista 网络实验环境已经成为网工标配。本文完整演示如何组合三大工具:Containerlab 拉起 5 台 cEOS 容器构成 spine-leaf 拓扑,Arista Validated Design(AVD)自动生成配置,CloudVision(CVP)负责变更管理与配置下发,最后用两个 Linux 容器跨 VXLAN 验证东西向流量。全程真实命令输出,可直接复现。

工具链概览

CloudVision 是 Arista 的多域管理平台,支持 configlets、containers、tasks 与 change control;AVD 基于 Ansible 生成已验证的拓扑配置;Containerlab 提供 CLI 编排容器网络实验环境。三者组合可实现从拓扑定义到配置下发的全自动化。

CloudVision is the system of record. Every knob that gets pushed to a device lands there as a change control task, with a rollback point, an approval trail and an audit record; streaming telemetry from TerminAttr comes back through the same platform, so the lab you build here behaves exactly like a production fabric under change control. AVD (Arista Validated Design) sits above CloudVision: instead of hand-writing EOS configuration, you describe intent in a handful of YAML variables — fabric name, spine/leaf roles, VLANs, VNIs, BGP AS numbers — and AVD's roles render the hundreds of lines of underlay, overlay and network-services configuration that Arista has validated for that topology. Containerlab is the multiplier that makes both testable on one machine: it reads a topology file, starts cEOS containers, wires veth pairs between the endpoints you declare, and gives each node a management address so CloudVision can reach it over gNMI.

Why containerlab instead of GNS3 or EVE-NG? Because cEOS is a real EOS image with a real control plane, resource consumption is a fraction of a full virtual machine, startup is measured in seconds, and the entire lab is a text file that you can commit to git next to the Ansible inventory. When the lab breaks, containerlab destroy plus deploy gives you a clean topology in under a minute.

Prerequisites and Host Preparation

The host matters. Use a Linux machine or a Linux VM — Ubuntu 22.04 or 24.04 is the best-tested combination. On macOS, containerlab runs inside the Docker Desktop Linux VM, and veth-pair and VXLAN behaviour in that VM is not identical to bare metal; it works, but you will spend time chasing kernel-level oddities instead of learning EVPN. Budget 8 vCPU, 16 GB RAM and 60 GB of disk for five cEOS nodes plus two client containers. Each cEOS container idles around 1.5–2 GB of RAM, so five nodes plus the host OS fits comfortably in 16 GB.

Install Docker first, then containerlab, then import the cEOS image. The cEOS image is not publicly redistributable: download cEOS64-lab from the Arista support portal with your Arista account, and keep the file as a plain .tar archive (or .tar.xz). The imported image tag is what the topology file references, so pick something you can read at a glance.

# Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER && newgrp docker
docker ps

# cEOS image (file downloaded from Arista support portal)
docker import cEOS64-lab-4.32.2F.tar.xz ceos:4.32.2F
docker images | grep ceos

One more decision before you start: where CloudVision lives. This lab assumes a separate CVP node (on-prem VM or cloud instance) reachable from the management subnet 192.168.20.0/24. The cEOS containers need outbound TCP 9910 to the CVP address — that is the gNMI/OpenConfig+protobuf port TerminAttr uses — and nothing else. If your lab host sits behind NAT, put CVP on the same host or forward that port deliberately.

部署 cEOS 实验环境

1. 安装 Containerlab 并导入 cEOS 镜像

curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"
docker import cEOS-lab-4.32.2F.tar.xz ceos:4.32.2F

The setup script installs containerlab, its dependencies and the container images it needs for helper nodes. Verify with containerlab version. If docker import is slow, that is normal for a multi-gigabyte image — it takes minutes, not seconds, and the command is silent while it works.

2. 编写拓扑文件

name: spine-leaf-borderleaf
mgmt:
  network: custom_mgmt
  ipv4-subnet: 192.168.20.0/24
topology:
  nodes:
    node-1:
      kind: arista_ceos
      image: ceos:4.32.2F
      startup-config: node1-startup-config.cfg
      mgmt-ipv4: 192.168.20.2
    # ... node-2 ~ node-5 类似,IP 依次递增
  links:
    - endpoints: ["node-3:eth1", "node-1:eth1"]
    - endpoints: ["node-3:eth2", "node-2:eth1"]

A couple of details that save debugging later. The kind: arista_ceos value tells containerlab to use the EOS-specific node handler, which is what puts every container into the CLI-ready state and maps the management interface correctly. The mgmt.network: custom_mgmt line creates a dedicated Docker bridge instead of using the default management network, so the lab does not collide with anything else on the host. Number your nodes consistently with the Ansible inventory: node-1 and node-2 are spines, node-3 to node-5 are leaves, and the link list declares exactly the physical cabling you would install in a rack.

3. cEOS startup-config 要点

每台 cEOS 的启动配置需要包含 TerminAttr 守护进程(用于把设备加入 CVP 清单)、管理 VRF 与 API 配置:

daemon TerminAttr
 exec /usr/bin/TerminAttr -cvaddr=172.18.100.99:9910 -cvauth=token,/tmp/token -cvvrf=MGMT -disableaaa
 no shutdown
!
vrf instance MGMT
interface Management0
 no shutdown
 vrf MGMT
 ip address 192.168.20.2/24
!
ip route vrf MGMT 0.0.0.0/0 192.168.20.1
management api http-commands
 protocol https
 no shutdown
 vrf MGMT
 no shutdown

Read the TerminAttr line carefully, because three of its flags are the ones people get wrong. -cvaddr points at the CloudVision node and must be an address the container can reach, and -cvvrf=MGMT tells TerminAttr to source that connection from the management VRF — omit it and the daemon tries to egress through the default VRF, which in a lab has no route. -disableaaa suppresses authentication for lab convenience and must never appear in production. -cvauth=token,/tmp/token uses an enrolment token generated in the CloudVision UI; the alternative is -cvauth=key,<key>.

Two more notes. The MGMT VRF must exist before interface Management0 can be assigned to it — order matters in the startup config, because EOS applies the lines sequentially. And the management api block is what allows AVD to push configuration via eAPI rather than SSH; without it the Ansible run fails at the first task with an authentication timeout that looks like a credential problem but is actually a missing API.

4. 部署与验证

sudo containerlab deploy -t spine-leaf-border.yaml
sudo containerlab graph -t spine-leaf-border.yaml   # 浏览器查看拓扑图 http://localhost:50080

部署后 cEOS 会自动出现在 CloudVision 的 Undefined 容器中,等待 AVD 创建任务。

Verify the lab before touching AVD. Check that the containers are running, then hop into a spine and confirm the underlay came up on its own from the startup configs:

docker ps --format "table {{.Names}}\t{{.Status}}"
docker exec -it clab-spine-leaf-borderleaf-node-3 Cli
node-3# show ip route vrf MGMT
node-3# show interfaces status
node-3# show ip bgp summary
node-3# show lldp neighbors

If LLDP shows the expected neighbours and BGP sessions between leaves and spines are Established, the physical and underlay layers are good. If BGP is Idle or Active, the problem is almost always in the startup-config — a wrong interface name, an IP typo, or a missing no shutdown on a routed port — and it is far cheaper to find that now than after AVD has pushed overlay config on top.

AVD 对接 CloudVision

AVD 项目需要修改 deploy-cvp.yml 的 execute_tasks 开关(false 表示只创建 configlets/tasks 等人工审批,true 表示自动执行),并在 inventory.yml 中按 FABRIC > DC1 > DC1_SPINES / DC1_L3_LEAVES 组织设备组。AVD 会根据 group_vars 生成意图配置,通过 CVP 的 Change Control 审批后下发到对应设备。

# install AVD into a virtualenv
python3 -m venv avd && source avd/bin/activate
pip install "pyavd[ansible]" ansible-core
ansible-galaxy collection install arista.avd

# run the build
ansible-playbook playbooks/build.yml
# inventory.yml
all:
  children:
    FABRIC:
      children:
        DC1:
          children:
            DC1_SPINES:
              hosts:
                node-1:
                  ansible_host: 192.168.20.2
                node-2:
                  ansible_host: 192.168.20.3
            DC1_L3_LEAVES:
              hosts:
                node-3:
                  ansible_host: 192.168.20.4
# group_vars/DC1.yml
fabric_name: DC1
design:
  type: l3ls-evpn
underlay:
  routing_protocol: bgp
  bgp_as: "65001"
bgp_peers:
  - peer: 172.18.100.99
    peer_as: "65000"
    ipv4_afi: evpn

The mental model is that build.yml renders configuration locally and deploy-cvp.yml pushes it. In the lab, run build first, inspect the generated EOS config under intended/configs/, and only then deploy. With execute_tasks: false the deploy run creates configlets, containers and change control tasks but stops short of executing them, so you can review the diff in the CloudVision UI and approve it by hand — which is precisely how you would work in production. Flip it to true once you trust the pipeline and want the lab to rebuild itself unattended. Because the cEOS startup-configs already include the TerminAttr daemon, the devices report into CloudVision's Undefined container within about a minute of deployment; AVD moves them into the DC1 container as part of the deploy run.

跨 VXLAN 连通性验证

在拓扑中追加两个 ubuntu 客户端容器,分别接在两台 leaf 的下联 bridge 上,配置静态 IP 后互 ping:

root@client-1:/# ip addr add 10.71.0.11/24 dev eth1
root@client-1:/# ip route add 10.70.0.0/24 via 10.71.0.1
root@client-1:/# ping 10.70.0.12

在 leaf 上可用 show vxlan address-table 与 show arp vrf VRF11 确认远端 MAC 经 EVPN 学习、VXLAN 隧道转发正常——整个实验环境从零到可验证仅需几分钟,故障后 destroy 重建即可。

node-3# show vxlan address-table
node-3# show bgp evpn route-type mac-ip
node-3# show interfaces vxlan1
node-3# show arp vrf VRF11
node-3# show ip route vrf VRF11 10.70.0.12

What success looks like: show vxlan address-table lists the remote client's MAC with a VTEP interface reference, show bgp evpn route-type mac-ip shows the Type-2 route with the remote VTEP as the next hop, and the ping returns five replies. If the ping fails, check MTU first — VXLAN adds 50 bytes of overhead, so a client interface left at 1500 with an underlay that cannot carry 1550 will pass small pings and drop anything large. Raise the underlay MTU to 9214 on the spine-leaf links and keep the client side at 1500, then retest with ping -s 1400 -M do.

Troubleshooting Common Failures

  • Device never leaves the Undefined container — TerminAttr is not connected. Check show daemon TerminAttr inside the container, confirm -cvaddr is reachable from the MGMT VRF, and verify the enrolment token has not expired.
  • AVD build succeeds but the deploy run fails — usually the token/key or the management api block missing from startup-config, so eAPI refuses the connection.
  • BGP EVPN sessions come up but no VXLAN traffic flows — the VLAN-to-VNI mapping differs between leaves, or the same VNI is bound to different VLAN IDs; the overlay matches on VNI, so the VNI must be identical while the local VLAN ID can be anything you like.
  • containerlab deploy fails immediately — the image tag in the topology file does not match the imported image (ceos:4.32.2F versus whatever you actually imported), or the kind is wrong.
  • Everything works until you restart the host — containerlab does not auto-start labs at boot; re-run deploy, which reuses the existing directory and re-establishes the topology.

From Lab to Production

The reason to build this lab is that the artefacts scale. The topology file becomes a rack elevation, the Ansible inventory becomes your real inventory, and the group_vars become the single place where intent lives. What changes in production is mostly process: execute_tasks stays false, every push goes through change control with an approval, TerminAttr runs with real AAA instead of -disableaaa, and the lab host is replaced by real hardware. What does not change is the workflow — render locally, review the diff, approve in CloudVision, verify with the same show commands you used in the lab. Teams that practise the workflow on containers before a maintenance window ship far fewer self-inflicted outages.

Reusing the lab: changing the design and rebuilding

The lab pays for itself the second time you build it, because the artifacts are inputs, not outputs. To add a second leaf pair, extend the topology file with two more arista_ceos nodes and the new links, add the hosts to the Ansible inventory under DC1_L3_LEAVES, and re-run build.yml. AVD re-renders the underlay, the overlay and the device configuration from the same group_vars; nothing is hand-edited. Compare the freshly rendered config with git diff before you push, and the review becomes a review of intent rather than of syntax.

# after editing the topology file and the inventory
sudo containerlab deploy -t spine-leaf-border.yaml --reconfigure
ansible-playbook playbooks/build.yml
git -C avd diff intended/configs/ | head -60
ansible-playbook playbooks/deploy-cvp.yml

Three things change as the fabric grows. The management subnet in mgmt.ipv4-subnet has to cover every node, and ansible_host must match the container IP exactly or the deploy run fails on the first device. The number of change control tasks in CloudVision scales with the device count, so a five-node fabric that produced a handful of tasks will produce a screenful — use containers to group them rather than approving device by device. And RAM becomes the limiting resource long before CPU: each cEOS container holds its own control plane, so measure the host before you double the node count rather than after.

For teams that want to run the same topology with a second vendor in the same lab, the multi-vendor containerlab guide covers mixing node kinds and sharing the management bridge; for the inventory and playbook mechanics that AVD depends on, see Ansible network automation playbook examples.

相关阅读

EOS 的 MLAG 双活配置见本站 Arista EOS MLAG Configuration: Complete Guide;EVPN-VXLAN 数据中心架构见 EVPN-VXLAN Data Center Fabric: Design Guide 2026;Spine-Leaf 架构基础见 Spine-Leaf Architecture: Modern Data Center Topology。多厂商容器实验室的通用搭建方法见 Containerlab Guide: Build Multi-Vendor Network Labs with Docker,遥测采集方式的选择见 gNMI Telemetry: Dial-In vs Dial-Out for Networks。

原文链接:https://blog.andreasm.io/2024/08/22/arista-cloudvision-and-avd-using-containerlab/