Containerlab, the future of your virtual network lab (Part 3)

Containerlab original image of demostration
cotntainerlab

It has been almost 7 months since the last post: Containerlab, the future of your virtual network lab (Part 2). A new year has started as well, so before we get into the topic, I would like to say

Happy new year to all our readers!

Going back into the topic. In the last two posts, we talked about what Containerlab is, and how to get a basic network mapped and working.

The problem with our network setup from part-2

While the initial setup we had on part 2 of this series gives us a working setup, it has some pitfalls, for example:

  1. Once we stop our lab, configurations are lost for all nodes
  2. We would have to reconfigure all our nodes every time we start the lab

Solving config issues

Fortunately, we can go around the issues expressed above, and make our nodes apply configurations after they boot up. This will allow us to simplify the deployment, and spend less time performing configuration changes and setup

Folder structure

This is our folder structure for this lab:

.
├── configs
│   ├── ceos1.cfg
│   ├── ceos2.cfg
│   ├── init_centos1.sh
│   └── init_centos2.sh
└── lab01.clab.yml

Now, let us explore the content of each file inside configs:

ceos1.cfg:

vlan internal order descending range 3000 4094
!
hostname ceos1-jtech
!
spanning-tree mode none
!
no aaa root
!
username autoadmin privilege 15 role network-admin secret sha512 $6$oyqPzrhnOWEv/vTa$9Aiol0gBc.O/C0MXmP2mKEqqv5u2eRc4jRXNTZ/44Dhi8ZTRmV3kgzKJgoMo27V/RqwlL5l1SU0o41JrXFC0d1
!
vrf instance mgmtVrf
!
ip routing
ip routing vrf mgmtVrf
!
ipv6 unicast-routing
ipv6 unicast-routing vrf mgmtVrf
!
vlan 10
  name servers
!
vlan 20
  name clients
!
interface Loopback0
  description C: cEOS1-Loopback0
  ip address 1.1.1.1/32
  ipv6 address 2001:db8::1:1:1:1/128
!
interface ethernet1
  description L: cEOS2-Eth1
  no switchport
  load-interval 30
  ip address 10.10.10.0/31
  ipv6 address 2001:db8:100::0/127
  ip ospf area 0
  ipv6 ospf 1 area 0
!
interface ethernet2
  description L: centos1
  load-interval 30
  switchport
  switchport mode access
  switchport access vlan 10
!
interface Management0
  description L: Mgmt Interface
  vrf mgmtVrf
  ip address 10.10.10.2/24
  ipv6 address 2001:10:10:10::2/64
!  
interface vlan 10
  description H: Servers vlan
  load-interval 30
  ip address 172.16.10.1/24
  ipv6 address 2001:db8:172:16:10::1/64
!
router ospf 1
  router-id 1.1.1.1
  redistribute connected
  redistribute static
  log-adjacency-changes details
  bfd default
!
ipv6 router ospf 1
  router-id 1.1.1.1
  redistribute static
  redisttibute connected
  log-adjacency-changes details
  bfd default
!
router bfd
   interval 500 min-rx 500 multiplier 3 default
!
management api http-commands
   no shutdown
!
management api gnmi
   transport grpc default
!
management api netconf
   transport ssh default
!

ceos2.cfg

vlan internal order descending range 3000 4094
!
hostname ceos2-jtech
!
spanning-tree mode none
!
no aaa root
!
username autoadmin privilege 15 role network-admin secret sha512 $6$oyqPzrhnOWEv/vTa$9Aiol0gBc.O/C0MXmP2mKEqqv5u2eRc4jRXNTZ/44Dhi8ZTRmV3kgzKJgoMo27V/RqwlL5l1SU0o41JrXFC0d1
!
vrf instance mgmtVrf
!
ip routing
ip routing vrf mgmtVrf
!
ipv6 unicast-routing
ipv6 unicast-routing vrf mgmtVrf
!
vlan 10
  name servers
!
vlan 20
  name clients
!
interface Loopback0
  description C: cEOS2-Loopback0
  ip address 2.2.2.2/32
  ipv6 address 2001:db8::2:2:2:2/128
!
interface ethernet1
  description L: cEOS2-Eth1
  no switchport
  load-interval 30
  ip address 10.10.10.1/31
  ipv6 address 2001:db8:100::1/127
  ip ospf area 0
  ipv6 ospf 1 area 0
!
interface ethernet2
  description L: centos1
  load-interval 30
  switchport
  switchport mode access
  switchport access vlan 10
!
interface Management0
  description L: Mgmt Interface
  vrf mgmtVrf
  ip address 10.10.10.3/24
  ipv6 address 2001:10:10:10::3/64
!
interface vlan 10
  description H: Servers vlan
  load-interval 30
  ip address 192.168.10.1/24
  ipv6 address 2001:db8:192:168:10::1/64
!
router ospf 1
  router-id 2.2.2.2
  redistribute connected
  redistribute static
  log-adjacency-changes details
  bfd default
!
ipv6 router ospf 1
  router-id 2.2.2.2
  redistribute static
  redisttibute connected
  log-adjacency-changes details
  bfd default
!
router bfd
   interval 500 min-rx 500 multiplier 3 default
!
management api http-commands
   no shutdown
!
management api gnmi
   transport grpc default
!
management api netconf
   transport ssh default
!

init_centos1.sh

#!/bin/bash 
ip address add 172.16.10.2/24 dev eth1
ip address add 2001:db8:172:16:10::2/64 dev eth1
ip route del default
ip route add 172.16.0.0/12 via 172.16.10.1 dev eth1
ip route add default via 172.16.10.1
ip route add 2001:db8::/32 via 2001:db8:172:16:10::1 dev eth1

init_centos2.sh

#!/bin/bash
ip address add 192.168.10.2/24 dev eth1
ip address add 2001:db8:192:168:10::2/64 dev eth1
ip route del default
ip route add 192.168.0.0/16 via 192.168.10.1 dev eth1
ip route add default via 192.168.10.1
ip route add 2001:db8::/32 via 2001:db8:192:168:10::1 dev eth1

As we can see these are very basic configs for the Arista and Centos nodes. Which allows us to have the nodes communicating with each other.

Adding the configs to our lab network setup

Now that we have the configurations added into our configs folder, we need to specify the actions on the containerlab topology file (lab01.clab.yml)

For nodes that are like Arista, Nokia, Juniper, etc, we will use the startup-config keyword and pass the file path where the config is located, in this case, configs/ceos1.cfg

Now our node definition on the lab01.clab.yml will look like this:

topology:
  nodes:
    ceos1:
      kind: ceos
      image: ceos:4.25.4M
      startup-config: ./configs/ceos1.cfg
      mgmt_ipv4: 10.10.10.2
      mgmt_ipv6: 2001:10:10:10::2

You can repeat this process for the other nodes that you want to apply the configuration to

Adding and running scripts on Linux nodes

Although the startup-config loads any configuration we need for our networking nodes, applying configuration to Linux nodes is a bit different. For that reason, we need to take a different approach as to how we need to configure it.

If we look back at our lab topology, you will see that we have 2 client nodes running centos 8

Now, based on our topology, and what we want to achieve of the 2 centos devices being able to ping each other while on different subnets, we need to implement some basic IP configuration as well as routes.

The problem with the centos devices is the same as we have with the cEOS devices, where every time we destroy our lab the configuration is removed and we would have to add it again.

To solve this issue we created a bash script for each node that configures the IPv4 and IPv6 addresses as well as routes.

To be able to accomplish this, we need to add some changes to our node definition on the lab topology file

centos1:
      kind: linux
      image: centos:8
      binds:
        - ./configs/init_centos1.sh:/init_centos1.sh
      exec:
        - bash /init_centos1.sh
      mgmt_ipv4: 10.10.10.4
      mgmt_ipv6: 2001:10:10:10::4

So, if we look at the configuration here, what we are doing is using the binds: keyword to map our local directory and file to a specific directory and file on our node.

This is like the volume keyword in Docker.

The binds keyword take a list of bindings, allowing multiple files or folders to be shared between your local machine to your virtual node

After that, we will use the exec: keyword to specify a list of commands that we want the node to run after startup.

This is similar to the command keyword on Docker

As the binds keyword, the exec keyword can take a list of commands to be run after startup

Running our lab after the changes

Now with all these changes in place, we can start to run our lab again. At this time, when we run the lab though, a full configuration will be in place on our nodes.

we can verify this by entering the container bash and checking if we can ping between devices

We can see the results here:

docker exec -it clab-lab01-centos1 bash
[root@centos1 /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
111: eth0@if112: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:0a:0a:0a:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.10.10.4/24 brd 10.10.10.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2001:10:10:10::4/64 scope global nodad 
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe0a:a04/64 scope link 
       valid_lft forever preferred_lft forever
119: eth1@if120: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9500 qdisc noqueue state UP group default 
    link/ether aa:c1:ab:59:59:6d brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 172.16.10.2/24 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 2001:db8:172:16:a8c1:abff:fe59:596d/64 scope global dynamic mngtmpaddr 
       valid_lft 2591944sec preferred_lft 604744sec
    inet6 2001:db8:172:16:10::2/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::a8c1:abff:fe59:596d/64 scope link 
       valid_lft forever preferred_lft forever
[root@centos1 /]# ip route 
default via 172.16.10.1 dev eth1 
10.10.10.0/24 dev eth0 proto kernel scope link src 10.10.10.4 
172.16.0.0/12 via 172.16.10.1 dev eth1 
172.16.10.0/24 dev eth1 proto kernel scope link src 172.16.10.2 
[root@centos1 /]# ip -6 route
2001:10:10:10::/64 dev eth0 proto kernel metric 256 pref medium
2001:db8:172:16::/64 dev eth1 proto kernel metric 256 pref medium
2001:db8::/32 via 2001:db8:172:16:10::1 dev eth1 metric 1024 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth1 proto kernel metric 256 pref medium
default via 2001:10:10:10::1 dev eth0 metric 1024 pref medium
default via fe80::21c:73ff:fee3:6760 dev eth1 proto ra metric 1024 expires 1736sec mtu 1500 hoplimit 64 pref medium
[root@centos1 /]# ping 172.16.10.1
PING 172.16.10.1 (172.16.10.1) 56(84) bytes of data.
64 bytes from 172.16.10.1: icmp_seq=1 ttl=64 time=1.97 ms
64 bytes from 172.16.10.1: icmp_seq=2 ttl=64 time=2.10 ms
^C
--- 172.16.10.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 1.972/2.035/2.098/0.063 ms
[root@centos1 /]# ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=62 time=6.71 ms
64 bytes from 192.168.10.2: icmp_seq=2 ttl=62 time=4.42 ms
64 bytes from 192.168.10.2: icmp_seq=3 ttl=62 time=4.70 ms
^C
--- 192.168.10.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 4.417/5.274/6.712/1.026 ms

Where to go from here

Now that you have most of the bases of Containerlab you can go over to their official site and documentation and explore other network Operative Systems that you can use with Containerlab

There are also multiple examples of lab examples that you can run with a more complicated topology than the example here

If you are interested in this lab example and code, you can find all the files on my GitHub repository

Leave a Reply

%d bloggers like this: